aboutsummaryrefslogtreecommitdiff
path: root/internal/ingest/client_retry_test.go
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-02-27 15:49:45 -0800
committerFuwn <[email protected]>2026-02-27 15:49:45 -0800
commitcace626823320be8702d9015e7a7fe29b8f206e6 (patch)
treeee5c1fc21a9f8faa54f1daebad6e3dcead136b6e /internal/ingest/client_retry_test.go
parentdocs: clarify mode comparison table wording (diff)
downloadplutia-test-cace626823320be8702d9015e7a7fe29b8f206e6.tar.xz
plutia-test-cace626823320be8702d9015e7a7fe29b8f206e6.zip
fix(thin): retry DID fetches on 429 and align DID doc shape
Diffstat (limited to 'internal/ingest/client_retry_test.go')
-rw-r--r--internal/ingest/client_retry_test.go108
1 files changed, 108 insertions, 0 deletions
diff --git a/internal/ingest/client_retry_test.go b/internal/ingest/client_retry_test.go
index b019ac6..cee3280 100644
--- a/internal/ingest/client_retry_test.go
+++ b/internal/ingest/client_retry_test.go
@@ -2,6 +2,7 @@ package ingest
import (
"context"
+ "encoding/json"
"fmt"
"net/http"
"net/http/httptest"
@@ -73,3 +74,110 @@ func TestFetchExportLimitedDoesNotRetry400(t *testing.T) {
t.Fatalf("unexpected retries on 400: got attempts=%d want 1", got)
}
}
+
+func TestFetchDIDLogRetries429ThenSucceeds(t *testing.T) {
+ const did = "did:plc:retry429"
+ var attempts atomic.Int32
+
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path != "/"+did+"/log" {
+ http.NotFound(w, r)
+
+ return
+ }
+
+ n := attempts.Add(1)
+ if n <= 2 {
+ w.Header().Set("Retry-After", "0")
+ http.Error(w, "rate limited", http.StatusTooManyRequests)
+
+ return
+ }
+
+ _ = json.NewEncoder(w).Encode([]map[string]any{
+ {
+ "did": did,
+ "cid": "bafy-retry",
+ "createdAt": "2026-02-27T00:00:00Z",
+ "operation": json.RawMessage(`{"did":"did:plc:retry429","sig":"x","sigPayload":"e30","rotationKeys":["did:key:z"],"verificationMethods":{"atproto":"did:key:z"}}`),
+ },
+ })
+ }))
+
+ defer srv.Close()
+
+ client := NewClient(srv.URL, ClientOptions{
+ MaxAttempts: 5,
+ BaseDelay: time.Millisecond,
+ MaxDelay: 2 * time.Millisecond,
+ })
+ records, err := client.FetchDIDLog(context.Background(), did)
+
+ if err != nil {
+ t.Fatalf("fetch did log: %v", err)
+ }
+
+ if len(records) != 1 {
+ t.Fatalf("record count mismatch: got %d want 1", len(records))
+ }
+
+ if got := attempts.Load(); got != 3 {
+ t.Fatalf("attempt count mismatch: got %d want 3", got)
+ }
+}
+
+func TestFetchDIDLogAuditFallbackRetries429ThenSucceeds(t *testing.T) {
+ const did = "did:plc:auditretry"
+ var auditAttempts atomic.Int32
+
+ op := json.RawMessage(`{"did":"did:plc:auditretry","sig":"x","sigPayload":"e30","rotationKeys":["did:key:z"],"verificationMethods":{"atproto":"did:key:z"}}`)
+
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ switch r.URL.Path {
+ case "/" + did + "/log":
+ _ = json.NewEncoder(w).Encode([]json.RawMessage{op})
+ case "/" + did + "/log/audit":
+ n := auditAttempts.Add(1)
+ if n == 1 {
+ w.Header().Set("Retry-After", "0")
+ http.Error(w, "rate limited", http.StatusTooManyRequests)
+
+ return
+ }
+
+ _ = json.NewEncoder(w).Encode([]map[string]any{{
+ "did": did,
+ "operation": op,
+ "cid": "bafy-audit",
+ "createdAt": "2026-02-27T00:00:00Z",
+ }})
+ default:
+ http.NotFound(w, r)
+ }
+ }))
+
+ defer srv.Close()
+
+ client := NewClient(srv.URL, ClientOptions{
+ MaxAttempts: 5,
+ BaseDelay: time.Millisecond,
+ MaxDelay: 2 * time.Millisecond,
+ })
+ records, err := client.FetchDIDLog(context.Background(), did)
+
+ if err != nil {
+ t.Fatalf("fetch did log: %v", err)
+ }
+
+ if len(records) != 1 {
+ t.Fatalf("record count mismatch: got %d want 1", len(records))
+ }
+
+ if records[0].CID != "bafy-audit" {
+ t.Fatalf("cid mismatch: got %q want %q", records[0].CID, "bafy-audit")
+ }
+
+ if got := auditAttempts.Load(); got != 2 {
+ t.Fatalf("audit attempt count mismatch: got %d want 2", got)
+ }
+}