"""Tests for the 18+ age gate on user registration.""" from datetime import date, timedelta import pytest from fastapi.testclient import TestClient from app.main import app from app.routers.auth import calculate_age, MIN_AGE, RegisterRequest class TestCalculateAge: """Unit tests for the calculate_age helper.""" def test_exactly_18(self): today = date(2026, 6, 15) dob = today.replace(year=today.year - 18) assert calculate_age(dob, today=today) == 18 def test_just_turned_18(self): today = date(2026, 6, 15) dob = today.replace(year=today.year - 18) - timedelta(days=1) assert calculate_age(dob, today=today) == 18 def test_one_day_short_of_18(self): today = date(2026, 6, 15) dob = today.replace(year=today.year - 18) + timedelta(days=1) assert calculate_age(dob, today=today) == 17 def test_leap_day_birthday_before(self): assert calculate_age(date(2000, 2, 29), today=date(2024, 2, 28)) == 23 def test_leap_day_birthday_after(self): assert calculate_age(date(2000, 2, 29), today=date(2024, 3, 1)) == 24 class TestRegisterRequestDob: def test_dob_parsed_from_iso_string(self): req = RegisterRequest(username="u", email="u@example.com", password="p", date_of_birth="1990-01-01") assert req.date_of_birth == date(1990, 1, 1) def test_dob_required(self): with pytest.raises(Exception): RegisterRequest(username="u", email="u@example.com", password="p") def test_bad_dob_string_rejected(self): with pytest.raises(Exception): RegisterRequest(username="u", email="u@example.com", password="p", date_of_birth="not-a-date") class TestRegisterAgeGate: """End-to-end: the /register endpoint computes age from the DOB.""" def _dob_for_age(self, years: int) -> str: return (date.today().replace(year=date.today().year - years)).isoformat() def test_adult_accepted(self): client = TestClient(app) r = client.post("/api/auth/register", json={ "username": "gate_adult", "email": "gate_adult@e.com", "password": "secret123", "date_of_birth": self._dob_for_age(25), }) assert r.status_code == 201, r.text def test_minor_rejected(self): client = TestClient(app) r = client.post("/api/auth/register", json={ "username": "gate_minor", "email": "gate_minor@e.com", "password": "secret123", "date_of_birth": self._dob_for_age(15), }) assert r.status_code == 403 assert str(MIN_AGE) in r.json()["detail"] def test_exactly_18_accepted(self): client = TestClient(app) r = client.post("/api/auth/register", json={ "username": "gate_18", "email": "gate_18@e.com", "password": "secret123", "date_of_birth": (date.today().replace(year=date.today().year - 18) - timedelta(days=1)).isoformat(), }) assert r.status_code == 201, r.text def test_future_dob_rejected(self): client = TestClient(app) r = client.post("/api/auth/register", json={ "username": "gate_future", "email": "gate_future@e.com", "password": "secret123", "date_of_birth": "2999-01-01", }) assert r.status_code == 400 def test_dob_is_stored(self): client = TestClient(app) dob = self._dob_for_age(30) reg = client.post("/api/auth/register", json={ "username": "gate_stored", "email": "gate_stored@e.com", "password": "secret123", "date_of_birth": dob, }) assert reg.status_code == 201 me = client.get("/api/auth/me", headers={ "Authorization": f"Bearer {reg.json()['access_token']}" }) assert me.json()["date_of_birth"] == dob