Files
browser/platform/validator/scum_live_data_test.go
T

310 lines
19 KiB
Go

package validator
import (
"strings"
"testing"
"time"
"browser.local/platform/domain"
)
const scumProbeHash = "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
func TestValidateSCUMSchemaProbeRequestAllowsBoundedGenericProbe(t *testing.T) {
request := domain.SCUMSchemaProbeRequest{RequestID: "probe-1", JobID: "job-1", Binding: validatorSCUMBinding(), Bounds: domain.DefaultSCUMSchemaProbeBounds(), RequestedAt: time.Now()}
if err := ValidateSCUMSchemaProbeRequest(request); err != nil {
t.Fatalf("expected valid probe request, got %v", err)
}
}
func TestValidateSCUMSchemaProbeRequestRejectsHostPathsAndLooseBounds(t *testing.T) {
request := domain.SCUMSchemaProbeRequest{RequestID: "probe-1", JobID: "job-1", Binding: validatorSCUMBinding(), Bounds: domain.DefaultSCUMSchemaProbeBounds()}
request.Binding.DatabaseIdentity = `C:\SCUM\Saved\SaveFiles\SCUM.db`
request.Bounds.MaxSampleRows = 25
err := ValidateSCUMSchemaProbeRequest(request)
if err == nil || !strings.Contains(err.Error(), "protected connection material") || !strings.Contains(err.Error(), "maxSampleRows") {
t.Fatalf("expected protected material and bound violations, got %v", err)
}
}
func TestValidateSCUMSchemaProbeResultRejectsRawSQLAndRows(t *testing.T) {
result := domain.SCUMSchemaProbeResult{
RequestID: "probe-1",
JobID: "job-1",
Binding: validatorSCUMBinding(),
Status: domain.SCUMCapabilityEvidenceCompatible,
SchemaFingerprint: scumProbeHash,
ObservedAt: time.Now(),
ResultDigest: scumProbeHash,
Limits: domain.DefaultSCUMSchemaProbeBounds(),
Objects: []domain.SCUMSchemaObjectEvidence{{
ObjectHash: scumProbeHash,
Kind: "table",
NameFingerprint: "select * from players",
DeclaredColumns: []domain.SCUMSchemaColumnEvidence{{NameFingerprint: scumProbeHash, DeclaredType: "TEXT", Ordinal: 1}},
SampleFingerprints: []string{"{\"raw\":\"row\"}"},
}},
}
err := ValidateSCUMSchemaProbeResult(result)
if err == nil || !strings.Contains(err.Error(), "nameFingerprint must be redacted") || !strings.Contains(err.Error(), "sampleFingerprints") {
t.Fatalf("expected redaction violations, got %v", err)
}
}
func TestValidateSCUMSchemaProbeResultAcceptsRunTerminalStatuses(t *testing.T) {
succeeded := domain.SCUMSchemaProbeResult{RequestID: "probe-1", JobID: "job-1", Binding: validatorSCUMBinding(), Status: domain.SCUMSchemaProbeStatusSucceeded, SourceFingerprint: scumProbeHash, SchemaFingerprint: scumProbeHash, ObservedAt: time.Now(), ResultDigest: scumProbeHash, Limits: domain.DefaultSCUMSchemaProbeBounds(), SafeError: domain.SCUMSafeError{Code: domain.SCUMSafeErrorNone}}
if err := ValidateSCUMSchemaProbeResult(succeeded); err != nil {
t.Fatalf("expected succeeded Run probe result to validate, got %v", err)
}
succeeded.SourceFingerprint = "C:/db/SCUM.db"
if err := ValidateSCUMSchemaProbeResult(succeeded); err == nil || !strings.Contains(err.Error(), "sourceFingerprint must be a digest/fingerprint") {
t.Fatalf("expected raw source fingerprint rejection, got %v", err)
}
succeeded.SourceFingerprint = scumProbeHash
failed := succeeded
failed.Status = domain.SCUMCapabilityEvidenceFailed
failed.SchemaFingerprint = ""
failed.SafeError = domain.SCUMSafeError{Code: domain.SCUMSafeErrorTargetUnavailable, Retryable: false}
if err := ValidateSCUMSchemaProbeResult(failed); err != nil {
t.Fatalf("expected safe failed Run probe result to validate, got %v", err)
}
failed.SafeError = domain.SCUMSafeError{Code: domain.SCUMSafeErrorCode("data_target_plan_invalid"), Retryable: false}
if err := ValidateSCUMSchemaProbeResult(failed); err != nil {
t.Fatalf("expected generic Run data-target failure to validate, got %v", err)
}
failed.SafeError = domain.SCUMSafeError{Code: domain.SCUMSafeErrorCode("data_target_invalid path"), Retryable: false}
if err := ValidateSCUMSchemaProbeResult(failed); err == nil || !strings.Contains(err.Error(), "safeError.code is invalid") {
t.Fatalf("expected unsafe data-target code rejection, got %v", err)
}
}
func TestValidateSCUMSQLiteTemplateRequestAllowsBoundedGenericTemplate(t *testing.T) {
request := validatorSCUMSQLiteTemplateRequest()
if err := ValidateSCUMSQLiteTemplateRequest(request); err != nil {
t.Fatalf("expected valid SQLite template request, got %v", err)
}
}
func TestValidateSCUMSQLiteTemplateRequestRejectsSQLPathsAndLooseBounds(t *testing.T) {
request := validatorSCUMSQLiteTemplateRequest()
request.TemplateKey = "select * from players"
request.Parameters = map[string]any{"profilePath": `C:\SCUM\Saved\SCUM.db`}
request.Bounds.MaxRows = 50000
err := ValidateSCUMSQLiteTemplateRequest(request)
if err == nil || !strings.Contains(err.Error(), "templateKey is unsafe") || !strings.Contains(err.Error(), "protected material") || !strings.Contains(err.Error(), "maxRows") {
t.Fatalf("expected template/key/bounds violations, got %v", err)
}
}
func TestValidateSCUMSQLiteTemplateResultAcceptsTypedRows(t *testing.T) {
result := validatorSCUMSQLiteTemplateResult()
if err := ValidateSCUMSQLiteTemplateResult(result); err != nil {
t.Fatalf("expected valid SQLite template result, got %v", err)
}
}
func TestValidateSCUMSQLiteTemplateResultRejectsUnsafeRowsAndMismatchedCounts(t *testing.T) {
result := validatorSCUMSQLiteTemplateResult()
result.RowCount = 2
result.Rows[0]["displayName"] = "select * from user_profile"
err := ValidateSCUMSQLiteTemplateResult(result)
if err == nil || !strings.Contains(err.Error(), "rowCount") || !strings.Contains(err.Error(), "protected material") {
t.Fatalf("expected row-count and protected-row violations, got %v", err)
}
}
func TestValidateSCUMTypedRCONTemplateRequestAllowsBoundedGenericTemplate(t *testing.T) {
request := validatorSCUMTypedRCONTemplateRequest()
if err := ValidateSCUMTypedRCONTemplateRequest(request); err != nil {
t.Fatalf("expected valid typed RCON template request, got %v", err)
}
}
func TestValidateSCUMTypedRCONTemplateRequestRejectsRawCommandAndLooseBounds(t *testing.T) {
request := validatorSCUMTypedRCONTemplateRequest()
request.Payload = map[string]any{"rawCommand": "#SetFamePoints 7 100"}
request.ReviewReason = `use C:\SCUM\secret.txt`
request.Bounds.MaxPayloadBytes = 100000
err := ValidateSCUMTypedRCONTemplateRequest(request)
if err == nil || !strings.Contains(err.Error(), "payload key is unsafe") || !strings.Contains(err.Error(), "reviewReason is unsafe") || !strings.Contains(err.Error(), "maxPayloadBytes") {
t.Fatalf("expected raw command/reason/bounds violations, got %v", err)
}
}
func TestValidateSCUMTypedRCONTemplateResultAcceptsConfirmedEnvelope(t *testing.T) {
result := validatorSCUMTypedRCONTemplateResult()
if err := ValidateSCUMTypedRCONTemplateResult(result); err != nil {
t.Fatalf("expected valid typed RCON template result, got %v", err)
}
}
func TestValidateSCUMTypedRCONTemplateResultRejectsUnconfirmedSuccessAndUnsafeSummary(t *testing.T) {
result := validatorSCUMTypedRCONTemplateResult()
result.ConfirmationStatus = domain.SCUMRCONConfirmationUnknown
result.SafeSummary = "rcon password leaked"
err := ValidateSCUMTypedRCONTemplateResult(result)
if err == nil || !strings.Contains(err.Error(), "confirmed") || !strings.Contains(err.Error(), "safeSummary") {
t.Fatalf("expected confirmation and summary violations, got %v", err)
}
}
func TestValidateSCUMGuardedMutationRequestAllowsBoundedGenericTemplate(t *testing.T) {
request := validatorSCUMGuardedMutationRequest()
if err := ValidateSCUMGuardedMutationRequest(request); err != nil {
t.Fatalf("expected valid guarded mutation request, got %v", err)
}
}
func TestValidateSCUMGuardedMutationRequestRejectsRawXMLSQL855AndMissingGuards(t *testing.T) {
request := validatorSCUMGuardedMutationRequest()
request.Payload = map[string]any{"rawXml": "<CharacterTemplate><Attribute name=\"Strength\" value=\"9\" /></CharacterTemplate>", "fieldKey855": "855"}
request.BackupEvidenceDigest = ""
request.OfflineEvidenceDigest = ""
request.DangerConfirmationDigest = ""
request.ReadbackExpectationDigest = ""
request.ReviewReason = "update sqlite:///private/tmp/SCUM.db directly"
request.Bounds.MaxAffectedRows = 2
err := ValidateSCUMGuardedMutationRequest(request)
if err == nil || !strings.Contains(err.Error(), "payload key is unsafe") || !strings.Contains(err.Error(), "payload value is unsafe") || !strings.Contains(err.Error(), "backupEvidenceDigest") || !strings.Contains(err.Error(), "offlineEvidenceDigest") || !strings.Contains(err.Error(), "dangerConfirmationDigest") || !strings.Contains(err.Error(), "readbackExpectationDigest") || !strings.Contains(err.Error(), "maxAffectedRows") || !strings.Contains(err.Error(), "reviewReason") {
t.Fatalf("expected guarded mutation safety violations, got %v", err)
}
}
func TestValidateSCUMGuardedMutationResultAcceptsConfirmedSingleRowEnvelope(t *testing.T) {
result := validatorSCUMGuardedMutationResult()
if err := ValidateSCUMGuardedMutationResult(result); err != nil {
t.Fatalf("expected valid guarded mutation result, got %v", err)
}
}
func TestValidateSCUMGuardedMutationResultRejectsMultiRowMissingReadbackAndUnsafeSummary(t *testing.T) {
result := validatorSCUMGuardedMutationResult()
result.AffectedRows = 2
result.ReadbackStatus = domain.SCUMMutationReadbackUnknown
result.ReadbackDigest = ""
result.SafeSummary = "raw <CharacterTemplate /> leaked"
err := ValidateSCUMGuardedMutationResult(result)
if err == nil || !strings.Contains(err.Error(), "exactly one affected row") || !strings.Contains(err.Error(), "readback") || !strings.Contains(err.Error(), "safeSummary") {
t.Fatalf("expected affected-row/readback/summary violations, got %v", err)
}
}
func TestValidateSCUMParsedLogBatchResultAcceptsRotationSafeEnvelope(t *testing.T) {
result := validatorSCUMParsedLogBatchResult()
if err := ValidateSCUMParsedLogBatchResult(result); err != nil {
t.Fatalf("expected valid parsed log batch result, got %v", err)
}
}
func TestValidateSCUMParsedLogBatchResultRejectsRawLineNetworkMaterialAndLooseBounds(t *testing.T) {
result := validatorSCUMParsedLogBatchResult()
result.ParserDigest = ""
result.FirstCursor.SourceIdentityDigest = `C:\SCUM\Saved\Logs\login.log`
result.Events[0].Payload = map[string]any{"rawLine": "2026.08.13: '203.0.113.10 player' logged in"}
result.Limits.MaxLineBytes = 1000000
err := ValidateSCUMParsedLogBatchResult(result)
if err == nil || !strings.Contains(err.Error(), "parserDigest") || !strings.Contains(err.Error(), "sourceIdentityDigest") || !strings.Contains(err.Error(), "payload key is unsafe") || !strings.Contains(err.Error(), "payload value is unsafe") || !strings.Contains(err.Error(), "maxLineBytes") {
t.Fatalf("expected parsed-log safety violations, got %v", err)
}
}
func TestValidateSCUMCapabilityEvidenceRequiresSafeCurrentServiceIdentity(t *testing.T) {
evidence := domain.SCUMCapabilityEvidence{Capability: domain.SCUMDataCapabilityPlayerRead, Status: domain.SCUMCapabilityEvidenceCompatible, Binding: validatorSCUMBinding(), AdapterVersion: "adapter-1", SchemaFingerprint: scumProbeHash, ProbeResultDigest: scumProbeHash, AssetDigests: []string{scumProbeHash}, SafeError: domain.SCUMSafeError{Code: domain.SCUMSafeErrorNone}}
if err := ValidateSCUMCapabilityEvidence(evidence); err != nil {
t.Fatalf("expected valid evidence, got %v", err)
}
evidence.SafeError = domain.SCUMSafeError{Code: domain.SCUMSafeErrorProbeFailed, Message: "sqlite:///private/tmp/SCUM.db locked"}
if err := ValidateSCUMCapabilityEvidence(evidence); err == nil || !strings.Contains(err.Error(), "protected material") {
t.Fatalf("expected safe error redaction violation, got %v", err)
}
}
func validatorSCUMBinding() domain.SCUMBindingIdentity {
return domain.SCUMBindingIdentity{ServerInstanceID: "server-1", RunBindingID: "binding-1", RunEndpointID: "run-1", PluginID: "game.scum", PluginVersion: "0.1.6", AdapterVersion: "adapter-1", GameVersion: "scum-1", DatabaseIdentity: "db-fingerprint-1"}
}
func validatorSCUMSQLiteTemplateRequest() domain.SCUMSQLiteTemplateRequest {
return domain.SCUMSQLiteTemplateRequest{RequestID: "request-1", JobID: "job-1", Binding: validatorSCUMBinding(), Capability: domain.SCUMDataCapabilityPlayerRead, TargetKey: "scum-database", TemplateKey: "players.active.v1", AdapterVersion: "adapter-1", RequiredSchemaFingerprint: scumProbeHash, AssetDigest: scumProbeHash, ParameterDigest: scumProbeHash, Parameters: map[string]any{"cursor": "", "limit": 100.0}, Bounds: domain.DefaultSCUMSQLiteTemplateBounds(), RequestedAt: time.Now()}
}
func validatorSCUMSQLiteTemplateResult() domain.SCUMSQLiteTemplateResult {
request := validatorSCUMSQLiteTemplateRequest()
return domain.SCUMSQLiteTemplateResult{RequestID: request.RequestID, JobID: request.JobID, Binding: request.Binding, Status: domain.SCUMTerminalResultSucceeded, Capability: request.Capability, TargetKey: request.TargetKey, TemplateKey: request.TemplateKey, AdapterVersion: request.AdapterVersion, SchemaFingerprint: request.RequiredSchemaFingerprint, AssetDigest: request.AssetDigest, ParameterDigest: request.ParameterDigest, SourceFingerprint: scumProbeHash, ObservedAt: time.Now(), ResultDigest: scumProbeHash, RowCount: 1, Rows: []map[string]any{{"externalPlayerId": "player-redacted", "displayName": "Known Player", "fame": 12.5, "online": true, "squadId": nil}}, Limits: request.Bounds, SafeError: domain.SCUMSafeError{Code: domain.SCUMSafeErrorNone}}
}
func validatorSCUMTypedRCONTemplateRequest() domain.SCUMTypedRCONTemplateRequest {
return domain.SCUMTypedRCONTemplateRequest{RequestID: "request-rcon-1", JobID: "job-rcon-1", Binding: validatorSCUMBinding(), Capability: domain.SCUMDataCapabilityEconomyCommand, TransportKey: "scum-rcon", TargetKey: "scum-rcon", TemplateKey: "economy.fame.set.v1", AdapterVersion: "adapter-1", RequiredSchemaFingerprint: scumProbeHash, AssetDigest: scumProbeHash, PayloadDigest: scumProbeHash, ConfirmationDigest: scumProbeHash, TargetIdentityDigest: scumProbeHash, IdempotencyKey: "idem-rcon-1", Payload: map[string]any{"externalPlayerId": "player-redacted", "absoluteValue": 100.0}, ReviewReason: "operator reviewed absolute fame update", Bounds: domain.DefaultSCUMTypedRCONTemplateBounds(), RequestedAt: time.Now()}
}
func validatorSCUMTypedRCONTemplateResult() domain.SCUMTypedRCONTemplateResult {
request := validatorSCUMTypedRCONTemplateRequest()
return domain.SCUMTypedRCONTemplateResult{RequestID: request.RequestID, JobID: request.JobID, Binding: request.Binding, Status: domain.SCUMTerminalResultSucceeded, Capability: request.Capability, TransportKey: request.TransportKey, TargetKey: request.TargetKey, TemplateKey: request.TemplateKey, AdapterVersion: request.AdapterVersion, SchemaFingerprint: request.RequiredSchemaFingerprint, AssetDigest: request.AssetDigest, PayloadDigest: request.PayloadDigest, ConfirmationDigest: request.ConfirmationDigest, TargetIdentityDigest: request.TargetIdentityDigest, ObservedAt: time.Now(), ResultDigest: scumProbeHash, ResponseDigest: scumProbeHash, ConfirmationStatus: domain.SCUMRCONConfirmationConfirmed, ConfirmationDigestID: scumProbeHash, SafeSummary: "confirmed by declared readback", Limits: request.Bounds, SafeError: domain.SCUMSafeError{Code: domain.SCUMSafeErrorNone}}
}
func validatorSCUMGuardedMutationRequest() domain.SCUMGuardedMutationRequest {
return domain.SCUMGuardedMutationRequest{RequestID: "request-mutation-1", JobID: "job-mutation-1", Binding: validatorSCUMBinding(), Capability: domain.SCUMDataCapabilityProfileXMLWrite, TargetKey: "scum-mutation-db", TemplateKey: "profile.attributes.patch.v1", AdapterVersion: "adapter-1", RequiredSchemaFingerprint: scumProbeHash, AssetDigest: scumProbeHash, TargetIdentityDigest: scumProbeHash, ExpectedRowDigest: scumProbeHash, ExpectedValueDigest: scumProbeHash, ExpectedXMLDigest: scumProbeHash, PatchDigest: scumProbeHash, BackupEvidenceDigest: scumProbeHash, OfflineEvidenceDigest: scumProbeHash, DangerConfirmationDigest: scumProbeHash, ReadbackExpectationDigest: scumProbeHash, IdempotencyKey: "idem-mutation-1", Payload: map[string]any{"attributeKey": "Strength", "absoluteValue": 8.5}, ReviewReason: "operator confirmed offline profile attribute patch", Bounds: domain.DefaultSCUMGuardedMutationBounds(), RequestedAt: time.Now()}
}
func validatorSCUMGuardedMutationResult() domain.SCUMGuardedMutationResult {
request := validatorSCUMGuardedMutationRequest()
return domain.SCUMGuardedMutationResult{RequestID: request.RequestID, JobID: request.JobID, Binding: request.Binding, Status: domain.SCUMTerminalResultSucceeded, Capability: request.Capability, TargetKey: request.TargetKey, TemplateKey: request.TemplateKey, AdapterVersion: request.AdapterVersion, SchemaFingerprint: request.RequiredSchemaFingerprint, AssetDigest: request.AssetDigest, SourceFingerprint: scumProbeHash, TargetIdentityDigest: request.TargetIdentityDigest, ExpectedRowDigest: request.ExpectedRowDigest, ExpectedValueDigest: request.ExpectedValueDigest, ExpectedXMLDigest: request.ExpectedXMLDigest, PatchDigest: request.PatchDigest, BackupEvidenceDigest: request.BackupEvidenceDigest, OfflineEvidenceDigest: request.OfflineEvidenceDigest, DangerConfirmationDigest: request.DangerConfirmationDigest, ReadbackExpectationDigest: request.ReadbackExpectationDigest, ObservedAt: time.Now(), ResultDigest: scumProbeHash, BeforeDigest: scumProbeHash, AfterDigest: scumProbeHash, ReadbackDigest: scumProbeHash, AffectedRows: 1, ReadbackStatus: domain.SCUMMutationReadbackConfirmed, SafeSummary: "confirmed by declared readback", SafeError: domain.SCUMSafeError{Code: domain.SCUMSafeErrorNone}, Limits: request.Bounds}
}
func validatorSCUMParsedLogBatchResult() domain.SCUMParsedLogBatchResult {
cursor := domain.SCUMParsedLogCursor{SourceIdentityDigest: scumProbeHash, StreamGeneration: scumProbeHash, Sequence: 7}
return domain.SCUMParsedLogBatchResult{
RequestID: "request-log-1",
JobID: "job-log-1",
Binding: validatorSCUMBinding(),
Status: domain.SCUMTerminalResultSucceeded,
SourceKey: "scum-login-events",
StreamKey: "scum.login",
ParserKey: "scum-login-log-login-parser",
ParserVersion: "scum-login-log-v1",
AdapterVersion: "adapter-1",
AssetDigest: scumProbeHash,
ParserDigest: scumProbeHash,
ObservedAt: time.Now(),
ResultDigest: scumProbeHash,
FirstCursor: cursor,
LastCursor: cursor,
TailState: domain.SCUMLogTailRotated,
Replay: true,
EventCount: 1,
SafeSummary: "one sanitized login event parsed from declared source",
SafeError: domain.SCUMSafeError{Code: domain.SCUMSafeErrorNone},
Limits: domain.DefaultSCUMParsedLogBatchBounds(),
Events: []domain.SCUMParsedLogEvent{{
EventType: "scum.login",
OccurredAt: time.Now(),
Cursor: cursor,
LogicalEventDigest: scumProbeHash,
EventDigest: scumProbeHash,
PayloadDigest: scumProbeHash,
Payload: map[string]any{"externalPlayerId": "player-redacted", "displayName": "Known Player", "profileLocalId": "profile-redacted"},
}},
}
}