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 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}} }