diff --git a/pkg/npm/test/archive-fixtures.js b/pkg/npm/test/archive-fixtures.js index 5b28a2e91..253f5b2a1 100644 --- a/pkg/npm/test/archive-fixtures.js +++ b/pkg/npm/test/archive-fixtures.js @@ -5,6 +5,19 @@ const { execFileSync } = require('node:child_process'); const fs = require('node:fs'); const path = require('node:path'); +// The npm wrapper takes the zip path whenever Node reports win32 (see +// getPlatform and `ext` in install.js), so the tar path is unreachable on a +// Windows host and the tar cases would test nothing a user can reach there. +// They cannot even build their fixtures on the Windows runner: the first +// `tar` on its PATH is Git for Windows' GNU tar, which reads a drive-letter +// path such as C:\... as a remote host ("tar (child): Cannot connect to C: +// resolve failed"; every tar case failed that way in the first Windows run), +// while Windows' own bsdtar accepts it. Under WSL Node is a Linux binary, +// takes the tar path with POSIX paths, and these cases run there. +const TAR_PATH_SKIP = process.platform === 'win32' + ? 'the npm wrapper never takes the tar path on win32 (it installs the zip asset)' + : false; + // Deterministic, poorly compressible bytes (xorshift32), so a fixture of a // few hundred KiB still spans several pipe chunks after gzip. function patternBytes(length, seed = 0x9e3779b9) { @@ -110,4 +123,4 @@ function buildZip(entries) { return Buffer.concat([...locals, directory, end]); } -module.exports = { buildZip, patternBytes, writeTarGz }; +module.exports = { TAR_PATH_SKIP, buildZip, patternBytes, writeTarGz }; diff --git a/pkg/npm/test/archive-limits.test.js b/pkg/npm/test/archive-limits.test.js index 3aa786954..998f772e6 100644 --- a/pkg/npm/test/archive-limits.test.js +++ b/pkg/npm/test/archive-limits.test.js @@ -17,7 +17,9 @@ const { extractExactTarArchive, extractZipOnWindows, } = require('../install.js'); -const { buildZip, patternBytes, writeTarGz } = require('./archive-fixtures.js'); +const { + TAR_PATH_SKIP, buildZip, patternBytes, writeTarGz, +} = require('./archive-fixtures.js'); const EXECUTABLE = 'codebase-memory-mcp'; // Spans several pipe chunks, so an over-limit member is cut off mid-stream. @@ -63,7 +65,7 @@ function neverLists() { throw new Error('the listing must not run'); } -test('tar extraction refuses an archive larger than the compressed limit', () => +test('tar extraction refuses an archive larger than the compressed limit', { skip: TAR_PATH_SKIP }, () => withScratch(async (root) => { const archive = writeTarGz(root, 'release.tar.gz', releaseEntries(UNIX_ARCHIVE_NAMES)); const destination = makeDestination(root); @@ -78,7 +80,7 @@ test('tar extraction refuses an archive larger than the compressed limit', () => assert.deepEqual(fs.readdirSync(destination), []); })); -test('tar extraction refuses an exact namespace wider than the member limit', () => +test('tar extraction refuses an exact namespace wider than the member limit', { skip: TAR_PATH_SKIP }, () => withScratch(async (root) => { const archive = writeTarGz(root, 'release.tar.gz', releaseEntries(UNIX_ARCHIVE_NAMES)); const destination = makeDestination(root); @@ -93,7 +95,7 @@ test('tar extraction refuses an exact namespace wider than the member limit', () assert.deepEqual(fs.readdirSync(destination), []); })); -test('tar extraction stops an over-limit member in the counted writer and leaves no file', () => +test('tar extraction stops an over-limit member in the counted writer and leaves no file', { skip: TAR_PATH_SKIP }, () => withScratch(async (root) => { const archive = writeTarGz( root, 'release.tar.gz', releaseEntries(UNIX_ARCHIVE_NAMES, LARGE_EXECUTABLE), @@ -112,7 +114,7 @@ test('tar extraction stops an over-limit member in the counted writer and leaves assert.deepEqual(fs.readdirSync(destination), []); })); -test('tar extraction leaves a pre-existing target untouched', () => +test('tar extraction leaves a pre-existing target untouched', { skip: TAR_PATH_SKIP }, () => withScratch(async (root) => { const archive = writeTarGz(root, 'release.tar.gz', releaseEntries(UNIX_ARCHIVE_NAMES)); const destination = makeDestination(root); @@ -126,7 +128,7 @@ test('tar extraction leaves a pre-existing target untouched', () => assert.equal(fs.readFileSync(target, 'utf8'), 'keep'); })); -test('tar extraction leaves no file when tar fails mid-stream', () => +test('tar extraction leaves no file when tar fails mid-stream', { skip: TAR_PATH_SKIP }, () => withScratch(async (root) => { const complete = writeTarGz( root, 'complete.tar.gz', releaseEntries(UNIX_ARCHIVE_NAMES, LARGE_EXECUTABLE), @@ -147,7 +149,7 @@ test('tar extraction leaves no file when tar fails mid-stream', () => assert.deepEqual(fs.readdirSync(destination), []); })); -test('tar extraction rejects an empty executable', () => +test('tar extraction rejects an empty executable', { skip: TAR_PATH_SKIP }, () => withScratch(async (root) => { const archive = writeTarGz( root, 'release.tar.gz', releaseEntries(UNIX_ARCHIVE_NAMES, Buffer.alloc(0)), @@ -161,7 +163,7 @@ test('tar extraction rejects an empty executable', () => assert.deepEqual(fs.readdirSync(destination), []); })); -test('tar extraction installs the executable byte-identical with the executable bit set', () => +test('tar extraction installs the executable byte-identical with the executable bit set', { skip: TAR_PATH_SKIP }, () => withScratch(async (root) => { const archive = writeTarGz( root, 'release.tar.gz', releaseEntries(UNIX_ARCHIVE_NAMES, LARGE_EXECUTABLE), diff --git a/pkg/npm/test/package-publication.test.js b/pkg/npm/test/package-publication.test.js index 258bdd74b..ad120b9ed 100644 --- a/pkg/npm/test/package-publication.test.js +++ b/pkg/npm/test/package-publication.test.js @@ -13,7 +13,7 @@ const { installWindowsBinaryAtomically, validateExactTarMemberListing, } = require('../install.js'); -const { writeTarGz } = require('./archive-fixtures.js'); +const { TAR_PATH_SKIP, writeTarGz } = require('./archive-fixtures.js'); function exactUnixListing(extra = []) { return [...UNIX_ARCHIVE_NAMES, ...extra].join('\n') + '\n'; @@ -34,7 +34,7 @@ test('Unix archive validation rejects traversal and unexpected members', () => { ); }); -test('Unix extraction lists through the system tar and writes only the root executable', async () => { +test('Unix extraction lists through the system tar and writes only the root executable', { skip: TAR_PATH_SKIP }, async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cbm-npm-extract-test-')); try { const archive = writeTarGz(