flow-store-performance.test.mjs
98 lines 3.1 KB
Raw
sha256:8915fe406161f95c1681f9469375e7bae5b28c884f00bedbdef65e4b0cd0738d docs(flow): commit FLOW-V0-SPEC.md hygiene for 7A-INT merge Human 16 hours ago
1 /**
2 * Tier 6 — PERFORMANCE: list/get p95 budget on large fixture.
3 *
4 * @see docs/FLOW-STORE-CONTRACT-7A-10.md §9
5 */
6 import { describe, it, beforeEach, afterEach } from 'node:test';
7 import assert from 'node:assert/strict';
8 import fs from 'node:fs';
9 import path from 'node:path';
10 import { fileURLToPath } from 'node:url';
11 import {
12 saveFlowStore,
13 listFlows,
14 getFlow,
15 MAX_FLOW_SUMMARIES,
16 buildFlowStepId,
17 } from '../lib/flow/flow-store.mjs';
18
19 const __dirname = path.dirname(fileURLToPath(import.meta.url));
20 const tmpRoot = path.join(__dirname, 'fixtures', 'tmp-flow-performance');
21
22 function percentile(values, p) {
23 const sorted = [...values].sort((a, b) => a - b);
24 const idx = Math.ceil((p / 100) * sorted.length) - 1;
25 return sorted[Math.max(0, idx)];
26 }
27
28 describe('Flow store — performance', () => {
29 const dataDir = path.join(tmpRoot, 'data');
30 const vaultId = 'default';
31
32 beforeEach(() => {
33 fs.rmSync(tmpRoot, { recursive: true, force: true });
34 fs.mkdirSync(dataDir, { recursive: true });
35 const flows = [];
36 const steps = [];
37 for (let i = 0; i < MAX_FLOW_SUMMARIES; i += 1) {
38 const id = `flow_perf_${String(i).padStart(3, '0')}`;
39 flows.push({
40 schema: 'knowtation.flow/v0',
41 flow_id: id,
42 title: id,
43 version: '1.0.0',
44 scope: i % 3 === 0 ? 'org' : i % 2 === 0 ? 'project' : 'personal',
45 summary: 'perf',
46 tags: i % 5 === 0 ? ['tagged'] : [],
47 steps: [buildFlowStepId(id, 1)],
48 updated: `2026-06-${String((i % 28) + 1).padStart(2, '0')}T00:00:00Z`,
49 truncated: false,
50 });
51 steps.push({
52 schema: 'knowtation.flow_step/v0',
53 step_id: buildFlowStepId(id, 1),
54 flow_id: id,
55 ordinal: 1,
56 owned_job: 'j',
57 instruction: 'i',
58 trigger: 't',
59 when_not_to_run: 'n',
60 boundaries: [],
61 output_shape: 'o',
62 verification: { kind: 'human_review', evidence_required: false, description: 'd' },
63 automatable: 'manual',
64 });
65 }
66 saveFlowStore(dataDir, { vaults: { [vaultId]: { flows, steps, runs: [], candidates: [], projections: [] } } });
67 });
68
69 afterEach(() => {
70 fs.rmSync(tmpRoot, { recursive: true, force: true });
71 });
72
73 it('listFlows and getFlow stay within p95 budget', () => {
74 const listTimes = [];
75 for (let i = 0; i < 40; i += 1) {
76 const t0 = performance.now();
77 listFlows(dataDir, vaultId, {
78 filterScopes: new Set(['personal', 'project', 'org']),
79 effectiveScope: 'org',
80 tag: i % 2 === 0 ? 'tagged' : undefined,
81 limit: 200,
82 });
83 listTimes.push(performance.now() - t0);
84 }
85 const getTimes = [];
86 for (let i = 0; i < 40; i += 1) {
87 const t0 = performance.now();
88 getFlow(dataDir, vaultId, `flow_perf_${String(i).padStart(3, '0')}`, {
89 filterScopes: new Set(['personal', 'project', 'org']),
90 });
91 getTimes.push(performance.now() - t0);
92 }
93 const listP95 = percentile(listTimes, 95);
94 const getP95 = percentile(getTimes, 95);
95 assert.ok(listP95 < 150, `listFlows p95 ${listP95}ms`);
96 assert.ok(getP95 < 50, `getFlow p95 ${getP95}ms`);
97 });
98 });
File History 1 commit
sha256:8915fe406161f95c1681f9469375e7bae5b28c884f00bedbdef65e4b0cd0738d docs(flow): commit FLOW-V0-SPEC.md hygiene for 7A-INT merge Human 16 hours ago