diff options
| author | Fuwn <[email protected]> | 2026-02-27 15:49:45 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-02-27 15:49:45 -0800 |
| commit | cace626823320be8702d9015e7a7fe29b8f206e6 (patch) | |
| tree | ee5c1fc21a9f8faa54f1daebad6e3dcead136b6e /internal/ingest/client_retry_test.go | |
| parent | docs: clarify mode comparison table wording (diff) | |
| download | plutia-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.go | 108 |
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) + } +} |