defmodule Symbiont.QueueTest do use ExUnit.Case, async: false @moduletag :capture_log setup do tmp_dir = Path.join(System.tmp_dir!(), "symbiont_queue_test_#{:rand.uniform(999_999)}") File.mkdir_p!(tmp_dir) if Process.whereis(Symbiont.Queue), do: GenServer.stop(Symbiont.Queue) {:ok, _pid} = Symbiont.Queue.start_link(data_dir: tmp_dir) on_exit(fn -> if Process.whereis(Symbiont.Queue), do: GenServer.stop(Symbiont.Queue) File.rm_rf!(tmp_dir) end) %{tmp_dir: tmp_dir} end test "starts with empty queue" do assert Symbiont.Queue.size() == 0 assert Symbiont.Queue.list() == [] end test "enqueue adds tasks and returns IDs" do {:ok, id1} = Symbiont.Queue.enqueue("Task one") {:ok, id2} = Symbiont.Queue.enqueue("Task two", "high") assert is_binary(id1) assert is_binary(id2) assert id1 != id2 assert Symbiont.Queue.size() == 2 end test "take returns pending tasks and marks them as processing" do {:ok, _} = Symbiont.Queue.enqueue("Task A") {:ok, _} = Symbiont.Queue.enqueue("Task B") {:ok, _} = Symbiont.Queue.enqueue("Task C") taken = Symbiont.Queue.take(2) assert length(taken) == 2 assert Enum.all?(taken, &(&1["status"] == "processing")) # Only 1 pending remains assert Symbiont.Queue.size() == 1 end test "complete marks a task as done" do {:ok, id} = Symbiont.Queue.enqueue("Complete me") [task] = Symbiont.Queue.take(1) assert task["id"] == id Symbiont.Queue.complete(id, "All done!") Process.sleep(50) tasks = Symbiont.Queue.list() done = Enum.find(tasks, &(&1["id"] == id)) assert done["status"] == "done" assert done["result"] == "All done!" end test "fail marks a task as failed" do {:ok, id} = Symbiont.Queue.enqueue("Fail me") _taken = Symbiont.Queue.take(1) Symbiont.Queue.fail(id, "something broke") Process.sleep(50) tasks = Symbiont.Queue.list() failed = Enum.find(tasks, &(&1["id"] == id)) assert failed["status"] == "failed" assert failed["result"] == "something broke" end test "list filters by status" do {:ok, _} = Symbiont.Queue.enqueue("Pending 1") {:ok, _} = Symbiont.Queue.enqueue("To complete") # Take one task (first pending) [taken] = Symbiont.Queue.take(1) # Complete the taken task Symbiont.Queue.complete(taken["id"], "done") Process.sleep(50) pending = Symbiont.Queue.list("pending") assert length(pending) == 1 done = Symbiont.Queue.list("done") assert length(done) == 1 end test "queue persists to JSONL file", %{tmp_dir: tmp_dir} do {:ok, _} = Symbiont.Queue.enqueue("Persistent task") path = Path.join(tmp_dir, "queue.jsonl") content = File.read!(path) assert String.contains?(content, "Persistent task") assert String.contains?(content, "pending") end test "queue loads tasks from file on restart", %{tmp_dir: tmp_dir} do {:ok, _} = Symbiont.Queue.enqueue("Survivor task") assert Symbiont.Queue.size() == 1 # Stop and restart GenServer.stop(Symbiont.Queue) {:ok, _} = Symbiont.Queue.start_link(data_dir: tmp_dir) assert Symbiont.Queue.size() == 1 [task] = Symbiont.Queue.list("pending") assert task["task"] == "Survivor task" end end