Skip to content

Commit 69a1689

Browse files
committed
test(cli): the secret guards' stack doubles carry SchemaStack.tableAbsent, as the real boot returns it
The two mocked-boot suites built a stack with only kernel and shutdown, so the doors' first ask (tableAbsent) threw 'stack.tableAbsent is not a function' and every case fell into the scan_failed catch. The doubles now follow the real SchemaStack shape; rewrap.guards also pins the not-asked dry run. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016GiHYRmLSNWTfbX9gVQkpz
1 parent 491087e commit 69a1689

2 files changed

Lines changed: 47 additions & 6 deletions

File tree

‎packages/cli/src/commands/secret/orphans.guards.test.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ async function runDelete(
199199
: name === 'settings' ? { listManifests: () => SETTINGS_MANIFESTS }
200200
: undefined,
201201
},
202+
// `SchemaStack.tableAbsent`: nothing is deferred on a `--delete` boot, which is
203+
// the plain one, so no table is measured absent.
204+
tableAbsent: () => false,
202205
shutdown: async () => { /* nothing was booted */ },
203206
} as never;
204207
});

‎packages/cli/src/commands/secret/rewrap.guards.test.ts‎

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,24 @@ function freshRows(): { secrets: Row[]; settings: Row[] } {
6969
interface Harness {
7070
secrets: Row[];
7171
writes: Array<{ where: Row; data: Row }>;
72+
/** Every table a driver read was issued for, in order. */
73+
reads: string[];
7274
}
7375

74-
/** Wire the mocked boot to a fake engine over `rows`. */
75-
function wireBoot(rows: { secrets: Row[]; settings: Row[] }, opts: { conditionalWrite?: boolean } = {}): Harness {
76-
const harness: Harness = { secrets: rows.secrets, writes: [] };
76+
/**
77+
* Wire the mocked boot to a fake engine over `rows`.
78+
*
79+
* `absent` names the tables the boot MEASURED absent, which is what the stack's
80+
* `tableAbsent` answers (`SchemaStack.tableAbsent`). The default is none: every
81+
* table exists, as on a plain `--apply` boot, where nothing is deferred.
82+
*/
83+
function wireBoot(
84+
rows: { secrets: Row[]; settings: Row[] },
85+
opts: { conditionalWrite?: boolean; absent?: readonly string[] } = {},
86+
): Harness {
87+
const harness: Harness = { secrets: rows.secrets, writes: [], reads: [] };
7788
const secretDriver: Record<string, unknown> = {
78-
async find() { return harness.secrets.map((r) => ({ ...r })); },
89+
async find() { harness.reads.push('sys_secret'); return harness.secrets.map((r) => ({ ...r })); },
7990
};
8091
if (opts.conditionalWrite !== false) {
8192
secretDriver.updateMany = async (_object: string, query: { where: Row }, data: Row) => {
@@ -94,13 +105,17 @@ function wireBoot(rows: { secrets: Row[]; settings: Row[] }, opts: { conditional
94105
listDatasourceDefs: () => [],
95106
getDriverForObject: (object: string) => {
96107
if (object === 'sys_secret') return secretDriver;
97-
if (object === 'sys_setting') return { async find() { return rows.settings.map((r) => ({ ...r })); } };
98-
if (object === 'sys_metadata') return { async find() { return []; } };
108+
if (object === 'sys_setting') {
109+
return { async find() { harness.reads.push('sys_setting'); return rows.settings.map((r) => ({ ...r })); } };
110+
}
111+
if (object === 'sys_metadata') return { async find() { harness.reads.push('sys_metadata'); return []; } };
99112
return undefined;
100113
},
101114
};
115+
const absent = new Set(opts.absent ?? []);
102116
vi.mocked(bootSchemaStack).mockResolvedValue({
103117
kernel: { getService: (name: string) => (name === 'objectql' ? engine : undefined) },
118+
tableAbsent: (objectName: string) => absent.has(objectName),
104119
shutdown: async () => {},
105120
} as never);
106121
return harness;
@@ -231,6 +246,29 @@ describe('os secret rewrap — guards that stop a run before any row is opened o
231246
expect(text).not.toContain(KEY_HEX);
232247
}, 60_000);
233248

249+
it('a dry run over tables the boot measured absent reads none of them, and reports empty work', async () => {
250+
const h = wireBoot(freshRows(), { absent: ['sys_secret', 'sys_setting', 'sys_metadata'] });
251+
const { payload, exitCode } = await run(['--no-declared-datasources']);
252+
253+
// "Not asked": a table that does not exist holds nothing, so no read is issued.
254+
expect(h.reads).toEqual([]);
255+
expect(payload.mode).toBe('dry-run');
256+
expect(payload.report.counts).toEqual({ total: 0, rewrap: 0, done: 0, left: 0, refused: 0, notWritten: 0 });
257+
// The union is enumerated, not gapped: an absent table holds no reference.
258+
for (const family of Object.values(payload.report.families) as Array<{ status: string }>) {
259+
expect(family.status).toBe('enumerated');
260+
}
261+
expect(payload.report.refusal).toBeNull();
262+
expect(exitCode).toBe(0);
263+
expect(h.writes).toEqual([]);
264+
265+
// POSITIVE CONTROL: the same rows, tables present — they are read, and the row is planned.
266+
const present = wireBoot(freshRows());
267+
const ok = await run(['--no-declared-datasources']);
268+
expect(present.reads).toContain('sys_secret');
269+
expect(ok.payload.report.counts.total).toBe(1);
270+
}, 60_000);
271+
234272
it('an unreadable --declared-datasources file is refused before the boot, never read as []', async () => {
235273
const dir = mkdtempSync(join(tmpdir(), 'os-rewrap-ds-'));
236274
try {

0 commit comments

Comments
 (0)