Skip to content

Commit 6763bc8

Browse files
committed
Add Testkit support to DenoJS
Enabling the support for the acceptance tests to the generated DenoJS driver is a step towards to release it as library.
1 parent 565310f commit 6763bc8

File tree

12 files changed

+191
-48
lines changed

12 files changed

+191
-48
lines changed

packages/bolt-connection/src/channel/browser/browser-channel.js

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -144,17 +144,26 @@ export default class WebSocketChannel {
144144
if (this._pending !== null) {
145145
this._pending.push(buffer)
146146
} else if (buffer instanceof ChannelBuffer) {
147-
try {
148-
this._ws.send(buffer._buffer)
149-
} catch (error) {
150-
if (this._ws.readyState !== WS_OPEN) {
151-
// Websocket has been closed
152-
this._handleConnectionError()
153-
} else {
154-
// Some other error occured
155-
throw error
147+
// We should wait for the connection state change before sending
148+
setTimeout(() => {
149+
try {
150+
if (this._ws.readyState !== WS_OPEN){
151+
console.log('WebSocket not open')
152+
return;
153+
}
154+
this._ws.send(buffer._buffer)
155+
} catch (error) {
156+
if (this._ws.readyState !== WS_OPEN) {
157+
// Websocket has been closed
158+
this._handleConnectionError()
159+
} else {
160+
console.log('errror', error)
161+
// Some other error occured
162+
throw error
163+
}
156164
}
157-
}
165+
}, 500)
166+
console.log('after timeout')
158167
} else {
159168
throw newError("Don't know how to send buffer: " + buffer)
160169
}
@@ -166,14 +175,17 @@ export default class WebSocketChannel {
166175
*/
167176
close () {
168177
return new Promise((resolve, reject) => {
169-
if (this._ws && this._ws.readyState !== WS_CLOSED) {
170-
this._open = false
171-
this._clearConnectionTimeout()
172-
this._ws.onclose = () => resolve()
173-
this._ws.close()
174-
} else {
175-
resolve()
176-
}
178+
setTimeout(() => {
179+
if (this._ws && this._ws.readyState !== WS_CLOSED && this._ws.readyState !== WS_CLOSING) {
180+
this._open = false
181+
this._clearConnectionTimeout()
182+
this._ws.onclose = () => resolve()
183+
this._ws.close()
184+
} else {
185+
resolve()
186+
}
187+
}, 500)
188+
177189
})
178190
}
179191

packages/testkit-backend/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
},
1111
"type": "module",
1212
"scripts": {
13-
"build": "rollup src/index.js --config rollup.config.js",
14-
"start": "node --version | grep -q v10. && node -r esm src/index.js || node --experimental-specifier-resolution=node src/index.js",
13+
"build": "echo 'Error: no build script specified'",
14+
"build::": "rollup src/index.js --config rollup.config.js",
15+
"start::": "node --version | grep -q v10. && node -r esm src/index.js || node --experimental-specifier-resolution=node src/index.js",
16+
"start": "deno run --allow-read --allow-write --allow-net --allow-env --allow-run src/index.deno.ts",
1517
"clean": "rm -fr node_modules public/index.js",
1618
"prepare": "npm run build",
1719
"node": "node"

packages/testkit-backend/src/cypher-native-binders.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import neo4j from './neo4j'
1+
import neo4j from './neo4j.deno.js'
22

33
export function valueResponse (name, value) {
44
return { name: name, data: { value: value } }

packages/testkit-backend/src/feature/common.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
import tls from 'tls'
1+
22

33
const SUPPORTED_TLS = (() => {
4+
const tls = {
5+
DEFAULT_MAX_VERSION: false
6+
}
47
if (tls.DEFAULT_MAX_VERSION) {
58
const min = Number(tls.DEFAULT_MIN_VERSION.split('TLSv')[1])
69
const max = Number(tls.DEFAULT_MAX_VERSION.split('TLSv')[1])

packages/testkit-backend/src/feature/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import commonFeatures from './common'
2-
import rxFeatures from './rx'
3-
import asyncFeatures from './async'
1+
import commonFeatures from './common.js'
2+
import rxFeatures from './rx.js'
3+
import asyncFeatures from './async.js'
44

55
const featuresByContext = new Map([
66
['async', asyncFeatures],
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import Context from './context.js';
2+
import { getShouldRunTest } from './skipped-tests/index.js';
3+
import neo4j from "../../neo4j-driver-deno/lib/mod.ts";
4+
import { createGetFeatures } from './feature/index.js';
5+
import * as handlers from './request-handlers.js';
6+
7+
const listener = Deno.listen({ port: 9876 });
8+
let index = 0;
9+
const contexts = new Map<number, Context>();
10+
11+
interface RequestHandler {
12+
(c: Context, data: any, wire: any): void
13+
}
14+
15+
interface RequestHandlerMap {
16+
[key: string]: RequestHandler
17+
}
18+
19+
addEventListener('uncaughtException', (event) => {
20+
console.log('unhandled rejection', event);
21+
})
22+
23+
// @ts-ignore
24+
const requestHandlers: RequestHandlerMap = handlers as RequestHandlerMap
25+
26+
addEventListener('events.errorMonitor', (event) => {
27+
console.log('something here ========================')
28+
})
29+
30+
interface Backend {
31+
openContext(contextId: number): void
32+
closeContext(contextId: number): void
33+
handle(contextId: number, conn: Deno.Conn, request: { name: string, data: object }): void
34+
}
35+
36+
function write(conn: Deno.Conn, response: object) {
37+
const responseStr = JSON.stringify(response, (_, value) =>
38+
typeof value === 'bigint' ? `${value}n` : value
39+
)
40+
const responseArr = ['#response begin', responseStr, '#response end'].join('\n') + '\n'
41+
console.log('response', responseArr);
42+
conn.write(new TextEncoder().encode(responseArr))
43+
.catch(e => {
44+
console.log('error writing to connection', e);
45+
});
46+
}
47+
48+
const descriptor = [] as string[]
49+
const shouldRunTest = getShouldRunTest(descriptor);
50+
const getFeatures = createGetFeatures(descriptor);
51+
52+
const backend: Backend = {
53+
openContext: (contextId) => {
54+
console.log("Open context:", contextId);
55+
contexts.set(contextId, new Context(shouldRunTest, getFeatures));
56+
},
57+
closeContext: (contextId) => {
58+
console.log('Close context', contextId);
59+
contexts.delete(contextId);
60+
},
61+
handle: (contextId, conn, req) => {
62+
const { data, name } = req;
63+
if (!contexts.has(contextId)) {
64+
throw new Error(`Context ${contextId} does not exist`)
65+
} else if (!(name in requestHandlers)) {
66+
console.log('Unknown request: ' + name)
67+
throw new Error(`Unknown request: ${name}`)
68+
}
69+
70+
console.log('Handle', req.name, req.data);
71+
72+
const handler: (c: Context, data: any, wire: any) => void = requestHandlers[name as string];
73+
74+
handler(contexts.get(contextId)!!, data, {
75+
writeResponse: (response: object) => write(conn, response),
76+
writeError: (e: Error) => {
77+
console.log('writeError', e);
78+
if (e.name && e instanceof neo4j.Neo4jError) {
79+
if (contexts.has(contextId)) {
80+
const id = contexts.get(contextId)!!.addError(e)
81+
write(conn, { name: 'DriverError', data: { id, msg: e.message + ' (' + e.code + ')', code: e.code } })
82+
return
83+
} else {
84+
console.log('Context does not exist', contextId, e)
85+
}
86+
return
87+
}
88+
write(conn, { name: 'BackendError', data: { msg: e.message } })
89+
90+
},
91+
writeBackendError: (msg: string) => write(conn, { name: 'BackendError', data: { msg } })
92+
});
93+
}
94+
}
95+
96+
for await (const conn of listener) {
97+
const contextId = index++;
98+
handleConnection(conn, contextId)
99+
}
100+
101+
async function handleConnection( conn: Deno.Conn, contextId: number): Promise<void> {
102+
backend.openContext(contextId);
103+
let inRequest = false
104+
let requestString = ''
105+
for await (const message of Deno.iter(conn)) {
106+
const rawTxtMessage = new TextDecoder().decode(message);
107+
const lines = rawTxtMessage.split('\n');
108+
for (const line of lines) {
109+
switch (line) {
110+
case '#request begin':
111+
if(inRequest) {
112+
throw new Error('Already in request');
113+
}
114+
inRequest = true;
115+
break;
116+
case '#request end':
117+
if(!inRequest) {
118+
throw new Error('Not in request');
119+
}
120+
const request = JSON.parse(requestString);
121+
backend.handle(contextId, conn, request);
122+
inRequest = false;
123+
requestString = '';
124+
break
125+
case '':
126+
// ignore empty lines
127+
break;
128+
default:
129+
if(!inRequest) {
130+
throw new Error('Not in request');
131+
}
132+
requestString += line;
133+
break
134+
}
135+
}
136+
}
137+
backend.closeContext(contextId);
138+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import neo4j from '../../neo4j-driver-deno/lib/mod.ts'
2+
3+
export default neo4j

packages/testkit-backend/src/request-handlers.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import neo4j from './neo4j'
1+
import neo4j from './neo4j.deno.js'
22
import { cypherToNative } from './cypher-native-binders.js'
33
import * as responses from './responses.js'
44

@@ -52,7 +52,7 @@ export function NewDriver (context, data, wire) {
5252
userAgent,
5353
resolver,
5454
useBigInt: true,
55-
logging: neo4j.logging.console(process.env.LOG_LEVEL || context.logLevel)
55+
logging: neo4j.logging.console('debug')
5656
}
5757
if ('encrypted' in data) {
5858
config.encrypted = data.encrypted ? 'ENCRYPTION_ON' : 'ENCRYPTION_OFF'

packages/testkit-backend/src/skipped-tests/browser.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import skip, { ifEndsWith, ifEquals, ifStartsWith } from './skip'
1+
import skip, { ifEndsWith, ifEquals, ifStartsWith } from './skip.js'
22
const skippedTests = [
33
skip(
44
"Browser doesn't support socket timeouts",

packages/testkit-backend/src/skipped-tests/common.js

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,6 @@
1-
import skip, { ifEquals, ifEndsWith, endsWith, ifStartsWith, startsWith, not } from './skip'
1+
import skip, { ifEquals, ifEndsWith } from './skip'
22

33
const skippedTests = [
4-
skip(
5-
'Driver does not return offset for old DateTime implementations',
6-
ifStartsWith('stub.types.test_temporal_types.TestTemporalTypes')
7-
.and(not(startsWith('stub.types.test_temporal_types.TestTemporalTypesV5')))
8-
.and(endsWith('test_zoned_date_time')),
9-
ifEquals('neo4j.datatypes.test_temporal_types.TestDataTypes.test_nested_datetime'),
10-
ifEquals('neo4j.datatypes.test_temporal_types.TestDataTypes.test_should_echo_all_timezone_ids'),
11-
ifEquals('neo4j.datatypes.test_temporal_types.TestDataTypes.test_cypher_created_datetime')
12-
),
13-
skip(
14-
'Using numbers out of bound',
15-
ifEquals('neo4j.datatypes.test_temporal_types.TestDataTypes.test_should_echo_temporal_type'),
16-
ifEquals('neo4j.datatypes.test_temporal_types.TestDataTypes.test_nested_duration'),
17-
ifEquals('neo4j.datatypes.test_temporal_types.TestDataTypes.test_duration_components')
18-
),
194
skip(
205
'Testkit implemenation is deprecated',
216
ifEquals('stub.basic_query.test_basic_query.TestBasicQuery.test_5x0_populates_node_only_element_id'),

packages/testkit-backend/src/skipped-tests/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import commonSkippedTests from './common'
2-
import browserSkippedTests from './browser'
3-
import rxSessionSkippedTests from './rx'
1+
import commonSkippedTests from './common.js'
2+
import browserSkippedTests from './browser.js'
3+
import rxSessionSkippedTests from './rx.js'
44

55
const skippedTestsByContext = new Map([
66
['browser', browserSkippedTests],

packages/testkit-backend/src/skipped-tests/rx.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { skip, ifEquals } from './skip'
1+
import { skip, ifEquals } from './skip.js'
22

33
const skippedTests = [
44
skip(

0 commit comments

Comments
 (0)