Coverage for src/couchers/helpers/postal_verification.py: 100%

11 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-12-20 11:53 +0000

1import secrets 

2 

3from sqlalchemy import exists 

4from sqlalchemy.orm import Session 

5 

6from couchers.constants import ( 

7 POSTAL_VERIFICATION_CODE_ALPHABET, 

8 POSTAL_VERIFICATION_CODE_LENGTH, 

9) 

10from couchers.models import User 

11from couchers.models.postal_verification import PostalVerificationAttempt 

12from couchers.sql import couchers_select as select 

13 

14 

15def generate_postal_verification_code() -> str: 

16 """ 

17 Generates a random 6-character uppercase alphanumeric code. 

18 Uses a reduced alphabet to avoid confusion (no I, O, 0, 1). 

19 """ 

20 return "".join(secrets.choice(POSTAL_VERIFICATION_CODE_ALPHABET) for _ in range(POSTAL_VERIFICATION_CODE_LENGTH)) 

21 

22 

23def has_postal_verification(session: Session, user: User) -> bool: 

24 """ 

25 Check if user has valid postal verification. 

26 

27 Similar to strong verification, we query the database rather than 

28 storing a denormalized flag on the user. 

29 """ 

30 return session.execute( 

31 select( 

32 exists( 

33 select(PostalVerificationAttempt) 

34 .where(PostalVerificationAttempt.user_id == user.id) 

35 .where(PostalVerificationAttempt.is_valid) 

36 ) 

37 ) 

38 ).scalar()