feat(scum): wire platform schema probe jobs
This commit is contained in:
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
@@ -86,3 +87,97 @@ func TestRemoteAdapterRequestPropagatesTypedInputsToRunJob(t *testing.T) {
|
||||
t.Fatal("real Run claim aliases persisted remote inputs")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSCUMSchemaProbeDispatchIsPlatformScheduledAndFenced(t *testing.T) {
|
||||
svc := newTestCoreService()
|
||||
plugin, endpoint := createPluginAndRunEndpoint(t, svc)
|
||||
plugin.Permissions.RemoteAccess = true
|
||||
plugin.DeclaredPermissions = append(plugin.DeclaredPermissions, "server.remote.access")
|
||||
plugin.RequiredRunCapabilities = append(plugin.RequiredRunCapabilities, domain.JobCapabilityRemoteRunDBSQLiteProbe)
|
||||
plugin.RemoteAccess = domain.GamePluginRemoteAccess{Methods: []string{"run"}, RunCapabilities: []string{domain.JobCapabilityRemoteRunDBSQLiteProbe}, DatabaseEngines: []string{"sqlite"}}
|
||||
plugin.RuntimeProfiles.TransportProfiles = append(plugin.RuntimeProfiles.TransportProfiles, domain.RuntimeTransportProfile{Key: "scum-database", Kind: "sqlite", TargetKey: "scum-database", Capabilities: []string{domain.JobCapabilityRemoteRunDBSQLiteProbe}})
|
||||
plugin.RuntimeProfiles.LifecycleProfiles[0].TransportKeys = append(plugin.RuntimeProfiles.LifecycleProfiles[0].TransportKeys, "scum-database")
|
||||
plugin.SCUMLiveData = domain.SCUMLiveDataManifest{SchemaVersion: "1", Probe: domain.SCUMSchemaProbeDeclaration{Capability: domain.JobCapabilityRemoteRunDBSQLiteProbe, TargetKey: "scum-database", Bounds: domain.DefaultSCUMSchemaProbeBounds()}, CapabilityGates: []domain.SCUMLiveDataCapabilityGateDeclaration{{Capability: domain.SCUMDataCapabilitySchemaProbe, Gate: domain.SCUMCapabilityGateDisabled, AdapterVersion: "scum-live-data-v0", EvidenceStatus: domain.SCUMCapabilityEvidenceMissing, SafeReason: "waiting for current service evidence"}}}
|
||||
if err := svc.store.GamePlugins().Update(plugin); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
endpoint.Capabilities = append(endpoint.Capabilities, domain.JobCapabilityRemoteRunDBSQLiteProbe)
|
||||
if err := svc.store.RunEndpoints().Update(endpoint); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
session := createServiceUserAndLogin(t, svc, domain.User{ID: "user-scum-probe-owner", DisplayName: "SCUM Probe Owner", Email: "scum-probe-owner@example.test", Roles: []string{"server-owner"}, PasswordHash: "secret-password"})
|
||||
instance, err := svc.CreateServerInstanceForSession(session, domain.ServerInstance{ID: "server-scum-probe", PluginID: plugin.ID, RunEndpointID: endpoint.ID, Name: "SCUM Probe"})
|
||||
if err != nil {
|
||||
t.Fatalf("create server instance: %v", err)
|
||||
}
|
||||
createCompleteRuntimeBinding(t, svc, instance, "local")
|
||||
|
||||
_, err = svc.RequestRemoteAdapterForSession(session, domain.RemoteAdapterRequest{ServerInstanceID: instance.ID, DeclarationKey: "scum-database", TargetKey: "scum-database", Capability: domain.JobCapabilityRemoteRunDBSQLiteProbe, IdempotencyKey: "direct-probe-denied", InputRef: "input://scum-schema-probe/direct-probe-denied"})
|
||||
if err == nil || !strings.Contains(err.Error(), "scheduled by Platform") {
|
||||
t.Fatalf("expected public probe request denial, got %v", err)
|
||||
}
|
||||
endpoint.Capabilities = withoutCapability(endpoint.Capabilities, domain.JobCapabilityRemoteRunDBSQLiteProbe)
|
||||
if err := svc.store.RunEndpoints().Update(endpoint); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, _, err = svc.RequestSCUMSchemaProbeForSession(session, instance.ID, "probe-missing-run-capability"); err == nil || !strings.Contains(err.Error(), "does not expose") {
|
||||
t.Fatalf("expected missing active Run capability, got %v", err)
|
||||
}
|
||||
jobs, err := svc.store.Jobs().List(domain.JobFilter{ServerInstanceID: instance.ID})
|
||||
if err != nil || len(jobs) != 0 {
|
||||
t.Fatalf("missing probe executor must not create jobs: len=%d err=%v", len(jobs), err)
|
||||
}
|
||||
|
||||
endpoint.Capabilities = append(endpoint.Capabilities, domain.JobCapabilityRemoteRunDBSQLiteProbe)
|
||||
if err := svc.store.RunEndpoints().Update(endpoint); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
probeRequest, queued, err := svc.RequestSCUMSchemaProbeForSession(session, instance.ID, "probe-current-schema")
|
||||
if err != nil {
|
||||
t.Fatalf("queue SCUM schema probe: %v", err)
|
||||
}
|
||||
job, err := svc.store.Jobs().Get(queued.RequestID)
|
||||
if err != nil {
|
||||
t.Fatalf("get probe job: %v", err)
|
||||
}
|
||||
if job.Capability != domain.JobCapabilityRemoteRunDBSQLiteProbe || job.TargetKey != "scum-database" || job.ExecutionInput.RemoteAdapterKind != string(domain.RemoteAdapterDatabase) || job.ExecutionInput.Inputs["databaseIdentity"] != "logical:scum-database" {
|
||||
t.Fatalf("unexpected probe job envelope: %+v", job)
|
||||
}
|
||||
if job.ExecutionInput.Inputs["jobId"] != job.ID || job.ExecutionInput.Inputs["requestId"] != probeRequest.RequestID || job.ExecutionInput.Inputs["maxResultBytes"] != "524288" {
|
||||
t.Fatalf("probe inputs are not fenced and bounded: %+v", job.ExecutionInput.Inputs)
|
||||
}
|
||||
|
||||
helloRequest := validRunControlHello()
|
||||
helloRequest.CapabilityReport.Capabilities = append(helloRequest.CapabilityReport.Capabilities, domain.JobCapabilityRemoteRunDBSQLiteProbe)
|
||||
helloRequest.CapabilityReport.Fingerprint = "cap-scum-probe"
|
||||
hello, err := svc.RegisterRunHello(helloRequest)
|
||||
if err != nil {
|
||||
t.Fatalf("register Run hello: %v", err)
|
||||
}
|
||||
claim, err := svc.ClaimRunJob(domain.RunJobClaim{RunEndpointID: endpoint.ID, SessionToken: hello.SessionToken, Capabilities: []string{domain.JobCapabilityRemoteRunDBSQLiteProbe}, Capacity: domain.RunCapacity{MaxJobs: 1}})
|
||||
if err != nil {
|
||||
t.Fatalf("claim probe job: %v", err)
|
||||
}
|
||||
if !claim.HasJob || claim.Job == nil || claim.Job.ExecutionInput.Inputs["adapterVersion"] != "scum-live-data-v0" || claim.Job.ExecutionInput.Inputs["targetKey"] != "scum-database" {
|
||||
t.Fatalf("claimed probe job lost typed inputs: %+v", claim.Job)
|
||||
}
|
||||
badBinding := probeRequest.Binding
|
||||
badBinding.RunBindingID = "runtime-binding-other"
|
||||
badProbe := domain.SCUMSchemaProbeResult{RequestID: probeRequest.RequestID, JobID: probeRequest.JobID, Binding: badBinding, Status: domain.SCUMCapabilityEvidenceCompatible, SchemaFingerprint: "sha256:" + strings.Repeat("a", 64), ObservedAt: fixedTime, ResultDigest: "sha256:" + strings.Repeat("b", 64), Limits: probeRequest.Bounds}
|
||||
_, err = svc.CompleteRunJob(domain.RunJobResult{RunEndpointID: endpoint.ID, SessionToken: hello.SessionToken, JobID: claim.Job.JobID, LeaseToken: claim.Job.LeaseToken, Attempt: claim.Job.Attempt, State: domain.JobStateSucceeded, Progress: domain.RunJobProgressReport{Percent: 100, Message: "probe complete"}, Message: "probe complete", ExecutionResult: domain.JobExecutionResult{Kind: scumSchemaProbeExecutionKind, SQLiteSchemaProbe: &badProbe, AuditSummary: "redacted schema probe"}})
|
||||
if err == nil || !strings.Contains(err.Error(), "binding identity") {
|
||||
t.Fatalf("expected binding mismatch rejection, got %v", err)
|
||||
}
|
||||
goodProbe := badProbe
|
||||
goodProbe.Binding = probeRequest.Binding
|
||||
if _, err := svc.CompleteRunJob(domain.RunJobResult{RunEndpointID: endpoint.ID, SessionToken: hello.SessionToken, JobID: claim.Job.JobID, LeaseToken: claim.Job.LeaseToken, Attempt: claim.Job.Attempt, State: domain.JobStateSucceeded, Progress: domain.RunJobProgressReport{Percent: 100, Message: "probe complete"}, Message: "probe complete", ExecutionResult: domain.JobExecutionResult{Kind: scumSchemaProbeExecutionKind, SQLiteSchemaProbe: &goodProbe, AuditSummary: "redacted schema probe"}}); err != nil {
|
||||
t.Fatalf("complete fenced probe job: %v", err)
|
||||
}
|
||||
stored, err := svc.store.Jobs().Get(job.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("get completed probe job: %v", err)
|
||||
}
|
||||
if stored.ExecutionResult.SQLiteSchemaProbe == nil || stored.ExecutionResult.SQLiteSchemaProbe.ResultDigest != goodProbe.ResultDigest || stored.ExecutionResult.SQLiteSchemaProbe.Binding.RunBindingID != probeRequest.Binding.RunBindingID {
|
||||
t.Fatalf("typed probe result was not persisted safely: %+v", stored.ExecutionResult.SQLiteSchemaProbe)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user