Skip to content

Commit 81797e4

Browse files
committed
add IntegrationTests and test app
1 parent edb2f24 commit 81797e4

22 files changed

Lines changed: 3163 additions & 0 deletions

IntegrationTests/README.md

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
# Integration Tests
2+
3+
Recording mParticle Web SDK API requests using WireMock for integration testing.
4+
5+
## Prerequisites
6+
7+
Before getting started, ensure you have the following installed:
8+
9+
- **Node.js** (v18 or higher)
10+
- **Docker** (for running WireMock locally)
11+
- **Python 3** (for mapping transformation scripts)
12+
13+
### Install Dependencies
14+
15+
```bash
16+
cd IntegrationTests
17+
npm install
18+
```
19+
20+
This will install:
21+
- **Playwright** - Headless browser for running tests
22+
- **http-server** - Local HTTP server for serving the test app
23+
24+
### Install Playwright Browsers
25+
26+
```bash
27+
npx playwright install chromium
28+
```
29+
30+
## Overview
31+
32+
This project provides tools for integration testing the mParticle Web SDK by:
33+
- Building the mParticle Web SDK from source
34+
- Running a minimal test web application in a headless browser
35+
- Recording/verifying all API traffic with WireMock
36+
37+
The framework supports two modes:
38+
1. **Recording Mode** - Captures SDK network requests to create baseline mappings
39+
2. **Verification Mode** - Validates SDK requests against stored baselines
40+
41+
## Directory Structure
42+
43+
```
44+
IntegrationTests/
45+
├── common.sh # Shared shell functions
46+
├── run_wiremock_recorder.sh # Recording mode script
47+
├── run_clean_integration_tests.sh # Verification mode script
48+
├── run-browser-test.js # Playwright browser test runner
49+
├── sanitize_mapping.py # API key sanitization script
50+
├── transform_mapping_body.py # Request body transformation script
51+
├── package.json # Node.js dependencies
52+
├── README.md # This file
53+
├── test-app/ # Test web application
54+
│ ├── index.html # HTML page
55+
│ ├── test-app.js # Test logic
56+
│ └── test-config.js # WireMock endpoint configuration
57+
└── wiremock-recordings/ # Baseline mappings
58+
├── mappings/ # Request/response mapping files
59+
└── __files/ # Response body files
60+
```
61+
62+
## Available Scripts
63+
64+
### `run_wiremock_recorder.sh` - Record API Requests
65+
66+
Records all mParticle SDK API requests using WireMock for later use in integration testing.
67+
68+
```bash
69+
./run_wiremock_recorder.sh
70+
```
71+
72+
**What it does:**
73+
1. Builds mParticle Web SDK from source (`npm run build`)
74+
2. Installs Playwright and dependencies
75+
3. Starts WireMock container in recording mode
76+
4. Starts local HTTP server for test app
77+
5. Runs test app in headless Chromium browser
78+
6. Records all API traffic to mapping files
79+
7. Displays recorded files
80+
81+
**Recorded Files:**
82+
- `wiremock-recordings/mappings/*.json` - API request/response mappings
83+
- `wiremock-recordings/__files/*` - Response body files
84+
85+
### `run_clean_integration_tests.sh` - Verify Against Baselines
86+
87+
Runs the test app and verifies all requests match the stored baseline mappings.
88+
89+
```bash
90+
./run_clean_integration_tests.sh
91+
```
92+
93+
**What it does:**
94+
1. Builds mParticle Web SDK from source
95+
2. Escapes mapping bodies for WireMock compatibility
96+
3. Starts WireMock in verification mode with stored mappings
97+
4. Runs test app in headless browser
98+
5. Verifies all requests matched mappings
99+
6. Verifies all mappings were invoked
100+
7. Returns exit code 1 on any verification failure
101+
102+
**Exit Codes:**
103+
- `0` - All tests passed
104+
- `1` - Verification failed (unmatched requests or unused mappings)
105+
106+
### `sanitize_mapping.py` - Remove API Keys
107+
108+
Sanitizes WireMock mapping files by replacing API keys with regex patterns.
109+
110+
```bash
111+
python3 sanitize_mapping.py <mapping_file> --test-name <name>
112+
```
113+
114+
**Example:**
115+
```bash
116+
python3 sanitize_mapping.py \
117+
wiremock-recordings/mappings/mapping-v1-us1-abc123-identify.json \
118+
--test-name identify
119+
```
120+
121+
**What it does:**
122+
- Replaces API keys in URLs with regex pattern `us1-[a-f0-9]+`
123+
- Renames mapping file to `mapping-{test-name}.json`
124+
- Renames body file to `body-{test-name}.json`
125+
- Updates all references in the mapping JSON
126+
127+
### `transform_mapping_body.py` - Transform Request Bodies
128+
129+
Transforms request body JSON in WireMock mappings.
130+
131+
```bash
132+
# Display as readable JSON
133+
python3 transform_mapping_body.py <mapping_file> unescape
134+
135+
# Convert back to escaped string (WireMock format)
136+
python3 transform_mapping_body.py <mapping_file> escape
137+
138+
# Replace dynamic fields and save
139+
python3 transform_mapping_body.py <mapping_file> unescape+update
140+
```
141+
142+
**Modes:**
143+
- `unescape` - Convert escaped string to formatted JSON object
144+
- `escape` - Convert JSON object back to escaped string
145+
- `unescape+update` - Parse, replace dynamic fields with `${json-unit.ignore}`, and save
146+
147+
**Dynamic fields replaced:**
148+
- `session_id`, `timestamp_unixtime_ms`, `ct`, `est`, `sct`
149+
- `id`, `mpid`, `das`, `sdk_version`, `client_generated_id`
150+
- And more (see script for full list)
151+
152+
## Development Workflow
153+
154+
### Initial Recording of API Requests
155+
156+
⚠️ **Important**: Recording requires a **real mParticle API key** and **Docker**.
157+
158+
#### Step 1: Configure Real API Key (Temporarily)
159+
160+
Edit `test-app/test-config.js` and replace the placeholder with your real API key:
161+
162+
```javascript
163+
// TEMPORARILY set your real API key for recording
164+
API_KEY: 'us1-YOUR_REAL_API_KEY_HERE', // Replace with actual key
165+
```
166+
167+
#### Step 2: Run the WireMock Recorder
168+
169+
From your **local terminal** (not Cursor terminal, to access Docker):
170+
171+
```bash
172+
cd IntegrationTests
173+
./run_wiremock_recorder.sh
174+
```
175+
176+
This will:
177+
- Build the SDK
178+
- Start WireMock in recording mode
179+
- Run all tests in a headless browser
180+
- Save recordings to `wiremock-recordings/mappings/`
181+
182+
#### Step 3: Process the Recordings
183+
184+
Sanitize and transform each recorded mapping:
185+
186+
```bash
187+
# Sanitize API keys and rename
188+
python3 sanitize_mapping.py \
189+
wiremock-recordings/mappings/mapping-xxx.json \
190+
--test-name identify
191+
192+
# Transform request bodies
193+
python3 transform_mapping_body.py \
194+
wiremock-recordings/mappings/mapping-identify.json \
195+
unescape+update
196+
```
197+
198+
#### Step 4: Revert API Key to Placeholder
199+
200+
**Critical**: After recording, restore the placeholder in `test-config.js`:
201+
202+
```javascript
203+
API_KEY: 'us1-test0000000000000000000000000', // Placeholder restored
204+
```
205+
206+
#### Step 5: Verify and Commit
207+
208+
```bash
209+
# Test with placeholder key
210+
./run_clean_integration_tests.sh
211+
212+
# If tests pass, commit
213+
git add wiremock-recordings/ test-app/test-config.js
214+
git commit -m "Add sanitized baseline mappings"
215+
```
216+
217+
### Running Integration Tests
218+
219+
```bash
220+
./run_clean_integration_tests.sh
221+
```
222+
223+
The script will:
224+
1. Rebuild the SDK with latest changes
225+
2. Run tests against stored baselines
226+
3. Report any mismatches
227+
4. Exit with code 1 if verification fails
228+
229+
## Test App Configuration
230+
231+
The test app is configured via `test-app/test-config.js`:
232+
233+
```javascript
234+
const TEST_CONFIG = {
235+
WIREMOCK_HOST: '127.0.0.1',
236+
WIREMOCK_HTTPS_PORT: '8443',
237+
API_KEY: 'us1-test0000000000000000000000000',
238+
API_SECRET: 'test-secret-key',
239+
// ...
240+
};
241+
```
242+
243+
This configuration:
244+
- Points all SDK endpoints to local WireMock (`127.0.0.1:8443`)
245+
- Uses a test API key (will be sanitized in recordings)
246+
- Works identically in local development and CI environments
247+
248+
## Adding New Tests
249+
250+
1. **Add test function** in `test-app/test-app.js`:
251+
```javascript
252+
async function testMyNewFeature() {
253+
log('Starting Test: My New Feature');
254+
// Call SDK methods
255+
mParticle.logEvent('Test Event', mParticle.EventType.Other);
256+
await waitForUpload();
257+
log('Test complete', 'success');
258+
}
259+
```
260+
261+
2. **Call the test** in `runTests()`:
262+
```javascript
263+
async function runTests() {
264+
// ... existing tests
265+
await testMyNewFeature();
266+
}
267+
```
268+
269+
3. **Record new baselines:**
270+
```bash
271+
./run_wiremock_recorder.sh
272+
```
273+
274+
4. **Sanitize and commit** the new mappings.
275+
276+
## Current Test Coverage
277+
278+
| Test | Description | Status |
279+
|------|-------------|--------|
280+
| Identity | SDK initialization with identify call | Implemented |
281+
| Simple Event | `logEvent()` with basic attributes | Implemented |
282+
| Event with Attributes & Flags | `logEvent()` with custom flags | Implemented |
283+
| Screen View | `logPageView()` | Implemented |
284+
| Commerce Event | `eCommerce.logProductAction()` purchase | Implemented |
285+
286+
### Test Details
287+
288+
1. **Identity** (`testIdentify`)
289+
- Initializes SDK with `identifyRequest`
290+
- Verifies MPID is returned
291+
- Sets user attribute
292+
293+
2. **Simple Event** (`testSimpleEvent`)
294+
- Logs `"Simple Event Name"` event
295+
- Includes `{ SimpleKey: "SimpleValue" }` attributes
296+
297+
3. **Event with Attributes & Flags** (`testEventWithCustomAttributesAndFlags`)
298+
- Logs `"Event With Attributes"` event
299+
- Includes string, number, boolean, date attributes
300+
- Includes custom flags (not forwarded to providers)
301+
302+
4. **Screen View** (`testLogScreen`)
303+
- Logs `"Home Screen"` page view
304+
- Includes page category and referrer
305+
306+
5. **Commerce Event** (`testCommerceEvent`)
307+
- Creates product ("Awesome Book", SKU: 1234567890)
308+
- Logs purchase action with transaction attributes
309+
- Includes revenue, tax, shipping, coupon code
310+

0 commit comments

Comments
 (0)