"""Integration tests for Story 2.2: View Exchange List.""" from datetime import datetime, timedelta from src.models import Exchange class TestViewExchangeList: """Test cases for viewing exchange list (Story 2.2).""" def test_dashboard_shows_list_of_exchanges(self, client, db, admin): # noqa: ARG002 """Test that dashboard displays all exchanges. Acceptance Criteria: - Dashboard shows list of all exchanges """ # Login first client.post( "/admin/login", data={ "email": "admin@example.com", "password": "testpassword123", }, follow_redirects=True, ) # Create multiple exchanges future_close_date = datetime.utcnow() + timedelta(days=7) future_exchange_date = datetime.utcnow() + timedelta(days=14) exchange1 = Exchange( slug=Exchange.generate_slug(), name="Family Christmas 2025", budget="$20-30", max_participants=20, registration_close_date=future_close_date, exchange_date=future_exchange_date, timezone="America/New_York", state=Exchange.STATE_REGISTRATION_OPEN, ) exchange2 = Exchange( slug=Exchange.generate_slug(), name="Office Gift Exchange", budget="$15-25", max_participants=15, registration_close_date=future_close_date + timedelta(days=7), exchange_date=future_exchange_date + timedelta(days=7), timezone="America/Chicago", state=Exchange.STATE_DRAFT, ) db.session.add(exchange1) db.session.add(exchange2) db.session.commit() # View dashboard response = client.get("/admin/dashboard") assert response.status_code == 200 # Verify both exchanges appear assert b"Family Christmas 2025" in response.data assert b"Office Gift Exchange" in response.data def test_dashboard_displays_exchange_info(self, client, db, admin): # noqa: ARG002 """Test that each exchange displays required information. Acceptance Criteria: - Each exchange displays: name, state, participant count, exchange date """ # Login first client.post( "/admin/login", data={ "email": "admin@example.com", "password": "testpassword123", }, follow_redirects=True, ) # Create an exchange future_close_date = datetime.utcnow() + timedelta(days=7) future_exchange_date = datetime.utcnow() + timedelta(days=14) exchange = Exchange( slug=Exchange.generate_slug(), name="Test Exchange", budget="$20-30", max_participants=10, registration_close_date=future_close_date, exchange_date=future_exchange_date, timezone="America/New_York", state=Exchange.STATE_REGISTRATION_OPEN, ) db.session.add(exchange) db.session.commit() # View dashboard response = client.get("/admin/dashboard") assert response.status_code == 200 # Verify exchange information is displayed assert b"Test Exchange" in response.data assert b"registration_open" in response.data assert b"0 / 10" in response.data # Participant count assert future_exchange_date.strftime("%Y-%m-%d").encode() in response.data def test_dashboard_sorted_by_exchange_date(self, client, db, admin): # noqa: ARG002 """Test that exchanges are sorted by exchange date (upcoming first). Acceptance Criteria: - Exchanges sorted by exchange date (upcoming first) """ # Login first client.post( "/admin/login", data={ "email": "admin@example.com", "password": "testpassword123", }, follow_redirects=True, ) # Create exchanges with different dates base_close_date = datetime.utcnow() + timedelta(days=7) # Later exchange exchange1 = Exchange( slug=Exchange.generate_slug(), name="Later Exchange", budget="$20-30", max_participants=10, registration_close_date=base_close_date + timedelta(days=30), exchange_date=base_close_date + timedelta(days=60), # Far in future timezone="America/New_York", state=Exchange.STATE_DRAFT, ) # Earlier exchange exchange2 = Exchange( slug=Exchange.generate_slug(), name="Earlier Exchange", budget="$20-30", max_participants=10, registration_close_date=base_close_date, exchange_date=base_close_date + timedelta(days=7), # Sooner timezone="America/New_York", state=Exchange.STATE_REGISTRATION_OPEN, ) db.session.add(exchange1) db.session.add(exchange2) db.session.commit() # View dashboard response = client.get("/admin/dashboard") assert response.status_code == 200 # Verify earlier exchange appears before later exchange response_text = response.data.decode() earlier_pos = response_text.index("Earlier Exchange") later_pos = response_text.index("Later Exchange") assert earlier_pos < later_pos def test_dashboard_shows_state_indicators(self, client, db, admin): # noqa: ARG002 """Test that visual indicators for exchange state are displayed. Acceptance Criteria: - Visual indicator for exchange state """ # Login first client.post( "/admin/login", data={ "email": "admin@example.com", "password": "testpassword123", }, follow_redirects=True, ) # Create exchanges in different states future_close_date = datetime.utcnow() + timedelta(days=7) future_exchange_date = datetime.utcnow() + timedelta(days=14) draft_exchange = Exchange( slug=Exchange.generate_slug(), name="Draft Exchange", budget="$20-30", max_participants=10, registration_close_date=future_close_date, exchange_date=future_exchange_date, timezone="America/New_York", state=Exchange.STATE_DRAFT, ) open_exchange = Exchange( slug=Exchange.generate_slug(), name="Open Exchange", budget="$20-30", max_participants=10, registration_close_date=future_close_date, exchange_date=future_exchange_date, timezone="America/New_York", state=Exchange.STATE_REGISTRATION_OPEN, ) db.session.add(draft_exchange) db.session.add(open_exchange) db.session.commit() # View dashboard response = client.get("/admin/dashboard") assert response.status_code == 200 # Verify state indicators are present assert b"draft" in response.data assert b"registration_open" in response.data def test_dashboard_shows_summary_counts(self, client, db, admin): # noqa: ARG002 """Test that dashboard shows summary counts by state. Acceptance Criteria: - Dashboard shows counts for draft, active, and completed exchanges """ # Login first client.post( "/admin/login", data={ "email": "admin@example.com", "password": "testpassword123", }, follow_redirects=True, ) # Create exchanges in different states future_close_date = datetime.utcnow() + timedelta(days=7) future_exchange_date = datetime.utcnow() + timedelta(days=14) # 2 draft exchanges for i in range(2): exchange = Exchange( slug=Exchange.generate_slug(), name=f"Draft Exchange {i}", budget="$20-30", max_participants=10, registration_close_date=future_close_date, exchange_date=future_exchange_date, timezone="America/New_York", state=Exchange.STATE_DRAFT, ) db.session.add(exchange) # 3 active exchanges (registration_open) for i in range(3): exchange = Exchange( slug=Exchange.generate_slug(), name=f"Active Exchange {i}", budget="$20-30", max_participants=10, registration_close_date=future_close_date, exchange_date=future_exchange_date, timezone="America/New_York", state=Exchange.STATE_REGISTRATION_OPEN, ) db.session.add(exchange) # 1 completed exchange completed_exchange = Exchange( slug=Exchange.generate_slug(), name="Completed Exchange", budget="$20-30", max_participants=10, registration_close_date=future_close_date - timedelta(days=30), exchange_date=future_exchange_date - timedelta(days=20), timezone="America/New_York", state=Exchange.STATE_COMPLETED, ) db.session.add(completed_exchange) db.session.commit() # View dashboard response = client.get("/admin/dashboard") assert response.status_code == 200 # Verify counts are displayed response_text = response.data.decode() # Look for summary section with counts assert "2" in response_text # draft_count assert "3" in response_text # active_count assert "1" in response_text # completed_count def test_dashboard_empty_state(self, client, db, admin): # noqa: ARG002 """Test dashboard when no exchanges exist. Acceptance Criteria: - Show helpful message when no exchanges exist - Link to create first exchange """ # Login first client.post( "/admin/login", data={ "email": "admin@example.com", "password": "testpassword123", }, follow_redirects=True, ) # View dashboard with no exchanges response = client.get("/admin/dashboard") assert response.status_code == 200 # Verify empty state message assert ( b"No exchanges yet" in response.data or b"no exchanges" in response.data.lower() ) # Verify link to create exchange assert b"Create" in response.data or b"create" in response.data.lower()