symbiont_ex/test/symbiont/ledger_test.exs

104 lines
3.0 KiB
Elixir

defmodule Symbiont.LedgerTest do
use ExUnit.Case, async: false
import Symbiont.TestHelpers
@moduletag :capture_log
setup do
tmp_dir = Path.join(System.tmp_dir!(), "symbiont_test_#{:rand.uniform(999_999)}")
File.mkdir_p!(tmp_dir)
safe_stop(Symbiont.Ledger)
{:ok, _pid} = Symbiont.Ledger.start_link(data_dir: tmp_dir)
on_exit(fn ->
safe_stop(Symbiont.Ledger)
File.rm_rf!(tmp_dir)
end)
%{tmp_dir: tmp_dir}
end
test "starts with empty ledger" do
assert Symbiont.Ledger.recent() == []
assert Symbiont.Ledger.stats() == %{
"total_calls" => 0,
"total_cost_estimated_usd" => 0.0,
"by_model" => %{},
"by_date" => %{}
}
end
test "appending entries and reading them back" do
Symbiont.Ledger.append(%{
model: "haiku",
success: true,
input_tokens: 100,
output_tokens: 50,
estimated_cost_usd: 0.008,
elapsed_seconds: 1.2
})
Process.sleep(50)
entries = Symbiont.Ledger.recent()
assert length(entries) == 1
[entry] = entries
assert entry["model"] == "haiku"
assert entry["success"] == true
assert entry["input_tokens"] == 100
assert entry["estimated_cost_usd"] == 0.008
assert entry["timestamp"] != nil
end
test "stats aggregate correctly across multiple entries" do
entries = [
%{model: "haiku", estimated_cost_usd: 0.008, success: true, input_tokens: 50, output_tokens: 25},
%{model: "haiku", estimated_cost_usd: 0.006, success: true, input_tokens: 40, output_tokens: 20},
%{model: "sonnet", estimated_cost_usd: 0.04, success: true, input_tokens: 200, output_tokens: 100},
%{model: "opus", estimated_cost_usd: 0.15, success: true, input_tokens: 500, output_tokens: 300}
]
Enum.each(entries, &Symbiont.Ledger.append/1)
# Ensure all async casts are processed
_ = Symbiont.Ledger.stats()
Process.sleep(50)
stats = Symbiont.Ledger.stats()
assert stats["total_calls"] == 4
assert stats["total_cost_estimated_usd"] == 0.204
assert stats["by_model"]["haiku"]["calls"] == 2
assert stats["by_model"]["sonnet"]["calls"] == 1
assert stats["by_model"]["opus"]["calls"] == 1
end
test "recent limits the number of entries returned" do
for i <- 1..10 do
Symbiont.Ledger.append(%{model: "haiku", estimated_cost_usd: 0.001 * i, success: true})
end
Process.sleep(100)
assert length(Symbiont.Ledger.recent(3)) == 3
assert length(Symbiont.Ledger.recent(50)) == 10
end
test "ledger persists to JSONL file", %{tmp_dir: tmp_dir} do
Symbiont.Ledger.append(%{model: "sonnet", estimated_cost_usd: 0.04, success: true})
Process.sleep(50)
path = Path.join(tmp_dir, "ledger.jsonl")
content = File.read!(path)
assert String.contains?(content, "sonnet")
lines = content |> String.split("\n", trim: true)
assert length(lines) == 1
{:ok, decoded} = Jason.decode(hd(lines))
assert decoded["model"] == "sonnet"
end
end