Restore durable log ingest and typed plugin projections

This commit is contained in:
npc0-hue
2026-09-02 10:20:30 +08:00
parent 40ac46ba17
commit 6018d8f0fc
61 changed files with 809 additions and 3157 deletions
@@ -11,7 +11,7 @@ import (
func seedGameClientBridgeComponentSession(t *testing.T, svc *CoreService, now time.Time, token string) (domain.ClientManagerInstallation, domain.ClientManagerSession) {
t.Helper()
installation := domain.ClientManagerInstallation{ID: "installation-1", ServerInstanceID: "server-1", PluginID: "game.scum", ProfileKey: "scum-client", RunEndpointID: "run-1", Status: domain.ClientManagerLifecycleOnline, ActiveArtifactID: "artifact-1", KeyGeneration: 2, DeploymentGeneration: 3}
session := domain.ClientManagerSession{ID: "component-session-1", InstallationID: installation.ID, ServerInstanceID: installation.ServerInstanceID, ProfileKey: installation.ProfileKey, RunEndpointID: installation.RunEndpointID, ArtifactID: installation.ActiveArtifactID, KeyGeneration: installation.KeyGeneration, DeploymentGeneration: installation.DeploymentGeneration, TokenHash: tokenHash(token), Capabilities: []string{"component.heartbeat", gameClientBridgeCapability, gameClientBridgeLogStreamCapability}, Status: domain.ClientManagerSessionActive, ExpiresAt: now.Add(time.Hour)}
session := domain.ClientManagerSession{ID: "component-session-1", InstallationID: installation.ID, ServerInstanceID: installation.ServerInstanceID, ProfileKey: installation.ProfileKey, RunEndpointID: installation.RunEndpointID, ArtifactID: installation.ActiveArtifactID, KeyGeneration: installation.KeyGeneration, DeploymentGeneration: installation.DeploymentGeneration, TokenHash: tokenHash(token), Capabilities: []string{"component.heartbeat", gameClientBridgeCapability}, Status: domain.ClientManagerSessionActive, ExpiresAt: now.Add(time.Hour)}
key := domain.EncryptedComponentKey{ID: "key-1", ServerInstanceID: installation.ServerInstanceID, ComponentKind: domain.DistributionComponentClientManager, ComponentKey: installation.ProfileKey, Generation: installation.KeyGeneration, Status: domain.ComponentKeyStatusActive}
if err := svc.store.ClientManagerInstallations().Create(installation); err != nil {
t.Fatal(err)
@@ -80,34 +80,6 @@ func TestGameClientBridgeComponentSessionAuthorizesCommandsAndSnapshots(t *testi
}
}
func TestGameClientBridgeComponentSessionAuthorizesLiveLogStream(t *testing.T) {
svc, clock := newGameClientBridgeService(t)
const token = "component-session-token"
_, session := seedGameClientBridgeComponentSession(t, svc, *clock, token)
if err := svc.store.RunEndpoints().Create(domain.RunEndpoint{ID: session.RunEndpointID, DisplayName: "Component Run", Status: domain.RunEndpointStatusOnline, Capabilities: []string{"process.start", "logs.read"}, Capacity: domain.RunCapacity{MaxJobs: 4}, LastHeartbeatAt: *clock}); err != nil {
t.Fatalf("seed run endpoint: %v", err)
}
server := domain.ServerInstance{ID: session.ServerInstanceID, PluginID: "game.scum", PluginVersion: "1.0.0", RunEndpointID: session.RunEndpointID, Name: "SCUM"}
if err := svc.store.ServerInstances().Create(server); err != nil {
t.Fatalf("create server instance: %v", err)
}
instance, err := svc.AuthorizeGameClientBridgeLogStream(domain.GameClientBridgeLogStreamRequest{SessionToken: token})
if err != nil || instance.ID != server.ID || instance.PluginID != server.PluginID {
t.Fatalf("authorize companion log stream: instance=%#v err=%v", instance, err)
}
if instance.RunEndpointID != session.RunEndpointID {
t.Fatalf("authorized stream was not bound to component server: %#v", instance)
}
session.Capabilities = []string{"component.heartbeat", gameClientBridgeCapability}
if err := svc.store.ClientManagerSessions().Update(session); err != nil {
t.Fatal(err)
}
if _, err := svc.AuthorizeGameClientBridgeLogStream(domain.GameClientBridgeLogStreamRequest{SessionToken: token}); !errors.Is(err, ErrForbidden) {
t.Fatalf("expected missing logs.stream rejection, got %v", err)
}
}
func TestGameClientBridgeClaimCannotBeCompletedByAnotherCurrentSession(t *testing.T) {
svc, clock := newGameClientBridgeService(t)
const firstToken = "component-session-token-one"