package service import ( "testing" "browser.local/platform/domain" ) func scumDeploymentTestPlugin() domain.GamePlugin { return domain.GamePlugin{ ID: "game.scum", CreateFields: []domain.PluginCreateField{ {Key: "serverName", Type: "text", DefaultValue: "SCUM Test"}, {Key: "gamePort", Type: "port", DefaultValue: "7777"}, {Key: "queryPort", Type: "port", DefaultValue: "27015"}, {Key: "maxPlayers", Type: "number", DefaultValue: "64"}, }, RuntimeProfiles: domain.GamePluginRuntimeProfiles{ServerDeployments: []domain.RuntimeServerDeploymentProfile{{ Key: "scum-steamcmd-windows", Version: "1.0.0", SteamAppID: "3792580", ExecutableKey: "scum/server-executable", InstallRootKey: "server/install-root", ConfigKey: "scum/server-settings", ConfigFormat: "ini", Prerequisites: []domain.RuntimeServerPrerequisite{{Key: "steamcmd", Kind: "steamcmd"}, {Key: "vcredist-2012-x86", Kind: "windows-vcredist"}, {Key: "vcredist-2012-x64", Kind: "windows-vcredist"}, {Key: "vcredist-2013-x86", Kind: "windows-vcredist"}, {Key: "vcredist-2013-x64", Kind: "windows-vcredist"}, {Key: "vcredist-2015-2022-x86", Kind: "windows-vcredist"}, {Key: "vcredist-2015-2022-x64", Kind: "windows-vcredist"}, {Key: "directx-jun2010", Kind: "windows-directx"}}, ConfigMappings: []domain.RuntimeServerConfigMapping{{FieldKey: "serverName", ConfigKey: "server-settings.server-name", ValueType: "text", Required: true}, {FieldKey: "gamePort", ConfigKey: "server-settings.game-port", ValueType: "port", Required: true}, {FieldKey: "queryPort", ConfigKey: "server-settings.query-port", ValueType: "port", Required: true}, {FieldKey: "maxPlayers", ConfigKey: "server-settings.max-players", ValueType: "integer", Required: true}}, VerificationChecks: []domain.RuntimeServerVerificationCheck{{Key: "executable", Kind: "executable.present", TargetKey: "scum/server-executable", Required: true}, {Key: "game-port", Kind: "port.bound", TargetKey: "game-port", Required: true}, {Key: "config", Kind: "config.readable", TargetKey: "scum/server-settings", Required: true}, {Key: "process", Kind: "process.healthy", TargetKey: "scum/server-executable", Required: true}}, }}}, } } func requireManualSCUMRuntimeBindings(plugin *domain.GamePlugin, profileKey string) { plugin.RuntimeProfiles.Discovery = []domain.RuntimeDiscoveryProbe{ {Key: "steamcmd", Kind: "command.version", TargetKey: "steamcmd", Required: true}, {Key: "scum-install", Kind: "file.exists", TargetKey: "server/install-root", Required: true, Platforms: []string{"windows"}}, } plugin.RuntimeProfiles.DependencyProbes = []domain.RuntimeDependencyProbe{{Key: "steamcmd", Kind: "command.version", TargetKey: "steamcmd", Required: true, Platforms: []string{"windows"}}} plugin.RuntimeProfiles.LogSources = []domain.RuntimeLogSource{ {Key: "scum-console-stdout", Kind: "process.stdout", TargetKey: "scum/server-process", StreamKey: "scum.console.stdout", CursorKind: "sequence", RetentionDays: 30}, {Key: "scum-server-events", Kind: "file.tail", TargetKey: "logs/server", StreamKey: "scum.server", CursorKind: "fingerprint", RetentionDays: 90}, } plugin.RuntimeProfiles.TransportProfiles = []domain.RuntimeTransportProfile{{Key: "server-files", Kind: "file", TargetKey: "server-root", Capabilities: []string{domain.JobCapabilityRemoteRunFilesRead, domain.JobCapabilityRemoteRunFilesWrite}}} for i := range plugin.RuntimeProfiles.LifecycleProfiles { if plugin.RuntimeProfiles.LifecycleProfiles[i].Key == profileKey { plugin.RuntimeProfiles.LifecycleProfiles[i].TransportKeys = []string{"server-files"} } } } func TestSCUMDeploymentPlanSeparatesInstallAndAdopt(t *testing.T) { plugin := scumDeploymentTestPlugin() definition := domain.ServerDeploymentDefinition{Mode: domain.ServerDeploymentModeGuided, CreateInputs: map[string]string{"serverName": "Alpha", "gamePort": "7777", "queryPort": "27015", "maxPlayers": "64"}} plan, err := scumDeploymentPlan(plugin, definition, "install") if err != nil || plan == nil || plan.Operation != "install" || plan.SteamAppID != "3792580" { t.Fatalf("unexpected install plan: %+v, %v", plan, err) } definition.Mode = domain.ServerDeploymentModeExisting plan, err = scumDeploymentPlan(plugin, definition, "adopt") if err != nil || plan == nil || plan.Operation != "adopt" { t.Fatalf("unexpected adopt plan: %+v, %v", plan, err) } } func TestSCUMDeploymentEvidenceRequiresAllRequiredChecks(t *testing.T) { plugin := scumDeploymentTestPlugin() plan, err := scumDeploymentPlan(plugin, domain.ServerDeploymentDefinition{Mode: domain.ServerDeploymentModeGuided}, "install") if err != nil { t.Fatal(err) } result := domain.RunJobResult{State: domain.JobStateSucceeded, ExecutionResult: domain.JobExecutionResult{Kind: "scum.install.completed", ServerDeploymentEvidence: &domain.ServerDeploymentEvidence{TemplateKey: plan.TemplateKey, TemplateVersion: plan.TemplateVersion, PreflightState: "passed", DiscoveryState: "passed", MappingState: "applied", VerificationState: "passed", MappingResults: map[string]string{"serverName": "applied", "gamePort": "applied", "queryPort": "applied", "maxPlayers": "applied"}, VerificationResults: map[string]string{"executable": "passed", "version": "passed", "game-port": "passed", "config": "passed", "process": "passed"}}}} if err := validateSCUMDeploymentEvidence(plan, result); err != nil { t.Fatalf("valid evidence rejected: %v", err) } delete(result.ExecutionResult.ServerDeploymentEvidence.VerificationResults, "process") if err := validateSCUMDeploymentEvidence(plan, result); err == nil { t.Fatal("missing required process check was accepted") } } func TestSCUMAdoptionMaySkipConfigurationMappingAfterDiscovery(t *testing.T) { plugin := scumDeploymentTestPlugin() plan, err := scumDeploymentPlan(plugin, domain.ServerDeploymentDefinition{Mode: domain.ServerDeploymentModeExisting, ServerRoot: `C:\\scum`}, "adopt") if err != nil { t.Fatal(err) } result := domain.RunJobResult{State: domain.JobStateSucceeded, ExecutionResult: domain.JobExecutionResult{Kind: "scum.adopt.completed", ServerDeploymentEvidence: &domain.ServerDeploymentEvidence{ TemplateKey: plan.TemplateKey, TemplateVersion: plan.TemplateVersion, PreflightState: "passed", DiscoveryState: "passed", MappingState: "skipped", VerificationState: "passed", VerificationResults: map[string]string{"executable": "passed", "version": "passed", "game-port": "passed", "config": "passed", "process": "passed"}, }}} if err := validateSCUMDeploymentEvidence(plan, result); err != nil { t.Fatalf("valid adoption evidence rejected: %v", err) } } func TestSCUMInstallRequiresDirectoryButAdoptionDoesNotRequireCreateInputs(t *testing.T) { plugin := scumDeploymentTestPlugin() install := domain.ServerDeploymentDefinition{Mode: domain.ServerDeploymentModeGuided, CreateInputs: map[string]string{"serverName": "Alpha"}} if err := validateScumDeploymentInputs(plugin, install); err == nil { t.Fatal("SCUM install without a directory was accepted") } install.ServerRoot = `C:\\scum` if err := validateScumDeploymentInputs(plugin, install); err != nil { t.Fatalf("SCUM install with defaults was rejected: %v", err) } adopt := domain.ServerDeploymentDefinition{Mode: domain.ServerDeploymentModeExisting, ServerRoot: `C:\\scum`} if err := validateScumDeploymentInputs(plugin, adopt); err != nil { t.Fatalf("SCUM adoption without create inputs was rejected: %v", err) } } func TestSCUMDeploymentFailureRequiresStableCode(t *testing.T) { plugin := scumDeploymentTestPlugin() plan, _ := scumDeploymentPlan(plugin, domain.ServerDeploymentDefinition{Mode: domain.ServerDeploymentModeExisting}, "adopt") result := domain.RunJobResult{State: domain.JobStateFailed, ExecutionResult: domain.JobExecutionResult{Kind: "scum.adopt.failed", ServerDeploymentEvidence: &domain.ServerDeploymentEvidence{TemplateKey: plan.TemplateKey, TemplateVersion: plan.TemplateVersion}}} if err := validateSCUMDeploymentEvidence(plan, result); err == nil { t.Fatal("failed deployment without failure code was accepted") } } func TestSCUMDeploymentTargetMustMatchTemplate(t *testing.T) { plugin := scumDeploymentTestPlugin() plugin.RuntimeProfiles.ServerDeployments[0].SupportedTargets = []domain.RuntimeTarget{{OS: "windows", Arch: "amd64"}} definition := domain.ServerDeploymentDefinition{Mode: domain.ServerDeploymentModeGuided} if err := validateSCUMDeploymentTarget(plugin, definition, domain.RunEndpoint{Platform: "linux", Architecture: "amd64"}); err == nil { t.Fatal("linux Run target was accepted for the Windows-only SCUM template") } }