Add runtime SQLite query targets

This commit is contained in:
npc0-hue
2026-09-01 17:15:09 +08:00
parent 65da73eff0
commit 163fbda394
17 changed files with 708 additions and 114 deletions
+103
View File
@@ -0,0 +1,103 @@
package runtime
import (
"context"
"database/sql"
"encoding/json"
"os"
"path/filepath"
"testing"
"browser.local/run/protocol"
)
func TestSQLiteQueryReturnsDeclaredRowsVerbatim(t *testing.T) {
root := t.TempDir()
assignment := sqliteQueryAssignment()
scope, err := NewWorkspaceResolver(root).Scope(assignment.ServerInstanceID, assignment.ExecutionInput.WorkspaceScope)
if err != nil {
t.Fatalf("create scope: %v", err)
}
createSQLiteQueryFixture(t, filepath.Join(scope, assignment.TargetKey))
writeSQLiteQueryAsset(t, scope, assignment.ExecutionInput.Inputs["sqlRef"], `
SELECT id AS steamId, name AS displayName, note AS originalText
FROM users
WHERE (:steamId IS NULL OR id = :steamId)
ORDER BY id
LIMIT :limit;
`)
result := NewSQLiteQueryExecutor(root).Execute(context.Background(), assignment)
if result.State != lifecycleResultStateSucceeded || result.ExecutionResult.Kind != "sqlite.query" || result.ExecutionResult.Checksum == "" || result.ExecutionResult.SizeBytes <= 0 {
t.Fatalf("expected successful SQLite rows, got %+v", result)
}
var payload struct {
Rows []map[string]any `json:"rows"`
}
if err := json.Unmarshal([]byte(result.ExecutionResult.Content), &payload); err != nil {
t.Fatalf("decode rows: %v", err)
}
if len(payload.Rows) != 2 || payload.Rows[0]["steamId"] != "steam-1" || payload.Rows[0]["originalText"] != "password=opaque C:/game/users.db" {
t.Fatalf("SQLite rows were not faithfully returned: %+v", payload.Rows)
}
}
func TestSQLiteQueryRejectsUnsafeAssetsAndLimitOverrun(t *testing.T) {
root := t.TempDir()
assignment := sqliteQueryAssignment()
scope, err := NewWorkspaceResolver(root).Scope(assignment.ServerInstanceID, assignment.ExecutionInput.WorkspaceScope)
if err != nil {
t.Fatalf("create scope: %v", err)
}
createSQLiteQueryFixture(t, filepath.Join(scope, assignment.TargetKey))
writeSQLiteQueryAsset(t, scope, assignment.ExecutionInput.Inputs["sqlRef"], "SELECT id AS steamId FROM users ORDER BY id")
unsafe := assignment
unsafe.ExecutionInput.Inputs = map[string]string{"sqlRef": "../outside.sql", "maxRows": "2"}
if result := NewSQLiteQueryExecutor(root).Execute(context.Background(), unsafe); result.ErrorCode != "invalid_request" {
t.Fatalf("expected unsafe asset rejection, got %+v", result)
}
limited := assignment
limited.ExecutionInput.Inputs = map[string]string{"sqlRef": assignment.ExecutionInput.Inputs["sqlRef"], "maxRows": "1"}
if result := NewSQLiteQueryExecutor(root).Execute(context.Background(), limited); result.ErrorCode != "result_limit_exceeded" {
t.Fatalf("expected result limit failure instead of truncation, got %+v", result)
}
}
func sqliteQueryAssignment() protocol.RunJobAssignment {
assignment := lifecycleAssignment(protocol.RunCapabilityRemoteRunDBSQLiteQuery)
assignment.TargetKey = "databases/current.db"
assignment.InputRef = "input://plugin-query-poll/server-1/users"
assignment.ExecutionInput.WorkspaceScope = "profile-default"
assignment.ExecutionInput.RemoteAdapterKey = "current-db"
assignment.ExecutionInput.RemoteAdapterKind = "database"
assignment.ExecutionInput.TimeoutSeconds = 10
assignment.ExecutionInput.Inputs = map[string]string{"sqlRef": "sql/users.sql", "maxRows": "2"}
return assignment
}
func createSQLiteQueryFixture(t *testing.T, path string) {
t.Helper()
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
t.Fatal(err)
}
database, err := sql.Open(sqliteSchemaProbeDriver, path)
if err != nil {
t.Fatal(err)
}
defer database.Close()
if _, err := database.Exec(`CREATE TABLE users (id TEXT PRIMARY KEY, name TEXT NOT NULL, note TEXT); INSERT INTO users(id, name, note) VALUES ('steam-1', 'Ada', 'password=opaque C:/game/users.db'), ('steam-2', 'Lin', 'unchanged');`); err != nil {
t.Fatal(err)
}
}
func writeSQLiteQueryAsset(t *testing.T, scope string, asset string, body string) {
t.Helper()
path := filepath.Join(scope, filepath.FromSlash(asset))
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, []byte(body), 0o600); err != nil {
t.Fatal(err)
}
}