feat: add comprehensive tests for Story 2.6 Generate Registration Link
Add integration tests covering all acceptance criteria: - Unique link generated for each exchange - Link is copyable to clipboard - Link is displayed when exchange is in appropriate state - Link leads to registration page for that specific exchange - Link uses slug instead of numeric ID for security - Link contains full URL for easy sharing Story: 2.6 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
338
tests/integration/test_registration_link.py
Normal file
338
tests/integration/test_registration_link.py
Normal file
@@ -0,0 +1,338 @@
|
||||
"""Integration tests for Story 2.6: Generate Registration Link."""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from src.models import Exchange
|
||||
|
||||
|
||||
class TestGenerateRegistrationLink:
|
||||
"""Test cases for registration link generation (Story 2.6)."""
|
||||
|
||||
def test_registration_link_generated_for_exchange(self, client, db, admin): # noqa: ARG002
|
||||
"""Test that unique link is generated for each exchange.
|
||||
|
||||
Acceptance Criteria:
|
||||
- Unique link generated for each exchange
|
||||
"""
|
||||
# Login first
|
||||
client.post(
|
||||
"/admin/login",
|
||||
data={
|
||||
"email": "admin@example.com",
|
||||
"password": "testpassword123",
|
||||
},
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
# Create two exchanges
|
||||
future_close_date = datetime.utcnow() + timedelta(days=7)
|
||||
future_exchange_date = datetime.utcnow() + timedelta(days=14)
|
||||
|
||||
exchange1 = Exchange(
|
||||
slug=Exchange.generate_slug(),
|
||||
name="Exchange 1",
|
||||
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,
|
||||
)
|
||||
|
||||
exchange2 = Exchange(
|
||||
slug=Exchange.generate_slug(),
|
||||
name="Exchange 2",
|
||||
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(exchange1)
|
||||
db.session.add(exchange2)
|
||||
db.session.commit()
|
||||
|
||||
# View first exchange details
|
||||
response1 = client.get(f"/admin/exchange/{exchange1.id}")
|
||||
assert response1.status_code == 200
|
||||
|
||||
# View second exchange details
|
||||
response2 = client.get(f"/admin/exchange/{exchange2.id}")
|
||||
assert response2.status_code == 200
|
||||
|
||||
# Verify different slugs in registration links
|
||||
assert exchange1.slug.encode() in response1.data
|
||||
assert exchange2.slug.encode() in response2.data
|
||||
assert exchange1.slug != exchange2.slug
|
||||
|
||||
def test_registration_link_is_copyable(self, client, db, admin): # noqa: ARG002
|
||||
"""Test that link is copyable to clipboard.
|
||||
|
||||
Acceptance Criteria:
|
||||
- Link is copyable to clipboard
|
||||
"""
|
||||
# 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 copy button or mechanism exists
|
||||
assert b"copy" in response.data.lower() or b"clipboard" in response.data.lower()
|
||||
|
||||
def test_registration_link_displayed_in_detail_view(self, client, db, admin): # noqa: ARG002
|
||||
"""Test that link is displayed when exchange is in appropriate state.
|
||||
|
||||
Acceptance Criteria:
|
||||
- Link is displayed when exchange is in appropriate state
|
||||
"""
|
||||
# Login first
|
||||
client.post(
|
||||
"/admin/login",
|
||||
data={
|
||||
"email": "admin@example.com",
|
||||
"password": "testpassword123",
|
||||
},
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
# Create 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 section exists
|
||||
assert (
|
||||
b"Registration Link" in response.data
|
||||
or b"registration link" in response.data.lower()
|
||||
)
|
||||
|
||||
def test_registration_link_leads_to_correct_page(self, client, db, admin): # noqa: ARG002
|
||||
"""Test that link leads to registration page for that specific exchange.
|
||||
|
||||
Acceptance Criteria:
|
||||
- Link leads to registration page for that specific exchange
|
||||
"""
|
||||
# Login first
|
||||
client.post(
|
||||
"/admin/login",
|
||||
data={
|
||||
"email": "admin@example.com",
|
||||
"password": "testpassword123",
|
||||
},
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
# Create 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 the link format contains the slug
|
||||
expected_link_part = f"/exchange/{exchange.slug}/register"
|
||||
assert expected_link_part.encode() in response.data
|
||||
|
||||
def test_registration_link_uses_slug_not_id(self, client, db, admin): # noqa: ARG002
|
||||
"""Test that registration link uses slug instead of numeric ID.
|
||||
|
||||
Acceptance Criteria:
|
||||
- Unique link generated for each exchange
|
||||
- Link uses slug (not ID) for security
|
||||
"""
|
||||
# Login first
|
||||
client.post(
|
||||
"/admin/login",
|
||||
data={
|
||||
"email": "admin@example.com",
|
||||
"password": "testpassword123",
|
||||
},
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
# Create 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 slug is in the link
|
||||
assert exchange.slug.encode() in response.data
|
||||
|
||||
# Verify numeric ID is NOT in the registration link part
|
||||
# (It will be in admin URLs, but not in the public registration link)
|
||||
response_text = response.data.decode()
|
||||
# Extract registration link section
|
||||
if "Registration Link" in response_text:
|
||||
# Find the registration link
|
||||
assert f"/exchange/{exchange.slug}/register" in response_text
|
||||
# Should NOT contain /exchange/{id}/register
|
||||
assert f"/exchange/{exchange.id}/register" not in response_text
|
||||
|
||||
def test_registration_link_contains_full_url(self, client, db, admin): # noqa: ARG002
|
||||
"""Test that registration link is a complete URL.
|
||||
|
||||
Acceptance Criteria:
|
||||
- Link is copyable (implies full URL)
|
||||
"""
|
||||
# Login first
|
||||
client.post(
|
||||
"/admin/login",
|
||||
data={
|
||||
"email": "admin@example.com",
|
||||
"password": "testpassword123",
|
||||
},
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
# Create 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 full URL is displayed (starts with http:// or https://)
|
||||
assert (
|
||||
b"http://" in response.data or b"https://" in response.data
|
||||
), "Registration link should be a full URL"
|
||||
|
||||
def test_registration_link_available_in_all_states(self, client, db, admin): # noqa: ARG002
|
||||
"""Test that registration link is available regardless of exchange state.
|
||||
|
||||
Acceptance Criteria:
|
||||
- Link is displayed when exchange is in appropriate state
|
||||
- Note: Based on design, link is always shown but may have different behavior
|
||||
"""
|
||||
# Login first
|
||||
client.post(
|
||||
"/admin/login",
|
||||
data={
|
||||
"email": "admin@example.com",
|
||||
"password": "testpassword123",
|
||||
},
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
future_close_date = datetime.utcnow() + timedelta(days=7)
|
||||
future_exchange_date = datetime.utcnow() + timedelta(days=14)
|
||||
|
||||
# Test in different states
|
||||
for state in [
|
||||
Exchange.STATE_DRAFT,
|
||||
Exchange.STATE_REGISTRATION_OPEN,
|
||||
]:
|
||||
exchange = Exchange(
|
||||
slug=Exchange.generate_slug(),
|
||||
name=f"Exchange in {state}",
|
||||
budget="$20-30",
|
||||
max_participants=10,
|
||||
registration_close_date=future_close_date,
|
||||
exchange_date=future_exchange_date,
|
||||
timezone="America/New_York",
|
||||
state=state,
|
||||
)
|
||||
|
||||
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
|
||||
assert exchange.slug.encode() in response.data
|
||||
assert b"/exchange/" in response.data
|
||||
assert b"/register" in response.data
|
||||
Reference in New Issue
Block a user