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
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:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user