dont connect to is server in test

This commit is contained in:
Graham McIntire 2025-06-16 12:45:35 -05:00
parent 3aad7db955
commit 6e8f1e434f
No known key found for this signature in database
8 changed files with 816 additions and 52 deletions

View file

@ -24,6 +24,15 @@ config :aprs, AprsWeb.Endpoint,
secret_key_base: "IV9+ENaw9i8xjReRk4sULRvRgsmFVTGQwQGGrf4G+Q/SFMeHBCNWRlPXQ2YvT36R",
server: false
# Disable APRS-IS external connections in test environment
config :aprs,
aprs_is_server: nil,
aprs_is_port: nil,
aprs_is_default_filter: nil,
aprs_is_login_id: nil,
aprs_is_password: nil,
disable_aprs_connection: true
# Disable automatic migrations during tests
config :aprs, auto_migrate: false

View file

@ -13,55 +13,62 @@ defmodule Aprs.Is do
@impl true
def init(_opts) do
# Trap exits so we can gracefully shut down
Process.flag(:trap_exit, true)
# Add a small delay to prevent rapid reconnection attempts
Process.sleep(2000)
# Get startup parameters
server = Application.get_env(:aprs, :aprs_is_server, ~c"rotate.aprs2.net")
port = Application.get_env(:aprs, :aprs_is_port, 14_580)
default_filter = Application.get_env(:aprs, :aprs_is_default_filter, "r/33/-96/100")
aprs_user_id = Application.get_env(:aprs, :aprs_is_login_id, "W5ISP")
aprs_passcode = Application.get_env(:aprs, :aprs_is_password, "-1")
# Record connection start time
connected_at = DateTime.utc_now()
# Initialize packet statistics
packet_stats = %{
total_packets: 0,
last_packet_at: nil,
packets_per_second: 0,
last_second_count: 0,
last_second_timestamp: System.system_time(:second)
}
with {:ok, socket} <- connect_to_aprs_is(server, port),
:ok <- send_login_string(socket, aprs_user_id, aprs_passcode, default_filter) do
timer = create_timer(@aprs_timeout)
keepalive_timer = create_keepalive_timer(@keepalive_interval)
{:ok,
%{
server: server,
port: port,
socket: socket,
timer: timer,
keepalive_timer: keepalive_timer,
connected_at: connected_at,
packet_stats: packet_stats,
login_params: %{
user_id: aprs_user_id,
passcode: aprs_passcode,
filter: default_filter
}
}}
# Prevent APRS-IS connections in test environment
if Application.get_env(:aprs, :env) == :test or
Application.get_env(:aprs, :disable_aprs_connection, false) do
Logger.warning("APRS-IS connection disabled in test environment")
{:stop, :test_environment_disabled}
else
_ ->
Logger.error("Unable to establish connection or log in to APRS-IS")
{:stop, :aprs_connection_failed}
# Trap exits so we can gracefully shut down
Process.flag(:trap_exit, true)
# Add a small delay to prevent rapid reconnection attempts
Process.sleep(2000)
# Get startup parameters
server = Application.get_env(:aprs, :aprs_is_server, ~c"rotate.aprs2.net")
port = Application.get_env(:aprs, :aprs_is_port, 14_580)
default_filter = Application.get_env(:aprs, :aprs_is_default_filter, "r/33/-96/100")
aprs_user_id = Application.get_env(:aprs, :aprs_is_login_id, "W5ISP")
aprs_passcode = Application.get_env(:aprs, :aprs_is_password, "-1")
# Record connection start time
connected_at = DateTime.utc_now()
# Initialize packet statistics
packet_stats = %{
total_packets: 0,
last_packet_at: nil,
packets_per_second: 0,
last_second_count: 0,
last_second_timestamp: System.system_time(:second)
}
with {:ok, socket} <- connect_to_aprs_is(server, port),
:ok <- send_login_string(socket, aprs_user_id, aprs_passcode, default_filter) do
timer = create_timer(@aprs_timeout)
keepalive_timer = create_keepalive_timer(@keepalive_interval)
{:ok,
%{
server: server,
port: port,
socket: socket,
timer: timer,
keepalive_timer: keepalive_timer,
connected_at: connected_at,
packet_stats: packet_stats,
login_params: %{
user_id: aprs_user_id,
passcode: aprs_passcode,
filter: default_filter
}
}}
else
_ ->
Logger.error("Unable to establish connection or log in to APRS-IS")
{:stop, :aprs_connection_failed}
end
end
end
@ -130,9 +137,16 @@ defmodule Aprs.Is do
# Server methods
defp connect_to_aprs_is(server, port) do
Logger.debug("Connecting to: #{server}:#{port}")
opts = [:binary, active: true]
:gen_tcp.connect(String.to_charlist(server), port, opts)
# Additional safeguard: prevent connections in test environment
if Application.get_env(:aprs, :env) == :test or
Application.get_env(:aprs, :disable_aprs_connection, false) do
Logger.warning("Attempted APRS-IS connection blocked in test environment")
{:error, :test_environment_blocked}
else
Logger.debug("Connecting to: #{server}:#{port}")
opts = [:binary, active: true]
:gen_tcp.connect(String.to_charlist(server), port, opts)
end
end
defp send_login_string(socket, aprs_user_id, aprs_passcode, filter) do
@ -271,7 +285,6 @@ defmodule Aprs.Is do
try do
# Store the packet if it has position data
if has_position_data?(parsed_message) do
Logger.info("Storing packet with position data: #{inspect(parsed_message.sender)}")
# Always set received_at timestamp to ensure consistency
current_time = DateTime.truncate(DateTime.utc_now(), :microsecond)
packet_data = Map.put(parsed_message, :received_at, current_time)

