Files
gbrain/test/ingest-log-source-id.test.ts
triton6564685 18f2dcdbe5 feat(v0.18.2.fork.1): migration v25 — ingest_log.source_id
Closes the upstream Step 5 deferral noted in schema-embedded.ts:202-204
("ingest_log.source_id is NOT added yet — lands in v17 alongside the sync
rewrite (Step 5)"). Upstream's v17 only addressed pages.source_id; the
ingest_log half was deferred without ever shipping.

- migrate.ts: v25 ALTER TABLE adds source_id NOT NULL DEFAULT 'default'
  REFERENCES sources(id) ON DELETE CASCADE + idx_ingest_log_source_id
- schema-embedded.ts: fresh-install schema mirrors the migration outcome
- types.ts: IngestLogInput.source_id?: string
- {postgres,pglite}-engine.ts logIngest: thread entry.source_id when set,
  fall back to schema DEFAULT 'default' otherwise
- import.ts + sync.ts: pass opts.sourceId to logIngest call sites

Tests:
- test/ingest-log-source-id.test.ts (new): col schema, FK enforcement,
  logIngest write-through both source-explicit and default-fallback paths

Strategy: fork-local commit, NOT sent upstream — kept separate from
Phase 1 to make it easy to drop if upstream eventually adds their own
ingest_log.source_id (would just be a rebase delete).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 22:14:22 +08:00

99 lines
3.3 KiB
TypeScript

/**
* v0.18.2.fork.1 — migration v25 ingest_log.source_id.
*
* Closes the upstream Step 5 deferral noted at schema-embedded.ts:202-204:
*
* "ingest_log.source_id is NOT added yet — lands in v17 alongside the
* sync rewrite (Step 5)"
*
* Verifies:
* - migration v25 adds the column with NOT NULL DEFAULT 'default'
* - existing rows backfill to 'default' (the schema seed exists)
* - new rows can be written with explicit source_id
* - logIngest signature accepts entry.source_id and threads it through
* - omitting source_id falls back to schema DEFAULT 'default'
*/
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
import { PGLiteEngine } from '../src/core/pglite-engine.ts';
let engine: PGLiteEngine;
beforeAll(async () => {
engine = new PGLiteEngine();
await engine.connect({ type: 'pglite' } as never);
await engine.initSchema();
await engine.executeRaw(
`INSERT INTO sources (id, name, config) VALUES
('memory-dashboard', 'memory-dashboard', '{"federated": true}'::jsonb)
ON CONFLICT (id) DO NOTHING`,
);
});
afterAll(async () => {
await engine.disconnect();
});
describe('v25 — ingest_log.source_id schema', () => {
test('source_id column exists with NOT NULL DEFAULT default', async () => {
const rows = await engine.executeRaw<{ column_default: string | null; is_nullable: string }>(
`SELECT column_default, is_nullable FROM information_schema.columns
WHERE table_name = 'ingest_log' AND column_name = 'source_id'`,
);
expect(rows.length).toBe(1);
expect(rows[0].is_nullable).toBe('NO');
expect(rows[0].column_default).toContain('default');
});
test('idx_ingest_log_source_id index exists', async () => {
const rows = await engine.executeRaw<{ indexname: string }>(
`SELECT indexname FROM pg_indexes WHERE indexname = 'idx_ingest_log_source_id'`,
);
expect(rows.length).toBe(1);
});
test('FK to sources(id) is enforced (insert with bogus source rejected)', async () => {
let threw = false;
try {
await engine.executeRaw(
`INSERT INTO ingest_log (source_id, source_type, source_ref, summary)
VALUES ('does-not-exist', 'directory', '/tmp', '')`,
);
} catch {
threw = true;
}
expect(threw).toBe(true);
});
});
describe('v25 — logIngest write-through', () => {
test('logIngest with source_id writes to that source', async () => {
await engine.logIngest({
source_type: 'directory',
source_ref: '/tmp/md',
pages_updated: ['a', 'b'],
summary: 'test ingest md',
source_id: 'memory-dashboard',
});
const rows = await engine.executeRaw<{ source_id: string }>(
`SELECT source_id FROM ingest_log WHERE source_ref = '/tmp/md'`,
);
expect(rows.length).toBe(1);
expect(rows[0].source_id).toBe('memory-dashboard');
});
test('logIngest without source_id falls back to schema DEFAULT default', async () => {
await engine.logIngest({
source_type: 'directory',
source_ref: '/tmp/legacy',
pages_updated: [],
summary: 'legacy single-source caller',
});
const rows = await engine.executeRaw<{ source_id: string }>(
`SELECT source_id FROM ingest_log WHERE source_ref = '/tmp/legacy'`,
);
expect(rows.length).toBe(1);
expect(rows[0].source_id).toBe('default');
});
});