package tracking_test import ( "os" "testing" "github.com/jfraeys/fetch_ml/internal/worker" ) // TestConfigHashPostDefaults verifies that config hash is computed after // defaults and env expansion, not from raw file content. func TestConfigHashPostDefaults(t *testing.T) { t.Run("HashAfterEnvExpansion", func(t *testing.T) { // Set env var t.Setenv("TEST_MAX_WORKERS", "8") // Create config that will use env expansion cfg := &worker.Config{ Host: "localhost", Port: 22, MaxWorkers: 4, // Base value GPUVendor: "nvidia", Sandbox: worker.SandboxConfig{ NetworkMode: "none", SeccompProfile: "default-hardened", }, } // Compute hash before env expansion hashBefore, err := cfg.ComputeResolvedConfigHash() if err != nil { t.Fatalf("ComputeResolvedConfigHash failed: %v", err) } // Simulate env override (as would happen during config loading) if v := os.Getenv("TEST_MAX_WORKERS"); v != "" { // In real config loading, this would parse and apply // For test, we manually apply to simulate cfg.MaxWorkers = 8 } // Compute hash after env expansion hashAfter, err := cfg.ComputeResolvedConfigHash() if err != nil { t.Fatalf("ComputeResolvedConfigHash failed: %v", err) } // Hashes should be different after env expansion changes config if hashBefore == hashAfter { t.Error("hash should change after env expansion modifies config") } }) t.Run("HashAfterDefaultsApplied", func(t *testing.T) { // Config with minimal settings (relies on defaults) cfg1 := &worker.Config{ Host: "localhost", // MaxWorkers not set - will use default GPUVendor: "nvidia", Sandbox: worker.SandboxConfig{ NetworkMode: "none", SeccompProfile: "default-hardened", }, } // Config with explicit same values cfg2 := &worker.Config{ Host: "localhost", MaxWorkers: 4, // Assuming 4 is default GPUVendor: "nvidia", Sandbox: worker.SandboxConfig{ NetworkMode: "none", SeccompProfile: "default-hardened", }, } hash1, err := cfg1.ComputeResolvedConfigHash() if err != nil { t.Fatalf("ComputeResolvedConfigHash failed: %v", err) } hash2, err := cfg2.ComputeResolvedConfigHash() if err != nil { t.Fatalf("ComputeResolvedConfigHash failed: %v", err) } // These will differ because cfg1 has MaxWorkers=0, cfg2 has MaxWorkers=4 // This is expected - the test documents current behavior t.Logf("Hash with zero MaxWorkers: %s", hash1) t.Logf("Hash with explicit MaxWorkers=4: %s", hash2) if hash1 == hash2 { t.Log("Note: hashes match - defaults may be applied during hash computation") } }) t.Run("SameResolvedConfigSameHash", func(t *testing.T) { // Two configs that resolve to the same values should have same hash cfg1 := &worker.Config{ Host: "localhost", Port: 22, MaxWorkers: 4, GPUVendor: "nvidia", Sandbox: worker.SandboxConfig{ NetworkMode: "none", SeccompProfile: "default-hardened", NoNewPrivileges: true, }, ComplianceMode: "standard", } cfg2 := &worker.Config{ Host: "localhost", Port: 22, MaxWorkers: 4, GPUVendor: "nvidia", Sandbox: worker.SandboxConfig{ NetworkMode: "none", SeccompProfile: "default-hardened", NoNewPrivileges: true, }, ComplianceMode: "standard", } hash1, err := cfg1.ComputeResolvedConfigHash() if err != nil { t.Fatalf("ComputeResolvedConfigHash failed: %v", err) } hash2, err := cfg2.ComputeResolvedConfigHash() if err != nil { t.Fatalf("ComputeResolvedConfigHash failed: %v", err) } // Same resolved config should produce same hash if hash1 != hash2 { t.Errorf("identical configs should produce identical hashes: %s vs %s", hash1, hash2) } }) t.Run("DifferentResolvedConfigDifferentHash", func(t *testing.T) { cfg1 := &worker.Config{ Host: "localhost", Port: 22, MaxWorkers: 4, GPUVendor: "nvidia", } cfg2 := &worker.Config{ Host: "localhost", Port: 22, MaxWorkers: 8, // Different GPUVendor: "nvidia", } hash1, err := cfg1.ComputeResolvedConfigHash() if err != nil { t.Fatalf("ComputeResolvedConfigHash failed: %v", err) } hash2, err := cfg2.ComputeResolvedConfigHash() if err != nil { t.Fatalf("ComputeResolvedConfigHash failed: %v", err) } // Different configs should produce different hashes if hash1 == hash2 { t.Error("different configs should produce different hashes") } }) }