Compare commits
2 commits
e19ad9960e
...
236e9ed25a
| Author | SHA1 | Date | |
|---|---|---|---|
| 236e9ed25a | |||
| c97255cb48 |
2 changed files with 47 additions and 6 deletions
|
|
@ -837,18 +837,21 @@ WITH auth AS (
|
|||
SELECT u.id, u.phone, u.username, u.first_name, u.last_name, u.created_at, u.updated_at,
|
||||
COALESCE(r.frozen, false), COALESCE(r.reason, ''), u.verified, u.scam, u.fake,
|
||||
COALESCE(EXTRACT(EPOCH FROM u.premium_expires_at), 0)::bigint,
|
||||
auth.last_active_at, auth.device_count,
|
||||
COALESCE(auth.last_active_at, '0001-01-01 00:00:00+00'::timestamptz), COALESCE(auth.device_count, 0)::int,
|
||||
COALESCE(NULLIF(u.username, ''), p.username_lower, '') AS display_username,
|
||||
COALESCE(ap.login_email, ''),
|
||||
`+accountCollectibleUsernamesColumn+` AS collectibles
|
||||
FROM users u
|
||||
JOIN auth ON auth.user_id = u.id
|
||||
-- LEFT JOIN, not JOIN: an account with no authorizations (never finished login,
|
||||
-- all sessions revoked, frozen-then-unfrozen) must still appear here, matching
|
||||
-- CountAccounts and SearchAccounts.
|
||||
LEFT JOIN auth ON auth.user_id = u.id
|
||||
LEFT JOIN account_restrictions r ON r.user_id = u.id
|
||||
LEFT JOIN peer_usernames p ON p.peer_type = 'user' AND p.peer_id = u.id AND p.editable
|
||||
LEFT JOIN account_passwords ap ON ap.user_id = u.id
|
||||
WHERE NOT u.is_bot
|
||||
AND ($1::bigint = 0 OR (auth.last_active_at, u.id) < (to_timestamp(($1::double precision) / 1000000.0), $2::bigint))
|
||||
ORDER BY auth.last_active_at DESC, u.id DESC
|
||||
AND ($1::bigint = 0 OR (COALESCE(auth.last_active_at, '0001-01-01 00:00:00+00'::timestamptz), u.id) < (to_timestamp(($1::double precision) / 1000000.0), $2::bigint))
|
||||
ORDER BY COALESCE(auth.last_active_at, '0001-01-01 00:00:00+00'::timestamptz) DESC, u.id DESC
|
||||
LIMIT $3`, beforeActiveUS, beforeID, limit+1)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("list accounts: %w", err)
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ VALUES ($1, $2, $3, 'Collector', '', $4, now(), now())`,
|
|||
userID, userID, "+1889"+suffix, editable); err != nil {
|
||||
t.Fatalf("seed user: %v", err)
|
||||
}
|
||||
// The list query joins authorizations, so an account with no device never
|
||||
// appears there at all; an authorization in turn needs its auth key to exist.
|
||||
// Give this account a device so its device_count / last_active columns are
|
||||
// exercised; an authorization needs its auth key to exist first.
|
||||
if _, err := pool.Exec(ctx, `
|
||||
INSERT INTO auth_keys (auth_key_id, body, server_salt) VALUES ($1, '\x00', 0)`, userID); err != nil {
|
||||
t.Fatalf("seed auth key: %v", err)
|
||||
|
|
@ -123,6 +123,44 @@ WHERE peer_type='user' AND peer_id=$1 AND collectible_id IS NOT NULL`, userID);
|
|||
}
|
||||
}
|
||||
|
||||
// An account with no authorizations (never finished login, all sessions revoked,
|
||||
// frozen-then-unfrozen) must still show up in the Accounts tab - it did not,
|
||||
// because ListAccounts inner-joined the authorizations aggregate.
|
||||
func TestReadStoreListAccountsIncludesAccountsWithoutSessions(t *testing.T) {
|
||||
store, pool := verificationReadStore(t)
|
||||
ctx := context.Background()
|
||||
suffix := fmt.Sprintf("%d", time.Now().UnixNano()%1_000_000)
|
||||
userID := 3_700_000_000 + time.Now().UnixNano()%1_000_000
|
||||
|
||||
t.Cleanup(func() {
|
||||
_, _ = pool.Exec(ctx, `DELETE FROM users WHERE id=$1`, userID)
|
||||
})
|
||||
if _, err := pool.Exec(ctx, `
|
||||
INSERT INTO users (id, access_hash, phone, first_name, last_name, username, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, 'Sessionless', '', '', now(), now())`,
|
||||
userID, userID, "+42777"+suffix); err != nil {
|
||||
t.Fatalf("seed user: %v", err)
|
||||
}
|
||||
// Deliberately no auth_keys / authorizations rows.
|
||||
|
||||
rows, _, err := store.ListAccounts(ctx, 0, 0, 500)
|
||||
if err != nil {
|
||||
t.Fatalf("ListAccounts: %v", err)
|
||||
}
|
||||
found := false
|
||||
for i := range rows {
|
||||
if rows[i].ID == userID {
|
||||
found = true
|
||||
if rows[i].DeviceCount != 0 {
|
||||
t.Fatalf("device count = %d, want 0 for a sessionless account", rows[i].DeviceCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("sessionless account %d absent from ListAccounts (%d rows)", userID, len(rows))
|
||||
}
|
||||
}
|
||||
|
||||
func assertCollectibles(t *testing.T, surface string, row AccountRow, editable string, want []AccountUsername) {
|
||||
t.Helper()
|
||||
if row.Username != editable {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue