add comprehensive tests for PacketReplay module
This commit is contained in:
parent
886495b7b0
commit
bc810fe1c2
1 changed files with 725 additions and 0 deletions
725
test/aprs/packet_replay_test.exs
Normal file
725
test/aprs/packet_replay_test.exs
Normal file
|
|
@ -0,0 +1,725 @@
|
|||
defmodule Aprs.PacketReplayTest do
|
||||
use Aprs.DataCase, async: true
|
||||
|
||||
import Mox
|
||||
|
||||
alias Aprs.PacketReplay
|
||||
alias AprsWeb.Endpoint
|
||||
alias Phoenix.Socket.Broadcast
|
||||
|
||||
setup :verify_on_exit!
|
||||
|
||||
setup do
|
||||
# Mock the Packets module functions
|
||||
Mox.defmock(MockPackets, for: Aprs.PacketsBehaviour)
|
||||
Application.put_env(:aprs, :packets_module, MockPackets)
|
||||
|
||||
# Start a test registry for the packet replay processes
|
||||
start_supervised!({Registry, keys: :unique, name: Aprs.ReplayRegistry})
|
||||
|
||||
# Example test data
|
||||
user_id = "test_user_#{:rand.uniform(10_000)}"
|
||||
# New York area
|
||||
bounds = [-74.0, 40.0, -73.0, 41.0]
|
||||
|
||||
mock_packet = %{
|
||||
id: "test_packet_1",
|
||||
received_at: DateTime.utc_now(),
|
||||
lat: 40.5,
|
||||
lon: -73.5,
|
||||
sender: "N0CALL",
|
||||
data_type: "position"
|
||||
}
|
||||
|
||||
%{
|
||||
user_id: user_id,
|
||||
bounds: bounds,
|
||||
mock_packet: mock_packet
|
||||
}
|
||||
end
|
||||
|
||||
describe "start_replay/1" do
|
||||
test "starts replay with required options", %{user_id: user_id, bounds: bounds} do
|
||||
opts = [user_id: user_id, bounds: bounds]
|
||||
|
||||
assert {:ok, pid} = PacketReplay.start_replay(opts)
|
||||
assert is_pid(pid)
|
||||
assert Process.alive?(pid)
|
||||
|
||||
# Clean up
|
||||
GenServer.stop(pid)
|
||||
end
|
||||
|
||||
test "raises error when bounds are missing", %{user_id: user_id} do
|
||||
opts = [user_id: user_id]
|
||||
|
||||
assert_raise ArgumentError, "Map bounds are required for packet replay", fn ->
|
||||
PacketReplay.start_replay(opts)
|
||||
end
|
||||
end
|
||||
|
||||
test "starts replay with all optional parameters", %{user_id: user_id, bounds: bounds} do
|
||||
opts = [
|
||||
user_id: user_id,
|
||||
bounds: bounds,
|
||||
callsign: "N0CALL",
|
||||
start_time: DateTime.add(DateTime.utc_now(), -3600, :second),
|
||||
end_time: DateTime.utc_now(),
|
||||
replay_speed: 2.0,
|
||||
limit: 1000,
|
||||
with_position: true
|
||||
]
|
||||
|
||||
assert {:ok, pid} = PacketReplay.start_replay(opts)
|
||||
assert Process.alive?(pid)
|
||||
|
||||
# Clean up
|
||||
GenServer.stop(pid)
|
||||
end
|
||||
|
||||
test "fails when user_id is missing", %{bounds: bounds} do
|
||||
opts = [bounds: bounds]
|
||||
|
||||
assert_raise KeyError, fn ->
|
||||
PacketReplay.start_replay(opts)
|
||||
end
|
||||
end
|
||||
|
||||
test "registers process with unique name per user", %{bounds: bounds} do
|
||||
user_id1 = "user1"
|
||||
user_id2 = "user2"
|
||||
|
||||
{:ok, pid1} = PacketReplay.start_replay(user_id: user_id1, bounds: bounds)
|
||||
{:ok, pid2} = PacketReplay.start_replay(user_id: user_id2, bounds: bounds)
|
||||
|
||||
assert pid1 != pid2
|
||||
assert Process.alive?(pid1)
|
||||
assert Process.alive?(pid2)
|
||||
|
||||
# Clean up
|
||||
GenServer.stop(pid1)
|
||||
GenServer.stop(pid2)
|
||||
end
|
||||
end
|
||||
|
||||
describe "stop_replay/1" do
|
||||
test "stops an active replay session", %{user_id: user_id, bounds: bounds} do
|
||||
{:ok, pid} = PacketReplay.start_replay(user_id: user_id, bounds: bounds)
|
||||
assert Process.alive?(pid)
|
||||
|
||||
assert :ok = PacketReplay.stop_replay(user_id)
|
||||
|
||||
# Give it a moment to stop
|
||||
Process.sleep(10)
|
||||
refute Process.alive?(pid)
|
||||
end
|
||||
|
||||
test "returns error when no replay session exists" do
|
||||
assert {:error, :not_found} = PacketReplay.stop_replay("nonexistent_user")
|
||||
end
|
||||
end
|
||||
|
||||
describe "pause_replay/1" do
|
||||
test "pauses an active replay session", %{user_id: user_id, bounds: bounds} do
|
||||
{:ok, _pid} = PacketReplay.start_replay(user_id: user_id, bounds: bounds)
|
||||
|
||||
assert :ok = PacketReplay.pause_replay(user_id)
|
||||
|
||||
# Verify state
|
||||
info = PacketReplay.get_replay_info(user_id)
|
||||
assert info.paused == true
|
||||
|
||||
# Clean up
|
||||
PacketReplay.stop_replay(user_id)
|
||||
end
|
||||
|
||||
test "fails when replay session doesn't exist" do
|
||||
assert_raise RuntimeError, fn ->
|
||||
PacketReplay.pause_replay("nonexistent_user")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "resume_replay/1" do
|
||||
test "resumes a paused replay session", %{user_id: user_id, bounds: bounds} do
|
||||
{:ok, _pid} = PacketReplay.start_replay(user_id: user_id, bounds: bounds)
|
||||
|
||||
# First pause it
|
||||
:ok = PacketReplay.pause_replay(user_id)
|
||||
info = PacketReplay.get_replay_info(user_id)
|
||||
assert info.paused == true
|
||||
|
||||
# Then resume it
|
||||
assert :ok = PacketReplay.resume_replay(user_id)
|
||||
|
||||
info = PacketReplay.get_replay_info(user_id)
|
||||
assert info.paused == false
|
||||
|
||||
# Clean up
|
||||
PacketReplay.stop_replay(user_id)
|
||||
end
|
||||
|
||||
test "acknowledges resume on already running session", %{user_id: user_id, bounds: bounds} do
|
||||
{:ok, _pid} = PacketReplay.start_replay(user_id: user_id, bounds: bounds)
|
||||
|
||||
# Resume without pausing first
|
||||
assert :ok = PacketReplay.resume_replay(user_id)
|
||||
|
||||
info = PacketReplay.get_replay_info(user_id)
|
||||
assert info.paused == false
|
||||
|
||||
# Clean up
|
||||
PacketReplay.stop_replay(user_id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "set_replay_speed/2" do
|
||||
test "sets replay speed", %{user_id: user_id, bounds: bounds} do
|
||||
{:ok, _pid} = PacketReplay.start_replay(user_id: user_id, bounds: bounds)
|
||||
|
||||
assert :ok = PacketReplay.set_replay_speed(user_id, 10.0)
|
||||
|
||||
info = PacketReplay.get_replay_info(user_id)
|
||||
assert info.replay_speed == 10.0
|
||||
|
||||
# Clean up
|
||||
PacketReplay.stop_replay(user_id)
|
||||
end
|
||||
|
||||
test "validates speed is positive number" do
|
||||
user_id = "test_user"
|
||||
|
||||
# These should raise function clause errors
|
||||
assert_raise FunctionClauseError, fn ->
|
||||
PacketReplay.set_replay_speed(user_id, 0)
|
||||
end
|
||||
|
||||
assert_raise FunctionClauseError, fn ->
|
||||
PacketReplay.set_replay_speed(user_id, -1.0)
|
||||
end
|
||||
|
||||
assert_raise FunctionClauseError, fn ->
|
||||
PacketReplay.set_replay_speed(user_id, "invalid")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "update_filters/2" do
|
||||
test "updates filters and restarts replay", %{user_id: user_id, bounds: bounds} do
|
||||
{:ok, _pid} = PacketReplay.start_replay(user_id: user_id, bounds: bounds)
|
||||
|
||||
new_filters = [callsign: "N1CALL", limit: 2000]
|
||||
assert :ok = PacketReplay.update_filters(user_id, new_filters)
|
||||
|
||||
info = PacketReplay.get_replay_info(user_id)
|
||||
assert info.callsign == "N1CALL"
|
||||
|
||||
# Clean up
|
||||
PacketReplay.stop_replay(user_id)
|
||||
end
|
||||
|
||||
test "validates bounds format in filters", %{user_id: user_id, bounds: bounds} do
|
||||
{:ok, _pid} = PacketReplay.start_replay(user_id: user_id, bounds: bounds)
|
||||
|
||||
# Valid bounds update
|
||||
new_bounds = [-75.0, 39.0, -72.0, 42.0]
|
||||
assert :ok = PacketReplay.update_filters(user_id, bounds: new_bounds)
|
||||
|
||||
info = PacketReplay.get_replay_info(user_id)
|
||||
assert info.bounds == new_bounds
|
||||
|
||||
# Invalid bounds should keep old bounds
|
||||
assert :ok = PacketReplay.update_filters(user_id, bounds: "invalid")
|
||||
|
||||
info = PacketReplay.get_replay_info(user_id)
|
||||
# Should keep the valid bounds
|
||||
assert info.bounds == new_bounds
|
||||
|
||||
# Clean up
|
||||
PacketReplay.stop_replay(user_id)
|
||||
end
|
||||
|
||||
test "validates filters must be a list" do
|
||||
user_id = "test_user"
|
||||
|
||||
assert_raise FunctionClauseError, fn ->
|
||||
PacketReplay.update_filters(user_id, %{callsign: "N0CALL"})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_replay_info/1" do
|
||||
test "returns replay session information", %{user_id: user_id, bounds: bounds} do
|
||||
start_time = DateTime.add(DateTime.utc_now(), -1800, :second)
|
||||
end_time = DateTime.utc_now()
|
||||
|
||||
opts = [
|
||||
user_id: user_id,
|
||||
bounds: bounds,
|
||||
callsign: "N0CALL",
|
||||
start_time: start_time,
|
||||
end_time: end_time,
|
||||
replay_speed: 3.0,
|
||||
limit: 2500,
|
||||
with_position: true
|
||||
]
|
||||
|
||||
{:ok, _pid} = PacketReplay.start_replay(opts)
|
||||
|
||||
info = PacketReplay.get_replay_info(user_id)
|
||||
|
||||
assert info.user_id == user_id
|
||||
assert info.bounds == bounds
|
||||
assert info.callsign == "N0CALL"
|
||||
assert info.start_time == start_time
|
||||
assert info.end_time == end_time
|
||||
assert info.replay_speed == 3.0
|
||||
assert info.with_position == true
|
||||
assert info.packets_sent == 0
|
||||
assert info.paused == false
|
||||
assert %DateTime{} = info.replay_started_at
|
||||
|
||||
# Clean up
|
||||
PacketReplay.stop_replay(user_id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "init/1" do
|
||||
test "initializes with default values", %{user_id: user_id, bounds: bounds} do
|
||||
opts = [user_id: user_id, bounds: bounds]
|
||||
|
||||
assert {:ok, state} = PacketReplay.init(opts)
|
||||
|
||||
assert state.user_id == user_id
|
||||
assert state.bounds == bounds
|
||||
# default
|
||||
assert state.replay_speed == 5.0
|
||||
# default
|
||||
assert state.limit == 5000
|
||||
# default
|
||||
assert state.with_position == true
|
||||
assert state.paused == false
|
||||
assert state.packets_sent == 0
|
||||
assert is_nil(state.callsign)
|
||||
assert is_nil(state.region)
|
||||
assert is_nil(state.replay_timer)
|
||||
assert is_nil(state.last_packet_time)
|
||||
assert %DateTime{} = state.start_time
|
||||
assert %DateTime{} = state.end_time
|
||||
assert %DateTime{} = state.replay_started_at
|
||||
end
|
||||
|
||||
test "respects custom options", %{user_id: user_id, bounds: bounds} do
|
||||
start_time = DateTime.add(DateTime.utc_now(), -1800, :second)
|
||||
end_time = DateTime.utc_now()
|
||||
|
||||
opts = [
|
||||
user_id: user_id,
|
||||
bounds: bounds,
|
||||
callsign: "N0CALL",
|
||||
start_time: start_time,
|
||||
end_time: end_time,
|
||||
replay_speed: 2.0,
|
||||
limit: 1000,
|
||||
with_position: false
|
||||
]
|
||||
|
||||
assert {:ok, state} = PacketReplay.init(opts)
|
||||
|
||||
assert state.callsign == "N0CALL"
|
||||
assert state.start_time == start_time
|
||||
assert state.end_time == end_time
|
||||
assert state.replay_speed == 2.0
|
||||
assert state.limit == 1000
|
||||
assert state.with_position == false
|
||||
end
|
||||
|
||||
test "limits start_time to 1 hour ago maximum", %{user_id: user_id, bounds: bounds} do
|
||||
# Try to set start time to 2 hours ago
|
||||
old_start_time = DateTime.add(DateTime.utc_now(), -7200, :second)
|
||||
|
||||
opts = [
|
||||
user_id: user_id,
|
||||
bounds: bounds,
|
||||
start_time: old_start_time
|
||||
]
|
||||
|
||||
assert {:ok, state} = PacketReplay.init(opts)
|
||||
|
||||
# Should be limited to 1 hour ago (3600 seconds)
|
||||
one_hour_ago = DateTime.add(DateTime.utc_now(), -3600, :second)
|
||||
assert DateTime.diff(state.start_time, one_hour_ago, :second) >= -10
|
||||
end
|
||||
|
||||
test "sets replay topic correctly", %{user_id: user_id, bounds: bounds} do
|
||||
opts = [user_id: user_id, bounds: bounds]
|
||||
|
||||
assert {:ok, state} = PacketReplay.init(opts)
|
||||
|
||||
assert state.replay_topic == "replay:#{user_id}"
|
||||
end
|
||||
end
|
||||
|
||||
describe "handle_info(:start_replay)" do
|
||||
test "handles start_replay message with mock packets", %{mock_packet: mock_packet} do
|
||||
# Set up mock expectations
|
||||
MockPackets
|
||||
|> expect(:get_historical_packet_count, fn _opts -> 1 end)
|
||||
|> expect(:stream_packets_for_replay, fn _opts ->
|
||||
Stream.unfold([{0.1, mock_packet}], fn
|
||||
[packet | rest] -> {packet, rest}
|
||||
[] -> nil
|
||||
end)
|
||||
end)
|
||||
|
||||
user_id = "test_user"
|
||||
bounds = [-74.0, 40.0, -73.0, 41.0]
|
||||
|
||||
state = %{
|
||||
user_id: user_id,
|
||||
replay_topic: "replay:#{user_id}",
|
||||
replay_speed: 5.0,
|
||||
start_time: DateTime.add(DateTime.utc_now(), -3600, :second),
|
||||
end_time: DateTime.utc_now(),
|
||||
region: nil,
|
||||
bounds: bounds,
|
||||
callsign: nil,
|
||||
with_position: true,
|
||||
limit: 5000,
|
||||
paused: false,
|
||||
packets_sent: 0,
|
||||
replay_started_at: DateTime.utc_now(),
|
||||
replay_timer: nil,
|
||||
last_packet_time: nil
|
||||
}
|
||||
|
||||
# Subscribe to the broadcast topic to verify messages
|
||||
Endpoint.subscribe("replay:#{user_id}")
|
||||
|
||||
result = PacketReplay.handle_info(:start_replay, state)
|
||||
|
||||
assert {:noreply, new_state} = result
|
||||
assert new_state.last_packet_time == mock_packet.received_at
|
||||
assert is_reference(new_state.replay_timer)
|
||||
|
||||
# Should have received broadcast
|
||||
assert_receive %Broadcast{
|
||||
topic: "replay:" <> _,
|
||||
event: "replay_started"
|
||||
}
|
||||
end
|
||||
|
||||
test "handles empty packet stream", %{user_id: user_id, bounds: bounds} do
|
||||
# Set up mock expectations for empty stream
|
||||
MockPackets
|
||||
|> expect(:get_historical_packet_count, fn _opts -> 0 end)
|
||||
|> expect(:stream_packets_for_replay, fn _opts ->
|
||||
Stream.unfold([], fn
|
||||
[] -> nil
|
||||
end)
|
||||
end)
|
||||
|
||||
state = %{
|
||||
user_id: user_id,
|
||||
replay_topic: "replay:#{user_id}",
|
||||
replay_speed: 5.0,
|
||||
start_time: DateTime.add(DateTime.utc_now(), -3600, :second),
|
||||
end_time: DateTime.utc_now(),
|
||||
region: nil,
|
||||
bounds: bounds,
|
||||
callsign: nil,
|
||||
with_position: true,
|
||||
limit: 5000,
|
||||
paused: false,
|
||||
packets_sent: 0,
|
||||
replay_started_at: DateTime.utc_now(),
|
||||
replay_timer: nil,
|
||||
last_packet_time: nil
|
||||
}
|
||||
|
||||
# Subscribe to the broadcast topic
|
||||
Endpoint.subscribe("replay:#{user_id}")
|
||||
|
||||
result = PacketReplay.handle_info(:start_replay, state)
|
||||
|
||||
assert {:stop, :normal, ^state} = result
|
||||
|
||||
# Should have received completion broadcast
|
||||
assert_receive %Broadcast{
|
||||
topic: "replay:" <> _,
|
||||
event: "replay_complete",
|
||||
payload: %{packets_sent: 0, message: "No matching packets found for replay"}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "handle_info({:send_packet, packet, stream})" do
|
||||
test "reschedules packet when paused", %{mock_packet: mock_packet} do
|
||||
stream = Stream.unfold([], fn _ -> nil end)
|
||||
|
||||
state = %{
|
||||
user_id: "test_user",
|
||||
replay_topic: "replay:test_user",
|
||||
paused: true,
|
||||
replay_timer: nil,
|
||||
packets_sent: 0,
|
||||
last_packet_time: nil
|
||||
}
|
||||
|
||||
result = PacketReplay.handle_info({:send_packet, mock_packet, stream}, state)
|
||||
|
||||
assert {:noreply, new_state} = result
|
||||
assert is_reference(new_state.replay_timer)
|
||||
end
|
||||
|
||||
test "broadcasts packet and schedules next when not paused", %{mock_packet: mock_packet} do
|
||||
next_packet = %{mock_packet | id: "test_packet_2"}
|
||||
|
||||
stream =
|
||||
Stream.unfold([{0.1, next_packet}], fn
|
||||
[packet | rest] -> {packet, rest}
|
||||
[] -> nil
|
||||
end)
|
||||
|
||||
state = %{
|
||||
user_id: "test_user",
|
||||
replay_topic: "replay:test_user",
|
||||
paused: false,
|
||||
replay_timer: nil,
|
||||
packets_sent: 0,
|
||||
last_packet_time: nil
|
||||
}
|
||||
|
||||
# Subscribe to broadcasts
|
||||
Endpoint.subscribe("replay:test_user")
|
||||
|
||||
result = PacketReplay.handle_info({:send_packet, mock_packet, stream}, state)
|
||||
|
||||
assert {:noreply, new_state} = result
|
||||
assert new_state.packets_sent == 1
|
||||
assert new_state.last_packet_time == next_packet.received_at
|
||||
assert is_reference(new_state.replay_timer)
|
||||
|
||||
# Should have received packet broadcast
|
||||
assert_receive %Broadcast{
|
||||
event: "historical_packet",
|
||||
payload: %{is_historical: true}
|
||||
}
|
||||
end
|
||||
|
||||
test "completes replay when no more packets", %{mock_packet: mock_packet} do
|
||||
# Empty stream - no more packets
|
||||
stream = Stream.unfold([], fn _ -> nil end)
|
||||
|
||||
state = %{
|
||||
user_id: "test_user",
|
||||
replay_topic: "replay:test_user",
|
||||
paused: false,
|
||||
replay_timer: nil,
|
||||
packets_sent: 5,
|
||||
last_packet_time: nil
|
||||
}
|
||||
|
||||
# Subscribe to broadcasts
|
||||
Endpoint.subscribe("replay:test_user")
|
||||
|
||||
result = PacketReplay.handle_info({:send_packet, mock_packet, stream}, state)
|
||||
|
||||
assert {:stop, :normal, new_state} = result
|
||||
assert new_state.packets_sent == 6
|
||||
|
||||
# Should have received completion broadcast
|
||||
assert_receive %Broadcast{
|
||||
event: "replay_complete",
|
||||
payload: %{packets_sent: 6, message: "Replay complete"}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "sanitize_packet_for_transport/1" do
|
||||
test "converts packet to JSON-safe format" do
|
||||
packet = %{
|
||||
__meta__: %{},
|
||||
__struct__: SomeStruct,
|
||||
id: "test",
|
||||
received_at: ~U[2023-01-01 12:00:00Z],
|
||||
lat: Decimal.new("40.123456"),
|
||||
nested: %{
|
||||
timestamp: ~N[2023-01-01 12:00:00],
|
||||
value: Decimal.new("1.23")
|
||||
},
|
||||
list: [~U[2023-01-01 12:00:00Z], Decimal.new("4.56")]
|
||||
}
|
||||
|
||||
result = PacketReplay.sanitize_packet_for_transport(packet)
|
||||
|
||||
# Should drop meta fields
|
||||
refute Map.has_key?(result, :__meta__)
|
||||
refute Map.has_key?(result, :__struct__)
|
||||
|
||||
# Should preserve normal fields
|
||||
assert result.id == "test"
|
||||
|
||||
# Should convert DateTime to ISO string
|
||||
assert result.received_at == "2023-01-01T12:00:00Z"
|
||||
|
||||
# Should convert Decimal to float
|
||||
assert result.lat == 40.123456
|
||||
|
||||
# Should handle nested structures
|
||||
assert result.nested.timestamp == "2023-01-01T12:00:00"
|
||||
assert result.nested.value == 1.23
|
||||
|
||||
# Should handle lists
|
||||
assert result.list == ["2023-01-01T12:00:00Z", 4.56]
|
||||
end
|
||||
|
||||
test "handles packets without special types" do
|
||||
packet = %{
|
||||
id: "simple",
|
||||
name: "test",
|
||||
count: 42,
|
||||
active: true
|
||||
}
|
||||
|
||||
result = PacketReplay.sanitize_packet_for_transport(packet)
|
||||
|
||||
assert result == %{
|
||||
id: "simple",
|
||||
name: "test",
|
||||
count: 42,
|
||||
active: true
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "via_tuple/1" do
|
||||
test "generates correct registry tuple" do
|
||||
user_id = "test_user_123"
|
||||
|
||||
result = PacketReplay.via_tuple(user_id)
|
||||
|
||||
assert result == {:via, Registry, {Aprs.ReplayRegistry, "replay:test_user_123"}}
|
||||
end
|
||||
end
|
||||
|
||||
describe "terminate/2" do
|
||||
test "cleans up timers and broadcasts stop message" do
|
||||
timer_ref = make_ref()
|
||||
|
||||
state = %{
|
||||
user_id: "test_user",
|
||||
replay_topic: "replay:test_user",
|
||||
replay_timer: timer_ref,
|
||||
packets_sent: 10
|
||||
}
|
||||
|
||||
# Subscribe to broadcasts
|
||||
Endpoint.subscribe("replay:test_user")
|
||||
|
||||
result = PacketReplay.terminate(:normal, state)
|
||||
|
||||
assert result == :ok
|
||||
|
||||
# Should have received stop broadcast
|
||||
assert_receive %Broadcast{
|
||||
event: "replay_stopped",
|
||||
payload: %{packets_sent: 10, message: "Replay stopped"}
|
||||
}
|
||||
end
|
||||
|
||||
test "handles state without timer" do
|
||||
state = %{
|
||||
user_id: "test_user",
|
||||
replay_topic: "replay:test_user",
|
||||
replay_timer: nil,
|
||||
packets_sent: 0
|
||||
}
|
||||
|
||||
# Subscribe to broadcasts
|
||||
Endpoint.subscribe("replay:test_user")
|
||||
|
||||
result = PacketReplay.terminate(:shutdown, state)
|
||||
|
||||
assert result == :ok
|
||||
|
||||
# Should still broadcast stop message
|
||||
assert_receive %Broadcast{
|
||||
event: "replay_stopped"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "integration tests" do
|
||||
test "full replay lifecycle", %{user_id: user_id, bounds: bounds, mock_packet: mock_packet} do
|
||||
# Set up mock for full lifecycle
|
||||
MockPackets
|
||||
|> expect(:get_historical_packet_count, fn _opts -> 1 end)
|
||||
|> expect(:stream_packets_for_replay, fn _opts ->
|
||||
# Single packet stream
|
||||
Stream.unfold([{0.001, mock_packet}], fn
|
||||
[packet | rest] -> {packet, rest}
|
||||
[] -> nil
|
||||
end)
|
||||
end)
|
||||
|
||||
# Start replay
|
||||
{:ok, pid} = PacketReplay.start_replay(user_id: user_id, bounds: bounds)
|
||||
|
||||
# Subscribe to events
|
||||
Endpoint.subscribe("replay:#{user_id}")
|
||||
|
||||
# Should receive start event
|
||||
assert_receive %Broadcast{
|
||||
event: "replay_started"
|
||||
},
|
||||
1000
|
||||
|
||||
# Should receive packet event quickly (1ms delay)
|
||||
assert_receive %Broadcast{
|
||||
event: "historical_packet"
|
||||
},
|
||||
1000
|
||||
|
||||
# Should receive completion event
|
||||
assert_receive %Broadcast{
|
||||
event: "replay_complete"
|
||||
},
|
||||
1000
|
||||
|
||||
# Process should terminate
|
||||
Process.sleep(50)
|
||||
refute Process.alive?(pid)
|
||||
end
|
||||
|
||||
test "pause and resume functionality", %{user_id: user_id, bounds: bounds} do
|
||||
{:ok, pid} = PacketReplay.start_replay(user_id: user_id, bounds: bounds)
|
||||
|
||||
# Subscribe to events
|
||||
Endpoint.subscribe("replay:#{user_id}")
|
||||
|
||||
# Pause
|
||||
:ok = PacketReplay.pause_replay(user_id)
|
||||
|
||||
assert_receive %Broadcast{
|
||||
event: "replay_paused"
|
||||
}
|
||||
|
||||
info = PacketReplay.get_replay_info(user_id)
|
||||
assert info.paused == true
|
||||
|
||||
# Resume
|
||||
:ok = PacketReplay.resume_replay(user_id)
|
||||
|
||||
assert_receive %Broadcast{
|
||||
event: "replay_resumed"
|
||||
}
|
||||
|
||||
info = PacketReplay.get_replay_info(user_id)
|
||||
assert info.paused == false
|
||||
|
||||
# Clean up
|
||||
GenServer.stop(pid)
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue