// @vitest-environment node import { readFileSync } from "node:fs"; import { expect, it } from "vitest"; import { verifyAudioAsset } from "../src/audio/integrity"; // 신뢰 기준은 JS 번들에 고정한 manifest다. 요청 파일이 자기 해시를 선언할 수 없다. it("109개 승인 WAV만 SHA-256·bytes·형식 검증을 통과한다", async () => { let count = 0; for (const directory of ["audio", "ambience"]) { const manifest = JSON.parse( readFileSync(`public/${directory}/manifest.json`, "utf8"), ); for (const entry of manifest.files) { const bytes = Uint8Array.from( readFileSync(`public/${directory}/${entry.file}`), ).buffer; await expect( verifyAudioAsset(`/${directory}/${entry.file}?v=0.2.5`, bytes), ).resolves.toBeUndefined(); count++; } } expect(count).toBe(109); }); it("변조·다른 파일·외부 URL·이전 캐시·축약 응답은 decode 전에 거부한다", async () => { const bytes = Uint8Array.from(readFileSync("public/ambience/street.wav")); for (const url of [ "/ambience/rain.wav?v=0.2.5", "/ambience/street.wav?v=0.2.3", "https://external.example/ambience/street.wav?v=0.2.5", "/ambience/unknown.wav?v=0.2.5", ]) { await expect(verifyAudioAsset(url, bytes.slice().buffer)).rejects.toThrow(); } const corrupt = bytes.slice(); corrupt[100] = corrupt[100]! ^ 1; await expect( verifyAudioAsset("/ambience/street.wav?v=0.2.5", corrupt.buffer), ).rejects.toThrow(); await expect( verifyAudioAsset("/ambience/street.wav?v=0.2.5", bytes.slice(0, -2).buffer), ).rejects.toThrow(); });