package openai import ( "crypto/sha256" "encoding/hex" "encoding/json" "net/http" "net/http/httptest" "os" "reflect" "strings" "testing" "time" "iop/apps/edge/internal/authprojection" edgeservice "iop/apps/edge/internal/service" "iop/packages/go/config" iop "iop/proto/gen/iop" ) func TestAnthropicCanonicalAndAliasRoutes(t *testing.T) { fixture := mustReadAnthropicFixture(t, "native_message.json") candidate := anthropicTestCandidate(t, "anthropic") fake := &providerFakeRunService{ poolDispatchPath: string(edgeservice.ProviderPoolPathTunnel), poolSelectedCandidate: candidate, tunnelServedTarget: "upstream-claude", } srv := NewServer(config.EdgeOpenAIConf{BearerToken: "iop-token"}, fake, nil) srv.SetModelCatalog([]config.ModelCatalogEntry{{ID: "claude-route", Providers: map[string]string{"anthropic": "upstream-claude"}}}) for _, path := range []string{"/v1/messages", "/anthropic/v1/messages"} { t.Run(path, func(t *testing.T) { fake.tunnelFrames = anthropicTunnelFrames(http.StatusOK, "application/json", fixture) req := httptest.NewRequest(http.MethodPost, path, strings.NewReader(`{"model":"claude-route","max_tokens":32,"messages":[{"role":"user","content":"hello"}]}`)) req.Header.Set("X-Api-Key", "iop-token") req.Header.Set(anthropicVersionHeader, anthropicSupportedVersion) w := httptest.NewRecorder() srv.routes().ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status: got %d body=%s", w.Code, w.Body.String()) } if got := w.Body.Bytes(); !reflect.DeepEqual(got, fixture) { t.Fatalf("native body changed:\n got: %s\nwant: %s", got, fixture) } }) } if got := fake.poolSubmitCountSnapshot(); got != 2 { t.Fatalf("provider-pool submissions: got %d, want 2", got) } } func TestAnthropicPrincipalBearerAndAPIKey(t *testing.T) { raw := "principal-token" sum := sha256.Sum256([]byte(raw)) cfg := config.EdgeOpenAIConf{PrincipalTokens: []config.OpenAIPrincipalTokenConf{{ TokenRef: "token-1", TokenHashSHA256: hex.EncodeToString(sum[:]), PrincipalRef: "principal-1", }}} srv := NewServer(cfg, &fakeRunService{}, nil) for _, headers := range []map[string]string{ {"Authorization": "Bearer " + raw}, {"X-Api-Key": raw}, {"Authorization": "Bearer " + raw, "X-Api-Key": raw}, } { req := httptest.NewRequest(http.MethodGet, "/v1/models", nil) req.Header.Set(anthropicVersionHeader, anthropicSupportedVersion) for key, value := range headers { req.Header.Set(key, value) } w := httptest.NewRecorder() srv.routes().ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("headers=%v status=%d body=%s", headers, w.Code, w.Body.String()) } } } func TestAnthropicPrincipalDualCredentialConflict(t *testing.T) { srv := NewServer(config.EdgeOpenAIConf{BearerToken: "first-secret"}, &fakeRunService{}, nil) for _, headers := range []map[string]string{ {"Authorization": "Bearer first-secret", "X-Api-Key": "second-secret"}, {"Authorization": "Basic first-secret", "X-Api-Key": "first-secret"}, } { req := httptest.NewRequest(http.MethodGet, "/anthropic/v1/models", nil) for key, value := range headers { req.Header.Set(key, value) } req.Header.Set(anthropicVersionHeader, anthropicSupportedVersion) w := httptest.NewRecorder() srv.routes().ServeHTTP(w, req) if w.Code != http.StatusUnauthorized { t.Fatalf("headers=%v status: got %d body=%s", headers, w.Code, w.Body.String()) } if strings.Contains(w.Body.String(), "first-secret") || strings.Contains(w.Body.String(), "second-secret") { t.Fatalf("credential leaked in response: %s", w.Body.String()) } } } func TestManagedAnthropicDualHeadersSameToken(t *testing.T) { now := time.Date(2026, 8, 1, 12, 0, 0, 0, time.UTC) cache := authprojection.NewCache(authprojection.DefaultLimits(), func() time.Time { return now }) if err := cache.Apply(managedProjectionFixture(1, now, time.Minute, "managed-anthropic-token")); err != nil { t.Fatal(err) } srv := NewServer(config.EdgeOpenAIConf{}, &fakeRunService{}, nil) setManagedPrincipalProjection(srv, cache) handlerCalls := 0 handler := srv.withAuth(func(w http.ResponseWriter, r *http.Request) { handlerCalls++ principal, ok := principalFromContext(r.Context()) if !ok || principal.PrincipalRef != "managed-principal" || principal.Source != principalSourceProjection { t.Fatalf("managed principal: %+v ok=%v", principal, ok) } w.WriteHeader(http.StatusNoContent) }) req := httptest.NewRequest(http.MethodGet, "/anthropic/v1/models", nil) req.Header.Set("Authorization", "Bearer managed-anthropic-token") req.Header.Set("X-Api-Key", "managed-anthropic-token") w := httptest.NewRecorder() handler(w, req) if w.Code != http.StatusNoContent || handlerCalls != 1 { t.Fatalf("status=%d handler_calls=%d body=%s", w.Code, handlerCalls, w.Body.String()) } } func TestManagedAnthropicDualHeadersMismatch(t *testing.T) { now := time.Date(2026, 8, 1, 12, 0, 0, 0, time.UTC) cache := authprojection.NewCache(authprojection.DefaultLimits(), func() time.Time { return now }) if err := cache.Apply(managedProjectionFixture(1, now, time.Minute, "managed-anthropic-token")); err != nil { t.Fatal(err) } srv := NewServer(config.EdgeOpenAIConf{BearerToken: "legacy-token"}, &fakeRunService{}, nil) setManagedPrincipalProjection(srv, cache) handlerCalls := 0 handler := srv.withAuth(func(w http.ResponseWriter, _ *http.Request) { handlerCalls++ w.WriteHeader(http.StatusNoContent) }) for _, tc := range []struct { name string bearer string apiKey string }{ {name: "mismatch", bearer: "managed-anthropic-token", apiKey: "different-token"}, {name: "unregistered", apiKey: "unregistered-token"}, {name: "legacy source", bearer: "legacy-token", apiKey: "legacy-token"}, } { t.Run(tc.name, func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/anthropic/v1/models", nil) if tc.bearer != "" { req.Header.Set("Authorization", "Bearer "+tc.bearer) } if tc.apiKey != "" { req.Header.Set("X-Api-Key", tc.apiKey) } w := httptest.NewRecorder() handler(w, req) if w.Code != http.StatusUnauthorized || !strings.Contains(w.Body.String(), `"type":"authentication_error"`) { t.Fatalf("status=%d body=%s", w.Code, w.Body.String()) } }) } if err := cache.Apply(managedProjectionFixture(2, now, time.Minute, "")); err != nil { t.Fatal(err) } revokedReq := httptest.NewRequest(http.MethodGet, "/anthropic/v1/models", nil) revokedReq.Header.Set("X-Api-Key", "managed-anthropic-token") revokedW := httptest.NewRecorder() handler(revokedW, revokedReq) if revokedW.Code != http.StatusUnauthorized { t.Fatalf("revoked status=%d body=%s", revokedW.Code, revokedW.Body.String()) } if handlerCalls != 0 { t.Fatalf("rejected managed auth reached handler %d times", handlerCalls) } } func TestManagedProjectionExpiryRejectsBeforeDispatch(t *testing.T) { clock := time.Date(2026, 8, 1, 12, 0, 0, 0, time.UTC) cache := authprojection.NewCache(authprojection.DefaultLimits(), func() time.Time { return clock }) if err := cache.Apply(managedProjectionFixture(1, clock, time.Minute, "managed-anthropic-token")); err != nil { t.Fatal(err) } srv := NewServer(config.EdgeOpenAIConf{}, &fakeRunService{}, nil) setManagedPrincipalProjection(srv, cache) handlerCalls := 0 handler := srv.withAuth(func(w http.ResponseWriter, _ *http.Request) { handlerCalls++ w.WriteHeader(http.StatusNoContent) }) clock = clock.Add(time.Minute) req := httptest.NewRequest(http.MethodGet, "/anthropic/v1/models", nil) req.Header.Set("X-Api-Key", "managed-anthropic-token") w := httptest.NewRecorder() handler(w, req) if w.Code != http.StatusUnauthorized || handlerCalls != 0 { t.Fatalf("expired status=%d handler_calls=%d body=%s", w.Code, handlerCalls, w.Body.String()) } } func TestAnthropicModelsVersionSelectsShape(t *testing.T) { srv := NewServer(config.EdgeOpenAIConf{}, &fakeRunService{}, nil) srv.SetModelCatalog([]config.ModelCatalogEntry{{ID: "claude-route", DisplayName: "Claude Route"}}) req := httptest.NewRequest(http.MethodGet, "/v1/models", nil) req.Header.Set(anthropicVersionHeader, anthropicSupportedVersion) req.Header.Set("User-Agent", "not-a-schema-selector") w := httptest.NewRecorder() srv.routes().ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status: got %d body=%s", w.Code, w.Body.String()) } var got, want any if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil { t.Fatal(err) } if err := json.Unmarshal(mustReadAnthropicFixture(t, "models_anthropic.json"), &want); err != nil { t.Fatal(err) } if !reflect.DeepEqual(got, want) { t.Fatalf("Anthropic model schema mismatch:\n got: %s\nwant: %s", w.Body.String(), mustReadAnthropicFixture(t, "models_anthropic.json")) } aliasReq := httptest.NewRequest(http.MethodGet, "/anthropic/v1/models", nil) aliasReq.Header.Set(anthropicVersionHeader, anthropicSupportedVersion) aliasW := httptest.NewRecorder() srv.routes().ServeHTTP(aliasW, aliasReq) if aliasW.Code != http.StatusOK || aliasW.Body.String() != w.Body.String() { t.Fatalf("alias schema mismatch: status=%d body=%s", aliasW.Code, aliasW.Body.String()) } for _, req := range []*http.Request{ httptest.NewRequest(http.MethodGet, "/anthropic/v1/models", nil), func() *http.Request { r := httptest.NewRequest(http.MethodGet, "/v1/models", nil) r.Header.Set(anthropicVersionHeader, "2024-01-01") return r }(), } { invalidW := httptest.NewRecorder() srv.routes().ServeHTTP(invalidW, req) if invalidW.Code != http.StatusBadRequest || !strings.Contains(invalidW.Body.String(), `"type":"invalid_request_error"`) { t.Fatalf("invalid version accepted: path=%s status=%d body=%s", req.URL.Path, invalidW.Code, invalidW.Body.String()) } } openAIReq := httptest.NewRequest(http.MethodGet, "/v1/models", nil) openAIReq.Header.Set("User-Agent", "claude-code") openAIW := httptest.NewRecorder() srv.routes().ServeHTTP(openAIW, openAIReq) if !strings.Contains(openAIW.Body.String(), `"object":"list"`) || strings.Contains(openAIW.Body.String(), `"has_more"`) { t.Fatalf("User-Agent selected the wrong model schema: %s", openAIW.Body.String()) } } func TestAnthropicCountTokensNativeLocalUnsupported(t *testing.T) { requestBody := `{"model":"claude-route","system":"Count this.","messages":[{"role":"user","content":"hello tokens"}]}` t.Run("local deterministic", func(t *testing.T) { fake := &providerFakeRunService{} srv := NewServer(config.EdgeOpenAIConf{}, fake, nil) srv.SetModelCatalog([]config.ModelCatalogEntry{{ ID: "claude-route", Providers: map[string]string{"chat": "served-chat"}, TokenCounter: &config.TokenCounterConf{Mode: config.TokenCounterDeterministic}, }}) w := serveAnthropicRequest(srv, "/v1/messages/count_tokens", requestBody) if w.Code != http.StatusOK || !strings.Contains(w.Body.String(), `"input_tokens":`) { t.Fatalf("status=%d body=%s", w.Code, w.Body.String()) } if got := fake.poolSubmitCountSnapshot(); got != 0 { t.Fatalf("local counter dispatched provider pool %d times", got) } }) t.Run("native", func(t *testing.T) { fake := &providerFakeRunService{ poolDispatchPath: string(edgeservice.ProviderPoolPathTunnel), poolSelectedCandidate: anthropicTestCandidate(t, "anthropic"), tunnelServedTarget: "upstream-claude", tunnelFrames: anthropicTunnelFrames(http.StatusOK, "application/json", []byte(`{"input_tokens":7}`)), } srv := NewServer(config.EdgeOpenAIConf{}, fake, nil) srv.SetModelCatalog([]config.ModelCatalogEntry{{ID: "claude-route", Providers: map[string]string{"anthropic": "upstream-claude"}}}) w := serveAnthropicRequest(srv, "/v1/messages/count_tokens", requestBody) if w.Code != http.StatusOK || w.Body.String() != `{"input_tokens":7}` { t.Fatalf("status=%d body=%s", w.Code, w.Body.String()) } reqs := fake.tunnelReqsSnapshot() if len(reqs) != 1 || reqs[0].Operation != string(config.OperationCountTokens) { t.Fatalf("native count request mismatch: %+v", reqs) } }) t.Run("unsupported chat profile", func(t *testing.T) { fake := &providerFakeRunService{ poolDispatchPath: string(edgeservice.ProviderPoolPathTunnel), poolSelectedCandidate: anthropicTestCandidate(t, "openai"), } srv := NewServer(config.EdgeOpenAIConf{}, fake, nil) srv.SetModelCatalog([]config.ModelCatalogEntry{{ID: "claude-route", Providers: map[string]string{"chat": "served-chat"}}}) w := serveAnthropicRequest(srv, "/v1/messages/count_tokens", requestBody) if w.Code != http.StatusBadRequest || !strings.Contains(w.Body.String(), `"type":"not_supported_error"`) { t.Fatalf("status=%d body=%s", w.Code, w.Body.String()) } if got := len(fake.tunnelReqsSnapshot()); got != 0 { t.Fatalf("unsupported count reached provider wire: %d requests", got) } }) } func serveAnthropicRequest(srv *Server, path, body string) *httptest.ResponseRecorder { req := httptest.NewRequest(http.MethodPost, path, strings.NewReader(body)) req.Header.Set(anthropicVersionHeader, anthropicSupportedVersion) w := httptest.NewRecorder() srv.routes().ServeHTTP(w, req) return w } func anthropicTestCandidate(t *testing.T, profileID string) edgeservice.ProviderPoolCandidate { t.Helper() profile, err := config.ResolveProtocolProfile(profileID, "", config.BuiltInProtocolProfileCatalog()) if err != nil { t.Fatal(err) } return edgeservice.ProviderPoolCandidate{ ActualModel: "served-model", ProviderID: profileID + "-provider", ExecutionPath: string(edgeservice.ProviderPoolPathTunnel), ProfileID: profile.ID, ProfileDriver: string(profile.Driver), ProfileCapabilities: append([]string(nil), profile.Capabilities...), ProtocolProfile: &profile, } } func anthropicTunnelFrames(status int, contentType string, chunks ...[]byte) chan *iop.ProviderTunnelFrame { frames := make(chan *iop.ProviderTunnelFrame, len(chunks)+2) frames <- &iop.ProviderTunnelFrame{ Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START, StatusCode: int32(status), Headers: map[string]string{"Content-Type": contentType, "X-Request-Id": "req_fixture"}, } for index, chunk := range chunks { frames <- &iop.ProviderTunnelFrame{ Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY, Sequence: int64(index + 1), Body: append([]byte(nil), chunk...), } } frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END, End: true} close(frames) return frames } func mustReadAnthropicFixture(t *testing.T, name string) []byte { t.Helper() body, err := os.ReadFile("testdata/anthropic/" + name) if err != nil { t.Fatal(err) } return body }