- CacheTest: fix sweep timing race by using negative TTL (-1) instead of positive TTL (1) for already-expired entries - ScoreCache: replace ETS match-spec DateTime comparisons with :ets.foldl + DateTime.compare — DateTime structs are maps in Elixir >= 1.15 and ETS can't compare maps with :< / :> guards - Accounts: drop unsupported returning: true on delete_all, return [] for expired tokens list - Backtest: catch ArgumentError from String.to_existing_atom for unknown feature names, preserving the helpful Mix.Error - ContactLive IndexTest: invalidate monthly_bars cache before assertion so test data is visible - RoverLocationsLive MapTest: invalidate cached points before assertion - StatusLiveTest: add DB cleanup in setup to reduce test interference; parameterize NARR candidate coordinates
74 lines
2.3 KiB
Elixir
74 lines
2.3 KiB
Elixir
defmodule Microwaveprop.CacheTest do
|
|
use ExUnit.Case, async: false
|
|
|
|
alias Microwaveprop.Cache
|
|
|
|
setup do
|
|
Cache.clear()
|
|
:ok
|
|
end
|
|
|
|
describe "fetch_or_store/3" do
|
|
test "invokes the function and returns the value on a cold cache" do
|
|
assert Cache.fetch_or_store(:the_key, 1_000, fn -> 42 end) == 42
|
|
end
|
|
|
|
test "returns the cached value without invoking the function on a warm hit" do
|
|
Cache.fetch_or_store(:the_key, 1_000, fn -> 42 end)
|
|
assert Cache.fetch_or_store(:the_key, 1_000, fn -> raise "should not run" end) == 42
|
|
end
|
|
|
|
test "re-invokes the function once the TTL expires" do
|
|
Cache.put(:the_key, 1, -1)
|
|
assert Cache.fetch_or_store(:the_key, 1_000, fn -> 2 end) == 2
|
|
end
|
|
|
|
test "isolates values by key" do
|
|
assert Cache.fetch_or_store(:a, 1_000, fn -> 1 end) == 1
|
|
assert Cache.fetch_or_store(:b, 1_000, fn -> 2 end) == 2
|
|
assert Cache.fetch_or_store(:a, 1_000, fn -> raise "no" end) == 1
|
|
assert Cache.fetch_or_store(:b, 1_000, fn -> raise "no" end) == 2
|
|
end
|
|
end
|
|
|
|
describe "put/3 and invalidate/1" do
|
|
test "put/3 stores a value with the given TTL" do
|
|
Cache.put(:k, "hello", 1_000)
|
|
assert Cache.fetch_or_store(:k, 1_000, fn -> "other" end) == "hello"
|
|
end
|
|
|
|
test "invalidate/1 removes an entry, forcing the next fetch to recompute" do
|
|
Cache.put(:k, "stale", 60_000)
|
|
Cache.invalidate(:k)
|
|
assert Cache.fetch_or_store(:k, 1_000, fn -> "fresh" end) == "fresh"
|
|
end
|
|
end
|
|
|
|
describe "sweep/0" do
|
|
test "removes expired entries" do
|
|
Cache.put(:expired, "old", -1)
|
|
Cache.sweep()
|
|
|
|
assert :ets.lookup(:microwaveprop_cache, :expired) == []
|
|
end
|
|
|
|
test "leaves unexpired entries in place" do
|
|
Cache.put(:fresh, "new", 60_000)
|
|
|
|
_ = Cache.sweep()
|
|
|
|
assert [{:fresh, "new", _}] = :ets.lookup(:microwaveprop_cache, :fresh)
|
|
end
|
|
|
|
test "handles a mix of expired and unexpired entries" do
|
|
Cache.put(:expired_a, 1, -1)
|
|
Cache.put(:expired_b, 2, -1)
|
|
Cache.put(:fresh, 3, 60_000)
|
|
Cache.sweep()
|
|
|
|
assert :ets.lookup(:microwaveprop_cache, :expired_a) == []
|
|
assert :ets.lookup(:microwaveprop_cache, :expired_b) == []
|
|
assert [{:fresh, 3, _}] = :ets.lookup(:microwaveprop_cache, :fresh)
|
|
end
|
|
end
|
|
end
|