From ec8c2c29135c1aa0f0ae044b58cb51369860bbaf Mon Sep 17 00:00:00 2001 From: sususu98 Date: Thu, 18 Jun 2026 22:31:50 +0800 Subject: [PATCH] test(antigravity): cover invalid-signature replay cache clear Add executor httptest for upstream 400 clearing HOME reasoning replay items, and a whitespace call_id matcher regression test for replay filtering. --- ...antigravity_reasoning_replay_clear_test.go | 66 +++++++++++++++++++ .../antigravity_reasoning_replay_test.go | 7 ++ 2 files changed, 73 insertions(+) create mode 100644 internal/runtime/executor/antigravity_reasoning_replay_clear_test.go diff --git a/internal/runtime/executor/antigravity_reasoning_replay_clear_test.go b/internal/runtime/executor/antigravity_reasoning_replay_clear_test.go new file mode 100644 index 00000000..a15f15ec --- /dev/null +++ b/internal/runtime/executor/antigravity_reasoning_replay_clear_test.go @@ -0,0 +1,66 @@ +package executor + +import ( + "context" + "io" + "net/http" + "net/http/httptest" + "testing" + "time" + + internalcache "github.com/router-for-me/CLIProxyAPI/v7/internal/cache" + "github.com/router-for-me/CLIProxyAPI/v7/internal/config" + cliproxyauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth" + cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor" + sdktranslator "github.com/router-for-me/CLIProxyAPI/v7/sdk/translator" +) + +func TestAntigravityReasoningReplayClearsOnInvalidSignature400(t *testing.T) { + internalcache.ClearAntigravityReasoningReplayCache() + t.Cleanup(internalcache.ClearAntigravityReasoningReplayCache) + + model := "gemini-3-flash-agent" + sessionKey := "session:pr3900-invalid-sig" + bad := []byte(`{"type":"thought_signature","thoughtSignature":"INVALID_REPLAY_SIGNATURE_PR3900_XXXXXXXXX","contentIndex":1,"partIndex":0}`) + if !internalcache.CacheAntigravityReasoningReplayItems(model, sessionKey, [][]byte{bad}) { + t.Fatal("failed to seed replay cache") + } + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, _ = io.ReadAll(r.Body) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusBadRequest) + _, _ = w.Write([]byte(`{"error":{"message":"Invalid thoughtSignature in model content","code":400}}`)) + })) + defer server.Close() + + exec := NewAntigravityExecutor(&config.Config{RequestRetry: 1}) + auth := &cliproxyauth.Auth{ + ID: "auth-pr3900-invalid-sig", + Attributes: map[string]string{ + "base_url": server.URL, + }, + Metadata: map[string]any{ + "access_token": "token", + "project_id": "project-1", + "expired": time.Now().Add(1 * time.Hour).Format(time.RFC3339), + }, + } + + payload := []byte(`{"sessionId":"pr3900-invalid-sig","request":{"contents":[{"role":"user","parts":[{"text":"hi"}]},{"role":"user","parts":[{"functionResponse":{"id":"id1","name":"Bash","response":{"result":"ok"}}}]}]}}`) + _, err := exec.Execute(context.Background(), auth, cliproxyexecutor.Request{ + Model: model, + Payload: payload, + }, cliproxyexecutor.Options{ + SourceFormat: sdktranslator.FormatAntigravity, + Stream: false, + }) + if err == nil { + t.Fatal("expected upstream 400 error") + } + if _, ok, errGet := internalcache.GetAntigravityReasoningReplayItemsRequired(context.Background(), model, sessionKey); errGet != nil { + t.Fatalf("get after clear: %v", errGet) + } else if ok { + t.Fatal("invalid signature 400 should clear cached replay item") + } +} diff --git a/internal/runtime/executor/antigravity_reasoning_replay_test.go b/internal/runtime/executor/antigravity_reasoning_replay_test.go index 688dd7ad..cc53da27 100644 --- a/internal/runtime/executor/antigravity_reasoning_replay_test.go +++ b/internal/runtime/executor/antigravity_reasoning_replay_test.go @@ -137,3 +137,10 @@ func TestAntigravityReplayToolCallKeysUsesNativeFunctionCallID(t *testing.T) { t.Fatalf("parallel tool calls should not share replay key: %v vs %v", keys, keys2) } } + +func TestAntigravityRequestHasMatchingFunctionResponseWhitespaceCallID(t *testing.T) { + item := gjson.Parse(`{"call_id":" "}`) + if !antigravityRequestHasMatchingFunctionResponse(nil, item) { + t.Fatal("whitespace-only call_id should be treated as empty => true") + } +}