fetch_ml/tests/unit/reproducibility/environment_capture_test.go
Jeremie Fraeys 9f9d75dd68
test(phase-4): reproducibility crossover tests
Implement reproducibility crossover requirements:

- TestManifestEnvironmentCapture: Environment population with ConfigHash and DetectionMethod
- TestConfigHashPostDefaults: Hash computation after env expansion and defaults

Verifies manifest.Environment is properly populated for reproducibility tracking
2026-02-23 20:25:37 -05:00

151 lines
4.3 KiB
Go

package reproducibility
import (
"os"
"path/filepath"
"testing"
"time"
"github.com/jfraeys/fetch_ml/internal/manifest"
"github.com/jfraeys/fetch_ml/internal/worker"
)
// TestManifestEnvironmentCapture verifies that RunManifest.Environment
// is populated on every scan with ConfigHash and DetectionMethod.
func TestManifestEnvironmentCapture(t *testing.T) {
t.Run("EnvironmentPopulatedInManifest", func(t *testing.T) {
// Create config
cfg := &worker.Config{
ComplianceMode: "standard",
MaxWorkers: 4,
GPUVendor: "nvidia",
Sandbox: worker.SandboxConfig{
NetworkMode: "none",
SeccompProfile: "default-hardened",
},
}
// Compute expected config hash
configHash, err := cfg.ComputeResolvedConfigHash()
if err != nil {
t.Fatalf("ComputeResolvedConfigHash failed: %v", err)
}
// Perform GPU detection to get detection method
factory := &worker.GPUDetectorFactory{}
result := factory.CreateDetectorWithInfo(cfg)
detectionMethod := result.Info.DetectionMethod
// Create manifest with environment info
created := time.Now().UTC()
m := manifest.NewRunManifest("run-env-test", "task-env", "job-env", created)
m.Environment = &manifest.ExecutionEnvironment{
ConfigHash: configHash,
GPUDetectionMethod: string(detectionMethod),
GPUCount: 1,
MaxWorkers: cfg.MaxWorkers,
SandboxNetworkMode: cfg.Sandbox.NetworkMode,
SandboxNoNewPrivs: cfg.Sandbox.NoNewPrivileges,
ComplianceMode: cfg.ComplianceMode,
}
// R.1: Environment must be populated
if m.Environment == nil {
t.Fatal("Environment is nil")
}
// R.1: Environment.ConfigHash must be non-empty
if m.Environment.ConfigHash == "" {
t.Error("Environment.ConfigHash is empty")
}
// R.1: Environment.DetectionMethod must be non-empty
if m.Environment.GPUDetectionMethod == "" {
t.Error("Environment.DetectionMethod is empty")
}
// Verify ConfigHash matches expected value
if m.Environment.ConfigHash != configHash {
t.Errorf("ConfigHash mismatch: got %q, want %q", m.Environment.ConfigHash, configHash)
}
// Verify DetectionMethod matches
if m.Environment.GPUDetectionMethod != string(detectionMethod) {
t.Errorf("DetectionMethod mismatch: got %q, want %q", m.Environment.GPUDetectionMethod, detectionMethod)
}
// Scan artifacts
runDir := t.TempDir()
mustWrite := func(rel string, data []byte) {
p := filepath.Join(runDir, rel)
if err := os.MkdirAll(filepath.Dir(p), 0750); err != nil {
t.Fatalf("mkdir: %v", err)
}
if err := os.WriteFile(p, data, 0600); err != nil {
t.Fatalf("write file: %v", err)
}
}
mustWrite("results/metrics.jsonl", []byte("metrics"))
mustWrite("checkpoints/best.pt", []byte("checkpoint"))
caps := &worker.SandboxConfig{
MaxArtifactFiles: 100,
MaxArtifactTotalBytes: 1024 * 1024 * 1024,
}
arts, err := worker.ScanArtifacts(runDir, false, caps)
if err != nil {
t.Fatalf("ScanArtifacts failed: %v", err)
}
// Attach artifacts to manifest
m.Artifacts = arts
// Write and reload manifest
dir := t.TempDir()
if err := m.WriteToDir(dir); err != nil {
t.Fatalf("WriteToDir failed: %v", err)
}
loaded, err := manifest.LoadFromDir(dir)
if err != nil {
t.Fatalf("LoadFromDir failed: %v", err)
}
// Verify environment persisted
if loaded.Environment == nil {
t.Fatal("Environment not persisted in manifest")
}
if loaded.Environment.ConfigHash != configHash {
t.Errorf("loaded ConfigHash = %q, want %q", loaded.Environment.ConfigHash, configHash)
}
if loaded.Environment.GPUDetectionMethod != string(detectionMethod) {
t.Errorf("loaded DetectionMethod = %q, want %q", loaded.Environment.GPUDetectionMethod, detectionMethod)
}
})
t.Run("EnvironmentRequiredFields", func(t *testing.T) {
env := &manifest.ExecutionEnvironment{
ConfigHash: "required-hash",
GPUDetectionMethod: "config",
MaxWorkers: 4,
SandboxNetworkMode: "none",
SandboxNoNewPrivs: true,
}
// Verify required fields
_ = env.MaxWorkers
_ = env.SandboxNoNewPrivs
if env.ConfigHash == "" {
t.Error("ConfigHash is required")
}
if env.GPUDetectionMethod == "" {
t.Error("DetectionMethod is required")
}
if env.SandboxNetworkMode == "" {
t.Error("SandboxNetworkMode is required")
}
})
}