nip/tests/test_resolution_cache.nim

564 lines
14 KiB
Nim

## Tests for Resolution Cache with CAS Integration
##
## This test suite verifies:
## - L1 (in-memory) cache operations
## - L2 (CAS) cache operations
## - Cache invalidation on repo state changes
## - Cache metrics and hit rates
## - Disabled cache behavior
import unittest
import options
import tables
import ../src/nip/resolver/resolution_cache
import ../src/nip/resolver/types
import ../src/nip/cas/storage
suite "Resolution Cache Construction":
test "Create cache with default settings":
let cas = newCASStorage("/tmp/test-cas-1")
let cache = newResolutionCache(cas)
check cache.isEnabled
check cache.l1Capacity == 100
test "Create cache with custom capacity":
let cas = newCASStorage("/tmp/test-cas-2")
let cache = newResolutionCache(cas, l1Capacity = 50)
check cache.l1Capacity == 50
test "Create disabled cache":
let cas = newCASStorage("/tmp/test-cas-3")
let cache = newResolutionCache(cas, enabled = false)
check not cache.isEnabled
suite "L1 Cache Operations":
test "Cache miss on empty cache":
let cas = newCASStorage("/tmp/test-cas-4")
let cache = newResolutionCache(cas)
let key = CacheKey(
rootPackage: "nginx",
rootConstraint: ">=1.24.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
let result = cache.get(key)
check result.value.isNone
check result.source == CacheMiss
test "Put and get from L1 cache":
let cas = newCASStorage("/tmp/test-cas-5")
let cache = newResolutionCache(cas)
let key = CacheKey(
rootPackage: "nginx",
rootConstraint: ">=1.24.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
let graph = DependencyGraph(
rootPackage: PackageId(name: "nginx", version: "1.24.0", variant: "default"),
nodes: @[],
timestamp: 1700000000
)
cache.put(key, graph)
let result = cache.get(key)
check result.value.isSome
check result.source == L1Hit
check result.value.get.rootPackage.name == "nginx"
test "Multiple entries in L1 cache":
let cas = newCASStorage("/tmp/test-cas-6")
let cache = newResolutionCache(cas)
let key1 = CacheKey(
rootPackage: "nginx",
rootConstraint: ">=1.24.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
let key2 = CacheKey(
rootPackage: "apache",
rootConstraint: ">=2.4.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
let graph1 = DependencyGraph(
rootPackage: PackageId(name: "nginx", version: "1.24.0", variant: "default"),
nodes: @[],
timestamp: 1700000000
)
let graph2 = DependencyGraph(
rootPackage: PackageId(name: "apache", version: "2.4.0", variant: "default"),
nodes: @[],
timestamp: 1700000000
)
cache.put(key1, graph1)
cache.put(key2, graph2)
let result1 = cache.get(key1)
let result2 = cache.get(key2)
check result1.value.isSome
check result1.source == L1Hit
check result1.value.get.rootPackage.name == "nginx"
check result2.value.isSome
check result2.source == L1Hit
check result2.value.get.rootPackage.name == "apache"
test "Different variant demands produce different cache keys":
let cas = newCASStorage("/tmp/test-cas-7")
let cache = newResolutionCache(cas)
let key1 = CacheKey(
rootPackage: "nginx",
rootConstraint: ">=1.24.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @["ssl"],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
let key2 = CacheKey(
rootPackage: "nginx",
rootConstraint: ">=1.24.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @["ssl", "http2"], # Different USE flags
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
let graph1 = DependencyGraph(
rootPackage: PackageId(name: "nginx", version: "1.24.0", variant: "ssl"),
nodes: @[],
timestamp: 1700000000
)
let graph2 = DependencyGraph(
rootPackage: PackageId(name: "nginx", version: "1.24.0", variant: "ssl-http2"),
nodes: @[],
timestamp: 1700000000
)
cache.put(key1, graph1)
cache.put(key2, graph2)
let result1 = cache.get(key1)
let result2 = cache.get(key2)
check result1.value.get.rootPackage.variant == "ssl"
check result2.value.get.rootPackage.variant == "ssl-http2"
suite "Cache Invalidation":
test "Invalidate specific entry":
let cas = newCASStorage("/tmp/test-cas-8")
let cache = newResolutionCache(cas)
let key = CacheKey(
rootPackage: "nginx",
rootConstraint: ">=1.24.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
let graph = DependencyGraph(
rootPackage: PackageId(name: "nginx", version: "1.24.0", variant: "default"),
nodes: @[],
timestamp: 1700000000
)
cache.put(key, graph)
check cache.get(key).value.isSome
cache.invalidate(key)
check cache.get(key).value.isNone
test "Clear all L1 entries":
let cas = newCASStorage("/tmp/test-cas-9")
let cache = newResolutionCache(cas)
let key1 = CacheKey(
rootPackage: "nginx",
rootConstraint: ">=1.24.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
let key2 = CacheKey(
rootPackage: "apache",
rootConstraint: ">=2.4.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
let graph1 = DependencyGraph(
rootPackage: PackageId(name: "nginx", version: "1.24.0", variant: "default"),
nodes: @[],
timestamp: 1700000000
)
let graph2 = DependencyGraph(
rootPackage: PackageId(name: "apache", version: "2.4.0", variant: "default"),
nodes: @[],
timestamp: 1700000000
)
cache.put(key1, graph1)
cache.put(key2, graph2)
cache.clear()
check cache.get(key1).value.isNone
check cache.get(key2).value.isNone
test "Update repo hash invalidates cache":
let cas = newCASStorage("/tmp/test-cas-10")
let cache = newResolutionCache(cas)
let key = CacheKey(
rootPackage: "nginx",
rootConstraint: ">=1.24.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
let graph = DependencyGraph(
rootPackage: PackageId(name: "nginx", version: "1.24.0", variant: "default"),
nodes: @[],
timestamp: 1700000000
)
cache.updateRepoHash("hash123")
cache.put(key, graph)
check cache.get(key).value.isSome
# Update repo hash (simulates metadata change)
cache.updateRepoHash("hash456")
# Cache should be invalidated
check cache.get(key).value.isNone
test "Same repo hash doesn't invalidate cache":
let cas = newCASStorage("/tmp/test-cas-11")
let cache = newResolutionCache(cas)
let key = CacheKey(
rootPackage: "nginx",
rootConstraint: ">=1.24.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
let graph = DependencyGraph(
rootPackage: PackageId(name: "nginx", version: "1.24.0", variant: "default"),
nodes: @[],
timestamp: 1700000000
)
cache.updateRepoHash("hash123")
cache.put(key, graph)
check cache.get(key).value.isSome
# Update with same hash
cache.updateRepoHash("hash123")
# Cache should still be valid
check cache.get(key).value.isSome
suite "Disabled Cache Behavior":
test "Disabled cache returns miss":
let cas = newCASStorage("/tmp/test-cas-12")
let cache = newResolutionCache(cas, enabled = false)
let key = CacheKey(
rootPackage: "nginx",
rootConstraint: ">=1.24.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
let graph = DependencyGraph(
rootPackage: PackageId(name: "nginx", version: "1.24.0", variant: "default"),
nodes: @[],
timestamp: 1700000000
)
cache.put(key, graph)
let result = cache.get(key)
check result.value.isNone
check result.source == CacheMiss
test "Enable and disable cache":
let cas = newCASStorage("/tmp/test-cas-13")
let cache = newResolutionCache(cas, enabled = true)
let key = CacheKey(
rootPackage: "nginx",
rootConstraint: ">=1.24.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
let graph = DependencyGraph(
rootPackage: PackageId(name: "nginx", version: "1.24.0", variant: "default"),
nodes: @[],
timestamp: 1700000000
)
# Enabled: should cache
cache.put(key, graph)
check cache.get(key).value.isSome
# Disable: should return miss
cache.setEnabled(false)
check cache.get(key).value.isNone
# Re-enable: should still have cached value
cache.setEnabled(true)
check cache.get(key).value.isSome
suite "Cache Metrics":
test "Track L1 hits":
let cas = newCASStorage("/tmp/test-cas-14")
let cache = newResolutionCache(cas)
let key = CacheKey(
rootPackage: "nginx",
rootConstraint: ">=1.24.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
let graph = DependencyGraph(
rootPackage: PackageId(name: "nginx", version: "1.24.0", variant: "default"),
nodes: @[],
timestamp: 1700000000
)
cache.put(key, graph)
discard cache.get(key) # Hit
discard cache.get(key) # Hit
let metrics = cache.getMetrics()
check metrics.l1Hits == 2
check metrics.misses == 0
test "Track cache misses":
let cas = newCASStorage("/tmp/test-cas-15")
let cache = newResolutionCache(cas)
let key1 = CacheKey(
rootPackage: "nginx",
rootConstraint: ">=1.24.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
let key2 = CacheKey(
rootPackage: "apache",
rootConstraint: ">=2.4.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
discard cache.get(key1) # Miss
discard cache.get(key2) # Miss
let metrics = cache.getMetrics()
check metrics.l1Hits == 0
check metrics.misses == 2
test "Calculate hit rate":
let cas = newCASStorage("/tmp/test-cas-16")
let cache = newResolutionCache(cas)
let key = CacheKey(
rootPackage: "nginx",
rootConstraint: ">=1.24.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
let graph = DependencyGraph(
rootPackage: PackageId(name: "nginx", version: "1.24.0", variant: "default"),
nodes: @[],
timestamp: 1700000000
)
cache.put(key, graph)
discard cache.get(key) # Hit
discard cache.get(key) # Hit
discard cache.get(key) # Hit
let key2 = CacheKey(
rootPackage: "apache",
rootConstraint: ">=2.4.0",
repoStateHash: "hash123",
variantDemand: VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
discard cache.get(key2) # Miss
let metrics = cache.getMetrics()
check metrics.l1HitRate == 0.75 # 3 hits / 4 total
suite "Convenience Methods":
test "getCached with individual parameters":
let cas = newCASStorage("/tmp/test-cas-17")
let cache = newResolutionCache(cas)
let graph = DependencyGraph(
rootPackage: PackageId(name: "nginx", version: "1.24.0", variant: "default"),
nodes: @[],
timestamp: 1700000000
)
cache.putCached(
"nginx",
">=1.24.0",
"hash123",
VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
),
graph
)
let result = cache.getCached(
"nginx",
">=1.24.0",
"hash123",
VariantDemand(
useFlags: @[],
libc: "musl",
allocator: "jemalloc",
targetArch: "x86_64",
buildFlags: @[]
)
)
check result.value.isSome
check result.source == L1Hit