## Unit Tests for Source Selection Strategy ## ## Tests for the source selection logic that chooses the best ## source adapter based on strategy (PreferBinary, PreferSource, Balanced). import std/[unittest, options, strutils] import ../src/nip/resolver/source_adapter import ../src/nip/resolver/frozen_adapter import ../src/nip/resolver/flexible_adapter import ../src/nip/resolver/variant_types suite "Source Selection Strategy Tests": test "PreferBinary: Chooses frozen when available": ## Test that PreferBinary strategy prefers frozen sources # Create frozen adapter with package let frozenAdapter = newFrozenAdapter("nix", priority = 50) var profile = newVariantProfile() profile.addFlag("optimization", "lto") profile.calculateHash() let frozenMetadata = PackageMetadata( name: "nginx", version: "1.24.0", availableVariants: @[profile], sourceHash: "nix-store-hash", buildTime: 0 ) frozenAdapter.addPackage(frozenMetadata) # Create flexible adapter with same package let flexibleAdapter = newFlexibleAdapter("gentoo", priority = 30) let flexibleMetadata = PackageMetadata( name: "nginx", version: "1.24.0", availableVariants: @[profile], sourceHash: "https://nginx.org/nginx-1.24.0.tar.gz", buildTime: 300 ) flexibleAdapter.addPackage(flexibleMetadata) # Create demand let demand = VariantDemand( packageName: "nginx", variantProfile: profile, optional: false ) # Select source with PreferBinary strategy let adapters = @[SourceAdapter(frozenAdapter), SourceAdapter(flexibleAdapter)] let selection = selectSource(adapters, demand, PreferBinary) check selection.isSome check selection.get.adapter.name == "nix" check selection.get.adapter.class == Frozen check selection.get.estimatedTime == 0 check "binary" in selection.get.reason.toLowerAscii() test "PreferBinary: Falls back to flexible when no frozen available": ## Test that PreferBinary falls back to flexible sources # Create only flexible adapter let flexibleAdapter = newFlexibleAdapter("gentoo", priority = 30) var profile = newVariantProfile() profile.addFlag("optimization", "lto") profile.calculateHash() let metadata = PackageMetadata( name: "nginx", version: "1.24.0", availableVariants: @[profile], sourceHash: "https://nginx.org/nginx-1.24.0.tar.gz", buildTime: 300 ) flexibleAdapter.addPackage(metadata) let demand = VariantDemand( packageName: "nginx", variantProfile: profile, optional: false ) let adapters = @[SourceAdapter(flexibleAdapter)] let selection = selectSource(adapters, demand, PreferBinary) check selection.isSome check selection.get.adapter.name == "gentoo" check selection.get.adapter.class == Flexible check selection.get.estimatedTime == 300 check "source" in selection.get.reason.toLowerAscii() test "PreferSource: Always chooses flexible when available": ## Test that PreferSource strategy prefers flexible sources # Create both frozen and flexible adapters let frozenAdapter = newFrozenAdapter("nix", priority = 50) var profile = newVariantProfile() profile.addFlag("optimization", "lto") profile.calculateHash() let frozenMetadata = PackageMetadata( name: "nginx", version: "1.24.0", availableVariants: @[profile], sourceHash: "nix-store-hash", buildTime: 0 ) frozenAdapter.addPackage(frozenMetadata) let flexibleAdapter = newFlexibleAdapter("gentoo", priority = 30) let flexibleMetadata = PackageMetadata( name: "nginx", version: "1.24.0", availableVariants: @[profile], sourceHash: "https://nginx.org/nginx-1.24.0.tar.gz", buildTime: 300 ) flexibleAdapter.addPackage(flexibleMetadata) let demand = VariantDemand( packageName: "nginx", variantProfile: profile, optional: false ) # Select with PreferSource strategy let adapters = @[SourceAdapter(frozenAdapter), SourceAdapter(flexibleAdapter)] let selection = selectSource(adapters, demand, PreferSource) check selection.isSome check selection.get.adapter.name == "gentoo" check selection.get.adapter.class == Flexible check selection.get.estimatedTime == 300 check "source" in selection.get.reason.toLowerAscii() test "PreferSource: Falls back to frozen when no flexible available": ## Test that PreferSource falls back to frozen sources # Create only frozen adapter let frozenAdapter = newFrozenAdapter("nix", priority = 50) var profile = newVariantProfile() profile.addFlag("optimization", "lto") profile.calculateHash() let metadata = PackageMetadata( name: "nginx", version: "1.24.0", availableVariants: @[profile], sourceHash: "nix-store-hash", buildTime: 0 ) frozenAdapter.addPackage(metadata) let demand = VariantDemand( packageName: "nginx", variantProfile: profile, optional: false ) let adapters = @[SourceAdapter(frozenAdapter)] let selection = selectSource(adapters, demand, PreferSource) check selection.isSome check selection.get.adapter.name == "nix" check selection.get.adapter.class == Frozen check selection.get.estimatedTime == 0 test "Balanced: Considers priority": ## Test that Balanced strategy respects adapter priority # Create two frozen adapters with different priorities let highPriorityAdapter = newFrozenAdapter("nix", priority = 100) let lowPriorityAdapter = newFrozenAdapter("arch", priority = 50) var profile = newVariantProfile() profile.addFlag("optimization", "lto") profile.calculateHash() let metadata = PackageMetadata( name: "nginx", version: "1.24.0", availableVariants: @[profile], sourceHash: "hash", buildTime: 0 ) highPriorityAdapter.addPackage(metadata) lowPriorityAdapter.addPackage(metadata) let demand = VariantDemand( packageName: "nginx", variantProfile: profile, optional: false ) # Select with Balanced strategy let adapters = @[SourceAdapter(lowPriorityAdapter), SourceAdapter(highPriorityAdapter)] let selection = selectSource(adapters, demand, Balanced) check selection.isSome check selection.get.adapter.name == "nix" check selection.get.adapter.priority == 100 test "No source available returns None": ## Test that selectSource returns None when no source can satisfy let frozenAdapter = newFrozenAdapter("nix", priority = 50) var profile = newVariantProfile() profile.addFlag("optimization", "lto") profile.calculateHash() # Don't add any packages to adapter let demand = VariantDemand( packageName: "nonexistent", variantProfile: profile, optional: false ) let adapters = @[SourceAdapter(frozenAdapter)] let selection = selectSource(adapters, demand, PreferBinary) check selection.isNone test "Empty adapter list returns None": ## Test that selectSource handles empty adapter list var profile = newVariantProfile() profile.addFlag("optimization", "lto") profile.calculateHash() let demand = VariantDemand( packageName: "nginx", variantProfile: profile, optional: false ) let adapters: seq[SourceAdapter] = @[] let selection = selectSource(adapters, demand, PreferBinary) check selection.isNone test "Multiple adapters with same priority": ## Test behavior when multiple adapters have same priority let adapter1 = newFrozenAdapter("nix", priority = 50) let adapter2 = newFrozenAdapter("arch", priority = 50) var profile = newVariantProfile() profile.addFlag("optimization", "lto") profile.calculateHash() let metadata = PackageMetadata( name: "nginx", version: "1.24.0", availableVariants: @[profile], sourceHash: "hash", buildTime: 0 ) adapter1.addPackage(metadata) adapter2.addPackage(metadata) let demand = VariantDemand( packageName: "nginx", variantProfile: profile, optional: false ) # With Balanced strategy, should pick one deterministically let adapters = @[SourceAdapter(adapter1), SourceAdapter(adapter2)] let selection = selectSource(adapters, demand, Balanced) check selection.isSome # Should pick one of them (deterministic based on order) check selection.get.adapter.name in ["nix", "arch"] test "Frozen adapter with wrong variant is skipped": ## Test that frozen adapters with wrong variants are not selected let frozenAdapter = newFrozenAdapter("nix", priority = 50) var availableProfile = newVariantProfile() availableProfile.addFlag("optimization", "lto") availableProfile.calculateHash() let metadata = PackageMetadata( name: "nginx", version: "1.24.0", availableVariants: @[availableProfile], sourceHash: "hash", buildTime: 0 ) frozenAdapter.addPackage(metadata) # Request different variant var demandProfile = newVariantProfile() demandProfile.addFlag("optimization", "pgo") demandProfile.calculateHash() let demand = VariantDemand( packageName: "nginx", variantProfile: demandProfile, optional: false ) let adapters = @[SourceAdapter(frozenAdapter)] let selection = selectSource(adapters, demand, PreferBinary) # Frozen adapter can't satisfy wrong variant check selection.isNone test "Flexible adapter accepts any variant": ## Test that flexible adapters can satisfy any variant let flexibleAdapter = newFlexibleAdapter("gentoo", priority = 30) var availableProfile = newVariantProfile() availableProfile.addFlag("optimization", "lto") availableProfile.calculateHash() let metadata = PackageMetadata( name: "nginx", version: "1.24.0", availableVariants: @[availableProfile], sourceHash: "https://nginx.org/nginx-1.24.0.tar.gz", buildTime: 300 ) flexibleAdapter.addPackage(metadata) # Request different variant var demandProfile = newVariantProfile() demandProfile.addFlag("optimization", "pgo") demandProfile.addFlag("graphics", "wayland") demandProfile.calculateHash() let demand = VariantDemand( packageName: "nginx", variantProfile: demandProfile, optional: false ) let adapters = @[SourceAdapter(flexibleAdapter)] let selection = selectSource(adapters, demand, PreferBinary) # Flexible adapter can satisfy any variant check selection.isSome check selection.get.adapter.name == "gentoo" check selection.get.estimatedTime == 300