from cryptography.fernet import Fernet from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from sqlalchemy.pool import StaticPool from app.auth.account import authenticate_self_service from app.db.models import Base from app.db.repositories import UserRepository from app.security.credentials import CredentialCipher def _make_session_and_cipher(): engine = create_engine("sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool) Base.metadata.create_all(engine) factory = sessionmaker(bind=engine, expire_on_commit=False) cipher = CredentialCipher(Fernet.generate_key().decode("ascii")) return factory(), cipher def _seed_user(session, cipher, **overrides): values = dict( name="Max", enabled=True, mywhoosh_email_enc=cipher.encrypt("max@mywhoosh.example"), mywhoosh_password_enc=cipher.encrypt("mw-secret"), garmin_email_enc=cipher.encrypt("max@garmin.example"), garmin_password_enc=cipher.encrypt("garmin-secret"), ) values.update(overrides) return UserRepository(session).create(**values) def test_authenticates_with_mywhoosh_credentials() -> None: session, cipher = _make_session_and_cipher() user = _seed_user(session, cipher) result = authenticate_self_service(session, cipher, "max@mywhoosh.example", "mw-secret") assert result is not None assert result.id == user.id def test_authenticates_with_garmin_credentials() -> None: session, cipher = _make_session_and_cipher() user = _seed_user(session, cipher) result = authenticate_self_service(session, cipher, "max@garmin.example", "garmin-secret") assert result is not None assert result.id == user.id def test_rejects_wrong_password() -> None: session, cipher = _make_session_and_cipher() _seed_user(session, cipher) assert authenticate_self_service(session, cipher, "max@mywhoosh.example", "wrong") is None def test_rejects_unknown_email() -> None: session, cipher = _make_session_and_cipher() _seed_user(session, cipher) assert authenticate_self_service(session, cipher, "nobody@example.com", "mw-secret") is None def test_rejects_mixed_email_and_password_from_different_accounts() -> None: """A MyWhoosh email paired with the Garmin password (or vice versa) for the same user must not authenticate -- each pair is checked together.""" session, cipher = _make_session_and_cipher() _seed_user(session, cipher) assert authenticate_self_service(session, cipher, "max@mywhoosh.example", "garmin-secret") is None assert authenticate_self_service(session, cipher, "max@garmin.example", "mw-secret") is None def test_rejects_empty_password() -> None: session, cipher = _make_session_and_cipher() _seed_user(session, cipher) assert authenticate_self_service(session, cipher, "max@mywhoosh.example", "") is None def test_picks_correct_user_among_several() -> None: session, cipher = _make_session_and_cipher() _seed_user( session, cipher, name="Anna", mywhoosh_email_enc=cipher.encrypt("anna@mywhoosh.example"), mywhoosh_password_enc=cipher.encrypt("anna-secret"), garmin_email_enc=cipher.encrypt("anna@garmin.example"), garmin_password_enc=cipher.encrypt("anna-garmin-secret"), ) bob = _seed_user( session, cipher, name="Bob", mywhoosh_email_enc=cipher.encrypt("bob@mywhoosh.example"), mywhoosh_password_enc=cipher.encrypt("bob-secret"), garmin_email_enc=cipher.encrypt("bob@garmin.example"), garmin_password_enc=cipher.encrypt("bob-garmin-secret"), ) result = authenticate_self_service(session, cipher, "bob@mywhoosh.example", "bob-secret") assert result is not None assert result.id == bob.id