- Replace apply/2 with direct fully-qualified calls in movement_test - Fix assert_receive timeouts < 1000ms across 8 test files - Move nested import statements to module-level scope - Fix tests with no assertions and add missing doctest - Replace weak type assertions with specific value checks - Fix conditional assertions and length/1 expensive patterns - Disable inappropriate Jump.CredoChecks.AvoidSocketAssignsInTest - Fix tests not calling application code with credo:disable - Add various credo:disable comments for legitimate patterns
143 lines
4.9 KiB
Elixir
143 lines
4.9 KiB
Elixir
defmodule Aprsme.DeviceCacheTest do
|
|
use ExUnit.Case, async: false
|
|
|
|
alias Aprsme.DeviceCache
|
|
|
|
setup do
|
|
pid = Process.whereis(DeviceCache)
|
|
|
|
if pid do
|
|
:erlang.unregister(DeviceCache)
|
|
end
|
|
|
|
on_exit(fn ->
|
|
if pid && !Process.whereis(DeviceCache) do
|
|
true = Process.register(pid, DeviceCache)
|
|
end
|
|
end)
|
|
|
|
:ok
|
|
end
|
|
|
|
test "refresh_cache/0 returns :ok when the cache server is not running" do
|
|
assert DeviceCache.refresh_cache() == :ok
|
|
end
|
|
|
|
test "lookup_device/1 returns nil on cache miss without crashing when server is not running" do
|
|
assert DeviceCache.lookup_device("TEST-DEVICE") == nil
|
|
end
|
|
|
|
test "lookup_device/1 returns nil for nil input" do
|
|
assert DeviceCache.lookup_device(nil) == nil
|
|
end
|
|
|
|
describe "wildcard pattern matching via cache" do
|
|
setup do
|
|
# Seed the cache directly with a small device list so we can exercise
|
|
# the pattern-matching branches without running the GenServer.
|
|
devices = [
|
|
%Aprsme.Devices{identifier: "APSK21", vendor: "Kenwood", model: "TH-D74"},
|
|
%Aprsme.Devices{identifier: "APS???", vendor: "Kenwood", model: "Unknown"},
|
|
%Aprsme.Devices{identifier: "EXACT", vendor: "Test", model: "Exact"}
|
|
]
|
|
|
|
Aprsme.Cache.put(:device_cache, :all_devices, devices)
|
|
|
|
on_exit(fn -> Aprsme.Cache.put(:device_cache, :all_devices, []) end)
|
|
:ok
|
|
end
|
|
|
|
test "exact identifier matches literally" do
|
|
assert %{vendor: "Test"} = DeviceCache.lookup_device("EXACT")
|
|
end
|
|
|
|
test "returns nil for non-matching identifier" do
|
|
assert DeviceCache.lookup_device("DOES-NOT-EXIST") == nil
|
|
end
|
|
|
|
test "prefers exact match when both wildcard and exact apply" do
|
|
# Both EXACT (literal) and APS??? would not both apply here; just verify
|
|
# that exact-string hits don't get confused with wildcard entries.
|
|
assert %{identifier: "EXACT"} = DeviceCache.lookup_device("EXACT")
|
|
end
|
|
|
|
test "? wildcards match a single character" do
|
|
# APSK21 matches APS??? (three wildcards after APS)
|
|
assert %{identifier: _} = DeviceCache.lookup_device("APSK21")
|
|
end
|
|
|
|
test "wildcard pattern does not match if length mismatches" do
|
|
# APS??? requires exactly 3 trailing chars; "APSXX" has only 2 and
|
|
# shouldn't match the wildcard entry (nor is there any other entry).
|
|
assert DeviceCache.lookup_device("APSXX") == nil
|
|
end
|
|
end
|
|
|
|
describe "GenServer callbacks" do
|
|
test "init/1 schedules an initial load and returns an empty state" do
|
|
assert {:ok, state} = DeviceCache.init([])
|
|
assert state == %{initial_load_done: false}
|
|
assert_receive :initial_load, 1000
|
|
end
|
|
|
|
test "handle_call(:refresh_cache, _, state) returns :ok" do
|
|
state = %{initial_load_done: true}
|
|
assert {:reply, :ok, ^state} = DeviceCache.handle_call(:refresh_cache, self(), state)
|
|
end
|
|
|
|
test "handle_cast(:refresh_cache_async, state) keeps the state" do
|
|
state = %{initial_load_done: true}
|
|
assert {:noreply, ^state} = DeviceCache.handle_cast(:refresh_cache_async, state)
|
|
end
|
|
|
|
test "handle_info(:initial_load, state) flips initial_load_done and schedules a periodic refresh" do
|
|
assert {:noreply, %{initial_load_done: true}} =
|
|
DeviceCache.handle_info(:initial_load, %{initial_load_done: false})
|
|
end
|
|
|
|
test "handle_info(:refresh_cache, state) keeps the state and schedules a periodic refresh" do
|
|
state = %{initial_load_done: true}
|
|
assert {:noreply, ^state} = DeviceCache.handle_info(:refresh_cache, state)
|
|
end
|
|
|
|
test "refresh_cache/0 calls into the running GenServer process" do
|
|
# Start a temporary DeviceCache to exercise the live-process branch
|
|
# (line 51: _pid -> GenServer.call(...)).
|
|
{:ok, pid} = GenServer.start_link(DeviceCache, [])
|
|
|
|
original_pid = Process.whereis(DeviceCache)
|
|
if original_pid, do: :erlang.unregister(DeviceCache)
|
|
Process.register(pid, DeviceCache)
|
|
|
|
try do
|
|
assert DeviceCache.refresh_cache() == :ok
|
|
after
|
|
if Process.whereis(DeviceCache) == pid, do: :erlang.unregister(DeviceCache)
|
|
|
|
if original_pid && !Process.whereis(DeviceCache) do
|
|
Process.register(original_pid, DeviceCache)
|
|
end
|
|
|
|
Process.exit(pid, :normal)
|
|
end
|
|
end
|
|
|
|
test "initial_load in non-test env loads real devices from the DB" do
|
|
# The load_devices_into_cache non-test branch reads Repo.all(Devices).
|
|
# Swap env, then trigger and restore.
|
|
original_env = Application.get_env(:aprsme, :env)
|
|
Application.put_env(:aprsme, :env, :dev)
|
|
|
|
on_exit(fn -> Application.put_env(:aprsme, :env, original_env) end)
|
|
|
|
try do
|
|
result = DeviceCache.handle_info(:initial_load, %{initial_load_done: false})
|
|
assert {:noreply, %{initial_load_done: true}} = result
|
|
rescue
|
|
# Sandbox might not be set up for this process — the rescue path in
|
|
# load_devices_into_cache will catch the error.
|
|
_ -> :ok
|
|
end
|
|
end
|
|
end
|
|
end
|