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
This commit is contained in:
Claude
2025-12-26 09:35:07 +00:00
parent f2271e368e
commit c8ac2cb609
14 changed files with 2095 additions and 0 deletions

255
e2e/README.md Normal file
View File

@@ -0,0 +1,255 @@
# 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:
```typescript
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
```bash
# 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
```bash
cd e2e
npm install
npx playwright install
```
### 3. Run Tests
```bash
# 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
```bash
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.
```bash
# 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
```bash
# 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
```bash
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
```typescript
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:
```bash
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:
```bash
curl "http://localhost:5000/api/kafka/dump?topic=user-interactions"
```
### "Pipeline timeout"
Increase timeout in `playwright.config.ts`:
```typescript
timeout: 180_000, // 3 minutes
```
### "Price not updated"
Check Redis has latest prices:
```bash
docker exec -it PHANTOM-e2e-redis redis-cli GET prices:latest
```