defmodule Symbiont.HeartbeatTest do use ExUnit.Case, async: false import Symbiont.TestHelpers @moduletag :capture_log setup do tmp_dir = Path.join(System.tmp_dir!(), "symbiont_hb_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 test "pulse returns a health snapshot" do snapshot = Symbiont.Heartbeat.pulse() assert snapshot["status"] == "healthy" assert is_integer(snapshot["queue_size"]) assert snapshot["queue_size"] == 0 assert snapshot["tasks_processed"] == 0 assert snapshot["timestamp"] != nil end test "pulse logs to heartbeat.jsonl", %{tmp_dir: tmp_dir} do _snapshot = Symbiont.Heartbeat.pulse() path = Path.join(tmp_dir, "heartbeat.jsonl") content = File.read!(path) assert String.contains?(content, "healthy") lines = content |> String.split("\n", trim: true) assert length(lines) >= 1 end test "last_snapshot returns nil before first heartbeat" do snapshot = Symbiont.Heartbeat.last_snapshot() assert is_nil(snapshot) or is_map(snapshot) end test "pulse processes queued tasks" do {:ok, _} = Symbiont.Queue.enqueue("Test task 1") {:ok, _} = Symbiont.Queue.enqueue("Test task 2") assert Symbiont.Queue.size() == 2 snapshot = Symbiont.Heartbeat.pulse() assert snapshot["tasks_processed"] == 2 assert Symbiont.Queue.size() == 0 end end