defmodule TarakanWeb.API.WorkControllerTest do use TarakanWeb.ConnCase, async: true alias Tarakan.Accounts alias Tarakan.Accounts.ApiCredentials alias Tarakan.Repositories.Repository alias Tarakan.Repo alias Tarakan.Work setup %{conn: conn} do creator = github_account_fixture() repository = listed_github_repository_fixture(creator) worker = account_fixture() worker_token = client_token(worker) creator_token = client_token(creator) %{ conn: conn, creator: creator, creator_token: creator_token, repository: repository, worker: worker, worker_token: worker_token } end defp authed(conn, token), do: put_req_header(conn, "authorization", "Bearer #{token}") defp client_token(account) do {:ok, token, _credential} = ApiCredentials.create(account, %{ name: "work controller test", scopes: ["tasks:read", "tasks:claim", "contributions:write"] }) token end test "all work queue endpoints require an API token", %{conn: conn} do conn = get(conn, ~p"/api/github.com/openai/codex/jobs") assert json_response(conn, 401)["error"] =~ "API token" end test "lists the global open jobs queue", %{ conn: conn, creator: creator, repository: repository, worker_token: token } do task = review_task_fixture(repository, creator) conn = conn |> authed(token) |> get(~p"/api/jobs") assert %{"jobs" => jobs} = json_response(conn, 200) assert Enum.any?(jobs, &(&1["id"] == task.id)) match = Enum.find(jobs, &(&1["id"] == task.id)) assert match["status"] == "open" assert match["repository"]["owner"] == repository.owner assert match["repository"]["name"] == repository.name assert match["repository"]["primary_language"] == repository.primary_language assert match["repository"]["stars_count"] == repository.stars_count end test "global jobs queue filters by language and min stars", %{ conn: conn, creator: creator, repository: repository, worker_token: token } do task = review_task_fixture(repository, creator) # Fixture repo is Rust with 42000 stars (GitHub stub). conn = conn |> authed(token) |> get(~p"/api/jobs", %{"language" => "Rust", "min_stars" => "1000"}) assert %{"jobs" => jobs} = json_response(conn, 200) assert Enum.any?(jobs, &(&1["id"] == task.id)) conn = build_conn() |> authed(token) |> get(~p"/api/jobs", %{"language" => "Elixir"}) assert %{"jobs" => jobs} = json_response(conn, 200) refute Enum.any?(jobs, &(&1["id"] == task.id)) conn = build_conn() |> authed(token) |> get(~p"/api/jobs", %{"min_stars" => "999999"}) assert %{"jobs" => jobs} = json_response(conn, 200) refute Enum.any?(jobs, &(&1["id"] == task.id)) end # A worker declares what it can run so it never claims a kind it does not # implement and then holds the lease until it expires. test "global jobs queue filters by the kinds a worker can run", %{ conn: conn, creator: creator, repository: repository, worker_token: token } do task = review_task_fixture(repository, creator) conn = conn |> authed(token) |> get(~p"/api/jobs", %{"kinds" => "#{task.kind},refute_finding"}) assert %{"jobs" => jobs} = json_response(conn, 200) assert Enum.any?(jobs, &(&1["id"] == task.id)) conn = build_conn() |> authed(token) |> get(~p"/api/jobs", %{"kinds" => "reproduce_finding"}) assert %{"jobs" => jobs} = json_response(conn, 200) refute Enum.any?(jobs, &(&1["id"] == task.id)) end # An old client and a new server must still be able to talk, so a name the # server does not know is dropped rather than erroring or filtering to empty. test "unknown kind names are ignored rather than rejected", %{ conn: conn, creator: creator, repository: repository, worker_token: token } do task = review_task_fixture(repository, creator) conn = conn |> authed(token) |> get(~p"/api/jobs", %{"kinds" => "not_a_real_kind"}) assert %{"jobs" => jobs} = json_response(conn, 200) assert Enum.any?(jobs, &(&1["id"] == task.id)) end test "global jobs queue includes the caller's active claims", %{ conn: conn, creator: creator, repository: repository, worker: worker, worker_token: token } do task = review_task_fixture(repository, creator, %{"kind" => "code_review", "capability" => "agent"}) {:ok, claimed} = Work.claim_task(task, worker) assert claimed.status == "claimed" conn = conn |> authed(token) |> get(~p"/api/jobs") assert %{"jobs" => jobs} = json_response(conn, 200) match = Enum.find(jobs, &(&1["id"] == claimed.id)) assert match assert match["status"] == "claimed" assert match["lease"]["active"] == true end test "lists a repository's tasks with client-ready relationships", %{ conn: conn, creator: creator, repository: repository, worker_token: token } do task = review_task_fixture(repository, creator) conn = conn |> authed(token) |> get(~p"/api/github.com/openai/codex/jobs") assert %{"jobs" => [response]} = json_response(conn, 200) assert response["id"] == task.id assert response["kind"] == "threat_model" assert response["capability"] == "human" assert response["status"] == "open" assert response["visibility"] == "public" assert response["commit_sha"] == task.commit_sha assert response["commit_committed_at"] == "2026-07-01T12:00:00.000000Z" assert response["repository"]["canonical_url"] == "https://github.com/openai/codex" assert response["repository"]["host"] == "github.com" assert response["repository"]["id"] == repository.id assert response["repository"]["name"] == "codex" assert response["repository"]["owner"] == "openai" assert response["repository"]["participation_mode"] == repository.participation_mode assert response["repository"]["record_url"] =~ "/github.com/openai/codex" assert response["creator"]["id"] == creator.id assert response["creator"]["handle"] == creator.handle assert response["creator"] |> Map.keys() |> Enum.sort() == ["handle", "id"] assert response["claimant"] == nil assert response["lease"] == nil assert response["contribution"] == nil assert response["job_url"] =~ "/jobs/#{task.id}" end test "returns an empty list and 404s an unregistered repository", %{ conn: conn, worker_token: token } do conn1 = conn |> authed(token) |> get(~p"/api/github.com/openai/codex/jobs") assert json_response(conn1, 200) == %{"jobs" => []} conn2 = conn |> recycle() |> authed(token) |> get(~p"/api/github.com/unknown/repository/jobs") assert json_response(conn2, 404)["error"] =~ "not registered" end test "does not disclose a quarantined repository to an unrelated credential", %{ conn: conn, repository: repository, worker_token: token } do _contained = repository |> Repository.listing_changeset(%{listing_status: "quarantined"}) |> Repo.update!() conn = conn |> authed(token) |> get(~p"/api/github.com/openai/codex/jobs") assert json_response(conn, 404)["error"] =~ "not registered" end test "shows a task and returns 404 for missing or malformed ids", %{ conn: conn, creator: creator, repository: repository, worker_token: token } do task = review_task_fixture(repository, creator) conn1 = conn |> authed(token) |> get(~p"/api/jobs/#{task.id}") assert json_response(conn1, 200)["id"] == task.id conn2 = conn |> recycle() |> authed(token) |> get("/api/jobs/not-an-id") assert json_response(conn2, 404)["error"] == "job not found" conn3 = conn |> recycle() |> authed(token) |> get("/api/jobs/999999999") assert json_response(conn3, 404)["error"] == "job not found" end test "claims a task and exposes the active lease", %{ conn: conn, creator: creator, repository: repository, worker: worker, worker_token: token } do task = review_task_fixture(repository, creator) conn = conn |> authed(token) |> post(~p"/api/jobs/#{task.id}/claim") response = json_response(conn, 200) assert response["status"] == "claimed" assert response["claimant"]["id"] == worker.id assert response["lease"]["active"] == true assert response["lease"]["claimed_at"] assert response["lease"]["expires_at"] original_expiry = response["lease"]["expires_at"] conn = conn |> recycle() |> authed(token) |> post(~p"/api/jobs/#{task.id}/claim") repeated = json_response(conn, 200) assert repeated["claimant"]["id"] == worker.id assert repeated["lease"]["expires_at"] == original_expiry end test "creators may claim their own jobs; active claims conflict for others", %{ conn: conn, creator: creator, creator_token: creator_token, repository: repository, worker_token: worker_token } do task = review_task_fixture(repository, creator) # Solo/hosted workflow: job creators may claim and perform their own Jobs. conn1 = conn |> authed(creator_token) |> post(~p"/api/jobs/#{task.id}/claim") assert json_response(conn1, 200)["claimant"]["id"] == creator.id conn_release = conn |> recycle() |> authed(creator_token) |> delete(~p"/api/jobs/#{task.id}/claim") assert json_response(conn_release, 200)["status"] == "open" conn2 = conn |> recycle() |> authed(worker_token) |> post(~p"/api/jobs/#{task.id}/claim") assert json_response(conn2, 200) outsider = account_fixture() outsider_token = client_token(outsider) conn3 = conn |> recycle() |> authed(outsider_token) |> post(~p"/api/jobs/#{task.id}/claim") assert json_response(conn3, 409)["error"] =~ "active claim" end test "only the claimant can release a task", %{ conn: conn, creator: creator, repository: repository, worker: worker, worker_token: worker_token } do task = review_task_fixture(repository, creator) {:ok, task} = Work.claim_task(task, worker) outsider_token = client_token(account_fixture()) conn1 = conn |> authed(outsider_token) |> delete(~p"/api/jobs/#{task.id}/claim") assert json_response(conn1, 403)["error"] =~ "current claimant" conn2 = conn |> recycle() |> authed(worker_token) |> delete(~p"/api/jobs/#{task.id}/claim") response = json_response(conn2, 200) assert response["status"] == "open" assert response["claimant"] == nil assert response["lease"] == nil end test "claimant can renew an active lease", %{ conn: conn, creator: creator, repository: repository, worker_token: token } do task = review_task_fixture(repository, creator) conn = conn |> authed(token) |> post(~p"/api/jobs/#{task.id}/claim") first = json_response(conn, 200) conn = conn |> recycle() |> authed(token) |> post(~p"/api/jobs/#{task.id}/claim/renew") renewed = json_response(conn, 200) assert renewed["lease"]["active"] assert renewed["lease"]["expires_at"] >= first["lease"]["expires_at"] end test "completion path submits a claimed task for independent review", %{ conn: conn, creator: creator, repository: repository, worker: worker, worker_token: token } do task = review_task_fixture(repository, creator) {:ok, task} = Work.claim_task(task, worker) body = %{ "provenance" => "hybrid", "summary" => "The authorization boundary is enforced.", "evidence" => "Reviewed both entry points and ran the focused regression test." } conn = conn |> authed(token) |> post(~p"/api/jobs/#{task.id}/complete", body) response = json_response(conn, 200) assert response["status"] == "submitted" assert response["visibility"] == "public" assert response["submitted_at"] assert response["completed_at"] == nil assert response["lease"]["active"] == false assert response["contribution"] == %{ "contributor" => %{ "handle" => worker.handle, "id" => worker.id }, "evidence" => body["evidence"], "id" => response["contribution"]["id"], "version" => 1, "provenance" => "hybrid", "submitted_at" => response["contribution"]["submitted_at"], "summary" => body["summary"] } conn2 = conn |> recycle() |> authed(token) |> delete(~p"/api/jobs/#{task.id}/claim") assert json_response(conn2, 403)["error"] =~ "current claimant" end test "completion requires the active claimant and valid evidence metadata", %{ conn: conn, creator: creator, repository: repository, worker: worker, worker_token: token } do task = review_task_fixture(repository, creator) conn1 = conn |> authed(token) |> post(~p"/api/jobs/#{task.id}/complete", %{ "provenance" => "human", "summary" => "Completed", "evidence" => "Attempted submission without first holding the task's active claim." }) assert json_response(conn1, 403)["error"] =~ "current claimant" {:ok, _task} = Work.claim_task(task, worker) conn2 = conn |> recycle() |> authed(token) |> post(~p"/api/jobs/#{task.id}/complete", %{ "provenance" => "unverifiable", "summary" => "" }) errors = json_response(conn2, 422)["errors"] assert errors["provenance"] == ["is invalid"] assert errors["summary"] == ["can't be blank"] end test "completion provenance must satisfy the requested capability", %{ conn: conn, creator: creator, repository: repository, worker: worker, worker_token: token } do task = review_task_fixture(repository, creator, %{"capability" => "hybrid"}) {:ok, _task} = Work.claim_task(task, worker) conn = conn |> authed(token) |> post(~p"/api/jobs/#{task.id}/complete", %{ "provenance" => "human", "summary" => "Manual review completed.", "evidence" => "Manually traced the requested paths and recorded reproducible notes." }) assert json_response(conn, 422)["errors"]["provenance"] == [ "does not satisfy this task's required capability" ] end test "proposals are publicly listed and publishing is not exposed to client credentials", %{ conn: conn, creator: creator, repository: repository, creator_token: creator_token, worker_token: worker_token } do proposal = proposed_review_task_fixture(repository, creator) conn1 = conn |> authed(worker_token) |> get(~p"/api/github.com/openai/codex/jobs") assert [listed] = json_response(conn1, 200)["jobs"] assert listed["id"] == proposal.id assert listed["status"] == "proposed" assert listed["visibility"] == "public" conn2 = conn |> recycle() |> authed(creator_token) |> get(~p"/api/jobs/#{proposal.id}") assert json_response(conn2, 200)["status"] == "proposed" moderator = moderator_account_fixture() moderator_token = Accounts.create_account_api_token(moderator) conn3 = conn |> recycle() |> authed(moderator_token) |> post("/api/jobs/#{proposal.id}/publish", %{ "reason" => "The task scope is safe, bounded, and useful to contributors." }) assert response(conn3, 404) == "Not Found" assert {:ok, open} = Work.publish_task(proposal, moderator, %{ "reason" => "The task scope is safe, bounded, and useful to contributors." }) assert open.status == "open" end test "acceptance is not exposed to client credentials", %{ conn: conn, creator: creator, repository: repository, worker: worker, worker_token: worker_token } do task = review_task_fixture(repository, creator) {:ok, task} = Work.claim_task(task, worker) {:ok, submitted} = Work.submit_task(task, worker, %{ "provenance" => "human", "summary" => "The requested boundary was independently traced.", "evidence" => "Ran the negative authorization suite against both repository entry points." }) conn1 = conn |> authed(worker_token) |> post("/api/jobs/#{submitted.id}/accept", %{ "reason" => "I should not be allowed to approve my own contribution.", "evidence" => "This evidence comes from the original contributor and is not independent." }) assert response(conn1, 404) == "Not Found" reviewer = moderator_account_fixture() reviewer_token = Accounts.create_account_api_token(reviewer) assert Work.get_visible_task(submitted.id, Tarakan.Accounts.Scope.for_account(reviewer)) conn2 = conn |> recycle() |> authed(reviewer_token) |> post("/api/jobs/#{submitted.id}/accept", %{ "reason" => "The result was independently reproduced at the pinned commit.", "evidence" => "Checked out the pinned SHA and ran the documented negative-path tests." }) assert response(conn2, 404) == "Not Found" assert {:ok, accepted} = Work.accept_task(submitted, reviewer, %{ "reason" => "The result was independently reproduced at the pinned commit.", "evidence" => "Checked out the pinned SHA and ran the documented negative-path tests." }) assert accepted.status == "accepted" assert accepted.visibility == "public" outsider = account_fixture() outsider_token = Accounts.create_account_api_token(outsider) conn3 = conn |> recycle() |> authed(outsider_token) |> get(~p"/api/jobs/#{accepted.id}") response = json_response(conn3, 200) assert response["status"] == "accepted" assert response["visibility"] == "public" assert response["contribution"]["summary"] =~ "boundary" assert response["contribution"]["evidence"] =~ "negative authorization suite" assert {:ok, redacted} = Work.disclose_task(accepted, reviewer, "public_summary", %{ "reason" => "The redacted result is safe and useful without publishing raw evidence." }) conn4 = conn |> recycle() |> authed(outsider_token) |> get(~p"/api/jobs/#{redacted.id}") response = json_response(conn4, 200) assert response["visibility"] == "public_summary" assert response["contribution"]["summary"] =~ "boundary" assert response["contribution"]["evidence"] == nil assert response["decisions"] == [] assert response["disclosed_at"] assert response["sensitive_data_reviewed"] == false end end