import { describe, expect, it, vi } from 'vitest'; import { createMinioIngestTool, minioIngestInternals } from './minio-ingest.js'; import type { BackupConfig } from '../../config/schema.js'; import type { MemoryStore } from '../../memory/store.js'; function makeBackupConfig(overrides?: Partial): BackupConfig { return { enabled: true, schedule: undefined, interval: '24h', run_on_start: false, notify: undefined, failure_threshold: 1, notify_recovery: true, local_dir: '~/.local/share/flynn/backups', include_vectors: true, minio: { enabled: true, endpoint: 'localhost:9000', access_key: 'minio-admin', secret_key: 'minio-secret', bucket: 'flynn-knowledge', prefix: 'flynn', secure: false, }, ...overrides, }; } describe('minio ingest internals', () => { it('accepts known text-like extensions', () => { expect(minioIngestInternals.isLikelyTextObject('notes/today.md')).toBe(true); expect(minioIngestInternals.isLikelyTextObject('logs/daemon.log')).toBe(true); expect(minioIngestInternals.isLikelyTextObject('manual.pdf')).toBe(true); expect(minioIngestInternals.isLikelyTextObject('manual.docx')).toBe(true); }); it('rejects likely binary extensions', () => { expect(minioIngestInternals.isLikelyTextObject('manual.exe')).toBe(false); }); }); describe('createMinioIngestTool', () => { it('ingests object and writes to memory', async () => { const write = vi.fn(); const store = { write } as unknown as MemoryStore; const execRunner = vi.fn(async () => ({ stdout: '# Runbook\n\nRestart service before deploy.\n', stderr: '', })); const tool = createMinioIngestTool(makeBackupConfig(), store, { execRunner, now: () => new Date('2026-02-16T15:00:00.000Z'), }); const result = await tool.execute({ object_key: 'knowledge/runbook.md', namespace: 'global/runbooks', mode: 'append', }); expect(result.success).toBe(true); expect(result.output).toContain('Ingested MinIO object'); expect(write).toHaveBeenCalledWith( 'global/runbooks', expect.stringContaining('source: minio://flynn-knowledge/knowledge/runbook.md'), 'append', ); expect(execRunner).toHaveBeenCalledWith( 'mc', ['cat', 'flynningest/flynn-knowledge/knowledge/runbook.md'], expect.objectContaining({ env: expect.any(Object) }), ); }); it('rejects unsupported binary object unless force=true', async () => { const write = vi.fn(); const store = { write } as unknown as MemoryStore; const execRunner = vi.fn(); const tool = createMinioIngestTool(makeBackupConfig(), store, { execRunner }); const result = await tool.execute({ object_key: 'knowledge/diagram.exe' }); expect(result.success).toBe(false); expect(result.error).toContain('Unsupported object type'); expect(execRunner).not.toHaveBeenCalled(); }); it('extracts PDF text with pdftotext', async () => { const write = vi.fn(); const store = { write } as unknown as MemoryStore; const execRunner = vi.fn(async (_file: string, args: string[]) => { if (args[0] === 'cp') { return { stdout: '', stderr: '' }; } if (_file === 'pdftotext') { return { stdout: 'Extracted PDF text', stderr: '' }; } return { stdout: '', stderr: '' }; }); const tool = createMinioIngestTool(makeBackupConfig(), store, { execRunner }); const result = await tool.execute({ object_key: 'knowledge/diagram.pdf', mode: 'replace', }); expect(result.success).toBe(true); expect(write).toHaveBeenCalledWith( 'global/knowledge', expect.stringContaining('Extracted PDF text'), 'replace', ); expect(execRunner).toHaveBeenCalledWith( 'mc', expect.arrayContaining(['cp', 'flynningest/flynn-knowledge/knowledge/diagram.pdf']), expect.objectContaining({ env: expect.any(Object) }), ); expect(execRunner).toHaveBeenCalledWith( 'pdftotext', expect.arrayContaining(['-q']), expect.any(Object), ); }); it('extracts DOCX text with pandoc', async () => { const write = vi.fn(); const store = { write } as unknown as MemoryStore; const execRunner = vi.fn(async (_file: string, args: string[]) => { if (_file === 'mc' && args[0] === 'cp') { return { stdout: '', stderr: '' }; } if (_file === 'pandoc') { return { stdout: 'Extracted DOCX text', stderr: '' }; } return { stdout: '', stderr: '' }; }); const tool = createMinioIngestTool(makeBackupConfig(), store, { execRunner }); const result = await tool.execute({ object_key: 'knowledge/spec.docx', mode: 'replace', }); expect(result.success).toBe(true); expect(write).toHaveBeenCalledWith( 'global/knowledge', expect.stringContaining('Extracted DOCX text'), 'replace', ); expect(execRunner).toHaveBeenCalledWith( 'pandoc', expect.arrayContaining(['-t', 'plain']), expect.any(Object), ); }); it('allows non-text extension when force=true', async () => { const write = vi.fn(); const store = { write } as unknown as MemoryStore; const execRunner = vi.fn(async () => ({ stdout: 'PDF text extracted upstream', stderr: '', })); const tool = createMinioIngestTool(makeBackupConfig(), store, { execRunner }); const result = await tool.execute({ object_key: 'knowledge/diagram.pdf', force: true, mode: 'replace', }); expect(result.success).toBe(true); expect(write).toHaveBeenCalledWith( 'global/knowledge', expect.stringContaining('PDF text extracted upstream'), 'replace', ); }); it('returns an error when minio is disabled', async () => { const write = vi.fn(); const store = { write } as unknown as MemoryStore; const tool = createMinioIngestTool(makeBackupConfig({ minio: { enabled: false, endpoint: undefined, access_key: undefined, secret_key: undefined, bucket: undefined, prefix: 'flynn', secure: true, }, }), store); const result = await tool.execute({ object_key: 'notes/today.md' }); expect(result.success).toBe(false); expect(result.error).toContain('backup.minio.enabled=true'); }); });