View file

@ -0,0 +1,53 @@
defmodule Aprs.ConnectionPreventionTest do
@moduledoc """
Simple test to verify APRS connections are prevented in test environment.
"""
use ExUnit.Case, async: true
test "APRS connection is disabled in test environment" do
# Verify we're in test environment
assert Mix.env() == :test
assert Application.get_env(:aprs, :env) == :test
# Verify APRS connection is explicitly disabled
assert Application.get_env(:aprs, :disable_aprs_connection) == true
# Verify APRS.Is process is not running
assert Process.whereis(Aprs.Is) == nil
end
test "APRS configuration is safe for testing" do
# Verify server configuration is neutralized
server = Application.get_env(:aprs, :aprs_is_server)
assert server == nil or server == "mock.aprs.test"
# Verify test credentials are used
login_id = Application.get_env(:aprs, :aprs_is_login_id)
assert login_id == "TEST"
# Verify no real APRS servers in config
forbidden_servers = [
"rotate.aprs2.net",
"dallas.aprs2.net",
"seattle.aprs2.net"
]
if server do
server_str = to_string(server)
refute Enum.any?(forbidden_servers, fn forbidden ->
String.contains?(server_str, forbidden)
end)
end
end
test "get_status works without external connection" do
# This should not attempt external connections
status = Aprs.Is.get_status()
# Should return disconnected state
assert status.connected == false
assert status.uptime_seconds == 0
assert status.connected_at == nil
end
end

148
test/aprs/is_test.exs Normal file
View file

