133 lines
6.6 KiB
Go
133 lines
6.6 KiB
Go
package validator
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"browser.local/platform/domain"
|
|
)
|
|
|
|
func validGameClientBridgeCompanionManifest() (domain.GameClientBridgeManifest, domain.GamePluginRuntimeProfiles) {
|
|
bridge := domain.GameClientBridgeManifest{
|
|
Retention: domain.GameClientBridgeRetention{KeepForSeconds: 86400, MaxRecords: 1000},
|
|
Companion: domain.GameClientBridgeCompanionDeclaration{
|
|
ProfileKey: "scum-client-manager",
|
|
ConfigTemplateKey: "client-config",
|
|
ConfigSchemaRef: "schemas/companion/config.schema.json",
|
|
ConfigFormat: "yaml",
|
|
PlatformBaseURLSource: "run-control",
|
|
RegistrationProof: "hmac-sha256",
|
|
ProofMaterialSource: "component-package",
|
|
ProofMaterialEnv: "SCUM_COMPONENT_PROOF",
|
|
SessionMode: "component-session",
|
|
TLSPolicy: "verify-system-roots",
|
|
HeartbeatIntervalSeconds: 30,
|
|
CommandPollIntervalSeconds: 5,
|
|
RequestTimeoutSeconds: 15,
|
|
},
|
|
}
|
|
profiles := domain.GamePluginRuntimeProfiles{ClientManagers: []domain.RuntimeClientManagerProfile{{
|
|
Key: "scum-client-manager",
|
|
ConfigTemplates: []domain.RuntimeConfigTemplate{{Key: "client-config", TemplateRef: "config.yaml.example", OutputRef: "config.yaml"}},
|
|
Health: domain.RuntimeClientManagerHealth{IntervalSeconds: 30, RequiredCapabilities: []string{"component.register", "component.heartbeat", "component.health", "game-client.bridge"}},
|
|
}}}
|
|
return bridge, profiles
|
|
}
|
|
|
|
func TestValidateGameClientBridgeCompanionDeclaration(t *testing.T) {
|
|
bridge, profiles := validGameClientBridgeCompanionManifest()
|
|
if violations := validateGameClientBridgeManifest("gameClientBridge", bridge, nil, nil, profiles); len(violations) != 0 {
|
|
t.Fatalf("expected valid companion declaration, got %v", violations)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
expected string
|
|
mutate func(*domain.GameClientBridgeManifest, *domain.GamePluginRuntimeProfiles)
|
|
}{
|
|
{name: "undeclared profile", expected: "profileKey must reference", mutate: func(bridge *domain.GameClientBridgeManifest, _ *domain.GamePluginRuntimeProfiles) {
|
|
bridge.Companion.ProfileKey = "missing"
|
|
}},
|
|
{name: "undeclared template", expected: "configTemplateKey must reference", mutate: func(bridge *domain.GameClientBridgeManifest, _ *domain.GamePluginRuntimeProfiles) {
|
|
bridge.Companion.ConfigTemplateKey = "missing"
|
|
}},
|
|
{name: "unsafe schema", expected: "configSchemaRef", mutate: func(bridge *domain.GameClientBridgeManifest, _ *domain.GamePluginRuntimeProfiles) {
|
|
bridge.Companion.ConfigSchemaRef = "/etc/config.json"
|
|
}},
|
|
{name: "insecure tls", expected: "security policy", mutate: func(bridge *domain.GameClientBridgeManifest, _ *domain.GamePluginRuntimeProfiles) {
|
|
bridge.Companion.TLSPolicy = "skip-verification"
|
|
}},
|
|
{name: "heartbeat mismatch", expected: "must match", mutate: func(bridge *domain.GameClientBridgeManifest, _ *domain.GamePluginRuntimeProfiles) {
|
|
bridge.Companion.HeartbeatIntervalSeconds = 31
|
|
}},
|
|
{name: "missing bridge capability", expected: "game-client.bridge", mutate: func(_ *domain.GameClientBridgeManifest, profiles *domain.GamePluginRuntimeProfiles) {
|
|
profiles.ClientManagers[0].Health.RequiredCapabilities = []string{"component.register", "component.heartbeat", "component.health"}
|
|
}},
|
|
{name: "partial declaration", expected: "profile or config template key", mutate: func(bridge *domain.GameClientBridgeManifest, _ *domain.GamePluginRuntimeProfiles) {
|
|
bridge.Companion.ProfileKey = ""
|
|
}},
|
|
{name: "reserved proof environment", expected: "proofMaterialEnv", mutate: func(bridge *domain.GameClientBridgeManifest, _ *domain.GamePluginRuntimeProfiles) {
|
|
bridge.Companion.ProofMaterialEnv = "LD_PRELOAD"
|
|
}},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
candidateBridge, candidateProfiles := validGameClientBridgeCompanionManifest()
|
|
test.mutate(&candidateBridge, &candidateProfiles)
|
|
violations := validateGameClientBridgeManifest("gameClientBridge", candidateBridge, nil, nil, candidateProfiles)
|
|
if !strings.Contains(strings.Join(violations, "; "), test.expected) {
|
|
t.Fatalf("expected %q violation, got %v", test.expected, violations)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateClientManagerCompanionConfigInputFailsClosed(t *testing.T) {
|
|
valid := domain.ClientManagerCompanionConfigInput{
|
|
SchemaVersion: domain.ClientManagerCompanionConfigSchemaVersion,
|
|
ConfigTemplateKey: "client-config",
|
|
ConfigTemplateRef: "config.yaml.example",
|
|
ConfigOutputRef: "config.yaml",
|
|
ConfigSchemaRef: "schemas/companion/config.schema.json",
|
|
ConfigFormat: "yaml",
|
|
PlatformBaseURLSource: "run-control",
|
|
InstallationID: "installation-1",
|
|
ServerInstanceID: "server-1",
|
|
PluginID: "game.scum",
|
|
ProfileKey: "scum-client-manager",
|
|
ArtifactID: "artifact-1",
|
|
Version: "1.0.0",
|
|
SourceRevision: "revision-1",
|
|
TargetOS: "windows",
|
|
TargetArch: "amd64",
|
|
KeyGeneration: 1,
|
|
DeploymentGeneration: 2,
|
|
Capabilities: []string{"component.register", "component.heartbeat", "component.health", "game-client.bridge"},
|
|
RegistrationProof: "hmac-sha256",
|
|
ProofMaterialSource: "component-package",
|
|
ProofMaterialEnv: "SCUM_COMPONENT_PROOF",
|
|
SessionMode: "component-session",
|
|
TLSPolicy: "verify-system-roots",
|
|
HeartbeatIntervalSeconds: 30,
|
|
CommandPollIntervalSeconds: 5,
|
|
RequestTimeoutSeconds: 15,
|
|
}
|
|
if err := ValidateClientManagerCompanionConfigInput(valid); err != nil {
|
|
t.Fatalf("expected valid companion input, got %v", err)
|
|
}
|
|
for name, mutate := range map[string]func(*domain.ClientManagerCompanionConfigInput){
|
|
"insecure tls": func(value *domain.ClientManagerCompanionConfigInput) { value.TLSPolicy = "skip-verification" },
|
|
"legacy session": func(value *domain.ClientManagerCompanionConfigInput) { value.SessionMode = "shared-token" },
|
|
"reserved env": func(value *domain.ClientManagerCompanionConfigInput) { value.ProofMaterialEnv = "PATH" },
|
|
"unsafe template": func(value *domain.ClientManagerCompanionConfigInput) { value.ConfigTemplateRef = "../config.yaml" },
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
candidate := domain.CopyClientManagerCompanionConfigInput(valid)
|
|
mutate(&candidate)
|
|
if err := ValidateClientManagerCompanionConfigInput(candidate); err == nil {
|
|
t.Fatalf("expected invalid companion input: %+v", candidate)
|
|
}
|
|
})
|
|
}
|
|
}
|