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:
parent
7d978286f5
commit
ad8a889ecc
@ -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",
|
||||||
|
@ -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,
|
||||||
|
@ -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'
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
35
test/transport/test_transport.js
Normal file
35
test/transport/test_transport.js
Normal 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)
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user