Files
tutti/services/tuttid/api/daemon_account_test.go
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

199 lines
6.7 KiB
Go

package api
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
authbridge "github.com/tutti-os/tutti/packages/auth/bridge-go"
tuttigenerated "github.com/tutti-os/tutti/services/tuttid/api/generated"
accountservice "github.com/tutti-os/tutti/services/tuttid/service/account"
)
func TestAccountLoginStatusMapsServiceStatus(t *testing.T) {
api := DaemonAPI{
AccountService: accountServiceStub{
status: authbridge.LoginStatus{
AttemptID: "attempt-1",
ExpiresAt: time.UnixMilli(123),
Status: "completed",
User: &authbridge.UserInfo{UserID: "user-1", Email: "user@example.com"},
},
},
}
response, err := api.GetAccountLoginStatus(context.Background(), tuttigenerated.GetAccountLoginStatusRequestObject{
Params: tuttigenerated.GetAccountLoginStatusParams{AttemptId: "attempt-1"},
})
if err != nil {
t.Fatal(err)
}
got, ok := response.(tuttigenerated.GetAccountLoginStatus200JSONResponse)
if !ok {
t.Fatalf("response = %T, want 200", response)
}
if got.AttemptId != "attempt-1" || got.Status != tuttigenerated.AccountLoginStatusValueCompleted || got.User == nil || got.User.UserId != "user-1" {
t.Fatalf("response = %#v", got)
}
}
func TestAccountProductSummaryMapsServiceSummary(t *testing.T) {
availableCredits := "2450.52"
expiringCredits := "100.25"
cancelAtPeriodEnd := false
api := DaemonAPI{
AccountService: accountServiceStub{
summary: accountservice.ProductSummary{
User: &authbridge.UserInfo{UserID: "user-1", Name: "Jane", Email: "jane@example.com"},
Membership: &accountservice.MembershipSummary{
TierKey: "basic",
DisplayName: "Basic",
BillingPeriod: "month",
Status: "active",
AccessStatus: "active",
CurrentPeriodEnd: "2026-08-01T00:00:00Z",
CancelAtPeriodEnd: &cancelAtPeriodEnd,
},
Credits: &accountservice.CreditsSummary{
AvailableCredits: &availableCredits,
ExpiringCreditsWithin24h: &expiringCredits,
NextExpireAt: "2026-07-07T00:00:00Z",
RefreshedAt: "2026-07-06T00:00:00Z",
},
RegistrationCreditsReward: &accountservice.RegistrationCreditsReward{
ID: "registrationCreditsToastShown:user-1:grant-1",
UserID: "user-1",
GrantNo: "grant-1",
Credits: 500,
CreatedAt: time.UnixMilli(123),
},
Links: accountservice.ProductSummaryLinks{
PlanURL: "https://tutti.sh/profile/plan",
UsageURL: "https://tutti.sh/profile/usage",
SettingsURL: "https://tutti.sh/profile/settings",
},
},
},
}
response, err := api.GetAccountProductSummary(context.Background(), tuttigenerated.GetAccountProductSummaryRequestObject{})
if err != nil {
t.Fatal(err)
}
got, ok := response.(tuttigenerated.GetAccountProductSummary200JSONResponse)
if !ok {
t.Fatalf("response = %T, want 200", response)
}
if got.User == nil || got.User.UserId != "user-1" || got.Membership == nil || got.Membership.DisplayName != "Basic" {
t.Fatalf("response = %#v", got)
}
if got.Credits == nil || got.Credits.AvailableCredits == nil || *got.Credits.AvailableCredits != "2450.52" {
t.Fatalf("credits = %#v", got.Credits)
}
if got.RegistrationCreditsReward == nil || got.RegistrationCreditsReward.Id != "registrationCreditsToastShown:user-1:grant-1" || got.RegistrationCreditsReward.Credits != 500 {
t.Fatalf("registration credits reward = %#v", got.RegistrationCreditsReward)
}
if got.Links.PlanUrl != "https://tutti.sh/profile/plan" || got.Links.UsageUrl != "https://tutti.sh/profile/usage" {
t.Fatalf("links = %#v", got.Links)
}
}
func TestAccountProductSummaryRouteIsRegistered(t *testing.T) {
availableCredits := "2450.52"
mux := http.NewServeMux()
RegisterRoutes(
mux,
NewRoutes(DaemonAPI{
AccountService: accountServiceStub{
summary: accountservice.ProductSummary{
User: &authbridge.UserInfo{UserID: "user-1", Name: "Jane", Email: "jane@example.com"},
Credits: &accountservice.CreditsSummary{AvailableCredits: &availableCredits},
Links: accountservice.ProductSummaryLinks{
PlanURL: "https://tutti.sh/profile/plan",
UsageURL: "https://tutti.sh/profile/usage",
SettingsURL: "https://tutti.sh/profile/settings",
},
},
},
}),
)
request := httptest.NewRequest(http.MethodGet, "/v1/account/product_summary", nil)
response := httptest.NewRecorder()
mux.ServeHTTP(response, request)
if response.Code != http.StatusOK {
t.Fatalf("status = %d, body = %q", response.Code, response.Body.String())
}
var body tuttigenerated.AccountProductSummaryResponse
if err := json.Unmarshal(response.Body.Bytes(), &body); err != nil {
t.Fatal(err)
}
if body.User == nil || body.User.UserId != "user-1" {
t.Fatalf("user = %#v", body.User)
}
if body.Credits == nil || body.Credits.AvailableCredits == nil || *body.Credits.AvailableCredits != "2450.52" {
t.Fatalf("credits = %#v", body.Credits)
}
if body.MembershipAccess != tuttigenerated.AccountMembershipAccessStateUnknown {
t.Fatalf("membership access = %q, want unknown", body.MembershipAccess)
}
}
func TestDismissAccountRegistrationCreditsRewardMapsRequest(t *testing.T) {
var dismissedRewardID string
api := DaemonAPI{
AccountService: accountServiceStub{
dismissedRewardID: &dismissedRewardID,
},
}
response, err := api.DismissAccountRegistrationCreditsReward(context.Background(), tuttigenerated.DismissAccountRegistrationCreditsRewardRequestObject{
Body: &tuttigenerated.DismissAccountRegistrationCreditsRewardJSONRequestBody{
RewardId: "registrationCreditsToastShown:user-1:grant-1",
},
})
if err != nil {
t.Fatal(err)
}
if _, ok := response.(tuttigenerated.DismissAccountRegistrationCreditsReward204Response); !ok {
t.Fatalf("response = %T, want 204", response)
}
if dismissedRewardID != "registrationCreditsToastShown:user-1:grant-1" {
t.Fatalf("dismissed reward id = %q", dismissedRewardID)
}
}
type accountServiceStub struct {
status authbridge.LoginStatus
summary accountservice.ProductSummary
dismissedRewardID *string
}
func (s accountServiceStub) GetProductSummary(context.Context) (accountservice.ProductSummary, error) {
return s.summary, nil
}
func (s accountServiceStub) DismissRegistrationCreditsReward(_ context.Context, rewardID string) error {
if s.dismissedRewardID != nil {
*s.dismissedRewardID = rewardID
}
return nil
}
func (accountServiceStub) GetUserInfo(context.Context) (*authbridge.UserInfo, error) {
return nil, nil
}
func (s accountServiceStub) LoginStatus(string) (authbridge.LoginStatus, error) {
return s.status, nil
}
func (accountServiceStub) Logout(context.Context) error {
return nil
}
func (accountServiceStub) StartLogin(context.Context) (accountservice.LoginStart, error) {
return accountservice.LoginStart{}, nil
}