Implement property-based invariant verification: - TestPropertyConfigHashAlwaysPresent: Valid configs produce non-empty hash - TestPropertyConfigHashDeterministic: Same config produces same hash - TestPropertyDetectionSourceAlwaysValid: CreateDetectorWithInfo returns valid source - TestPropertyProvenanceFailClosed: Strict mode fails on incomplete env - TestPropertyScanArtifactsNeverNilEnvironment: Artifacts can hold Environment - TestPropertyManifestEnvironmentSurvivesRoundtrip: Environment survives write/load Uses gopter for property-based testing with deterministic seeds
118 lines
3.2 KiB
Go
118 lines
3.2 KiB
Go
package property
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/jfraeys/fetch_ml/internal/worker"
|
|
"github.com/leanovate/gopter"
|
|
"github.com/leanovate/gopter/gen"
|
|
"github.com/leanovate/gopter/prop"
|
|
)
|
|
|
|
// TestPropertyConfigHashAlwaysPresent verifies that any valid config passing Validate()
|
|
// produces a non-empty hash. This is a property-based test using gopter.
|
|
func TestPropertyConfigHashAlwaysPresent(t *testing.T) {
|
|
parameters := gopter.DefaultTestParameters()
|
|
parameters.Rng.Seed(12345) // Deterministic seed for reproducibility
|
|
|
|
properties := gopter.NewProperties(parameters)
|
|
|
|
// Property: valid config always has non-empty hash
|
|
properties.Property("valid config always produces non-empty hash", prop.ForAll(
|
|
func(complianceMode string, maxWorkers, port int, host string) bool {
|
|
// Generate a valid config
|
|
cfg := &worker.Config{
|
|
Host: host,
|
|
Port: port,
|
|
MaxWorkers: maxWorkers,
|
|
ComplianceMode: complianceMode,
|
|
GPUVendor: "none",
|
|
Sandbox: worker.SandboxConfig{
|
|
NetworkMode: "none",
|
|
SeccompProfile: "default-hardened",
|
|
NoNewPrivileges: true,
|
|
},
|
|
}
|
|
|
|
// Apply security defaults
|
|
cfg.Sandbox.ApplySecurityDefaults()
|
|
|
|
// Skip invalid configs (validation would fail)
|
|
if err := cfg.Validate(); err != nil {
|
|
// Invalid configs are okay for this property - we only care about valid ones
|
|
return true
|
|
}
|
|
|
|
// Compute hash
|
|
hash, err := cfg.ComputeResolvedConfigHash()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
// Property: hash must be non-empty for valid configs
|
|
return hash != ""
|
|
},
|
|
// Generators for inputs
|
|
gen.OneConstOf("hipaa", "standard", ""),
|
|
gen.IntRange(1, 100),
|
|
gen.IntRange(1, 65535),
|
|
gen.OneConstOf("localhost", "127.0.0.1", "0.0.0.0"),
|
|
))
|
|
|
|
properties.TestingRun(t)
|
|
}
|
|
|
|
// TestPropertyConfigHashDeterministic verifies that the same config always
|
|
// produces the same hash (deterministic property).
|
|
func TestPropertyConfigHashDeterministic(t *testing.T) {
|
|
parameters := gopter.DefaultTestParameters()
|
|
parameters.Rng.Seed(12345)
|
|
|
|
properties := gopter.NewProperties(parameters)
|
|
|
|
properties.Property("same config produces same hash", prop.ForAll(
|
|
func(host string, port, maxWorkers int, gpuVendor string) bool {
|
|
// Create two identical configs
|
|
cfg1 := &worker.Config{
|
|
Host: host,
|
|
Port: port,
|
|
MaxWorkers: maxWorkers,
|
|
GPUVendor: gpuVendor,
|
|
Sandbox: worker.SandboxConfig{
|
|
NetworkMode: "none",
|
|
SeccompProfile: "default-hardened",
|
|
},
|
|
}
|
|
cfg1.Sandbox.ApplySecurityDefaults()
|
|
|
|
cfg2 := &worker.Config{
|
|
Host: host,
|
|
Port: port,
|
|
MaxWorkers: maxWorkers,
|
|
GPUVendor: gpuVendor,
|
|
Sandbox: worker.SandboxConfig{
|
|
NetworkMode: "none",
|
|
SeccompProfile: "default-hardened",
|
|
},
|
|
}
|
|
cfg2.Sandbox.ApplySecurityDefaults()
|
|
|
|
// Compute hashes
|
|
hash1, err1 := cfg1.ComputeResolvedConfigHash()
|
|
hash2, err2 := cfg2.ComputeResolvedConfigHash()
|
|
|
|
if err1 != nil || err2 != nil {
|
|
return false
|
|
}
|
|
|
|
// Property: identical configs must have identical hashes
|
|
return hash1 == hash2
|
|
},
|
|
gen.OneConstOf("localhost", "127.0.0.1"),
|
|
gen.IntRange(22, 8080),
|
|
gen.IntRange(1, 32),
|
|
gen.OneConstOf("nvidia", "amd", "none"),
|
|
))
|
|
|
|
properties.TestingRun(t)
|
|
}
|