mirror of
https://github.com/pnpm/pnpm.git
synced 2026-06-28 18:05:29 -04:00
* 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.
112 lines
4.6 KiB
TypeScript
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)
|
|
})
|
|
})
|