mirror of
https://github.com/pnpm/pnpm.git
synced 2026-04-10 18:18:56 -04:00
111 lines
4.5 KiB
TypeScript
111 lines
4.5 KiB
TypeScript
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)
|
|
})
|
|
})
|