import { describe, expect, it } from 'vitest'; import { decryptAesGcm } from '@/utils/crypto/decryptAesGcm'; import { importAesGcmKey } from '@/utils/crypto/importAesGcmKey'; import { base64UrlToBytes } from '@/utils/encoding/base64UrlToBytes'; import { bytesToBase64 } from '@/utils/encoding/bytesToBase64'; import { encryptHotlinkUpload } from '@/utils/upload/encryptHotlinkUpload'; describe('encryptHotlinkUpload', () => { it('roundtrips plaintext using the path secret as the DEK', async () => { const plaintext = new TextEncoder().encode('hotlink-image'); const encrypted = await encryptHotlinkUpload(plaintext); const dek = base64UrlToBytes(encrypted.secret); const key = await importAesGcmKey(dek); const recovered = await decryptAesGcm(encrypted.ciphertext, key, encrypted.iv); expect(recovered).toEqual(plaintext); }); it('returns a distinct secret per call', async () => { const plaintext = new Uint8Array([0]); const first = await encryptHotlinkUpload(plaintext); const second = await encryptHotlinkUpload(plaintext); expect(first.secret).not.toEqual(second.secret); }); it('serializes iv and ciphertext for the upload API', async () => { const encrypted = await encryptHotlinkUpload(new Uint8Array([1, 2, 3])); expect(bytesToBase64(encrypted.iv)).toMatch(/^[A-Za-z0-9+/]+=*$/); expect(bytesToBase64(encrypted.ciphertext)).toMatch(/^[A-Za-z0-9+/]+=*$/); }); });