feat(server): propagate is_test_account through User model & store
User dataclass, create_user, and all SELECT lists now round-trip the new column. Value is always FALSE until Task 4 wires the register flow to the invite code's marks_as_test flag. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,7 @@ class User:
|
|||||||
is_banned: Whether user is banned.
|
is_banned: Whether user is banned.
|
||||||
ban_reason: Reason for ban (if banned).
|
ban_reason: Reason for ban (if banned).
|
||||||
force_password_reset: Whether user must reset password on next login.
|
force_password_reset: Whether user must reset password on next login.
|
||||||
|
is_test_account: True for accounts created by the soak test harness.
|
||||||
"""
|
"""
|
||||||
id: str
|
id: str
|
||||||
username: str
|
username: str
|
||||||
@@ -66,6 +67,7 @@ class User:
|
|||||||
is_banned: bool = False
|
is_banned: bool = False
|
||||||
ban_reason: Optional[str] = None
|
ban_reason: Optional[str] = None
|
||||||
force_password_reset: bool = False
|
force_password_reset: bool = False
|
||||||
|
is_test_account: bool = False
|
||||||
|
|
||||||
def is_admin(self) -> bool:
|
def is_admin(self) -> bool:
|
||||||
"""Check if user has admin role."""
|
"""Check if user has admin role."""
|
||||||
@@ -100,6 +102,7 @@ class User:
|
|||||||
"is_banned": self.is_banned,
|
"is_banned": self.is_banned,
|
||||||
"ban_reason": self.ban_reason,
|
"ban_reason": self.ban_reason,
|
||||||
"force_password_reset": self.force_password_reset,
|
"force_password_reset": self.force_password_reset,
|
||||||
|
"is_test_account": self.is_test_account,
|
||||||
}
|
}
|
||||||
if include_sensitive:
|
if include_sensitive:
|
||||||
d["password_hash"] = self.password_hash
|
d["password_hash"] = self.password_hash
|
||||||
@@ -146,6 +149,7 @@ class User:
|
|||||||
is_banned=d.get("is_banned", False),
|
is_banned=d.get("is_banned", False),
|
||||||
ban_reason=d.get("ban_reason"),
|
ban_reason=d.get("ban_reason"),
|
||||||
force_password_reset=d.get("force_password_reset", False),
|
force_password_reset=d.get("force_password_reset", False),
|
||||||
|
is_test_account=d.get("is_test_account", False),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -476,6 +476,7 @@ class UserStore:
|
|||||||
guest_id: Optional[str] = None,
|
guest_id: Optional[str] = None,
|
||||||
verification_token: Optional[str] = None,
|
verification_token: Optional[str] = None,
|
||||||
verification_expires: Optional[datetime] = None,
|
verification_expires: Optional[datetime] = None,
|
||||||
|
is_test_account: bool = False,
|
||||||
) -> Optional[User]:
|
) -> Optional[User]:
|
||||||
"""
|
"""
|
||||||
Create a new user account.
|
Create a new user account.
|
||||||
@@ -488,6 +489,7 @@ class UserStore:
|
|||||||
guest_id: Guest session ID if converting.
|
guest_id: Guest session ID if converting.
|
||||||
verification_token: Email verification token.
|
verification_token: Email verification token.
|
||||||
verification_expires: Token expiration time.
|
verification_expires: Token expiration time.
|
||||||
|
is_test_account: True for accounts created by the soak test harness.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Created User, or None if username/email already exists.
|
Created User, or None if username/email already exists.
|
||||||
@@ -497,12 +499,13 @@ class UserStore:
|
|||||||
row = await conn.fetchrow(
|
row = await conn.fetchrow(
|
||||||
"""
|
"""
|
||||||
INSERT INTO users_v2 (username, password_hash, email, role, guest_id,
|
INSERT INTO users_v2 (username, password_hash, email, role, guest_id,
|
||||||
verification_token, verification_expires)
|
verification_token, verification_expires,
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
is_test_account)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||||
RETURNING id, username, email, password_hash, role, email_verified,
|
RETURNING id, username, email, password_hash, role, email_verified,
|
||||||
verification_token, verification_expires, reset_token, reset_expires,
|
verification_token, verification_expires, reset_token, reset_expires,
|
||||||
guest_id, deleted_at, preferences, created_at, last_login, last_seen_at,
|
guest_id, deleted_at, preferences, created_at, last_login, last_seen_at,
|
||||||
is_active, is_banned, ban_reason, force_password_reset
|
is_active, is_banned, ban_reason, force_password_reset, is_test_account
|
||||||
""",
|
""",
|
||||||
username,
|
username,
|
||||||
password_hash,
|
password_hash,
|
||||||
@@ -511,6 +514,7 @@ class UserStore:
|
|||||||
guest_id,
|
guest_id,
|
||||||
verification_token,
|
verification_token,
|
||||||
verification_expires,
|
verification_expires,
|
||||||
|
is_test_account,
|
||||||
)
|
)
|
||||||
return self._row_to_user(row)
|
return self._row_to_user(row)
|
||||||
except asyncpg.UniqueViolationError:
|
except asyncpg.UniqueViolationError:
|
||||||
@@ -524,7 +528,7 @@ class UserStore:
|
|||||||
SELECT id, username, email, password_hash, role, email_verified,
|
SELECT id, username, email, password_hash, role, email_verified,
|
||||||
verification_token, verification_expires, reset_token, reset_expires,
|
verification_token, verification_expires, reset_token, reset_expires,
|
||||||
guest_id, deleted_at, preferences, created_at, last_login, last_seen_at,
|
guest_id, deleted_at, preferences, created_at, last_login, last_seen_at,
|
||||||
is_active, is_banned, ban_reason, force_password_reset
|
is_active, is_banned, ban_reason, force_password_reset, is_test_account
|
||||||
FROM users_v2
|
FROM users_v2
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
""",
|
""",
|
||||||
@@ -540,7 +544,7 @@ class UserStore:
|
|||||||
SELECT id, username, email, password_hash, role, email_verified,
|
SELECT id, username, email, password_hash, role, email_verified,
|
||||||
verification_token, verification_expires, reset_token, reset_expires,
|
verification_token, verification_expires, reset_token, reset_expires,
|
||||||
guest_id, deleted_at, preferences, created_at, last_login, last_seen_at,
|
guest_id, deleted_at, preferences, created_at, last_login, last_seen_at,
|
||||||
is_active, is_banned, ban_reason, force_password_reset
|
is_active, is_banned, ban_reason, force_password_reset, is_test_account
|
||||||
FROM users_v2
|
FROM users_v2
|
||||||
WHERE LOWER(username) = LOWER($1)
|
WHERE LOWER(username) = LOWER($1)
|
||||||
""",
|
""",
|
||||||
@@ -556,7 +560,7 @@ class UserStore:
|
|||||||
SELECT id, username, email, password_hash, role, email_verified,
|
SELECT id, username, email, password_hash, role, email_verified,
|
||||||
verification_token, verification_expires, reset_token, reset_expires,
|
verification_token, verification_expires, reset_token, reset_expires,
|
||||||
guest_id, deleted_at, preferences, created_at, last_login, last_seen_at,
|
guest_id, deleted_at, preferences, created_at, last_login, last_seen_at,
|
||||||
is_active, is_banned, ban_reason, force_password_reset
|
is_active, is_banned, ban_reason, force_password_reset, is_test_account
|
||||||
FROM users_v2
|
FROM users_v2
|
||||||
WHERE LOWER(email) = LOWER($1)
|
WHERE LOWER(email) = LOWER($1)
|
||||||
""",
|
""",
|
||||||
@@ -572,7 +576,7 @@ class UserStore:
|
|||||||
SELECT id, username, email, password_hash, role, email_verified,
|
SELECT id, username, email, password_hash, role, email_verified,
|
||||||
verification_token, verification_expires, reset_token, reset_expires,
|
verification_token, verification_expires, reset_token, reset_expires,
|
||||||
guest_id, deleted_at, preferences, created_at, last_login, last_seen_at,
|
guest_id, deleted_at, preferences, created_at, last_login, last_seen_at,
|
||||||
is_active, is_banned, ban_reason, force_password_reset
|
is_active, is_banned, ban_reason, force_password_reset, is_test_account
|
||||||
FROM users_v2
|
FROM users_v2
|
||||||
WHERE verification_token = $1
|
WHERE verification_token = $1
|
||||||
""",
|
""",
|
||||||
@@ -588,7 +592,7 @@ class UserStore:
|
|||||||
SELECT id, username, email, password_hash, role, email_verified,
|
SELECT id, username, email, password_hash, role, email_verified,
|
||||||
verification_token, verification_expires, reset_token, reset_expires,
|
verification_token, verification_expires, reset_token, reset_expires,
|
||||||
guest_id, deleted_at, preferences, created_at, last_login, last_seen_at,
|
guest_id, deleted_at, preferences, created_at, last_login, last_seen_at,
|
||||||
is_active, is_banned, ban_reason, force_password_reset
|
is_active, is_banned, ban_reason, force_password_reset, is_test_account
|
||||||
FROM users_v2
|
FROM users_v2
|
||||||
WHERE reset_token = $1
|
WHERE reset_token = $1
|
||||||
""",
|
""",
|
||||||
@@ -686,7 +690,7 @@ class UserStore:
|
|||||||
RETURNING id, username, email, password_hash, role, email_verified,
|
RETURNING id, username, email, password_hash, role, email_verified,
|
||||||
verification_token, verification_expires, reset_token, reset_expires,
|
verification_token, verification_expires, reset_token, reset_expires,
|
||||||
guest_id, deleted_at, preferences, created_at, last_login, last_seen_at,
|
guest_id, deleted_at, preferences, created_at, last_login, last_seen_at,
|
||||||
is_active, is_banned, ban_reason, force_password_reset
|
is_active, is_banned, ban_reason, force_password_reset, is_test_account
|
||||||
"""
|
"""
|
||||||
|
|
||||||
async with self.pool.acquire() as conn:
|
async with self.pool.acquire() as conn:
|
||||||
@@ -730,7 +734,8 @@ class UserStore:
|
|||||||
"""
|
"""
|
||||||
SELECT id, username, email, password_hash, role, email_verified,
|
SELECT id, username, email, password_hash, role, email_verified,
|
||||||
verification_token, verification_expires, reset_token, reset_expires,
|
verification_token, verification_expires, reset_token, reset_expires,
|
||||||
guest_id, deleted_at, preferences, created_at, last_login, is_active
|
guest_id, deleted_at, preferences, created_at, last_login, is_active,
|
||||||
|
is_test_account
|
||||||
FROM users_v2
|
FROM users_v2
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
"""
|
"""
|
||||||
@@ -740,7 +745,8 @@ class UserStore:
|
|||||||
"""
|
"""
|
||||||
SELECT id, username, email, password_hash, role, email_verified,
|
SELECT id, username, email, password_hash, role, email_verified,
|
||||||
verification_token, verification_expires, reset_token, reset_expires,
|
verification_token, verification_expires, reset_token, reset_expires,
|
||||||
guest_id, deleted_at, preferences, created_at, last_login, is_active
|
guest_id, deleted_at, preferences, created_at, last_login, is_active,
|
||||||
|
is_test_account
|
||||||
FROM users_v2
|
FROM users_v2
|
||||||
WHERE is_active = TRUE AND deleted_at IS NULL
|
WHERE is_active = TRUE AND deleted_at IS NULL
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
@@ -1033,6 +1039,7 @@ class UserStore:
|
|||||||
is_banned=row.get("is_banned", False) or False,
|
is_banned=row.get("is_banned", False) or False,
|
||||||
ban_reason=row.get("ban_reason"),
|
ban_reason=row.get("ban_reason"),
|
||||||
force_password_reset=row.get("force_password_reset", False) or False,
|
force_password_reset=row.get("force_password_reset", False) or False,
|
||||||
|
is_test_account=row.get("is_test_account", False) or False,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _row_to_session(self, row: asyncpg.Record) -> UserSession:
|
def _row_to_session(self, row: asyncpg.Record) -> UserSession:
|
||||||
|
|||||||
Reference in New Issue
Block a user