111 lines · 5.1 KB · Go
RawDownload
package executor import (	"bytes"	"context"	"io"	"net/http"	"strings"	"testing" 	"github.com/google/uuid"	claudeauth "github.com/router-for-me/CLIProxyAPI/v7/internal/auth/claude"	"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"	"github.com/tidwall/gjson") func TestInjectClaudeDiagnosticsMatchesNativeFieldOrderAndContinuity(t *testing.T) {	t.Parallel() 	body := []byte(`{"context_management":{"edits":[{"type":"clear_thinking_20251015","keep":"all"}]},"max_tokens":1,"messages":[]}`)	testID := uuid.NewString()	auth := &cliproxyauth.Auth{ID: "credential-diagnostics-order-" + testID}	first, state := injectClaudeDiagnostics(body, auth, "session-diagnostics-order-"+testID)	wantOrder := `"context_management":{"edits":[{"type":"clear_thinking_20251015","keep":"all"}]},"diagnostics":{"previous_message_id":null},"max_tokens"`	if !bytes.Contains(first, []byte(wantOrder)) {		t.Fatalf("diagnostics field order differs from native: %s", first)	}	if got := gjson.GetBytes(first, "diagnostics.previous_message_id"); got.Type != gjson.Null {		t.Fatalf("first previous_message_id = %s, want null", got.Raw)	} 	commitClaudeDiagnostics(state, "msg_01ABCDEF0123456789ABCDEFG")	second, _ := injectClaudeDiagnostics(body, auth, "session-diagnostics-order-"+testID)	if got := gjson.GetBytes(second, "diagnostics.previous_message_id").String(); got != "msg_01ABCDEF0123456789ABCDEFG" {		t.Fatalf("second previous_message_id = %q, want committed upstream ID", got)	}} func TestClaudeExecutorDiagnosticsAdvancesAfterSuccessfulResponse(t *testing.T) {	var previousValues []gjson.Result	var betaValues []string	call := 0	transport := roundTripperFunc(func(req *http.Request) (*http.Response, error) {		body, errRead := io.ReadAll(req.Body)		if errRead != nil {			t.Fatal(errRead)		}		previousValues = append(previousValues, gjson.GetBytes(body, "diagnostics.previous_message_id"))		betas := req.Header.Get("Anthropic-Beta")		if betas == "" {			betas = strings.Join(req.Header["anthropic-beta"], ",")		}		betaValues = append(betaValues, betas)		call++		response := `{"id":"msg_diagnostics_` + string(rune('0'+call)) + `","type":"message","model":"claude-opus-5","role":"assistant","content":[{"type":"text","text":"ok"}],"usage":{"input_tokens":1,"output_tokens":1}}`		return &http.Response{StatusCode: http.StatusOK, Header: http.Header{"Content-Type": []string{"application/json"}}, Body: io.NopCloser(strings.NewReader(response)), Request: req}, nil	})	ctx := context.WithValue(context.Background(), "cliproxy.roundtripper", http.RoundTripper(transport))	deviceIDs := []string{"0000000000000000000000000000000000000000000000000000000000000000"}	testID := uuid.NewString()	auth := &cliproxyauth.Auth{		ID:         "diagnostics-live-path-" + testID,		Attributes: map[string]string{"api_key": "sk-ant-oat-diagnostics-live-path"},		Metadata: map[string]any{			"account_uuid":                        "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",			claudeauth.ClaudeDeviceIDsMetadataKey: deviceIDs,		},	}	executor := NewClaudeExecutor(&config.Config{})	request := cliproxyexecutor.Request{Model: "claude-opus-5", Payload: []byte(`{"model":"claude-opus-5","messages":[{"role":"user","content":"x"}],"max_tokens":16}`)}	options := cliproxyexecutor.Options{		SourceFormat: sdktranslator.FormatClaude,		Metadata:     map[string]any{cliproxyexecutor.ExecutionSessionMetadataKey: "diagnostics-conversation-" + testID},	}	for turn := range 2 {		if _, errExecute := executor.Execute(ctx, auth, request, options); errExecute != nil {			t.Fatalf("Execute() error = %v", errExecute)		}		if turn == 0 {			auth.Attributes["api_key"] = "sk-ant-oat-diagnostics-live-path-rotated"		}	}	if len(previousValues) != 2 || previousValues[0].Type != gjson.Null || previousValues[0].Raw != "null" {		t.Fatalf("first diagnostics value = %#v, want explicit null", previousValues)	}	if got := previousValues[1].String(); got != "msg_diagnostics_1" {		t.Fatalf("second diagnostics previous_message_id = %q, want first upstream response ID", got)	}	wantTrailer := claudeExtendedCacheTTLBeta + "," + claudeCacheDiagnosisBeta	for turn, betas := range betaValues {		if !strings.HasSuffix(betas, wantTrailer) {			t.Fatalf("turn %d Anthropic-Beta = %q, want native diagnostics trailer %q", turn+1, betas, wantTrailer)		}	}} func TestClaudeMessageIDFromSSECommitsOnlyCompletedMessage(t *testing.T) {	t.Parallel() 	complete := []byte("event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"id\":\"msg_complete\"}}\n\nevent: message_stop\ndata: {\"type\":\"message_stop\"}\n\n")	if got := claudeMessageIDFromSSE(complete); got != "msg_complete" {		t.Fatalf("completed SSE message ID = %q, want msg_complete", got)	}	incomplete := []byte(strings.Replace(string(complete), "event: message_stop\ndata: {\"type\":\"message_stop\"}\n\n", "", 1))	if got := claudeMessageIDFromSSE(incomplete); got != "" {		t.Fatalf("incomplete SSE message ID = %q, want empty", got)	}}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t