import { describe, expect, it } from 'vitest' import { formatBytes } from '../src/lib/bytes' import { DEFAULT_RANDOM_BYTES, MAX_RANDOM_BYTES, MIN_RANDOM_BYTES, secureRandomBytes, type RandomSource, } from '../src/lib/random' describe('secure random bytes', () => { it('uses the supplied cryptographic source and preserves requested length', () => { let next = 0 const source: RandomSource = { getRandomValues(array: T): T { const bytes = new Uint8Array(array.buffer, array.byteOffset, array.byteLength) bytes.forEach((_, index) => { bytes[index] = next; next += 1 }) return array }, } expect(DEFAULT_RANDOM_BYTES).toBe(16) expect(secureRandomBytes(4, source)).toEqual(Uint8Array.from([0, 1, 2, 3])) }) it.each([0, -1, 1.5, Number.NaN, MAX_RANDOM_BYTES + 1])('rejects invalid length %s', (length) => { expect(() => secureRandomBytes(length, crypto)).toThrow(`${MIN_RANDOM_BYTES} 至 ${MAX_RANDOM_BYTES}`) }) it('accepts both length boundaries without exceeding the browser call limit', () => { const calls: number[] = [] const source: RandomSource = { getRandomValues(array: T): T { calls.push(array.byteLength) return array }, } expect(secureRandomBytes(MIN_RANDOM_BYTES, source)).toHaveLength(MIN_RANDOM_BYTES) expect(secureRandomBytes(MAX_RANDOM_BYTES, source)).toHaveLength(MAX_RANDOM_BYTES) expect(calls).toEqual([MIN_RANDOM_BYTES, MAX_RANDOM_BYTES]) }) it('fails closed when the cryptographic source is unavailable or throws', () => { expect(() => secureRandomBytes(DEFAULT_RANDOM_BYTES, null as unknown as RandomSource)).toThrow('不支持安全随机数') const failure = new Error('entropy source failed') const source: RandomSource = { getRandomValues: () => { throw failure } } expect(() => secureRandomBytes(DEFAULT_RANDOM_BYTES, source)).toThrow(failure) }) it('formats the same bytes for OpenSSL and source-code styles', () => { const bytes = Uint8Array.from([0x0a, 0xbc, 0xff]) expect(formatBytes(bytes)).toBe('0ABCFF') expect(formatBytes(bytes, { format: 'spaced-hex' })).toBe('0A BC FF') expect(formatBytes(bytes, { format: 'byte-array', uppercase: false })).toBe('0x0a, 0xbc, 0xff') }) })