import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import fs from 'node:fs/promises'; import path from 'node:path'; import os from 'node:os'; import { resetDb } from '../../helpers/db.js'; import { migrateUp } from '../../../lib/db/migrate.js'; import { stopBoss, waitForJob } from '../../helpers/boss.js'; import * as queue from '../../../lib/jobs/queue.js'; import { registerWorkers } from '../../../lib/jobs/index.js'; import * as spaces from '../../../lib/db/repos/spaces.js'; import * as refs from '../../../lib/db/repos/refs.js'; import * as jobsRepo from '../../../lib/db/repos/jobs.js'; let tmpRoot; beforeEach(async () => { tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'void-blobs-')); process.env.BLOB_ROOT = tmpRoot; await resetDb(); await migrateUp(); await queue.start(); await registerWorkers(); }); afterEach(async () => { await stopBoss(); }); describe('ingest.blob worker', () => { it('creates a ref pointing at the blob (kind=file)', async () => { const sp = await spaces.create({ slug: 'b', name: 'B' }, { kind: 'user', id: null }); const upTmp = path.join(tmpRoot, 'up.tmp'); await fs.writeFile(upTmp, Buffer.from('hello blob')); const id = await queue.enqueue('ingest.blob', { space_id: sp.id, tmp_path: upTmp, filename: 'hello.txt', content_type: 'text/plain' }); const j = await waitForJob('ingest.blob', id, { timeoutMs: 10_000 }); expect(j.state).toBe('completed'); const rows = await refs.list({ space_id: sp.id }); expect(rows[0].kind).toBe('file'); expect(rows[0].blob_path).toBeTruthy(); expect(rows[0].title).toBe('hello.txt'); }); it('classifies image content_type as image kind', async () => { const sp = await spaces.create({ slug: 'b2', name: 'B2' }, { kind: 'user', id: null }); const upTmp = path.join(tmpRoot, 'pic.tmp'); await fs.writeFile(upTmp, Buffer.from([0x89, 0x50, 0x4e, 0x47])); // PNG magic const id = await queue.enqueue('ingest.blob', { space_id: sp.id, tmp_path: upTmp, filename: 'x.png', content_type: 'image/png' }); await waitForJob('ingest.blob', id, { timeoutMs: 10_000 }); const rows = await refs.list({ space_id: sp.id }); expect(rows[0].kind).toBe('image'); }); it('enqueues extract.pdf after creating a pdf ref', async () => { const sp = await spaces.create({ slug: 'bpdf', name: 'BPdf' }, { kind: 'user', id: null }); const upTmp = path.join(tmpRoot, 'doc.tmp'); await fs.writeFile(upTmp, Buffer.from('%PDF-1.4 ...')); const id = await queue.enqueue('ingest.blob', { space_id: sp.id, tmp_path: upTmp, filename: 'a.pdf', content_type: 'application/pdf' }); await waitForJob('ingest.blob', id, { timeoutMs: 10_000 }); const pending = await jobsRepo.list({ name: 'extract.pdf' }); expect(pending.length).toBeGreaterThan(0); }); it('enqueues extract.image after creating an image ref', async () => { const sp = await spaces.create({ slug: 'bimg', name: 'BImg' }, { kind: 'user', id: null }); const upTmp = path.join(tmpRoot, 'pic.tmp'); await fs.writeFile(upTmp, Buffer.from([0x89, 0x50, 0x4e, 0x47])); const id = await queue.enqueue('ingest.blob', { space_id: sp.id, tmp_path: upTmp, filename: 'a.png', content_type: 'image/png' }); await waitForJob('ingest.blob', id, { timeoutMs: 10_000 }); const pending = await jobsRepo.list({ name: 'extract.image' }); expect(pending.length).toBeGreaterThan(0); }); });