package eval import ( "context" "database/sql" "os" "path/filepath" "strings" "testing" _ "modernc.org/sqlite" ) func setupReportDB(t *testing.T) *sql.DB { t.Helper() db, err := sql.Open("sqlite", ":memory:") if err != nil { t.Fatal(err) } schema := ` CREATE TABLE jobs ( id INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT UNIQUE NOT NULL, company TEXT NOT NULL, title TEXT NOT NULL, description TEXT, source TEXT, discovered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE applications ( id INTEGER PRIMARY KEY AUTOINCREMENT, job_id INTEGER NOT NULL, status TEXT NOT NULL DEFAULT 'Evaluated', score REAL, archetype TEXT, legitimacy TEXT, evaluated_at TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE reports ( id INTEGER PRIMARY KEY AUTOINCREMENT, application_id INTEGER NOT NULL, report_md TEXT, seq INTEGER, slug TEXT, legitimacy TEXT, score REAL, block_a TEXT, block_b TEXT, block_c TEXT, block_d TEXT, block_e TEXT, block_f TEXT, block_g TEXT, file_path TEXT, stories TEXT, generated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ` if _, err := db.Exec(schema); err != nil { t.Fatal(err) } return db } func TestSlugify(t *testing.T) { cases := map[string]string{ "Acme Corp": "acme-corp", "Dragos, Inc.": "dragos-inc", " Red/Canary ": "redcanary", "../../etc/passwd": "etcpasswd", "": "unknown", "!@#$%^&*()": "unknown", "Foo ___ Bar": "foo-bar", strings.Repeat("x", 80): strings.Repeat("x", 64), } for in, want := range cases { if got := Slugify(in); got != want { t.Errorf("Slugify(%q) = %q, want %q", in, got, want) } } } func TestReportWriter_SaveEndToEnd(t *testing.T) { db := setupReportDB(t) defer db.Close() tmp := t.TempDir() w := &ReportWriter{DB: db, ReportsDir: tmp} job := JobContext{ URL: "https://example.com/jobs/42", Company: "Acme Corp", Title: "Staff LLMOps", Source: "greenhouse", JDText: "Job description body.", } eval := Evaluation{ Archetype: "LLMOps", Score: 4.2, Legitimacy: TierHigh, BlockA: "## A) Role Summary\n\nStaff LLMOps role.", BlockB: "## B) CV Match\n\nStrong match.\n\n**Weighted score:** 4.2/5", BlockG: "## G) Posting Legitimacy\n\n**Assessment:** High Confidence", Raw: "full raw", } saved, err := w.Save(context.Background(), job, eval) if err != nil { t.Fatalf("Save: %v", err) } if saved.Seq != 1 { t.Errorf("Seq = %d, want 1", saved.Seq) } if saved.ApplicationID == 0 || saved.ReportID == 0 { t.Errorf("missing ids: app=%d report=%d", saved.ApplicationID, saved.ReportID) } if filepath.Dir(saved.FilePath) != tmp { t.Errorf("file path outside ReportsDir: %q", saved.FilePath) } if _, err := os.Stat(saved.FilePath); err != nil { t.Fatalf("report file missing: %v", err) } // Contents exercise RenderMarkdown: Block G appears before Block A. body, err := os.ReadFile(saved.FilePath) if err != nil { t.Fatal(err) } s := string(body) if !strings.Contains(s, "**Legitimacy:** high") { t.Errorf("missing legitimacy header in report:\n%s", s) } gIdx := strings.Index(s, "## G)") aIdx := strings.Index(s, "## A)") if gIdx == -1 || aIdx == -1 || gIdx > aIdx { t.Errorf("Block G should appear before Block A; gIdx=%d aIdx=%d", gIdx, aIdx) } // Save second eval for the same URL: seq should become 2, application // should be updated (not duplicated). eval.Score = 4.5 saved2, err := w.Save(context.Background(), job, eval) if err != nil { t.Fatalf("Save#2: %v", err) } if saved2.Seq != 2 { t.Errorf("Seq#2 = %d, want 2", saved2.Seq) } if saved2.ApplicationID != saved.ApplicationID { t.Errorf("application_id drifted: %d → %d", saved.ApplicationID, saved2.ApplicationID) } var appCount int if err := db.QueryRow(`SELECT COUNT(*) FROM applications`).Scan(&appCount); err != nil { t.Fatal(err) } if appCount != 1 { t.Errorf("applications count = %d, want 1", appCount) } var reportCount int if err := db.QueryRow(`SELECT COUNT(*) FROM reports`).Scan(&reportCount); err != nil { t.Fatal(err) } if reportCount != 2 { t.Errorf("reports count = %d, want 2", reportCount) } } func TestReportWriter_StatusPreservedWhenReEvaluated(t *testing.T) { db := setupReportDB(t) defer db.Close() w := &ReportWriter{DB: db, ReportsDir: t.TempDir()} job := JobContext{URL: "https://example.com/j/1", Company: "Beta", Title: "SWE", Source: "ashby"} eval := Evaluation{Score: 3.0, Legitimacy: TierCaution, BlockA: "## A) x", BlockG: "## G) y"} if _, err := w.Save(context.Background(), job, eval); err != nil { t.Fatalf("first save: %v", err) } // Simulate user advancing the pipeline. if _, err := db.Exec(`UPDATE applications SET status = 'Applied' WHERE job_id = (SELECT id FROM jobs WHERE url = ?)`, job.URL); err != nil { t.Fatal(err) } // Re-run eval — must NOT clobber 'Applied' back to 'Evaluated'. eval.Score = 3.5 if _, err := w.Save(context.Background(), job, eval); err != nil { t.Fatalf("second save: %v", err) } var status string if err := db.QueryRow(`SELECT status FROM applications WHERE job_id = (SELECT id FROM jobs WHERE url = ?)`, job.URL).Scan(&status); err != nil { t.Fatal(err) } if status != "Applied" { t.Errorf("status = %q, want Applied (re-eval should not regress)", status) } }