diff --git a/frontend/test-client/src/agent/mock-agent.ts b/frontend/test-client/src/agent/mock-agent.ts index b702859..21163d5 100644 --- a/frontend/test-client/src/agent/mock-agent.ts +++ b/frontend/test-client/src/agent/mock-agent.ts @@ -4,7 +4,7 @@ import { assert } from "../utils/assert"; import type { RelativePath, SyncSettings } from "sync-client"; import { LogLevel } from "sync-client"; import { MockClient } from "./mock-client"; -import chalk from "chalk"; +import { sleep } from "../utils/sleep"; export class MockAgent extends MockClient { private readonly writtenContents: string[] = []; @@ -14,8 +14,8 @@ export class MockAgent extends MockClient { public constructor( initialSettings: Partial, public readonly name: string, - private readonly color: string, - private readonly doDeletes: boolean + private readonly doDeletes: boolean, + private readonly jitterScaleInSeconds: number ) { super(initialSettings); } @@ -23,11 +23,18 @@ export class MockAgent extends MockClient { public async init(): Promise { await super.init(); - this.client.logger.addOnMessageListener((message) => { - const formatted = chalk.hex(this.color)( - `[${this.name}] ${message.timestamp.toISOString()} ${message.level} ${message.message}` - ); + this.client.fetchImplementation = async ( + input: string | URL | globalThis.Request, + init?: RequestInit + ): Promise => { + await sleep(Math.random() * this.jitterScaleInSeconds * 1000); + const response = await fetch(input, init); + await sleep(Math.random() * this.jitterScaleInSeconds * 1000); + return response; + }; + this.client.logger.addOnMessageListener((message) => { + const formatted = `[${this.name}] ${message.timestamp.toISOString()} ${message.level} ${message.message}`; switch (message.level) { case LogLevel.ERROR: console.error(formatted); diff --git a/frontend/test-client/src/cli.ts b/frontend/test-client/src/cli.ts index b6f4eba..7f8b29a 100644 --- a/frontend/test-client/src/cli.ts +++ b/frontend/test-client/src/cli.ts @@ -7,16 +7,17 @@ async function runTest({ agentCount, concurrency, iterations, - doDeletes + doDeletes, + jitterScaleInSeconds }: { agentCount: number; concurrency: number; iterations: number; doDeletes: boolean; + jitterScaleInSeconds: number; }): Promise { - console.info( - `Running test with ${agentCount} agents, concurrency ${concurrency}, iterations ${iterations}, doDeletes ${doDeletes}` - ); + const settings = `with ${agentCount} agents, concurrency ${concurrency}, iterations ${iterations}, doDeletes ${doDeletes}, jitterScaleInSeconds ${jitterScaleInSeconds}`; + console.info(`Running test ${settings}`); const initialSettings: Partial = { isSyncEnabled: true, @@ -29,7 +30,12 @@ async function runTest({ const clients: MockAgent[] = []; for (let i = 0; i < agentCount; i++) { clients.push( - new MockAgent(initialSettings, `agent-${i}`, "#ff0000", doDeletes) + new MockAgent( + initialSettings, + `agent-${i}`, + doDeletes, + jitterScaleInSeconds + ) ); } @@ -42,8 +48,15 @@ async function runTest({ await sleep(100); } + console.info("Stopping agents"); + + // Each agent can have unpushed changes which might conflict with eachother so each has to resolve the conflicts & push, and + for (const client of clients) { + await client.finish(); + } + + // then we need a second pass to ensure that all agents pull the same state. for (const client of clients) { - // todo: make it less hacky await client.finish(); } @@ -65,33 +78,35 @@ async function runTest({ console.info(`Content check for ${client.name} passed`); }); - console.info( - `Test passed with ${agentCount} agents, concurrency ${concurrency}, iterations ${iterations}, doDeletes ${doDeletes}` - ); + console.info(`Test passed with ${settings}`); } catch (err) { - console.error( - `Test failed with ${agentCount} agents, concurrency ${concurrency}, iterations ${iterations}, doDeletes ${doDeletes}` - ); + console.error(`Test failed with ${settings}`); throw err; } } async function runTests(): Promise { const agentCounts = [2, 10]; + const jitterScaleInSeconds = [0.5, 3, 0]; const concurrencies = [1, 16]; const iterations = [50, 300]; const doDeletes = [false, true]; for (const agentCount of agentCounts) { for (const concurrency of concurrencies) { - for (const iteration of iterations) { - for (const deleteFiles of doDeletes) { - await runTest({ - agentCount, - concurrency, - iterations: iteration, - doDeletes: deleteFiles - }); + for (const jitter of jitterScaleInSeconds) { + for (const iteration of iterations) { + for (const deleteFiles of doDeletes) { + while (true) { + await runTest({ + agentCount, + concurrency, + iterations: iteration, + doDeletes: deleteFiles, + jitterScaleInSeconds: jitter + }); + } + } } } }