@ -0,0 +1,148 @@
defmodule Aprs.IsTest do
@moduledoc """
Tests to ensure APRS-IS external connections are properly blocked in test environment.
"""
use ExUnit.Case, async: true
require Logger
describe "APRS-IS connection prevention in test environment" do
test "APRS.Is module should not start in test environment" do
# Verify that the APRS.Is GenServer is not running
assert Process.whereis(Aprs.Is) == nil
end
test "attempting to start APRS.Is should fail with test environment disabled" do
# Try to start the APRS.Is GenServer manually
result = Aprs.Is.start_link([])
# Should fail because we're in test environment
assert {:error, reason} = result
assert reason in [:test_environment_disabled, :normal]
end
test "get_status should return disconnected state without external connection" do
status = Aprs.Is.get_status()
# Should return disconnected status without attempting external connection
assert status.connected == false
assert status.server != nil
assert status.port != nil
assert status.connected_at == nil
assert status.uptime_seconds == 0
end
test "configuration should disable APRS connections in test" do
# Verify test configuration properly disables connections
assert Application.get_env(:aprs, :disable_aprs_connection) == true
assert Application.get_env(:aprs, :env) == :test
end
test "APRS server configuration should be nil or test values in test environment" do
# Verify APRS server config is neutralized for tests
server = Application.get_env(:aprs, :aprs_is_server)
login_id = Application.get_env(:aprs, :aprs_is_login_id)
# Server should be nil (disabled) or a test value
assert server == nil or server == "mock.aprs.test"
# Login should be test value
assert login_id == "TEST"
end
end
describe "APRS-IS mock functionality" do
setup do
# Start the mock if not already running
case GenServer.start_link(AprsIsMock, [], name: AprsIsMock) do
{:ok, pid} ->
on_exit(fn -> GenServer.stop(pid, :normal) end)
{:ok, mock_pid: pid}
{:error, {:already_started, pid}} ->
{:ok, mock_pid: pid}
end
end
test "mock should provide status without external connections", %{mock_pid: _pid} do
status = AprsIsMock.get_status()
assert status.connected == false
assert status.server == "mock.aprs.test"
assert status.port == 14_580
assert status.login_id == "TEST"
assert is_map(status.packet_stats)
end
test "mock should handle message sending safely", %{mock_pid: _pid} do
# These should not attempt external connections
assert AprsIsMock.send_message("test message") == :ok
assert AprsIsMock.send_message("TEST", "DEST", "hello") == :ok
assert AprsIsMock.set_filter("r/0/0/1") == :ok
assert AprsIsMock.list_active_filters() == :ok
end
test "mock can simulate packet reception for testing", %{mock_pid: _pid} do
# This allows tests to simulate receiving packets without external connections
test_packet = %{
sender: "TEST-1",
destination: "APRS",
path: ["WIDE1-1", "WIDE2-1"],
data_type: :position,
latitude: 33.0,
longitude: -96.0
}
assert AprsIsMock.simulate_packet(test_packet) == :ok
end
test "mock can simulate connection state changes", %{mock_pid: _pid} do
# Simulate connected state
assert AprsIsMock.simulate_connection_state(true) == :ok
status = AprsIsMock.get_status()
assert status.connected == true
assert status.connected_at != nil
# Simulate disconnected state
assert AprsIsMock.simulate_connection_state(false) == :ok
status = AprsIsMock.get_status()
assert status.connected == false
assert status.connected_at == nil
end
end
describe "network isolation verification" do
test "no external network calls should be made during test runs" do
# This test verifies that no actual TCP connections are attempted
# We can do this by checking that :gen_tcp.connect is not called
# with real APRS server addresses
# Common APRS-IS servers that should never be contacted in tests
forbidden_servers = [
"rotate.aprs2.net",
"dallas.aprs2.net",
"seattle.aprs2.net",
"chicago.aprs2.net",
"atlanta.aprs2.net"
]
# Verify these are not in the current configuration
current_server = Application.get_env(:aprs, :aprs_is_server)
if current_server do
server_str = to_string(current_server)
refute Enum.any?(forbidden_servers, fn forbidden ->
String.contains?(server_str, forbidden)
end),
"Test environment should not be configured with real APRS servers"
end
end
test "test environment marker is properly set" do
# Verify we're definitely in test environment
assert Mix.env() == :test
assert Application.get_env(:aprs, :env) == :test
end
end
end

View file

