test/openagents_web/controllers/changelog_controller_test.exs

58e6347eeb72 · 2 KB

defmodule OpenAgentsWeb.ChangelogControllerTest do
  @moduledoc """
  The `/api/changelog` controller (#138, TRANSPARENCY-001): the public,
  schema-versioned projection is redacted per the requester's viewer and the
  entry's transparency tier.
  """

  use OpenAgentsWeb.ConnCase, async: false

  alias OpenAgents.Changelog

  setup do
    :persistent_term.erase({OpenAgents.Changelog, :cache})
    on_exit(fn -> :persistent_term.erase({OpenAgents.Changelog, :cache}) end)
    :ok
  end

  defp full_sha(prefix), do: String.pad_trailing(prefix, 40, "0")

  describe "GET /api/changelog" do
    test "redacts dark-tier trace_ref, trace_digest, and detail", %{conn: conn} do
      {:ok, _} =
        Changelog.record(%{
          repo: "openagents.com",
          sha: full_sha("feed0201"),
          summary: "Dark tier entry",
          category: "ui",
          source: "operator",
          entry_at: DateTime.utc_now(),
          visibility: "l2",
          transparency_tier: "dark",
          trace_ref: "trace:v1:dark",
          trace_digest: "sha256:dark",
          detail: %{"note" => "hidden"}
        })

      conn = get(conn, ~p"/api/changelog")
      payload = json_response(conn, 200)

      assert [entry] = Enum.filter(payload["entries"], &(&1["summary"] == "Dark tier entry"))
      assert entry["trace_ref"] == nil
      assert entry["trace_digest"] == nil
      assert entry["detail"] == %{}
    end

    test "keeps pulse-tier trace_ref and trace_digest but redacts detail", %{conn: conn} do
      {:ok, _} =
        Changelog.record(%{
          repo: "openagents.com",
          sha: full_sha("feed0202"),
          summary: "Pulse tier entry",
          category: "ui",
          source: "operator",
          entry_at: DateTime.utc_now(),
          visibility: "l2",
          transparency_tier: "pulse",
          trace_ref: "trace:v1:pulse",
          trace_digest: "sha256:pulse",
          detail: %{"note" => "hidden"}
        })

      conn = get(conn, ~p"/api/changelog")
      payload = json_response(conn, 200)

      assert [entry] = Enum.filter(payload["entries"], &(&1["summary"] == "Pulse tier entry"))
      assert entry["trace_ref"] == "trace:v1:pulse"
      assert entry["trace_digest"] == "sha256:pulse"
      assert entry["detail"] == %{}
    end
  end
end