feat: expose bounded workflow activation and signal history

This commit is contained in:
2026-09-18 07:56:18 +08:00
parent ae0d08584a
commit 17a8e0126c
35 changed files with 1756 additions and 59 deletions

View File

@@ -0,0 +1,89 @@
package orbit
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"sync/atomic"
"testing"
)
func TestWorkflowHistoryIdentityBoundsAndResponseValidation(t *testing.T) {
const id = "11111111111111111111111111111111"
const child = "22222222222222222222222222222222"
ctx := context.Background()
var calls atomic.Int32
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
calls.Add(1)
if r.Method != "GET" || r.Header.Get("Idempotency-Key") != "" || r.Header.Get("Authorization") != "Bearer test-token" {
t.Error("invalid read request")
}
if r.URL.Query().Get("cursor") != "opaque+cursor/=" || r.URL.Query().Get("limit") != "1" {
t.Error("query changed")
}
// 合法响应信封中的错误父资源,SDK不得将其作为请求资源的数据返回。
json.NewEncoder(w).Encode(map[string]any{"request_id": id, "data": map[string]any{"instance_id": child, "workflow_id": child, "step_id": child, "revision": "1", "items": []any{}}})
}))
defer server.Close()
c, err := NewClient(server.URL, Options{Token: "test-token"})
if err != nil {
t.Fatal(err)
}
for _, bad := range []string{"../other", strings.Repeat("A", 32), "a?x=b"} {
if _, err = c.ListWorkflowActivations(ctx, bad, "work", HistoryPageRequest{}); err == nil {
t.Fatal("bad workflow accepted")
}
if _, err = c.GetWorkflowActivation(ctx, id, "work", bad); err == nil {
t.Fatal("bad activation accepted")
}
if _, err = c.ListWorkflowPollRounds(ctx, id, "work", bad, HistoryPageRequest{}); err == nil {
t.Fatal("bad round scope accepted")
}
if _, err = c.ListWorkflowSignals(ctx, bad, HistoryPageRequest{}); err == nil {
t.Fatal("bad signal parent accepted")
}
if _, err = c.ListWorkflowSignalOperations(ctx, id, bad, HistoryPageRequest{}); err == nil {
t.Fatal("bad signal accepted")
}
if _, err = c.GetWorkflowSignalOperation(ctx, id, child, bad); err == nil {
t.Fatal("bad operation accepted")
}
}
for _, key := range []string{"$root", "../other", "", strings.Repeat("x", 65)} {
if _, err = c.ListWorkflowActivations(ctx, id, key, HistoryPageRequest{}); err == nil {
t.Fatal("bad key accepted")
}
}
for _, in := range []HistoryPageRequest{{Limit: -1}, {Limit: 101}, {Cursor: strings.Repeat("x", 1025)}} {
if _, err = c.ListWorkflowActivations(ctx, id, "work", in); err == nil {
t.Fatal("bad activation page")
}
if _, err = c.ListWorkflowPollRounds(ctx, id, "work", child, in); err == nil {
t.Fatal("bad round page")
}
if _, err = c.ListWorkflowSignals(ctx, id, in); err == nil {
t.Fatal("bad signal page")
}
if _, err = c.ListWorkflowSignalOperations(ctx, id, child, in); err == nil {
t.Fatal("bad operation page")
}
}
if calls.Load() != 0 {
t.Fatal("invalid arguments reached transport")
}
page := HistoryPageRequest{Limit: 1, Cursor: "opaque+cursor/="}
if _, err = c.ListWorkflowActivations(ctx, id, "work", page); err == nil {
t.Fatal("foreign activation page accepted")
}
if _, err = c.ListWorkflowPollRounds(ctx, id, "work", child, page); err == nil {
t.Fatal("foreign round page accepted")
}
if _, err = c.ListWorkflowSignals(ctx, id, page); err == nil {
t.Fatal("foreign signal page accepted")
}
if calls.Load() != 3 {
t.Fatal("unexpected automatic read retry")
}
}