@ -0,0 +1,203 @@
defmodule AprsWeb.Integration.AprsStatusTest do
@moduledoc """
Integration tests to verify that web interfaces work properly
without external APRS connections in the test environment.
"""
use AprsWeb.ConnCase
import Phoenix.LiveViewTest
describe "APRS status endpoints without external connections" do
test "status JSON endpoint returns proper response without APRS connection", %{conn: conn} do
conn = get(conn, "/api/status")
assert response = json_response(conn, 200)
# Should have basic structure even without APRS connection
assert Map.has_key?(response, "aprs_status")
assert Map.has_key?(response, "version")
assert Map.has_key?(response, "uptime")
# APRS status should indicate disconnected state
aprs_status = response["aprs_status"]
assert aprs_status["connected"] == false
assert is_binary(aprs_status["server"])
assert is_integer(aprs_status["port"])
assert aprs_status["uptime_seconds"] == 0
# Should not contain real APRS server addresses
forbidden_servers = [
"rotate.aprs2.net",
"dallas.aprs2.net",
"seattle.aprs2.net"
]
refute Enum.any?(forbidden_servers, fn server ->
String.contains?(aprs_status["server"], server)
end)
end
test "main map page loads without APRS connection", %{conn: conn} do
{:ok, view, html} = live(conn, "/")
# Page should load successfully
assert html =~ "APRS Map"
assert has_element?(view, "#aprs-map")
# Should not show connected status
refute html =~ "Connected to APRS"
# Should handle disconnected state gracefully
# Basic content should be present
assert html =~ "APRS"
end
test "status live view works without APRS connection", %{conn: conn} do
# Try to access status page if it exists
case live(conn, "/status") do
{:ok, view, html} ->
# If status page exists, verify it handles disconnected state
assert html =~ "Status"
# Should show disconnected state information
assert has_element?(view, "[data-testid='connection-status']") ||
html =~ "disconnected" ||
html =~ "not connected"
{:error, {:live_redirect, %{to: "/"}}} ->
# If redirected to home, that's acceptable
:ok
{:error, {:redirect, %{to: "/"}}} ->
# If redirected to home, that's acceptable
:ok
end
end
test "packet data endpoints handle no external connection gracefully", %{conn: conn} do
# Test any packet-related endpoints
case get(conn, "/api/packets") do
%{status: 200} = conn ->
response = json_response(conn, 200)
# Should return empty or default data structure
assert is_list(response) or is_map(response)
%{status: 404} ->
# Endpoint might not exist, which is fine
:ok
%{status: status} when status in [500, 503] ->
# Should not crash with server errors
flunk("Packet endpoint crashed without APRS connection")
end
end
end
describe "LiveView event handling without APRS" do
test "map events work without APRS connection", %{conn: conn} do
{:ok, view, _html} = live(conn, "/")
# Test map bounds change event
bounds_params = %{
"bounds" => %{
"north" => "33.1",
"south" => "32.9",
"east" => "-95.9",
"west" => "-96.1"
}
}
# Should not crash when handling events without APRS connection
assert render_hook(view, "bounds_changed", bounds_params)
# Test map ready event
assert render_hook(view, "map_ready", %{})
# Test replay speed adjustment if available
speed_params = %{"speed" => "1.0"}
render_hook(view, "adjust_replay_speed", speed_params)
end
test "real-time updates are disabled without APRS connection", %{conn: conn} do
{:ok, view, _html} = live(conn, "/")
# Verify that the view is not subscribed to real-time APRS updates
# since there's no APRS connection
# Send a test broadcast that would normally come from APRS.Is
test_packet = %{
sender: "TEST-1",
latitude: 33.0,
longitude: -96.0,
timestamp: DateTime.utc_now()
}
# This should not cause any updates since APRS.Is is not running
AprsWeb.Endpoint.broadcast("aprs_messages", "packet", test_packet)
# Give a moment for any potential updates
Process.sleep(100)
# View should remain stable (no crashes from missing APRS connection)
assert render(view) =~ "APRS Map"
end
end
describe "error handling without external dependencies" do
test "application handles missing APRS connection gracefully", %{conn: conn} do
# Verify various endpoints don't crash when APRS.Is is not available
# Home page
assert {:ok, _view, html} = live(conn, "/")
assert html =~ "APRS"
# API status
conn = get(conn, "/api/status")
assert json_response(conn, 200)
# Any authentication pages should still work
conn = build_conn()
conn = get(conn, "/users/register")
# Redirect or not found is acceptable
assert html_response(conn, 200) =~ "Register" or
conn.status in [302, 404]
end
test "database operations work independently of APRS connection", %{conn: conn} do
# Verify that core application functionality works without APRS
# Database should be accessible
assert Ecto.Adapters.SQL.query!(Aprs.Repo, "SELECT 1", [])
# Web interface should load
{:ok, _view, html} = live(conn, "/")
assert html =~ "APRS"
# This confirms the app can function without external APRS data
end
end
describe "test environment verification" do
test "confirms we're in test environment" do
# Double-check we're actually in test mode
assert Mix.env() == :test
assert Application.get_env(:aprs, :env) == :test
assert Application.get_env(:aprs, :disable_aprs_connection) == true
end
test "APRS.Is process is not running" do
# Verify the real APRS.Is GenServer is not started
assert Process.whereis(Aprs.Is) == nil
end
test "no external network configuration in test" do
# Verify test config doesn't point to real servers
server = Application.get_env(:aprs, :aprs_is_server)
# Should be nil or a test value
assert server == nil or
(is_binary(server) and String.contains?(server, "test")) or
(is_binary(server) and String.contains?(server, "mock"))
end
end
end

158
test/support/README.md Normal file
View file

