Incognito-Wiki/tests/migration-manifest.test.mjs
msa46 bcb8dcfe2a
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
fix: resolve final Starlight migration review
2026-08-02 18:59:28 +02:00

103 lines
4.3 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { access, mkdir, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { loadManifest, validateManifest, checkDestinations } from '../scripts/lib/migration.mjs';
test('manifest accounts for exactly 29 unique source pages', async () => {
const manifest = await loadManifest('docs/migration-manifest.json');
const result = validateManifest(manifest);
assert.equal(result.entries.length, 29);
assert.equal(new Set(result.entries.map(({ source }) => source)).size, 29);
});
test('generic DokuWiki pages are excluded', async () => {
const manifest = await loadManifest('docs/migration-manifest.json');
assert.equal(manifest.entries.some(({ source }) => source.startsWith('pages/wiki/')), false);
});
test('validation rejects source traversal outside the selected scope', async () => {
const manifest = await loadManifest('docs/migration-manifest.json');
manifest.entries[2].source = 'pages/study/../../wiki/x.txt';
assert.throws(() => validateManifest(manifest), /outside the selected migration scope/);
});
test('validation rejects backslash source traversal outside the selected scope', async () => {
const manifest = await loadManifest('docs/migration-manifest.json');
manifest.entries[2].source = 'pages/study/..\\..\\wiki\\x.txt';
assert.throws(() => validateManifest(manifest), /outside the selected migration scope/);
});
test('validation rejects a fabricated source inside the broad study path', async () => {
const manifest = await loadManifest('docs/migration-manifest.json');
manifest.entries[2].source = 'pages/study/fabricated.txt';
assert.throws(() => validateManifest(manifest), /canonical migration contract/);
});
test('validation rejects a mode that differs from the canonical migration contract', async () => {
const manifest = await loadManifest('docs/migration-manifest.json');
manifest.entries[2].mode = 'merge';
assert.throws(() => validateManifest(manifest), /canonical migration contract/);
});
test('validation rejects destination traversal outside the docs root', async () => {
const manifest = await loadManifest('docs/migration-manifest.json');
manifest.entries[2].destination = 'src/content/docs/../../../outside.md';
assert.throws(() => validateManifest(manifest), /outside src\/content\/docs/);
});
test('validation rejects backslash destination traversal outside the docs root', async () => {
const manifest = await loadManifest('docs/migration-manifest.json');
manifest.entries[2].destination = 'src/content/docs/..\\..\\..\\outside.md';
assert.throws(() => validateManifest(manifest), /outside src\/content\/docs/);
});
test('validation rejects duplicate destinations unless the later entry is a merge', async () => {
const manifest = await loadManifest('docs/migration-manifest.json');
manifest.entries[1].mode = 'page';
assert.throws(() => validateManifest(manifest), /Duplicate destination/);
});
test('destination check reports only files that have not been migrated', async () => {
const root = await mkdir(join(tmpdir(), `migration-${process.pid}-${Date.now()}`), { recursive: true });
const existing = join(root, 'src/content/docs/present.md');
await mkdir(join(root, 'src/content/docs'), { recursive: true });
await writeFile(existing, '# Present\n');
try {
const result = await checkDestinations({
entries: [
{ destination: 'src/content/docs/present.md' },
{ destination: 'src/content/docs/missing.md' },
],
}, root);
await access(existing);
assert.deepEqual(result.missing, ['src/content/docs/missing.md']);
} finally {
await rm(root, { recursive: true, force: true });
}
});
test('destination check rejects a directory at a manifest file path', async () => {
const root = await mkdir(join(tmpdir(), `migration-${process.pid}-${Date.now()}`), { recursive: true });
const directory = join(root, 'src/content/docs/not-a-file.md');
await mkdir(directory, { recursive: true });
try {
const result = await checkDestinations({
entries: [{ destination: 'src/content/docs/not-a-file.md' }],
}, root);
assert.deepEqual(result.notFiles, ['src/content/docs/not-a-file.md']);
} finally {
await rm(root, { recursive: true, force: true });
}
});