1
0
mirror of https://github.com/bigchaindb/js-bigchaindb-driver.git synced 2024-11-22 09:46:58 +01:00

add and fix tests

This commit is contained in:
manolodewiner 2018-08-23 17:14:59 +02:00
parent 7d978286f5
commit ad8a889ecc
8 changed files with 139 additions and 56 deletions

View File

@ -22,7 +22,6 @@
"dev": "webpack -w", "dev": "webpack -w",
"clean": "rimraf dist/bundle dist/node", "clean": "rimraf dist/bundle dist/node",
"test": "npm run lint && nyc ava test/ && npm run thanks && npm run report-coverage", "test": "npm run lint && nyc ava test/ && npm run thanks && npm run report-coverage",
"mine-test": "nyc ava test/connection/test_mine*",
"thanks": "cowsay Hi, thanks for your interest in BigchainDB. We appreciate your contribution!", "thanks": "cowsay Hi, thanks for your interest in BigchainDB. We appreciate your contribution!",
"release": "./node_modules/release-it/bin/release-it.js --src.tagName='v%s' --github.release --npm.publish --non-interactive", "release": "./node_modules/release-it/bin/release-it.js --src.tagName='v%s' --github.release --npm.publish --non-interactive",
"release-minor": "./node_modules/release-it/bin/release-it.js minor --src.tagName='v%s' --github.release --npm.publish --non-interactive", "release-minor": "./node_modules/release-it/bin/release-it.js minor --src.tagName='v%s' --github.release --npm.publish --non-interactive",

View File

