flow-store-versioned-step-keying-data-integrity.test.mjs
96 lines 3.9 KB
Raw
sha256:cfe8c8cf68336f6d46318bd40610c18d9ff7df231df2fb190af1f5a9c4f4f93b fix(flow-store): versioned step keying for multi-version fl… Human minor ⚠ breaking 7 hours ago
1 /**
2 * Tier 5 — DATA-INTEGRITY: divergent step bodies across flow versions (7A-10c).
3 *
4 * Regression for the 7A-12 store finding: two versions of one Flow must carry
5 * independent step text when pinned by semver.
6 *
7 * @see docs/evidence/7A-12/README.md
8 */
9 import { describe, it, beforeEach, afterEach } from 'node:test';
10 import assert from 'node:assert/strict';
11 import fs from 'node:fs';
12 import path from 'node:path';
13 import { fileURLToPath } from 'node:url';
14 import {
15 upsertFlowVersion,
16 getFlow,
17 loadFlowStore,
18 } from '../lib/flow/flow-store.mjs';
19 import { makeFlowBundle, emptyStarterDir } from './fixtures/flow/authoring-helpers.mjs';
20
21 const __dirname = path.dirname(fileURLToPath(import.meta.url));
22 const tmpRoot = path.join(__dirname, 'fixtures', 'tmp-flow-versioned-step-keying');
23 const visible = new Set(['personal', 'project', 'org']);
24
25 describe('Flow store — versioned step keying (data integrity)', () => {
26 const dataDir = path.join(tmpRoot, 'data');
27 const vaultId = 'default';
28 let starterDir;
29
30 beforeEach(() => {
31 fs.rmSync(tmpRoot, { recursive: true, force: true });
32 fs.mkdirSync(dataDir, { recursive: true });
33 starterDir = emptyStarterDir(dataDir);
34 });
35
36 afterEach(() => {
37 fs.rmSync(tmpRoot, { recursive: true, force: true });
38 });
39
40 it('two versions with the same step_id retain divergent instruction text', () => {
41 const v1 = makeFlowBundle({ flowId: 'flow_10c_di', version: '1.0.0', steps: 2 });
42 const v2 = structuredClone(makeFlowBundle({ flowId: 'flow_10c_di', version: '2.0.0', steps: 2 }));
43 v2.steps[0].instruction = 'Reworded step 1 for v2.0.0.';
44 v2.steps[1].instruction = 'Reworded step 2 for v2.0.0.';
45
46 upsertFlowVersion(dataDir, vaultId, v1.flow, v1.steps);
47 upsertFlowVersion(dataDir, vaultId, v2.flow, v2.steps);
48
49 const gotV1 = getFlow(dataDir, vaultId, 'flow_10c_di', {
50 filterScopes: visible, version: '1.0.0', starterDir,
51 });
52 const gotV2 = getFlow(dataDir, vaultId, 'flow_10c_di', {
53 filterScopes: visible, version: '2.0.0', starterDir,
54 });
55 const gotLatest = getFlow(dataDir, vaultId, 'flow_10c_di', {
56 filterScopes: visible, starterDir,
57 });
58
59 assert.ok(gotV1);
60 assert.ok(gotV2);
61 assert.ok(gotLatest);
62 assert.equal(gotV1.steps[0].instruction, v1.steps[0].instruction);
63 assert.equal(gotV2.steps[0].instruction, v2.steps[0].instruction);
64 assert.equal(gotLatest.flow.version, '2.0.0');
65 assert.equal(gotLatest.steps[0].instruction, v2.steps[0].instruction);
66 assert.notEqual(gotV1.steps[0].instruction, gotV2.steps[0].instruction);
67 });
68
69 it('upserting v2 does not mutate v1 step bodies in the store file', () => {
70 const v1 = makeFlowBundle({ flowId: 'flow_10c_preserve', version: '1.0.0', steps: 1 });
71 const v2 = structuredClone(makeFlowBundle({ flowId: 'flow_10c_preserve', version: '2.0.0', steps: 1 }));
72 v2.steps[0].instruction = 'Only v2 text.';
73
74 upsertFlowVersion(dataDir, vaultId, v1.flow, v1.steps);
75 upsertFlowVersion(dataDir, vaultId, v2.flow, v2.steps);
76
77 const store = loadFlowStore(dataDir);
78 const rows = store.vaults[vaultId].steps.filter((s) => s.flow_id === 'flow_10c_preserve');
79 assert.equal(rows.length, 2);
80 const rowV1 = rows.find((s) => s.flow_version === '1.0.0');
81 const rowV2 = rows.find((s) => s.flow_version === '2.0.0');
82 assert.ok(rowV1);
83 assert.ok(rowV2);
84 assert.equal(rowV1.instruction, v1.steps[0].instruction);
85 assert.equal(rowV2.instruction, v2.steps[0].instruction);
86 });
87
88 it('flowDefinitionForClient omits store-internal flow_version on wire', () => {
89 const bundle = makeFlowBundle({ flowId: 'flow_10c_wire', version: '1.0.0', steps: 1 });
90 upsertFlowVersion(dataDir, vaultId, bundle.flow, bundle.steps);
91 const got = getFlow(dataDir, vaultId, 'flow_10c_wire', { filterScopes: visible, starterDir });
92 assert.ok(got);
93 assert.equal(got.steps[0].flow_version, undefined);
94 assert.ok(!('flow_version' in got.steps[0]));
95 });
96 });
File History 1 commit
sha256:cfe8c8cf68336f6d46318bd40610c18d9ff7df231df2fb190af1f5a9c4f4f93b fix(flow-store): versioned step keying for multi-version fl… Human minor 7 hours ago