test/openagents/forge/targets_test.exs

58e6347eeb72 · 3 KB

defmodule OpenAgents.Forge.TargetsTest do
  @moduledoc """
  The two surfaces of `OpenAgents.Forge.Targets` that the end-to-end
  lifecycle test (`OpenAgents.Forge.TargetLifecycleTest`, which drives a real
  bare repo) does not reach: the injectable `commit_store`, and the bounded
  `details` map.

  Promotability and the transition table are deliberately NOT re-asserted
  here — they have one contract and one home. This file used to promote
  never-pushed SHAs like `"abc123"` through a test-environment bypass, which
  contradicted that contract ("only pushed commits are ever promotable") and
  meant the precondition was never actually exercised anywhere.
  """

  use OpenAgents.DataCase, async: false

  alias OpenAgents.Forge.Target
  alias OpenAgents.Forge.Targets

  test "a hosted user repository is not deployable" do
    assert {:error, :repository_not_deployable} =
             Targets.promote(
               Ecto.UUID.generate(),
               String.duplicate("a", 40),
               "operator:test",
               commit_store: fn _repo, _sha -> :ok end
             )
  end

  @repo "OpenAgentsInc/openagents.com"

  # A SHA that is well-formed but not in any repo, so only the injected
  # store decides whether it is promotable.
  defp sha, do: 20 |> :crypto.strong_rand_bytes() |> Base.encode16(case: :lower)

  defp promote(attrs \\ []) do
    Targets.promote(
      Keyword.get(attrs, :repo, @repo),
      Keyword.get(attrs, :sha, sha()),
      Keyword.get(attrs, :operator, "operator-1"),
      Keyword.take(attrs, [:commit_store, :details])
      |> Keyword.put_new(:commit_store, fn _repo, _sha -> :ok end)
    )
  end

  test "an accepting commit store promotes the commit" do
    sha = sha()

    assert {:ok, %Target{} = target} = promote(sha: sha)
    assert target.repo == @repo
    assert target.sha == sha
    assert target.promoted_by == "operator-1"
    assert target.status == "promoted"
  end

  test "a refusing commit store refuses the promotion" do
    assert {:error, :unknown_sha} = promote(commit_store: fn _repo, _sha -> :error end)
  end

  test "a commit store may name its own reason" do
    store = fn _repo, _sha -> {:error, :mirror_behind} end
    assert {:error, :mirror_behind} = promote(commit_store: store)
  end

  test "a malformed SHA is refused before the store is consulted" do
    store = fn _repo, _sha -> flunk("commit store must not be consulted") end

    assert {:error, :invalid_sha} =
             Targets.promote(@repo, "not-a-sha!", "operator-1", commit_store: store)
  end

  test "details are bounded to 100 keys and 32KB per value" do
    too_many = Map.new(0..100, fn i -> {"key#{i}", "value"} end)

    assert {:error, {:invalid, %Ecto.Changeset{} = changeset}} = promote(details: too_many)
    assert "exceeds the 100-key bound" in errors_on(changeset).details

    huge = String.duplicate("x", 40_000)

    assert {:error, {:invalid, %Ecto.Changeset{} = changeset}} =
             promote(details: %{data: huge})

    assert "exceeds the 32KB bound" in errors_on(changeset).details
  end

  test "latest/1 and transition/2 are the current/advance aliases" do
    {:ok, first} = promote()
    {:ok, second} = promote()

    assert Targets.latest(@repo).id == second.id
    assert {:ok, %Target{status: "building"}} = Targets.transition(first.id, "building")

    assert {:error, {:invalid_transition, "promoted", "live"}} =
             Targets.transition(second.id, "live")
  end
end