@ -1,4 +1,4 @@
import Transport from './Transport' import Transport from './transport'
const HEADER_BLACKLIST = ['content-type'] const HEADER_BLACKLIST = ['content-type']
const DEFAULT_NODE = 'http://localhost:9984' const DEFAULT_NODE = 'http://localhost:9984'
@ -44,7 +44,7 @@ export default class Connection {
} else { } else {
// TODO normalize URL if needed // TODO normalize URL if needed
const allHeaders = Object.assign({}, headers, node.headers) const allHeaders = Object.assign({}, headers, node.headers)
return { 'endpoint': node, 'headers': allHeaders } return { 'endpoint': node.endpoint, 'headers': allHeaders }
} }
} }
@ -125,7 +125,6 @@ export default class Connection {
* @param operation * @param operation
*/ */
listTransactions(assetId, operation) { listTransactions(assetId, operation) {
console.log('listtransaction', assetId)
return this._req(Connection.getApiUrls('transactions'), { return this._req(Connection.getApiUrls('transactions'), {
query: { query: {
asset_id: assetId, asset_id: assetId,

View File

@ -1,7 +1,6 @@
export Ed25519Keypair from './Ed25519Keypair' export Ed25519Keypair from './Ed25519Keypair'
export Connection from './connection' export Connection from './connection'
export Request from './request'
export Transaction from './transaction' export Transaction from './transaction'
export ccJsonLoad from './utils/ccJsonLoad' export ccJsonLoad from './utils/ccJsonLoad'
export ccJsonify from './utils/ccJsonify' export ccJsonify from './utils/ccJsonify'

View File

@ -21,11 +21,11 @@ export default class Request {
this.node = node this.node = node
this.requestConfig = requestConfig this.requestConfig = requestConfig
this.backoffTime = null this.backoffTime = null
this.retries = 0
this.connectionError = null
} }
async request(endpoint, config, timeout) { async request(endpoint, config, setTimeout) {
// Num or retries to the same node
this.retries = 0
// Load default fetch configuration and remove any falsy query parameters // Load default fetch configuration and remove any falsy query parameters
const requestConfig = Object.assign({}, this.node.headers, DEFAULT_REQUEST_CONFIG, config, { const requestConfig = Object.assign({}, this.node.headers, DEFAULT_REQUEST_CONFIG, config, {
query: config.query && sanitize(config.query) query: config.query && sanitize(config.query)
@ -43,7 +43,7 @@ export default class Request {
// If `ConnectionError` occurs, a timestamp equal to now + // If `ConnectionError` occurs, a timestamp equal to now +
// the default delay (`BACKOFF_DELAY`) is assigned to the object. // the default delay (`BACKOFF_DELAY`) is assigned to the object.
// The timestamp is in UTC. Next time the function is called, it either // Next time the function is called, it either
// waits till the timestamp is passed or raises `TimeoutError`. // waits till the timestamp is passed or raises `TimeoutError`.
// If `ConnectionError` occurs two or more times in a row, // If `ConnectionError` occurs two or more times in a row,
// the retry count is incremented and the new timestamp is calculated // the retry count is incremented and the new timestamp is calculated
@ -52,28 +52,27 @@ export default class Request {
// If a request is successful, the backoff timestamp is removed, // If a request is successful, the backoff timestamp is removed,
// the retry count is back to zero. // the retry count is back to zero.
this.backoffTimedelta = this.getBackoffTimedelta() const backoffTimedelta = this.getBackoffTimedelta()
if (timeout != null && timeout < this.backoffTimedelta) { if (setTimeout != null && setTimeout < this.backoffTimedelta) {
throw new Error() const errorObject = {
message: 'TimeoutError'
}
throw errorObject
} }
if (this.backoffTimedelta > 0) { if (backoffTimedelta > 0) {
await Request.sleep(this.backoffTimedelta) await Request.sleep(backoffTimedelta)
} }
this.timeout = this.timeout ? this.timeout - this.backoffTimedelta : timeout this.timeout = setTimeout ? setTimeout - backoffTimedelta : setTimeout
return baseRequest(apiUrl, requestConfig) return baseRequest(apiUrl, requestConfig)
.then(res => async function handleResponse() { .then(res => res.json())
res.json()
if (!(res.status >= 200 && res.status < 300)) {
console.log('Valid response')
}
})
.catch(err => { .catch(err => {
throw err // ConnectionError
this.connectionError = err
// throw err
}) })
.finally((res) => { .finally(() => {
this.updateBackoffTime(res) this.updateBackoffTime()
}) })
} }
@ -84,13 +83,13 @@ export default class Request {
return (this.backoffTime - Date.now()) return (this.backoffTime - Date.now())
} }
updateBackoffTime(success) { updateBackoffTime() {
if (success) { if (!this.connectionError) {
this.retries = 0 this.retries = 0
this.backoffTime = null this.backoffTime = null
} else { } else {
this.backoffTimedelta = BACKOFF_DELAY * (2 ** this.retries) const backoffTimedelta = BACKOFF_DELAY * (2 ** this.retries)
this.backoffTime = Date.now() + this.backoffTimedelta this.backoffTime = Date.now() + backoffTimedelta
this.retries += 1 this.retries += 1
} }
} }

View File

@ -1,3 +1,7 @@
// Copyright BigchainDB GmbH and BigchainDB contributors
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
// Code is Apache-2.0 and docs are CC-BY-4.0
import Request from './request' import Request from './request'
@ -15,11 +19,8 @@ export default class Transport {
if (this.connectionPool.length === 1) { if (this.connectionPool.length === 1) {
return this.connectionPool[0] return this.connectionPool[0]
} }
return this.minBackoff()
}
minBackoff() {
let connection = this.connectionPool[0] let connection = this.connectionPool[0]
this.connectionPool.forEach(conn => { this.connectionPool.forEach(conn => {
// 0 the lowest value is the time for Thu Jan 01 1970 01:00:00 GMT+0100 (CET) // 0 the lowest value is the time for Thu Jan 01 1970 01:00:00 GMT+0100 (CET)
conn.backoffTime = conn.backoffTime ? conn.backoffTime : 0 conn.backoffTime = conn.backoffTime ? conn.backoffTime : 0
@ -29,28 +30,36 @@ export default class Transport {
} }
async forwardRequest(path, headers) { async forwardRequest(path, headers) {
let response
let connection
while (!this.timeout || this.timeout > 0) { while (!this.timeout || this.timeout > 0) {
const connection = this.pickConnection() connection = this.pickConnection()
// Date in milliseconds // Date in milliseconds
const startTime = Date.now() const startTime = Date.now()
try { try {
// TODO wait until request is done // eslint-disable-next-line no-await-in-loop
const response = connection.request( response = await connection.request(
path, path,
headers, headers,
this.timeout this.timeout
) )
return response const elapsed = Date.now() - startTime
if (connection.backoffTime) {
this.timeout += elapsed
} else {
return response
}
if (connection.retries > 3) {
throw connection.connectionError
}
} catch (err) { } catch (err) {
throw err throw err
} finally {
const elapsed = Date.now() - startTime
if (this.timeout) {
this.timeout -= elapsed
}
} }
} }
throw new Error() const errorObject = {
message: 'Timeout error',
}
throw errorObject
} }
} }

View File

@ -1,8 +1,12 @@
import test from 'ava' import test from 'ava'
import sinon from 'sinon' import sinon from 'sinon'
import { Connection, Request } from '../../src' import {
import { API_PATH } from '../constants' Connection
} from '../../src'
import {
API_PATH
} from '../constants'
const conn = new Connection(API_PATH) const conn = new Connection(API_PATH)
@ -37,22 +41,59 @@ test('Generate API URLS', t => {
}) })
}) })
// TODO Redefine test test('Normalize node from an object', t => {
test('Request with custom headers', t => { const headers = {
const testConn = new Connection(API_PATH, { hello: 'world' }) custom: 'headers'
const expectedOptions = { }
const node = {
endpoint: API_PATH,
headers: { headers: {
hello: 'world'
}
}
const expectedNode = {
'endpoint': API_PATH,
'headers': {
hello: 'world', hello: 'world',
custom: 'headers' custom: 'headers'
} }
} }
// request is read only, cannot be mocked? t.deepEqual(Connection.normalizeNode(node, headers), expectedNode)
sinon.spy(Request, 'default') })
testConn._req(API_PATH, { headers: { custom: 'headers' } })
t.truthy(Request.default.calledWith(API_PATH, expectedOptions)) test('Normalize node from a string', t => {
Request.default.restore() const headers = {
custom: 'headers'
}
const expectedNode = {
'endpoint': API_PATH,
'headers': {
custom: 'headers'
}
}
t.deepEqual(Connection.normalizeNode(API_PATH, headers), expectedNode)
})
test('Request with custom headers', t => {
const testConn = new Connection(API_PATH, {
hello: 'world'
})
const expectedOptions = {
headers: {
custom: 'headers'
}
}
const PATH = 'blocks'
testConn.transport.forwardRequest = sinon.spy()
testConn._req(PATH, {
headers: {
custom: 'headers'
}
})
t.truthy(testConn.transport.forwardRequest.calledWith(PATH, expectedOptions))
}) })

View File

@ -36,7 +36,9 @@ test('Valid CREATE transaction', t => {
const txSigned = Transaction.signTransaction(tx, alice.privateKey) const txSigned = Transaction.signTransaction(tx, alice.privateKey)
return conn.postTransaction(txSigned) return conn.postTransaction(txSigned)
.then(resTx => t.truthy(resTx)) .then(resTx => {
t.truthy(resTx)
})
}) })

View File

@ -0,0 +1,35 @@
// Copyright BigchainDB GmbH and BigchainDB contributors
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
// Code is Apache-2.0 and docs are CC-BY-4.0
import test from 'ava'
import {
Connection
} from '../../src'
test('Pick connection with earliest backoff time', t => {
const path1 = 'http://localhost:9984/api/v1/'
const path2 = 'http://localhost:9984/api/wrong/'
const conn = new Connection([path1, path2])
conn.searchAssets('example')
const connection1 = conn.transport.connectionPool[0]
t.deepEqual(conn.transport.pickConnection(), connection1)
})
test('Pick connection with earliest backoff time', async t => {
const path1 = 'http://localhost:9984/api/v1/'
const path2 = 'http://localhost:9984/api/wrong/'
// Reverse order
const conn = new Connection([path2, path1])
await conn.searchAssets('example')
const connection1 = conn.transport.connectionPool[1]
t.deepEqual(conn.transport.pickConnection(), connection1)
})