Implement 4 prerequisite test requirements: - TestConfigIntegrityVerification: Config signing, tamper detection, hash stability - TestManifestFilenameNonce: Cryptographic nonce generation and filename patterns - TestGPUDetectionAudit: Structured logging of GPU detection at startup - TestResourceEnvVarParsing: Resource env var parsing and override behavior Also update manifest run_manifest.go: - Add nonce-based filename support to WriteToDir - Add nonce-based file detection to LoadFromDir
220 lines
6.3 KiB
Go
220 lines
6.3 KiB
Go
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)
|
|
}
|
|
}
|