feat: add idempotent batch task submission and recovery examples
This commit is contained in:
75
sdk/go/batch_test.go
Normal file
75
sdk/go/batch_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user