Freeze SCUM guarded mutation run contract

This commit is contained in:
npc0-hue
2026-08-13 14:04:44 +08:00
parent ac6f5419fe
commit 41c109128d
17 changed files with 741 additions and 37 deletions
+54
View File
@@ -163,6 +163,51 @@ func TestValidateSCUMTypedRCONTemplateResultRejectsUnconfirmedSuccessAndUnsafeSu
}
}
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 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 {
@@ -196,3 +241,12 @@ 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}
}