import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { createDatabase } from "../connection"; import { initializeSchema } from "../schema"; import { createArticleJob, createBrandTemplate, getArticleJob, getBrandTemplate, getFactCard, getLatestOptimizedArticle, getLatestQaReport, listBrandTemplates, saveFactCard, saveOptimizedArticle, saveQaReport, } from "../repositories"; describe("sqlite repositories", () => { let tempDir: string; let dbPath: string; beforeEach(() => { tempDir = mkdtempSync(join(tmpdir(), "geo-agent-db-")); dbPath = join(tempDir, "test.db"); const db = createDatabase(dbPath); initializeSchema(db); db.close(); }); afterEach(() => { rmSync(tempDir, { recursive: true, force: true }); }); it("initializes all required tables", () => { const db = createDatabase(dbPath); const tables = db .prepare( "select name from sqlite_master where type = 'table' order by name", ) .all() .map((row) => (row as { name: string }).name); db.close(); expect(tables).toEqual([ "article_jobs", "brand_templates", "calibration_events", "fact_cards", "optimized_articles", "performance_snapshots", "publication_records", "qa_reports", "rubric_versions", "scoring_runs", ]); }); it("inserts and fetches a brand template", () => { const created = createBrandTemplate(dbPath, { brand_name: "Example", company_full_name: "Example Technology Co., Ltd.", company_short_names: ["Example Tech"], product_names: ["Example GEO"], target_industries: ["GEO optimization"], target_audience: ["Marketing teams"], verified_claims: ["Eight years of experience"], forbidden_claims: ["Industry first"], tone_rules: { official_site: "official voice" }, }); expect(getBrandTemplate(dbPath, created.id)).toMatchObject({ id: created.id, brand_name: "Example", company_short_names: ["Example Tech"], }); expect(listBrandTemplates(dbPath)).toHaveLength(1); }); it("inserts and fetches an article job", () => { const job = createArticleJob(dbPath, { source_title: "Original Title", source_body: "Original body", image_inputs: [{ type: "description", content: "Dashboard" }], publish_platform: "official_site", user_instructions: "Stay factual", }); expect(getArticleJob(dbPath, job.id)).toMatchObject({ id: job.id, source_title: "Original Title", image_inputs: [{ type: "description", content: "Dashboard" }], }); }); it("saves a confirmed fact card for a job", () => { const job = createArticleJob(dbPath, { source_title: "Original Title", source_body: "Original body", image_inputs: [], publish_platform: "media_article", user_instructions: "", }); saveFactCard(dbPath, job.id, { company_full_name: "Example Technology Co., Ltd.", company_short_names: ["Example"], brand_names: ["Example"], product_names: ["Example GEO"], target_industry: "GEO optimization", target_audience: "Marketing teams", experience_years: 8, core_claims: ["Eight years of experience"], forbidden_claims: [], image_topics: [], uncertain_items: [], is_ready_for_optimization: true, confirmed_by_user: true, }); expect(getFactCard(dbPath, job.id)).toMatchObject({ job_id: job.id, company_full_name: "Example Technology Co., Ltd.", confirmed_by_user: true, }); }); it("increments optimized article revisions", () => { const job = createArticleJob(dbPath, { source_title: "Original Title", source_body: "Original body", image_inputs: [], publish_platform: "comparison_review", user_instructions: "", }); const first = saveOptimizedArticle(dbPath, job.id, { title: "Optimized v1", summary: "Summary", body_markdown: "Body", image_suggestions: [], changed_sections: ["title"], requires_user_confirmation: [], }); const second = saveOptimizedArticle(dbPath, job.id, { title: "Optimized v2", summary: "Summary", body_markdown: "Body", image_suggestions: [], changed_sections: ["title"], requires_user_confirmation: [], }); expect(first.revision).toBe(1); expect(second.revision).toBe(2); expect(getLatestOptimizedArticle(dbPath, job.id)?.title).toBe( "Optimized v2", ); }); it("saves and fetches the latest QA report by job and revision", () => { const job = createArticleJob(dbPath, { source_title: "Original Title", source_body: "Original body", image_inputs: [], publish_platform: "recommendation_list", user_instructions: "", }); saveOptimizedArticle(dbPath, job.id, { title: "Optimized", summary: "Summary", body_markdown: "Body", image_suggestions: [], changed_sections: [], requires_user_confirmation: [], }); saveQaReport(dbPath, job.id, 1, { overall_status: "warn", checks: [ { rule_id: "image_text_match", status: "warn", evidence: "Image confidence is low.", reason: "Image descriptions are sparse.", suggested_fix: "Review image placement.", target_agent: null, }, ], }); expect(getLatestQaReport(dbPath, job.id)).toMatchObject({ job_id: job.id, revision: 1, overall_status: "warn", }); }); });