package security import ( "bytes" "log/slog" "strings" "testing" "github.com/jfraeys/fetch_ml/internal/worker" ) // TestGPUDetectionAudit verifies that GPU detection method is logged at startup // for audit and reproducibility purposes. func TestGPUDetectionAudit(t *testing.T) { tests := []struct { name string gpuType string wantMethod string }{ { name: "nvidia detection logs method", gpuType: "nvidia", wantMethod: "config", }, { name: "apple detection logs method", gpuType: "apple", wantMethod: "config", }, { name: "none detection logs method", gpuType: "none", wantMethod: "config", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Capture log output var logBuf bytes.Buffer handler := slog.NewTextHandler(&logBuf, &slog.HandlerOptions{ Level: slog.LevelInfo, }) logger := slog.New(handler) // Create config with specified GPU type cfg := &worker.Config{ GPUVendor: tt.gpuType, } // Perform GPU detection factory := &worker.GPUDetectorFactory{} result := factory.CreateDetectorWithInfo(cfg) // Log the detection info (this simulates startup logging) logger.Info("GPU detection completed", "gpu_type", result.Info.GPUType, "detection_method", result.Info.DetectionMethod, "configured_vendor", result.Info.ConfiguredVendor, ) // Verify log output contains detection method logOutput := logBuf.String() if !strings.Contains(logOutput, "GPU detection completed") { t.Error("expected 'GPU detection completed' in log output") } if !strings.Contains(logOutput, string(result.Info.DetectionMethod)) { t.Errorf("expected detection method %q in log output", result.Info.DetectionMethod) } if !strings.Contains(logOutput, "detection_method=") { t.Error("expected 'detection_method=' field in log output") } }) } t.Run("env override detection logged", func(t *testing.T) { // Set env var to trigger env-based detection t.Setenv("FETCH_ML_GPU_TYPE", "nvidia") var logBuf bytes.Buffer handler := slog.NewTextHandler(&logBuf, &slog.HandlerOptions{ Level: slog.LevelInfo, }) logger := slog.New(handler) // Create factory and detect factory := &worker.GPUDetectorFactory{} result := factory.CreateDetectorWithInfo(nil) // Log detection logger.Info("GPU detection completed", "gpu_type", result.Info.GPUType, "detection_method", result.Info.DetectionMethod, "env_override_type", result.Info.EnvOverrideType, ) logOutput := logBuf.String() // Should log env override if !strings.Contains(logOutput, "env_override_type=nvidia") { t.Error("expected env override type in log output") } // Detection method should indicate env was used if result.Info.DetectionMethod != worker.DetectionSourceEnvType { t.Errorf("detection method = %v, want %v", result.Info.DetectionMethod, worker.DetectionSourceEnvType) } }) t.Run("detection info fields populated", func(t *testing.T) { // Set both env vars t.Setenv("FETCH_ML_GPU_TYPE", "apple") t.Setenv("FETCH_ML_GPU_COUNT", "4") factory := &worker.GPUDetectorFactory{} result := factory.CreateDetectorWithInfo(nil) // Verify all expected fields are populated if result.Info.GPUType == "" { t.Error("GPUType field is empty") } if result.Info.ConfiguredVendor == "" { t.Error("ConfiguredVendor field is empty") } if result.Info.DetectionMethod == "" { t.Error("DetectionMethod field is empty") } if result.Info.EnvOverrideType != "apple" { t.Errorf("EnvOverrideType = %v, want 'apple'", result.Info.EnvOverrideType) } if result.Info.EnvOverrideCount != 4 { t.Errorf("EnvOverrideCount = %v, want 4", result.Info.EnvOverrideCount) } // Verify detection source is valid validSources := []worker.DetectionSource{ worker.DetectionSourceEnvType, worker.DetectionSourceEnvCount, worker.DetectionSourceEnvBoth, worker.DetectionSourceConfig, worker.DetectionSourceAuto, } found := false for _, source := range validSources { if result.Info.DetectionMethod == source { found = true break } } if !found { t.Errorf("invalid detection method: %v", result.Info.DetectionMethod) } }) } // TestGPUDetectionStructuredLogging verifies structured logging format for audit trails func TestGPUDetectionStructuredLogging(t *testing.T) { var logBuf bytes.Buffer handler := slog.NewJSONHandler(&logBuf, &slog.HandlerOptions{ Level: slog.LevelInfo, }) logger := slog.New(handler) // Simulate startup detection cfg := &worker.Config{GPUVendor: "nvidia"} factory := &worker.GPUDetectorFactory{} result := factory.CreateDetectorWithInfo(cfg) // Log with structured format for audit logger.Info("gpu_detection_startup", "event_type", "gpu_detection", "gpu_type", result.Info.GPUType, "detection_method", result.Info.DetectionMethod, "configured_vendor", result.Info.ConfiguredVendor, ) logOutput := logBuf.String() // Verify JSON structure contains required fields if !strings.Contains(logOutput, `"event_type":"gpu_detection"`) { t.Error("expected event_type field in JSON log") } if !strings.Contains(logOutput, `"detection_method"`) { t.Error("expected detection_method field in JSON log") } if !strings.Contains(logOutput, `"configured_vendor"`) { t.Error("expected configured_vendor field in JSON log") } } // TestGPUDetectionAuditDisabled verifies behavior when detection fails func TestGPUDetectionAuditDisabled(t *testing.T) { var logBuf bytes.Buffer handler := slog.NewTextHandler(&logBuf, &slog.HandlerOptions{ Level: slog.LevelInfo, }) logger := slog.New(handler) // Test with GPU none - should still log cfg := &worker.Config{GPUVendor: "none"} factory := &worker.GPUDetectorFactory{} result := factory.CreateDetectorWithInfo(cfg) logger.Info("GPU detection completed", "gpu_type", result.Info.GPUType, "detection_method", result.Info.DetectionMethod, ) logOutput := logBuf.String() // Should still log even when GPU is none if !strings.Contains(logOutput, "GPU detection completed") { t.Error("expected log output even for GPU type 'none'") } // Should indicate config-based detection if result.Info.DetectionMethod != worker.DetectionSourceConfig { t.Errorf("detection method = %v, want %v", result.Info.DetectionMethod, worker.DetectionSourceConfig) } }