import-audio.test.mjs
104 lines 3.4 KB
Raw
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor ⚠ breaking 1 day ago
1 /**
2 * Audio/video importer: file checks, dry-run skips Whisper, happy path with mocked fetch.
3 */
4 import { describe, it, beforeEach, afterEach } from 'node:test';
5 import assert from 'node:assert';
6 import fs from 'fs';
7 import os from 'os';
8 import path from 'path';
9 import { fileURLToPath } from 'url';
10 import { importAudio, importVideo } from '../lib/importers/audio.mjs';
11 import { readNote } from '../lib/vault.mjs';
12
13 const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
15 describe('importAudio / importVideo', () => {
16 let vault;
17 let tmpDir;
18 let origFetch;
19 let origKey;
20
21 beforeEach(() => {
22 vault = fs.mkdtempSync(path.join(os.tmpdir(), 'kn-audio-vault-'));
23 tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'kn-audio-in-'));
24 origFetch = globalThis.fetch;
25 process.env.OPENAI_API_KEY = 'sk-test-mock';
26 });
27
28 afterEach(() => {
29 globalThis.fetch = origFetch;
30 delete process.env.OPENAI_API_KEY;
31 try {
32 fs.rmSync(vault, { recursive: true, force: true });
33 } catch (_) {}
34 try {
35 fs.rmSync(tmpDir, { recursive: true, force: true });
36 } catch (_) {}
37 });
38
39 const ctxBase = (dryRun) => ({
40 vaultPath: vault,
41 outputBase: 'inbox',
42 project: undefined,
43 tags: [],
44 dryRun,
45 });
46
47 it('throws when input file is missing', async () => {
48 await assert.rejects(
49 () => importAudio(path.join(tmpDir, 'missing.mp3'), ctxBase(false)),
50 /file not found/i
51 );
52 });
53
54 it('throws on unsupported extension', async () => {
55 const p = path.join(tmpDir, 'x.ogg');
56 fs.writeFileSync(p, 'x');
57 await assert.rejects(() => importAudio(p, ctxBase(false)), /Unsupported format/);
58 });
59
60 it('dryRun does not call OpenAI and does not write a note', async () => {
61 let fetchCalls = 0;
62 globalThis.fetch = async () => {
63 fetchCalls++;
64 return new Response(JSON.stringify({ text: 'should not run' }), { status: 200 });
65 };
66
67 const p = path.join(tmpDir, 'clip.mp3');
68 fs.writeFileSync(p, Buffer.from([0, 0, 0]));
69
70 const result = await importAudio(p, ctxBase(true));
71 assert.strictEqual(fetchCalls, 0);
72 assert.strictEqual(result.count, 1);
73 assert.ok(result.imported[0].path.includes('audio_'));
74 assert.throws(() => readNote(vault, result.imported[0].path), /Note not found/);
75 });
76
77 it('writes note with transcript when not dryRun (fetch mocked)', async () => {
78 globalThis.fetch = async () =>
79 new Response(JSON.stringify({ text: 'Fixture transcript line.' }), { status: 200 });
80
81 const p = path.join(tmpDir, 'talk.m4a');
82 fs.writeFileSync(p, Buffer.from([0, 1, 2, 3]));
83
84 const result = await importAudio(p, ctxBase(false));
85 assert.strictEqual(result.count, 1);
86 const note = readNote(vault, result.imported[0].path);
87 assert.strictEqual(note.frontmatter.source, 'audio');
88 assert.strictEqual(note.frontmatter.title, 'talk');
89 assert.ok(String(note.body || '').includes('Fixture transcript'));
90 });
91
92 it('importVideo sets source: video', async () => {
93 globalThis.fetch = async () =>
94 new Response(JSON.stringify({ text: 'Video track speech.' }), { status: 200 });
95
96 const p = path.join(tmpDir, 'clip.mp4');
97 fs.writeFileSync(p, Buffer.from([0, 1, 2, 3]));
98
99 const result = await importVideo(p, ctxBase(false));
100 const note = readNote(vault, result.imported[0].path);
101 assert.strictEqual(note.frontmatter.source, 'video');
102 assert.ok(result.imported[0].path.includes('video_'));
103 });
104 });
File History 2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor 1 day ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6 docs: accept Calendar Events v0 spec with Phase 0 security … Human 1 day ago