324 lines
No EOL
9.6 KiB
Lua
324 lines
No EOL
9.6 KiB
Lua
-- 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) |