import { describe, expect, test } from 'vitest'; import { byteDigest, canonicalJsonBytes, canonicalJsonDigest, parseCanonicalJson, } from '../../src/security/canonical-digest.ts'; describe('canonical digest primitives', () => { test('matches an independently fixed RFC 8785 byte and digest vector', () => { const bytes = canonicalJsonBytes('portfolio-test/v1', { z: 1, a: 'é' }); expect(new TextDecoder().decode(bytes)).toBe( '{"schema":"portfolio-test/v1","value":{"a":"é","z":1}}', ); expect(canonicalJsonDigest('portfolio-test/v1', { z: 1, a: 'é' })).toBe( '2d38a0324bcda2075f853db9972f6ce34ecb8072a85ec579b7c0e89be38df76a', ); expect(byteDigest(new TextEncoder().encode('abc'))).toBe( 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad', ); }); test.each([ ['duplicate keys', '{"a":1,"a":2}'], ['non-canonical whitespace', '{ "a":1}'], ['alternate number encoding', '{"a":1.0}'], ['non-NFC string', '{"a":"e\\u0301"}'], ['unpaired surrogate', '{"a":"\\ud800"}'], ])('rejects %s at JSON ingest', (_label, source) => { expect(() => parseCanonicalJson(source)).toThrow(); }); });