fetch_ml/tests/unit/reproducibility/config_hash_test.go
Jeremie Fraeys 2bd7f97ae2
test(integration,unit): update test suites for new features and APIs
Integration test updates:
- jupyter_experiment_test.go: update for new workspace handling
- run_manifest_test.go: reproducibility manifest validation
- secrets_integration_test.go: KMS and secret provider tests
- storage_redis_integration_test.go: Redis-backed storage tests

Unit test updates:
- response_helpers_test.go: API response helper tests
- config_hash_test.go: configuration hashing for reproducibility
- filetype_test.go: security file type detection tests

Load testing:
- load_test.go: scheduler load and stress tests
2026-03-12 12:09:15 -04:00

171 lines
4.4 KiB
Go

package reproducibility
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")
}
})
}