76 lines
3.4 KiB
Go
76 lines
3.4 KiB
Go
package orbit
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
)
|
|
|
|
func TestBatchSDKWireValidationAndNoAutomaticRetry(t *testing.T) {
|
|
id := strings.Repeat("1", 32)
|
|
var calls atomic.Int32
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
calls.Add(1)
|
|
if r.URL.Path != "/v1/tasks:batch" || r.Method != "POST" || r.Header.Get("Authorization") != "Bearer test-token" || r.Header.Get("Idempotency-Key") != "" {
|
|
t.Error("invalid batch transport")
|
|
}
|
|
raw, _ := io.ReadAll(r.Body)
|
|
if !strings.Contains(string(raw), `"idempotency_key":"one"`) || !strings.Contains(string(raw), `9007199254740993`) {
|
|
t.Error("identity/precision lost")
|
|
}
|
|
if calls.Load() == 1 {
|
|
w.WriteHeader(503)
|
|
fmt.Fprintf(w, `{"request_id":%q,"error":{"code":"UNAVAILABLE","message":"retry original identities"}}`, id)
|
|
return
|
|
}
|
|
fmt.Fprintf(w, `{"request_id":%q,"data":{"items":[{"index":0,"status":"ACCEPTED","receipt":{"task_id":%q,"queue_id":%q,"revision":"9007199254740993","created_at":"2026-09-17T00:00:00Z","deduplicated":true}}]}}`, id, id, id)
|
|
}))
|
|
defer srv.Close()
|
|
c, _ := NewClient(srv.URL, Options{Token: "test-token"})
|
|
in := SubmitTasksRequest{Items: []SubmitTasksItem{{IdempotencyKey: "one", Task: SubmitTaskRequest{QueueID: id, Payload: json.RawMessage(`{"n":9007199254740993}`)}}}}
|
|
if _, err := c.SubmitTasks(context.Background(), in); err == nil || calls.Load() != 1 {
|
|
t.Fatal("request silently retried", err)
|
|
}
|
|
out, err := c.SubmitTasks(context.Background(), in)
|
|
if err != nil || out.Items[0].Receipt.Revision != 9007199254740993 || !out.Items[0].Receipt.Deduplicated {
|
|
t.Fatal(out, err)
|
|
}
|
|
for _, bad := range []SubmitTasksRequest{{}, {Items: make([]SubmitTasksItem, 33)}, {Items: []SubmitTasksItem{in.Items[0], in.Items[0]}}, {Items: []SubmitTasksItem{{IdempotencyKey: " "}}}} {
|
|
if _, err := c.SubmitTasks(context.Background(), bad); err == nil {
|
|
t.Fatal("accepted invalid batch")
|
|
}
|
|
}
|
|
in.Items[0].Task.Payload = json.RawMessage(`"` + strings.Repeat("x", 512<<10) + `"`)
|
|
if _, err := c.SubmitTasks(context.Background(), in); err == nil || calls.Load() != 2 {
|
|
t.Fatal("unbounded batch reached network")
|
|
}
|
|
}
|
|
|
|
func TestBatchSDKRejectsIncompleteAndContradictoryReceipts(t *testing.T) {
|
|
id := strings.Repeat("1", 32)
|
|
for _, data := range []string{
|
|
`{"items":[]}`,
|
|
`{"items":[{"index":1,"status":"REJECTED","error":{"code":"INVALID_ARGUMENT","message":"bad"}}]}`,
|
|
`{"items":[{"index":0,"status":"UNKNOWN","error":{"code":"INVALID_ARGUMENT","message":"bad"}}]}`,
|
|
`{"items":[{"index":0,"status":"REJECTED","error":{"code":"UNAVAILABLE","message":"unknown"}}]}`,
|
|
`{"items":[{"index":0,"status":"NOT_ATTEMPTED","error":{"code":"UNAVAILABLE","message":"bad"}}]}`,
|
|
`{"items":[{"index":0,"status":"ACCEPTED"}]}`,
|
|
`{"items":[{"index":0,"status":"REJECTED","receipt":{},"error":{"code":"QUEUE_FULL","message":"full"}}]}`,
|
|
`{"items":[{"index":0,"status":"NEW_STATUS"}]}`,
|
|
} {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, `{"request_id":%q,"data":%s}`, id, data) }))
|
|
c, _ := NewClient(srv.URL, Options{Token: "test-token"})
|
|
out, err := c.SubmitTasks(context.Background(), SubmitTasksRequest{Items: []SubmitTasksItem{{IdempotencyKey: "one", Task: SubmitTaskRequest{QueueID: id}}}})
|
|
srv.Close()
|
|
if err == nil || len(out.Items) != 0 {
|
|
t.Fatal("malformed response exposed as success", out, data)
|
|
}
|
|
}
|
|
}
|