import { describe, expect, it } from 'vitest'; import { buildMultipart, makeBoundary } from '../src/core/multipart'; import { bytesIndexOf } from './fakes'; const enc = new TextEncoder(); function concat(...parts: Uint8Array[]): Uint8Array { const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0)); let at = 0; for (const p of parts) { out.set(p, at); at += p.length; } return out; } describe('makeBoundary', () => { it('builds 24 hex chars from random()', () => { expect(makeBoundary(() => 0)).toBe(`----voice-note-${'0'.repeat(24)}`); expect(makeBoundary(() => 0.99)).toBe(`----voice-note-${'f'.repeat(24)}`); }); it('clamps random() === 1 to f', () => { expect(makeBoundary(() => 1)).toBe(`----voice-note-${'f'.repeat(24)}`); }); }); describe('buildMultipart', () => { // Байты, которые сломались бы при прогоне через строку: 0x00, >0x7F, CRLF, "--". const audio = new Uint8Array([0x00, 0xff, 0x80, 0x0d, 0x0a, 0x2d, 0x2d]).buffer; const file = { name: 'file', filename: 'audio.m4a', contentType: 'audio/mp4', data: audio }; it('builds exact bytes with the boundary from contentType', () => { const { body, contentType } = buildMultipart( [ ['model', 'm1'], ['response_format', 'json'], ], file, 'BND', ); expect(contentType).toBe('multipart/form-data; boundary=BND'); const expected = concat( enc.encode( '--BND\r\nContent-Disposition: form-data; name="model"\r\n\r\nm1\r\n' + '--BND\r\nContent-Disposition: form-data; name="response_format"\r\n\r\njson\r\n' + '--BND\r\nContent-Disposition: form-data; name="file"; filename="audio.m4a"\r\nContent-Type: audio/mp4\r\n\r\n', ), new Uint8Array(audio), enc.encode('\r\n--BND--\r\n'), ); expect(new Uint8Array(body)).toEqual(expected); }); it('keeps binary audio bytes unchanged', () => { const { body } = buildMultipart([], file, 'BND'); expect(bytesIndexOf(new Uint8Array(body), new Uint8Array(audio))).toBeGreaterThan(0); }); it('encodes non-ASCII field values as UTF-8', () => { const { body } = buildMultipart([['language', 'ру']], file, 'BND'); expect(bytesIndexOf(new Uint8Array(body), enc.encode('\r\n\r\nру\r\n'))).toBeGreaterThan(0); }); it('does not share memory with the input audio', () => { const data = new Uint8Array([1, 2, 3]); const { body } = buildMultipart([], { ...file, data: data.buffer }, 'BND'); data[0] = 9; expect(bytesIndexOf(new Uint8Array(body), new Uint8Array([1, 2, 3]))).toBeGreaterThan(0); }); });