from datetime import UTC, datetime, timedelta from porchlight.models import ( CredentialType, MagicLink, PasswordCredential, User, WebAuthnCredential, ) # TODO: Add model validation tests: # - Required fields reject missing values (userid, username, etc.) # - CredentialType rejects invalid enum values # - WebAuthnCredential requires credential_id and public_key # - sign_count rejects negative values def test_user_creation() -> None: user = User( userid="lusab-bansen", username="alice", ) assert user.userid == "lusab-bansen" assert user.username == "alice" assert user.preferred_username is None assert user.email is None assert user.active is True assert user.groups == [] assert user.created_at is not None assert user.updated_at is not None def test_user_with_all_fields() -> None: user = User( userid="lusab-bansen", username="alice", preferred_username="Alice W.", given_name="Alice", family_name="Wonderland", nickname="ally", email="alice@example.com", email_verified=True, phone_number="+1234567890", phone_number_verified=False, picture="https://example.com/alice.jpg", locale="en-US", active=True, groups=["admin", "users"], ) assert user.given_name == "Alice" assert user.groups == ["admin", "users"] def test_webauthn_credential() -> None: cred = WebAuthnCredential( user_id="lusab-bansen", credential_id=b"\x01\x02\x03", public_key=b"\x04\x05\x06", sign_count=0, device_name="YubiKey 5", ) assert cred.type == CredentialType.WEBAUTHN assert cred.credential_id == b"\x01\x02\x03" assert cred.device_name == "YubiKey 5" def test_password_credential() -> None: cred = PasswordCredential( user_id="lusab-bansen", password_hash="$argon2id$v=19$m=65536,t=3,p=4$hash", ) assert cred.type == CredentialType.PASSWORD assert cred.password_hash.startswith("$argon2") def test_magic_link() -> None: expires = datetime.now(UTC) + timedelta(hours=24) link = MagicLink( token="abc123def456", username="newuser", expires_at=expires, ) assert link.token == "abc123def456" assert link.username == "newuser" assert link.used is False assert link.created_by is None assert link.expires_at == expires