@substrat-run/contract-tests 0.139.0 → 0.140.0

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.
Files changed (90) hide show
  1. package/dist/capability-suite.d.ts.map +1 -1
  2. package/dist/capability-suite.js +5 -0
  3. package/dist/capability-suite.js.map +1 -1
  4. package/dist/caused-by-suite.d.ts.map +1 -1
  5. package/dist/caused-by-suite.js +54 -10
  6. package/dist/caused-by-suite.js.map +1 -1
  7. package/dist/connect-link-suite.d.ts +19 -0
  8. package/dist/connect-link-suite.d.ts.map +1 -0
  9. package/dist/connect-link-suite.js +215 -0
  10. package/dist/connect-link-suite.js.map +1 -0
  11. package/dist/entity-state-migration-suite.d.ts +16 -0
  12. package/dist/entity-state-migration-suite.d.ts.map +1 -0
  13. package/dist/entity-state-migration-suite.js +305 -0
  14. package/dist/entity-state-migration-suite.js.map +1 -0
  15. package/dist/entity-state-module.d.ts +183 -0
  16. package/dist/entity-state-module.d.ts.map +1 -1
  17. package/dist/entity-state-module.js +139 -1
  18. package/dist/entity-state-module.js.map +1 -1
  19. package/dist/entity-state-suite.d.ts.map +1 -1
  20. package/dist/entity-state-suite.js +35 -12
  21. package/dist/entity-state-suite.js.map +1 -1
  22. package/dist/entity-trash-module.d.ts +439 -0
  23. package/dist/entity-trash-module.d.ts.map +1 -0
  24. package/dist/entity-trash-module.js +287 -0
  25. package/dist/entity-trash-module.js.map +1 -0
  26. package/dist/entity-trash-suite.d.ts +59 -0
  27. package/dist/entity-trash-suite.d.ts.map +1 -0
  28. package/dist/entity-trash-suite.js +568 -0
  29. package/dist/entity-trash-suite.js.map +1 -0
  30. package/dist/erasure-module.d.ts +259 -0
  31. package/dist/erasure-module.d.ts.map +1 -0
  32. package/dist/erasure-module.js +251 -0
  33. package/dist/erasure-module.js.map +1 -0
  34. package/dist/index.d.ts +13 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +13 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/list-suite.d.ts.map +1 -1
  39. package/dist/list-suite.js +101 -1
  40. package/dist/list-suite.js.map +1 -1
  41. package/dist/membership-executor-suite.d.ts.map +1 -1
  42. package/dist/membership-executor-suite.js +60 -9
  43. package/dist/membership-executor-suite.js.map +1 -1
  44. package/dist/migration-comments-suite.d.ts +3 -0
  45. package/dist/migration-comments-suite.d.ts.map +1 -0
  46. package/dist/migration-comments-suite.js +92 -0
  47. package/dist/migration-comments-suite.js.map +1 -0
  48. package/dist/migration-comments.d.ts +3 -0
  49. package/dist/migration-comments.d.ts.map +1 -0
  50. package/dist/migration-comments.js +50 -0
  51. package/dist/migration-comments.js.map +1 -0
  52. package/dist/migration-digest-suite.d.ts +3 -0
  53. package/dist/migration-digest-suite.d.ts.map +1 -0
  54. package/dist/migration-digest-suite.js +175 -0
  55. package/dist/migration-digest-suite.js.map +1 -0
  56. package/dist/modules.d.ts +542 -0
  57. package/dist/modules.d.ts.map +1 -1
  58. package/dist/modules.js +155 -7
  59. package/dist/modules.js.map +1 -1
  60. package/dist/peer-suite.d.ts +2 -1
  61. package/dist/peer-suite.d.ts.map +1 -1
  62. package/dist/peer-suite.js +103 -1
  63. package/dist/peer-suite.js.map +1 -1
  64. package/dist/permission-suite.d.ts.map +1 -1
  65. package/dist/permission-suite.js +515 -2
  66. package/dist/permission-suite.js.map +1 -1
  67. package/dist/scope-host-suite.d.ts.map +1 -1
  68. package/dist/scope-host-suite.js +54 -1
  69. package/dist/scope-host-suite.js.map +1 -1
  70. package/dist/split-cases.d.ts +12 -0
  71. package/dist/split-cases.d.ts.map +1 -0
  72. package/dist/split-cases.js +59 -0
  73. package/dist/split-cases.js.map +1 -0
  74. package/dist/subject-erasure-suite.d.ts +6 -0
  75. package/dist/subject-erasure-suite.d.ts.map +1 -0
  76. package/dist/subject-erasure-suite.js +287 -0
  77. package/dist/subject-erasure-suite.js.map +1 -0
  78. package/dist/switch-audit-fault.d.ts +48 -0
  79. package/dist/switch-audit-fault.d.ts.map +1 -0
  80. package/dist/switch-audit-fault.js +80 -0
  81. package/dist/switch-audit-fault.js.map +1 -0
  82. package/dist/system-switch-suite.d.ts +2 -1
  83. package/dist/system-switch-suite.d.ts.map +1 -1
  84. package/dist/system-switch-suite.js +90 -0
  85. package/dist/system-switch-suite.js.map +1 -1
  86. package/dist/vertical-events-suite.d.ts +41 -0
  87. package/dist/vertical-events-suite.d.ts.map +1 -1
  88. package/dist/vertical-events-suite.js +55 -0
  89. package/dist/vertical-events-suite.js.map +1 -1
  90. package/package.json +3 -3
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The statement-boundary cases `splitSqlStatements` is held to (#2068, Codex #2084 r4): SQLite's
3
+ * own boundary shapes (complete.test) plus the three reproductions the review found. The kernel
4
+ * runs each differentially on node:sqlite — whole blob against statement by statement — and the
5
+ * Durable-Object adapter runs each statement by statement in workerd.
6
+ */
7
+ export declare const SPLIT_CASES: readonly {
8
+ name: string;
9
+ sql: string;
10
+ statements: number;
11
+ }[];
12
+ //# sourceMappingURL=split-cases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"split-cases.d.ts","sourceRoot":"","sources":["../src/split-cases.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,EAqDnF,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * The statement-boundary cases `splitSqlStatements` is held to (#2068, Codex #2084 r4): SQLite's
3
+ * own boundary shapes (complete.test) plus the three reproductions the review found. The kernel
4
+ * runs each differentially on node:sqlite — whole blob against statement by statement — and the
5
+ * Durable-Object adapter runs each statement by statement in workerd.
6
+ */
7
+ export const SPLIT_CASES = [
8
+ { name: 'plain statements', sql: 'CREATE TABLE a(x); INSERT INTO a VALUES(1);', statements: 2 },
9
+ { name: 'no terminating semicolon', sql: 'CREATE TABLE a(x); INSERT INTO a VALUES(1)', statements: 2 },
10
+ { name: 'a semicolon in a string', sql: "CREATE TABLE a(x); INSERT INTO a VALUES('a;b');", statements: 2 },
11
+ { name: "a doubled quote in a string", sql: "CREATE TABLE a(x); INSERT INTO a VALUES('it''s; fine');", statements: 2 },
12
+ // Reproduction 2: a `;` inside a quoted identifier.
13
+ { name: 'a semicolon in a "quoted" identifier', sql: 'CREATE TABLE "x;y"(a); INSERT INTO "x;y" VALUES(1);', statements: 2 },
14
+ { name: 'a semicolon in [bracketed] and `backquoted` identifiers', sql: 'CREATE TABLE [a;b](x); CREATE TABLE `c;d`(y);', statements: 2 },
15
+ // Reproduction 3: a comment between two keywords must not glue them.
16
+ { name: 'a comment between keywords', sql: 'CREATE/*c*/TABLE t2(a); INSERT/**/INTO t2 VALUES(1);', statements: 2 },
17
+ { name: 'semicolons in comments', sql: 'CREATE TABLE u(a); /* ; */ INSERT INTO u VALUES(1); -- ;\nINSERT INTO u VALUES(2)', statements: 3 },
18
+ { name: 'a trailing comment only', sql: 'CREATE TABLE a(x); -- the end; really', statements: 1 },
19
+ { name: 'only whitespace and comments', sql: ' -- nothing ;\n /* ; */ ', statements: 0 },
20
+ { name: 'empty statements', sql: ';;CREATE TABLE a(x);;;', statements: 1 },
21
+ {
22
+ name: 'a trigger body',
23
+ sql: 'CREATE TABLE t(a, b); CREATE TRIGGER tr AFTER INSERT ON t BEGIN UPDATE t SET b = 1; UPDATE t SET b = b + 1; END; INSERT INTO t(a) VALUES(1);',
24
+ statements: 3,
25
+ },
26
+ // Reproduction 1: `CASE … END;` inside a trigger body is not the trigger's END.
27
+ {
28
+ name: 'CASE … END inside a trigger body',
29
+ sql: 'CREATE TABLE t(a, b); CREATE TRIGGER tr AFTER INSERT ON t BEGIN ' +
30
+ 'UPDATE t SET b = CASE WHEN new.a > 0 THEN 1 ELSE 0 END; UPDATE t SET b = b + 10; END; ' +
31
+ 'INSERT INTO t(a) VALUES(5);',
32
+ statements: 3,
33
+ },
34
+ {
35
+ name: 'a TEMP trigger, and END as a quoted name',
36
+ sql: 'CREATE TABLE e("end" TEXT); CREATE TEMP TRIGGER te AFTER INSERT ON e BEGIN UPDATE e SET "end" = \'x;\'; END; INSERT INTO e VALUES(\'y\');',
37
+ statements: 3,
38
+ },
39
+ {
40
+ name: 'END as a quoted name inside a trigger body',
41
+ sql: 'CREATE TABLE e2("end" TEXT); CREATE TRIGGER te2 AFTER INSERT ON e2 BEGIN UPDATE e2 SET "end" = \'x;\'; END; INSERT INTO e2 VALUES(\'y\');',
42
+ statements: 3,
43
+ },
44
+ {
45
+ name: 'a trigger whose body raises with a semicolon in the message',
46
+ sql: "CREATE TABLE t(a); CREATE TRIGGER g BEFORE DELETE ON t BEGIN SELECT RAISE(ABORT, 'no; end'); END; INSERT INTO t VALUES(1);",
47
+ statements: 3,
48
+ },
49
+ { name: 'a word that only contains TRIGGER', sql: 'CREATE TABLE triggers(a); INSERT INTO triggers VALUES(1);', statements: 2 },
50
+ { name: 'a non-ASCII identifier', sql: 'CREATE TABLE tråd(ö); INSERT INTO tråd VALUES(1);', statements: 2 },
51
+ {
52
+ name: 'an fts5 table and the derived-index trigger shape',
53
+ sql: "CREATE TABLE d(body); CREATE VIRTUAL TABLE dx USING fts5(body, content='d', content_rowid='rowid');" +
54
+ 'CREATE TRIGGER d_ai AFTER INSERT ON d BEGIN INSERT INTO dx(rowid, body) VALUES (new.rowid, new.body); END;' +
55
+ "INSERT INTO d VALUES('hello; world');",
56
+ statements: 4,
57
+ },
58
+ ];
59
+ //# sourceMappingURL=split-cases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"split-cases.js","sourceRoot":"","sources":["../src/split-cases.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAiE;IACvF,EAAE,IAAI,EAAE,kBAAkB,EAAE,GAAG,EAAE,6CAA6C,EAAE,UAAU,EAAE,CAAC,EAAE;IAC/F,EAAE,IAAI,EAAE,0BAA0B,EAAE,GAAG,EAAE,4CAA4C,EAAE,UAAU,EAAE,CAAC,EAAE;IACtG,EAAE,IAAI,EAAE,yBAAyB,EAAE,GAAG,EAAE,iDAAiD,EAAE,UAAU,EAAE,CAAC,EAAE;IAC1G,EAAE,IAAI,EAAE,6BAA6B,EAAE,GAAG,EAAE,yDAAyD,EAAE,UAAU,EAAE,CAAC,EAAE;IACtH,oDAAoD;IACpD,EAAE,IAAI,EAAE,sCAAsC,EAAE,GAAG,EAAE,qDAAqD,EAAE,UAAU,EAAE,CAAC,EAAE;IAC3H,EAAE,IAAI,EAAE,yDAAyD,EAAE,GAAG,EAAE,+CAA+C,EAAE,UAAU,EAAE,CAAC,EAAE;IACxI,qEAAqE;IACrE,EAAE,IAAI,EAAE,4BAA4B,EAAE,GAAG,EAAE,sDAAsD,EAAE,UAAU,EAAE,CAAC,EAAE;IAClH,EAAE,IAAI,EAAE,wBAAwB,EAAE,GAAG,EAAE,mFAAmF,EAAE,UAAU,EAAE,CAAC,EAAE;IAC3I,EAAE,IAAI,EAAE,yBAAyB,EAAE,GAAG,EAAE,uCAAuC,EAAE,UAAU,EAAE,CAAC,EAAE;IAChG,EAAE,IAAI,EAAE,8BAA8B,EAAE,GAAG,EAAE,0BAA0B,EAAE,UAAU,EAAE,CAAC,EAAE;IACxF,EAAE,IAAI,EAAE,kBAAkB,EAAE,GAAG,EAAE,wBAAwB,EAAE,UAAU,EAAE,CAAC,EAAE;IAC1E;QACE,IAAI,EAAE,gBAAgB;QACtB,GAAG,EAAE,8IAA8I;QACnJ,UAAU,EAAE,CAAC;KACd;IACD,gFAAgF;IAChF;QACE,IAAI,EAAE,kCAAkC;QACxC,GAAG,EACD,kEAAkE;YAClE,wFAAwF;YACxF,6BAA6B;QAC/B,UAAU,EAAE,CAAC;KACd;IACD;QACE,IAAI,EAAE,0CAA0C;QAChD,GAAG,EAAE,2IAA2I;QAChJ,UAAU,EAAE,CAAC;KACd;IACD;QACE,IAAI,EAAE,4CAA4C;QAClD,GAAG,EAAE,2IAA2I;QAChJ,UAAU,EAAE,CAAC;KACd;IACD;QACE,IAAI,EAAE,6DAA6D;QACnE,GAAG,EAAE,4HAA4H;QACjI,UAAU,EAAE,CAAC;KACd;IACD,EAAE,IAAI,EAAE,mCAAmC,EAAE,GAAG,EAAE,2DAA2D,EAAE,UAAU,EAAE,CAAC,EAAE;IAC9H,EAAE,IAAI,EAAE,wBAAwB,EAAE,GAAG,EAAE,mDAAmD,EAAE,UAAU,EAAE,CAAC,EAAE;IAC3G;QACE,IAAI,EAAE,mDAAmD;QACzD,GAAG,EACD,qGAAqG;YACrG,4GAA4G;YAC5G,uCAAuC;QACzC,UAAU,EAAE,CAAC;KACd;CACF,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { type ScopeId, type TenantId } from '@substrat-run/contracts';
2
+ import type { ScopeHostFixture } from './scope-host-suite.js';
3
+ /** Raw SQL on one scope's own database, past `ctx.sql` — reads the FTS shadow tables. */
4
+ export type RawScopeQuery = (tenant: TenantId, scope: ScopeId, sql: string, params?: readonly unknown[]) => Promise<unknown[]>;
5
+ export declare function subjectErasureContractSuite(adapterName: string, makeFixture: () => Promise<ScopeHostFixture>, raw: RawScopeQuery): void;
6
+ //# sourceMappingURL=subject-erasure-suite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subject-erasure-suite.d.ts","sourceRoot":"","sources":["../src/subject-erasure-suite.ts"],"names":[],"mappings":"AAWA,OAAO,EAQL,KAAK,OAAO,EAEZ,KAAK,QAAQ,EACd,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG9D,yFAAyF;AACzF,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;AAI/H,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,EAC5C,GAAG,EAAE,aAAa,GACjB,IAAI,CAgUN"}
@@ -0,0 +1,287 @@
1
+ /**
2
+ * Contract suite for subject erasure inside a module's own tables (#2068): the declared blank
3
+ * and delete, the `onSubjectErased` hook and its reach, the search index, the receipt, and the
4
+ * failure that must leave nothing erased and the key alive.
5
+ *
6
+ * Every adapter owes all of it. The property the whole suite circles is K-37's order, extended:
7
+ * the module rows, the spine and the key go together or not at all, and only a completed
8
+ * erasure is receipted. Each refusal here has its positive twin beside it, so a test cannot
9
+ * pass by an erasure that refuses everything — or by one that reaches nothing.
10
+ */
11
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
12
+ import { dataSubjectId, errorCodeOf, platformActorId, principalId, scopeId, tenantId, } from '@substrat-run/contracts';
13
+ import { SECURE_DELETE_MIN_SQLITE, searchIndexPlans, ulid } from '@substrat-run/kernel';
14
+ import { erasureMisdeclaredMod, erasureMod, erasureModManifest, erasureOtherMod } from './erasure-module.js';
15
+ export function subjectErasureContractSuite(adapterName, makeFixture, raw) {
16
+ describe(`subject erasure in a module's own tables (#2068): ${adapterName}`, () => {
17
+ let fixture;
18
+ let host;
19
+ let stub;
20
+ const t1 = tenantId.parse(ulid());
21
+ const s1 = scopeId.parse(ulid());
22
+ /** A scope nothing in this suite writes to: the erasure there holds nothing for anyone. */
23
+ const subjectErasureFreshScope = scopeId.parse(ulid());
24
+ const staff = platformActorId.parse(ulid());
25
+ const alice = principalId.parse(ulid());
26
+ const indexTable = searchIndexPlans(erasureModManifest.id, erasureModManifest.searchables)[0].indexTable;
27
+ const subject = () => dataSubjectId.parse(ulid());
28
+ const put = (sql, params = []) => stub.invoke('erasure/put', { sql, params });
29
+ const read = (sql, params = []) => stub.invoke('erasure/read', { sql, params });
30
+ const one = async (sql, params = []) => (await read(sql, params))[0];
31
+ const errOf = async (call) => call.then(() => {
32
+ throw new Error('expected a refusal, got an answer');
33
+ }, (e) => e);
34
+ const rowsFor = (receipt, entityType) => receipt.verticalRows.find((r) => r.module === erasureModManifest.id && r.entityType === entityType)?.rows;
35
+ /** One subject's whole footprint in the module, with a decoy that belongs to someone else. */
36
+ const seed = async (who, word = `w${ulid().toLowerCase()}`) => {
37
+ const other = subject();
38
+ const note = ulid();
39
+ const edited = ulid();
40
+ const decoy = ulid();
41
+ await put('INSERT INTO er_people (id, email, name) VALUES (?, ?, ?)', [who, 'p@example.test', 'Pat Person']);
42
+ await put('INSERT INTO er_people (id, email, name) VALUES (?, ?, ?)', [other, 'o@example.test', 'Other Person']);
43
+ await put('INSERT INTO er_notes (id, author, editor, title, body) VALUES (?, ?, NULL, ?, ?)', [note, who, 'mine', `${word} they wrote`]);
44
+ await put('INSERT INTO er_notes (id, author, editor, title, body) VALUES (?, ?, ?, ?, ?)', [edited, other, who, 'edited', 'edited by them']);
45
+ await put('INSERT INTO er_notes (id, author, editor, title, body) VALUES (?, ?, NULL, ?, ?)', [decoy, other, 'theirs', `${word} someone else wrote`]);
46
+ await put('INSERT INTO er_signups (id, kind, email) VALUES (?, ?, ?)', [who, 'news', `${who}@example.test`]);
47
+ await put('INSERT INTO er_signups (id, kind, email) VALUES (?, ?, ?)', [other, 'news', `${other}@example.test`]);
48
+ await put('INSERT INTO er_ratings (note_id, comment) VALUES (?, ?)', [note, 'great service']);
49
+ await put('INSERT INTO er_ratings (note_id, comment) VALUES (?, ?)', [decoy, 'kept']);
50
+ // Unreached by any erasure, so a write to it can only be a hook's.
51
+ await put('INSERT INTO er_loose (id, memo) VALUES (?, ?)', [who, 'untouched']);
52
+ return { other, note, edited, decoy, word };
53
+ };
54
+ beforeAll(async () => {
55
+ fixture = await makeFixture();
56
+ host = fixture.host;
57
+ host.registerModule(erasureMod);
58
+ host.registerModule(erasureOtherMod);
59
+ await host.admin.createTenant(staff, { id: t1, slug: `erasure-${t1.toLowerCase()}`, name: 'Erasure' });
60
+ await host.admin.grantEntitlement(staff, t1, 'erasure');
61
+ await host.provisionScope(staff, { tenantId: t1, scopeId: s1, vertical: 'erasure-vertical' });
62
+ await host.admin.activateScope(staff, t1, s1);
63
+ await host.provisionScope(staff, { tenantId: t1, scopeId: subjectErasureFreshScope, vertical: 'erasure-vertical' });
64
+ await host.admin.activateScope(staff, t1, subjectErasureFreshScope);
65
+ stub = await host.getScope(alice, t1, s1);
66
+ });
67
+ afterAll(async () => {
68
+ await fixture.cleanup();
69
+ });
70
+ it("refuses, at registration, an erasure declared on a table the module's migrations do not create", () => {
71
+ // `er_other` is `@test/erasure-other`'s. Its declared blank would have run against that
72
+ // module's rows through the kernel's handle; the module is refused before it is recorded.
73
+ expect(() => host.registerModule(erasureMisdeclaredMod)).toThrow(/'er_other', which its own migrations do not create/);
74
+ });
75
+ it('records which module created each table, from what its migrations actually did', async () => {
76
+ const owners = (await raw(t1, s1, "SELECT table_name, module_id, source FROM _substrat_table_owners WHERE table_name IN ('er_notes', 'er_other') ORDER BY table_name"));
77
+ expect(owners).toEqual([
78
+ { table_name: 'er_notes', module_id: '@test/erasure', source: 'migration' },
79
+ { table_name: 'er_other', module_id: '@test/erasure-other', source: 'migration' },
80
+ ]);
81
+ // A view is not a table anyone owns.
82
+ expect(await raw(t1, s1, "SELECT 1 FROM _substrat_table_owners WHERE table_name = 'er_view'")).toEqual([]);
83
+ });
84
+ it('backfills ownership from the migration journal for a scope migrated before it was recorded', async () => {
85
+ // As a scope that ran its migrations before #2068 has it: no ownership rows at all.
86
+ await raw(t1, s1, 'DELETE FROM _substrat_table_owners RETURNING table_name');
87
+ const who = subject();
88
+ await seed(who);
89
+ const receipt = await host.admin.shredSubject(staff, t1, s1, who);
90
+ expect(rowsFor(receipt, 'erperson')).toBe(1);
91
+ const owners = (await raw(t1, s1, "SELECT table_name, module_id, source FROM _substrat_table_owners WHERE table_name IN ('er_people', 'er_ratings') ORDER BY table_name"));
92
+ expect(owners).toEqual([
93
+ { table_name: 'er_people', module_id: '@test/erasure', source: 'journal' },
94
+ { table_name: 'er_ratings', module_id: '@test/erasure', source: 'journal' },
95
+ ]);
96
+ });
97
+ it('blanks every declared erasable column on every row a subject column names, and only those', async () => {
98
+ const who = subject();
99
+ const { other, note, edited, decoy } = await seed(who);
100
+ const receipt = await host.admin.shredSubject(staff, t1, s1, who);
101
+ // NULL where the field admits it, '' where it does not — the row and its keys stay.
102
+ expect(await one('SELECT email, name FROM er_people WHERE id = ?', [who])).toEqual({ email: null, name: '' });
103
+ expect(await one('SELECT title, body, author FROM er_notes WHERE id = ?', [note])).toEqual({ title: null, body: '', author: who });
104
+ // The SECOND subject column reaches too: a note they edited is theirs to erase.
105
+ expect(await one('SELECT title, body FROM er_notes WHERE id = ?', [edited])).toEqual({ title: null, body: '' });
106
+ // Someone else's rows are untouched — an erasure that over-reaches is its own bug.
107
+ expect(await one('SELECT email, name FROM er_people WHERE id = ?', [other])).toEqual({ email: 'o@example.test', name: 'Other Person' });
108
+ expect(await one('SELECT title FROM er_notes WHERE id = ?', [decoy])).toEqual({ title: 'theirs' });
109
+ expect(rowsFor(receipt, 'erperson')).toBe(1);
110
+ expect(rowsFor(receipt, 'ernote')).toBe(2);
111
+ });
112
+ it('deletes a delete-mode row outright, and keeps the next subject\'s', async () => {
113
+ const who = subject();
114
+ const { other } = await seed(who);
115
+ const receipt = await host.admin.shredSubject(staff, t1, s1, who);
116
+ expect(await read('SELECT id FROM er_signups WHERE id = ?', [who])).toEqual([]);
117
+ expect(await read('SELECT id FROM er_signups WHERE id = ?', [other])).toHaveLength(1);
118
+ expect(receipt.verticalRows.find((r) => r.entityType === 'ersignup')).toMatchObject({ mode: 'delete', rows: 1 });
119
+ });
120
+ it("runs the module's hook for the link a row does not hold, and reports what it changed", async () => {
121
+ const who = subject();
122
+ const { note, decoy } = await seed(who);
123
+ const receipt = await host.admin.shredSubject(staff, t1, s1, who);
124
+ expect(await one('SELECT comment FROM er_ratings WHERE note_id = ?', [note])).toEqual({ comment: null });
125
+ expect(await one('SELECT comment FROM er_ratings WHERE note_id = ?', [decoy])).toEqual({ comment: 'kept' });
126
+ expect(receipt.hookRows).toContainEqual({ module: erasureModManifest.id, rows: 1 });
127
+ });
128
+ it('names every erasable entity nothing reaches, on every receipt', async () => {
129
+ const receipt = await host.admin.shredSubject(staff, t1, s1, subject());
130
+ expect(receipt.unreachedEntities).toContainEqual({ module: erasureModManifest.id, entityType: 'erloose' });
131
+ // A declared entity is reported reached even when it held nothing — the zero is the fact.
132
+ expect(rowsFor(receipt, 'erperson')).toBe(0);
133
+ expect(receipt.unreachedEntities.find((u) => u.entityType === 'erperson')).toBeUndefined();
134
+ });
135
+ it('is idempotent — a re-run changes nothing and counts zero everywhere', async () => {
136
+ const who = subject();
137
+ await seed(who);
138
+ const first = await host.admin.shredSubject(staff, t1, s1, who);
139
+ expect(rowsFor(first, 'ernote')).toBe(2);
140
+ const again = await host.admin.shredSubject(staff, t1, s1, who);
141
+ for (const line of again.verticalRows.filter((r) => r.module === erasureModManifest.id))
142
+ expect(line.rows).toBe(0);
143
+ expect(again.hookRows).toContainEqual({ module: erasureModManifest.id, rows: 0 });
144
+ });
145
+ it('takes the erased words out of the search index AND out of its stored segments', async () => {
146
+ const who = subject();
147
+ const theirs = subject();
148
+ // One word only the subject wrote, one only someone else did. Checked by the word's random
149
+ // TAIL: FTS5 stores a term prefix-compressed against its neighbour, so its leading bytes
150
+ // may be shared, but a run of random characters at its end is stored as written.
151
+ const word = () => `q${crypto.randomUUID().replace(/-/g, '')}`;
152
+ const solo = word();
153
+ const kept = word();
154
+ await put('INSERT INTO er_notes (id, author, editor, title, body) VALUES (?, ?, NULL, NULL, ?)', [ulid(), who, `${solo} private`]);
155
+ await put('INSERT INTO er_notes (id, author, editor, title, body) VALUES (?, ?, NULL, NULL, ?)', [ulid(), theirs, `${kept} public`]);
156
+ const hits = async (term) => (await stub.invoke('erasure/search', { term })).length;
157
+ const stored = async (term) => (await raw(t1, s1, `SELECT count(*) AS n FROM ${indexTable}_data WHERE instr(block, CAST(? AS BLOB)) > 0`, [
158
+ term.slice(-12),
159
+ ]))[0].n;
160
+ // The positive twin: before, the word is found and its bytes are in the segments — so the
161
+ // scan below can see a term when there is one to see.
162
+ expect(await hits(solo)).toBe(1);
163
+ expect(await stored(solo)).toBeGreaterThan(0);
164
+ await host.admin.shredSubject(staff, t1, s1, who);
165
+ // Gone from the results AND from the stored segments: a plain FTS5 delete leaves the term
166
+ // in the older segment until a merge, readable by anyone with the database file.
167
+ expect(await hits(solo)).toBe(0);
168
+ expect(await stored(solo)).toBe(0);
169
+ // The index still works for everyone else.
170
+ expect(await hits(kept)).toBe(1);
171
+ expect(await stored(kept)).toBeGreaterThan(0);
172
+ });
173
+ it(`runs on SQLite ${SECURE_DELETE_MIN_SQLITE}+: the index it deleted from is at FTS5 format 5, and still reads`, async () => {
174
+ // The proof of the floor on the runtime this adapter actually runs — a Durable Object will
175
+ // not answer `sqlite_version()`, so the format the index is left at is the evidence: only
176
+ // an FTS5 with secure-delete (3.42.0+) writes version 5, and the same runtime then reading
177
+ // the index back is the reader that matters (a dump never carries the index).
178
+ const who = subject();
179
+ const kept = `q${crypto.randomUUID().replace(/-/g, '')}`;
180
+ await seed(who);
181
+ await put('INSERT INTO er_notes (id, author, editor, title, body) VALUES (?, ?, NULL, NULL, ?)', [ulid(), subject(), kept]);
182
+ await host.admin.shredSubject(staff, t1, s1, who);
183
+ const version = (await raw(t1, s1, `SELECT v FROM ${indexTable}_config WHERE k = 'version'`));
184
+ expect(version).toEqual([{ v: 5 }]);
185
+ expect(await stub.invoke('erasure/search', { term: kept })).toHaveLength(1);
186
+ // Named where the runtime will say it, for the record in the test log.
187
+ const named = await raw(t1, s1, 'SELECT sqlite_version() AS v').then((rows) => rows[0].v, () => 'not exposed');
188
+ console.info(`[#2068] ${adapterName}: SQLite ${named}, FTS5 index format ${version[0].v}`);
189
+ });
190
+ it('switches secure-delete on only for an index whose table it changes, and leaves it off', async () => {
191
+ const configOf = async () => (await raw(t1, s1, `SELECT v FROM ${indexTable}_config WHERE k = 'secure-delete'`));
192
+ // An erasure that holds nothing here never touches the index: the switch leaves a row in
193
+ // the index's config that SQLite lets nobody remove, so a scope the erasure is not about
194
+ // must read exactly as it did — a fork or restore rebuilds the index without that row.
195
+ const fresh = subjectErasureFreshScope;
196
+ await host.admin.shredSubject(staff, t1, fresh, subject());
197
+ expect(await raw(t1, fresh, `SELECT k FROM ${indexTable}_config WHERE k = 'secure-delete'`)).toEqual([]);
198
+ // One that changed a searchable row switched it on for the erasure, and off again after.
199
+ const who = subject();
200
+ await seed(who);
201
+ await host.admin.shredSubject(staff, t1, s1, who);
202
+ expect((await configOf()).map((c) => c.v)).toEqual([0]);
203
+ });
204
+ describe('a hook that fails', () => {
205
+ /** A subject with a classified event, a sealed copy and module rows — everything an erasure reaches. */
206
+ const armed = async (kind) => {
207
+ const who = subject();
208
+ await seed(who);
209
+ await stub.invoke('erasure/emit', { subject: who, secret: 'said-about-them' });
210
+ const [sealed] = await host.admin.sealSubjectPayloads(staff, t1, s1, [{ subjectId: who, plaintext: 'in the backup' }]);
211
+ await put('INSERT INTO er_bombs (subject, kind) VALUES (?, ?)', [who, kind]);
212
+ return { who, sealed: sealed };
213
+ };
214
+ /** Nothing was erased: the module rows, the spine payload and the key all survive. */
215
+ const intact = async (who, sealed) => {
216
+ expect(await one('SELECT email FROM er_people WHERE id = ?', [who])).toEqual({ email: 'p@example.test' });
217
+ expect(await read('SELECT id FROM er_signups WHERE id = ?', [who])).toHaveLength(1);
218
+ const spine = (await raw(t1, s1, 'SELECT payload FROM _substrat_outbox WHERE subject_id = ?', [who]));
219
+ expect(spine[0]?.payload).toContain('said-about-them');
220
+ expect(await host.admin.openSubjectPayloads(staff, t1, s1, [{ subjectId: who, sealed }])).toEqual(['in the backup']);
221
+ };
222
+ it('refuses the whole erasure — module rows, spine and key all survive — and a re-run once fixed converges', async () => {
223
+ const { who, sealed } = await armed('throw');
224
+ await expect(host.admin.shredSubject(staff, t1, s1, who)).rejects.toThrow(/the hook failed half-way/);
225
+ await intact(who, sealed);
226
+ // The positive twin: with the fault removed, the same erasure completes and reaches all three.
227
+ await put('DELETE FROM er_bombs WHERE subject = ?', [who]);
228
+ const receipt = await host.admin.shredSubject(staff, t1, s1, who);
229
+ expect(receipt.eventsRedacted).toBe(1);
230
+ expect(rowsFor(receipt, 'erperson')).toBe(1);
231
+ expect(await one('SELECT email FROM er_people WHERE id = ?', [who])).toEqual({ email: null });
232
+ expect(await host.admin.openSubjectPayloads(staff, t1, s1, [{ subjectId: who, sealed }])).toEqual([null]);
233
+ });
234
+ for (const [kind, what] of [
235
+ ['spine', 'a read of the spine'],
236
+ ['foreign', "a read of another module's table"],
237
+ ['pragma', 'a PRAGMA'],
238
+ ['chained', "a write to another module's table chained after its own"],
239
+ ['view', "a read of a view, even one the module's migration made"],
240
+ ['cte', "a CTE that shadows one of its own tables' names"],
241
+ ['comma', "another module's table joined by a comma after a subquery"],
242
+ ]) {
243
+ it(`refuses ${what} from inside the hook, before it runs, and erases nothing`, async () => {
244
+ const { who, sealed } = await armed(kind);
245
+ const err = await errOf(host.admin.shredSubject(staff, t1, s1, who));
246
+ expect(errorCodeOf(err)).toBe('forbidden');
247
+ expect(String(err.message)).toMatch(/onSubjectErased/);
248
+ await intact(who, sealed);
249
+ });
250
+ }
251
+ it('a refused async hook writes nothing after the await — its ctx is revoked when it returns', async () => {
252
+ const { who, sealed } = await armed('async-write');
253
+ const err = await errOf(host.admin.shredSubject(staff, t1, s1, who));
254
+ expect(errorCodeOf(err)).toBe('precondition_failed');
255
+ // Give the continuation every chance to have run: another round trip through the scope.
256
+ await read('SELECT 1');
257
+ expect(await one('SELECT memo FROM er_loose WHERE id = ?', [who])).toEqual({ memo: 'untouched' });
258
+ await intact(who, sealed);
259
+ });
260
+ it('a handle a hook stashed on a global is dead once the hook returns', async () => {
261
+ const who = subject();
262
+ await seed(who);
263
+ await put('INSERT INTO er_bombs (subject, kind) VALUES (?, ?)', [who, 'stash']);
264
+ // The erasure itself completes — stashing is not an error until the handle is used.
265
+ await host.admin.shredSubject(staff, t1, s1, who);
266
+ const err = await errOf(stub.invoke('erasure/use-stash', { subject: who }));
267
+ expect(errorCodeOf(err)).toBe('forbidden');
268
+ expect(String(err.message)).toMatch(/used after the hook returned/);
269
+ expect(await one('SELECT memo FROM er_loose WHERE id = ?', [who])).toEqual({ memo: 'untouched' });
270
+ });
271
+ for (const [kind, what] of [
272
+ ['async', 'a promise (an async hook)'],
273
+ ['generator', 'an iterator (a generator hook, whose body never ran)'],
274
+ ['value', 'a value'],
275
+ ]) {
276
+ it(`refuses a hook that returns ${what} — it must return nothing — and erases nothing`, async () => {
277
+ const { who, sealed } = await armed(kind);
278
+ const err = await errOf(host.admin.shredSubject(staff, t1, s1, who));
279
+ expect(errorCodeOf(err)).toBe('precondition_failed');
280
+ await intact(who, sealed);
281
+ expect(await one('SELECT memo FROM er_loose WHERE id = ?', [who])).toEqual({ memo: 'untouched' });
282
+ });
283
+ }
284
+ });
285
+ });
286
+ }
287
+ //# sourceMappingURL=subject-erasure-suite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subject-erasure-suite.js","sourceRoot":"","sources":["../src/subject-erasure-suite.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { AdminAction, PlatformActorId, ScopeId, TenantId } from '@substrat-run/contracts';
2
+ import { type ScopeHost } from '@substrat-run/kernel';
3
+ /**
4
+ * #2089: the fault a kill-switch suite (and, #2114, the replay lever's) injects to make an OUTCOME row fail — the
5
+ * directory refusing admin-log rows of one phase on one scope, until the returned function
6
+ * lifts it. Each adapter's fixture runs `adminRowFaultSql` against its own directory, so the
7
+ * write fails where production's would: in the store, not in a mocked method.
8
+ */
9
+ export interface AdminRowFault {
10
+ refuseAdminRows(scopeId: ScopeId, phase: 'applied' | 'refused' | 'failed'): Promise<() => Promise<void>>;
11
+ refuseSwitchRecord(scopeId: ScopeId, kind: 'system' | 'peer'): Promise<() => Promise<void>>;
12
+ }
13
+ /** The trigger both adapters' fixtures install, and the statement that drops it. */
14
+ export declare function adminRowFaultSql(scopeId: string, phase: string): {
15
+ create: string;
16
+ drop: string;
17
+ };
18
+ /** Reject a switch's directory write before the scope moves, to drive its `failed` outcome. */
19
+ export declare function switchRecordFaultSql(scopeId: string, kind: 'system' | 'peer'): {
20
+ create: string[];
21
+ drop: string[];
22
+ };
23
+ /** Run `call` with `phase` refused on `scopeId`, capturing the error log; the fault is lifted after. */
24
+ export declare function withRefusedOutcome<T>(fault: Pick<AdminRowFault, 'refuseAdminRows'>, scopeId: ScopeId, phase: 'applied' | 'refused' | 'failed', call: () => Promise<T>): Promise<{
25
+ settled: PromiseSettledResult<T>;
26
+ unrecorded: Record<string, unknown>[];
27
+ }>;
28
+ /**
29
+ * The admin-log half of the assertion: the operation holds only its intent, the host's settle
30
+ * closes it as `unknown` (the intent's own fields, plus why) with its ops-failure row, and a
31
+ * second settle writes nothing.
32
+ */
33
+ export declare function expectSettledUnknown(host: ScopeHost, staff: PlatformActorId, where: {
34
+ tenantId: TenantId;
35
+ scopeId: ScopeId;
36
+ action: AdminAction;
37
+ operationId: string;
38
+ }, subject: Record<string, unknown>): Promise<void>;
39
+ /** The twin: the outcome landed, so the settle finds nothing to close. */
40
+ export declare function expectAnswered(host: ScopeHost, staff: PlatformActorId, where: {
41
+ tenantId: TenantId;
42
+ scopeId: ScopeId;
43
+ action: AdminAction;
44
+ operationId: string;
45
+ }, phase: 'applied' | 'refused' | 'failed'): Promise<void>;
46
+ /** Pin adapter warning propagation independently of the store's error-message formatting. */
47
+ export declare function withEmptyOutcomeError<T>(call: () => Promise<T>): Promise<T>;
48
+ //# sourceMappingURL=switch-audit-fault.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"switch-audit-fault.d.ts","sourceRoot":"","sources":["../src/switch-audit-fault.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAC/F,OAAO,EAA0B,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAE9E;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACzG,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;CAC7F;AAED,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAQjG;AAED,+FAA+F;AAC/F,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAWnH;AAED,wGAAwG;AACxG,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,iBAAiB,CAAC,EAC7C,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,EACvC,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACrB,OAAO,CAAC;IAAE,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;CAAE,CAAC,CAatF;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,SAAS,EACf,KAAK,EAAE,eAAe,EACtB,KAAK,EAAE;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EACzF,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC,CAiBf;AAED,0EAA0E;AAC1E,wBAAsB,cAAc,CAClC,IAAI,EAAE,SAAS,EACf,KAAK,EAAE,eAAe,EACtB,KAAK,EAAE;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EACzF,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,GACtC,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,6FAA6F;AAC7F,wBAAsB,qBAAqB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAOjF"}
@@ -0,0 +1,80 @@
1
+ import { expect, vi } from 'vitest';
2
+ import * as kernel from '@substrat-run/kernel';
3
+ import { UNRECORDED_OUTCOME_LOG } from '@substrat-run/kernel';
4
+ /** The trigger both adapters' fixtures install, and the statement that drops it. */
5
+ export function adminRowFaultSql(scopeId, phase) {
6
+ const name = `test_refuse_${phase}_${scopeId.toLowerCase()}`;
7
+ return {
8
+ create: `CREATE TRIGGER ${name} BEFORE INSERT ON _substrat_admin_log
9
+ WHEN NEW.scope_id = '${scopeId}' AND json_extract(NEW.after, '$.phase') = '${phase}'
10
+ BEGIN SELECT RAISE(ABORT, 'the admin log refused the ${phase} row (test fault)'); END`,
11
+ drop: `DROP TRIGGER IF EXISTS ${name}`,
12
+ };
13
+ }
14
+ /** Reject a switch's directory write before the scope moves, to drive its `failed` outcome. */
15
+ export function switchRecordFaultSql(scopeId, kind) {
16
+ const table = kind === 'system' ? '_substrat_system_switches' : '_substrat_peer_switches';
17
+ const name = `test_refuse_${kind}_record_${scopeId.toLowerCase()}`;
18
+ const fault = `SELECT RAISE(ABORT, 'the ${kind} switch record was refused (test fault)')`;
19
+ return {
20
+ create: ['INSERT', 'UPDATE'].map((verb) => `CREATE TRIGGER ${name}_${verb.toLowerCase()} BEFORE ${verb} ON ${table}
21
+ WHEN NEW.scope_id = '${scopeId}' BEGIN ${fault}; END`),
22
+ drop: ['insert', 'update'].map((verb) => `DROP TRIGGER IF EXISTS ${name}_${verb}`),
23
+ };
24
+ }
25
+ /** Run `call` with `phase` refused on `scopeId`, capturing the error log; the fault is lifted after. */
26
+ export async function withRefusedOutcome(fault, scopeId, phase, call) {
27
+ const lift = await fault.refuseAdminRows(scopeId, phase);
28
+ const log = vi.spyOn(console, 'error').mockImplementation(() => undefined);
29
+ try {
30
+ const [settled] = await Promise.allSettled([call()]);
31
+ const unrecorded = log.mock.calls
32
+ .filter(([message]) => message === UNRECORDED_OUTCOME_LOG)
33
+ .map(([, fields]) => fields);
34
+ return { settled: settled, unrecorded };
35
+ }
36
+ finally {
37
+ log.mockRestore();
38
+ await lift();
39
+ }
40
+ }
41
+ /**
42
+ * The admin-log half of the assertion: the operation holds only its intent, the host's settle
43
+ * closes it as `unknown` (the intent's own fields, plus why) with its ops-failure row, and a
44
+ * second settle writes nothing.
45
+ */
46
+ export async function expectSettledUnknown(host, staff, where, subject) {
47
+ const rows = async () => (await host.admin.auditLog(staff, { tenantId: where.tenantId, scopeId: where.scopeId, action: where.action }))
48
+ .filter((r) => r.after.operationId === where.operationId);
49
+ const before = await rows();
50
+ expect(before.map((r) => r.after.phase)).toEqual(['intent']);
51
+ const error = 'no outcome was recorded (test)';
52
+ expect(await host.admin.settleUnrecordedOutcome(staff, { intentId: before[0].id, error })).toBe(true);
53
+ const after = await rows();
54
+ expect(after.map((r) => r.after)).toEqual([
55
+ { ...subject, operationId: where.operationId, phase: 'intent', reason: expect.any(String) },
56
+ { ...subject, operationId: where.operationId, phase: 'unknown', reason: expect.any(String), error },
57
+ ]);
58
+ const failures = (await host.admin.listOpsFailures(staff, { tenantId: where.tenantId, operation: `audit.${where.action}` }))
59
+ .filter((f) => f.reference === where.operationId);
60
+ expect(failures.map((f) => [f.stage, f.scopeId])).toEqual([['outcome-unknown', where.scopeId]]);
61
+ expect(await host.admin.settleUnrecordedOutcome(staff, { intentId: before[0].id, error })).toBe(false);
62
+ }
63
+ /** The twin: the outcome landed, so the settle finds nothing to close. */
64
+ export async function expectAnswered(host, staff, where, phase) {
65
+ const rows = (await host.admin.auditLog(staff, { tenantId: where.tenantId, scopeId: where.scopeId, action: where.action }))
66
+ .filter((r) => r.after.operationId === where.operationId);
67
+ expect(rows.map((r) => r.after.phase)).toEqual(['intent', phase]);
68
+ expect(await host.admin.settleUnrecordedOutcome(staff, { intentId: rows[0].id, error: 'x' })).toBe(false);
69
+ }
70
+ /** Pin adapter warning propagation independently of the store's error-message formatting. */
71
+ export async function withEmptyOutcomeError(call) {
72
+ const outcome = vi.spyOn(kernel, 'recordAuditOutcome').mockResolvedValue('');
73
+ try {
74
+ return await call();
75
+ }
76
+ finally {
77
+ outcome.mockRestore();
78
+ }
79
+ }
80
+ //# sourceMappingURL=switch-audit-fault.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"switch-audit-fault.js","sourceRoot":"","sources":["../src/switch-audit-fault.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import type { ScopeHostFixture } from './scope-host-suite.js';
2
+ import { type AdminRowFault } from './switch-audit-fault.js';
2
3
  /**
3
4
  * The schedule kill switch (#1666), against both adapters — `revokeFromSystem` turns one
4
5
  * module's scheduled work off on one scope, `restoreToSystem` turns it back on, and the
@@ -12,5 +13,5 @@ import type { ScopeHostFixture } from './scope-host-suite.js';
12
13
  * enumerates every active scope in the directory, and on the Cloudflare mount that is
13
14
  * every suite's scope in one shared control plane (#1591).
14
15
  */
15
- export declare function systemSwitchContractSuite(adapterName: string, makeFixture: () => Promise<ScopeHostFixture>): void;
16
+ export declare function systemSwitchContractSuite(adapterName: string, makeFixture: () => Promise<ScopeHostFixture & AdminRowFault>): void;
16
17
  //# sourceMappingURL=system-switch-suite.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"system-switch-suite.d.ts","sourceRoot":"","sources":["../src/system-switch-suite.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAQ9D;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,GAC3C,IAAI,CAwyBN"}
1
+ {"version":3,"file":"system-switch-suite.d.ts","sourceRoot":"","sources":["../src/system-switch-suite.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAmF,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAQ9I;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,OAAO,CAAC,gBAAgB,GAAG,aAAa,CAAC,GAC3D,IAAI,CA24BN"}