Allow overriding WebSocket implementation and add flaky version for testing

This commit is contained in:
Andras Schmelczer 2025-04-07 23:13:45 +01:00
parent 74a8060246
commit 3ec6bd4d5b
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C
8 changed files with 162 additions and 73 deletions

View file

@ -6,6 +6,8 @@ import { LogLevel } from "sync-client";
import { MockClient } from "./mock-client";
import { sleep } from "../utils/sleep";
import type { LogLine } from "sync-client/dist/types/tracing/logger";
import { flakyFetchFactory } from "../utils/flaky-fetch";
import { flakyWebSocketFactory } from "../utils/flaky-websocket";
export class MockAgent extends MockClient {
private readonly writtenContents: string[] = [];
@ -26,16 +28,8 @@ export class MockAgent extends MockClient {
public async init(): Promise<void> {
await super.init(
// flaky fetch implementation to use during testing
async (
input: string | URL | globalThis.Request,
init?: RequestInit
): Promise<Response> => {
await sleep(Math.random() * this.jitterScaleInSeconds * 1000);
const response = await fetch(input, init);
await sleep(Math.random() * this.jitterScaleInSeconds * 1000);
return response;
}
flakyFetchFactory(this.jitterScaleInSeconds),
flakyWebSocketFactory(this.jitterScaleInSeconds)
);
assert(

View file

@ -30,7 +30,8 @@ export class MockClient implements FileSystemOperations {
}
public async init(
fetchImplementation: typeof globalThis.fetch
fetchImplementation: typeof globalThis.fetch,
webSocketImplementation: typeof globalThis.WebSocket
): Promise<void> {
this.client = await SyncClient.create({
fs: this,
@ -38,7 +39,8 @@ export class MockClient implements FileSystemOperations {
load: async () => this.data,
save: async (data) => void (this.data = data)
},
fetch: fetchImplementation
fetch: fetchImplementation,
webSocket: webSocketImplementation
});
await this.client.start();

View file

@ -0,0 +1,20 @@
import { sleep } from "./sleep";
export const flakyFetchFactory =
(jitterScaleInSeconds: number) =>
async (
input: string | URL | globalThis.Request,
init?: RequestInit
): Promise<Response> => {
if (jitterScaleInSeconds > 0) {
await sleep(Math.random() * jitterScaleInSeconds * 1000);
}
const response = await fetch(input, init);
if (jitterScaleInSeconds > 0) {
await sleep(Math.random() * jitterScaleInSeconds * 1000);
}
return response;
};

View file

@ -0,0 +1,61 @@
import { sleep } from "./sleep";
export function flakyWebSocketFactory(
jitterScaleInSeconds: number
): typeof WebSocket {
// eslint-disable-next-line
return class FlakyWebSocket extends require("ws") {
public set onopen(callback: (event: Event) => void) {
// eslint-disable-next-line
super.onopen = async (event: Event): Promise<void> => {
if (jitterScaleInSeconds > 0) {
await sleep(Math.random() * jitterScaleInSeconds * 1000);
}
callback(event);
};
}
public set onmessage(callback: (event: MessageEvent) => void) {
// eslint-disable-next-line
super.onmessage = async (event: MessageEvent): Promise<void> => {
if (jitterScaleInSeconds > 0) {
await sleep(Math.random() * jitterScaleInSeconds * 1000);
}
callback(event);
};
}
public set onclose(callback: (event: CloseEvent) => void) {
// eslint-disable-next-line
super.onclose = async (event: CloseEvent): Promise<void> => {
if (jitterScaleInSeconds > 0) {
await sleep(Math.random() * jitterScaleInSeconds * 1000);
}
callback(event);
};
}
public set onerror(callback: (event: Event) => void) {
// eslint-disable-next-line
super.onerror = async (event: Event): Promise<void> => {
if (jitterScaleInSeconds > 0) {
await sleep(Math.random() * jitterScaleInSeconds * 1000);
}
callback(event);
};
}
public async send(
data: string | ArrayBufferLike | Blob | ArrayBufferView
): Promise<void> {
if (jitterScaleInSeconds > 0) {
await sleep(Math.random() * jitterScaleInSeconds * 1000);
}
// eslint-disable-next-line
super.send(data);
}
} as unknown as typeof WebSocket;
}