diff --git a/packages/testkit-backend/src/context.js b/packages/testkit-backend/src/context.js index df1c80b1e..bbe8dc234 100644 --- a/packages/testkit-backend/src/context.js +++ b/packages/testkit-backend/src/context.js @@ -1,5 +1,5 @@ export default class Context { - constructor (shouldRunTest) { + constructor (shouldRunTest, getFeatures) { this._id = 0 this._drivers = {} this._sessions = {} @@ -7,6 +7,7 @@ export default class Context { this._resolverRequests = {} this._errors = {} this._shouldRunTest = shouldRunTest + this._getFeatures = getFeatures this._results = {} } @@ -101,6 +102,10 @@ export default class Context { return this._shouldRunTest } + getFeatures() { + return this._getFeatures() + } + _add (map, object) { this._id++ map[this._id] = object diff --git a/packages/testkit-backend/src/controller/local.js b/packages/testkit-backend/src/controller/local.js index a4faddaee..c5aa1aad6 100644 --- a/packages/testkit-backend/src/controller/local.js +++ b/packages/testkit-backend/src/controller/local.js @@ -10,15 +10,16 @@ import stringify from '../stringify' */ export default class LocalController extends Controller { - constructor(requestHandlers = {}, shouldRunTest = () => {}) { + constructor(requestHandlers = {}, shouldRunTest = () => {}, getFeatures = () => []) { super() this._requestHandlers = requestHandlers this._shouldRunTest = shouldRunTest + this._getFeatures = getFeatures this._contexts = new Map() } openContext (contextId) { - this._contexts.set(contextId, new Context(this._shouldRunTest)) + this._contexts.set(contextId, new Context(this._shouldRunTest, this._getFeatures)) } closeContext (contextId) { @@ -35,38 +36,45 @@ export default class LocalController extends Controller { } return await this._requestHandlers[name](this._contexts.get(contextId), data, { - writeResponse: (name, data) => this._writeResponse(contextId, name, data), + writeResponse: (response) => this._writeResponse(contextId, response), writeError: (e) => this._writeError(contextId, e), writeBackendError: (msg) => this._writeBackendError(contextId, msg) }) } - _writeResponse (contextId, name, data) { - console.log('> writing response', name, data) - let response = { - name: name, - data: data - } - + _writeResponse (contextId, response) { + console.log('> writing response', response.name, response.data) this.emit('response', { contextId, response }) } _writeBackendError (contextId, msg) { - this._writeResponse(contextId, 'BackendError', { msg: msg }) + this._writeResponse(contextId, newResponse('BackendError', { msg: msg })) } _writeError (contextId, e) { if (e.name) { const id = this._contexts.get(contextId).addError(e) - this._writeResponse(contextId, 'DriverError', { + this._writeResponse(contextId, newResponse('DriverError', { id, msg: e.message + ' (' + e.code + ')', code: e.code - }) + })) return } this._writeBackendError(contextId, e) } + + _msg (name, data) { + return { + name, data + } + } } + +function newResponse (name, data) { + return { + name, data + } +} diff --git a/packages/testkit-backend/src/feature/async.js b/packages/testkit-backend/src/feature/async.js new file mode 100644 index 000000000..510225864 --- /dev/null +++ b/packages/testkit-backend/src/feature/async.js @@ -0,0 +1,8 @@ +const features = [ + 'Feature:API:Result.List', + 'Feature:API:Result.Peek', + 'Optimization:EagerTransactionBegin', + 'Optimization:PullPipelining', +] + +export default features diff --git a/packages/testkit-backend/src/feature/common.js b/packages/testkit-backend/src/feature/common.js new file mode 100644 index 000000000..167f0f9ef --- /dev/null +++ b/packages/testkit-backend/src/feature/common.js @@ -0,0 +1,48 @@ +import tls from 'tls' + +const SUPPORTED_TLS = (() => { + if (tls.DEFAULT_MAX_VERSION) { + const min = Number(tls.DEFAULT_MIN_VERSION.split('TLSv')[1]) + const max = Number(tls.DEFAULT_MAX_VERSION.split('TLSv')[1]) + const result = []; + for (let version = min > 1 ? min : 1.1; version <= max; version = Number((version + 0.1).toFixed(1)) ) { + result.push(`Feature:TLS:${version.toFixed(1)}`) + } + return result; + } + return []; +})(); + +const features = [ + 'Feature:Auth:Custom', + 'Feature:Auth:Kerberos', + 'Feature:Auth:Bearer', + 'Feature:API:SSLConfig', + 'Feature:API:SSLSchemes', + 'AuthorizationExpiredTreatment', + 'ConfHint:connection.recv_timeout_seconds', + 'Feature:Impersonation', + 'Feature:Bolt:3.0', + 'Feature:Bolt:4.1', + 'Feature:Bolt:4.2', + 'Feature:Bolt:4.3', + 'Feature:Bolt:4.4', + 'Feature:API:Driver:GetServerInfo', + 'Feature:API:Driver.VerifyConnectivity', + 'Feature:API:Result.Peek', + 'Feature:Configuration:ConnectionAcquisitionTimeout', + 'Optimization:ImplicitDefaultArguments', + 'Temporary:ConnectionAcquisitionTimeout', + 'Temporary:CypherPathAndRelationship', + 'Temporary:DriverFetchSize', + 'Temporary:DriverMaxConnectionPoolSize', + 'Temporary:DriverMaxTxRetryTime', + 'Temporary:GetConnectionPoolMetrics', + 'Temporary:FastFailingDiscovery', + 'Temporary:FullSummary', + 'Temporary:ResultKeys', + 'Temporary:TransactionClose', + ...SUPPORTED_TLS +] + +export default features diff --git a/packages/testkit-backend/src/feature/index.js b/packages/testkit-backend/src/feature/index.js new file mode 100644 index 000000000..6f3e815bf --- /dev/null +++ b/packages/testkit-backend/src/feature/index.js @@ -0,0 +1,18 @@ +import commonFeatures from './common' +import rxFeatures from './rx' +import asyncFeatures from './async' + +const featuresByContext = new Map([ + ['async', asyncFeatures], + ['rx', rxFeatures], +]) + + +export function createGetFeatures (contexts) { + const features = contexts + .filter(context => featuresByContext.has(context)) + .map(context => featuresByContext.get(context)) + .reduce((previous, current) => [ ...previous, ...current ], commonFeatures) + + return () => features +} diff --git a/packages/testkit-backend/src/feature/rx.js b/packages/testkit-backend/src/feature/rx.js new file mode 100644 index 000000000..a5fd52593 --- /dev/null +++ b/packages/testkit-backend/src/feature/rx.js @@ -0,0 +1,6 @@ + +const features = [ + +] + +export default features diff --git a/packages/testkit-backend/src/index.js b/packages/testkit-backend/src/index.js index 0f2db7c53..33657b18c 100644 --- a/packages/testkit-backend/src/index.js +++ b/packages/testkit-backend/src/index.js @@ -2,7 +2,9 @@ import Backend from './backend' import { SocketChannel, WebSocketChannel } from './channel' import { LocalController, RemoteController } from './controller' import { getShouldRunTest } from './skipped-tests' -import * as REQUEST_HANDLERS from './request-handlers' +import { createGetFeatures } from './feature' +import * as REQUEST_HANDLERS from './request-handlers.js' +import * as RX_REQUEST_HANDLERS from './request-handlers-rx.js' /** * Responsible for configure and run the backend server. @@ -13,15 +15,17 @@ function main( ) { const backendPort = process.env.BACKEND_PORT || 9876 const webserverPort = process.env.WEB_SERVER_PORT || 8000 const driverDescriptor = process.env.DRIVER_DESCRIPTOR || '' + const sessionType = process.env.SESSION_TYPE || 'ASYNC' + const sessionTypeDescriptor = sessionType === 'RX' ? 'rx' : 'async' const driverDescriptorList = driverDescriptor .split(',').map(s => s.trim().toLowerCase()) - const shouldRunTest = getShouldRunTest(driverDescriptorList) + const shouldRunTest = getShouldRunTest([...driverDescriptorList, sessionTypeDescriptor]) + const getFeatures = createGetFeatures([sessionTypeDescriptor]) const newChannel = () => { if ( channelType.toUpperCase() === 'WEBSOCKET' ) { return new WebSocketChannel(new URL(`ws://localhost:${backendPort}`)) - } return new SocketChannel(backendPort) } @@ -30,7 +34,7 @@ function main( ) { if ( testEnviroment.toUpperCase() === 'REMOTE' ) { return new RemoteController(webserverPort) } - return new LocalController(REQUEST_HANDLERS, shouldRunTest) + return new LocalController(getRequestHandlers(sessionType), shouldRunTest, getFeatures) } const backend = new Backend(newController, newChannel) @@ -52,4 +56,11 @@ function main( ) { } } +function getRequestHandlers(sessionType) { + if (sessionType.toUpperCase() === 'RX') { + return RX_REQUEST_HANDLERS + } + return REQUEST_HANDLERS +} + main() diff --git a/packages/testkit-backend/src/request-handlers-rx.js b/packages/testkit-backend/src/request-handlers-rx.js new file mode 100644 index 000000000..0582d8df2 --- /dev/null +++ b/packages/testkit-backend/src/request-handlers-rx.js @@ -0,0 +1,317 @@ +import * as responses from './responses.js'; +import neo4j from './neo4j.js'; +import { + cypherToNative +} from './cypher-native-binders.js' +import { from } from 'rxjs'; + +// Handlers which didn't change depending +export { + NewDriver, + DriverClose, + SessionLastBookmarks, + StartTest, + GetFeatures, + VerifyConnectivity, + GetServerInfo, + CheckMultiDBSupport, + ResolverResolutionCompleted, + GetRoutingTable, + ForcedRoutingTableUpdate, + ResultNext, + RetryablePositive, + RetryableNegative, +} from './request-handlers.js'; + +export function NewSession(context, data, wire) { + let { driverId, accessMode, bookmarks, database, fetchSize, impersonatedUser } = data + switch (accessMode) { + case 'r': + accessMode = neo4j.session.READ + break + case 'w': + accessMode = neo4j.session.WRITE + break + default: + wire.writeBackendError('Unknown accessmode: ' + accessMode) + return + } + const driver = context.getDriver(driverId) + const session = driver.rxSession({ + defaultAccessMode: accessMode, + bookmarks, + database, + fetchSize, + impersonatedUser + }) + const id = context.addSession(session) + wire.writeResponse(responses.Session({ id })) +} + +export function SessionClose(context, data, wire) { + const { sessionId } = data + const session = context.getSession(sessionId) + return session + .close() + .toPromise() + .then(() => wire.writeResponse(responses.Session({ id: sessionId }))) + .catch(err => wire.writeError(err)) +} + +export function SessionRun(context, data, wire) { + const { sessionId, cypher, params, txMeta: metadata, timeout } = data + const session = context.getSession(sessionId) + if (params) { + for (const [key, value] of Object.entries(params)) { + params[key] = cypherToNative(value) + } + } + + let result + try { + result = session.run(cypher, params, { metadata, timeout }) + } catch (e) { + console.log('got some err: ' + JSON.stringify(e)) + wire.writeError(e) + return + } + + const it = toAsyncIterator(result) + result[Symbol.asyncIterator] = () => it + + let id = context.addResult(result) + + wire.writeResponse(responses.Result({ id })) +} + +export function ResultConsume(context, data, wire) { + const { resultId } = data + const result = context.getResult(resultId) + + return result.consume() + .toPromise() + .then(summary => { + wire.writeResponse(responses.Summary({ summary })) + }).catch(e => wire.writeError(e)) +} + + +export function SessionBeginTransaction(context, data, wire) { + const { sessionId, txMeta: metadata, timeout } = data + const session = context.getSession(sessionId) + + try { + return session.beginTransaction({ metadata, timeout }) + .toPromise() + .then(tx => { + const id = context.addTx(tx, sessionId) + wire.writeResponse(responses.Transaction({ id })) + }).catch(e => { + console.log('got some err: ' + JSON.stringify(e)) + wire.writeError(e) + }) + } catch (e) { + console.log('got some err: ' + JSON.stringify(e)) + wire.writeError(e) + return + } +} + +export function TransactionRun(context, data, wire) { + const { txId, cypher, params } = data + const tx = context.getTx(txId) + if (params) { + for (const [key, value] of Object.entries(params)) { + params[key] = cypherToNative(value) + } + } + const result = tx.tx.run(cypher, params) + + const it = toAsyncIterator(result) + result[Symbol.asyncIterator] = () => it + + const id = context.addResult(result) + + wire.writeResponse(responses.Result({ id })) +} + +export function TransactionRollback(context, data, wire) { + const { txId: id } = data + const { tx } = context.getTx(id) + return tx.rollback() + .toPromise() + .then(() => wire.writeResponse(responses.Transaction({ id }))) + .catch(e => { + console.log('got some err: ' + JSON.stringify(e)) + wire.writeError(e) + }) +} + +export function TransactionCommit(context, data, wire) { + const { txId: id } = data + const { tx } = context.getTx(id) + return tx.commit() + .toPromise() + .then(() => wire.writeResponse(responses.Transaction({ id }))) + .catch(e => { + console.log('got some err: ' + JSON.stringify(e)) + wire.writeError(e) + }) +} + +export function TransactionClose(context, data, wire) { + const { txId: id } = data + const { tx } = context.getTx(id) + return tx.close() + .toPromise() + .then(() => wire.writeResponse(responses.Transaction({ id }))) + .catch(e => wire.writeError(e)) +} + +export function SessionReadTransaction(context, data, wire) { + const { sessionId, txMeta: metadata } = data + const session = context.getSession(sessionId) + + try { + return session.readTransaction(tx => { + return from(new Promise((resolve, reject) => { + const id = context.addTx(tx, sessionId, resolve, reject) + wire.writeResponse(responses.RetryableTry({ id })) + })) + }, { metadata }) + .toPromise() + .then(() => wire.writeResponse(responses.RetryableDone())) + .catch(e => wire.writeError(e)) + } catch (e) { + wire.writeError(e) + return + } +} + +export function SessionWriteTransaction(context, data, wire) { + const { sessionId, txMeta: metadata } = data + const session = context.getSession(sessionId) + + try { + return session.writeTransaction(tx => { + return from(new Promise((resolve, reject) => { + const id = context.addTx(tx, sessionId, resolve, reject) + wire.writeResponse(responses.RetryableTry({ id })) + })) + }, { metadata }) + .toPromise() + .then(() => wire.writeResponse(responses.RetryableDone())) + .catch(e => wire.writeError(e)) + } catch (e) { + wire.writeError(e) + return + } +} + + +function toAsyncIterator(result) { + function queueObserver() { + function createResolvablePromise() { + const resolvablePromise = {} + resolvablePromise.promise = new Promise((resolve, reject) => { + resolvablePromise.resolve = resolve + resolvablePromise.reject = reject + }); + return resolvablePromise; + } + + + function isError(elementOrError) { + return elementOrError instanceof Error + } + + const buffer = [] + const promiseHolder = { resolvable: null } + + const observer = { + next: (record) => { + observer._push({ done: false, value: record }) + }, + complete: (summary) => { + observer._push({ done: true, value: summary }) + }, + error: (error) => { + observer._push(error) + }, + _push(element) { + if (promiseHolder.resolvable !== null) { + const resolvable = promiseHolder.resolvable + promiseHolder.resolvable = null + if (isError(element)) { + resolvable.reject(element) + } else { + resolvable.resolve(element) + } + } else { + buffer.push(element) + } + }, + dequeue: async () => { + if (buffer.length > 0) { + const element = buffer.shift() + if (isError(element)) { + throw element + } + return element + } + promiseHolder.resolvable = createResolvablePromise() + return await promiseHolder.resolvable.promise + }, + head: async () => { + if (buffer.length > 0) { + const element = buffer[0] + if (isError(element)) { + throw element + } + return element + } + promiseHolder.resolvable = createResolvablePromise() + try { + const element = await promiseHolder.resolvable.promise + buffer.unshift(element) + return element + } catch (error) { + buffer.unshift(error) + throw error + } + }, + get size() { + return buffer.length + } + } + + return observer + } + const records = result.records() + + const observer = queueObserver() + records.subscribe(observer); + + const state = { + finished: false + } + + return { + next: async () => { + if (state.finished) { + return { done: true } + } + const next = await observer.dequeue() + if (next.done) { + state.finished = next.done + state.summary = next.value + } + return next + }, + return: async (value) => { + state.finished = true + state.summary = value + return { done: true, value: value } + } + } +} diff --git a/packages/testkit-backend/src/request-handlers.js b/packages/testkit-backend/src/request-handlers.js index 2611312d7..01e8b420b 100644 --- a/packages/testkit-backend/src/request-handlers.js +++ b/packages/testkit-backend/src/request-handlers.js @@ -1,25 +1,8 @@ import neo4j from './neo4j' import { - cypherToNative, - nativeToCypher, + cypherToNative } from './cypher-native-binders.js' -import { - nativeToTestkitSummary, -} from './summary-binder.js' -import tls from 'tls' - -const SUPPORTED_TLS = (() => { - if (tls.DEFAULT_MAX_VERSION) { - const min = Number(tls.DEFAULT_MIN_VERSION.split('TLSv')[1]) - const max = Number(tls.DEFAULT_MAX_VERSION.split('TLSv')[1]) - const result = []; - for (let version = min > 1 ? min : 1.1; version <= max; version = Number((version + 0.1).toFixed(1)) ) { - result.push(`Feature:TLS:${version.toFixed(1)}`) - } - return result; - } - return []; -})(); +import * as responses from './responses.js' export function NewDriver (context, data, wire) { const { @@ -55,7 +38,7 @@ export function NewDriver (context, data, wire) { ? address => new Promise((resolve, reject) => { const id = context.addResolverRequest(resolve, reject) - wire.writeResponse('ResolverResolutionRequired', { id, address }) + wire.writeResponse(responses.ResolverResolutionRequired({ id, address })) }) : undefined const config = { @@ -102,7 +85,7 @@ export function NewDriver (context, data, wire) { return } const id = context.addDriver(driver) - wire.writeResponse('Driver', { id }) + wire.writeResponse(responses.Driver({ id })) } export function DriverClose (context, data, wire) { @@ -111,7 +94,7 @@ export function DriverClose (context, data, wire) { return driver .close() .then(() => { - wire.writeResponse('Driver', { id: driverId }) + wire.writeResponse(responses.Driver({ id: driverId })) }) .catch(err => wire.writeError(err)) .finally(() => context.removeDriver(driverId)) @@ -139,7 +122,7 @@ export function NewSession (context, data, wire) { impersonatedUser }) const id = context.addSession(session) - wire.writeResponse('Session', { id }) + wire.writeResponse(responses.Session({ id })) } export function SessionClose (context, data, wire) { @@ -148,7 +131,7 @@ export function SessionClose (context, data, wire) { return session .close() .then(() => { - wire.writeResponse('Session', { id: sessionId }) + wire.writeResponse(responses.Session({ id: sessionId })) }) .catch(err => wire.writeError(err)) } @@ -173,7 +156,7 @@ export function SessionRun (context, data, wire) { let id = context.addResult(result) - wire.writeResponse('Result', { id }) + wire.writeResponse(responses.Result({ id })) } export function ResultNext (context, data, wire) { @@ -184,12 +167,9 @@ export function ResultNext (context, data, wire) { } return result.recordIt.next().then(({ value, done }) => { if (done) { - wire.writeResponse('NullRecord', null) + wire.writeResponse(responses.NullRecord()) } else { - const values = Array.from(value.values()).map(nativeToCypher) - wire.writeResponse('Record', { - values: values - }) + wire.writeResponse(responses.Record({ record: value })) } }).catch(e => { console.log('got some err: ' + JSON.stringify(e)) @@ -205,12 +185,9 @@ export function ResultPeek (context, data, wire) { } return result.recordIt.peek().then(({ value, done }) => { if (done) { - wire.writeResponse('NullRecord', null) + wire.writeResponse(responses.NullRecord()) } else { - const values = Array.from(value.values()).map(nativeToCypher) - wire.writeResponse('Record', { - values: values - }) + wire.writeResponse(responses.Record({ record: value })) } }).catch(e => { console.log('got some err: ' + JSON.stringify(e)) @@ -219,13 +196,11 @@ export function ResultPeek (context, data, wire) { } export function ResultConsume (context, data, wire) { - - const { resultId } = data const result = context.getResult(resultId) return result.summary().then(summary => { - wire.writeResponse('Summary', nativeToTestkitSummary(summary)) + wire.writeResponse(responses.Summary({ summary })) }).catch(e => wire.writeError(e)) } @@ -236,10 +211,7 @@ export function ResultList (context, data, wire) { return result .then(({ records }) => { - const cypherRecords = records.map(rec => { - return { values: Array.from(rec.values()).map(nativeToCypher) } - }) - wire.writeResponse('RecordList', { records: cypherRecords}) + wire.writeResponse(responses.RecordList({ records })) }) .catch(error => wire.writeError(error)) } @@ -252,10 +224,10 @@ export function SessionReadTransaction (context, data, wire) { tx => new Promise((resolve, reject) => { const id = context.addTx(tx, sessionId, resolve, reject) - wire.writeResponse('RetryableTry', { id }) + wire.writeResponse(responses.RetryableTry({ id })) }) , { metadata }) - .then(_ => wire.writeResponse('RetryableDone', null)) + .then(_ => wire.writeResponse(responses.RetryableDone())) .catch(error => wire.writeError(error)) } @@ -270,7 +242,7 @@ export function TransactionRun (context, data, wire) { const result = tx.tx.run(cypher, params) const id = context.addResult(result) - wire.writeResponse('Result', { id }) + wire.writeResponse(responses.Result({ id })) } export function RetryablePositive (context, data, wire) { @@ -296,7 +268,7 @@ export function SessionBeginTransaction (context, data, wire) { return session.beginTransaction({ metadata, timeout }) .then(tx => { const id = context.addTx(tx, sessionId) - wire.writeResponse('Transaction', { id }) + wire.writeResponse(responses.Transaction({ id })) }).catch(e => { console.log('got some err: ' + JSON.stringify(e)) wire.writeError(e) @@ -312,7 +284,7 @@ export function TransactionCommit (context, data, wire) { const { txId: id } = data const { tx } = context.getTx(id) return tx.commit() - .then(() => wire.writeResponse('Transaction', { id })) + .then(() => wire.writeResponse(responses.Transaction({ id }))) .catch(e => { console.log('got some err: ' + JSON.stringify(e)) wire.writeError(e) @@ -323,7 +295,7 @@ export function TransactionRollback (context, data, wire) { const { txId: id } = data const { tx } = context.getTx(id) return tx.rollback() - .then(() => wire.writeResponse('Transaction', { id })) + .then(() => wire.writeResponse(responses.Transaction({ id }))) .catch(e => wire.writeError(e)) } @@ -331,7 +303,7 @@ export function TransactionClose (context, data, wire) { const { txId: id } = data const { tx } = context.getTx(id) return tx.close() - .then(() => wire.writeResponse('Transaction', { id })) + .then(() => wire.writeResponse(responses.Transaction({ id }))) .catch(e => wire.writeError(e)) } @@ -339,7 +311,7 @@ export function SessionLastBookmarks (context, data, wire) { const { sessionId } = data const session = context.getSession(sessionId) const bookmarks = session.lastBookmarks() - wire.writeResponse('Bookmarks', { bookmarks }) + wire.writeResponse(responses.Bookmarks({ bookmarks })) } export function SessionWriteTransaction (context, data, wire) { @@ -350,65 +322,32 @@ export function SessionWriteTransaction (context, data, wire) { tx => new Promise((resolve, reject) => { const id = context.addTx(tx, sessionId, resolve, reject) - wire.writeResponse('RetryableTry', { id }) + wire.writeResponse(responses.RetryableTry({ id })) }) , { metadata }) - .then(_ => wire.writeResponse('RetryableDone', null)) + .then(_ => wire.writeResponse(responses.RetryableDone())) .catch(error => wire.writeError(error)) } export function StartTest (context, { testName }, wire) { const shouldRunTest = context.getShouldRunTestFunction() shouldRunTest(testName, { - onRun: () => wire.writeResponse('RunTest', null), - onSkip: reason => wire.writeResponse('SkipTest', { reason }) + onRun: () => wire.writeResponse(responses.RunTest()), + onSkip: reason => wire.writeResponse(responses.SkipTest({ reason })) }) } -export function GetFeatures (_context, _params, wire) { - wire.writeResponse('FeatureList', { - features: [ - 'Feature:Auth:Custom', - 'Feature:Auth:Kerberos', - 'Feature:Auth:Bearer', - 'Feature:API:SSLConfig', - 'Feature:API:SSLSchemes', - 'AuthorizationExpiredTreatment', - 'ConfHint:connection.recv_timeout_seconds', - 'Feature:Impersonation', - 'Feature:Bolt:3.0', - 'Feature:Bolt:4.1', - 'Feature:Bolt:4.2', - 'Feature:Bolt:4.3', - 'Feature:Bolt:4.4', - 'Feature:API:Driver:GetServerInfo', - 'Feature:API:Driver.VerifyConnectivity', - 'Feature:API:Result.List', - 'Feature:API:Result.Peek', - 'Feature:Configuration:ConnectionAcquisitionTimeout', - 'Optimization:EagerTransactionBegin', - 'Optimization:ImplicitDefaultArguments', - 'Optimization:PullPipelining', - 'Temporary:ConnectionAcquisitionTimeout', - 'Temporary:CypherPathAndRelationship', - 'Temporary:DriverFetchSize', - 'Temporary:DriverMaxConnectionPoolSize', - 'Temporary:DriverMaxTxRetryTime', - 'Temporary:GetConnectionPoolMetrics', - 'Temporary:FastFailingDiscovery', - 'Temporary:FullSummary', - 'Temporary:ResultKeys', - 'Temporary:TransactionClose', - ...SUPPORTED_TLS - ] - }) +export function GetFeatures (context, _params, wire) { + wire.writeResponse(responses.FeatureList({ + features: context.getFeatures() + })) } export function VerifyConnectivity (context, { driverId }, wire) { const driver = context.getDriver(driverId) return driver .verifyConnectivity() - .then(() => wire.writeResponse('Driver', { id: driverId })) + .then(() => wire.writeResponse(responses.Driver({ id: driverId }))) .catch(error => wire.writeError(error)) } @@ -416,10 +355,7 @@ export function GetServerInfo (context, { driverId }, wire) { const driver = context.getDriver(driverId) return driver .getServerInfo() - .then(info => wire.writeResponse('ServerInfo', { - ...info, - protocolVersion: info.protocolVersion.toFixed(1) - })) + .then(serverInfo => wire.writeResponse(responses.ServerInfo({ serverInfo }))) .catch(error => wire.writeError(error)) } @@ -428,7 +364,7 @@ export function CheckMultiDBSupport (context, { driverId }, wire) { return driver .supportsMultiDb() .then(available => - wire.writeResponse('MultiDBSupport', { id: driverId, available }) + wire.writeResponse(responses.MultiDBSupport({ id: driverId, available })) ) .catch(error => wire.writeError(error)) } @@ -443,7 +379,6 @@ export function ResolverResolutionCompleted ( } export function GetRoutingTable (context, { driverId, database }, wire) { - const serverAddressToString = serverAddress => serverAddress.asHostPort() const driver = context.getDriver(driverId) const routingTable = driver && @@ -460,13 +395,7 @@ export function GetRoutingTable (context, { driverId, database }, wire) { }) if (routingTable) { - wire.writeResponse('RoutingTable', { - database: routingTable.database, - ttl: Number(routingTable.ttl), - readers: routingTable.readers.map(serverAddressToString), - writers: routingTable.writers.map(serverAddressToString), - routers: routingTable.routers.map(serverAddressToString) - }) + wire.writeResponse(responses.RoutingTable({ routingTable })) } else { wire.writeError('Driver does not support routing') } @@ -485,7 +414,7 @@ export function ForcedRoutingTableUpdate (context, { driverId, database, bookmar bookmarks: bookmarks, onDatabaseNameResolved: () => {} }) - .then(() => wire.writeResponse("Driver", { "id": driverId })) + .then(() => wire.writeResponse(responses.Driver({ "id": driverId }))) .catch(error => wire.writeError(error)) } else { wire.writeError('Driver does not support routing') diff --git a/packages/testkit-backend/src/responses.js b/packages/testkit-backend/src/responses.js new file mode 100644 index 000000000..9ae1ec5f9 --- /dev/null +++ b/packages/testkit-backend/src/responses.js @@ -0,0 +1,98 @@ +import { + nativeToCypher +} from './cypher-native-binders.js' + +import { + nativeToTestkitSummary +} from './summary-binder.js' + +export function Driver ({ id }) { + return response('Driver', { id }) +} + +export function ResolverResolutionRequired ({ id, address }) { + return response('ResolverResolutionRequired', { id, address }) +} + +export function Session ({ id }) { + return response('Session', { id }) +} + +export function Transaction ({ id }) { + return response('Transaction', { id }) +} + +export function RetryableTry ({ id }) { + return response('RetryableTry', { id }) +} + +export function RetryableDone () { + return response('RetryableDone', null) +} + +export function Result ({ id }) { + return response('Result', { id }) +} + +export function NullRecord () { + return response('NullRecord', null) +} + +export function Record ({ record }) { + const values = Array.from(record.values()).map(nativeToCypher) + return response('Record', { values }) +} + +export function RecordList ({ records }) { + const cypherRecords = records.map(rec => { + return { values: Array.from(rec.values()).map(nativeToCypher) } + }) + return response('RecordList', { records: cypherRecords }) +} + +export function Summary ({ summary }) { + return response('Summary', nativeToTestkitSummary(summary)) +} + +export function Bookmarks ({ bookmarks }) { + return response('Bookmarks', { bookmarks }) +} + +export function ServerInfo ({ serverInfo }) { + return response('ServerInfo', { + ...serverInfo, + protocolVersion: serverInfo.protocolVersion.toFixed(1) + }) +} + +export function MultiDBSupport ({ id, available }) { + return response('MultiDBSupport', { id, available }) +} + +export function RoutingTable ({ routingTable }) { + const serverAddressToString = serverAddress => serverAddress.asHostPort() + return response('RoutingTable', { + database: routingTable.database, + ttl: Number(routingTable.ttl), + readers: routingTable.readers.map(serverAddressToString), + writers: routingTable.writers.map(serverAddressToString), + routers: routingTable.routers.map(serverAddressToString) + }) +} + +// Testkit controller messages +export function RunTest () { + return response('RunTest', null) +} + +export function SkipTest ({ reason }) { + return response('SkipTest', { reason }) +} + +export function FeatureList ({ features }) { + return response('FeatureList', { features }) +} + +function response(name, data) { + return { name, data } +} diff --git a/packages/testkit-backend/src/skipped-tests/index.js b/packages/testkit-backend/src/skipped-tests/index.js index 946f114ba..4f135b291 100644 --- a/packages/testkit-backend/src/skipped-tests/index.js +++ b/packages/testkit-backend/src/skipped-tests/index.js @@ -1,8 +1,10 @@ import commonSkippedTests from './common' import browserSkippedTests from './browser' +import rxSessionSkippedTests from './rx' const skippedTestsByContext = new Map([ - ['browser', browserSkippedTests] + ['browser', browserSkippedTests], + ['rx', rxSessionSkippedTests], ]) export function getShouldRunTest (contexts) { diff --git a/packages/testkit-backend/src/skipped-tests/rx.js b/packages/testkit-backend/src/skipped-tests/rx.js new file mode 100644 index 000000000..0d0be9514 --- /dev/null +++ b/packages/testkit-backend/src/skipped-tests/rx.js @@ -0,0 +1,10 @@ +import { skip, ifEquals } from './skip' + +const skippedTests = [ + skip( + 'Throws after run insted of the first next because of the backend implementation', + ifEquals('stub.disconnects.test_disconnects.TestDisconnects.test_disconnect_on_tx_begin') + ) +] + +export default skippedTests