Skip to content

Commit 85c21bc

Browse files
committed
ci(contract-test-utils): consolidating test websocket implementation
1 parent e254f77 commit 85c21bc

4 files changed

Lines changed: 63 additions & 124 deletions

File tree

Lines changed: 20 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,22 @@
1-
import { LDLogger } from '@launchdarkly/js-client-sdk';
2-
import { makeLogger } from '@launchdarkly/js-contract-test-utils/client';
3-
4-
import { ClientEntity, newSdkClientEntity } from './ClientEntity';
5-
6-
export default class TestHarnessWebSocket {
7-
private _ws?: WebSocket;
8-
private readonly _entities: Record<string, ClientEntity> = {};
9-
private _clientCounter = 0;
10-
private _logger: LDLogger = makeLogger('TestHarnessWebSocket');
11-
12-
constructor(private readonly _url: string) {}
13-
14-
connect() {
15-
this._logger.info(`Connecting to web socket.`);
16-
this._ws = new WebSocket(this._url, ['v1']);
17-
this._ws.onopen = () => {
18-
this._logger.info('Connected to websocket.');
19-
};
20-
this._ws.onclose = () => {
21-
this._logger.info('Websocket closed. Attempting to reconnect in 1 second.');
22-
setTimeout(() => {
23-
this.connect();
24-
}, 1000);
25-
};
26-
this._ws.onerror = (err) => {
27-
this._logger.info(`error:`, err);
28-
};
29-
30-
this._ws.onmessage = async (msg) => {
31-
this._logger.info('Test harness message', msg);
32-
const data = JSON.parse(msg.data);
33-
const resData: any = { reqId: data.reqId };
34-
switch (data.command) {
35-
case 'getCapabilities':
36-
resData.capabilities = [
37-
'client-side',
38-
'service-endpoints',
39-
'tags',
40-
'user-type',
41-
'inline-context-all',
42-
'anonymous-redaction',
43-
'strongly-typed',
44-
'client-prereq-events',
45-
'client-per-context-summaries',
46-
'track-hooks',
47-
];
48-
49-
break;
50-
case 'createClient':
51-
{
52-
resData.resourceUrl = `/clients/${this._clientCounter}`;
53-
resData.status = 201;
54-
const entity = await newSdkClientEntity(data.body);
55-
this._entities[this._clientCounter] = entity;
56-
this._clientCounter += 1;
57-
}
58-
break;
59-
case 'runCommand':
60-
if (Object.prototype.hasOwnProperty.call(this._entities, data.id)) {
61-
const entity = this._entities[data.id];
62-
const body = await entity.doCommand(data.body);
63-
resData.body = body;
64-
resData.status = body ? 200 : 204;
65-
} else {
66-
resData.status = 404;
67-
this._logger.warn(`Client did not exist: ${data.id}`);
68-
}
69-
70-
break;
71-
case 'deleteClient':
72-
if (Object.prototype.hasOwnProperty.call(this._entities, data.id)) {
73-
const entity = this._entities[data.id];
74-
entity.close();
75-
delete this._entities[data.id];
76-
} else {
77-
resData.status = 404;
78-
this._logger.warn(`Could not delete client because it did not exist: ${data.id}`);
79-
}
80-
break;
81-
default:
82-
break;
83-
}
84-
85-
this.send(resData);
86-
};
87-
}
88-
89-
disconnect() {
90-
this._ws?.close();
91-
}
92-
93-
send(data: unknown) {
94-
this._ws?.send(JSON.stringify(data));
1+
import { TestHarnessWebSocket as BaseTestHarnessWebSocket } from '@launchdarkly/js-contract-test-utils/client';
2+
3+
import { newSdkClientEntity } from './ClientEntity';
4+
5+
const CAPABILITIES = [
6+
'client-side',
7+
'service-endpoints',
8+
'tags',
9+
'user-type',
10+
'inline-context-all',
11+
'anonymous-redaction',
12+
'strongly-typed',
13+
'client-prereq-events',
14+
'client-per-context-summaries',
15+
'track-hooks',
16+
];
17+
18+
export default class TestHarnessWebSocket extends BaseTestHarnessWebSocket {
19+
constructor(url: string) {
20+
super(url, CAPABILITIES, newSdkClientEntity);
9521
}
9622
}

packages/sdk/react-native/contract-tests/entity/App.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,23 @@
11
import React, { useEffect, useState } from 'react';
22
import { StyleSheet, Text, View } from 'react-native';
33

4-
import TestHarnessWebSocket from './src/TestHarnessWebSocket';
4+
import { TestHarnessWebSocket } from '@launchdarkly/js-contract-test-utils/client';
5+
6+
import { newSdkClientEntity } from './src/ClientEntity';
7+
8+
const RN_CAPABILITIES = [
9+
'client-side',
10+
'mobile',
11+
'service-endpoints',
12+
'tags',
13+
'user-type',
14+
'inline-context-all',
15+
'anonymous-redaction',
16+
'strongly-typed',
17+
'client-prereq-events',
18+
'client-per-context-summaries',
19+
'track-hooks',
20+
];
521

622
const styles = StyleSheet.create({
723
container: {
@@ -24,7 +40,12 @@ export default function App() {
2440
const [connected, setConnected] = useState(false);
2541

2642
useEffect(() => {
27-
const ws = new TestHarnessWebSocket('ws://localhost:8001', setConnected);
43+
const ws = new TestHarnessWebSocket(
44+
'ws://localhost:8001',
45+
RN_CAPABILITIES,
46+
newSdkClientEntity,
47+
setConnected,
48+
);
2849
ws.connect();
2950
return () => ws.disconnect();
3051
}, []);

packages/sdk/react-native/contract-tests/entity/src/TestHarnessWebSocket.ts renamed to packages/tooling/contract-test-utils/src/client-side/TestHarnessWebSocket.ts

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,32 @@
1-
import { makeLogger } from '@launchdarkly/js-contract-test-utils/client';
2-
import { LDLogger } from '@launchdarkly/react-native-client-sdk';
1+
import { makeLogger } from '../logging/makeLogger.js';
2+
import { CommandParams } from '../types/CommandParams.js';
3+
import { CreateInstanceParams } from '../types/ConfigParams.js';
34

4-
import { ClientEntity, newSdkClientEntity } from './ClientEntity';
5+
export interface IClientEntity {
6+
doCommand(params: CommandParams): Promise<unknown>;
7+
close(): void | Promise<void>;
8+
}
9+
10+
export type CreateClientEntityFn = (options: CreateInstanceParams) => Promise<IClientEntity>;
511

612
export default class TestHarnessWebSocket {
713
private _ws?: WebSocket;
8-
private readonly _entities: Record<string, ClientEntity> = {};
14+
private readonly _entities: Record<string, IClientEntity> = {};
915
private _clientCounter = 0;
10-
private _logger: LDLogger = makeLogger('TestHarnessWebSocket');
16+
private _logger = makeLogger('TestHarnessWebSocket');
1117
private _intentionalClose = false;
12-
private _onConnectionChange?: (connected: boolean) => void;
1318

1419
constructor(
1520
private readonly _url: string,
16-
onConnectionChange?: (connected: boolean) => void,
17-
) {
18-
this._onConnectionChange = onConnectionChange;
19-
}
21+
private readonly _capabilities: string[],
22+
private readonly _createClient: CreateClientEntityFn,
23+
private readonly _onConnectionChange?: (connected: boolean) => void,
24+
) {}
2025

2126
connect() {
2227
this._intentionalClose = false;
2328
this._logger.info(`Connecting to web socket.`);
24-
this._ws = new WebSocket(this._url, 'v1');
29+
this._ws = new WebSocket(this._url, ['v1']);
2530
this._ws.onopen = () => {
2631
this._logger.info('Connected to websocket.');
2732
this._onConnectionChange?.(true);
@@ -45,26 +50,13 @@ export default class TestHarnessWebSocket {
4550
const resData: any = { reqId: data.reqId };
4651
switch (data.command) {
4752
case 'getCapabilities':
48-
resData.capabilities = [
49-
'client-side',
50-
'mobile',
51-
'service-endpoints',
52-
'tags',
53-
'user-type',
54-
'inline-context-all',
55-
'anonymous-redaction',
56-
'strongly-typed',
57-
'client-prereq-events',
58-
'client-per-context-summaries',
59-
'track-hooks',
60-
];
61-
53+
resData.capabilities = this._capabilities;
6254
break;
6355
case 'createClient':
6456
try {
6557
resData.resourceUrl = `/clients/${this._clientCounter}`;
6658
resData.status = 201;
67-
const entity = await newSdkClientEntity(data.body);
59+
const entity = await this._createClient(data.body);
6860
this._entities[this._clientCounter] = entity;
6961
this._clientCounter += 1;
7062
} catch (e: any) {
@@ -87,7 +79,6 @@ export default class TestHarnessWebSocket {
8779
resData.status = 404;
8880
this._logger.warn(`Client did not exist: ${data.id}`);
8981
}
90-
9182
break;
9283
case 'deleteClient':
9384
if (Object.prototype.hasOwnProperty.call(this._entities, data.id)) {
@@ -102,7 +93,6 @@ export default class TestHarnessWebSocket {
10293
default:
10394
break;
10495
}
105-
10696
this.send(resData);
10797
};
10898
}

packages/tooling/contract-test-utils/src/client.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ export * from './index.js';
33

44
// Client-side exports
55
export { default as ClientSideTestHook } from './client-side/TestHook.js';
6+
export { default as TestHarnessWebSocket } from './client-side/TestHarnessWebSocket.js';
7+
export type { IClientEntity, CreateClientEntityFn } from './client-side/TestHarnessWebSocket.js';

0 commit comments

Comments
 (0)