package reconcile import ( "testing" "sub2api-cn-relay-manager/internal/host/sub2api" "sub2api-cn-relay-manager/internal/pack" "sub2api-cn-relay-manager/internal/store/sqlite" ) func TestAccountIDFromProbeSummary(t *testing.T) { t.Parallel() accountID, err := AccountIDFromProbeSummary(`{"account_id":" account_1 "}`) if err != nil { t.Fatalf("AccountIDFromProbeSummary() error = %v", err) } if accountID != "account_1" { t.Fatalf("accountID = %q, want account_1", accountID) } if _, err := AccountIDFromProbeSummary(`{`); err == nil { t.Fatal("AccountIDFromProbeSummary() error = nil, want JSON decode error") } blank, err := AccountIDFromProbeSummary("") if err != nil { t.Fatalf("AccountIDFromProbeSummary(blank) error = %v", err) } if blank != "" { t.Fatalf("blank accountID = %q, want empty", blank) } } func TestDiffManagedResourcesCountsMissingAndExtra(t *testing.T) { t.Parallel() missing, extra := DiffManagedResources( []sqlite.ManagedResource{ {ResourceType: "group", HostResourceID: "group_1"}, {ResourceType: "account", HostResourceID: "account_1"}, }, sub2api.ManagedResourceSnapshot{ Groups: []sub2api.NamedResource{{ID: "group_1"}}, Accounts: []sub2api.NamedResource{{ID: "account_2"}}, }, ) if missing != 1 || extra != 1 { t.Fatalf("DiffManagedResources() = (%d, %d), want (1, 1)", missing, extra) } } func TestDeriveHealthyAccessStatus(t *testing.T) { t.Parallel() tests := []struct { name string closureType string want string }{ {name: "subscription", closureType: accessModeSubscription, want: accessStatusSubscriptionReady}, {name: "self-service", closureType: accessModeSelfService, want: accessStatusSelfServiceReady}, {name: "unknown", closureType: "other", want: "unknown"}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { if got := DeriveHealthyAccessStatus(tc.closureType); got != tc.want { t.Fatalf("DeriveHealthyAccessStatus(%q) = %q, want %q", tc.closureType, got, tc.want) } }) } } func TestBuildManagedResourceListRequestUsesModeSpecificNames(t *testing.T) { t.Parallel() provider := pack.ProviderManifest{ProviderID: "deepseek"} subscriptionReq := buildManagedResourceListRequest(provider, accessModeSubscription) if subscriptionReq.PlanName == "" { t.Fatal("subscription PlanName = empty, want mode-specific plan") } if subscriptionReq.AccountNamePrefix != "deepseek-" { t.Fatalf("subscription AccountNamePrefix = %q, want deepseek-", subscriptionReq.AccountNamePrefix) } selfServiceReq := buildManagedResourceListRequest(provider, accessModeSelfService) if selfServiceReq.PlanName != "" { t.Fatalf("selfService PlanName = %q, want empty", selfServiceReq.PlanName) } if selfServiceReq.GroupName == subscriptionReq.GroupName { t.Fatalf("GroupName = %q, want different names per access mode", selfServiceReq.GroupName) } } func TestAccountValidationStatusTreatsResponsesRaceAsWarning(t *testing.T) { t.Parallel() status := accountValidationStatus(sub2api.ProbeResult{ OK: false, Status: "failed", Message: "账号本身可正常使用,但当前测试接口仅支持 Responses API 路径。请直接通过实际 API 调用验证。", }, true) if status != accountStatusWarning { t.Fatalf("accountValidationStatus() = %q, want %q", status, accountStatusWarning) } } func TestAccountValidationStatusTreatsTransientProbeFailureAsWarning(t *testing.T) { t.Parallel() status := accountValidationStatus(sub2api.ProbeResult{ OK: false, Status: "failed", Message: "API returned 503: upstream service unavailable, retry later", }, true) if status != accountStatusWarning { t.Fatalf("accountValidationStatus() = %q, want %q", status, accountStatusWarning) } } func TestNormalizedUniqueAccountIDs(t *testing.T) { t.Parallel() got := normalizedUniqueAccountIDs([]string{" account_1 ", "", "account_2", "account_1", " "}) want := []string{"account_1", "account_2"} if len(got) != len(want) { t.Fatalf("normalizedUniqueAccountIDs() len = %d, want %d; values = %v", len(got), len(want), got) } for i := range want { if got[i] != want[i] { t.Fatalf("normalizedUniqueAccountIDs()[%d] = %q, want %q; values = %v", i, got[i], want[i], got) } } } func TestAccessClosureTypeReturnsLatestTrimmedType(t *testing.T) { t.Parallel() if got := accessClosureType(nil); got != "" { t.Fatalf("accessClosureType(nil) = %q, want empty", got) } got := accessClosureType([]sqlite.AccessClosureRecord{ {ClosureType: " subscription "}, {ClosureType: " self_service "}, }) if got != accessModeSelfService { t.Fatalf("accessClosureType() = %q, want %q", got, accessModeSelfService) } } func TestClassifyHistoricalAccountNoiseOnlyKeepsPrefixedOutOfBatchAccounts(t *testing.T) { t.Parallel() got := classifyHistoricalAccountNoise( []sqlite.ManagedResource{ {ResourceType: "account", HostResourceID: "account_1"}, {ResourceType: "group", HostResourceID: "group_1"}, }, []sub2api.NamedResource{ {ID: "account_1", Name: "deepseek-01"}, {ID: "account_2", Name: "deepseek-02"}, {ID: "account_3", Name: "other-03"}, {ID: " ", Name: "deepseek-04"}, }, "deepseek-", ) if len(got) != 1 { t.Fatalf("classifyHistoricalAccountNoise() len = %d, want 1; values = %+v", len(got), got) } if got[0].ID != "account_2" || got[0].Name != "deepseek-02" { t.Fatalf("classifyHistoricalAccountNoise()[0] = %+v, want account_2/deepseek-02", got[0]) } } func TestGatewayAccessReadyRequiresCompletionSuccess(t *testing.T) { t.Parallel() if gatewayAccessReady(sub2api.GatewayAccessResult{OK: true, HasExpectedModel: true}) { t.Fatal("gatewayAccessReady() = true, want false without completion success") } if !gatewayAccessReady(sub2api.GatewayAccessResult{OK: true, HasExpectedModel: true, CompletionOK: true}) { t.Fatal("gatewayAccessReady() = false, want true when gateway and completion are ready") } }