456 lines
12 KiB
Nim
456 lines
12 KiB
Nim
## Tests for Resolution Orchestrator
|
|
##
|
|
## This test suite verifies the orchestrator's ability to coordinate
|
|
## all resolver components and provide a unified resolution interface.
|
|
|
|
import unittest
|
|
import tables
|
|
import ../src/nip/resolver/orchestrator
|
|
import ../src/nip/resolver/types
|
|
import ../src/nip/cas/storage
|
|
|
|
suite "Orchestrator Construction":
|
|
test "Create with default config":
|
|
let cas = newCASStorage("/tmp/test-orch-1")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
check orch.getConfig().enableCache == true
|
|
check orch.getConfig().enableParallel == false
|
|
check orch.getConfig().maxRetries == 3
|
|
check orch.getConfig().l1CacheCapacity == 100
|
|
|
|
test "Create with custom config":
|
|
let cas = newCASStorage("/tmp/test-orch-2")
|
|
let repos: seq[Repository] = @[]
|
|
let config = ResolverConfig(
|
|
enableCache: false,
|
|
enableParallel: true,
|
|
maxRetries: 5,
|
|
timeout: initDuration(seconds = 600),
|
|
l1CacheCapacity: 200
|
|
)
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
check orch.getConfig().enableCache == false
|
|
check orch.getConfig().enableParallel == true
|
|
check orch.getConfig().maxRetries == 5
|
|
check orch.getConfig().l1CacheCapacity == 200
|
|
|
|
suite "Basic Resolution":
|
|
test "Resolve simple package":
|
|
let cas = newCASStorage("/tmp/test-orch-3")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
let result = orch.resolve(
|
|
"test-pkg",
|
|
"*",
|
|
VariantDemand(
|
|
useFlags: @[],
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @[]
|
|
)
|
|
)
|
|
|
|
check result.isOk
|
|
check result.get.cacheHit == false
|
|
check result.get.resolutionTime >= 0.0
|
|
|
|
test "Resolve with version constraint":
|
|
let cas = newCASStorage("/tmp/test-orch-4")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
let result = orch.resolve(
|
|
"nginx",
|
|
">=1.24.0",
|
|
VariantDemand(
|
|
useFlags: @["ssl", "http2"],
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @[]
|
|
)
|
|
)
|
|
|
|
check result.isOk
|
|
|
|
test "Resolve with USE flags":
|
|
let cas = newCASStorage("/tmp/test-orch-5")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
let result = orch.resolve(
|
|
"firefox",
|
|
"*",
|
|
VariantDemand(
|
|
useFlags: @["wayland", "pulseaudio", "alsa"],
|
|
libc: "glibc",
|
|
allocator: "default",
|
|
targetArch: "x86_64",
|
|
buildFlags: @["-O3", "-march=native"]
|
|
)
|
|
)
|
|
|
|
check result.isOk
|
|
|
|
suite "Caching Behavior":
|
|
test "Cache miss on first resolution":
|
|
let cas = newCASStorage("/tmp/test-orch-6")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
let result = orch.resolve(
|
|
"test-pkg",
|
|
"*",
|
|
VariantDemand(
|
|
useFlags: @[],
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @[]
|
|
)
|
|
)
|
|
|
|
check result.isOk
|
|
check result.get.cacheHit == false
|
|
|
|
let metrics = orch.getMetrics()
|
|
check metrics.cacheMisses == 1
|
|
check metrics.cacheHits == 0
|
|
|
|
test "Cache hit on second resolution":
|
|
let cas = newCASStorage("/tmp/test-orch-7")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
let demand = VariantDemand(
|
|
useFlags: @[],
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @[]
|
|
)
|
|
|
|
# First resolution
|
|
let result1 = orch.resolve("test-pkg", "*", demand)
|
|
check result1.isOk
|
|
check result1.get.cacheHit == false
|
|
|
|
# Second resolution (should hit cache)
|
|
let result2 = orch.resolve("test-pkg", "*", demand)
|
|
check result2.isOk
|
|
check result2.get.cacheHit == true
|
|
|
|
let metrics = orch.getMetrics()
|
|
check metrics.cacheHits == 1
|
|
check metrics.cacheMisses == 1
|
|
|
|
test "Different variants produce different cache keys":
|
|
let cas = newCASStorage("/tmp/test-orch-8")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
let demand1 = VariantDemand(
|
|
useFlags: @["ssl"],
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @[]
|
|
)
|
|
|
|
let demand2 = VariantDemand(
|
|
useFlags: @["ssl", "http2"], # Different USE flags
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @[]
|
|
)
|
|
|
|
# Resolve with first variant
|
|
let result1 = orch.resolve("nginx", "*", demand1)
|
|
check result1.isOk
|
|
check result1.get.cacheHit == false
|
|
|
|
# Resolve with second variant (different cache key)
|
|
let result2 = orch.resolve("nginx", "*", demand2)
|
|
check result2.isOk
|
|
check result2.get.cacheHit == false
|
|
|
|
let metrics = orch.getMetrics()
|
|
check metrics.cacheMisses == 2
|
|
check metrics.cacheHits == 0
|
|
|
|
test "Disabled cache always misses":
|
|
let cas = newCASStorage("/tmp/test-orch-9")
|
|
let repos: seq[Repository] = @[]
|
|
let config = ResolverConfig(
|
|
enableCache: false,
|
|
enableParallel: false,
|
|
maxRetries: 3,
|
|
timeout: initDuration(seconds = 300),
|
|
l1CacheCapacity: 100
|
|
)
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
let demand = VariantDemand(
|
|
useFlags: @[],
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @[]
|
|
)
|
|
|
|
# First resolution
|
|
let result1 = orch.resolve("test-pkg", "*", demand)
|
|
check result1.isOk
|
|
check result1.get.cacheHit == false
|
|
|
|
# Second resolution (cache disabled, should still miss)
|
|
let result2 = orch.resolve("test-pkg", "*", demand)
|
|
check result2.isOk
|
|
check result2.get.cacheHit == false
|
|
|
|
suite "Metrics Tracking":
|
|
test "Track total resolutions":
|
|
let cas = newCASStorage("/tmp/test-orch-10")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
let demand = VariantDemand(
|
|
useFlags: @[],
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @[]
|
|
)
|
|
|
|
for i in 0..<5:
|
|
discard orch.resolve(fmt"pkg-{i}", "*", demand)
|
|
|
|
let metrics = orch.getMetrics()
|
|
check metrics.totalResolutions == 5
|
|
check metrics.successfulResolutions == 5
|
|
|
|
test "Track cache hit rate":
|
|
let cas = newCASStorage("/tmp/test-orch-11")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
let demand = VariantDemand(
|
|
useFlags: @[],
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @[]
|
|
)
|
|
|
|
# First resolution (miss)
|
|
discard orch.resolve("test-pkg", "*", demand)
|
|
|
|
# Three more resolutions (hits)
|
|
for i in 0..<3:
|
|
discard orch.resolve("test-pkg", "*", demand)
|
|
|
|
let metrics = orch.getMetrics()
|
|
check metrics.totalResolutions == 4
|
|
check metrics.cacheHits == 3
|
|
check metrics.cacheMisses == 1
|
|
|
|
test "Reset metrics":
|
|
let cas = newCASStorage("/tmp/test-orch-12")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
let demand = VariantDemand(
|
|
useFlags: @[],
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @[]
|
|
)
|
|
|
|
# Do some resolutions
|
|
for i in 0..<3:
|
|
discard orch.resolve(fmt"pkg-{i}", "*", demand)
|
|
|
|
check orch.getMetrics().totalResolutions == 3
|
|
|
|
# Reset metrics
|
|
orch.resetMetrics()
|
|
|
|
check orch.getMetrics().totalResolutions == 0
|
|
check orch.getMetrics().cacheHits == 0
|
|
check orch.getMetrics().cacheMisses == 0
|
|
|
|
suite "Configuration Management":
|
|
test "Update configuration":
|
|
let cas = newCASStorage("/tmp/test-orch-13")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
check orch.getConfig().enableCache == true
|
|
|
|
var newConfig = config
|
|
newConfig.enableCache = false
|
|
|
|
orch.updateConfig(newConfig)
|
|
|
|
check orch.getConfig().enableCache == false
|
|
|
|
test "Configuration affects behavior":
|
|
let cas = newCASStorage("/tmp/test-orch-14")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
let demand = VariantDemand(
|
|
useFlags: @[],
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @[]
|
|
)
|
|
|
|
# Resolve with cache enabled
|
|
let result1 = orch.resolve("test-pkg", "*", demand)
|
|
check result1.isOk
|
|
|
|
let result2 = orch.resolve("test-pkg", "*", demand)
|
|
check result2.get.cacheHit == true
|
|
|
|
# Disable cache
|
|
var newConfig = orch.getConfig()
|
|
newConfig.enableCache = false
|
|
orch.updateConfig(newConfig)
|
|
|
|
# Resolve with cache disabled
|
|
let result3 = orch.resolve("test-pkg", "*", demand)
|
|
check result3.get.cacheHit == false
|
|
|
|
suite "Cache Management":
|
|
test "Clear cache":
|
|
let cas = newCASStorage("/tmp/test-orch-15")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
let demand = VariantDemand(
|
|
useFlags: @[],
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @[]
|
|
)
|
|
|
|
# Populate cache
|
|
discard orch.resolve("test-pkg", "*", demand)
|
|
|
|
# Verify cache hit
|
|
let result1 = orch.resolve("test-pkg", "*", demand)
|
|
check result1.get.cacheHit == true
|
|
|
|
# Clear cache
|
|
orch.clearCache()
|
|
|
|
# Verify cache miss
|
|
let result2 = orch.resolve("test-pkg", "*", demand)
|
|
check result2.get.cacheHit == false
|
|
|
|
test "Get cache metrics":
|
|
let cas = newCASStorage("/tmp/test-orch-16")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
let cacheMetrics = orch.getCacheMetrics()
|
|
check cacheMetrics.l1Size == 0
|
|
check cacheMetrics.l1Capacity == 100
|
|
|
|
suite "Repository Management":
|
|
test "Update repositories":
|
|
let cas = newCASStorage("/tmp/test-orch-17")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
check orch.getRepositories().len == 0
|
|
|
|
let newRepos = @[
|
|
Repository(
|
|
name: "main",
|
|
packages: @[]
|
|
)
|
|
]
|
|
|
|
orch.updateRepositories(newRepos)
|
|
|
|
check orch.getRepositories().len == 1
|
|
check orch.getRepositories()[0].name == "main"
|
|
|
|
test "Repository update invalidates cache":
|
|
let cas = newCASStorage("/tmp/test-orch-18")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
let demand = VariantDemand(
|
|
useFlags: @[],
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @[]
|
|
)
|
|
|
|
# Populate cache
|
|
discard orch.resolve("test-pkg", "*", demand)
|
|
|
|
# Verify cache hit
|
|
let result1 = orch.resolve("test-pkg", "*", demand)
|
|
check result1.get.cacheHit == true
|
|
|
|
# Update repositories (should invalidate cache)
|
|
let newRepos = @[
|
|
Repository(
|
|
name: "main",
|
|
packages: @[]
|
|
)
|
|
]
|
|
orch.updateRepositories(newRepos)
|
|
|
|
# Verify cache miss (invalidated)
|
|
let result2 = orch.resolve("test-pkg", "*", demand)
|
|
check result2.get.cacheHit == false
|