PiqueTickets API Checkout Testing Plan¶
Document Version: 1.0 Created: 2025-10-27 Status: Ready for Review Related Issues: PIQUE-573 (Race Condition Fix)
Table of Contents¶
- Executive Summary
- Checkout Architecture Overview
- Current Test Coverage Analysis
- Test Coverage Gaps
- Comprehensive Test Plan
- Implementation Strategy
- Test Data & Fixtures
- CI/CD Integration
- Appendices
Executive Summary¶
Purpose¶
This document outlines a comprehensive testing strategy for the PiqueTickets API checkout process, focusing on ensuring system reliability, data integrity, and race condition prevention.
Key Findings¶
Existing Coverage: - ✅ Basic order creation and validation (test_order_view.py) - ✅ Race condition prevention for concurrent purchases (test_order_race_condition.py) - ✅ Promo code validation and application - ✅ Lock timeout and safety mechanisms
Critical Gaps Identified: - ⚠️ Payment failure scenarios and rollback verification - ⚠️ Network timeout handling during Stripe API calls - ⚠️ Database connection failures mid-transaction - ⚠️ Redis cache unavailability scenarios - ⚠️ Complex edge cases (price changes, expired sessions, etc.) - ⚠️ Load testing and performance benchmarks - ⚠️ End-to-end integration tests with actual Stripe test mode
Recommendations¶
Priority Levels: - P0 (Critical): Tests that must pass for checkout to be considered functional - P1 (High): Important edge cases that could cause data integrity issues - P2 (Medium): Performance, monitoring, and nice-to-have coverage
Estimated Implementation Time: 40-60 hours for complete coverage
Checkout Architecture Overview¶
Components¶
┌─────────────────────────────────────────────────────────────────┐
│ Checkout Flow Overview │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. CheckoutSessionView (order_views.py) │
│ ├─ Request Validation (order_validation.py) │
│ ├─ Show & Promo Code Validation │
│ ├─ Redis Lock Acquisition (120s timeout) │
│ ├─ Database Transaction (atomic) │
│ │ ├─ Row-Level Locks (select_for_update) │
│ │ ├─ Ticket Availability Check │
│ │ ├─ Fee Calculation │
│ │ ├─ Order Creation │
│ │ └─ TicketOrder Creation │
│ ├─ Lock Release (finally block) │
│ └─ Stripe Session Creation / Free Order Handling │
│ │
│ 2. SuccessSessionView (order_views.py) │
│ ├─ Order Completion │
│ ├─ Attendee Creation │
│ ├─ Email Notifications (Celery) │
│ ├─ PDF Ticket Generation (Celery) │
│ ├─ Show Reminder Scheduling (Celery) │
│ └─ Stripe Metadata Updates │
│ │
└─────────────────────────────────────────────────────────────────┘
Data Models¶
Order - Customer information (name, email, phone) - Payment details (session_id, payment_intent_id, charge_id) - Financial tracking (total, platform_fees, payment_processing_fees) - Promo code tracking (promo_code, promo_code_discount) - Status (success, is_manual_order)
TicketOrder - Ticket reference and quantity - Pricing at time of purchase (price_per_ticket, total_price) - Discount tracking (discount_amount, promo_code) - Donation tracking (donation_amount) - VIP status (is_vip) - Attendee relationships
Ticket - Name, description, price - Inventory management (quantity) - Donation-based pricing support (is_donation_based) - Show relationship
TicketPromoCode - Code (auto-uppercased) - Discount percentage (0.00-1.00) - Show-specific
External Integrations¶
Stripe - Checkout Sessions - Payment Intents - Connected Accounts (Producer transfers) - Metadata tracking
Redis - Distributed locking mechanism - 120-second timeout - UUID-based lock values
Celery - Email notifications - PDF ticket generation - Show reminder scheduling
Current Test Coverage Analysis¶
Test Files Overview¶
1. test_order_view.py (734 lines)¶
TestOrderView Class: - ✅ Create order with regular tickets - ✅ Create order with donation tickets - ✅ Create order with mixed tickets - ✅ Invalid ticket quantity handling - ✅ Invalid donation amount handling - ✅ Sold out tickets handling - ✅ Missing required fields - ✅ Invalid email format - ✅ Past show date validation - ✅ Valid promo code application - ✅ Invalid promo code handling - ✅ Promo code with mixed tickets - ✅ Promo code with free tickets - ✅ Promo code discount limits (100% discount) - ✅ Failed orders don't affect remaining tickets
TestOrderValidations Class: - ✅ Name validation (valid names, special characters, length limits) - ✅ Show validation (existence, published status, past shows) - ✅ Quantity validation (positive integers, invalid formats) - ✅ Donation validation (non-negative, within limits) - ✅ Request parameters validation - ✅ Producer financial validation - ✅ Promo code validation (case sensitivity, show-specific)
2. test_order_race_condition.py (591 lines)¶
TicketPurchaseRaceConditionTest Class: - ✅ Concurrent purchase prevents overselling - ✅ Lock timeout extended to 120 seconds - ✅ Lock uses unique UUID values - ✅ Lock released after successful transaction - ✅ Lock released after failed transaction - ✅ Multiple tickets all locks acquired - ✅ Lock contention metrics logged - ✅ Stripe call happens outside locked section
LockSafetyTest Class: - ✅ Lock value prevents accidental release - ✅ Expired lock handling
Coverage Metrics (Estimated)¶
| Component | Coverage % | Notes |
|---|---|---|
| Order Creation | 85% | Good coverage of happy path and basic validations |
| Promo Codes | 90% | Comprehensive promo code testing |
| Race Conditions | 95% | Excellent lock mechanism coverage |
| Payment Processing | 40% | Mocked Stripe, minimal failure scenarios |
| Error Recovery | 30% | Limited database/network failure testing |
| Performance | 10% | No load testing or benchmarks |
| End-to-End | 20% | Mostly unit tests, few integration tests |
Test Coverage Gaps¶
Critical Gaps (P0)¶
- Payment Processing Failures
- Stripe API timeout during checkout
- Payment declined scenarios
- Network failures during payment processing
-
Webhook delivery failures
-
Database Transaction Failures
- Database connection loss mid-transaction
- Query timeout scenarios
- Deadlock handling
-
Transaction rollback verification
-
Data Integrity
- Negative inventory prevention
- Orphaned transaction cleanup
- Order total calculation accuracy
- Tax calculation verification (automatic tax enabled)
High Priority Gaps (P1)¶
- Complex Edge Cases
- Price changes between cart view and checkout
- Show cancellation during checkout
- Ticket deletion during active checkout
- Session expiration scenarios
- Concurrent promo code usage and limits
-
Mixed currency handling (if applicable)
-
External Service Failures
- Redis unavailability during lock acquisition
- Celery task queue failures
- Email delivery failures
-
PDF generation failures
-
Security & Validation
- SQL injection attempts in checkout fields
- CSRF token validation
- Rate limiting under attack
- Maximum order amount enforcement
- XSS in customer input fields
Medium Priority Gaps (P2)¶
- Performance & Load
- Concurrent checkout capacity (100+ users)
- Database connection pool exhaustion
- Lock contention under high load
- Response time benchmarks (p50, p95, p99)
-
Memory leak detection
-
User Experience
- Clear error messages for all failure modes
- Idempotency key handling for retries
- Browser back button handling
- Multiple tab scenarios
Comprehensive Test Plan¶
Test Categories by Priority¶
P0: Critical - Core Checkout Functionality¶
These tests MUST pass for checkout to be considered functional.
P0-001: Successful Single Ticket Purchase¶
Test Name: test_single_ticket_purchase_success
Priority: P0
Description: Verify complete flow for purchasing one ticket
Prerequisites:
- Published show with available tickets
- Valid customer information
- Producer with Stripe account configured
Test Steps: 1. Create checkout session with single ticket 2. Verify Stripe session created 3. Simulate successful Stripe payment 4. Call success callback 5. Verify order marked as successful 6. Verify attendee created 7. Verify ticket inventory decremented
Expected Outcomes:
- Order created with success=True
- One TicketAttendee record created
- Ticket remaining count reduced by 1
- Email notification queued
- PDF generation queued
Mocking Strategy: - Mock Stripe API calls - Mock Celery tasks
Dependencies: None
P0-002: Successful Multiple Ticket Purchase¶
Test Name: test_multiple_tickets_purchase_success
Priority: P0
Description: Verify purchasing multiple tickets in one order
Test Steps:
1. Create checkout with 2 different ticket types, 2 quantity each
2. Complete purchase flow
3. Verify all tickets and attendees created
Expected Outcomes: - 4 total attendees created - Both ticket types inventory decremented correctly - Total calculated correctly with fees
P0-003: Free Ticket Purchase¶
Test Name: test_free_ticket_purchase_no_stripe
Priority: P0
Description: Verify free tickets don't require Stripe
Test Steps:
1. Create ticket with $0 price
2. Attempt checkout
3. Verify no Stripe session created
4. Verify order completed immediately
Expected Outcomes:
- Order created with session_id FREE-{order_id}
- No Stripe API call
- No fees charged
- Order marked successful
P0-004: Prevent Overselling (Last Ticket)¶
Test Name: test_last_ticket_purchase_atomicity
Priority: P0
Description: Verify that attempting to purchase more tickets than available fails
Test Steps:
1. Create ticket with quantity=1
2. Attempt to purchase quantity=2
3. Verify error returned
Expected Outcomes: - 400 Bad Request - Error message indicates unavailability - No order created
P0-005: Sold Out Ticket Rejection¶
Test Name: test_sold_out_ticket_rejection
Priority: P0
Description: Verify sold out tickets cannot be purchased
Prerequisites:
- Ticket with all inventory allocated to successful orders
Test Steps: 1. Create successful order consuming all inventory 2. Attempt another purchase 3. Verify rejection
Expected Outcomes: - 400 Bad Request - Clear "not available" message - Ticket inventory unchanged
P0-006: Order Total Calculation Accuracy¶
Test Name: test_order_total_calculation_with_fees
Priority: P0
Description: Verify correct calculation of totals including all fees
Test Steps:
1. Purchase 2 tickets at $25 each
2. Verify platform fees: 2 × $1.50 = $3.00
3. Verify processing fees: ((50 + 3) × 0.029) + 0.30 = $1.84
4. Verify total: $50 + $3 + $1.84 = $54.84
Expected Outcomes: - Order.total = $54.84 - Order.platform_fees = $3.00 - Order.payment_processing_fees = $1.84 - Stripe line items match exactly
P0-007: Promo Code Discount Applied Correctly¶
Test Name: test_promo_code_discount_calculation
Priority: P0
Description: Verify promo codes discount tickets correctly, not fees
Prerequisites:
- Promo code with 20% discount
Test Steps: 1. Purchase $100 worth of tickets 2. Apply 20% promo code 3. Verify discount: $20 off tickets 4. Verify fees calculated on $80 (discounted amount)
Expected Outcomes: - Ticket subtotal: $80 - Platform fees: still calculated per ticket - Processing fees: calculated on ($80 + platform_fees) - Order.promo_code_discount = $20
P0-008: Email and Phone Validation¶
Test Name: test_customer_info_validation
Priority: P0
Description: Verify customer information is properly validated
Test Steps:
1. Attempt checkout with invalid email
2. Attempt checkout with overly long name
3. Attempt checkout with special characters in name
Expected Outcomes: - Invalid email: 400 error with "Invalid email format" - Long name: 400 error with character limit message - Special characters: Accept hyphens, apostrophes, spaces; reject numbers/symbols
P0-009: Negative Quantity Prevention¶
Test Name: test_negative_quantity_rejection
Priority: P0
Description: Prevent negative or zero quantity orders
Test Steps:
1. Attempt checkout with quantity=-1
2. Attempt checkout with quantity=0
Expected Outcomes: - Negative quantity: 400 error - Zero quantity: Skipped (no order created for that ticket)
P0-010: Maximum Tickets Per Order Enforcement¶
Test Name: test_max_tickets_per_order_limit
Priority: P0
Description: Enforce MAX_TICKETS_PER_ORDER (currently 10)
Test Steps:
1. Attempt to purchase 11 tickets
2. Verify rejection
Expected Outcomes: - 400 error with "Maximum 10 tickets per order" - No order created
P1: High Priority - Important Edge Cases¶
These tests cover important scenarios that could cause data integrity issues.
P1-001: Payment Declined by Stripe¶
Test Name: test_payment_declined_order_not_completed
Priority: P1
Description: Verify orders aren't marked successful if payment declines
Test Steps:
1. Create checkout session
2. Simulate Stripe payment declined
3. Verify order remains with success=False
4. Verify ticket inventory NOT decremented
Expected Outcomes: - Order exists but success=False - Inventory unchanged - No attendees created - No emails sent
P1-002: Stripe API Timeout During Checkout¶
Test Name: test_stripe_timeout_handling
Priority: P1
Description: Handle Stripe API timeouts gracefully
Test Steps:
1. Mock Stripe to timeout
2. Attempt checkout
3. Verify error handling
Expected Outcomes: - 500 or 503 error with retry message - Order may be created but not completed - Locks released properly
P1-003: Database Connection Failure Mid-Transaction¶
Test Name: test_database_failure_during_order_creation
Priority: P1
Description: Verify proper rollback on database failure
Test Steps:
1. Mock database connection failure during order creation
2. Attempt checkout
3. Verify rollback
Expected Outcomes: - No partial order created - Inventory unchanged - Locks released - Clear error message
P1-004: Redis Unavailable During Lock Acquisition¶
Test Name: test_redis_unavailable_graceful_degradation
Priority: P1
Description: Handle Redis unavailability
Test Steps:
1. Disable Redis cache
2. Attempt checkout
3. Verify behavior
Expected Outcomes: - Either: graceful degradation to database-only locking - Or: clear error message about system unavailability - No data corruption
P1-005: Lock Expiry During Long Transaction¶
Test Name: test_lock_expiry_warning_on_slow_transaction
Priority: P1
Description: Test behavior when transaction exceeds 120s
Test Steps:
1. Mock slow database queries (>120s)
2. Attempt checkout
3. Verify lock expiry handling
Expected Outcomes: - Transaction should complete (database locks still held) - Warning logged about lock expiry - No race condition (database locks prevent)
P1-006: Concurrent Promo Code Usage Limit¶
Test Name: test_promo_code_usage_limits
Priority: P1
Description: If promo codes have usage limits, test enforcement
Prerequisites:
- Promo code with max_uses (if implemented)
Test Steps: 1. Use promo code up to limit 2. Attempt to use again 3. Verify rejection
Expected Outcomes: - Rejection with "Promo code no longer valid" - Usage count incremented atomically
Note: Currently not implemented, add if feature exists
P1-007: Price Change Between View and Checkout¶
Test Name: test_price_change_during_checkout
Priority: P1
Description: Verify price at checkout time is used, not cached price
Test Steps:
1. View ticket with price $25
2. Admin changes price to $30
3. Attempt checkout
4. Verify $30 price used
Expected Outcomes: - TicketOrder.price_per_ticket reflects current price ($30) - No stale price used
P1-008: Show Unpublished During Checkout¶
Test Name: test_show_unpublished_during_checkout
Priority: P1
Description: Handle show being unpublished mid-checkout
Test Steps:
1. Start checkout for published show
2. Admin unpublishes show
3. Complete checkout attempt
4. Verify rejection
Expected Outcomes: - 400 error "Show not currently available" - No order created
P1-009: Ticket Deleted During Active Checkout¶
Test Name: test_ticket_deleted_during_checkout
Priority: P1
Description: Handle ticket deletion during checkout
Test Steps:
1. Acquire lock for ticket
2. Admin attempts to delete ticket (should wait or fail)
3. Complete or cancel checkout
4. Verify integrity
Expected Outcomes: - Ticket deletion waits for lock release (select_for_update) - Or deletion fails with lock held - No orphaned references
P1-010: Order Success Callback Fired Twice¶
Test Name: test_duplicate_success_callback_idempotency
Priority: P1
Description: Verify idempotency of success callback
Test Steps:
1. Complete order successfully
2. Call success callback again with same session_id
3. Verify no duplicate processing
Expected Outcomes: - Second callback does nothing (order.success already True) - No duplicate attendees created - No duplicate emails sent - Redirects to show page
P1-011: Maximum Order Amount Enforcement¶
Test Name: test_max_order_amount_limit
Priority: P1
Description: Enforce MAX_ORDER_AMOUNT ($5000)
Test Steps:
1. Create high-priced tickets
2. Attempt order totaling >$5000
3. Verify rejection
Expected Outcomes: - 400 error "Order total exceeds maximum amount of $5000" - No order created
P1-012: Donation Amount Limits¶
Test Name: test_donation_amount_limits
Priority: P1
Description: Enforce MAX_DONATION_PER_TICKET ($1000)
Test Steps:
1. Attempt donation of $1001
2. Verify rejection
Expected Outcomes: - 400 error with donation limit message
P1-013: Donation Below Minimum for Donation Tickets¶
Test Name: test_donation_below_minimum_rejected
Priority: P1
Description: Donation tickets require at least the base price
Prerequisites:
- Donation ticket with base price $10
Test Steps: 1. Attempt donation of $5 2. Verify rejection
Expected Outcomes: - 400 error "Donation amount must be at least $10"
P1-014: Non-Donation Tickets Cannot Accept Donations¶
Test Name: test_regular_ticket_no_donation_accepted
Priority: P1
Description: Regular tickets cannot have additional donations
Test Steps:
1. Attempt to add donation to regular ticket
2. Verify rejection
Expected Outcomes: - 400 error "Additional donations not allowed for non-donation ticket"
P1-015: Past Show Rejection¶
Test Name: test_past_show_checkout_rejection
Priority: P1
Description: Cannot purchase tickets for shows that have ended
Test Steps:
1. Set show end_time to past
2. Attempt checkout
3. Verify rejection
Expected Outcomes: - 400 error "This show has already occurred" - No order created
P1-016: Unpublished Show Rejection¶
Test Name: test_unpublished_show_checkout_rejection
Priority: P1
Description: Cannot purchase tickets for unpublished shows
Test Steps:
1. Set show published=False
2. Attempt checkout
3. Verify rejection
Expected Outcomes: - 400 error "This show is not currently available"
P1-017: Producer Without Stripe Account¶
Test Name: test_producer_no_stripe_account_rejection
Priority: P1
Description: Verify paid tickets require producer Stripe account
Test Steps:
1. Remove producer Stripe account
2. Attempt to purchase paid tickets
3. Verify rejection
Expected Outcomes: - 400 error "Producer payment details not configured" - Free tickets should still work
P1-018: Partial Order Failure Rollback¶
Test Name: test_partial_order_rollback_on_error
Priority: P1
Description: If one ticket in multi-ticket order fails, roll back entire order
Test Steps:
1. Attempt to purchase 2 ticket types
2. Mock failure on second ticket
3. Verify rollback
Expected Outcomes: - No order created - First ticket inventory unchanged - Atomic transaction enforced
P1-019: Stripe Transfer Metadata Accuracy¶
Test Name: test_stripe_transfer_metadata_populated
Priority: P1
Description: Verify Stripe transfer includes correct metadata
Test Steps:
1. Complete successful order
2. Verify Stripe transfer metadata includes:
- order_id
- show_id
- show_title
- attendee_count
- order_name
- order_email
Expected Outcomes: - All metadata fields populated - Transfer description accurate
P1-020: Automatic Tax Calculation¶
Test Name: test_automatic_tax_calculation
Priority: P1
Description: Verify Stripe automatic tax is enabled
Test Steps:
1. Create checkout session
2. Verify automatic_tax: {enabled: true} in Stripe session
Expected Outcomes: - Tax calculation delegated to Stripe - Tax amount not in our fee calculation
P2: Medium Priority - Performance & Resilience¶
These tests cover performance, monitoring, and nice-to-have coverage.
P2-001: Concurrent Checkout Load Test¶
Test Name: test_100_concurrent_checkouts
Priority: P2
Description: Load test with 100 concurrent users
Test Steps:
1. Create show with 100 tickets
2. Spawn 100 threads each attempting checkout
3. Verify all complete successfully
4. Measure response times
Expected Outcomes: - All 100 orders complete - p95 response time < 10 seconds - No lock timeouts - No database connection pool exhaustion
Tools: pytest-xdist, locust, or similar
P2-002: Database Connection Pool Exhaustion¶
Test Name: test_connection_pool_under_load
Priority: P2
Description: Test behavior when connection pool exhausted
Test Steps:
1. Reduce connection pool size
2. Generate high load
3. Verify graceful handling
Expected Outcomes: - Requests queue or fail gracefully - No crashes - Connections released after transactions
P2-003: Lock Contention Performance¶
Test Name: test_lock_wait_time_under_contention
Priority: P2
Description: Measure lock wait time during contention
Test Steps:
1. 50 concurrent attempts for same ticket
2. Measure time each waits for lock
3. Verify reasonable performance
Expected Outcomes: - Lock acquisition p95 < 1 second - Proper queuing behavior - No starvation
P2-004: Memory Leak Detection¶
Test Name: test_no_memory_leaks_over_1000_orders
Priority: P2
Description: Verify no memory leaks in checkout flow
Test Steps:
1. Measure baseline memory
2. Process 1000 orders
3. Force garbage collection
4. Measure final memory
Expected Outcomes: - Memory growth < 10MB - No circular references - Proper cleanup
Tools: memory_profiler, tracemalloc
P2-005: Celery Task Failure Recovery¶
Test Name: test_celery_task_failures_dont_block_checkout
Priority: P2
Description: Verify checkout succeeds even if email/PDF tasks fail
Test Steps:
1. Mock Celery task failures
2. Complete checkout
3. Verify order still successful
Expected Outcomes: - Order completed - Errors logged but not raised - Tasks can be retried later
P2-006: Browser Back Button After Success¶
Test Name: test_back_button_after_success_idempotency
Priority: P2
Description: Verify using back button after success is safe
Test Steps:
1. Complete successful checkout
2. Simulate back button (revisit checkout endpoint)
3. Verify no duplicate processing
Expected Outcomes: - Redirect or error (order already exists) - No duplicate charge
P2-007: Multiple Tabs Same Checkout¶
Test Name: test_multiple_tabs_same_checkout_race
Priority: P2
Description: User opens checkout in multiple tabs
Test Steps:
1. Initiate checkout in tab 1
2. Acquire lock
3. Attempt checkout in tab 2
4. Verify proper handling
Expected Outcomes: - One succeeds, one gets "being processed" error - No duplicate orders
P2-008: Long-Running Transaction Monitoring¶
Test Name: test_transaction_duration_logging
Priority: P2
Description: Verify transaction duration is logged
Test Steps:
1. Complete checkout
2. Verify logs include:
- "Lock acquired for tickets..."
- "Transaction completed in X.XXs"
- "Order {id} created in X.XXs"
Expected Outcomes: - All timing logs present - Can monitor for slow transactions
P2-009: Stripe Webhook Replay Attack Prevention¶
Test Name: test_stripe_webhook_signature_validation
Priority: P2
Description: Verify webhook signature validation (if implemented)
Prerequisites:
- Stripe webhook handler exists
Test Steps: 1. Send webhook with invalid signature 2. Verify rejection
Expected Outcomes: - Webhook rejected - No order processing
Note: May not be implemented yet
P2-010: Rate Limiting Under Attack¶
Test Name: test_checkout_rate_limiting
Priority: P2
Description: Verify rate limiting prevents abuse
Prerequisites:
- Rate limiting configured
Test Steps: 1. Make 100 rapid checkout attempts 2. Verify rate limiting kicks in
Expected Outcomes: - 429 Too Many Requests after threshold - System remains stable
Note: May not be implemented yet
P2-011: SQL Injection Attempt in Checkout¶
Test Name: test_sql_injection_prevention
Priority: P2
Description: Verify Django ORM prevents SQL injection
Test Steps:
1. Submit checkout with SQL in name: '; DROP TABLE orders; --
2. Verify no execution
Expected Outcomes: - Stored as literal string - No SQL execution - Order created safely
P2-012: XSS Attempt in Customer Fields¶
Test Name: test_xss_prevention_in_customer_input
Priority: P2
Description: Verify XSS payloads are sanitized
Test Steps:
1. Submit name with <script>alert('xss')</script>
2. Retrieve order
3. Verify script not executed
Expected Outcomes: - Stored safely - HTML escaped on display - No script execution
P2-013: CSRF Token Validation¶
Test Name: test_csrf_protection_on_checkout
Priority: P2
Description: Verify CSRF protection (if POST)
Note: Current implementation uses GET, consider if this should be POST
Expected Outcomes: - CSRF token required if POST - Invalid token rejected
P2-014: Orphaned Order Cleanup¶
Test Name: test_abandoned_order_cleanup
Priority: P2
Description: Test cleanup of orders that were never completed
Prerequisites:
- Cleanup task exists
Test Steps: 1. Create order with success=False 2. Wait beyond cleanup period 3. Run cleanup task 4. Verify order handled appropriately
Expected Outcomes: - Old failed orders archived or deleted - Inventory released
Note: May not be implemented yet
P2-015: Stripe Idempotency Key Usage¶
Test Name: test_stripe_idempotency_key_on_retry
Priority: P2
Description: Verify Stripe calls use idempotency keys
Test Steps:
1. Mock network failure on first Stripe call
2. Retry
3. Verify idempotency key used
Expected Outcomes: - Same idempotency key on retry - No duplicate charges
Note: May not be implemented yet
Test Coverage Summary by Category¶
| Category | P0 Tests | P1 Tests | P2 Tests | Total |
|---|---|---|---|---|
| Core Checkout | 10 | 0 | 0 | 10 |
| Edge Cases | 0 | 20 | 0 | 20 |
| Performance | 0 | 0 | 7 | 7 |
| Security | 0 | 0 | 3 | 3 |
| Resilience | 0 | 0 | 5 | 5 |
| Total | 10 | 20 | 15 | 45 |
Existing Coverage: ~15 tests (test_order_view.py + test_order_race_condition.py) New Tests Needed: ~30 tests Total Comprehensive Coverage: ~45 tests
Implementation Strategy¶
Phase 1: P0 Tests (Week 1)¶
Goal: Ensure all critical functionality is covered
Tasks: 1. Implement P0-001 through P0-010 (10 tests) 2. Ensure all tests pass with current implementation 3. Document any bugs found
Estimated Time: 16-20 hours
Deliverables:
- test_checkout_core.py with all P0 tests
- Bug reports for any issues found
Phase 2: P1 Tests (Week 2-3)¶
Goal: Cover important edge cases and failure scenarios
Tasks: 1. Implement P1-001 through P1-020 (20 tests) 2. May require adding mocking infrastructure 3. May uncover bugs in error handling
Estimated Time: 24-32 hours
Deliverables:
- test_checkout_edge_cases.py with all P1 tests
- test_checkout_failures.py for failure scenarios
- Error handling improvements (if needed)
Phase 3: P2 Tests (Week 4)¶
Goal: Performance, security, and resilience testing
Tasks: 1. Implement P2-001 through P2-015 (15 tests) 2. Set up load testing infrastructure 3. Add security testing tools 4. Performance benchmarking
Estimated Time: 20-28 hours
Deliverables:
- test_checkout_performance.py for load tests
- test_checkout_security.py for security tests
- Performance baselines documented
Testing Framework & Tools¶
Primary Framework: pytest with Django
Required Plugins:
pytest>=7.4.0
pytest-django>=4.5.0
pytest-cov>=4.1.0 # Coverage reporting
pytest-xdist>=3.3.1 # Parallel test execution
pytest-timeout>=2.1.0 # Timeout handling
pytest-mock>=3.11.1 # Enhanced mocking
Additional Tools:
factory-boy>=3.3.0 # Test data factories
faker>=19.0.0 # Fake data generation
freezegun>=1.2.2 # Time mocking
responses>=0.23.0 # HTTP mocking
locust>=2.15.0 # Load testing (P2)
memory-profiler>=0.61.0 # Memory profiling (P2)
Mocking Strategy:
-
Stripe API:
-
Redis Cache:
-
Celery Tasks:
Test Data & Fixtures¶
Shared Fixtures (conftest.py)¶
import pytest
from decimal import Decimal
from datetime import timedelta
from django.utils import timezone
@pytest.fixture
def test_producer():
"""Create a test producer with Stripe account."""
from producers.models import Producer, ProducerFinancial
producer = Producer.objects.create(name="Test Producer")
ProducerFinancial.objects.create(
producer=producer,
stripe_account_id="acct_test123",
stripe_account_link="https://test.stripe.com/acct_test123",
)
return producer
@pytest.fixture
def test_show(test_producer):
"""Create a test show."""
from tickets.models import Show
show_time = timezone.now() + timedelta(days=7)
return Show.objects.create(
title="Test Show",
description="A test show",
door_time=show_time,
start_time=show_time + timedelta(hours=1),
end_time=show_time + timedelta(hours=3),
published=True,
producer=test_producer,
)
@pytest.fixture
def test_ticket(test_show):
"""Create a test ticket."""
from tickets.models import Ticket
return Ticket.objects.create(
show=test_show,
name="General Admission",
description="Standard ticket",
price=Decimal("25.00"),
quantity=100,
is_donation_based=False,
)
@pytest.fixture
def test_promo_code(test_show):
"""Create a test promo code."""
from tickets.models import TicketPromoCode
return TicketPromoCode.objects.create(
code="TEST20",
discount=Decimal("0.20"), # 20% off
show=test_show,
)
Test Data Factories (Using factory_boy)¶
import factory
from factory.django import DjangoModelFactory
from decimal import Decimal
from datetime import timedelta
from django.utils import timezone
class ProducerFactory(DjangoModelFactory):
class Meta:
model = 'producers.Producer'
name = factory.Faker('company')
class ProducerFinancialFactory(DjangoModelFactory):
class Meta:
model = 'producers.ProducerFinancial'
producer = factory.SubFactory(ProducerFactory)
stripe_account_id = factory.Sequence(lambda n: f'acct_test{n}')
stripe_account_link = factory.LazyAttribute(
lambda obj: f'https://test.stripe.com/{obj.stripe_account_id}'
)
class ShowFactory(DjangoModelFactory):
class Meta:
model = 'tickets.Show'
title = factory.Faker('catch_phrase')
description = factory.Faker('paragraph')
producer = factory.SubFactory(ProducerFactory)
published = True
@factory.lazy_attribute
def door_time(self):
return timezone.now() + timedelta(days=7)
@factory.lazy_attribute
def start_time(self):
return self.door_time + timedelta(hours=1)
@factory.lazy_attribute
def end_time(self):
return self.start_time + timedelta(hours=3)
class TicketFactory(DjangoModelFactory):
class Meta:
model = 'tickets.Ticket'
show = factory.SubFactory(ShowFactory)
name = factory.Faker('word')
description = factory.Faker('sentence')
price = factory.Faker('pydecimal', left_digits=2, right_digits=2, positive=True)
quantity = 100
is_donation_based = False
CI/CD Integration¶
Test Execution Strategy¶
Local Development:
# Run all tests
pytest apps/api/tickets/tests/
# Run specific priority
pytest apps/api/tickets/tests/ -m "p0"
# Run with coverage
pytest apps/api/tickets/tests/ --cov=tickets --cov-report=html
# Run in parallel
pytest apps/api/tickets/tests/ -n auto
Continuous Integration (GitHub Actions):
name: Checkout Tests
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.12'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pytest pytest-django pytest-cov pytest-xdist
- name: Run P0 Tests (Critical)
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db
REDIS_URL: redis://localhost:6379/0
run: |
pytest apps/api/tickets/tests/ -m "p0" --cov=tickets
- name: Run P1 Tests (High Priority)
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db
REDIS_URL: redis://localhost:6379/0
run: |
pytest apps/api/tickets/tests/ -m "p1" --cov=tickets --cov-append
- name: Upload coverage
uses: codecov/codecov-action@v3
Test Markers (pytest.ini)¶
[pytest]
DJANGO_SETTINGS_MODULE = brktickets.settings
python_files = test_*.py
python_classes = Test*
python_functions = test_*
markers =
p0: Critical tests that must pass
p1: High priority tests for edge cases
p2: Medium priority tests for performance and security
slow: Tests that take more than 1 second
integration: Integration tests requiring external services
unit: Unit tests with no external dependencies
Coverage Requirements¶
Target Coverage: - Overall: 90% - Core checkout views: 95% - Validation functions: 100% - Models: 85%
Coverage Exclusions: - Migration files - Test files - Settings files - Development-only code
Appendices¶
Appendix A: Test Template¶
"""
Template for new checkout tests.
Priority: P0 | P1 | P2
Category: Core | Edge Case | Performance | Security | Resilience
"""
import pytest
from decimal import Decimal
from django.urls import reverse
from rest_framework import status
from unittest.mock import patch, MagicMock
class TestCheckoutFeature:
"""Test suite for [specific feature]."""
@pytest.mark.p0 # or p1, p2
def test_specific_scenario(self, test_show, test_ticket):
"""
Test [specific scenario description].
Prerequisites:
- [List any prerequisites]
Test Steps:
1. [Step 1]
2. [Step 2]
3. [Step 3]
Expected Outcomes:
- [Expected outcome 1]
- [Expected outcome 2]
"""
# Arrange
# ... setup code ...
# Act
# ... action code ...
# Assert
# ... assertions ...
Appendix B: Common Assertions¶
# Order assertions
assert order.success == True
assert order.total == expected_total
assert order.platform_fees == expected_platform_fees
assert order.payment_processing_fees == expected_processing_fees
# Ticket inventory assertions
ticket.refresh_from_db()
assert ticket.quantity == expected_quantity
# Error response assertions
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert response.data["status"] == "error"
assert "message" in response.data
# Lock assertions (from cache)
lock_key = f"ticket_lock_{ticket.id}"
assert cache.get(lock_key) is None # Lock released
# Email/task assertions
mock_send_email.assert_called_once()
assert mock_send_email.call_args[0][0]["order_id"] == order.id
Appendix C: Performance Benchmarks¶
Target Performance Metrics:
| Metric | Target | Current | Notes |
|---|---|---|---|
| Checkout response time (p50) | < 2s | TBD | Includes Stripe call |
| Checkout response time (p95) | < 5s | TBD | |
| Lock acquisition time (p95) | < 100ms | TBD | |
| Transaction duration (p95) | < 3s | TBD | Database operations only |
| Concurrent capacity | 100+ users | TBD | No lock timeouts |
| Lock contention (p95) | < 1s wait | TBD | Time waiting for lock |
Measurement Tools: - Django Debug Toolbar (local) - pytest-benchmark - locust (load testing) - Application Performance Monitoring (APM) in production
Appendix D: Known Issues & Limitations¶
Current Limitations: 1. Checkout uses GET instead of POST (should be POST for mutations) 2. No webhook handler for Stripe events (relies on redirect callbacks) 3. No promo code usage limits implemented 4. No rate limiting on checkout endpoint 5. No abandoned cart cleanup 6. Tax calculation delegated entirely to Stripe
Security Considerations: 1. Stripe API key must be kept secure 2. Redis should be password-protected in production 3. Database connection should use SSL 4. Customer email addresses are PII - handle according to GDPR
Appendix E: Glossary¶
Terms: - Order: Complete purchase transaction with customer info - TicketOrder: Line item in an order for specific ticket type - Attendee: Individual person attending (one per ticket) - Promo Code: Discount code for percentage off - Lock: Redis-based distributed lock to prevent race conditions - Session: Stripe Checkout Session for payment processing - Transaction: Database atomic transaction boundary
Document Approval¶
| Role | Name | Date | Signature |
|---|---|---|---|
| Developer | |||
| QA Lead | |||
| Tech Lead | |||
| Product Manager |
Next Steps: 1. Review and approve this test plan 2. Create GitHub issues for each test phase 3. Begin Phase 1 implementation (P0 tests) 4. Schedule regular test coverage reviews 5. Integrate into CI/CD pipeline
This document is a living document and should be updated as the checkout system evolves.