Files
Conflict_Checker/tests/api/test_job_log.py
T
John Wilganowski afa1089311
Docker Release / build-and-push (push) Successful in 55s
Docker Release / release (push) Skipped
Add job run logs, OpenRouter model picker, and discipline grouping.
- Job logs: each job's stdout/stderr is teed into outputs/<id>/job.log
  (survives restarts) and served at GET /jobs/{id}/log as text/plain, so
  full run logs can be shared for debugging and refinement.
- Model picker: GET /models proxies OpenRouter's public model list with
  per-1M-token pricing (1h cache, 502 on failure); the UI shows a model
  dropdown with costs when OpenRouter compute is selected, and the pick
  overrides vision+text models for that job (Classic and Agent modes).
- Conflicts in the report view are grouped by discipline pair
  (collapsible sections, severity-ordered within groups) instead of one
  flat severity-only list.
2026-07-28 21:23:42 +00:00

78 lines
2.5 KiB
Python

import threading
import pytest
from fastapi.testclient import TestClient
import backend.jobs as jobs
from backend.main import app
class _SyncThread:
"""Drop-in threading.Thread replacement that runs the target inline."""
def __init__(self, target=None, args=(), kwargs=None, **_ignored):
self._target = target
self._args = args
self._kwargs = kwargs or {}
def start(self):
self._target(*self._args, **self._kwargs)
@pytest.fixture
def job_env(monkeypatch, tmp_path):
monkeypatch.setattr("backend.config.OUTPUT_DIR", str(tmp_path))
monkeypatch.setattr(threading, "Thread", _SyncThread)
monkeypatch.setattr("backend.jobs.send_conflict_report", lambda *a, **k: True)
pdf = tmp_path / "set.pdf"
pdf.write_bytes(b"%PDF-1.4\n")
yield tmp_path
jobs._jobs.clear()
def test_job_log_captures_pipeline_output(job_env, monkeypatch):
def fake_runner(pdf_path, **kwargs):
print("STAGE banner: fake wave ran")
return {"source": "set.pdf", "summary": {"conflicts_found": 0}}
monkeypatch.setattr("backend.jobs.run_pipeline", fake_runner)
job_id = jobs.create_job(str(job_env / "set.pdf"), "set.pdf", pipeline_mode="classic")
log_path = job_env / job_id / "job.log"
assert log_path.is_file()
content = log_path.read_text()
assert "STAGE banner: fake wave ran" in content
assert job_id in content # header line
def test_job_log_endpoint_serves_log_and_404s(job_env, monkeypatch):
monkeypatch.setattr(
"backend.jobs.run_pipeline",
lambda pdf_path, **kw: {"source": "s", "summary": {}},
)
job_id = jobs.create_job(str(job_env / "set.pdf"), "set.pdf", pipeline_mode="classic")
client = TestClient(app)
ok = client.get(f"/jobs/{job_id}/log")
assert ok.status_code == 200
assert ok.headers["content-type"].startswith("text/plain")
assert "Job " + job_id in ok.text
assert client.get("/jobs/nope/log").status_code == 404
def test_model_override_set_and_cleared_around_run(job_env, monkeypatch):
from backend import llm
seen = {}
def fake_runner(pdf_path, **kwargs):
seen["override"] = llm._model_override
return {"source": "set.pdf", "summary": {}}
monkeypatch.setattr("backend.jobs.run_pipeline", fake_runner)
jobs.create_job(str(job_env / "set.pdf"), "set.pdf",
pipeline_mode="classic", model="openai/gpt-4o")
assert seen["override"] == "openai/gpt-4o"
assert llm._model_override is None # cleared after the run