From 7222ef89500c91dd079cdd0e1cc3d7759e3f5963 Mon Sep 17 00:00:00 2001 From: quality Date: Wed, 7 Oct 2026 15:16:59 -0400 Subject: [PATCH] test(mirror): cover mirrorArtworkUrls' non-regular-file destination arm mirrorArtworkUrls() guards its destination with `existing?.isSymbolicLink() || (existing && !existing.isFile())`. The symlink arm and the pass-through case were pinned; the second operand -- an entry that exists and is neither a symlink nor a regular file -- was the one uncovered region in scripts/lib/architecture-content.mjs. A directory on a mirror path needs no adversary: an interrupted run, or an upstream asset renamed from to /, leaves one behind, and the import is scheduled daily over whatever the last run left. Without the guard writeFileSync() raises EISDIR and aborts the whole import instead of skipping the single asset it cannot mirror. Two tests: one that the asset is skipped with exactly one audible warning, and one that the occupied destination is left byte-for-byte alone. Closes #1164 Signed-off-by: quality --- tests/architecture-content-mirror.test.mjs | 71 ++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/tests/architecture-content-mirror.test.mjs b/tests/architecture-content-mirror.test.mjs index 56fbfcbf..1390bd1f 100644 --- a/tests/architecture-content-mirror.test.mjs +++ b/tests/architecture-content-mirror.test.mjs @@ -3,6 +3,7 @@ import test from 'node:test'; import { lstatSync, mkdirSync, + readdirSync, mkdtempSync, readFileSync, rmSync, @@ -99,3 +100,73 @@ test('mirrorArtworkUrls still writes a regular-file destination', async () => { cleanup(); } }); + +// The destination guard has two arms. The symlink arm is pinned above; this is +// the other one -- an entry that exists but is not a regular file. It needs no +// adversary: a directory on a mirror path is what an interrupted run, or an +// upstream asset renamed from `` to `/`, leaves behind, and +// `.github/workflows/import-architectures.yml` reruns `npm run +// import:architectures` daily over whatever the last run left. Without the +// guard, writeFileSync() would throw EISDIR and abort the whole import instead +// of skipping the one asset it cannot mirror. +test('mirrorArtworkUrls skips a destination that is not a regular file', async () => { + const { root, cleanup } = sandbox(); + const originalWarn = console.warn; + const warnings = []; + try { + const relativeDestination = artworkMirrorPath(artworkPath(PNG_URL)); + const destination = join(root, relativeDestination); + // A directory, so the destination is neither a symlink nor a regular file: + // the second operand of the guard is what has to reject it. + mkdirSync(destination, { recursive: true }); + writeFileSync(join(destination, 'occupant'), 'left by an earlier run'); + + console.warn = (...args) => warnings.push(args.join(' ')); + await withStubbedFetch('mirrored-bytes', () => + mirrorArtworkUrls(root, [PNG_URL]), + ); + } finally { + console.warn = originalWarn; + cleanup(); + } + + // Skipping has to be audible: a silent `continue` would leave the asset + // missing with nothing in the import log explaining why. + assert.equal(warnings.length, 1); + assert.match(warnings[0], /destination is not a regular file/); +}); + +test('mirrorArtworkUrls leaves an irregular destination byte-for-byte alone', async () => { + const { root, cleanup } = sandbox(); + const originalWarn = console.warn; + try { + const relativeDestination = artworkMirrorPath(artworkPath(PNG_URL)); + const destination = join(root, relativeDestination); + mkdirSync(destination, { recursive: true }); + writeFileSync(join(destination, 'occupant'), 'left by an earlier run'); + + console.warn = () => {}; + await withStubbedFetch('mirrored-bytes', () => + mirrorArtworkUrls(root, [PNG_URL]), + ); + console.warn = originalWarn; + + assert.ok( + lstatSync(destination).isDirectory(), + 'the destination must still be the directory it was', + ); + assert.deepEqual( + readdirSync(destination), + ['occupant'], + 'nothing may be written underneath the occupied destination', + ); + assert.equal( + readFileSync(join(destination, 'occupant'), 'utf8'), + 'left by an earlier run', + 'the occupant must not be overwritten', + ); + } finally { + console.warn = originalWarn; + cleanup(); + } +});