import pytest from unittest.mock import AsyncMock, patch from sqlalchemy import select from app.models.account_invite import AccountInvite @pytest.mark.asyncio async def test_create_invite_sends_email_and_stamps_email_sent_at( client, test_db, test_user, auth_headers ): """Regression: today's create_invite does NOT send email. After this task, it MUST.""" with patch( "app.core.email.EmailService.send_account_invite_email", new_callable=AsyncMock, return_value=True, ) as mock_send: response = await client.post( "/api/v1/accounts/me/invites", json={"email": "teammate@example.com", "role": "engineer"}, headers=auth_headers, ) assert response.status_code == 201, response.json() mock_send.assert_called_once() kwargs = mock_send.call_args.kwargs assert kwargs["to_email"] == "teammate@example.com" assert kwargs["role"] == "engineer" assert kwargs["code"] invite = (await test_db.execute( select(AccountInvite).where(AccountInvite.email == "teammate@example.com") )).scalar_one() assert invite.email_sent_at is not None @pytest.mark.asyncio async def test_create_invite_email_failure_still_creates_row( client, test_db, test_user, auth_headers ): """When EmailService returns False, the invite row is still created but email_sent_at remains NULL.""" with patch( "app.core.email.EmailService.send_account_invite_email", new_callable=AsyncMock, return_value=False, ): response = await client.post( "/api/v1/accounts/me/invites", json={"email": "fail-mail@example.com", "role": "engineer"}, headers=auth_headers, ) assert response.status_code == 201 invite = (await test_db.execute( select(AccountInvite).where(AccountInvite.email == "fail-mail@example.com") )).scalar_one() assert invite.email_sent_at is None