notex.nvim/tests/unit/utils/test_cache.lua

324 lines
9.6 KiB
Lua
Raw Normal View History

2025-10-05 20:16:33 -04:00
-- Unit tests for caching utilities
local cache = require('notex.utils.cache')
describe("cache utilities", function()
before_each(function()
-- Reset cache before each test
cache.init({
memory = {enabled = true, max_size = 10},
lru = {enabled = true, max_size = 5},
timed = {enabled = true, default_ttl = 1}
})
end)
after_each(function()
cache.cleanup()
end)
describe("memory cache", function()
it("should store and retrieve values", function()
local ok = cache.memory_set("test_key", "test_value")
assert.is_true(ok)
local value = cache.memory_get("test_key")
assert.equals("test_value", value)
end)
it("should return nil for non-existent keys", function()
local value = cache.memory_get("non_existent")
assert.is_nil(value)
end)
it("should handle cache size limits", function()
-- Fill cache beyond max size
for i = 1, 15 do
cache.memory_set("key_" .. i, "value_" .. i)
end
-- Should still be able to set new values (eviction happens)
local ok = cache.memory_set("new_key", "new_value")
assert.is_true(ok)
-- Should still be able to get some values
local value = cache.memory_get("new_key")
assert.equals("new_value", value)
end)
end)
describe("LRU cache", function()
it("should store and retrieve values with LRU eviction", function()
cache.lru_set("key1", "value1")
cache.lru_set("key2", "value2")
cache.lru_set("key3", "value3")
-- Access key1 to make it most recently used
local value = cache.lru_get("key1")
assert.equals("value1", value)
-- Add more items to trigger eviction
cache.lru_set("key4", "value4")
cache.lru_set("key5", "value5")
cache.lru_set("key6", "value6") -- Should evict key2 (least recently used)
-- key2 should be evicted, key1 should still exist
assert.is_nil(cache.lru_get("key2"))
assert.equals("value1", cache.lru_get("key1"))
end)
it("should update access order on get", function()
cache.lru_set("key1", "value1")
cache.lru_set("key2", "value2")
-- Get key1 to make it most recently used
cache.lru_get("key1")
-- Fill cache to evict
cache.lru_set("key3", "value3")
cache.lru_set("key4", "value4")
cache.lru_set("key5", "value4")
cache.lru_set("key6", "value4") -- Should evict key2
assert.is_not_nil(cache.lru_get("key1")) -- Should still exist
assert.is_nil(cache.lru_get("key2")) -- Should be evicted
end)
end)
describe("timed cache", function()
it("should store values with TTL", function()
cache.timed_set("test_key", "test_value", 2) -- 2 second TTL
local value = cache.timed_get("test_key")
assert.equals("test_value", value)
end)
it("should expire values after TTL", function()
cache.timed_set("test_key", "test_value", 1) -- 1 second TTL
-- Should be available immediately
local value = cache.timed_get("test_key")
assert.equals("test_value", value)
-- Wait for expiration
vim.defer_fn(function()
value = cache.timed_get("test_key")
assert.is_nil(value)
end, 1100)
end)
it("should use default TTL when not specified", function()
cache.timed_set("test_key", "test_value")
local value = cache.timed_get("test_key")
assert.equals("test_value", value)
end)
end)
describe("generic cache operations", function()
it("should set and get with specified cache type", function()
local ok = cache.set("test_key", "test_value", "memory")
assert.is_true(ok)
local value = cache.get("test_key", "memory")
assert.equals("test_value", value)
end)
it("should default to memory cache", function()
local ok = cache.set("test_key", "test_value")
assert.is_true(ok)
local value = cache.get("test_key")
assert.equals("test_value", value)
end)
it("should handle unknown cache types", function()
local ok = cache.set("test_key", "test_value", "unknown")
assert.is_false(ok)
local value = cache.get("test_key", "unknown")
assert.is_nil(value)
end)
end)
describe("get_or_set", function()
it("should return cached value when exists", function()
cache.set("test_key", "cached_value")
local value = cache.get_or_set("test_key", function()
return "computed_value"
end)
assert.equals("cached_value", value)
end)
it("should compute and cache value when not exists", function()
local call_count = 0
local value = cache.get_or_set("test_key", function()
call_count = call_count + 1
return "computed_value"
end)
assert.equals("computed_value", value)
assert.equals(1, call_count)
-- Second call should use cached value
value = cache.get_or_set("test_key", function()
call_count = call_count + 1
return "new_value"
end)
assert.equals("computed_value", value)
assert.equals(1, call_count) -- Should not be called again
end)
it("should handle computation errors", function()
assert.has_error(function()
cache.get_or_set("test_key", function()
error("Computation failed")
end)
end)
end)
end)
describe("multi_get", function()
it("should try multiple cache types in order", function()
cache.set("test_key", "memory_value", "memory")
cache.set("test_key", "lru_value", "lru")
local value, cache_type = cache.multi_get("test_key", {"lru", "memory"})
assert.equals("lru_value", value)
assert.equals("lru", cache_type)
-- Should try memory if lru doesn't have it
cache.invalidate("test_key", "lru")
value, cache_type = cache.multi_get("test_key", {"lru", "memory"})
assert.equals("memory_value", value)
assert.equals("memory", cache_type)
end)
it("should return nil when not found in any cache", function()
local value = cache.multi_get("non_existent", {"memory", "lru", "timed"})
assert.is_nil(value)
end)
it("should use default cache types when not specified", function()
cache.set("test_key", "memory_value", "memory")
local value = cache.multi_get("test_key")
assert.equals("memory_value", value)
end)
end)
describe("invalidate", function()
it("should invalidate from specific cache type", function()
cache.set("test_key", "test_value", "memory")
cache.set("test_key", "test_value", "lru")
cache.invalidate("test_key", "memory")
assert.is_nil(cache.get("test_key", "memory"))
assert.equals("test_value", cache.get("test_key", "lru"))
end)
it("should invalidate from all caches when type not specified", function()
cache.set("test_key", "test_value", "memory")
cache.set("test_key", "test_value", "lru")
cache.set("test_key", "test_value", "timed")
cache.invalidate("test_key")
assert.is_nil(cache.get("test_key", "memory"))
assert.is_nil(cache.get("test_key", "lru"))
assert.is_nil(cache.get("test_key", "timed"))
end)
end)
describe("clear_all", function()
it("should clear all caches", function()
cache.set("key1", "value1", "memory")
cache.set("key2", "value2", "lru")
cache.set("key3", "value3", "timed")
cache.clear_all()
assert.is_nil(cache.get("key1", "memory"))
assert.is_nil(cache.get("key2", "lru"))
assert.is_nil(cache.get("key3", "timed"))
end)
it("should reset metrics", function()
-- Generate some metrics
cache.set("test", "value")
cache.get("test")
cache.get("non_existent")
local stats_before = cache.get_stats()
assert.is_true(stats_before.metrics.hits > 0)
assert.is_true(stats_before.metrics.misses > 0)
cache.clear_all()
local stats_after = cache.get_stats()
assert.equals(0, stats_after.metrics.hits)
assert.equals(0, stats_after.metrics.misses)
end)
end)
describe("get_stats", function()
it("should return comprehensive statistics", function()
-- Generate some activity
cache.set("test1", "value1")
cache.set("test2", "value2")
cache.get("test1")
cache.get("non_existent")
local stats = cache.get_stats()
assert.is_not_nil(stats.metrics)
assert.is_not_nil(stats.sizes)
assert.is_not_nil(stats.config)
assert.is_true(stats.metrics.sets >= 2)
assert.is_true(stats.metrics.hits >= 1)
assert.is_true(stats.metrics.misses >= 1)
assert.is_not_nil(stats.metrics.hit_ratio)
end)
it("should calculate hit ratio correctly", function()
-- Generate known metrics
cache.set("test", "value")
cache.get("test") -- hit
cache.get("test") -- hit
cache.get("non_existent") -- miss
local stats = cache.get_stats()
-- Should be 2 hits out of 3 total requests = ~0.67
assert.is_true(stats.metrics.hit_ratio > 0.6)
assert.is_true(stats.metrics.hit_ratio < 0.7)
end)
end)
describe("configuration", function()
it("should initialize with custom configuration", function()
cache.init({
memory = {enabled = false},
lru = {enabled = true, max_size = 20},
timed = {enabled = true, default_ttl = 10}
})
-- Memory cache should be disabled
local ok = cache.memory_set("test", "value")
assert.is_false(ok)
-- LRU cache should work with new size
ok = cache.lru_set("test", "value")
assert.is_true(ok)
-- Timed cache should work with new TTL
cache.timed_set("test2", "value")
local value = cache.timed_get("test2")
assert.equals("value", value)
end)
end)
end)