@ -0,0 +1,158 @@
# Test Environment APRS Isolation
This directory contains support files to ensure that the APRS.me application does not make external network connections during testing.
## Overview
The APRS.me application normally connects to external APRS-IS (Automatic Packet Reporting System - Internet Service) servers to receive real-time amateur radio packet data. During testing, we need to prevent these external connections to ensure:
1. **Test Isolation**: Tests run independently without external dependencies
2. **Network Security**: No unintended external connections during CI/CD
3. **Performance**: Tests run faster without network delays
4. **Reliability**: Tests don't fail due to external service availability
## Implementation
### Configuration-Based Prevention
The primary mechanism is environment-based configuration in `config/test.exs`:
```elixir
# Disable APRS-IS external connections in test environment
config :aprs,
aprs_is_server: nil,
aprs_is_port: nil,
aprs_is_default_filter: nil,
aprs_is_login_id: nil,
aprs_is_password: nil,
disable_aprs_connection: true
```
### Application-Level Guards
The `Aprs.Application` module only starts the APRS-IS supervisor in `:prod` and `:dev` environments:
```elixir
children =
if Application.get_env(:aprs, :env) in [:prod, :dev] do
children ++ [Aprs.Is.IsSupervisor]
else
children
end
```
### Module-Level Safeguards
The `Aprs.Is` module includes additional safeguards to prevent accidental connections:
- Early termination if started in test environment
- Connection blocking in the `connect_to_aprs_is/2` function
- Safe fallback responses for status queries
### Mock Implementation
The `AprsIsMock` module (`aprs_is_mock.ex`) provides a test-safe implementation that:
- Mimics the interface of the real `Aprs.Is` module
- Returns realistic status information without external connections
- Allows simulation of packet reception for testing
- Provides connection state simulation for comprehensive testing
## Usage in Tests
### Basic Setup
The mock is automatically configured in `test_helper.exs`:
```elixir
# Ensure no external APRS connections during tests
Application.put_env(:aprs, :disable_aprs_connection, true)
Code.require_file("support/aprs_is_mock.ex", __DIR__)
```
### Using the Mock
```elixir
# Start the mock in your test setup
{:ok, _pid} = AprsIsMock.start_link()
# Get status (no external connection)
status = AprsIsMock.get_status()
# Simulate packet reception
test_packet = %{sender: "TEST-1", latitude: 33.0, longitude: -96.0}
AprsIsMock.simulate_packet(test_packet)
# Simulate connection state changes
AprsIsMock.simulate_connection_state(true) # connected
AprsIsMock.simulate_connection_state(false) # disconnected
```
### Testing Network Isolation
The `Aprs.IsTest` module includes comprehensive tests to verify:
- APRS.Is module doesn't start in test environment
- Configuration properly disables connections
- No real APRS servers are contacted
- Mock provides expected functionality
## Verification
To verify that external connections are properly blocked:
1. **Run the test suite**: `mix test test/aprs/is_test.exs`
2. **Check configuration**: Ensure test config sets `disable_aprs_connection: true`
3. **Monitor network**: During test runs, no connections should be made to APRS-IS servers
4. **Review logs**: Test environment should log connection prevention messages
## Security Notes
- Never commit real APRS credentials to test configurations
- Use placeholder values like "TEST" for callsigns in tests
- Ensure production credentials are only available in production environment
- Regularly audit test configurations to prevent credential leakage
## Common APRS-IS Servers (Blocked in Tests)
The following servers should never be contacted during testing:
- `rotate.aprs2.net` (Primary rotation server)
- `dallas.aprs2.net` (Dallas server)
- `seattle.aprs2.net` (Seattle server)
- `chicago.aprs2.net` (Chicago server)
- `atlanta.aprs2.net` (Atlanta server)
Any test configuration pointing to these servers indicates a misconfiguration.
## Troubleshooting
### Tests Hanging or Timing Out
If tests hang, check:
- APRS.Is process isn't starting (`Process.whereis(Aprs.Is)` should return `nil`)
- Test configuration has `disable_aprs_connection: true`
- No real server addresses in test config
### External Connection Errors in Tests
If you see TCP connection errors during tests:
- Verify the application supervision tree excludes APRS-IS in test
- Check that `Mix.env()` returns `:test`
- Ensure test_helper.exs properly configures the environment
### Mock Not Working
If the mock doesn't provide expected responses:
- Verify `AprsIsMock` is properly loaded in test_helper.exs
- Check that the mock is started in test setup
- Ensure mock methods match the real module's interface
## Contributing
When adding new APRS-related functionality:
1. **Update the mock** to include new methods
2. **Add test coverage** for network isolation
3. **Verify configuration** prevents external connections
4. **Document any new** test isolation requirements

View file

