from __future__ import annotations from types import SimpleNamespace from sqlalchemy import select from gitea_codex_bot.config import get_settings from gitea_codex_bot.db import get_session_factory from gitea_codex_bot.models import ReviewJob from gitea_codex_bot.services.comments import get_persistent_review_comment_id, upsert_persistent_review_comment_id from gitea_codex_bot.services.jobs import enqueue_job from gitea_codex_bot.services.repo_config import RepoReviewConfig from gitea_codex_bot.types import ParsedCommand from gitea_codex_bot.workers.dispatcher import process_one_job def test_process_one_job_always_posts_new_review_comment(monkeypatch) -> None: posted_ids: list[int] = [] session_factory = get_session_factory() with session_factory() as session: job = enqueue_job( session, repo="acme/repo", pr_number=9, head_sha="deadbeef", trigger_comment_id=111, trigger_comment_body="@codex review", requested_by="alice", command=ParsedCommand(name="review", raw="@codex review"), ) upsert_persistent_review_comment_id( session, repo=job.repo, pr_number=job.pr_number, head_sha=job.head_sha, comment_id=289, ) monkeypatch.setattr( "gitea_codex_bot.workers.dispatcher.run_review_ephemeral", lambda *_args, **_kwargs: ( {"verdict": "has_issues", "confidence": 0.7, "summary": "runner error", "findings": []}, RepoReviewConfig(configured=True, enabled=True), ), ) class _FakeGiteaClient: def __init__(self, _settings) -> None: pass def get_pull_request(self, _repo: str, _pr_number: int): return SimpleNamespace(is_fork=False) def post_issue_comment(self, _repo: str, _pr_number: int, _body: str) -> int: new_id = 990 posted_ids.append(new_id) return new_id monkeypatch.setattr("gitea_codex_bot.workers.dispatcher.GiteaClient", _FakeGiteaClient) assert process_one_job(get_settings()) is True assert posted_ids == [990] with session_factory() as session: persisted_comment_id = get_persistent_review_comment_id(session, "acme/repo", 9) assert persisted_comment_id == 990 stored_job = session.execute(select(ReviewJob).where(ReviewJob.id == job.id)).scalar_one() assert stored_job.status.value == "succeeded" def test_process_one_job_passes_full_trigger_message_to_runner(monkeypatch) -> None: captured: dict[str, str] = {} session_factory = get_session_factory() with session_factory() as session: enqueue_job( session, repo="acme/repo", pr_number=10, head_sha="cafebabe", trigger_comment_id=112, trigger_comment_body="@codex review security --full\nFocus auth/session handling.", requested_by="alice", command=ParsedCommand(name="review", raw="@codex review security --full", arguments=["security", "--full"]), ) def _fake_run_review_ephemeral(_settings, *, repo: str, pr_number: int, command: ParsedCommand): captured["raw"] = command.raw return {"verdict": "correct", "confidence": 0.9, "summary": "ok", "findings": []}, RepoReviewConfig(configured=True, enabled=True) class _FakeGiteaClient: def __init__(self, _settings) -> None: pass def get_pull_request(self, _repo: str, _pr_number: int): return SimpleNamespace(is_fork=False) def post_issue_comment(self, _repo: str, _pr_number: int, _body: str) -> int: return 901 monkeypatch.setattr("gitea_codex_bot.workers.dispatcher.run_review_ephemeral", _fake_run_review_ephemeral) monkeypatch.setattr("gitea_codex_bot.workers.dispatcher.GiteaClient", _FakeGiteaClient) assert process_one_job(get_settings()) is True assert captured["raw"] == "@codex review security --full\nFocus auth/session handling." def test_process_one_job_skips_review_when_repo_config_disabled(monkeypatch) -> None: posted_comments: list[str] = [] session_factory = get_session_factory() with session_factory() as session: job = enqueue_job( session, repo="acme/repo", pr_number=11, head_sha="badc0de", trigger_comment_id=113, trigger_comment_body="@codex review", requested_by="alice", command=ParsedCommand(name="review", raw="@codex review"), ) monkeypatch.setattr( "gitea_codex_bot.workers.dispatcher.run_review_ephemeral", lambda *_args, **_kwargs: ( {"verdict": "correct", "confidence": 1.0, "summary": "ok", "findings": []}, RepoReviewConfig(configured=True, enabled=False), ), ) class _FakeGiteaClient: def __init__(self, _settings) -> None: pass def get_pull_request(self, _repo: str, _pr_number: int): return SimpleNamespace(is_fork=False) def post_issue_comment(self, _repo: str, _pr_number: int, body: str) -> int: posted_comments.append(body) return 902 monkeypatch.setattr("gitea_codex_bot.workers.dispatcher.GiteaClient", _FakeGiteaClient) assert process_one_job(get_settings()) is True assert any("Review is disabled" in body for body in posted_comments) with session_factory() as session: stored_job = session.execute(select(ReviewJob).where(ReviewJob.id == job.id)).scalar_one() assert stored_job.status.value == "skipped" def test_process_one_job_help_command_posts_summary(monkeypatch) -> None: posted_comments: list[str] = [] session_factory = get_session_factory() with session_factory() as session: enqueue_job( session, repo="acme/repo", pr_number=12, head_sha="abc12345", trigger_comment_id=114, trigger_comment_body="@codex -h", requested_by="alice", command=ParsedCommand(name="help", raw="@codex -h"), ) class _FakeGiteaClient: def __init__(self, _settings) -> None: pass def list_issue_comments(self, _repo: str, _pr_number: int): return [ {"body": "Please check auth edge cases", "user": {"username": "alice"}}, {"body": "On it, running review now.", "user": {"username": "codex-bot"}}, ] def post_issue_comment(self, _repo: str, _pr_number: int, body: str) -> int: posted_comments.append(body) return 903 monkeypatch.setattr("gitea_codex_bot.workers.dispatcher.GiteaClient", _FakeGiteaClient) assert process_one_job(get_settings()) is True assert posted_comments body = posted_comments[0] assert "## Codex Help" in body assert "@codex -h" in body assert "@codex fix" not in body assert "Discussion summary" in body assert "@alice: Please check auth edge cases" in body