Integrate SCUM real ops workflows

This commit is contained in:
npc0-hue
2026-08-10 21:12:53 +08:00
parent 1063330710
commit a770bc6250
88 changed files with 6375 additions and 2719 deletions
+110
View File
@@ -0,0 +1,110 @@
package service
import (
"testing"
"time"
"browser.local/platform/domain"
)
func TestSCUMObservationProjectsRealRowsAndSeparatesProfileFromSteamID(t *testing.T) {
svc, _ := newRegisteredLogIngestService(t)
observed := time.Date(2026, 8, 10, 9, 0, 0, 0, time.UTC)
observation, err := svc.ApplySCUMObservationResult(domain.SCUMObservationResult{
ServerInstanceID: "server-1",
PluginID: "server.scum",
Source: "run.sqlite.read",
QueryKey: "scum.player.profile",
Sequence: 10,
Checksum: "sha256:profile-10",
ObservedAt: observed,
Rows: []map[string]any{{
"gamePlayerId": "steam-1",
"userProfileId": "profile-99",
"steamId": "steam-1",
"displayName": "Moon",
"squadId": "squad-1",
"famePoints": 42,
"normalBalance": 500.0,
"goldBalance": 7.0,
"x": 100,
"y": 200,
"z": 30,
"lastSaveTime": observed.Add(-time.Minute).Format(time.RFC3339),
"future_column": "preserved",
}},
})
if err != nil || observation.Status != domain.SCUMObservationAccepted {
t.Fatalf("apply observation=%+v err=%v", observation, err)
}
player, err := svc.store.GamePlayers().Get(gamePlayerRecordID("server-1", "steam-1"))
if err != nil || player.DisplayName != "Moon" {
t.Fatalf("expected game player from real row: player=%+v err=%v", player, err)
}
states, err := svc.store.SCUMPlayerLiveStates().List(domain.SCUMProjectionFilter{ServerInstanceID: "server-1", UserProfileID: "profile-99"})
if err != nil || len(states) != 1 {
t.Fatalf("states=%+v err=%v", states, err)
}
state := states[0]
if state.GamePlayerID != "steam-1" || state.UserProfileID != "profile-99" || state.SteamID != "steam-1" || state.NormalBalance != 500 || state.Online {
t.Fatalf("identity/economy projection mixed IDs or inferred online incorrectly: %+v", state)
}
if !state.Position.HasCoordinates || state.Position.X != 100 || state.Position.Y != 200 || state.UnknownFields["future_column"] != "preserved" {
t.Fatalf("position/unknown fields not projected safely: %+v", state)
}
stale, err := svc.ApplySCUMObservationResult(domain.SCUMObservationResult{ServerInstanceID: "server-1", PluginID: "server.scum", Source: "run.sqlite.read", QueryKey: "scum.player.profile", Sequence: 9, Checksum: "sha256:profile-9", ObservedAt: observed.Add(-time.Hour), Rows: []map[string]any{{"gamePlayerId": "steam-1", "userProfileId": "profile-99", "displayName": "Old", "normalBalance": 9999}}})
if err != nil || stale.Status != domain.SCUMObservationStale || stale.ErrorCode != "older_observation" {
t.Fatalf("expected older observation stale, got %+v err=%v", stale, err)
}
again, err := svc.store.SCUMPlayerLiveStates().Get(state.ID)
if err != nil || again.DisplayName != "Moon" || again.NormalBalance != 500 {
t.Fatalf("older observation overwrote last-known-good: %+v err=%v", again, err)
}
}
func TestSCUMFailedObservationMarksStaleWithoutOverwritingProjection(t *testing.T) {
svc, _ := newRegisteredLogIngestService(t)
observed := time.Date(2026, 8, 10, 10, 0, 0, 0, time.UTC)
if _, err := svc.ApplySCUMObservationResult(domain.SCUMObservationResult{ServerInstanceID: "server-1", PluginID: "server.scum", Source: "run.sqlite.read", QueryKey: "scum.player.profile", Sequence: 1, Checksum: "sha256:ok", ObservedAt: observed, Rows: []map[string]any{{"gamePlayerId": "steam-2", "userProfileId": "profile-2", "displayName": "Nova", "normalBalance": 125}}}); err != nil {
t.Fatalf("apply initial observation: %v", err)
}
failed, err := svc.ApplySCUMObservationResult(domain.SCUMObservationResult{ServerInstanceID: "server-1", PluginID: "server.scum", Source: "run.sqlite.read", QueryKey: "scum.player.profile", Sequence: 2, Checksum: "sha256:failed", Status: domain.SCUMObservationFailed, ErrorCode: "sqlite_busy", ObservedAt: observed.Add(time.Minute)})
if err != nil || failed.Status != domain.SCUMObservationFailed {
t.Fatalf("failed observation=%+v err=%v", failed, err)
}
states, err := svc.store.SCUMPlayerLiveStates().List(domain.SCUMProjectionFilter{ServerInstanceID: "server-1", GamePlayerID: "steam-2"})
if err != nil || len(states) != 1 {
t.Fatalf("states=%+v err=%v", states, err)
}
if states[0].NormalBalance != 125 || states[0].Freshness.Status != domain.SCUMProjectionStale || states[0].Freshness.StaleReason != "observation_failed" {
t.Fatalf("failed query did not preserve values and mark stale: %+v", states[0])
}
}
func TestSCUMLoginLogsProjectLiveStateAndDatabaseSaveTimeDoesNotProveOnline(t *testing.T) {
svc, token := newRegisteredLogIngestService(t)
createLogStreamFixture(t, svc)
base := time.Date(2026, 8, 10, 11, 0, 0, 0, time.UTC)
login := gamePlayerBatch(t, token, 1, []domain.LogEntry{{Seq: 1, Timestamp: base, Line: "login accepted", Fields: map[string]string{"eventType": "scum.login", "playerId": "steam-3", "playerName": "Comet", "sessionId": "session-3", "outcome": "accepted"}}})
if _, err := svc.IngestLogBatch(login); err != nil {
t.Fatalf("ingest login: %v", err)
}
states, err := svc.store.SCUMPlayerLiveStates().List(domain.SCUMProjectionFilter{ServerInstanceID: "server-1", GamePlayerID: "steam-3"})
if err != nil || len(states) != 1 || !states[0].Online {
t.Fatalf("login did not mark live state online: states=%+v err=%v", states, err)
}
logout := gamePlayerBatch(t, token, 2, []domain.LogEntry{{Seq: 2, Timestamp: base.Add(time.Minute), Line: "logout", Fields: map[string]string{"eventType": "scum.logout", "playerId": "steam-3", "playerName": "Comet", "sessionId": "session-3", "reason": "disconnect"}}})
if _, err := svc.IngestLogBatch(logout); err != nil {
t.Fatalf("ingest logout: %v", err)
}
if _, err := svc.ApplySCUMObservationResult(domain.SCUMObservationResult{ServerInstanceID: "server-1", PluginID: "server.scum", Source: "run.sqlite.read", QueryKey: "scum.player.profile", Sequence: 3, Checksum: "sha256:save-time", ObservedAt: base.Add(2 * time.Minute), Rows: []map[string]any{{"gamePlayerId": "steam-3", "userProfileId": "profile-3", "displayName": "Comet", "lastSaveTime": base.Add(90 * time.Second).Format(time.RFC3339)}}}); err != nil {
t.Fatalf("apply save-time observation: %v", err)
}
states, err = svc.store.SCUMPlayerLiveStates().List(domain.SCUMProjectionFilter{ServerInstanceID: "server-1", GamePlayerID: "steam-3"})
if err != nil || len(states) != 1 {
t.Fatalf("states=%+v err=%v", states, err)
}
if states[0].Online || states[0].LastSaveTime.IsZero() {
t.Fatalf("last_save_time was incorrectly treated as online proof: %+v", states[0])
}
}