"""Integration tests for withdrawal functionality.""" from datetime import datetime from flask import url_for from src.models.participant import Participant def get_csrf_token(client, url): """Extract CSRF token from a page. Args: client: Flask test client. url: URL to fetch the CSRF token from. Returns: CSRF token string. """ response = client.get(url) # Extract CSRF token from the form data = response.data.decode() start = data.find('name="csrf_token" value="') + len('name="csrf_token" value="') end = data.find('"', start) return data[start:end] def test_withdrawal_get_shows_confirmation_page(client, auth_participant): # noqa: ARG001 """Test GET shows withdrawal confirmation page.""" response = client.get(url_for("participant.withdraw")) assert response.status_code == 200 assert b"Withdraw from Exchange" in response.data assert b"Are you sure" in response.data or b"cannot be undone" in response.data def test_withdrawal_post_success(client, auth_participant, db): """Test successful withdrawal flow.""" participant_id = auth_participant.id csrf_token = get_csrf_token(client, url_for("participant.withdraw")) response = client.post( url_for("participant.withdraw"), data={"confirm": True, "csrf_token": csrf_token}, follow_redirects=True, ) assert response.status_code == 200 assert b"withdrawn from the exchange" in response.data # Verify database participant = db.session.query(Participant).filter_by(id=participant_id).first() assert participant.withdrawn_at is not None # Verify session cleared with client.session_transaction() as session: assert "user_id" not in session def test_withdrawal_requires_confirmation(client, auth_participant): # noqa: ARG001 """Test withdrawal requires checkbox confirmation.""" csrf_token = get_csrf_token(client, url_for("participant.withdraw")) response = client.post( url_for("participant.withdraw"), data={"csrf_token": csrf_token}, follow_redirects=False, ) # Should re-render form with validation error assert response.status_code == 200 assert b"confirm" in response.data.lower() def test_withdrawal_blocked_after_registration_closes( client, exchange_factory, participant_factory ): """Test withdrawal blocked after registration closes.""" exchange = exchange_factory(state="registration_closed") participant = participant_factory(exchange=exchange) with client.session_transaction() as session: session["user_id"] = participant.id session["user_type"] = "participant" session["exchange_id"] = exchange.id response = client.get(url_for("participant.withdraw"), follow_redirects=True) assert ( b"Registration has closed" in response.data or b"Contact the admin" in response.data ) def test_withdrawal_blocked_after_matching( client, exchange_factory, participant_factory ): """Test withdrawal blocked after matching occurs.""" exchange = exchange_factory(state="matched") participant = participant_factory(exchange=exchange) with client.session_transaction() as session: session["user_id"] = participant.id session["user_type"] = "participant" session["exchange_id"] = exchange.id response = client.get(url_for("participant.withdraw"), follow_redirects=True) assert ( b"Withdrawal is no longer available" in response.data or b"Contact the admin" in response.data ) def test_withdrawal_requires_login(client): """Test that withdrawal requires login.""" response = client.get(url_for("participant.withdraw")) # Should redirect or show error assert response.status_code in [302, 401, 403] def test_dashboard_shows_withdraw_link_when_allowed(client, auth_participant): """Dashboard shows withdraw link when withdrawal is allowed.""" response = client.get( url_for("participant.dashboard", id=auth_participant.exchange_id) ) assert response.status_code == 200 assert b"Withdraw" in response.data def test_dashboard_hides_withdraw_link_after_close( client, exchange_factory, participant_factory ): """Dashboard hides withdraw link after registration closes.""" exchange = exchange_factory(state="registration_closed") participant = participant_factory(exchange=exchange) with client.session_transaction() as session: session["user_id"] = participant.id session["user_type"] = "participant" session["exchange_id"] = exchange.id response = client.get(url_for("participant.dashboard", id=exchange.id)) assert response.status_code == 200 # Withdraw link should not be present assert b"Withdraw from Exchange" not in response.data def test_already_withdrawn_redirects_to_register( client, exchange_factory, participant_factory, db, # noqa: ARG001 ): """Test that already withdrawn participants are redirected to register page.""" exchange = exchange_factory(state="registration_open") participant = participant_factory(exchange=exchange, withdrawn_at=datetime.utcnow()) with client.session_transaction() as session: session["user_id"] = participant.id session["user_type"] = "participant" session["exchange_id"] = exchange.id response = client.get(url_for("participant.withdraw"), follow_redirects=True) assert b"already withdrawn" in response.data # Should be on registration page assert exchange.name.encode() in response.data