Files
sub2api/backend/internal/service/account_test_service_kiro_test.go
T
2026-04-30 14:02:05 +08:00

318 lines
11 KiB
Go

//go:build unit
package service
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/require"
)
func TestAccountTestService_KiroUsesKiroUpstreamInsteadOfAnthropic(t *testing.T) {
gin.SetMode(gin.TestMode)
ctx, _ := newTestContext()
account := &Account{
ID: 1,
Name: "kiro-test",
Platform: PlatformKiro,
Type: AccountTypeOAuth,
Concurrency: 1,
Credentials: map[string]any{
"access_token": "kiro-access-token",
"profile_arn": "arn:aws:codewhisperer:us-east-1:123456789012:profile/TESTSOCIAL",
},
}
repo := &mockAccountRepoForGemini{accountsByID: map[int64]*Account{1: account}}
upstream := &queuedHTTPUpstream{
responses: []*http.Response{
newJSONResponse(http.StatusUnauthorized, `{"type":"error","error":{"type":"authentication_error","message":"Invalid bearer token"}}`),
},
}
svc := &AccountTestService{
accountRepo: repo,
kiroTokenProvider: NewKiroTokenProvider(nil, nil, nil),
httpUpstream: upstream,
tlsFPProfileService: &TLSFingerprintProfileService{},
}
err := svc.TestAccountConnection(ctx, account.ID, "gpt-4o", "", AccountTestModeDefault)
require.Error(t, err)
require.Len(t, upstream.requests, 1)
req := upstream.requests[0]
require.Equal(t, "q.us-east-1.amazonaws.com", req.URL.Host)
require.Equal(t, "/generateAssistantResponse", req.URL.Path)
require.Equal(t, "Bearer kiro-access-token", req.Header.Get("Authorization"))
require.Equal(t, "vibe", req.Header.Get("x-amzn-kiro-agent-mode"))
require.Empty(t, req.Header.Get("anthropic-version"))
require.NotContains(t, req.URL.Host, "api.anthropic.com")
}
func TestAccountTestService_Kiro429DoesNotFallbackToCodeWhispererEndpoint(t *testing.T) {
gin.SetMode(gin.TestMode)
ctx, _ := newTestContext()
account := &Account{
ID: 2,
Name: "kiro-fallback",
Platform: PlatformKiro,
Type: AccountTypeOAuth,
Concurrency: 1,
Credentials: map[string]any{
"access_token": "kiro-access-token",
"api_region": "us-west-2",
"region": "us-west-2",
"profile_arn": "arn:aws:codewhisperer:us-west-2:123456789012:profile/TESTFALLBACK",
},
}
repo := &mockAccountRepoForGemini{accountsByID: map[int64]*Account{2: account}}
upstream := &queuedHTTPUpstream{
responses: []*http.Response{
newJSONResponse(http.StatusTooManyRequests, `{"message":"slow down"}`),
},
}
svc := &AccountTestService{
accountRepo: repo,
kiroTokenProvider: NewKiroTokenProvider(nil, nil, nil),
httpUpstream: upstream,
tlsFPProfileService: &TLSFingerprintProfileService{},
}
err := svc.TestAccountConnection(ctx, account.ID, "claude-sonnet-4-6", "", AccountTestModeDefault)
require.Error(t, err)
require.Len(t, upstream.requests, 1)
require.Equal(t, "q.us-west-2.amazonaws.com", upstream.requests[0].URL.Host)
require.Empty(t, upstream.requests[0].Header.Get("X-Amz-Target"))
require.Contains(t, err.Error(), "API returned 429")
}
func TestAccountTestService_KiroIDCWithoutProfileArnOmitsProfileArnAndUsesDefaultRuntimeRegion(t *testing.T) {
gin.SetMode(gin.TestMode)
ctx, _ := newTestContext()
account := &Account{
ID: 5,
Name: "kiro-idc-default-region",
Platform: PlatformKiro,
Type: AccountTypeOAuth,
Concurrency: 1,
Credentials: map[string]any{
"access_token": "kiro-access-token",
"auth_method": "idc",
"provider": "AWS",
"region": "ap-northeast-2",
"start_url": "https://d-example.awsapps.com/start",
},
}
repo := &mockAccountRepoForGemini{accountsByID: map[int64]*Account{5: account}}
upstream := &queuedHTTPUpstream{
responses: []*http.Response{
newJSONResponse(http.StatusUnauthorized, `{"type":"error","error":{"message":"Invalid bearer token"}}`),
},
}
svc := &AccountTestService{
accountRepo: repo,
kiroTokenProvider: NewKiroTokenProvider(nil, nil, nil),
httpUpstream: upstream,
tlsFPProfileService: &TLSFingerprintProfileService{},
}
err := svc.TestAccountConnection(ctx, account.ID, "claude-sonnet-4-6", "", AccountTestModeDefault)
require.Error(t, err)
require.Len(t, upstream.requests, 1)
require.Equal(t, "q.us-east-1.amazonaws.com", upstream.requests[0].URL.Host)
body, readErr := io.ReadAll(upstream.requests[0].Body)
require.NoError(t, readErr)
require.NotContains(t, string(body), `"profileArn":`)
}
func TestAccountTestService_KiroInvalidModelErrorPassthrough(t *testing.T) {
gin.SetMode(gin.TestMode)
ctx, _ := newTestContext()
account := &Account{
ID: 6,
Name: "kiro-invalid-model",
Platform: PlatformKiro,
Type: AccountTypeOAuth,
Concurrency: 1,
Credentials: map[string]any{
"access_token": "kiro-access-token",
"profile_arn": "arn:aws:codewhisperer:us-west-2:123456789012:profile/TESTINVALIDMODEL",
},
}
repo := &mockAccountRepoForGemini{accountsByID: map[int64]*Account{6: account}}
upstream := &queuedHTTPUpstream{
responses: []*http.Response{
newJSONResponse(http.StatusBadRequest, `{"message":"Invalid model ID. Please select a different model to continue.","reason":"INVALID_MODEL_ID"}`),
},
}
svc := &AccountTestService{
accountRepo: repo,
kiroTokenProvider: NewKiroTokenProvider(nil, nil, nil),
httpUpstream: upstream,
tlsFPProfileService: &TLSFingerprintProfileService{},
}
err := svc.TestAccountConnection(ctx, account.ID, "claude-opus-4-6", "", AccountTestModeDefault)
require.Error(t, err)
require.Equal(t, `API returned 400: {"message":"Invalid model ID. Please select a different model to continue.","reason":"INVALID_MODEL_ID"}`, err.Error())
}
func TestAccountTestService_KiroInvalidModelDoesNotRefreshProfileArnOrRetry(t *testing.T) {
gin.SetMode(gin.TestMode)
ctx, _ := newTestContext()
account := &Account{
ID: 7,
Name: "kiro-invalid-model-refresh",
Platform: PlatformKiro,
Type: AccountTypeOAuth,
Concurrency: 1,
Credentials: map[string]any{
"access_token": "kiro-access-token",
"profile_arn": "arn:aws:codewhisperer:us-east-1:123456789012:profile/STALE",
},
}
repo := &mockAccountRepoForGemini{accountsByID: map[int64]*Account{7: account}}
upstream := &queuedHTTPUpstream{
responses: []*http.Response{
newJSONResponse(http.StatusBadRequest, `{"message":"Invalid model ID. Please select a different model to continue.","reason":"INVALID_MODEL_ID"}`),
},
}
svc := &AccountTestService{
accountRepo: repo,
kiroTokenProvider: NewKiroTokenProvider(nil, nil, nil),
httpUpstream: upstream,
tlsFPProfileService: &TLSFingerprintProfileService{},
}
err := svc.TestAccountConnection(ctx, account.ID, "claude-opus-4-6", "", AccountTestModeDefault)
require.Error(t, err)
require.Contains(t, err.Error(), "API returned 400")
require.Len(t, upstream.requests, 1)
firstBody, readErr := io.ReadAll(upstream.requests[0].Body)
require.NoError(t, readErr)
require.Contains(t, string(firstBody), `"profileArn":"arn:aws:codewhisperer:us-east-1:123456789012:profile/STALE"`)
require.Equal(t, "arn:aws:codewhisperer:us-east-1:123456789012:profile/STALE", account.GetCredential("profile_arn"))
}
func TestAccountTestService_KiroPreferredEndpointIsIgnored(t *testing.T) {
gin.SetMode(gin.TestMode)
ctx, _ := newTestContext()
account := &Account{
ID: 6,
Name: "kiro-preferred-endpoint",
Platform: PlatformKiro,
Type: AccountTypeOAuth,
Concurrency: 1,
Credentials: map[string]any{
"access_token": "kiro-access-token",
"api_region": "us-west-2",
"profile_arn": "arn:aws:codewhisperer:us-west-2:123456789012:profile/PREFERRED",
"preferred_endpoint": "codewhisperer",
},
}
repo := &mockAccountRepoForGemini{accountsByID: map[int64]*Account{6: account}}
upstream := &queuedHTTPUpstream{
responses: []*http.Response{
newJSONResponse(http.StatusUnauthorized, `{"type":"error","error":{"message":"Invalid bearer token"}}`),
},
}
svc := &AccountTestService{
accountRepo: repo,
kiroTokenProvider: NewKiroTokenProvider(nil, nil, nil),
httpUpstream: upstream,
tlsFPProfileService: &TLSFingerprintProfileService{},
}
err := svc.TestAccountConnection(ctx, account.ID, "claude-sonnet-4-6", "", AccountTestModeDefault)
require.Error(t, err)
require.Len(t, upstream.requests, 1)
require.Equal(t, "q.us-west-2.amazonaws.com", upstream.requests[0].URL.Host)
require.Empty(t, upstream.requests[0].Header.Get("X-Amz-Target"))
}
func TestBuildKiroPayloadForAccount_KiroBuilderIDWithoutProfileArnOmitsProfileArn(t *testing.T) {
account := &Account{
ID: 3,
Name: "kiro-builder-id",
Platform: PlatformKiro,
Type: AccountTypeOAuth,
Credentials: map[string]any{
"auth_method": "idc",
"provider": "BuilderId",
"region": "us-east-1",
"client_id": "builder-client-id",
},
}
testPayload, err := createTestPayload("claude-sonnet-4-6")
require.NoError(t, err)
payloadBytes, err := json.Marshal(testPayload)
require.NoError(t, err)
kiroPayload, err := buildKiroPayloadForAccount(context.Background(), account, payloadBytes, "claude-sonnet-4-6", "kiro-access-token", "claude-sonnet-4-6", nil)
require.NoError(t, err)
require.NotContains(t, string(kiroPayload), `"profileArn":`)
}
func TestBuildKiroPayloadForAccount_KiroBuilderIDUsesCredentialProfileArn(t *testing.T) {
account := &Account{
ID: 33,
Name: "kiro-builder-id-cached",
Platform: PlatformKiro,
Type: AccountTypeOAuth,
Credentials: map[string]any{
"auth_method": "builder-id",
"provider": "BuilderId",
"region": "us-east-1",
"client_id": "builder-client-id",
"profile_arn": "arn:aws:codewhisperer:us-east-1:123456789012:profile/CACHED",
},
}
testPayload, err := createTestPayload("claude-sonnet-4-6")
require.NoError(t, err)
payloadBytes, err := json.Marshal(testPayload)
require.NoError(t, err)
kiroPayload, err := buildKiroPayloadForAccount(context.Background(), account, payloadBytes, "claude-sonnet-4-6", "kiro-access-token", "claude-sonnet-4-6", nil)
require.NoError(t, err)
require.Contains(t, string(kiroPayload), `"profileArn":"arn:aws:codewhisperer:us-east-1:123456789012:profile/CACHED"`)
}
func TestBuildKiroPayloadForAccount_KiroEnterpriseIDCOmitsMissingProfileArn(t *testing.T) {
account := &Account{
ID: 4,
Name: "kiro-enterprise-idc",
Platform: PlatformKiro,
Type: AccountTypeOAuth,
Credentials: map[string]any{
"auth_method": "idc",
"provider": "AWS",
"region": "us-east-1",
"client_id": "enterprise-client-id",
"start_url": "https://d-example.awsapps.com/start",
},
}
testPayload, err := createTestPayload("claude-sonnet-4-6")
require.NoError(t, err)
payloadBytes, err := json.Marshal(testPayload)
require.NoError(t, err)
kiroPayload, err := buildKiroPayloadForAccount(context.Background(), account, payloadBytes, "claude-sonnet-4-6", "kiro-access-token", "claude-sonnet-4-6", nil)
require.NoError(t, err)
require.NotContains(t, string(kiroPayload), `"profileArn":`)
}