test/openagents/capacity_test.exs

main at 58e6347eeb72 · 10 KB

defmodule OpenAgents.CapacityTest do
  use OpenAgents.DataCase, async: false

  alias OpenAgents.Capacity
  alias OpenAgents.Capacity.Estimate
  alias OpenAgents.Capacity.Math

  setup do
    original_capacity = Application.get_env(:openagents, OpenAgents.Capacity, [])
    original_evidence = Application.get_env(:openagents, :capacity_test_evidence)

    Application.put_env(
      :openagents,
      OpenAgents.Capacity,
      Keyword.merge(original_capacity, evidence_source: OpenAgents.CapacityEvidenceStub)
    )

    on_exit(fn ->
      Application.put_env(:openagents, OpenAgents.Capacity, original_capacity)

      if is_nil(original_evidence) do
        Application.delete_env(:openagents, :capacity_test_evidence)
      else
        Application.put_env(:openagents, :capacity_test_evidence, original_evidence)
      end
    end)

    :ok
  end

  test "caps limit-derived capacity with already-free broker evidence" do
    config = [
      class_ceilings: %{"standard" => 16},
      reserved_headroom_fraction: 0.25
    ]

    quantities =
      Math.quantities(
        %{"id" => "standard"},
        %{
          "logical" => 30,
          "active_reservations" => 4,
          "observed_limit" => 24,
          "reported_free" => 8
        },
        config
      )

    assert quantities["allocatable"] == 8
    assert quantities["safety_headroom"] == 6
  end

  test "publishes fresh and stale evidence without inventing unavailable quantities" do
    now = DateTime.utc_now() |> DateTime.truncate(:second)

    Application.put_env(
      :openagents,
      :capacity_test_evidence,
      {:ok,
       %{
         "classes" => [
           %{
             "id" => "standard",
             "logical" => 30,
             "active_reservations" => 4,
             "observed_limit" => 24,
             "reported_free" => 8,
             "queued" => 2,
             "observed_at" => DateTime.to_iso8601(now)
           },
           %{
             "id" => "strong",
             "logical" => 2,
             "active_reservations" => 0,
             "observed_limit" => 2,
             "reported_free" => 2,
             "observed_at" => DateTime.to_iso8601(DateTime.add(now, -121, :second))
           },
           %{"id" => "batch", "private" => true}
         ]
       }}
    )

    projection = Capacity.projection(%{id: Ecto.UUID.generate()})
    standard = Enum.find(projection["classes"], &(&1["id"] == "standard"))
    strong = Enum.find(projection["classes"], &(&1["id"] == "strong"))

    assert standard["admits"] == true
    assert standard["quantities"]["allocatable"] == 8
    assert strong["admits"] == false
    assert strong["quantities"]["allocatable"] == 0
    assert strong["refusal"]["code"] == "evidence_stale"
    assert strong["refusal"]["detail"] == "Capacity evidence is stale."
    assert Enum.find(projection["classes"], &(&1["id"] == "batch")) == nil
  end

  test "returns a typed refusal and never includes broker-only sensitive fields" do
    secret = "project-123 eu-west-1 host.example 192.0.2.1 secret-token image/path"

    Application.put_env(
      :openagents,
      :capacity_test_evidence,
      {:ok,
       %{
         "classes" => [
           %{
             "id" => "standard",
             "observed_at" => DateTime.utc_now() |> DateTime.to_iso8601(),
             "observed_limit" => 0,
             "reported_free" => 0,
             "logical" => 0,
             "active_reservations" => 0,
             "provider" => secret,
             "project_id" => secret,
             "region" => secret,
             "zone" => secret,
             "hostname" => secret,
             "guest_ip" => secret,
             "credentials" => secret,
             "image_path" => secret,
             "raw_error" => secret
           }
         ]
       }}
    )

    projection = Capacity.projection(%{id: Ecto.UUID.generate()})
    serialized = Jason.encode!(projection)
    unavailable = Enum.find(projection["classes"], &(&1["id"] == "strong"))

    refute serialized =~ secret
    refute serialized =~ "project_id"

    assert unavailable["refusal"] == %{
             "code" => "evidence_unavailable",
             "detail" => "Capacity evidence is unavailable."
           }

    assert projection["classes"] |> Enum.find(&(&1["id"] == "standard")) |> Map.get("admits") ==
             true

    assert {:error, %{"error" => %{"code" => "quantity_unavailable"}}} =
             Capacity.match(
               %{id: Ecto.UUID.generate()},
               %{
                 "quantity" => 1,
                 "isolation" => "managed_standard",
                 "egress" => "policy_broker",
                 "data_location" => "openagents_managed"
               }
             )
  end

  test "estimates earnings only with a named buyer and verified payout policy" do
    class = %{"id" => "standard"}
    requirement = %{"quantity" => 1, "duration_seconds" => 3_600}
    base_config = [maximum_evidence_age_seconds: 120]

    no_buyer = Estimate.build(class, requirement, 0, 60, base_config)
    assert no_buyer["earnings"] == nil
    assert no_buyer["earnings_reason"] == "no_named_buyer"

    unverified_buyer =
      Estimate.build(
        class,
        requirement,
        0,
        60,
        Keyword.put(base_config, :buyer, %{"name" => "Acme"})
      )

    assert unverified_buyer["earnings"] == nil
    assert unverified_buyer["earnings_reason"] == "no_verified_payout_policy"

    verified_buyer =
      Estimate.build(
        class,
        requirement,
        0,
        60,
        Keyword.put(base_config, :buyer, %{
          "name" => "Acme",
          "verified_payout_policy" => true
        })
      )

    assert verified_buyer["earnings"] == %{
             "currency" => "usd_cents",
             "low" => 16,
             "high" => 32
           }

    assert verified_buyer["earnings_reason"] == nil
  end

  test "managed matching never falls back to connected computers" do
    Application.put_env(
      :openagents,
      :capacity_test_evidence,
      {:ok,
       %{
         "classes" => [
           %{
             "id" => "standard",
             "observed_at" => DateTime.utc_now() |> DateTime.to_iso8601(),
             "observed_limit" => 0,
             "reported_free" => 0,
             "logical" => 0,
             "active_reservations" => 0
           }
         ]
       }}
    )

    assert {:error, %{"error" => %{"code" => "quantity_unavailable"}}} =
             Capacity.match(
               %{id: Ecto.UUID.generate()},
               %{
                 "quantity" => 1,
                 "isolation" => "managed_standard",
                 "egress" => "policy_broker",
                 "data_location" => "openagents_managed"
               }
             )
  end

  test "managed standard matching can rank strong runtimes" do
    now = DateTime.utc_now() |> DateTime.truncate(:second) |> DateTime.to_iso8601()

    Application.put_env(
      :openagents,
      :capacity_test_evidence,
      {:ok,
       %{
         "classes" => [
           %{
             "id" => "strong",
             "observed_at" => now,
             "observed_limit" => 2,
             "reported_free" => 2,
             "logical" => 2,
             "active_reservations" => 0
           }
         ]
       }}
    )

    assert {:ok, %{"candidates" => [%{"class" => "strong"}]}} =
             Capacity.match(
               %{id: Ecto.UUID.generate()},
               %{
                 "quantity" => 1,
                 "isolation" => "managed_standard",
                 "egress" => "policy_broker",
                 "data_location" => "openagents_managed"
               }
             )
  end

  test "unsupported requirement fields and budget return typed refusals" do
    viewer = %{id: Ecto.UUID.generate()}

    for {field, value, code} <- [
          {"egress", "private_network", "unsupported_egress"},
          {"data_location", "restricted_zone", "unsupported_data_location"},
          {"tools", ["unsupported_tool"], "unsupported_tool"}
        ] do
      requirement = %{
        "isolation" => "managed_standard",
        "egress" => "policy_broker",
        "data_location" => "openagents_managed"
      }

      requirement =
        if field == "tools" do
          Map.put(requirement, field, value)
        else
          Map.put(requirement, field, value)
        end

      assert {:error, %{"error" => %{"code" => ^code}}} =
               Capacity.match(viewer, requirement)
    end

    assert {:error, %{"error" => %{"code" => "budget_below_minimum"}}} =
             Capacity.match(viewer, %{
               "isolation" => "managed_standard",
               "egress" => "policy_broker",
               "data_location" => "openagents_managed",
               "budget" => %{"currency" => "usd_cents", "amount" => 0}
             })
  end

  test "a customer computer target requires ownership" do
    assert {:error, %{"error" => %{"code" => "computer_not_found"}}} =
             Capacity.match(
               %{id: Ecto.UUID.generate()},
               %{
                 "isolation" => "customer_controlled",
                 "egress" => "customer_network",
                 "data_location" => "customer_premises",
                 "target" => "customer_computer",
                 "computer_id" => Ecto.UUID.generate()
               }
             )
  end

  test "incident-drained and exhausted classes expose refusal evidence" do
    now = DateTime.utc_now() |> DateTime.truncate(:second) |> DateTime.to_iso8601()

    Application.put_env(
      :openagents,
      :capacity_test_evidence,
      {:ok,
       %{
         "classes" => [
           %{
             "id" => "standard",
             "observed_at" => now,
             "observed_limit" => 16,
             "reported_free" => 0,
             "logical" => 16,
             "active_reservations" => 16,
             "queued" => 5
           },
           %{
             "id" => "batch",
             "observed_at" => now,
             "observed_limit" => 8,
             "reported_free" => 8,
             "logical" => 8,
             "active_reservations" => 0,
             "incident_drained" => true
           }
         ]
       }}
    )

    projection = Capacity.projection(%{id: Ecto.UUID.generate()})
    standard = Enum.find(projection["classes"], &(&1["id"] == "standard"))
    batch = Enum.find(projection["classes"], &(&1["id"] == "batch"))

    assert standard["quantities"]["allocatable"] == 0
    assert standard["quantities"]["queued"] == 5
    assert standard["admits"] == true
    assert batch["admits"] == false
    assert batch["refusal"]["code"] == "incident_drained"
    assert batch["refusal"]["detail"] == "Capacity is temporarily drained for an incident."
  end
end