bulk-delete backup snapshots, and forget a config's snapshots on delete

This commit is contained in:
Jarek Krochmalski committed 2026-09-13 14:50:02 +02:00
1 parent 0ca3b86f82
commit cd811d5117
14 files changed
+831 -34

No files matched your search

+28
View File
@@ -8,6 +8,7 @@ import {
assertInstanceOwned,
assertEnvAccess,
guardSnapshotAccess,
selectOwnedForForget,
listSnapshots,
resolveSnapshotEnvId,
filterSnapshotsByAccessibleEnv,
@@ -102,6 +103,33 @@ describe('guardSnapshotAccess — instance AND env, in order', () => {
});
});
describe('selectOwnedForForget — the bulk-delete preVetted contract', () => {
const ids = ['a'.repeat(64), 'b'.repeat(64), 'c'.repeat(64)];
it('preVetted forwards every id and NEVER calls the guard (route already vetted them)', async () => {
let guardCalls = 0;
const guard = async () => { guardCalls++; };
const out = await selectOwnedForForget(ids, { preVetted: true, guard });
expect(out.owned).toEqual(ids);
expect(out.skipped).toEqual([]);
expect(guardCalls).toBe(0); // the whole point: no per-id ownership re-listing
});
it('without preVetted, a guard rejection drops that id to skipped (never forgotten)', async () => {
// 'b' is not owned / not accessible -> the guard throws -> it must be skipped, not owned.
const guard = async (id: string) => { if (id === ids[1]) throw new Error('not owned'); };
const out = await selectOwnedForForget(ids, { preVetted: false, guard });
expect(out.owned).toEqual([ids[0], ids[2]]);
expect(out.skipped).toEqual([ids[1]]);
});
it('without preVetted, all-rejected yields no owned ids (nothing reaches restic)', async () => {
const out = await selectOwnedForForget(ids, { preVetted: false, guard: async () => { throw new Error('x'); } });
expect(out.owned).toEqual([]);
expect(out.skipped).toEqual(ids);
});
});
describe('listSnapshots — filters to accessible environments (enterprise)', () => {
const snaps = [
{ id: 'x'.repeat(64), tags: ['dockhand:instance=me', 'dockhand:envid=3'] },