package app import ( "context" "testing" "time" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/geo-platform/tenant-api/internal/shared/auth" "github.com/geo-platform/tenant-api/internal/tenant/repository" ) type desktopAccountRepoSpy struct { markDesktopID uuid.UUID markWorkspaceID int64 markUserID int64 tombstoneActorDesktopID uuid.UUID tombstoneActorWorkspaceID int64 tombstoneActorUserID int64 } func (r *desktopAccountRepoSpy) ListByClient(context.Context, int64, uuid.UUID) ([]repository.DesktopAccount, error) { return nil, nil } func (r *desktopAccountRepoSpy) ListByUser(context.Context, int64, int64) ([]repository.DesktopAccount, error) { return nil, nil } func (r *desktopAccountRepoSpy) GetByDesktopID(context.Context, uuid.UUID, int64) (*repository.DesktopAccount, error) { return nil, nil } func (r *desktopAccountRepoSpy) GetByIdentity(context.Context, int64, uuid.UUID, string, string) (*repository.DesktopAccount, error) { return nil, nil } func (r *desktopAccountRepoSpy) Upsert(context.Context, repository.UpsertDesktopAccountParams) (*repository.DesktopAccount, error) { return nil, nil } func (r *desktopAccountRepoSpy) Patch(context.Context, repository.PatchDesktopAccountParams) (*repository.DesktopAccount, error) { return nil, nil } func (r *desktopAccountRepoSpy) Tombstone(context.Context, uuid.UUID, int64, uuid.UUID, int64) (*repository.DesktopAccount, error) { return nil, nil } func (r *desktopAccountRepoSpy) TombstoneForActor(_ context.Context, desktopID uuid.UUID, workspaceID, userID int64) (*repository.DesktopAccount, error) { r.tombstoneActorDesktopID = desktopID r.tombstoneActorWorkspaceID = workspaceID r.tombstoneActorUserID = userID now := time.Date(2026, 6, 18, 9, 45, 0, 0, time.UTC) return &repository.DesktopAccount{ DesktopID: desktopID, TenantID: 10, WorkspaceID: workspaceID, UserID: userID, Platform: "baijiahao", PlatformUID: "351884467", DisplayName: "Media Account", Health: "live", DeletedAt: &now, }, nil } func (r *desktopAccountRepoSpy) MarkDeleteRequested(_ context.Context, desktopID uuid.UUID, workspaceID, userID int64) (*repository.DesktopAccount, error) { r.markDesktopID = desktopID r.markWorkspaceID = workspaceID r.markUserID = userID now := time.Date(2026, 6, 18, 9, 30, 0, 0, time.UTC) return &repository.DesktopAccount{ DesktopID: desktopID, TenantID: 10, WorkspaceID: workspaceID, UserID: userID, Platform: "toutiaohao", PlatformUID: "122707592", DisplayName: "Media Account", Health: "live", DeleteRequestedAt: &now, }, nil } func (r *desktopAccountRepoSpy) ClearDeleteRequested(context.Context, uuid.UUID, int64, int64) (*repository.DesktopAccount, error) { return nil, nil } func TestResolveDesktopClientOnline_PresenceHitWins(t *testing.T) { now := time.Date(2026, 4, 19, 21, 30, 0, 0, time.UTC) online := resolveDesktopClientOnline(&repository.DesktopClient{ ID: uuid.New(), LastSeenAt: nil, }, true, true, now) assert.True(t, online) } func TestRequestDeleteScopesToActorUser(t *testing.T) { repo := &desktopAccountRepoSpy{} service := &DesktopAccountService{repo: repo} accountID := uuid.New() view, err := service.RequestDelete( context.Background(), auth.Actor{UserID: 42, TenantID: 10, PrimaryWorkspaceID: 7}, accountID, false, ) assert.NoError(t, err) assert.Equal(t, accountID, repo.markDesktopID) assert.Equal(t, int64(7), repo.markWorkspaceID) assert.Equal(t, int64(42), repo.markUserID) assert.NotNil(t, view) assert.NotNil(t, view.DeleteRequestedAt) } func TestResolveDesktopClientOnline_PresenceMissWithKnownPresenceIsOffline(t *testing.T) { now := time.Date(2026, 4, 19, 21, 30, 0, 0, time.UTC) lastSeen := now.Add(-desktopClientPresenceTTL / 2) online := resolveDesktopClientOnline(&repository.DesktopClient{ ID: uuid.New(), LastSeenAt: &lastSeen, }, false, true, now) assert.False(t, online) } func TestResolveDesktopClientOnline_FallsBackToRecentHeartbeatWhenPresenceUnknown(t *testing.T) { now := time.Date(2026, 4, 19, 21, 30, 0, 0, time.UTC) lastSeen := now.Add(-desktopClientPresenceTTL / 2) online := resolveDesktopClientOnline(&repository.DesktopClient{ ID: uuid.New(), LastSeenAt: &lastSeen, }, false, false, now) assert.True(t, online) } func TestResolveDesktopClientOnline_StaleHeartbeatIsOffline(t *testing.T) { now := time.Date(2026, 4, 19, 21, 30, 0, 0, time.UTC) lastSeen := now.Add(-(desktopClientPresenceTTL + time.Second)) online := resolveDesktopClientOnline(&repository.DesktopClient{ ID: uuid.New(), LastSeenAt: &lastSeen, }, false, false, now) assert.False(t, online) } func TestBuildDesktopAccountView_StoredClientOwnershipWinsOverPresence(t *testing.T) { service := &DesktopAccountService{now: func() time.Time { return time.Date(2026, 4, 19, 21, 30, 0, 0, time.UTC) }} accountID := uuid.New() staleClientID := uuid.New() activeClientID := uuid.New() lastSeen := time.Date(2026, 4, 19, 21, 30, 0, 0, time.UTC) deviceName := "Desktop MacIntel" view := service.buildDesktopAccountView( &repository.DesktopAccount{ DesktopID: accountID, ClientID: &staleClientID, Platform: "toutiaohao", PlatformUID: "platform:4181862206", DisplayName: "ireader", Health: "live", }, map[uuid.UUID]*repository.DesktopClient{ activeClientID: { ID: activeClientID, LastSeenAt: &lastSeen, DeviceName: &deviceName, }, }, map[uuid.UUID]uuid.UUID{ accountID: activeClientID, }, map[uuid.UUID]bool{ activeClientID: true, }, map[uuid.UUID]bool{ activeClientID: true, }, ) if assert.NotNil(t, view.ClientID) { assert.Equal(t, staleClientID.String(), *view.ClientID) } if assert.NotNil(t, view.ClientOnline) { assert.False(t, *view.ClientOnline) } if assert.NotNil(t, view.AccountSessionOnline) { assert.False(t, *view.AccountSessionOnline) } if assert.NotNil(t, view.TaskConsumerOnline) { assert.False(t, *view.TaskConsumerOnline) } assert.Equal(t, "4181862206", view.PlatformUID) assert.Nil(t, view.ClientLastSeenAt) assert.Nil(t, view.ClientDeviceName) } func TestBuildDesktopAccountView_FallsBackToStoredClientWhenNoPresence(t *testing.T) { now := time.Date(2026, 4, 19, 21, 30, 0, 0, time.UTC) service := &DesktopAccountService{now: func() time.Time { return now }} accountID := uuid.New() storedClientID := uuid.New() lastSeen := now.Add(-desktopClientPresenceTTL / 2) view := service.buildDesktopAccountView( &repository.DesktopAccount{ DesktopID: accountID, ClientID: &storedClientID, Platform: "toutiaohao", PlatformUID: "4181862206", DisplayName: "ireader", Health: "live", }, map[uuid.UUID]*repository.DesktopClient{ storedClientID: { ID: storedClientID, LastSeenAt: &lastSeen, }, }, nil, nil, nil, ) if assert.NotNil(t, view.ClientID) { assert.Equal(t, storedClientID.String(), *view.ClientID) } if assert.NotNil(t, view.ClientOnline) { assert.True(t, *view.ClientOnline) } assert.Equal(t, &lastSeen, view.ClientLastSeenAt) } func TestBuildDesktopAccountView_SplitsHeartbeatAccountSessionAndTaskConsumer(t *testing.T) { now := time.Date(2026, 4, 19, 21, 30, 0, 0, time.UTC) service := &DesktopAccountService{now: func() time.Time { return now }} accountID := uuid.New() clientID := uuid.New() lastSeen := now.Add(-time.Minute) deviceName := "Windows Workstation" view := service.buildDesktopAccountView( &repository.DesktopAccount{ DesktopID: accountID, ClientID: &clientID, Platform: "sohuhao", PlatformUID: "110424528162", DisplayName: "全屋定制老炮", Health: "live", }, map[uuid.UUID]*repository.DesktopClient{ clientID: { ID: clientID, LastSeenAt: &lastSeen, DeviceName: &deviceName, }, }, map[uuid.UUID]uuid.UUID{ accountID: clientID, }, map[uuid.UUID]bool{ clientID: true, }, map[uuid.UUID]bool{ clientID: false, }, ) if assert.NotNil(t, view.ClientOnline) { assert.True(t, *view.ClientOnline) } if assert.NotNil(t, view.AccountSessionOnline) { assert.True(t, *view.AccountSessionOnline) } if assert.NotNil(t, view.TaskConsumerOnline) { assert.False(t, *view.TaskConsumerOnline) } assert.Equal(t, &lastSeen, view.ClientLastSeenAt) assert.Equal(t, &deviceName, view.ClientDeviceName) } func TestShouldApplyDesktopAccountRuntimeHealth_IgnoresReportOlderThanVerifiedAt(t *testing.T) { verifiedAt := time.Date(2026, 4, 30, 9, 30, 0, 0, time.UTC) reportCheckedAt := verifiedAt.Add(-time.Minute) shouldApply := shouldApplyDesktopAccountRuntimeHealth( DesktopAccountView{ ID: uuid.NewString(), Health: "live", VerifiedAt: &verifiedAt, }, bufferedDesktopAccountHealthReport{ Health: "expired", AuthState: "expired", CheckedAt: reportCheckedAt, }, ) assert.False(t, shouldApply) } func TestShouldApplyDesktopAccountRuntimeHealth_AppliesReportAfterVerifiedAt(t *testing.T) { verifiedAt := time.Date(2026, 4, 30, 9, 30, 0, 0, time.UTC) reportCheckedAt := verifiedAt.Add(time.Minute) shouldApply := shouldApplyDesktopAccountRuntimeHealth( DesktopAccountView{ ID: uuid.NewString(), Health: "live", VerifiedAt: &verifiedAt, }, bufferedDesktopAccountHealthReport{ Health: "expired", AuthState: "expired", CheckedAt: reportCheckedAt, }, ) assert.True(t, shouldApply) }