81 lines
3.1 KiB
TypeScript
81 lines
3.1 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { saveCapture, type Capture } from '../src/core/capture';
|
|
import { initialNote, readVoiceFields } from '../src/core/note';
|
|
import { MemoryNotes, MemoryRecordings, TestEnv } from './fakes';
|
|
|
|
const T = new Date('2026-09-27T11:15:03.000Z');
|
|
const buf = (...bytes: number[]) => new Uint8Array(bytes).buffer;
|
|
|
|
function setup() {
|
|
const env = new TestEnv(T);
|
|
const notes = new MemoryNotes();
|
|
const recordings = new MemoryRecordings();
|
|
return { env, notes, recordings, deps: { env, notes, recordings } };
|
|
}
|
|
|
|
const capture = (over: Partial<Capture> = {}): Capture => ({
|
|
chunks: [buf(1, 2), buf(3, 4, 5)],
|
|
mime: 'audio/mp4',
|
|
startedAt: T,
|
|
endedAt: new Date(T.getTime() + 47_000),
|
|
interrupted: false,
|
|
...over,
|
|
});
|
|
|
|
describe('saveCapture', () => {
|
|
it('writes concatenated audio and a saved note', async () => {
|
|
const { deps, notes, recordings } = setup();
|
|
const path = await saveCapture(deps, capture());
|
|
|
|
expect(path).toBe('voice/inbox/2026-09-27 14-15-03.md');
|
|
const audio = recordings.files.get('voice/source/20260927-111503-0000.m4a');
|
|
expect(new Uint8Array(audio!)).toEqual(new Uint8Array([1, 2, 3, 4, 5]));
|
|
expect(notes.files.get(path!)).toBe(
|
|
initialNote(
|
|
{
|
|
id: '20260927-111503-0000',
|
|
status: 'saved',
|
|
device: 'test-device',
|
|
audio: 'voice/source/20260927-111503-0000.m4a',
|
|
mime: 'audio/mp4',
|
|
attempts: 0,
|
|
},
|
|
47_000,
|
|
),
|
|
);
|
|
});
|
|
|
|
it.each([[[]], [[new ArrayBuffer(0)]], [[new ArrayBuffer(0), new ArrayBuffer(0)]]])(
|
|
'creates nothing for empty data %#',
|
|
async (chunks) => {
|
|
const { deps, notes, recordings } = setup();
|
|
expect(await saveCapture(deps, capture({ chunks, interrupted: true }))).toBeNull();
|
|
expect(notes.files.size).toBe(0);
|
|
expect(recordings.files.size).toBe(0);
|
|
},
|
|
);
|
|
|
|
it('marks interrupted recordings with duration', async () => {
|
|
const { deps, notes } = setup();
|
|
const path = await saveCapture(deps, capture({ interrupted: true }));
|
|
const content = notes.files.get(path!)!;
|
|
expect(readVoiceFields(content)['voice-interrupted']).toBe('true');
|
|
expect(content).toContain('> [!note] Запись прервана, сохранено 0:47');
|
|
});
|
|
|
|
it('adds (2), (3) on name collision', async () => {
|
|
const { deps, notes } = setup();
|
|
notes.files.set('voice/inbox/2026-09-27 14-15-03.md', 'чужая');
|
|
expect(await saveCapture(deps, capture())).toBe('voice/inbox/2026-09-27 14-15-03 (2).md');
|
|
expect(await saveCapture(deps, capture())).toBe('voice/inbox/2026-09-27 14-15-03 (3).md');
|
|
expect(notes.files.get('voice/inbox/2026-09-27 14-15-03.md')).toBe('чужая');
|
|
});
|
|
|
|
it('keeps full mime and picks extension from it', async () => {
|
|
const { deps, notes, recordings } = setup();
|
|
const path = await saveCapture(deps, capture({ mime: 'audio/webm;codecs=opus' }));
|
|
expect(recordings.files.has('voice/source/20260927-111503-0000.webm')).toBe(true);
|
|
expect(readVoiceFields(notes.files.get(path!)!)['voice-mime']).toBe('audio/webm;codecs=opus');
|
|
});
|
|
});
|