Files
pnpm/crypto/integrity/test/index.ts
Zoltan Kochan 187049055f chore: upgrade @typescript/native-preview to 7.0.0-dev.20260421.2 (#11332)
* chore: upgrade @typescript/native-preview to 7.0.0-dev.20260421.2

- Add explicit `types: ["node"]` to the shared tsconfig because tsgo
  20260421 no longer auto-acquires `@types/*` from `node_modules`.
- Refactor test files to explicitly import jest globals (`describe`,
  `it`, `test`, `expect`, `beforeEach`, etc.) from `@jest/globals`
  instead of relying on `@types/jest` ambient declarations. Under the
  new tsgo build, `import { jest } from '@jest/globals'` shadows the
  ambient `jest` namespace, breaking `@types/jest`'s `declare var
  describe: jest.Describe;` globals.
- Add `@jest/globals` to each package's devDependencies where tests
  now import from it, and add `@types/node` to packages that need it
  but were relying on hoisted resolution.
- Replace `fail()` calls with `throw new Error(...)` since `fail` is
  no longer globally available.

* chore: fix remaining tsgo type-strictness errors

- Strip `as <PnpmType>` casts on objects passed to toMatchObject /
  toStrictEqual / toEqual; @jest/globals rejects the typed objects
  (which include AsymmetricMatchers) vs. the repo-specific type.
- Type `jest.fn<...>()` explicitly where the mock's signature matters
  for toHaveBeenCalledWith.
- Replace `beforeEach(() => X)` with `beforeEach(() => { X })` so the
  return value is void, as the stricter jest typing requires.
- Use `expect.objectContaining({...})` in one place where the full
  expected object triggered stricter type resolution.
- Cast `prompt.mock.calls` arg through `as unknown as Record<...>[]`
  for patch.test.ts's nested-array matchers.
- Fix off-by-one `<reference path>` in pnpm/test/getConfig.test.ts
  that only surfaced now.
- Move `@jest/globals` from devDependencies to dependencies in the
  two `__utils__` packages that import it from `src/`.
- Clean up unused imports from the @jest/globals migration.

* chore: address Copilot review on #11332

- Move misplaced `@jest/globals` imports to the top import block in
  checkEngine, run.ts, and workspace/root-finder tests where the
  script dropped them below executable code.
- Replace `try { await x(); throw new Error('should have thrown') } catch`
  in bins/linker, lockfile/fs, and resolving/local-resolver tests with
  `await expect(x()).rejects.toMatchObject({...})`. The old pattern
  swallowed an unrelated `throw` if the under-test call silently
  succeeded, which would fail on the catch-block assertion with a
  misleading message.
2026-04-21 22:50:40 +02:00

112 lines
4.6 KiB
TypeScript

import { describe, expect, it } from '@jest/globals'
import { formatIntegrity, parseIntegrity } from '@pnpm/crypto.integrity'
describe('parseIntegrity', () => {
it('parses a valid sha512 integrity string', () => {
// "hello" hashed with sha512, base64 encoded
const integrity = 'sha512-9/u6bgY2+JDlb7vzKD5STG+jIErimDgtYkdB0NxmODJuKCxBvl5CVNiCB3LFUYosWowMf37aGVlKfrU5RT4e1w=='
const result = parseIntegrity(integrity)
expect(result.algorithm).toBe('sha512')
expect(result.hexDigest).toBe('f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7')
})
it('parses a valid sha256 integrity string', () => {
// "hello" hashed with sha256, base64 encoded
const integrity = 'sha256-LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ='
const result = parseIntegrity(integrity)
expect(result.algorithm).toBe('sha256')
expect(result.hexDigest).toBe('2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824')
})
it('parses a valid sha1 integrity string', () => {
// "hello" hashed with sha1, base64 encoded
const integrity = 'sha1-qvTGHdzF6KLavt4PO0gs2a6pQ00='
const result = parseIntegrity(integrity)
expect(result.algorithm).toBe('sha1')
expect(result.hexDigest).toBe('aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d')
})
it('handles algorithms with numbers', () => {
const integrity = 'sha384-OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb'
const result = parseIntegrity(integrity)
expect(result.algorithm).toBe('sha384')
expect(result.hexDigest).toHaveLength(96) // 384 bits = 48 bytes = 96 hex chars
})
it('is case-insensitive for base64 characters', () => {
// Same hash but with mixed case (valid base64)
const integrity = 'sha256-LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ='
const result = parseIntegrity(integrity)
expect(result.algorithm).toBe('sha256')
})
it('throws on missing algorithm', () => {
expect(() => parseIntegrity('LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ='))
.toThrow('Invalid integrity format')
})
it('throws on empty string', () => {
expect(() => parseIntegrity(''))
.toThrow('Invalid integrity format')
})
it('throws on missing hash', () => {
expect(() => parseIntegrity('sha256-'))
.toThrow('Invalid integrity format')
})
it('throws on invalid base64 characters', () => {
expect(() => parseIntegrity('sha256-invalid!@#$%'))
.toThrow('Invalid integrity format')
})
it('throws on multiple dashes in algorithm', () => {
// The regex requires algorithm to have no dashes (uses [^-]+)
expect(() => parseIntegrity('sha-256-LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ='))
.toThrow('Invalid integrity format')
})
it('throws when base64 decodes to empty', () => {
// Padding-only base64 decodes to empty buffer
expect(() => parseIntegrity('sha256-===='))
.toThrow('base64 hash decoded to empty digest')
})
it('handles base64 without padding', () => {
// Some systems omit padding
const integrity = 'sha256-LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ'
const result = parseIntegrity(integrity)
expect(result.algorithm).toBe('sha256')
// Node's Buffer.from handles missing padding gracefully
expect(result.hexDigest).toBeTruthy()
})
it('handles base64 special characters (+ and /)', () => {
const integrity = 'sha512-abc+def/ghi='
const result = parseIntegrity(integrity)
expect(result.algorithm).toBe('sha512')
expect(result.hexDigest).toBeTruthy()
})
})
describe('formatIntegrity', () => {
it('formats a sha512 hex digest to integrity string', () => {
const hexDigest = 'f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7'
const result = formatIntegrity('sha512', hexDigest)
expect(result).toBe('sha512-9/u6bgY2+JDlb7vzKD5STG+jIErimDgtYkdB0NxmODJuKCxBvl5CVNiCB3LFUYosWowMf37aGVlKfrU5RT4e1w==')
})
it('formats a sha256 hex digest to integrity string', () => {
const hexDigest = '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'
const result = formatIntegrity('sha256', hexDigest)
expect(result).toBe('sha256-LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=')
})
it('roundtrips with parseIntegrity', () => {
const original = 'sha512-9/u6bgY2+JDlb7vzKD5STG+jIErimDgtYkdB0NxmODJuKCxBvl5CVNiCB3LFUYosWowMf37aGVlKfrU5RT4e1w=='
const { algorithm, hexDigest } = parseIntegrity(original)
const formatted = formatIntegrity(algorithm, hexDigest)
expect(formatted).toBe(original)
})
})