Complete platform management workflows

This commit is contained in:
npc0-hue
2026-07-14 16:39:37 +08:00
parent 7e05d0a4e7
commit 4f33f761a3
106 changed files with 11313 additions and 460 deletions
+100 -1
View File
@@ -876,6 +876,105 @@ func TestCoreServiceAuthorizesPluginBridgeActions(t *testing.T) {
}
}
func TestCoreServiceRemoteAccessRequiresPluginDeclaration(t *testing.T) {
svc := newTestCoreService()
registration := validPluginManifestRegistration()
registration.Manifest.Capabilities = append(registration.Manifest.Capabilities,
domain.JobCapabilityRemoteRunDBSQLiteQuery,
domain.JobCapabilityRemoteRunLogsTransfer,
domain.JobCapabilityRemoteRunRCONCommand,
)
registration.Manifest.Permissions = append(registration.Manifest.Permissions, "server.remote.access")
registration.Manifest.Bridge.Actions = append(registration.Manifest.Bridge.Actions, string(domain.PluginBridgeActionRemoteAccessRequest))
registration.Manifest.Pages = append(registration.Manifest.Pages, domain.GamePluginPage{
Key: "remote",
Title: "Remote",
Path: "/remote",
Permissions: []string{"server.remote.access"},
BridgeActions: []string{string(domain.PluginBridgeActionRemoteAccessRequest)},
})
registration.Manifest.RemoteAccess = domain.GamePluginRemoteAccess{
Methods: []string{"run"},
RunCapabilities: []string{domain.JobCapabilityRemoteRunDBSQLiteQuery, domain.JobCapabilityRemoteRunLogsTransfer, domain.JobCapabilityRemoteRunRCONCommand},
DatabaseEngines: []string{"sqlite"},
RCON: true,
LogTransfer: true,
}
plugin, err := svc.RegisterGamePluginManifest(registration)
if err != nil {
t.Fatalf("register remote manifest: %v", err)
}
if !plugin.Permissions.RemoteAccess || !plugin.RemoteAccess.RCON || plugin.RemoteAccess.DatabaseEngines[0] != "sqlite" {
t.Fatalf("expected remote access metadata from manifest, got %+v", plugin)
}
marketplace, err := svc.GetMarketplacePlugin(plugin.ID)
if err != nil || !marketplace.RemoteAccess.LogTransfer || marketplace.RemoteAccess.Methods[0] != "run" {
t.Fatalf("expected marketplace remote access projection, got %+v err=%v", marketplace, err)
}
endpoint, err := svc.CreateRunEndpoint(domain.RunEndpoint{
ID: "run-remote",
DisplayName: "Remote Run",
Version: "0.1.0",
Capabilities: append([]string{"process.install", "process.start", "process.stop", "logs.read", "files.read", "artifacts.read", "ai.invoke"}, plugin.RemoteAccess.RunCapabilities...),
Capacity: domain.RunCapacity{MaxJobs: 2},
})
if err != nil {
t.Fatalf("create remote endpoint: %v", err)
}
ownerSession := createServiceUserAndLogin(t, svc, domain.User{
ID: "user-remote-owner",
DisplayName: "Remote Owner",
Email: "remote-owner@example.test",
Roles: []string{"server-owner"},
PasswordHash: "secret-password",
})
instance, err := svc.CreateServerInstanceForSession(ownerSession, domain.ServerInstance{
ID: "server-remote",
PluginID: plugin.ID,
RunEndpointID: endpoint.ID,
Name: "Remote Server",
State: domain.ServerInstanceStateRunning,
})
if err != nil {
t.Fatalf("create remote server: %v", err)
}
queued, err := svc.ExecutePluginBridgeAction(ownerSession, domain.PluginBridgeExecuteRequest{
RequestID: "remote-rcon-1",
PluginID: plugin.ID,
RouteKey: "remote",
ServerInstanceID: instance.ID,
Action: domain.PluginBridgeActionRemoteAccessRequest,
Payload: map[string]string{
"capability": domain.JobCapabilityRemoteRunRCONCommand,
"targetKey": "rcon/command",
"inputRef": "input://server-remote/rcon/command/1",
"idempotencyKey": "idem-remote-rcon",
},
})
if err != nil {
t.Fatalf("execute remote bridge action: %v", err)
}
if queued.Status != "queued" || queued.Result["capability"] != domain.JobCapabilityRemoteRunRCONCommand {
t.Fatalf("expected queued remote bridge job, got %+v", queued)
}
plainPlugin, plainEndpoint := createPluginAndRunEndpoint(t, svc)
plainEndpoint.Capabilities = append(plainEndpoint.Capabilities, domain.JobCapabilityRemoteRunRCONCommand)
if err := svc.store.RunEndpoints().Update(plainEndpoint); err != nil {
t.Fatalf("extend plain endpoint: %v", err)
}
plainInstance, err := svc.CreateServerInstance(domain.ServerInstance{ID: "server-plain", PluginID: plainPlugin.ID, RunEndpointID: plainEndpoint.ID, Name: "Plain Server"})
if err != nil {
t.Fatalf("create plain server: %v", err)
}
_, err = svc.CreateJob(domain.Job{ID: "job-remote-denied", ServerInstanceID: plainInstance.ID, RunEndpointID: plainEndpoint.ID, Capability: domain.JobCapabilityRemoteRunRCONCommand, TargetKey: "rcon/command", InputRef: "input://plain/rcon/command/1", IdempotencyKey: "idem-denied"})
if err == nil || !strings.Contains(err.Error(), "plugin missing required capability") {
t.Fatalf("expected undeclared remote job denial, got %v", err)
}
}
func TestCoreServiceRejectsDuplicateGamePluginManifest(t *testing.T) {
svc := newTestCoreService()
registration := validPluginManifestRegistration()
@@ -1034,7 +1133,7 @@ func createPluginAndRunEndpoint(t *testing.T, svc *CoreService) (domain.GamePlug
ServerType: "scum",
ManifestRef: "artifact://manifests/server.scum/1.0.0",
CreateFormSchemaRef: "artifact://schemas/server.scum/create-form/1.0.0",
RequiredRunCapabilities: []string{"process.install", "process.start", "process.stop", "logs.read"},
RequiredRunCapabilities: []string{"process.install", "process.start", "process.stop", "logs.read", "config.write", "files.read", "files.write"},
DeclaredPermissions: []string{"server.files.read", "server.files.write"},
LifecycleActions: domain.PluginLifecycleActions{
Install: "actions/install.json",