121 lines
4.2 KiB
Elixir
121 lines
4.2 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, 100
|
|
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 "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
|