@voltkit/volt-test 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/suites/analytics-studio.d.ts +22 -0
- package/dist/suites/analytics-studio.js +121 -0
- package/dist/suites/analytics-studio.js.map +1 -0
- package/dist/suites/index.d.ts +3 -0
- package/dist/suites/index.js +3 -0
- package/dist/suites/index.js.map +1 -1
- package/dist/suites/sync-storm.d.ts +26 -0
- package/dist/suites/sync-storm.js +164 -0
- package/dist/suites/sync-storm.js.map +1 -0
- package/dist/suites/workflow-lab.d.ts +21 -0
- package/dist/suites/workflow-lab.js +106 -0
- package/dist/suites/workflow-lab.js.map +1 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,6 @@ export type { LoadedVoltTestConfig, LoadVoltTestConfigOptions, RunSuitesOptions,
|
|
|
4
4
|
export { runSuites } from './runner.js';
|
|
5
5
|
export { VoltAppLauncher } from './launcher.js';
|
|
6
6
|
export { FileDialogAutomationDriver, MenuAutomationDriver, TrayAutomationDriver, } from './drivers/index.js';
|
|
7
|
-
export { createHelloWorldSmokeSuite, createIpcDemoSmokeSuite, } from './suites/index.js';
|
|
7
|
+
export { createHelloWorldSmokeSuite, createIpcDemoSmokeSuite, createAnalyticsStudioBenchmarkSuite, createSyncStormBenchmarkSuite, createWorkflowLabBenchmarkSuite, } from './suites/index.js';
|
|
8
8
|
export type { AutomationEvent, FileDialogAutomationDriverOptions, FileDialogAutomationPlatform, MenuAutomationDriverOptions, MenuSetupState, OpenDialogAutomationResult, SaveDialogAutomationResult, TrayAutomationDriverOptions, TraySetupState, } from './drivers/index.js';
|
|
9
|
-
export type { HelloWorldSmokePayload, HelloWorldSmokeSuiteOptions, IpcDemoSmokePayload, IpcDemoSmokeSuiteOptions, } from './suites/index.js';
|
|
9
|
+
export type { HelloWorldSmokePayload, HelloWorldSmokeSuiteOptions, IpcDemoSmokePayload, IpcDemoSmokeSuiteOptions, AnalyticsStudioBenchmarkPayload, AnalyticsStudioBenchmarkSuiteOptions, SyncStormBenchmarkPayload, SyncStormBenchmarkSuiteOptions, WorkflowLabBenchmarkPayload, WorkflowLabBenchmarkSuiteOptions, } from './suites/index.js';
|
package/dist/index.js
CHANGED
|
@@ -3,5 +3,5 @@ export { assertWindowReady, parseWindowStatus, waitForWindowStatus, } from './wi
|
|
|
3
3
|
export { runSuites } from './runner.js';
|
|
4
4
|
export { VoltAppLauncher } from './launcher.js';
|
|
5
5
|
export { FileDialogAutomationDriver, MenuAutomationDriver, TrayAutomationDriver, } from './drivers/index.js';
|
|
6
|
-
export { createHelloWorldSmokeSuite, createIpcDemoSmokeSuite, } from './suites/index.js';
|
|
6
|
+
export { createHelloWorldSmokeSuite, createIpcDemoSmokeSuite, createAnalyticsStudioBenchmarkSuite, createSyncStormBenchmarkSuite, createWorkflowLabBenchmarkSuite, } from './suites/index.js';
|
|
7
7
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAWrB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAWrB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,EACvB,mCAAmC,EACnC,6BAA6B,EAC7B,+BAA+B,GAChC,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { VoltTestSuite } from '../types.js';
|
|
2
|
+
export interface AnalyticsStudioBenchmarkSuiteOptions {
|
|
3
|
+
name?: string;
|
|
4
|
+
projectDir?: string;
|
|
5
|
+
timeoutMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface AnalyticsStudioBenchmarkPayload {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
profile: {
|
|
10
|
+
datasetSize: number;
|
|
11
|
+
cachedSizes: number[];
|
|
12
|
+
};
|
|
13
|
+
benchmark: {
|
|
14
|
+
datasetSize: number;
|
|
15
|
+
iterations: number;
|
|
16
|
+
backendDurationMs: number;
|
|
17
|
+
peakMatches: number;
|
|
18
|
+
payloadBytes: number;
|
|
19
|
+
};
|
|
20
|
+
roundTripMs: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function createAnalyticsStudioBenchmarkSuite(options?: AnalyticsStudioBenchmarkSuiteOptions): VoltTestSuite;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { VoltAppLauncher } from '../launcher.js';
|
|
4
|
+
const RESULT_FILE = '.volt-benchmark-result.json';
|
|
5
|
+
export function createAnalyticsStudioBenchmarkSuite(options = {}) {
|
|
6
|
+
const name = options.name ?? 'analytics-studio-benchmark';
|
|
7
|
+
const projectDir = options.projectDir ?? 'examples/analytics-studio';
|
|
8
|
+
const timeoutMs = options.timeoutMs ?? 900_000;
|
|
9
|
+
return {
|
|
10
|
+
name,
|
|
11
|
+
timeoutMs,
|
|
12
|
+
async run(context) {
|
|
13
|
+
const launcher = new VoltAppLauncher({
|
|
14
|
+
repoRoot: context.repoRoot,
|
|
15
|
+
cliEntryPath: context.cliEntryPath,
|
|
16
|
+
logger: context.logger,
|
|
17
|
+
});
|
|
18
|
+
await launcher.run({
|
|
19
|
+
sourceProjectDir: projectDir,
|
|
20
|
+
resultFile: RESULT_FILE,
|
|
21
|
+
timeoutMs: context.timeoutMs,
|
|
22
|
+
prepareProject(projectDirPath) {
|
|
23
|
+
writeFileSync(join(projectDirPath, 'src', 'main.ts'), ANALYTICS_AUTORUN_SOURCE, 'utf8');
|
|
24
|
+
},
|
|
25
|
+
validatePayload: validateAnalyticsPayload,
|
|
26
|
+
artifactsDir: context.artifactsDir,
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function validateAnalyticsPayload(payload) {
|
|
32
|
+
const value = asRecord(payload);
|
|
33
|
+
if (!value || value.ok !== true) {
|
|
34
|
+
throw new Error(`[volt:test] analytics benchmark failed: ${JSON.stringify(payload)}`);
|
|
35
|
+
}
|
|
36
|
+
const profile = asRecord(value.profile);
|
|
37
|
+
const benchmark = asRecord(value.benchmark);
|
|
38
|
+
const roundTripMs = value.roundTripMs;
|
|
39
|
+
if (!profile || !benchmark || typeof roundTripMs !== 'number') {
|
|
40
|
+
throw new Error('[volt:test] analytics benchmark payload missing profile, benchmark, or roundTripMs.');
|
|
41
|
+
}
|
|
42
|
+
const datasetSize = profile.datasetSize;
|
|
43
|
+
const cachedSizes = profile.cachedSizes;
|
|
44
|
+
const iterations = benchmark.iterations;
|
|
45
|
+
const backendDurationMs = benchmark.backendDurationMs;
|
|
46
|
+
const peakMatches = benchmark.peakMatches;
|
|
47
|
+
const payloadBytes = benchmark.payloadBytes;
|
|
48
|
+
if (typeof datasetSize !== 'number'
|
|
49
|
+
|| !Array.isArray(cachedSizes)
|
|
50
|
+
|| typeof iterations !== 'number'
|
|
51
|
+
|| typeof backendDurationMs !== 'number'
|
|
52
|
+
|| typeof peakMatches !== 'number'
|
|
53
|
+
|| typeof payloadBytes !== 'number') {
|
|
54
|
+
throw new Error('[volt:test] analytics benchmark payload has invalid numeric fields.');
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
ok: true,
|
|
58
|
+
profile: {
|
|
59
|
+
datasetSize,
|
|
60
|
+
cachedSizes: cachedSizes.filter((entry) => typeof entry === 'number'),
|
|
61
|
+
},
|
|
62
|
+
benchmark: {
|
|
63
|
+
datasetSize: Number(benchmark.datasetSize),
|
|
64
|
+
iterations,
|
|
65
|
+
backendDurationMs,
|
|
66
|
+
peakMatches,
|
|
67
|
+
payloadBytes,
|
|
68
|
+
},
|
|
69
|
+
roundTripMs,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function asRecord(value) {
|
|
73
|
+
if (!value || typeof value !== 'object') {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
const ANALYTICS_AUTORUN_SOURCE = `
|
|
79
|
+
interface VoltBridge {
|
|
80
|
+
invoke<T = unknown>(method: string, args?: unknown): Promise<T>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
declare global {
|
|
84
|
+
interface Window {
|
|
85
|
+
__volt__?: VoltBridge;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function run(): Promise<void> {
|
|
90
|
+
const bridge = window.__volt__;
|
|
91
|
+
if (!bridge?.invoke) {
|
|
92
|
+
throw new Error('window.__volt__.invoke is unavailable');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const startedAt = Date.now();
|
|
96
|
+
try {
|
|
97
|
+
const profile = await bridge.invoke('analytics:profile', { datasetSize: 50000 });
|
|
98
|
+
const benchmark = await bridge.invoke('analytics:run', {
|
|
99
|
+
datasetSize: 50000,
|
|
100
|
+
iterations: 8,
|
|
101
|
+
searchTerm: 'risk',
|
|
102
|
+
minScore: 61,
|
|
103
|
+
topN: 24,
|
|
104
|
+
});
|
|
105
|
+
await bridge.invoke('benchmark:complete', {
|
|
106
|
+
ok: true,
|
|
107
|
+
profile,
|
|
108
|
+
benchmark,
|
|
109
|
+
roundTripMs: Date.now() - startedAt,
|
|
110
|
+
});
|
|
111
|
+
} catch (error) {
|
|
112
|
+
await bridge.invoke('benchmark:complete', {
|
|
113
|
+
ok: false,
|
|
114
|
+
error: error instanceof Error ? error.message : String(error),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
void run();
|
|
120
|
+
`.trimStart();
|
|
121
|
+
//# sourceMappingURL=analytics-studio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics-studio.js","sourceRoot":"","sources":["../../src/suites/analytics-studio.ts"],"names":[],"mappings":"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"}
|
package/dist/suites/index.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
export { createHelloWorldSmokeSuite, type HelloWorldSmokePayload, type HelloWorldSmokeSuiteOptions, } from './hello-world.js';
|
|
2
2
|
export { createIpcDemoSmokeSuite, type IpcDemoSmokePayload, type IpcDemoSmokeSuiteOptions, } from './ipc-demo.js';
|
|
3
|
+
export { createAnalyticsStudioBenchmarkSuite, type AnalyticsStudioBenchmarkPayload, type AnalyticsStudioBenchmarkSuiteOptions, } from './analytics-studio.js';
|
|
4
|
+
export { createSyncStormBenchmarkSuite, type SyncStormBenchmarkPayload, type SyncStormBenchmarkSuiteOptions, } from './sync-storm.js';
|
|
5
|
+
export { createWorkflowLabBenchmarkSuite, type WorkflowLabBenchmarkPayload, type WorkflowLabBenchmarkSuiteOptions, } from './workflow-lab.js';
|
package/dist/suites/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export { createHelloWorldSmokeSuite, } from './hello-world.js';
|
|
2
2
|
export { createIpcDemoSmokeSuite, } from './ipc-demo.js';
|
|
3
|
+
export { createAnalyticsStudioBenchmarkSuite, } from './analytics-studio.js';
|
|
4
|
+
export { createSyncStormBenchmarkSuite, } from './sync-storm.js';
|
|
5
|
+
export { createWorkflowLabBenchmarkSuite, } from './workflow-lab.js';
|
|
3
6
|
//# sourceMappingURL=index.js.map
|
package/dist/suites/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/suites/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,GAG3B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,uBAAuB,GAGxB,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/suites/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,GAG3B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,uBAAuB,GAGxB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,mCAAmC,GAGpC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,6BAA6B,GAG9B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,+BAA+B,GAGhC,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { VoltTestSuite } from '../types.js';
|
|
2
|
+
export interface SyncStormBenchmarkSuiteOptions {
|
|
3
|
+
name?: string;
|
|
4
|
+
projectDir?: string;
|
|
5
|
+
timeoutMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface SyncStormBenchmarkPayload {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
start: {
|
|
10
|
+
scenarioId: string;
|
|
11
|
+
};
|
|
12
|
+
summary: {
|
|
13
|
+
totalTickEvents: number;
|
|
14
|
+
snapshotEvents: number;
|
|
15
|
+
backendDurationMs: number;
|
|
16
|
+
averageDriftMs: number;
|
|
17
|
+
maxDriftMs: number;
|
|
18
|
+
};
|
|
19
|
+
renderer: {
|
|
20
|
+
tickCount: number;
|
|
21
|
+
snapshotCount: number;
|
|
22
|
+
averageLagMs: number;
|
|
23
|
+
maxLagMs: number;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export declare function createSyncStormBenchmarkSuite(options?: SyncStormBenchmarkSuiteOptions): VoltTestSuite;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { VoltAppLauncher } from '../launcher.js';
|
|
4
|
+
const RESULT_FILE = '.volt-benchmark-result.json';
|
|
5
|
+
export function createSyncStormBenchmarkSuite(options = {}) {
|
|
6
|
+
const name = options.name ?? 'sync-storm-benchmark';
|
|
7
|
+
const projectDir = options.projectDir ?? 'examples/sync-storm';
|
|
8
|
+
const timeoutMs = options.timeoutMs ?? 900_000;
|
|
9
|
+
return {
|
|
10
|
+
name,
|
|
11
|
+
timeoutMs,
|
|
12
|
+
async run(context) {
|
|
13
|
+
const launcher = new VoltAppLauncher({
|
|
14
|
+
repoRoot: context.repoRoot,
|
|
15
|
+
cliEntryPath: context.cliEntryPath,
|
|
16
|
+
logger: context.logger,
|
|
17
|
+
});
|
|
18
|
+
await launcher.run({
|
|
19
|
+
sourceProjectDir: projectDir,
|
|
20
|
+
resultFile: RESULT_FILE,
|
|
21
|
+
timeoutMs: context.timeoutMs,
|
|
22
|
+
prepareProject(projectDirPath) {
|
|
23
|
+
writeFileSync(join(projectDirPath, 'src', 'main.ts'), SYNC_STORM_AUTORUN_SOURCE, 'utf8');
|
|
24
|
+
},
|
|
25
|
+
validatePayload: validateSyncStormPayload,
|
|
26
|
+
artifactsDir: context.artifactsDir,
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function validateSyncStormPayload(payload) {
|
|
32
|
+
const value = asRecord(payload);
|
|
33
|
+
if (!value || value.ok !== true) {
|
|
34
|
+
throw new Error(`[volt:test] sync-storm benchmark failed: ${JSON.stringify(payload)}`);
|
|
35
|
+
}
|
|
36
|
+
const start = asRecord(value.start);
|
|
37
|
+
const summary = asRecord(value.summary);
|
|
38
|
+
const renderer = asRecord(value.renderer);
|
|
39
|
+
if (!start || !summary || !renderer || typeof start.scenarioId !== 'string') {
|
|
40
|
+
throw new Error('[volt:test] sync-storm payload missing start, summary, or renderer metrics.');
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
ok: true,
|
|
44
|
+
start: {
|
|
45
|
+
scenarioId: start.scenarioId,
|
|
46
|
+
},
|
|
47
|
+
summary: {
|
|
48
|
+
totalTickEvents: Number(summary.totalTickEvents),
|
|
49
|
+
snapshotEvents: Number(summary.snapshotEvents),
|
|
50
|
+
backendDurationMs: Number(summary.backendDurationMs),
|
|
51
|
+
averageDriftMs: Number(summary.averageDriftMs),
|
|
52
|
+
maxDriftMs: Number(summary.maxDriftMs),
|
|
53
|
+
},
|
|
54
|
+
renderer: {
|
|
55
|
+
tickCount: Number(renderer.tickCount),
|
|
56
|
+
snapshotCount: Number(renderer.snapshotCount),
|
|
57
|
+
averageLagMs: Number(renderer.averageLagMs),
|
|
58
|
+
maxLagMs: Number(renderer.maxLagMs),
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function asRecord(value) {
|
|
63
|
+
if (!value || typeof value !== 'object') {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
const SYNC_STORM_AUTORUN_SOURCE = `
|
|
69
|
+
interface VoltBridge {
|
|
70
|
+
invoke<T = unknown>(method: string, args?: unknown): Promise<T>;
|
|
71
|
+
on(event: string, callback: (payload: unknown) => void): void;
|
|
72
|
+
off?(event: string, callback: (payload: unknown) => void): void;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
declare global {
|
|
76
|
+
interface Window {
|
|
77
|
+
__volt__?: VoltBridge;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function run(): Promise<void> {
|
|
82
|
+
const bridge = window.__volt__;
|
|
83
|
+
if (!bridge?.invoke || !bridge.on) {
|
|
84
|
+
throw new Error('window.__volt__ bridge is unavailable');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const payload = await new Promise<unknown>((resolve, reject) => {
|
|
89
|
+
let start: { scenarioId: string } | null = null;
|
|
90
|
+
let tickCount = 0;
|
|
91
|
+
let snapshotCount = 0;
|
|
92
|
+
let totalLagMs = 0;
|
|
93
|
+
let maxLagMs = 0;
|
|
94
|
+
const timeout = setTimeout(() => {
|
|
95
|
+
reject(new Error('timed out waiting for sync:complete'));
|
|
96
|
+
}, 45000);
|
|
97
|
+
|
|
98
|
+
const onTick = (eventPayload: unknown) => {
|
|
99
|
+
const value = eventPayload as { scenarioId?: string; issuedAt?: number } | null;
|
|
100
|
+
if (!value || value.scenarioId !== start?.scenarioId) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
tickCount += 1;
|
|
104
|
+
const lagMs = Math.max(0, Date.now() - Number(value.issuedAt ?? Date.now()));
|
|
105
|
+
totalLagMs += lagMs;
|
|
106
|
+
maxLagMs = Math.max(maxLagMs, lagMs);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const onSnapshot = (eventPayload: unknown) => {
|
|
110
|
+
const value = eventPayload as { scenarioId?: string } | null;
|
|
111
|
+
if (!value || value.scenarioId !== start?.scenarioId) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
snapshotCount += 1;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const onComplete = (eventPayload: unknown) => {
|
|
118
|
+
const value = eventPayload as { scenarioId?: string } | null;
|
|
119
|
+
if (!value || value.scenarioId !== start?.scenarioId || start === null) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
clearTimeout(timeout);
|
|
123
|
+
bridge.off?.('sync:tick', onTick);
|
|
124
|
+
bridge.off?.('sync:snapshot', onSnapshot);
|
|
125
|
+
bridge.off?.('sync:complete', onComplete);
|
|
126
|
+
resolve({
|
|
127
|
+
ok: true,
|
|
128
|
+
start,
|
|
129
|
+
summary: eventPayload,
|
|
130
|
+
renderer: {
|
|
131
|
+
tickCount,
|
|
132
|
+
snapshotCount,
|
|
133
|
+
averageLagMs: tickCount > 0 ? Math.round(totalLagMs / tickCount) : 0,
|
|
134
|
+
maxLagMs,
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
bridge.on('sync:tick', onTick);
|
|
140
|
+
bridge.on('sync:snapshot', onSnapshot);
|
|
141
|
+
bridge.on('sync:complete', onComplete);
|
|
142
|
+
|
|
143
|
+
void bridge.invoke<{ scenarioId: string }>('sync:run', {
|
|
144
|
+
workerCount: 12,
|
|
145
|
+
ticksPerWorker: 48,
|
|
146
|
+
intervalMs: 4,
|
|
147
|
+
burstSize: 8,
|
|
148
|
+
}).then((value) => {
|
|
149
|
+
start = value;
|
|
150
|
+
}, reject);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
await bridge.invoke('benchmark:complete', payload);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
await bridge.invoke('benchmark:complete', {
|
|
156
|
+
ok: false,
|
|
157
|
+
error: error instanceof Error ? error.message : String(error),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
void run();
|
|
163
|
+
`.trimStart();
|
|
164
|
+
//# sourceMappingURL=sync-storm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-storm.js","sourceRoot":"","sources":["../../src/suites/sync-storm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjD,MAAM,WAAW,GAAG,6BAA6B,CAAC;AA4BlD,MAAM,UAAU,6BAA6B,CAC3C,UAA0C,EAAE;IAE5C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,sBAAsB,CAAC;IACpD,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,qBAAqB,CAAC;IAC/D,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC;IAE/C,OAAO;QACL,IAAI;QACJ,SAAS;QACT,KAAK,CAAC,GAAG,CAAC,OAAO;YACf,MAAM,QAAQ,GAAG,IAAI,eAAe,CAAC;gBACnC,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,YAAY,EAAE,OAAO,CAAC,YAAY;gBAClC,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB,CAAC,CAAC;YAEH,MAAM,QAAQ,CAAC,GAAG,CAA4B;gBAC5C,gBAAgB,EAAE,UAAU;gBAC5B,UAAU,EAAE,WAAW;gBACvB,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,cAAc,CAAC,cAAc;oBAC3B,aAAa,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,yBAAyB,EAAE,MAAM,CAAC,CAAC;gBAC3F,CAAC;gBACD,eAAe,EAAE,wBAAwB;gBACzC,YAAY,EAAE,OAAO,CAAC,YAAY;aACnC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,wBAAwB,CAAC,OAAgB;IAChD,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,4CAA4C,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzF,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC1C,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,IAAI,CAAC,QAAQ,IAAI,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC5E,MAAM,IAAI,KAAK,CAAC,6EAA6E,CAAC,CAAC;IACjG,CAAC;IAED,OAAO;QACL,EAAE,EAAE,IAAI;QACR,KAAK,EAAE;YACL,UAAU,EAAE,KAAK,CAAC,UAAU;SAC7B;QACD,OAAO,EAAE;YACP,eAAe,EAAE,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC;YAChD,cAAc,EAAE,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC;YAC9C,iBAAiB,EAAE,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC;YACpD,cAAc,EAAE,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC;YAC9C,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;SACvC;QACD,QAAQ,EAAE;YACR,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;YACrC,aAAa,EAAE,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC;YAC7C,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC;YAC3C,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;SACpC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAgC,CAAC;AAC1C,CAAC;AAED,MAAM,yBAAyB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+FjC,CAAC,SAAS,EAAE,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { VoltTestSuite } from '../types.js';
|
|
2
|
+
export interface WorkflowLabBenchmarkSuiteOptions {
|
|
3
|
+
name?: string;
|
|
4
|
+
projectDir?: string;
|
|
5
|
+
timeoutMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface WorkflowLabBenchmarkPayload {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
plugins: Array<{
|
|
10
|
+
name: string;
|
|
11
|
+
label: string;
|
|
12
|
+
}>;
|
|
13
|
+
result: {
|
|
14
|
+
batchSize: number;
|
|
15
|
+
passes: number;
|
|
16
|
+
pipeline: string[];
|
|
17
|
+
backendDurationMs: number;
|
|
18
|
+
payloadBytes: number;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export declare function createWorkflowLabBenchmarkSuite(options?: WorkflowLabBenchmarkSuiteOptions): VoltTestSuite;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { VoltAppLauncher } from '../launcher.js';
|
|
4
|
+
const RESULT_FILE = '.volt-benchmark-result.json';
|
|
5
|
+
export function createWorkflowLabBenchmarkSuite(options = {}) {
|
|
6
|
+
const name = options.name ?? 'workflow-lab-benchmark';
|
|
7
|
+
const projectDir = options.projectDir ?? 'examples/workflow-lab';
|
|
8
|
+
const timeoutMs = options.timeoutMs ?? 900_000;
|
|
9
|
+
return {
|
|
10
|
+
name,
|
|
11
|
+
timeoutMs,
|
|
12
|
+
async run(context) {
|
|
13
|
+
const launcher = new VoltAppLauncher({
|
|
14
|
+
repoRoot: context.repoRoot,
|
|
15
|
+
cliEntryPath: context.cliEntryPath,
|
|
16
|
+
logger: context.logger,
|
|
17
|
+
});
|
|
18
|
+
await launcher.run({
|
|
19
|
+
sourceProjectDir: projectDir,
|
|
20
|
+
resultFile: RESULT_FILE,
|
|
21
|
+
timeoutMs: context.timeoutMs,
|
|
22
|
+
prepareProject(projectDirPath) {
|
|
23
|
+
writeFileSync(join(projectDirPath, 'src', 'main.ts'), WORKFLOW_LAB_AUTORUN_SOURCE, 'utf8');
|
|
24
|
+
},
|
|
25
|
+
validatePayload: validateWorkflowPayload,
|
|
26
|
+
artifactsDir: context.artifactsDir,
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function validateWorkflowPayload(payload) {
|
|
32
|
+
const value = asRecord(payload);
|
|
33
|
+
if (!value || value.ok !== true) {
|
|
34
|
+
throw new Error(`[volt:test] workflow-lab benchmark failed: ${JSON.stringify(payload)}`);
|
|
35
|
+
}
|
|
36
|
+
const plugins = Array.isArray(value.plugins) ? value.plugins : [];
|
|
37
|
+
const result = asRecord(value.result);
|
|
38
|
+
if (!result) {
|
|
39
|
+
throw new Error('[volt:test] workflow-lab payload missing result object.');
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
ok: true,
|
|
43
|
+
plugins: plugins
|
|
44
|
+
.map((plugin) => asRecord(plugin))
|
|
45
|
+
.filter((plugin) => plugin !== null)
|
|
46
|
+
.map((plugin) => ({
|
|
47
|
+
name: String(plugin.name),
|
|
48
|
+
label: String(plugin.label),
|
|
49
|
+
})),
|
|
50
|
+
result: {
|
|
51
|
+
batchSize: Number(result.batchSize),
|
|
52
|
+
passes: Number(result.passes),
|
|
53
|
+
pipeline: Array.isArray(result.pipeline)
|
|
54
|
+
? result.pipeline.filter((entry) => typeof entry === 'string')
|
|
55
|
+
: [],
|
|
56
|
+
backendDurationMs: Number(result.backendDurationMs),
|
|
57
|
+
payloadBytes: Number(result.payloadBytes),
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function asRecord(value) {
|
|
62
|
+
if (!value || typeof value !== 'object') {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
const WORKFLOW_LAB_AUTORUN_SOURCE = `
|
|
68
|
+
interface VoltBridge {
|
|
69
|
+
invoke<T = unknown>(method: string, args?: unknown): Promise<T>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
declare global {
|
|
73
|
+
interface Window {
|
|
74
|
+
__volt__?: VoltBridge;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function run(): Promise<void> {
|
|
79
|
+
const bridge = window.__volt__;
|
|
80
|
+
if (!bridge?.invoke) {
|
|
81
|
+
throw new Error('window.__volt__.invoke is unavailable');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const plugins = await bridge.invoke<Array<{ name: string; label: string }>>('workflow:plugins');
|
|
86
|
+
const result = await bridge.invoke('workflow:run', {
|
|
87
|
+
batchSize: 6500,
|
|
88
|
+
passes: 4,
|
|
89
|
+
pipeline: plugins.map((plugin) => plugin.name),
|
|
90
|
+
});
|
|
91
|
+
await bridge.invoke('benchmark:complete', {
|
|
92
|
+
ok: true,
|
|
93
|
+
plugins,
|
|
94
|
+
result,
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
await bridge.invoke('benchmark:complete', {
|
|
98
|
+
ok: false,
|
|
99
|
+
error: error instanceof Error ? error.message : String(error),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
void run();
|
|
105
|
+
`.trimStart();
|
|
106
|
+
//# sourceMappingURL=workflow-lab.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-lab.js","sourceRoot":"","sources":["../../src/suites/workflow-lab.ts"],"names":[],"mappings":"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"}
|