402 lines
11 KiB
Nim
402 lines
11 KiB
Nim
## End-to-End Integration Tests
|
|
##
|
|
## This test suite validates the complete dependency resolution workflow
|
|
## from CLI input to final resolution result, testing all components
|
|
## working together.
|
|
|
|
import unittest
|
|
import tables
|
|
import ../src/nip/resolver/orchestrator
|
|
import ../src/nip/resolver/types
|
|
import ../src/nip/cas/storage
|
|
|
|
suite "End-to-End Resolution Workflow":
|
|
test "Complete resolution workflow":
|
|
# Setup
|
|
let cas = newCASStorage("/tmp/test-e2e-1")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
# Create variant demand
|
|
let demand = VariantDemand(
|
|
useFlags: @["ssl", "http2"],
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @["-O2", "-march=native"]
|
|
)
|
|
|
|
# Resolve dependencies
|
|
let result = orch.resolve("nginx", ">=1.24.0", demand)
|
|
|
|
# Verify success
|
|
check result.isOk
|
|
|
|
let resolution = result.get
|
|
check resolution.resolutionTime >= 0.0
|
|
check resolution.cacheHit == false # First resolution
|
|
|
|
# Verify metrics updated
|
|
let metrics = orch.getMetrics()
|
|
check metrics.totalResolutions == 1
|
|
check metrics.successfulResolutions == 1
|
|
check metrics.cacheMisses == 1
|
|
|
|
test "Cache hit on repeated resolution":
|
|
# Setup
|
|
let cas = newCASStorage("/tmp/test-e2e-2")
|
|
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 (cache miss)
|
|
let result1 = orch.resolve("test-pkg", "*", demand)
|
|
check result1.isOk
|
|
check result1.get.cacheHit == false
|
|
|
|
# Second resolution (cache hit)
|
|
let result2 = orch.resolve("test-pkg", "*", demand)
|
|
check result2.isOk
|
|
check result2.get.cacheHit == true
|
|
|
|
# Verify cache speedup
|
|
check result2.get.resolutionTime < result1.get.resolutionTime
|
|
|
|
# Verify metrics
|
|
let metrics = orch.getMetrics()
|
|
check metrics.totalResolutions == 2
|
|
check metrics.cacheHits == 1
|
|
check metrics.cacheMisses == 1
|
|
|
|
test "Different variants produce different resolutions":
|
|
# Setup
|
|
let cas = newCASStorage("/tmp/test-e2e-3")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
# Resolve with first variant
|
|
let demand1 = VariantDemand(
|
|
useFlags: @["ssl"],
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @[]
|
|
)
|
|
|
|
let result1 = orch.resolve("nginx", "*", demand1)
|
|
check result1.isOk
|
|
|
|
# Resolve with second variant
|
|
let demand2 = VariantDemand(
|
|
useFlags: @["ssl", "http2"], # Different USE flags
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @[]
|
|
)
|
|
|
|
let result2 = orch.resolve("nginx", "*", demand2)
|
|
check result2.isOk
|
|
|
|
# Both should be cache misses (different variants)
|
|
check result1.get.cacheHit == false
|
|
check result2.get.cacheHit == false
|
|
|
|
# Verify metrics
|
|
let metrics = orch.getMetrics()
|
|
check metrics.cacheMisses == 2
|
|
check metrics.cacheHits == 0
|
|
|
|
suite "Cache Invalidation Workflow":
|
|
test "Repository update invalidates cache":
|
|
# Setup
|
|
let cas = newCASStorage("/tmp/test-e2e-4")
|
|
let repos1: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
let orch = newResolutionOrchestrator(cas, repos1, 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 hit)
|
|
let result2 = orch.resolve("test-pkg", "*", demand)
|
|
check result2.isOk
|
|
check result2.get.cacheHit == true
|
|
|
|
# Update repositories (simulates metadata change)
|
|
let repos2 = @[
|
|
Repository(
|
|
name: "main",
|
|
packages: @[]
|
|
)
|
|
]
|
|
orch.updateRepositories(repos2)
|
|
|
|
# Third resolution (cache miss due to invalidation)
|
|
let result3 = orch.resolve("test-pkg", "*", demand)
|
|
check result3.isOk
|
|
check result3.get.cacheHit == false
|
|
|
|
# Verify metrics
|
|
let metrics = orch.getMetrics()
|
|
check metrics.totalResolutions == 3
|
|
check metrics.cacheHits == 1
|
|
check metrics.cacheMisses == 2
|
|
|
|
suite "Configuration Management Workflow":
|
|
test "Disable cache affects behavior":
|
|
# Setup with cache enabled
|
|
let cas = newCASStorage("/tmp/test-e2e-5")
|
|
let repos: seq[Repository] = @[]
|
|
var config = defaultConfig()
|
|
config.enableCache = true
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
let demand = VariantDemand(
|
|
useFlags: @[],
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @[]
|
|
)
|
|
|
|
# First resolution (cache miss)
|
|
let result1 = orch.resolve("test-pkg", "*", demand)
|
|
check result1.isOk
|
|
check result1.get.cacheHit == false
|
|
|
|
# Second resolution (cache hit)
|
|
let result2 = orch.resolve("test-pkg", "*", demand)
|
|
check result2.isOk
|
|
check result2.get.cacheHit == true
|
|
|
|
# Disable cache
|
|
config.enableCache = false
|
|
orch.updateConfig(config)
|
|
|
|
# Third resolution (cache disabled, always miss)
|
|
let result3 = orch.resolve("test-pkg", "*", demand)
|
|
check result3.isOk
|
|
check result3.get.cacheHit == false
|
|
|
|
test "Clear cache resets state":
|
|
# Setup
|
|
let cas = newCASStorage("/tmp/test-e2e-6")
|
|
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
|
|
let result1 = orch.resolve("test-pkg", "*", demand)
|
|
check result1.isOk
|
|
|
|
# Verify cache hit
|
|
let result2 = orch.resolve("test-pkg", "*", demand)
|
|
check result2.get.cacheHit == true
|
|
|
|
# Clear cache
|
|
orch.clearCache()
|
|
|
|
# Verify cache miss
|
|
let result3 = orch.resolve("test-pkg", "*", demand)
|
|
check result3.get.cacheHit == false
|
|
|
|
suite "Metrics Tracking Workflow":
|
|
test "Metrics track complete workflow":
|
|
# Setup
|
|
let cas = newCASStorage("/tmp/test-e2e-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: @[]
|
|
)
|
|
|
|
# Initial metrics
|
|
var metrics = orch.getMetrics()
|
|
check metrics.totalResolutions == 0
|
|
check metrics.cacheHits == 0
|
|
check metrics.cacheMisses == 0
|
|
|
|
# First resolution
|
|
discard orch.resolve("pkg1", "*", demand)
|
|
metrics = orch.getMetrics()
|
|
check metrics.totalResolutions == 1
|
|
check metrics.cacheMisses == 1
|
|
|
|
# Second resolution (same package, cache hit)
|
|
discard orch.resolve("pkg1", "*", demand)
|
|
metrics = orch.getMetrics()
|
|
check metrics.totalResolutions == 2
|
|
check metrics.cacheHits == 1
|
|
|
|
# Third resolution (different package, cache miss)
|
|
discard orch.resolve("pkg2", "*", demand)
|
|
metrics = orch.getMetrics()
|
|
check metrics.totalResolutions == 3
|
|
check metrics.cacheMisses == 2
|
|
|
|
# Verify final state
|
|
check metrics.successfulResolutions == 3
|
|
check metrics.failedResolutions == 0
|
|
|
|
test "Reset metrics clears counters":
|
|
# Setup
|
|
let cas = newCASStorage("/tmp/test-e2e-8")
|
|
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..<5:
|
|
discard orch.resolve(fmt"pkg-{i}", "*", demand)
|
|
|
|
# Verify metrics
|
|
var metrics = orch.getMetrics()
|
|
check metrics.totalResolutions == 5
|
|
|
|
# Reset metrics
|
|
orch.resetMetrics()
|
|
|
|
# Verify reset
|
|
metrics = orch.getMetrics()
|
|
check metrics.totalResolutions == 0
|
|
check metrics.cacheHits == 0
|
|
check metrics.cacheMisses == 0
|
|
|
|
suite "Performance Characteristics":
|
|
test "Cache provides speedup":
|
|
# Setup
|
|
let cas = newCASStorage("/tmp/test-e2e-9")
|
|
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 (cold cache)
|
|
let result1 = orch.resolve("test-pkg", "*", demand)
|
|
check result1.isOk
|
|
let coldTime = result1.get.resolutionTime
|
|
|
|
# Second resolution (warm cache)
|
|
let result2 = orch.resolve("test-pkg", "*", demand)
|
|
check result2.isOk
|
|
let warmTime = result2.get.resolutionTime
|
|
|
|
# Verify speedup
|
|
check warmTime < coldTime
|
|
|
|
let speedup = coldTime / warmTime
|
|
echo fmt" Cache speedup: {speedup:.2f}x"
|
|
echo fmt" Cold: {coldTime * 1000:.2f}ms, Warm: {warmTime * 1000:.2f}ms"
|
|
|
|
test "Multiple resolutions maintain performance":
|
|
# Setup
|
|
let cas = newCASStorage("/tmp/test-e2e-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: @[]
|
|
)
|
|
|
|
# Resolve multiple packages
|
|
var totalTime = 0.0
|
|
for i in 0..<10:
|
|
let result = orch.resolve(fmt"pkg-{i}", "*", demand)
|
|
check result.isOk
|
|
totalTime += result.get.resolutionTime
|
|
|
|
let avgTime = totalTime / 10.0
|
|
echo fmt" Average resolution time: {avgTime * 1000:.2f}ms"
|
|
|
|
# Verify reasonable performance
|
|
check avgTime < 1.0 # Should be < 1 second per resolution
|
|
|
|
suite "Integration with Components":
|
|
test "Orchestrator integrates all components":
|
|
# This test verifies that the orchestrator properly coordinates
|
|
# all resolver components in the correct order
|
|
|
|
let cas = newCASStorage("/tmp/test-e2e-11")
|
|
let repos: seq[Repository] = @[]
|
|
let config = defaultConfig()
|
|
let orch = newResolutionOrchestrator(cas, repos, config)
|
|
|
|
let demand = VariantDemand(
|
|
useFlags: @["ssl", "http2"],
|
|
libc: "musl",
|
|
allocator: "jemalloc",
|
|
targetArch: "x86_64",
|
|
buildFlags: @["-O2"]
|
|
)
|
|
|
|
# Resolve
|
|
let result = orch.resolve("nginx", ">=1.24.0", demand)
|
|
|
|
# Verify all components were invoked
|
|
check result.isOk
|
|
|
|
# Verify resolution result structure
|
|
let resolution = result.get
|
|
check resolution.graph.rootPackage.name == "nginx"
|
|
check resolution.installOrder.len >= 0
|
|
check resolution.resolutionTime >= 0.0
|
|
|
|
# Verify cache was used
|
|
let cacheMetrics = orch.getCacheMetrics()
|
|
check cacheMetrics.l1Capacity == 100
|