mirror of
https://github.com/velocitatem/PHANTOM.git
synced 2026-05-31 08:33:36 +00:00
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:
255
e2e/README.md
Normal file
255
e2e/README.md
Normal 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
|
||||
```
|
||||
Reference in New Issue
Block a user