OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / test / unit / npm-package-pack-check.test.js

241 lines · javascript ref:
import test from 'brittle'
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { execFile } from 'node:child_process'
import {
  checkNpmPackagePack,
  findMissingExportTargets,
  findUnsafePackPaths,
  inspectPack,
  parsePackJson
} from '../../scripts/check-npm-package-pack.mjs'

test('npm package pack checker accepts clean workspace pack metadata', (t) => {
  const root = fixtureRoot()
  writePackage(root, 'packages/core', {
    name: 'p2p-hiverelay',
    version: '0.20.2',
    files: ['README.md', 'LICENSE', 'index.js']
  })

  const result = checkNpmPackagePack({
    cwd: root,
    workspaces: ['packages/core'],
    runner: () => ({
      stdout: JSON.stringify([{
        name: 'p2p-hiverelay',
        version: '0.20.2',
        filename: 'p2p-hiverelay-0.20.2.tgz',
        size: 123,
        unpackedSize: 456,
        entryCount: 3,
        files: [
          { path: 'README.md' },
          { path: 'LICENSE' },
          { path: 'index.js' }
        ]
      }])
    })
  })

  t.ok(result.ok)
  t.is(result.workspaces.length, 1)
  t.is(result.workspaces[0].hasReadme, true)
  t.is(result.workspaces[0].hasLicense, true)
  t.alike(result.workspaces[0].unsafe, [])
})

test('npm package pack checker rejects missing README/license and unsafe paths', (t) => {
  const root = fixtureRoot()
  writePackage(root, 'packages/core', {
    name: 'p2p-hiverelay',
    version: '0.20.2',
    files: ['index.js']
  })

  const result = checkNpmPackagePack({
    cwd: root,
    workspaces: ['packages/core'],
    runner: () => ({
      stdout: JSON.stringify([{
        name: 'p2p-hiverelay',
        version: '0.20.2',
        files: [
          { path: 'index.js' },
          { path: 'node_modules/bad/index.js' },
          { path: '.env' },
          { path: 'secret.pem' }
        ]
      }])
    })
  })

  t.absent(result.ok)
  t.ok(result.errors.some(error => error.includes('packed tarball is missing README.md')))
  t.ok(result.errors.some(error => error.includes('package.json files allowlist is missing LICENSE')))
  t.ok(result.errors.some(error => error.includes('unsafe packed path node_modules/bad/index.js')))
  t.ok(result.errors.some(error => error.includes('unsafe packed path .env')))
  t.ok(result.errors.some(error => error.includes('unsafe packed path secret.pem')))
})

test('npm package pack checker rejects name and version drift', (t) => {
  const row = inspectPack({
    workspace: 'packages/core',
    manifest: {
      name: 'p2p-hiverelay',
      version: '0.20.2',
      files: ['README.md', 'LICENSE']
    },
    pack: {
      name: 'wrong',
      version: '0.20.1',
      files: [
        { path: 'README.md' },
        { path: 'LICENSE' }
      ]
    }
  })

  t.absent(row.ok)
  t.ok(row.errors.some(error => error.includes('packed package name')))
  t.ok(row.errors.some(error => error.includes('packed package version')))
})

test('npm package pack checker rejects an export omitted from the packed tarball', async (t) => {
  const root = fixtureRoot()
  const out = path.join(root, 'dist')
  t.teardown(() => fs.rmSync(root, { recursive: true, force: true }))
  fs.mkdirSync(out)
  fs.writeFileSync(path.join(root, 'README.md'), '# fixture\n')
  fs.writeFileSync(path.join(root, 'LICENSE'), 'fixture\n')
  fs.writeFileSync(path.join(root, 'index.js'), 'export const present = true\n')
  fs.writeFileSync(path.join(root, 'exported.js'), 'export const shouldShip = true\n')

  const manifest = {
    name: 'hiverelay-export-closure-fixture',
    version: '1.0.0',
    type: 'module',
    exports: {
      '.': './index.js',
      './exported.js': './exported.js'
    },
    files: ['README.md', 'LICENSE', 'index.js']
  }
  fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify(manifest, null, 2) + '\n')

  const pack = parsePackJson((await execFileResult('npm', [
    'pack', '--json', '--pack-destination', out
  ], {
    cwd: root,
    env: {
      ...process.env,
      npm_config_cache: path.join(root, '.npm-cache')
    }
  })).stdout)
  const row = inspectPack({ workspace: '.', manifest, pack })

  t.absent(row.ok)
  t.alike(row.missingExportTargets, [{
    subpath: './exported.js',
    target: './exported.js'
  }])
  t.ok(row.errors.some(error => error.includes('packed tarball is missing export target ./exported.js')))
})

test('npm package pack checker parses npm JSON and rejects malformed output', (t) => {
  t.is(parsePackJson('[{"name":"pkg","files":[]}]').name, 'pkg')
  t.exception(() => parsePackJson('not-json'))
  t.exception(() => parsePackJson('[]'))
  t.exception(() => parsePackJson('[{"name":"a"},{"name":"b"}]'))
})

test('npm package pack checker classifies unsafe file paths', (t) => {
  const issues = findUnsafePackPaths([
    'README.md',
    'docs/private.md',
    'test/fixture.js',
    '.npmrc',
    'keys/relay.key'
  ])

  t.alike(issues.map(issue => issue.path), [
    'docs/private.md',
    'test/fixture.js',
    '.npmrc',
    'keys/relay.key'
  ])
})

test('npm package pack checker closes exact, conditional, array, and wildcard exports', (t) => {
  const missing = findMissingExportTargets({
    '.': './index.js',
    './conditional': {
      import: './esm.js',
      require: './cjs.cjs'
    },
    './fallback': ['./preferred.js', './fallback.js'],
    './features/*': './features/*.js'
  }, [
    'index.js',
    'esm.js',
    'cjs.cjs',
    'preferred.js',
    'fallback.js',
    'features/one.js'
  ])

  t.alike(missing, [])
})

test('npm package pack checker CLI emits JSON evidence', async (t) => {
  const res = await runCli(['--workspace', 'packages/verifier', '--json'])
  const payload = JSON.parse(res.stdout)

  t.is(res.status, 0)
  t.is(payload.ok, true)
  t.is(payload.workspaces.length, 1)
  t.is(payload.workspaces[0].name, 'p2p-hiverelay-verifier')
  t.is(payload.workspaces[0].hasReadme, true)
  t.is(payload.workspaces[0].hasLicense, true)
  t.alike(payload.workspaces[0].unsafe, [])
})

function fixtureRoot () {
  return fs.mkdtempSync(path.join(os.tmpdir(), 'hiverelay-pack-check-'))
}

function writePackage (root, rel, pkg) {
  const dir = path.join(root, rel)
  fs.mkdirSync(dir, { recursive: true })
  fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify(pkg, null, 2) + '\n')
}

function runCli (argv) {
  return new Promise((resolve) => {
    execFile(process.execPath, ['scripts/check-npm-package-pack.mjs', ...argv], {
      cwd: process.cwd(),
      env: {
        ...process.env,
        npm_config_cache: path.join(os.tmpdir(), 'hiverelay-npm-pack-check-test-cache')
      },
      timeout: 20000
    }, (err, stdout, stderr) => {
      resolve({
        status: err && typeof err.code === 'number' ? err.code : 0,
        stdout,
        stderr
      })
    })
  })
}

function execFileResult (file, argv, opts) {
  return new Promise((resolve, reject) => {
    execFile(file, argv, opts, (err, stdout, stderr) => {
      if (err) return reject(Object.assign(err, { stdout, stderr }))
      resolve({ stdout, stderr })
    })
  })
}