from __future__ import annotations import contextlib import io import json import sys import tempfile import unittest from pathlib import Path from unittest.mock import patch ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "resume_builder/helpers")) import application_records as records import application_tracker as tracker from argparse import Namespace import copy class ApplicationRecordTests(unittest.TestCase): def test_generated_views_are_current(self): self.assertEqual([], records.render(records.load(), check=True)) def test_imported_outcomes_preserve_interview_and_closed_state(self): applications = records.load()["applications"] self.assertEqual(len(applications), len({row["id"] for row in applications})) sbb = next(row for row in applications if row["company"] == "SBB") self.assertEqual("withdrawn_by_candidate", sbb["outcome"]) self.assertTrue(records.interview_reached(sbb)) for token in ("200040836", "JR106747", "JRQ$540-19848"): row = next(row for row in applications if token in row["label"]) self.assertEqual("rejected_no_interview", row["outcome"]) def test_both_agents_link_to_one_view(self): for name in ("AGENTS.md", "CLAUDE.md"): text = (ROOT / name).read_text(encoding="utf-8") section = text.split("## Active Sessions", 1)[1].split("## KB Corrections", 1)[0] self.assertIn("resume_builder/reference/current_applications.md", section) self.assertNotIn("| Session |", section) def test_atomic_write_leaves_no_shared_temp_file(self): with tempfile.TemporaryDirectory() as directory: path = Path(directory) / "state.json" records.atomic_write(path, '{"value": 1}\n') self.assertEqual({"value": 1}, json.loads(path.read_text())) self.assertEqual([path], list(Path(directory).iterdir())) def args(self, **overrides): fields = dict(id=None, company="Example", role="Engineer", date="2026-09-11", job_id="req-1", url=None, fit_class="core", evidence_fit=79, hard_gate="pass", channel="weak", outcome="applied", fit_override_reason=None, rubric_version="test", next_action="Await response.") fields.update(overrides) return Namespace(**fields) def test_more_than_ten_applications_and_idempotent_retry(self): data = records.load() for index in range(11): tracker.add_application(data, self.args(job_id=f"new-{index}", role=f"Engineer {index}")) count = len(data["applications"]) tracker.add_application(data, self.args(job_id="new-10", role="Engineer 10")) self.assertEqual(count, len(data["applications"])) def test_bad_dates_scores_and_unexplained_classification_are_rejected(self): for fields in ({"date":"2026-99-11"}, {"evidence_fit":101}, {"evidence_fit":float("nan")}, {"fit_class":"adjacent"}, {"hard_gate":"fail"}): with self.subTest(fields=fields), self.assertRaises(ValueError): tracker.add_application(records.load(), self.args(**fields)) def test_outcome_update_preserves_interview_event_and_is_idempotent(self): data = records.load() tracker.add_application(data, self.args()) row = data["applications"][-1] invited = Namespace(id=row["id"], date="2026-09-12", event="interview_invited", outcome=None, note=None, next_action=None) tracker.update_application(data, invited) withdrawn = Namespace(id=row["id"], date="2026-09-13", event=None, outcome="withdrawn_by_candidate", note="Decision", next_action=None) tracker.update_application(data, withdrawn) tracker.update_application(data, withdrawn) self.assertEqual(3, len(row["events"])) self.assertTrue(records.interview_reached(row)) self.assertEqual("withdrawn_by_candidate", row["outcome"]) tracker.add_application(data, self.args()) self.assertEqual("withdrawn_by_candidate", row["outcome"]) def test_transaction_failure_does_not_write_registry(self): with tempfile.TemporaryDirectory() as directory: path = Path(directory) / "applications.json" initial = json.dumps(records.load()) path.write_text(initial, encoding="utf-8") with patch.object(records, "REGISTRY", path): with self.assertRaises(ValueError), records.transaction() as data: data["applications"][0]["outcome"] = "made-up" self.assertEqual(initial, path.read_text(encoding="utf-8")) self.assertFalse(path.with_suffix(".lock").exists()) path.with_suffix(".lock").write_text("another writer") with self.assertRaises(ValueError), records.transaction(): pass