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:
@@ -508,3 +508,178 @@ result = test()
|
||||
t.Errorf("binding signature broken: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ─── http.batch ─────────────────────────────
|
||||
|
||||
func TestHTTPBatch_Starlark_BlockedDomains(t *testing.T) {
|
||||
mod := BuildHTTPModule(context.Background(), HTTPModuleConfig{
|
||||
AllowDomains: []string{"allowed.example.com"},
|
||||
})
|
||||
|
||||
sb := New(DefaultConfig())
|
||||
modules := map[string]starlark.Value{"http": mod}
|
||||
|
||||
// Both requests target a blocked domain — should return error dicts, not throw.
|
||||
script := `
|
||||
results = http.batch([
|
||||
{"method": "GET", "url": "https://blocked.example.com/a"},
|
||||
{"method": "POST", "url": "https://blocked.example.com/b", "body": "{}", "headers": {"Content-Type": "application/json"}},
|
||||
])
|
||||
count = len(results)
|
||||
`
|
||||
result, err := sb.Exec(context.Background(), "test.star", script, modules)
|
||||
if err != nil {
|
||||
t.Fatalf("script error: %v", err)
|
||||
}
|
||||
countVal, ok := result.Globals["count"].(starlark.Int)
|
||||
if !ok {
|
||||
t.Fatalf("count is %T, want starlark.Int", result.Globals["count"])
|
||||
}
|
||||
v, _ := countVal.Int64()
|
||||
if v != 2 {
|
||||
t.Errorf("got %d results, want 2", v)
|
||||
}
|
||||
|
||||
// Verify each result is an error dict with status=0
|
||||
results := result.Globals["results"].(*starlark.List)
|
||||
for i := 0; i < results.Len(); i++ {
|
||||
d, ok := results.Index(i).(*starlark.Dict)
|
||||
if !ok {
|
||||
t.Fatalf("result[%d] is %T, want *starlark.Dict", i, results.Index(i))
|
||||
}
|
||||
statusVal, found, _ := d.Get(starlark.String("status"))
|
||||
if !found {
|
||||
t.Fatalf("result[%d] missing 'status' key", i)
|
||||
}
|
||||
statusInt, _ := starlark.AsInt32(statusVal)
|
||||
if statusInt != 0 {
|
||||
t.Errorf("result[%d] status = %d, want 0 (error)", i, statusInt)
|
||||
}
|
||||
bodyVal, _, _ := d.Get(starlark.String("body"))
|
||||
bodyStr, _ := starlark.AsString(bodyVal)
|
||||
if !strings.Contains(bodyStr, "error:") {
|
||||
t.Errorf("result[%d] body should contain 'error:', got %q", i, bodyStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPBatch_EmptyList(t *testing.T) {
|
||||
mod := BuildHTTPModule(context.Background(), HTTPModuleConfig{})
|
||||
sb := New(DefaultConfig())
|
||||
modules := map[string]starlark.Value{"http": mod}
|
||||
|
||||
_, err := sb.Exec(context.Background(), "test.star", `results = http.batch([])`, modules)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for empty list")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "empty") {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPBatch_TooMany(t *testing.T) {
|
||||
mod := BuildHTTPModule(context.Background(), HTTPModuleConfig{})
|
||||
sb := New(DefaultConfig())
|
||||
modules := map[string]starlark.Value{"http": mod}
|
||||
|
||||
script := `
|
||||
def run():
|
||||
specs = []
|
||||
for i in range(11):
|
||||
specs.append({"method": "GET", "url": "https://x.com/"})
|
||||
return http.batch(specs)
|
||||
results = run()
|
||||
`
|
||||
_, err := sb.Exec(context.Background(), "test.star", script, modules)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for >10 requests")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "max 10") {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPBatch_MissingMethod(t *testing.T) {
|
||||
mod := BuildHTTPModule(context.Background(), HTTPModuleConfig{})
|
||||
sb := New(DefaultConfig())
|
||||
modules := map[string]starlark.Value{"http": mod}
|
||||
|
||||
_, err := sb.Exec(context.Background(), "test.star",
|
||||
`results = http.batch([{"url": "https://x.com/"}])`, modules)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for missing method")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "method") {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPBatch_MissingURL(t *testing.T) {
|
||||
mod := BuildHTTPModule(context.Background(), HTTPModuleConfig{})
|
||||
sb := New(DefaultConfig())
|
||||
modules := map[string]starlark.Value{"http": mod}
|
||||
|
||||
_, err := sb.Exec(context.Background(), "test.star",
|
||||
`results = http.batch([{"method": "GET"}])`, modules)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for missing url")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "url") {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPBatch_NotDict(t *testing.T) {
|
||||
mod := BuildHTTPModule(context.Background(), HTTPModuleConfig{})
|
||||
sb := New(DefaultConfig())
|
||||
modules := map[string]starlark.Value{"http": mod}
|
||||
|
||||
_, err := sb.Exec(context.Background(), "test.star",
|
||||
`results = http.batch(["not a dict"])`, modules)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for non-dict element")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "dict") {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPBatch_MixedResults(t *testing.T) {
|
||||
// One allowed domain (will fail at DNS but not at allowlist), one blocked.
|
||||
mod := BuildHTTPModule(context.Background(), HTTPModuleConfig{
|
||||
AllowDomains: []string{"allowed.invalid"},
|
||||
})
|
||||
|
||||
sb := New(DefaultConfig())
|
||||
modules := map[string]starlark.Value{"http": mod}
|
||||
|
||||
script := `
|
||||
results = http.batch([
|
||||
{"method": "GET", "url": "https://allowed.invalid/a"},
|
||||
{"method": "GET", "url": "https://blocked.invalid/b"},
|
||||
])
|
||||
`
|
||||
result, err := sb.Exec(context.Background(), "test.star", script, modules)
|
||||
if err != nil {
|
||||
t.Fatalf("script error: %v", err)
|
||||
}
|
||||
|
||||
results := result.Globals["results"].(*starlark.List)
|
||||
if results.Len() != 2 {
|
||||
t.Fatalf("got %d results, want 2", results.Len())
|
||||
}
|
||||
|
||||
// Both should be error dicts (first = DNS failure, second = allowlist block)
|
||||
// but neither should have caused the whole batch to fail.
|
||||
for i := 0; i < 2; i++ {
|
||||
d, ok := results.Index(i).(*starlark.Dict)
|
||||
if !ok {
|
||||
t.Fatalf("result[%d] is %T, want *starlark.Dict", i, results.Index(i))
|
||||
}
|
||||
statusVal, _, _ := d.Get(starlark.String("status"))
|
||||
statusInt, _ := starlark.AsInt32(statusVal)
|
||||
if statusInt != 0 {
|
||||
t.Errorf("result[%d] expected error (status 0), got %d", i, statusInt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user