symbiont_ex/test/symbiont/api_test.exs

150 lines
3.6 KiB
Elixir

defmodule Symbiont.APITest do
use ExUnit.Case, async: false
import Plug.Test
import Plug.Conn
import Symbiont.TestHelpers
@moduletag :capture_log
setup do
tmp_dir = Path.join(System.tmp_dir!(), "symbiont_api_test_#{:rand.uniform(999_999)}")
File.mkdir_p!(tmp_dir)
stop_all_services()
ensure_task_supervisor()
Application.put_env(:symbiont, :data_dir, tmp_dir)
Application.put_env(:symbiont, :heartbeat_interval_ms, :timer.hours(24))
{:ok, _} = Symbiont.Ledger.start_link(data_dir: tmp_dir)
{:ok, _} = Symbiont.Queue.start_link(data_dir: tmp_dir)
{:ok, _} = Symbiont.Heartbeat.start_link([])
on_exit(fn ->
stop_all_services()
File.rm_rf!(tmp_dir)
end)
%{tmp_dir: tmp_dir}
end
defp call_api(method, path, body \\ nil) do
conn =
if body do
conn(method, path, Jason.encode!(body))
|> put_req_header("content-type", "application/json")
else
conn(method, path)
end
Symbiont.API.call(conn, Symbiont.API.init([]))
end
defp json_body(conn) do
Jason.decode!(conn.resp_body)
end
describe "GET /health" do
test "returns ok" do
conn = call_api(:get, "/health")
assert conn.status == 200
body = json_body(conn)
assert body["status"] == "ok"
assert body["runtime"] == "elixir/otp"
end
end
describe "GET /status" do
test "returns system status" do
conn = call_api(:get, "/status")
assert conn.status == 200
body = json_body(conn)
assert body["status"] == "healthy"
assert body["runtime"] == "elixir/otp"
assert is_integer(body["queue_size"])
assert is_integer(body["total_calls"])
end
end
describe "GET /ledger" do
test "returns empty ledger initially" do
conn = call_api(:get, "/ledger")
assert conn.status == 200
body = json_body(conn)
assert body["entries"] == []
assert body["count"] == 0
end
test "returns entries after appending" do
Symbiont.Ledger.append(%{model: "haiku", estimated_cost_usd: 0.01, success: true})
Process.sleep(50)
conn = call_api(:get, "/ledger")
body = json_body(conn)
assert body["count"] == 1
assert hd(body["entries"])["model"] == "haiku"
end
end
describe "GET /ledger/stats" do
test "returns aggregate stats" do
conn = call_api(:get, "/ledger/stats")
assert conn.status == 200
body = json_body(conn)
assert body["total_calls"] == 0
assert body["total_cost_estimated_usd"] == 0.0
end
end
describe "POST /queue" do
test "enqueues a task" do
conn = call_api(:post, "/queue", %{task: "Do something later"})
assert conn.status == 200
body = json_body(conn)
assert body["status"] == "queued"
assert is_binary(body["id"])
assert body["position"] == 1
end
test "rejects empty task" do
conn = call_api(:post, "/queue", %{task: ""})
assert conn.status == 400
body = json_body(conn)
assert body["error"] =~ "missing"
end
test "rejects missing task field" do
conn = call_api(:post, "/queue", %{})
assert conn.status == 400
end
end
describe "POST /task" do
test "rejects empty task" do
conn = call_api(:post, "/task", %{task: ""})
assert conn.status == 400
end
test "rejects missing task" do
conn = call_api(:post, "/task", %{})
assert conn.status == 400
end
end
describe "unknown routes" do
test "returns 404" do
conn = call_api(:get, "/nonexistent")
assert conn.status == 404
body = json_body(conn)
assert body["error"] == "not found"
end
end
end