From a6950957137bdf97bff7bc007b3b45eecece0648 Mon Sep 17 00:00:00 2001 From: Phil Skentelbery Date: Mon, 22 Dec 2025 12:54:18 -0700 Subject: [PATCH] feat: add comprehensive tests for Story 2.3 View Exchange Details MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add integration tests covering all acceptance criteria: - Clicking an exchange opens detail view - Shows all exchange information - Shows list of registered participants - Shows current state - Shows registration link (when applicable) - Handle missing optional fields gracefully - Navigation back to dashboard Story: 2.3 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../integration/test_view_exchange_details.py | 330 ++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100644 tests/integration/test_view_exchange_details.py diff --git a/tests/integration/test_view_exchange_details.py b/tests/integration/test_view_exchange_details.py new file mode 100644 index 0000000..d2314db --- /dev/null +++ b/tests/integration/test_view_exchange_details.py @@ -0,0 +1,330 @@ +"""Integration tests for Story 2.3: View Exchange Details.""" + +from datetime import datetime, timedelta + +from src.models import Exchange + + +class TestViewExchangeDetails: + """Test cases for viewing exchange details (Story 2.3).""" + + def test_view_exchange_details_page(self, client, db, admin): # noqa: ARG002 + """Test that exchange detail page loads. + + Acceptance Criteria: + - Clicking an exchange opens detail view + """ + # 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", + description="Test description", + 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) + db.session.commit() + + # View exchange details + response = client.get(f"/admin/exchange/{exchange.id}") + assert response.status_code == 200 + + def test_exchange_details_shows_all_information(self, client, db, admin): # noqa: ARG002 + """Test that all exchange information is displayed. + + Acceptance Criteria: + - Shows all exchange information + """ + # Login first + client.post( + "/admin/login", + data={ + "email": "admin@example.com", + "password": "testpassword123", + }, + follow_redirects=True, + ) + + # Create an exchange with all fields + future_close_date = datetime.utcnow() + timedelta(days=7) + future_exchange_date = datetime.utcnow() + timedelta(days=14) + + exchange = Exchange( + slug=Exchange.generate_slug(), + name="Family Christmas 2025", + description="Annual family gift exchange", + 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, + ) + + db.session.add(exchange) + db.session.commit() + + # View exchange details + response = client.get(f"/admin/exchange/{exchange.id}") + assert response.status_code == 200 + + # Verify all exchange information is displayed + assert b"Family Christmas 2025" in response.data + assert b"Annual family gift exchange" in response.data + assert b"$20-30" in response.data + assert b"20" in response.data # max_participants + assert b"America/New_York" in response.data + assert b"registration_open" in response.data + + def test_exchange_details_shows_current_state(self, client, db, admin): # noqa: ARG002 + """Test that current state is displayed. + + Acceptance Criteria: + - Shows current 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, + ) + + db.session.add(draft_exchange) + db.session.commit() + + # View details + response = client.get(f"/admin/exchange/{draft_exchange.id}") + assert response.status_code == 200 + assert b"draft" in response.data + + def test_exchange_details_shows_registration_link(self, client, db, admin): # noqa: ARG002 + """Test that registration link is shown. + + Acceptance Criteria: + - Shows registration link (when applicable) + """ + # 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_DRAFT, + ) + + db.session.add(exchange) + db.session.commit() + + # View exchange details + response = client.get(f"/admin/exchange/{exchange.id}") + assert response.status_code == 200 + + # Verify registration link is displayed with the exchange slug + assert exchange.slug.encode() in response.data + assert b"/exchange/" in response.data + assert b"/register" in response.data + + def test_exchange_details_shows_participant_list(self, client, db, admin): # noqa: ARG002 + """Test that participant list is shown (empty initially). + + Acceptance Criteria: + - Shows list of registered participants + """ + # 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_DRAFT, + ) + + db.session.add(exchange) + db.session.commit() + + # View exchange details + response = client.get(f"/admin/exchange/{exchange.id}") + assert response.status_code == 200 + + # Verify participant section exists + assert b"Participants" in response.data or b"participants" in response.data + # Since no participants yet, should show empty message + assert ( + b"No participants yet" in response.data + or b"no participants" in response.data.lower() + ) + + def test_exchange_details_not_found(self, client, db, admin): # noqa: ARG002 + """Test that 404 is returned for non-existent exchange. + + Acceptance Criteria: + - Handle non-existent exchange gracefully + """ + # Login first + client.post( + "/admin/login", + data={ + "email": "admin@example.com", + "password": "testpassword123", + }, + follow_redirects=True, + ) + + # Try to view non-existent exchange + response = client.get("/admin/exchange/99999") + assert response.status_code == 404 + + def test_exchange_details_handles_missing_description(self, client, db, admin): # noqa: ARG002 + """Test that missing optional fields are handled gracefully. + + Acceptance Criteria: + - Shows all exchange information + - Handle optional fields (description) + """ + # Login first + client.post( + "/admin/login", + data={ + "email": "admin@example.com", + "password": "testpassword123", + }, + follow_redirects=True, + ) + + # Create exchange without description + 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", + description=None, # No description + 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) + db.session.commit() + + # View exchange details + response = client.get(f"/admin/exchange/{exchange.id}") + assert response.status_code == 200 + + # Should handle missing description gracefully + assert ( + b"No description" in response.data + or b"no description" in response.data.lower() + ) + + def test_exchange_details_navigation(self, client, db, admin): # noqa: ARG002 + """Test that navigation back to dashboard exists. + + Acceptance Criteria: + - Provide navigation back to dashboard + """ + # 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_DRAFT, + ) + + db.session.add(exchange) + db.session.commit() + + # View exchange details + response = client.get(f"/admin/exchange/{exchange.id}") + assert response.status_code == 200 + + # Verify back link to dashboard exists + assert b"dashboard" in response.data.lower() or b"back" in response.data.lower()