package runtime import ( "context" "encoding/json" "os" "path/filepath" "runtime" "strings" "testing" "browser.local/run/config" "browser.local/run/protocol" ) func TestMaterializeSQLiteSnapshotDataTargetCreatesScopedProbeTarget(t *testing.T) { workspaceRoot := t.TempDir() sourceRoot := t.TempDir() createSQLiteProbeFixture(t, filepath.Join(sourceRoot, "Saved", "SaveFiles", "current.db")) target := protocol.RunAutonomousDataTarget{Key: "current-db", Kind: "sqlite.snapshot", TransportKey: "current-db", SourceRootKey: "server-root", SourcePath: "Saved/SaveFiles/current.db", WorkspaceKey: "databases/current-db", RefreshPolicy: "on-demand-snapshot", MaxBytes: 128 * 1024 * 1024} manifest, err := materializeSQLiteSnapshotDataTarget(context.Background(), workspaceRoot, "server-data", "run-local", target, map[string]string{"server-root": sourceRoot}) if err != nil { t.Fatalf("materialize data target: %v", err) } if manifest.SnapshotChecksum == "" || manifest.SnapshotSizeBytes <= 0 || manifest.SourcePathFingerprint == "" { t.Fatalf("expected bounded snapshot metadata, got %+v", manifest) } scope, err := NewWorkspaceResolver(workspaceRoot).Scope("server-data", "run-local") if err != nil { t.Fatalf("resolve scope: %v", err) } manifestBody, err := os.ReadFile(filepath.Join(scope, "databases", "current-db.snapshot.json")) if err != nil { t.Fatalf("read snapshot manifest: %v", err) } if strings.Contains(string(manifestBody), sourceRoot) || strings.Contains(string(manifestBody), "Saved/SaveFiles/current.db") { t.Fatalf("snapshot manifest leaked host/source material: %s", manifestBody) } assignment := sqliteSchemaProbeAssignment() assignment.ServerInstanceID = "server-data" assignment.ExecutionInput.WorkspaceScope = "run-local" assignment.TargetKey = "databases/current-db" assignment.ExecutionInput.SQLiteSchemaProbe.Binding.ServerInstanceID = assignment.ServerInstanceID assignment.ExecutionInput.SQLiteSchemaProbe.Binding.RunEndpointID = assignment.RunEndpointID result := NewSQLiteSchemaProbeExecutor(workspaceRoot).Execute(context.Background(), assignment) if result.State != lifecycleResultStateSucceeded || result.ExecutionResult.SQLiteSchemaProbe == nil || result.ExecutionResult.SQLiteSchemaProbe.Status != "succeeded" { t.Fatalf("expected probe to read materialized snapshot, got %+v", result) } } func TestWorkerMaterializesMatchingDataTargetBeforeSQLiteProbe(t *testing.T) { client := newFakeWorkerClient() cfg := workerTestConfig(t) cfg.ServerInstanceID = "server-data" cfg.PluginID = "game.example" cfg.ComponentKind = config.PackageComponentRun cfg.ComponentKey = "run-local" sourceRoot := t.TempDir() createSQLiteProbeFixture(t, filepath.Join(sourceRoot, "Saved", "SaveFiles", "current.db")) writeAutonomousPlanFixture(t, cfg.WorkspaceRoot, cfg.ServerInstanceID, cfg.ComponentKey, protocol.RunAutonomousLifecyclePlan{ SchemaVersion: "1", ServerInstanceID: cfg.ServerInstanceID, PluginID: cfg.PluginID, PluginVersion: "1.0.0", RunEndpointID: cfg.RunEndpointID, ProfileKey: cfg.ComponentKey, TargetOS: runtime.GOOS, TargetArch: runtime.GOARCH, TargetRelease: "run-dist-test", Bootstrap: &protocol.RunAutonomousLifecycleAction{Action: "start", Operation: "start", Capability: protocol.RunCapabilityProcessStart, TargetKey: "actions/start.json"}, DataTargets: []protocol.RunAutonomousDataTarget{{Key: "current-db", Kind: "sqlite.snapshot", TransportKey: "current-db", SourceRootKey: "server-root", SourcePath: "Saved/SaveFiles/current.db", WorkspaceKey: "databases/current-db", RefreshPolicy: "on-demand-snapshot", MaxBytes: 128 * 1024 * 1024, Platforms: []string{runtime.GOOS}}}, RuntimeBindings: map[string]string{"server-root": sourceRoot}, }) worker, err := NewWorker(cfg, client) if err != nil { t.Fatalf("new worker: %v", err) } assignment := sqliteSchemaProbeAssignment() assignment.ServerInstanceID = cfg.ServerInstanceID assignment.RunEndpointID = cfg.RunEndpointID assignment.TargetKey = "current-db" assignment.ExecutionInput.WorkspaceScope = cfg.ComponentKey assignment.ExecutionInput.SQLiteSchemaProbe.Binding.ServerInstanceID = cfg.ServerInstanceID assignment.ExecutionInput.SQLiteSchemaProbe.Binding.RunEndpointID = cfg.RunEndpointID assignment.ExecutionInput.SQLiteSchemaProbe.Binding.PluginID = cfg.PluginID result := worker.executeAssignment(context.Background(), assignment) if result.State != lifecycleResultStateSucceeded || result.ExecutionResult.SQLiteSchemaProbe == nil || result.ExecutionResult.SQLiteSchemaProbe.Status != "succeeded" { serialized, _ := json.Marshal(result) t.Fatalf("expected worker probe to materialize and inspect data target, got %s", serialized) } } func TestWorkerMaterializesMatchingDataTargetBeforeSQLiteQuery(t *testing.T) { client := newFakeWorkerClient() cfg := workerTestConfig(t) cfg.ServerInstanceID = "server-data-query" cfg.PluginID = "game.example" cfg.ComponentKind = config.PackageComponentRun cfg.ComponentKey = "run-local" sourceRoot := t.TempDir() createSQLiteProbeFixture(t, filepath.Join(sourceRoot, "Saved", "SaveFiles", "current.db")) writeAutonomousPlanFixture(t, cfg.WorkspaceRoot, cfg.ServerInstanceID, cfg.ComponentKey, protocol.RunAutonomousLifecyclePlan{ SchemaVersion: "1", ServerInstanceID: cfg.ServerInstanceID, PluginID: cfg.PluginID, PluginVersion: "1.0.0", RunEndpointID: cfg.RunEndpointID, ProfileKey: cfg.ComponentKey, TargetOS: runtime.GOOS, TargetArch: runtime.GOARCH, TargetRelease: "run-dist-test", Bootstrap: &protocol.RunAutonomousLifecycleAction{Action: "start", Operation: "start", Capability: protocol.RunCapabilityProcessStart, TargetKey: "actions/start.json"}, DataTargets: []protocol.RunAutonomousDataTarget{{Key: "current-db", Kind: "sqlite.snapshot", TransportKey: "current-db", SourceRootKey: "server-root", SourcePath: "Saved/SaveFiles/current.db", WorkspaceKey: "databases/current-db", RefreshPolicy: "on-demand-snapshot", MaxBytes: 128 * 1024 * 1024, Platforms: []string{runtime.GOOS}}}, RuntimeBindings: map[string]string{"server-root": sourceRoot}, }) scope, err := NewWorkspaceResolver(cfg.WorkspaceRoot).Scope(cfg.ServerInstanceID, cfg.ComponentKey) if err != nil { t.Fatalf("create package scope: %v", err) } writeSQLiteQueryAsset(t, scope, "sql/members.sql", "SELECT CAST(id AS TEXT) AS memberId, name AS displayName FROM members ORDER BY id LIMIT :limit") worker, err := NewWorker(cfg, client) if err != nil { t.Fatalf("new worker: %v", err) } assignment := lifecycleAssignment(protocol.RunCapabilityRemoteRunDBSQLiteQuery) assignment.ServerInstanceID = cfg.ServerInstanceID assignment.RunEndpointID = cfg.RunEndpointID assignment.TargetKey = "current-db" assignment.InputRef = "input://plugin-query-poll/server-data-query/members" assignment.ExecutionInput.WorkspaceScope = cfg.ComponentKey assignment.ExecutionInput.PluginID = cfg.PluginID assignment.ExecutionInput.RemoteAdapterKey = "current-db" assignment.ExecutionInput.RemoteAdapterKind = "database" assignment.ExecutionInput.TimeoutSeconds = 10 assignment.ExecutionInput.Inputs = map[string]string{"sqlRef": "sql/members.sql", "maxRows": "2"} result := worker.executeAssignment(context.Background(), assignment) if result.State != lifecycleResultStateSucceeded || result.ExecutionResult.Kind != "sqlite.query" || !strings.Contains(result.ExecutionResult.Content, `"displayName":"alpha"`) { serialized, _ := json.Marshal(result) t.Fatalf("expected worker query to materialize and return rows, got %s", serialized) } } func TestWorkerSQLiteQueryRequiresDeclaredDataTarget(t *testing.T) { client := newFakeWorkerClient() cfg := workerTestConfig(t) cfg.ServerInstanceID = "server-data-query-missing" cfg.PluginID = "game.example" cfg.ComponentKind = config.PackageComponentRun cfg.ComponentKey = "run-local" writeAutonomousPlanFixture(t, cfg.WorkspaceRoot, cfg.ServerInstanceID, cfg.ComponentKey, protocol.RunAutonomousLifecyclePlan{ SchemaVersion: "1", ServerInstanceID: cfg.ServerInstanceID, PluginID: cfg.PluginID, PluginVersion: "1.0.0", RunEndpointID: cfg.RunEndpointID, ProfileKey: cfg.ComponentKey, TargetOS: runtime.GOOS, TargetArch: runtime.GOARCH, TargetRelease: "run-dist-test", }) worker, err := NewWorker(cfg, client) if err != nil { t.Fatalf("new worker: %v", err) } assignment := lifecycleAssignment(protocol.RunCapabilityRemoteRunDBSQLiteQuery) assignment.ServerInstanceID = cfg.ServerInstanceID assignment.RunEndpointID = cfg.RunEndpointID assignment.TargetKey = "current-db" assignment.InputRef = "input://plugin-query-poll/server-data-query-missing/members" assignment.ExecutionInput.WorkspaceScope = cfg.ComponentKey assignment.ExecutionInput.PluginID = cfg.PluginID assignment.ExecutionInput.RemoteAdapterKey = "current-db" assignment.ExecutionInput.RemoteAdapterKind = "database" assignment.ExecutionInput.Inputs = map[string]string{"sqlRef": "sql/members.sql", "maxRows": "2"} if result := worker.executeAssignment(context.Background(), assignment); result.ErrorCode != "data_target_not_declared" { t.Fatalf("expected missing target failure, got %+v", result) } } func TestMaterializeSQLiteSnapshotDataTargetRejectsUnboundSourceRoot(t *testing.T) { target := protocol.RunAutonomousDataTarget{Key: "current-db", Kind: "sqlite.snapshot", TransportKey: "current-db", SourceRootKey: "server-root", SourcePath: "Saved/SaveFiles/current.db", WorkspaceKey: "databases/current-db", RefreshPolicy: "on-demand-snapshot", MaxBytes: 128 * 1024 * 1024} _, err := materializeSQLiteSnapshotDataTarget(context.Background(), t.TempDir(), "server-data", "run-local", target, map[string]string{}) if err == nil || dataTargetErrorCode(err) != "data_target_source_unbound" { t.Fatalf("expected unbound source root rejection, got %v", err) } }