Feat v0.7.11 query http ergonomics (#65)
All checks were successful
CI/CD / test-frontend (push) Has been skipped
CI/CD / e2e-smoke (push) Has been skipped
CI/CD / test-runners (push) Has been skipped
CI/CD / test-go-pg (push) Successful in 2m53s
CI/CD / test-sqlite (push) Successful in 2m57s
CI/CD / build-and-deploy (push) Successful in 1m13s
CI/CD / detect-changes (push) Successful in 3s

Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #65.
This commit is contained in:
2026-04-02 23:32:17 +00:00
committed by xcaliber
parent f06c6c954b
commit c2d52f50c5
8 changed files with 924 additions and 77 deletions

View File

@@ -430,3 +430,257 @@ func TestDBQuerySearchLikeInvalidColumn(t *testing.T) {
}
}
// ── db.count ────────────────────────────────
func TestDBCount_Basic(t *testing.T) {
db, cfg := newTestDB(t)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id) VALUES ('a', 'one', 'u1')`)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id) VALUES ('b', 'two', 'u2')`)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id) VALUES ('c', 'three', 'u1')`)
result, err := execScript(t, cfg, `n = db.count("logs")`)
if err != nil {
t.Fatalf("script error: %v", err)
}
n, ok := result.Globals["n"].(starlark.Int)
if !ok {
t.Fatalf("n is %T, want starlark.Int", result.Globals["n"])
}
v, _ := n.Int64()
if v != 3 {
t.Errorf("got %d, want 3", v)
}
}
func TestDBCount_WithFilters(t *testing.T) {
db, cfg := newTestDB(t)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id) VALUES ('a', 'one', 'u1')`)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id) VALUES ('b', 'two', 'u2')`)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id) VALUES ('c', 'three', 'u1')`)
result, err := execScript(t, cfg, `n = db.count("logs", filters={"user_id": "u1"})`)
if err != nil {
t.Fatalf("script error: %v", err)
}
n, ok := result.Globals["n"].(starlark.Int)
if !ok {
t.Fatalf("n is %T, want starlark.Int", result.Globals["n"])
}
v, _ := n.Int64()
if v != 2 {
t.Errorf("got %d, want 2", v)
}
}
func TestDBCount_EmptyTable(t *testing.T) {
_, cfg := newTestDB(t)
result, err := execScript(t, cfg, `n = db.count("logs")`)
if err != nil {
t.Fatalf("script error: %v", err)
}
n, ok := result.Globals["n"].(starlark.Int)
if !ok {
t.Fatalf("n is %T, want starlark.Int", result.Globals["n"])
}
v, _ := n.Int64()
if v != 0 {
t.Errorf("got %d, want 0", v)
}
}
// ── db.aggregate ────────────────────────────
func TestDBAggregate_Sum(t *testing.T) {
db, cfg := newTestDB(t)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id, count) VALUES ('a', 'one', 'u1', 10)`)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id, count) VALUES ('b', 'two', 'u2', 20)`)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id, count) VALUES ('c', 'three', 'u1', 30)`)
result, err := execScript(t, cfg, `val = db.aggregate("logs", "count", "sum")`)
if err != nil {
t.Fatalf("script error: %v", err)
}
val, ok := result.Globals["val"].(starlark.Int)
if !ok {
t.Fatalf("val is %T, want starlark.Int", result.Globals["val"])
}
v, _ := val.Int64()
if v != 60 {
t.Errorf("got %d, want 60", v)
}
}
func TestDBAggregate_Avg(t *testing.T) {
db, cfg := newTestDB(t)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id, count) VALUES ('a', 'one', 'u1', 10)`)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id, count) VALUES ('b', 'two', 'u2', 20)`)
result, err := execScript(t, cfg, `val = db.aggregate("logs", "count", "avg")`)
if err != nil {
t.Fatalf("script error: %v", err)
}
// SQLite AVG of two integers (10+20)/2 = 15.0 → returned as int since 15.0 == int64(15)
val, ok := result.Globals["val"].(starlark.Int)
if !ok {
t.Fatalf("val is %T, want starlark.Int", result.Globals["val"])
}
v, _ := val.Int64()
if v != 15 {
t.Errorf("got %d, want 15", v)
}
}
func TestDBAggregate_MinMax(t *testing.T) {
db, cfg := newTestDB(t)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id, count) VALUES ('a', 'one', 'u1', 5)`)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id, count) VALUES ('b', 'two', 'u2', 50)`)
result, err := execScript(t, cfg, `
mn = db.aggregate("logs", "count", "min")
mx = db.aggregate("logs", "count", "max")
`)
if err != nil {
t.Fatalf("script error: %v", err)
}
mn, _ := result.Globals["mn"].(starlark.Int)
mx, _ := result.Globals["mx"].(starlark.Int)
mnV, _ := mn.Int64()
mxV, _ := mx.Int64()
if mnV != 5 {
t.Errorf("min: got %d, want 5", mnV)
}
if mxV != 50 {
t.Errorf("max: got %d, want 50", mxV)
}
}
func TestDBAggregate_WithFilters(t *testing.T) {
db, cfg := newTestDB(t)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id, count) VALUES ('a', 'one', 'u1', 10)`)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id, count) VALUES ('b', 'two', 'u2', 20)`)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id, count) VALUES ('c', 'three', 'u1', 30)`)
result, err := execScript(t, cfg, `val = db.aggregate("logs", "count", "sum", filters={"user_id": "u1"})`)
if err != nil {
t.Fatalf("script error: %v", err)
}
val, ok := result.Globals["val"].(starlark.Int)
if !ok {
t.Fatalf("val is %T, want starlark.Int", result.Globals["val"])
}
v, _ := val.Int64()
if v != 40 {
t.Errorf("got %d, want 40", v)
}
}
func TestDBAggregate_EmptyResult(t *testing.T) {
_, cfg := newTestDB(t)
result, err := execScript(t, cfg, `val = db.aggregate("logs", "count", "sum")`)
if err != nil {
t.Fatalf("script error: %v", err)
}
if result.Globals["val"] != starlark.None {
t.Errorf("expected None for empty table, got %v", result.Globals["val"])
}
}
func TestDBAggregate_InvalidOp(t *testing.T) {
_, cfg := newTestDB(t)
_, err := execScript(t, cfg, `val = db.aggregate("logs", "count", "median")`)
if err == nil {
t.Fatal("expected error for invalid op")
}
if !strings.Contains(err.Error(), "invalid op") {
t.Errorf("unexpected error: %v", err)
}
}
func TestDBAggregate_InvalidColumn(t *testing.T) {
_, cfg := newTestDB(t)
_, err := execScript(t, cfg, `val = db.aggregate("logs", "bad name", "sum")`)
if err == nil {
t.Fatal("expected error for invalid column name")
}
if !strings.Contains(err.Error(), "invalid column name") {
t.Errorf("unexpected error: %v", err)
}
}
// ── db.query_batch ──────────────────────────
func TestDBQueryBatch_Basic(t *testing.T) {
db, cfg := newTestDB(t)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id) VALUES ('a', 'one', 'u1')`)
db.Exec(`INSERT INTO ext_test_ext_logs (id, message, user_id) VALUES ('b', 'two', 'u2')`)
result, err := execScript(t, cfg, `
results = db.query_batch([
{"table": "logs", "filters": {"user_id": "u1"}},
{"table": "logs", "filters": {"user_id": "u2"}},
])
`)
if err != nil {
t.Fatalf("script error: %v", err)
}
results, ok := result.Globals["results"].(*starlark.List)
if !ok {
t.Fatalf("results is %T, want *starlark.List", result.Globals["results"])
}
if results.Len() != 2 {
t.Fatalf("got %d result sets, want 2", results.Len())
}
// Each result set should have 1 row
for i := 0; i < 2; i++ {
rs, ok := results.Index(i).(*starlark.List)
if !ok {
t.Fatalf("result[%d] is %T, want *starlark.List", i, results.Index(i))
}
if rs.Len() != 1 {
t.Errorf("result[%d] has %d rows, want 1", i, rs.Len())
}
}
}
func TestDBQueryBatch_EmptyList(t *testing.T) {
_, cfg := newTestDB(t)
_, err := execScript(t, cfg, `results = db.query_batch([])`)
if err == nil {
t.Fatal("expected error for empty list")
}
if !strings.Contains(err.Error(), "empty") {
t.Errorf("unexpected error: %v", err)
}
}
func TestDBQueryBatch_TooMany(t *testing.T) {
_, cfg := newTestDB(t)
_, err := execScript(t, cfg, `
def run():
specs = []
for i in range(11):
specs.append({"table": "logs"})
return db.query_batch(specs)
results = run()
`)
if err == nil {
t.Fatal("expected error for >10 queries")
}
if !strings.Contains(err.Error(), "max 10") {
t.Errorf("unexpected error: %v", err)
}
}
func TestDBQueryBatch_MissingTable(t *testing.T) {
_, cfg := newTestDB(t)
_, err := execScript(t, cfg, `results = db.query_batch([{"filters": {"user_id": "u1"}}])`)
if err == nil {
t.Fatal("expected error for missing table key")
}
if !strings.Contains(err.Error(), "table") {
t.Errorf("unexpected error: %v", err)
}
}