Files
PHANTOM/e2e/README.md
Claude c8ac2cb609 Add dynamic pricing E2E test suite with Playwright
Implement comprehensive E2E tests to validate the surge pricing pipeline:
- Test SimpleSurgePricer with configurable thresholds (high=3, surge=1.5x)
- Verify discount pricing when demand is below low_threshold
- Test multi-product differential pricing based on demand signals
- Validate price propagation from pipeline through Redis to API

Test infrastructure:
- Playwright configuration with custom fixtures
- Python pipeline worker for direct test execution (bypasses Airflow)
- API client for event ingestion and price verification
- Event generator for creating realistic interaction sequences
- docker-compose.e2e.yml with minimal services for testing
2025-12-26 09:35:07 +00:00

9.1 KiB
Raw Blame History

PHANTOM Dynamic Pricing E2E Test Suite

End-to-end tests validating the dynamic pricing pipeline, including SimpleSurgePricer and SessionAwarePricer functionality.

System Under Test (SUT)

┌─────────────────────────────────────────────────────────────────────────┐
│                        PHANTOM Pricing Pipeline                         │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌──────────────┐    ┌──────────────┐    ┌──────────────────────────┐  │
│  │  Test Runner │───▶│ Backend API  │───▶│ Kafka (user-interactions)│  │
│  │  (Playwright)│    │ POST /ingest │    │                          │  │
│  └──────────────┘    └──────────────┘    └────────────┬─────────────┘  │
│         │                                              │                │
│         │                                              ▼                │
│         │                                 ┌──────────────────────────┐  │
│         │                                 │   Pipeline Worker        │  │
│         │                                 │   - Fetch interactions   │  │
│         │                                 │   - Compute demand       │  │
│         │                                 │   - Apply surge pricing  │  │
│         │                                 └────────────┬─────────────┘  │
│         │                                              │                │
│         │                                              ▼                │
│         │                                 ┌──────────────────────────┐  │
│         │                                 │   Redis (Model Registry) │  │
│         │                                 │   - prices:latest        │  │
│         │                                 └────────────┬─────────────┘  │
│         │                                              │                │
│         │                                              ▼                │
│         │     ┌──────────────┐           ┌──────────────────────────┐  │
│         └────▶│ Pricing API  │◀──────────│   Pricing Provider       │  │
│               │ GET /price   │           │   (serves from Redis)    │  │
│               └──────────────┘           └──────────────────────────┘  │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Test Scenarios

Scenario Description Expected Outcome
Baseline No interactions for product Price = base_price (markup = 1.0)
Surge 5+ interactions (above threshold) Price = base_price × 1.5
Discount 1 interaction (at threshold) Price = base_price × 0.9
Multi-Product Different demand per product Each product priced by its demand
Propagation Pipeline → Redis → API Prices visible via API
Event Types Mix of view, click, cart All events counted in demand
Multi-Session Events from different sessions Demand aggregated correctly

Test Configuration

The tests use aggressive thresholds for fast feedback:

pricing: {
  highThreshold: 3,      // Surge after 3 interactions
  lowThreshold: 1,       // Discount at ≤1 interaction
  surgeMultiplier: 1.5,  // 50% price increase
  discountMultiplier: 0.9, // 10% discount
  windowSize: 10_000,    // 10 second window
}

Quick Start

1. Start E2E Services

# Start minimal services for E2E testing
docker compose -f docker-compose.e2e.yml up -d

# Wait for services to be healthy
docker compose -f docker-compose.e2e.yml ps

# Optional: Start with Kafka UI for debugging
docker compose -f docker-compose.e2e.yml --profile debug up -d

2. Install Test Dependencies

cd e2e
npm install
npx playwright install

3. Run Tests

# Run all E2E tests
npm test

# Run with UI (interactive mode)
npm run test:ui

# Run specific test file
npm run test:pricing

# Run in debug mode
npm run test:debug

# View test report
npm run test:report

4. Cleanup

docker compose -f docker-compose.e2e.yml down -v

Environment Variables

Variable Default Description
BACKEND_URL http://localhost:5000 Backend API URL
PROVIDER_URL http://localhost:5001 Pricing Provider URL
REDIS_HOST localhost Redis host
REDIS_PORT 6378 Redis port
KAFKA_HOST localhost Kafka host
KAFKA_PORT 9092 Kafka port

Test Architecture

e2e/
├── playwright.config.ts    # Playwright configuration
├── global-setup.ts         # Service health checks
├── global-teardown.ts      # Cleanup
├── package.json            # Dependencies and scripts
├── tsconfig.json           # TypeScript configuration
├── lib/
│   ├── api-client.ts       # API interaction utilities
│   ├── event-generator.ts  # Test event factory
│   ├── pipeline-runner.ts  # TypeScript pipeline wrapper
│   ├── pipeline-worker.py  # Python pipeline executor
│   ├── fixtures.ts         # Playwright test fixtures
│   └── index.ts            # Re-exports
└── tests/
    └── dynamic-pricing.spec.ts  # Main test file

Pipeline Worker

The tests use a dedicated Python pipeline worker (lib/pipeline-worker.py) instead of Airflow for faster, more reliable test execution.

# Run pipeline manually
python3 lib/pipeline-worker.py \
  --store-mode hotel \
  --high-threshold 3 \
  --surge-multiplier 1.5 \
  --json-output

# Dry run (no Redis publish)
python3 lib/pipeline-worker.py --dry-run

Debugging

View Kafka Events

# Via API
curl "http://localhost:5000/api/kafka/dump?topic=user-interactions&last_n=10"

# Via Redpanda Console (if started with --profile debug)
open http://localhost:8080

Check Redis State

docker exec -it PHANTOM-e2e-redis redis-cli
> GET prices:latest
> KEYS *

View Pipeline Logs

The pipeline worker logs detailed information:

[INFO] Starting E2E pricing pipeline: mode=hotel, high_threshold=3, surge_multiplier=1.5
[INFO] Fetched 15 interaction records
[INFO] Computed demand for 3 products
[INFO] Applied surge pricing:
  e2e-test...: base=$100.00 -> optimal=$150.00 (demand=5, markup=1.50x)
[INFO] Published 3 prices to Redis

Writing New Tests

import { test, expect } from '../lib/fixtures';
import { generateTestProductId } from '../lib/event-generator';

test('my new pricing test', async ({ api, events, triggerPriceUpdate }) => {
  // 1. Create unique product ID
  const productId = generateTestProductId('my-test');

  // 2. Log base price
  await api.logPrice({
    productId,
    price: 100.0,
    sessionId: events.session,
    storeMode: 'hotel',
  });

  // 3. Generate events
  const surgeEvents = events.generateSurgeSequence(productId, 5);
  await api.ingestEvents(surgeEvents);

  // 4. Trigger pipeline
  const result = await triggerPriceUpdate();

  // 5. Verify results
  expect(result.success).toBe(true);
  const pricedProduct = result.prices?.find(p => p.productId === productId);
  expect(pricedProduct?.optimal_price).toBeGreaterThan(100);
});

Troubleshooting

"Backend not available"

Ensure services are running:

docker compose -f docker-compose.e2e.yml ps
docker compose -f docker-compose.e2e.yml logs backend

"No interactions found"

Check Kafka topic has events:

curl "http://localhost:5000/api/kafka/dump?topic=user-interactions"

"Pipeline timeout"

Increase timeout in playwright.config.ts:

timeout: 180_000, // 3 minutes

"Price not updated"

Check Redis has latest prices:

docker exec -it PHANTOM-e2e-redis redis-cli GET prices:latest