@ -0,0 +1,169 @@
defmodule AprsIsMock do
@moduledoc """
Mock implementation of Aprs.Is for testing purposes.
This ensures no external APRS connections are made during tests.
"""
use GenServer
require Logger
def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
@impl true
def init(_opts) do
Logger.info("Starting APRS.Is mock for testing")
# Mock connection state
initial_state = %{
connected: false,
server: "mock.aprs.test",
port: 14_580,
connected_at: nil,
login_id: "TEST",
filter: "r/33/-96/100",
packet_stats: %{
total_packets: 0,
last_packet_at: nil,
packets_per_second: 0,
last_second_count: 0,
last_second_timestamp: System.system_time(:second)
},
stored_packet_count: 0
}
{:ok, initial_state}
end
# Client API - Mock implementations
def stop do
Logger.info("Stopping APRS.Is mock")
GenServer.stop(__MODULE__, :normal)
end
def get_status do
case Process.whereis(__MODULE__) do
nil ->
# Mock disconnected state
%{
connected: false,
server: "mock.aprs.test",
port: 14_580,
connected_at: nil,
uptime_seconds: 0,
login_id: "TEST",
filter: "r/33/-96/100",
packet_stats: %{
total_packets: 0,
last_packet_at: nil,
packets_per_second: 0,
last_second_count: 0,
last_second_timestamp: System.system_time(:second)
},
stored_packet_count: 0
}
_pid ->
try do
GenServer.call(__MODULE__, :get_status, 5000)
catch
:exit, _ ->
# Fallback mock state
%{
connected: false,
server: "mock.aprs.test",
port: 14_580,
connected_at: nil,
uptime_seconds: 0,
login_id: "TEST",
filter: "r/33/-96/100",
packet_stats: %{
total_packets: 0,
last_packet_at: nil,
packets_per_second: 0,
last_second_count: 0,
last_second_timestamp: System.system_time(:second)
},
stored_packet_count: 0
}
end
end
end
def set_filter(filter_string) do
Logger.debug("Mock: Setting filter to #{filter_string}")
:ok
end
def list_active_filters do
Logger.debug("Mock: Listing active filters")
:ok
end
def send_message(from, to, message) do
Logger.debug("Mock: Sending message from #{from} to #{to}: #{message}")
:ok
end
def send_message(message) do
Logger.debug("Mock: Sending message: #{message}")
GenServer.call(__MODULE__, {:send_message, message})
end
# Server callbacks
@impl true
def handle_call({:send_message, message}, _from, state) do
Logger.debug("Mock: Handling send message: #{message}")
{:reply, :ok, state}
end
def handle_call(:get_status, _from, state) do
mock_status = %{
state
| connected: false,
uptime_seconds: 0
}
{:reply, mock_status, state}
end
def handle_call({:set_connection_state, connected}, _from, state) do
new_state = %{state | connected: connected, connected_at: if(connected, do: DateTime.utc_now())}
{:reply, :ok, new_state}
end
@impl true
def handle_info(msg, state) do
Logger.debug("Mock: Received unexpected message: #{inspect(msg)}")
{:noreply, state}
end
@impl true
def terminate(reason, _state) do
Logger.info("Mock APRS.Is terminating: #{inspect(reason)}")
:ok
end
# Helper functions for testing
def simulate_packet(packet_data) do
# Simulate receiving an APRS packet for testing purposes.
# This can be used in tests to trigger packet processing without
# connecting to external servers.
Logger.debug("Mock: Simulating packet: #{inspect(packet_data)}")
# Broadcast to live clients like the real implementation would
AprsWeb.Endpoint.broadcast("aprs_messages", "packet", packet_data)
:ok
end
def simulate_connection_state(connected \\ true) do
# Simulate connection state changes for testing.
GenServer.call(__MODULE__, {:set_connection_state, connected})
end
end

View file

@ -1,2 +1,13 @@
ExUnit.start()
Ecto.Adapters.SQL.Sandbox.mode(Aprs.Repo, :manual)
# Ensure no external APRS connections during tests
Application.put_env(:aprs, :disable_aprs_connection, true)
Application.put_env(:aprs, :aprs_is_server, nil)
Application.put_env(:aprs, :aprs_is_port, nil)
Application.put_env(:aprs, :aprs_is_login_id, "TEST")
Application.put_env(:aprs, :aprs_is_password, "-1")
Application.put_env(:aprs, :aprs_is_default_filter, "r/0/0/1")
# Load the APRS mock for testing
Code.require_file("support/aprs_is_mock.ex", __DIR__)