Spaces:
Running
Running
File size: 2,861 Bytes
c3633b1 0ed8124 c3633b1 0ed8124 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | import { describe, expect, it, vi } from 'vitest';
import { verifyBlobSha256 } from './blob-integrity';
describe('verifyBlobSha256', () => {
it('accepts the known SHA-256 for a streamed blob', async () => {
const result = await verifyBlobSha256(
new Blob(['abc']),
'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad',
);
expect(result).toEqual({
actualSha256: 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad',
matches: true,
});
});
it('rejects same-size content with a different digest', async () => {
const expected = await verifyBlobSha256(new Blob(['good']), '0'.repeat(64));
const corrupted = await verifyBlobSha256(new Blob(['evil']), expected.actualSha256);
expect(new Blob(['good']).size).toBe(new Blob(['evil']).size);
expect(corrupted.matches).toBe(false);
expect(corrupted.actualSha256).not.toBe(expected.actualSha256);
});
it('reports monotonic byte progress while hashing a shard', async () => {
const data = new Uint8Array(256 * 1024);
data.fill(0x61);
const blob = new Blob([data]);
Object.defineProperty(blob, 'stream', {
value: () => {
let offset = 0;
return new ReadableStream<Uint8Array>({
pull(controller) {
if (offset >= data.byteLength) {
controller.close();
return;
}
const nextOffset = Math.min(data.byteLength, offset + (64 * 1024));
controller.enqueue(data.subarray(offset, nextOffset));
offset = nextOffset;
},
});
},
});
const progress: Array<{ loadedBytes: number; totalBytes: number }> = [];
let timestamp = 0;
const now = vi.spyOn(performance, 'now').mockImplementation(() => {
timestamp += 101;
return timestamp;
});
try {
await verifyBlobSha256(blob, '0'.repeat(64), undefined, (loadedBytes, totalBytes) => {
progress.push({ loadedBytes, totalBytes });
});
} finally {
now.mockRestore();
}
expect(progress[0]).toEqual({ loadedBytes: 0, totalBytes: blob.size });
expect(progress.at(-1)).toEqual({ loadedBytes: blob.size, totalBytes: blob.size });
expect(progress.some(({ loadedBytes }) => loadedBytes > 0 && loadedBytes < blob.size)).toBe(true);
expect(progress.every(({ totalBytes }) => totalBytes === blob.size)).toBe(true);
expect(progress.every((sample, index) => (
index === 0 || sample.loadedBytes >= (progress[index - 1]?.loadedBytes ?? 0)
))).toBe(true);
});
it('stops hashing when the caller aborts', async () => {
const controller = new AbortController();
controller.abort();
await expect(
verifyBlobSha256(new Blob(['abc']), '0'.repeat(64), controller.signal),
).rejects.toMatchObject({ name: 'AbortError' });
});
});
|