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) }) })