- Switch PacketProducer buffer from list to :queue for O(1) overflow handling - Add cached leadership check via :persistent_term to eliminate GenServer.call bottleneck in packet distribution hot path - Simplify packets_live coordinate extraction from 70 lines of nested conditionals to helper functions (extract_coordinate/2, format_coordinate/1) - Remove debug logging from hot paths (packets.ex queries, PacketDistributor, app.js console.logs) - Remove sobelow dependency (false positives blocking commits) - Add 25 new tests for PacketProducer, coordinate helpers, and cached leadership
112 lines
3.7 KiB
Elixir
112 lines
3.7 KiB
Elixir
defmodule Aprsme.PacketProducerTest do
|
|
use ExUnit.Case, async: true
|
|
|
|
alias Aprsme.PacketProducer
|
|
|
|
describe "init/1" do
|
|
test "initializes with default max_buffer_size" do
|
|
{:producer, state} = PacketProducer.init([])
|
|
assert state.demand == 0
|
|
assert state.buffer_size == 0
|
|
assert state.max_buffer_size == 1000
|
|
end
|
|
|
|
test "initializes with custom max_buffer_size" do
|
|
{:producer, state} = PacketProducer.init(max_buffer_size: 500)
|
|
assert state.max_buffer_size == 500
|
|
end
|
|
|
|
test "initializes with empty :queue buffer" do
|
|
{:producer, state} = PacketProducer.init([])
|
|
assert :queue.is_queue(state.buffer)
|
|
assert :queue.is_empty(state.buffer)
|
|
end
|
|
end
|
|
|
|
describe "handle_cast {:packet, _} with demand > 0" do
|
|
test "dispatches packet immediately when demand exists" do
|
|
state = %{demand: 3, buffer: :queue.new(), buffer_size: 0, max_buffer_size: 1000}
|
|
{:noreply, events, new_state} = PacketProducer.handle_cast({:packet, %{sender: "TEST"}}, state)
|
|
|
|
assert events == [%{sender: "TEST"}]
|
|
assert new_state.demand == 2
|
|
end
|
|
end
|
|
|
|
describe "handle_cast {:packet, _} with demand == 0" do
|
|
test "buffers packet when no demand" do
|
|
state = %{demand: 0, buffer: :queue.new(), buffer_size: 0, max_buffer_size: 1000}
|
|
{:noreply, [], new_state} = PacketProducer.handle_cast({:packet, %{sender: "TEST"}}, state)
|
|
|
|
assert new_state.buffer_size == 1
|
|
assert :queue.len(new_state.buffer) == 1
|
|
end
|
|
|
|
test "drops oldest packet when buffer overflows" do
|
|
# Fill buffer to max
|
|
buffer =
|
|
Enum.reduce(1..3, :queue.new(), fn i, q ->
|
|
:queue.in(%{sender: "PACKET#{i}"}, q)
|
|
end)
|
|
|
|
state = %{demand: 0, buffer: buffer, buffer_size: 3, max_buffer_size: 3}
|
|
|
|
{:noreply, [], new_state} =
|
|
PacketProducer.handle_cast({:packet, %{sender: "NEW"}}, state)
|
|
|
|
# Buffer size should still be max
|
|
assert new_state.buffer_size == 3
|
|
|
|
# Oldest packet (PACKET1) should be dropped, NEW should be present
|
|
items = :queue.to_list(new_state.buffer)
|
|
senders = Enum.map(items, & &1.sender)
|
|
refute "PACKET1" in senders
|
|
assert "NEW" in senders
|
|
assert "PACKET2" in senders
|
|
assert "PACKET3" in senders
|
|
end
|
|
end
|
|
|
|
describe "handle_demand/2" do
|
|
test "dispatches buffered packets in FIFO order" do
|
|
buffer =
|
|
Enum.reduce(1..5, :queue.new(), fn i, q ->
|
|
:queue.in(%{sender: "P#{i}"}, q)
|
|
end)
|
|
|
|
state = %{demand: 0, buffer: buffer, buffer_size: 5, max_buffer_size: 1000}
|
|
{:noreply, events, new_state} = PacketProducer.handle_demand(3, state)
|
|
|
|
# Should dispatch first 3 (FIFO order)
|
|
assert length(events) == 3
|
|
assert Enum.map(events, & &1.sender) == ["P1", "P2", "P3"]
|
|
assert new_state.buffer_size == 2
|
|
assert new_state.demand == 0
|
|
end
|
|
|
|
test "dispatches all available when demand exceeds buffer" do
|
|
buffer = :queue.in(%{sender: "ONLY"}, :queue.new())
|
|
|
|
state = %{demand: 0, buffer: buffer, buffer_size: 1, max_buffer_size: 1000}
|
|
{:noreply, events, new_state} = PacketProducer.handle_demand(5, state)
|
|
|
|
assert events == [%{sender: "ONLY"}]
|
|
assert new_state.buffer_size == 0
|
|
assert new_state.demand == 4
|
|
end
|
|
|
|
test "stores demand when buffer is empty" do
|
|
state = %{demand: 0, buffer: :queue.new(), buffer_size: 0, max_buffer_size: 1000}
|
|
{:noreply, [], new_state} = PacketProducer.handle_demand(10, state)
|
|
|
|
assert new_state.demand == 10
|
|
end
|
|
|
|
test "accumulates demand" do
|
|
state = %{demand: 5, buffer: :queue.new(), buffer_size: 0, max_buffer_size: 1000}
|
|
{:noreply, [], new_state} = PacketProducer.handle_demand(3, state)
|
|
|
|
assert new_state.demand == 8
|
|
end
|
|
end
|
|
end
|