package validator import ( "strings" "testing" "browser.local/platform/domain" ) func TestValidateGamePluginRuntimeProfilesAcceptsReadyUE4SSDLLExtension(t *testing.T) { profiles := validRuntimeDLLExtensionProfiles() if err := ValidateGamePluginRuntimeProfiles(profiles); err != nil { t.Fatalf("expected ready DLL extension profile to validate: %v", err) } } func TestValidateGamePluginRuntimeProfilesRejectsUnsafeOrUnpublishedDLLExtension(t *testing.T) { cases := []struct { name string mutate func(*domain.GamePluginRuntimeProfiles) want string }{ {name: "linux target", mutate: func(profiles *domain.GamePluginRuntimeProfiles) { profiles.DLLExtensions[0].SupportedTargets = []domain.RuntimeTarget{{OS: "linux", Arch: "amd64"}} }, want: "windows/amd64"}, {name: "unsafe URL", mutate: func(profiles *domain.GamePluginRuntimeProfiles) { profiles.DLLExtensions[0].ReleaseURL = "https://127.0.0.1/plugin.dll" }, want: "public credential-free HTTPS DLL URL"}, {name: "query URL", mutate: func(profiles *domain.GamePluginRuntimeProfiles) { profiles.DLLExtensions[0].ReleaseURL = "https://cdn.npc0.com/plugin.dll?release=1" }, want: "public credential-free HTTPS DLL URL"}, {name: "unsafe path", mutate: func(profiles *domain.GamePluginRuntimeProfiles) { profiles.DLLExtensions[0].DLLRef = "ue4ss/Mods/scum_simple_rcon/dlls/plugin.exe" }, want: "main.dll path"}, {name: "missing pin", mutate: func(profiles *domain.GamePluginRuntimeProfiles) { profiles.DLLExtensions[0].Checksum = "" }, want: "SHA-256"}, {name: "unpublished reference", mutate: func(profiles *domain.GamePluginRuntimeProfiles) { profiles.DLLExtensions[0].ReleaseState = "unpublished" }, want: "unpublished DLL extension"}, } for _, testCase := range cases { t.Run(testCase.name, func(t *testing.T) { profiles := validRuntimeDLLExtensionProfiles() testCase.mutate(&profiles) err := ValidateGamePluginRuntimeProfiles(profiles) if err == nil || !strings.Contains(err.Error(), testCase.want) { t.Fatalf("expected %q validation error, got %v", testCase.want, err) } }) } } func TestValidateJobRejectsDLLPlanOutsideProcessStart(t *testing.T) { profiles := validRuntimeDLLExtensionProfiles() extension := profiles.DLLExtensions[0] job := domain.Job{ID: "dll-job", ServerInstanceID: "server-1", RunEndpointID: "run-1", Capability: domain.LifecycleCapabilityStop, IdempotencyKey: "dll-stop", State: domain.JobStateQueued, RetryPolicy: domain.JobRetryPolicy{MaxAttempts: 1, InitialBackoffSeconds: 1, MaxBackoffSeconds: 1}, ExecutionInput: domain.JobExecutionInput{LifecycleOperation: "stop", DLLExtensions: []domain.RuntimeDLLExtensionPlan{{Key: extension.Key, Version: extension.Version, ReleaseURL: extension.ReleaseURL, Checksum: extension.Checksum, SizeBytes: extension.SizeBytes, TargetKey: extension.TargetKey, ModKey: extension.ModKey, DLLRef: extension.DLLRef, SCUMExecutableChecksum: extension.SCUMExecutableChecksum, UE4SSABI: extension.UE4SSABI, RCONPort: extension.RCONPort}}}} if err := ValidateJob(job); err == nil || !strings.Contains(err.Error(), "process.start") { t.Fatalf("expected process.start plan restriction, got %v", err) } } func validRuntimeDLLExtensionProfiles() domain.GamePluginRuntimeProfiles { return domain.GamePluginRuntimeProfiles{ LifecycleProfiles: []domain.RuntimeLifecycleProfile{{Key: "run-local", Mode: "local-process", Capabilities: []string{domain.LifecycleCapabilityStart}, Platforms: []string{"windows"}, DLLExtensionRefs: []string{"scum-simple-rcon"}}}, DLLExtensions: []domain.RuntimeDLLExtensionProfile{{ Key: "scum-simple-rcon", DisplayName: "SCUM Simple RCON", Kind: "ue4ss-dll", Activation: "server-start", Version: "0.1.0", ReleaseState: "ready", ReleaseURL: "https://cdn.npc0.com/scum_simple_rcon_ue4s.dll", Checksum: "sha256:" + strings.Repeat("a", 64), SizeBytes: 1024, TargetKey: "ue4ss/scum-simple-rcon", ModKey: "scum_simple_rcon", DLLRef: "ue4ss/Mods/scum_simple_rcon/dlls/main.dll", SCUMExecutableChecksum: "sha256:" + strings.Repeat("b", 64), UE4SSABI: "ue4ss-3.0", SupportedTargets: []domain.RuntimeTarget{{OS: "windows", Arch: "amd64"}}, UpdateOnStart: true, RCONPort: 27015, }}, } }