moving
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled

This commit is contained in:
Oleg Maslov
2026-09-02 10:10:29 +02:00
commit 0c3e2ead3b
3841 changed files with 970576 additions and 0 deletions

View File

@@ -0,0 +1,278 @@
/**
* Pure-helper tests for the data-erase flow.
*
* These cover the safety-critical primitives that the route + boot wipe
* sit on top of:
* - confirmation gate (header + exact phrase)
* - snapshot accuracy (file count + bytes)
* - safe-to-wipe assertion (refuses non-Waggle dirs, refuses cwd, refuses root)
* - path-escape resistance during the wipe walk (symlinks, ..)
* - marker round-trip (write then read)
* - performWipe receipt correctness
*
* These are pure-function tests on a tmp dir — they do NOT touch any
* server, DB, or network. The integration with the route lives in
* data-erase.test.ts; the integration with service startup lives
* implicitly in startService (smoke-tested when first-run.test.ts runs).
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import {
validateEraseConfirmation,
snapshotDataDir,
assertDataDirIsSafeToWipe,
writeEraseMarker,
readEraseMarker,
performWipe,
writeWipeReceipt,
ERASE_CONFIRMATION_PHRASE,
ERASE_CONFIRMATION_HEADER_VALUE,
ERASE_MARKER_FILENAME,
type EraseMarker,
} from '../src/local/data-erase-helpers.js';
function mkTmp(name: string): string {
return fs.mkdtempSync(path.join(os.tmpdir(), `waggle-${name}-`));
}
describe('validateEraseConfirmation', () => {
it('accepts the exact header + body phrase', () => {
const r = validateEraseConfirmation(
{ 'x-confirm-erase': ERASE_CONFIRMATION_HEADER_VALUE },
{ confirmation: ERASE_CONFIRMATION_PHRASE },
);
expect(r.ok).toBe(true);
});
it('rejects missing header', () => {
const r = validateEraseConfirmation({}, { confirmation: ERASE_CONFIRMATION_PHRASE });
expect(r.ok).toBe(false);
expect(r.error).toMatch(/X-Confirm-Erase/);
});
it('rejects header with wrong value', () => {
const r = validateEraseConfirmation(
{ 'x-confirm-erase': 'YES' }, // wrong case
{ confirmation: ERASE_CONFIRMATION_PHRASE },
);
expect(r.ok).toBe(false);
expect(r.error).toMatch(/X-Confirm-Erase/);
});
it('rejects body with wrong phrase', () => {
const r = validateEraseConfirmation(
{ 'x-confirm-erase': ERASE_CONFIRMATION_HEADER_VALUE },
{ confirmation: 'i understand this is permanent' }, // wrong case
);
expect(r.ok).toBe(false);
expect(r.error).toMatch(/exact phrase/);
});
it('rejects body that is not a JSON object', () => {
const r = validateEraseConfirmation(
{ 'x-confirm-erase': ERASE_CONFIRMATION_HEADER_VALUE },
'I UNDERSTAND THIS IS PERMANENT',
);
expect(r.ok).toBe(false);
expect(r.error).toMatch(/JSON object/);
});
it('rejects body with non-string confirmation field', () => {
const r = validateEraseConfirmation(
{ 'x-confirm-erase': ERASE_CONFIRMATION_HEADER_VALUE },
{ confirmation: true },
);
expect(r.ok).toBe(false);
expect(r.error).toMatch(/must be a string/);
});
});
describe('snapshotDataDir', () => {
let dir: string;
beforeEach(() => { dir = mkTmp('erase-snapshot'); });
afterEach(() => { try { fs.rmSync(dir, { recursive: true, force: true }); } catch { /* */ } });
it('returns zeroes for a non-existent dir', () => {
const snap = snapshotDataDir(path.join(dir, 'does-not-exist'));
expect(snap.fileCount).toBe(0);
expect(snap.totalBytes).toBe(0);
expect(snap.topLevelEntries).toEqual([]);
});
it('counts top-level files + recursive subdir bytes correctly', () => {
fs.writeFileSync(path.join(dir, 'a.txt'), 'aaaaa'); // 5 bytes
fs.writeFileSync(path.join(dir, 'b.txt'), 'bb'); // 2 bytes
fs.mkdirSync(path.join(dir, 'sub'));
fs.writeFileSync(path.join(dir, 'sub', 'c.txt'), 'ccc'); // 3 bytes
fs.writeFileSync(path.join(dir, 'sub', 'd.txt'), 'dddd'); // 4 bytes
const snap = snapshotDataDir(dir);
expect(snap.fileCount).toBe(4);
expect(snap.totalBytes).toBe(14);
expect(snap.topLevelEntries).toHaveLength(3);
const sub = snap.topLevelEntries.find(e => e.name === 'sub');
expect(sub?.isDirectory).toBe(true);
expect(sub?.bytes).toBe(7);
});
});
describe('assertDataDirIsSafeToWipe', () => {
it('passes for a path with "waggle" in the basename', () => {
const dir = mkTmp('waggle-safe-name');
expect(assertDataDirIsSafeToWipe(dir)).toBeNull();
fs.rmSync(dir, { recursive: true, force: true });
});
it('passes for a non-waggle-named dir if it contains a Waggle artifact', () => {
const dir = mkTmp('foreign-name'); // mkdtemp prefix gets "waggle-" — work around
// Rename into a foreign-shape path so the basename heuristic fails.
const renamed = path.join(path.dirname(dir), `foreign-${Date.now()}`);
fs.renameSync(dir, renamed);
fs.writeFileSync(path.join(renamed, 'personal.mind'), '');
expect(assertDataDirIsSafeToWipe(renamed)).toBeNull();
fs.rmSync(renamed, { recursive: true, force: true });
});
it('refuses a foreign-named, artifact-free dir', () => {
const dir = mkTmp('safe-suffix');
const renamed = path.join(path.dirname(dir), `notrelated-${Date.now()}`);
fs.renameSync(dir, renamed);
const err = assertDataDirIsSafeToWipe(renamed);
expect(err).toMatch(/does not look like a Waggle data dir/);
fs.rmSync(renamed, { recursive: true, force: true });
});
it('refuses cwd', () => {
const err = assertDataDirIsSafeToWipe(process.cwd());
expect(err).toMatch(/forbidden top-level path/);
});
it('refuses an empty / non-string path', () => {
expect(assertDataDirIsSafeToWipe('')).toMatch(/empty/);
// @ts-expect-error — testing the runtime guard
expect(assertDataDirIsSafeToWipe(null)).toMatch(/empty/);
});
});
describe('writeEraseMarker / readEraseMarker round-trip', () => {
let dir: string;
beforeEach(() => { dir = mkTmp('erase-marker'); });
afterEach(() => { try { fs.rmSync(dir, { recursive: true, force: true }); } catch { /* */ } });
it('writes a JSON marker that readEraseMarker parses back', () => {
const marker: EraseMarker = {
schemaVersion: 1,
requestedAt: '2026-05-08T10:00:00.000Z',
snapshot: { fileCount: 3, totalBytes: 100, topLevelEntries: [] },
};
const p = writeEraseMarker(dir, marker);
expect(p).toBe(path.join(dir, ERASE_MARKER_FILENAME));
const round = readEraseMarker(dir);
expect(round).toEqual(marker);
});
it('returns null when no marker file exists', () => {
expect(readEraseMarker(dir)).toBeNull();
});
it('returns null when marker JSON has wrong schemaVersion', () => {
fs.writeFileSync(
path.join(dir, ERASE_MARKER_FILENAME),
JSON.stringify({ schemaVersion: 2, requestedAt: 'x', snapshot: {} }),
);
expect(readEraseMarker(dir)).toBeNull();
});
it('returns null when marker JSON is malformed', () => {
fs.writeFileSync(path.join(dir, ERASE_MARKER_FILENAME), 'not json');
expect(readEraseMarker(dir)).toBeNull();
});
});
describe('performWipe', () => {
let dir: string;
beforeEach(() => { dir = mkTmp('erase-wipe'); });
afterEach(() => { try { fs.rmSync(dir, { recursive: true, force: true }); } catch { /* */ } });
function fakeMarker(): EraseMarker {
return {
schemaVersion: 1,
requestedAt: new Date().toISOString(),
snapshot: { fileCount: 0, totalBytes: 0, topLevelEntries: [] },
};
}
it('removes every file inside the data dir and returns them in filesRemoved', () => {
fs.writeFileSync(path.join(dir, 'a.txt'), 'a');
fs.writeFileSync(path.join(dir, 'b.mind'), 'b');
fs.mkdirSync(path.join(dir, 'sub'));
fs.writeFileSync(path.join(dir, 'sub', 'c.txt'), 'c');
// The marker itself
writeEraseMarker(dir, fakeMarker());
const r = performWipe(dir, fakeMarker());
expect(r.filesSkipped).toEqual([]);
// All four files + the marker should be removed.
const removed = new Set(r.filesRemoved);
expect(removed.has('a.txt')).toBe(true);
expect(removed.has('b.mind')).toBe(true);
expect(removed.has(path.join('sub', 'c.txt'))).toBe(true);
expect(removed.has(ERASE_MARKER_FILENAME)).toBe(true);
// Data dir still exists, but is empty.
expect(fs.existsSync(dir)).toBe(true);
expect(fs.readdirSync(dir)).toEqual([]);
});
it('refuses to wipe a foreign-named, artifact-free dir and returns refusal in filesSkipped', () => {
// Build a foreign-shape dir so assertDataDirIsSafeToWipe refuses.
const renamed = path.join(path.dirname(dir), `notrelated-${Date.now()}`);
fs.renameSync(dir, renamed);
fs.writeFileSync(path.join(renamed, 'a.txt'), 'a');
const r = performWipe(renamed, fakeMarker());
expect(r.filesRemoved).toEqual([]);
expect(r.filesSkipped).toHaveLength(1);
expect(r.filesSkipped[0].reason).toMatch(/does not look like a Waggle data dir/);
// File untouched.
expect(fs.existsSync(path.join(renamed, 'a.txt'))).toBe(true);
fs.rmSync(renamed, { recursive: true, force: true });
});
});
describe('writeWipeReceipt', () => {
let dir: string;
beforeEach(() => { dir = mkTmp('erase-receipt'); });
afterEach(() => { try { fs.rmSync(dir, { recursive: true, force: true }); } catch { /* */ } });
it('writes a JSON receipt to a unique timestamped filename', () => {
const receipt = {
requestedAt: '2026-05-08T10:00:00.000Z',
wipedAt: '2026-05-08T10:01:00.000Z',
snapshot: { fileCount: 0, totalBytes: 0, topLevelEntries: [] },
filesRemoved: ['a.txt'],
filesSkipped: [],
};
const p = writeWipeReceipt(dir, receipt);
expect(path.basename(p)).toMatch(/^audit-receipt-2026-05-08T10-01-00-000Z\.json$/);
const back = JSON.parse(fs.readFileSync(p, 'utf-8'));
expect(back).toEqual(receipt);
});
it('creates the dir if it has been wiped to non-existence', () => {
fs.rmSync(dir, { recursive: true, force: true });
expect(fs.existsSync(dir)).toBe(false);
const receipt = {
requestedAt: '2026-05-08T10:00:00.000Z',
wipedAt: '2026-05-08T10:01:00.000Z',
snapshot: { fileCount: 0, totalBytes: 0, topLevelEntries: [] },
filesRemoved: [],
filesSkipped: [],
};
const p = writeWipeReceipt(dir, receipt);
expect(fs.existsSync(p)).toBe(true);
});
});