wup 0.2.15__tar.gz → 0.2.20__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {wup-0.2.15/wup.egg-info → wup-0.2.20}/PKG-INFO +117 -12
- {wup-0.2.15 → wup-0.2.20}/README.md +116 -11
- {wup-0.2.15 → wup-0.2.20}/pyproject.toml +1 -1
- wup-0.2.20/tests/test_web_client.py +167 -0
- {wup-0.2.15 → wup-0.2.20}/wup/__init__.py +1 -1
- wup-0.2.20/wup/anomaly_detector.py +593 -0
- wup-0.2.20/wup/assistant.py +692 -0
- {wup-0.2.15 → wup-0.2.20}/wup/cli.py +28 -0
- {wup-0.2.15 → wup-0.2.20}/wup/config.py +78 -4
- {wup-0.2.15 → wup-0.2.20}/wup/models/__init__.py +21 -8
- {wup-0.2.15 → wup-0.2.20}/wup/models/config.py +38 -1
- {wup-0.2.15 → wup-0.2.20}/wup/testql_watcher.py +67 -5
- {wup-0.2.15 → wup-0.2.20}/wup/visual_diff.py +64 -9
- wup-0.2.20/wup/web_client.py +178 -0
- {wup-0.2.15 → wup-0.2.20/wup.egg-info}/PKG-INFO +117 -12
- {wup-0.2.15 → wup-0.2.20}/wup.egg-info/SOURCES.txt +4 -0
- {wup-0.2.15 → wup-0.2.20}/LICENSE +0 -0
- {wup-0.2.15 → wup-0.2.20}/setup.cfg +0 -0
- {wup-0.2.15 → wup-0.2.20}/tests/test_e2e.py +0 -0
- {wup-0.2.15 → wup-0.2.20}/tests/test_testql_watcher.py +0 -0
- {wup-0.2.15 → wup-0.2.20}/tests/test_wup.py +0 -0
- {wup-0.2.15 → wup-0.2.20}/wup/core.py +0 -0
- {wup-0.2.15 → wup-0.2.20}/wup/dependency_mapper.py +0 -0
- {wup-0.2.15 → wup-0.2.20}/wup/testql_discovery.py +0 -0
- {wup-0.2.15 → wup-0.2.20}/wup.egg-info/dependency_links.txt +0 -0
- {wup-0.2.15 → wup-0.2.20}/wup.egg-info/entry_points.txt +0 -0
- {wup-0.2.15 → wup-0.2.20}/wup.egg-info/requires.txt +0 -0
- {wup-0.2.15 → wup-0.2.20}/wup.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wup
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.20
|
|
4
4
|
Summary: WUP (What's Up) - Intelligent file watcher for regression testing in large projects
|
|
5
5
|
Author-email: Tom Sapletta <tom@sapletta.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -29,17 +29,17 @@ Dynamic: license-file
|
|
|
29
29
|
|
|
30
30
|
## AI Cost Tracking
|
|
31
31
|
|
|
32
|
-
    
|
|
33
|
+
  
|
|
34
34
|
|
|
35
|
-
- 🤖 **LLM usage:** $
|
|
36
|
-
- 👤 **Human dev:** ~$
|
|
35
|
+
- 🤖 **LLM usage:** $4.6500 (31 commits)
|
|
36
|
+
- 👤 **Human dev:** ~$732 (7.3h @ $100/h, 30min dedup)
|
|
37
37
|
|
|
38
38
|
Generated on 2026-04-29 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
42
|
-
    
|
|
43
43
|
|
|
44
44
|
**WUP (What's Up)** - Intelligent file watcher for regression testing in large projects.
|
|
45
45
|
|
|
@@ -74,16 +74,19 @@ pip install -e ".[dev]"
|
|
|
74
74
|
## Quick Start
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
|
-
# 1.
|
|
78
|
-
wup
|
|
77
|
+
# 1. Interactive configuration (recommended)
|
|
78
|
+
wup assistant
|
|
79
|
+
|
|
80
|
+
# 2. Or quick auto-setup
|
|
81
|
+
wup assistant --quick --template fastapi
|
|
79
82
|
|
|
80
|
-
#
|
|
83
|
+
# 3. Build dependency map (one-time setup)
|
|
81
84
|
wup map-deps ./my-project
|
|
82
85
|
|
|
83
|
-
#
|
|
86
|
+
# 4. Start watching for changes
|
|
84
87
|
wup watch ./my-project
|
|
85
88
|
|
|
86
|
-
#
|
|
89
|
+
# 5. Start with live dashboard
|
|
87
90
|
wup watch ./my-project --dashboard
|
|
88
91
|
```
|
|
89
92
|
|
|
@@ -186,6 +189,16 @@ Full regression: 15s test → 15% CPU spike
|
|
|
186
189
|
|
|
187
190
|
## Configuration
|
|
188
191
|
|
|
192
|
+
### Service Types
|
|
193
|
+
|
|
194
|
+
WUP supports three service types for coincidence detection and intelligent testing:
|
|
195
|
+
|
|
196
|
+
- **web** - HTTP/API services (FastAPI, Flask, Django, Express.js, etc.)
|
|
197
|
+
- **shell** - CLI tools, scripts, and command-line services
|
|
198
|
+
- **auto** - Automatic detection (default)
|
|
199
|
+
|
|
200
|
+
Coincidence detection allows WUP to identify related services. For example, if you have `users-web` and `users-shell`, WUP will detect they share the same domain and test both when relevant files change.
|
|
201
|
+
|
|
189
202
|
### wup.yaml Configuration File
|
|
190
203
|
|
|
191
204
|
WUP supports declarative configuration via `wup.yaml` (or `.wup.yaml`) in your project root. This allows you to define watch paths, service-specific settings, and test strategies.
|
|
@@ -196,6 +209,11 @@ Generate a default configuration:
|
|
|
196
209
|
wup init
|
|
197
210
|
```
|
|
198
211
|
|
|
212
|
+
The generated `wup.yaml` includes:
|
|
213
|
+
- **Metadata header**: Version, generation date, documentation links
|
|
214
|
+
- **Dependencies info**: WUP version and optional wupbro dashboard
|
|
215
|
+
- **Quick start guide**: Common commands to get started
|
|
216
|
+
|
|
199
217
|
Example `wup.yaml`:
|
|
200
218
|
|
|
201
219
|
```yaml
|
|
@@ -333,6 +351,39 @@ Visible in `wup status` as a "Visual DOM diffs" section.
|
|
|
333
351
|
|
|
334
352
|
If Playwright is not installed, the visual diff module logs a warning and skips scanning — it does **not** break the watcher.
|
|
335
353
|
|
|
354
|
+
## Web Dashboard (wupbro)
|
|
355
|
+
|
|
356
|
+
Optional FastAPI backend that receives events from WUP agents and renders a live dashboard.
|
|
357
|
+
|
|
358
|
+
### Run
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
pip install -e wupbro/
|
|
362
|
+
wupbro --reload --port 8000
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Open <http://localhost:8000/> to see regressions, passes, anomalies, visual diffs, and health transitions in real time.
|
|
366
|
+
|
|
367
|
+
### Configure agent → backend
|
|
368
|
+
|
|
369
|
+
```yaml
|
|
370
|
+
# wup.yaml
|
|
371
|
+
web:
|
|
372
|
+
enabled: true
|
|
373
|
+
endpoint: "http://localhost:8000"
|
|
374
|
+
timeout_s: 2.0
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Or via env:
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
export WUPBRO_ENDPOINT=http://localhost:8000
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
The agent fire-and-forgets `REGRESSION`, `PASS`, `ANOMALY`, `VISUAL_DIFF`, and `HEALTH_TRANSITION` events. Network errors never break the watcher (soft-fail).
|
|
384
|
+
|
|
385
|
+
See `wupbro/README.md` for full API reference and driver endpoints (DOM diff, browserless, anomaly).
|
|
386
|
+
|
|
336
387
|
## Project Structure
|
|
337
388
|
|
|
338
389
|
```
|
|
@@ -346,12 +397,21 @@ wup/
|
|
|
346
397
|
│ ├── testql_discovery.py # TestQLEndpointDiscovery: scenario parsing
|
|
347
398
|
│ ├── testql_watcher.py # TestQLWatcher: scenario runner + health tracking
|
|
348
399
|
│ ├── visual_diff.py # VisualDiffer: Playwright DOM snapshot + diff engine
|
|
400
|
+
│ ├── web_client.py # WebClient: async HTTP event sink → wupbro
|
|
349
401
|
│ └── models/
|
|
350
402
|
│ ├── __init__.py
|
|
351
|
-
│ └── config.py # Dataclasses: WupConfig, VisualDiffConfig,
|
|
403
|
+
│ └── config.py # Dataclasses: WupConfig, VisualDiffConfig, WebConfig...
|
|
404
|
+
├── wupbro/ # Optional FastAPI dashboard (separate package)
|
|
405
|
+
│ ├── wupbro/
|
|
406
|
+
│ │ ├── main.py # FastAPI app
|
|
407
|
+
│ │ ├── routers/ # events, drivers, dashboard
|
|
408
|
+
│ │ ├── storage.py # EventStore (in-memory + JSONL)
|
|
409
|
+
│ │ └── templates/ # index.html dashboard
|
|
410
|
+
│ └── tests/ # FastAPI endpoint tests (pytest + TestClient)
|
|
352
411
|
├── tests/
|
|
353
412
|
│ ├── test_wup.py # unit/integration tests (incl. VisualDiffer, config)
|
|
354
413
|
│ ├── test_testql_watcher.py # TestQLWatcher + VisualDiffer integration tests
|
|
414
|
+
│ ├── test_web_client.py # WebClient + WebConfig tests
|
|
355
415
|
│ └── test_e2e.py # end-to-end CLI tests
|
|
356
416
|
├── examples/
|
|
357
417
|
│ ├── fastapi-app/ # FastAPI example project
|
|
@@ -394,6 +454,18 @@ python3 examples/visual_diff_demo.py http://localhost:8100/health
|
|
|
394
454
|
|
|
395
455
|
# TestQL + visual diff integration
|
|
396
456
|
python3 examples/testql_integration.py /path/to/project
|
|
457
|
+
|
|
458
|
+
# Monorepo analysis (c2004-style large projects)
|
|
459
|
+
python3 examples/c2004_monorepo_demo.py /path/to/monorepo
|
|
460
|
+
|
|
461
|
+
# CI/CD integration patterns
|
|
462
|
+
python3 examples/ci_cd_integration.py
|
|
463
|
+
|
|
464
|
+
# Generate GitHub Actions workflow
|
|
465
|
+
python3 examples/ci_cd_integration.py --generate-github-actions
|
|
466
|
+
|
|
467
|
+
# Webhook notifications (Slack, Teams, Discord)
|
|
468
|
+
python3 examples/webhook_notifications.py
|
|
397
469
|
```
|
|
398
470
|
|
|
399
471
|
### Building & Publishing
|
|
@@ -402,6 +474,39 @@ python3 examples/testql_integration.py /path/to/project
|
|
|
402
474
|
python -m build
|
|
403
475
|
```
|
|
404
476
|
|
|
477
|
+
## Real-World Testing
|
|
478
|
+
|
|
479
|
+
WUP has been tested on production-scale projects:
|
|
480
|
+
|
|
481
|
+
- **c2004 Project** (maskservice/c2004): Large IoT platform with 21+ connect-* modules
|
|
482
|
+
- 29 services auto-detected by assistant
|
|
483
|
+
- 100+ YAML configuration files monitored
|
|
484
|
+
- Anomaly detection: 0.06s for 5 config files (~1ms/file)
|
|
485
|
+
- Framework: Custom Python/FastAPI hybrid
|
|
486
|
+
|
|
487
|
+
## Documentation
|
|
488
|
+
|
|
489
|
+
Comprehensive documentation is available in the `docs/` directory:
|
|
490
|
+
|
|
491
|
+
- **[Configuration Assistant](docs/WUP_ASSISTANT.md)** - Interactive setup guide for `wup.yaml`
|
|
492
|
+
- `wup assistant` - Interactive configuration wizard
|
|
493
|
+
- Auto-detects framework and services
|
|
494
|
+
- Intelligent suggestions and validation
|
|
495
|
+
|
|
496
|
+
- **[Anomaly Detection](docs/ANOMALY_DETECTION.md)** - Fast alternatives to Playwright
|
|
497
|
+
- Hash-based change detection (~1ms per file)
|
|
498
|
+
- YAML structure analysis
|
|
499
|
+
- Python AST diff for API changes
|
|
500
|
+
- Configure with `anomaly_detection:` in wup.yaml
|
|
501
|
+
|
|
502
|
+
- **[Browser Notifications](docs/NOTIFICATIONS.md)** - Real-time alerts in wupbro
|
|
503
|
+
- 7 notification types (regressions, status changes, recoveries)
|
|
504
|
+
- Configurable per-type with cooldown
|
|
505
|
+
- Server-Sent Events for instant delivery
|
|
506
|
+
- Browser Notifications API integration
|
|
507
|
+
|
|
508
|
+
- **[TestQL Integration](docs/TESTQL_INTEGRATION.md)** - TestQL scenario support
|
|
509
|
+
|
|
405
510
|
## License
|
|
406
511
|
|
|
407
512
|
Licensed under Apache-2.0.
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
|
|
4
4
|
## AI Cost Tracking
|
|
5
5
|
|
|
6
|
-
    
|
|
7
|
+
  
|
|
8
8
|
|
|
9
|
-
- 🤖 **LLM usage:** $
|
|
10
|
-
- 👤 **Human dev:** ~$
|
|
9
|
+
- 🤖 **LLM usage:** $4.6500 (31 commits)
|
|
10
|
+
- 👤 **Human dev:** ~$732 (7.3h @ $100/h, 30min dedup)
|
|
11
11
|
|
|
12
12
|
Generated on 2026-04-29 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
    
|
|
17
17
|
|
|
18
18
|
**WUP (What's Up)** - Intelligent file watcher for regression testing in large projects.
|
|
19
19
|
|
|
@@ -48,16 +48,19 @@ pip install -e ".[dev]"
|
|
|
48
48
|
## Quick Start
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
|
-
# 1.
|
|
52
|
-
wup
|
|
51
|
+
# 1. Interactive configuration (recommended)
|
|
52
|
+
wup assistant
|
|
53
|
+
|
|
54
|
+
# 2. Or quick auto-setup
|
|
55
|
+
wup assistant --quick --template fastapi
|
|
53
56
|
|
|
54
|
-
#
|
|
57
|
+
# 3. Build dependency map (one-time setup)
|
|
55
58
|
wup map-deps ./my-project
|
|
56
59
|
|
|
57
|
-
#
|
|
60
|
+
# 4. Start watching for changes
|
|
58
61
|
wup watch ./my-project
|
|
59
62
|
|
|
60
|
-
#
|
|
63
|
+
# 5. Start with live dashboard
|
|
61
64
|
wup watch ./my-project --dashboard
|
|
62
65
|
```
|
|
63
66
|
|
|
@@ -160,6 +163,16 @@ Full regression: 15s test → 15% CPU spike
|
|
|
160
163
|
|
|
161
164
|
## Configuration
|
|
162
165
|
|
|
166
|
+
### Service Types
|
|
167
|
+
|
|
168
|
+
WUP supports three service types for coincidence detection and intelligent testing:
|
|
169
|
+
|
|
170
|
+
- **web** - HTTP/API services (FastAPI, Flask, Django, Express.js, etc.)
|
|
171
|
+
- **shell** - CLI tools, scripts, and command-line services
|
|
172
|
+
- **auto** - Automatic detection (default)
|
|
173
|
+
|
|
174
|
+
Coincidence detection allows WUP to identify related services. For example, if you have `users-web` and `users-shell`, WUP will detect they share the same domain and test both when relevant files change.
|
|
175
|
+
|
|
163
176
|
### wup.yaml Configuration File
|
|
164
177
|
|
|
165
178
|
WUP supports declarative configuration via `wup.yaml` (or `.wup.yaml`) in your project root. This allows you to define watch paths, service-specific settings, and test strategies.
|
|
@@ -170,6 +183,11 @@ Generate a default configuration:
|
|
|
170
183
|
wup init
|
|
171
184
|
```
|
|
172
185
|
|
|
186
|
+
The generated `wup.yaml` includes:
|
|
187
|
+
- **Metadata header**: Version, generation date, documentation links
|
|
188
|
+
- **Dependencies info**: WUP version and optional wupbro dashboard
|
|
189
|
+
- **Quick start guide**: Common commands to get started
|
|
190
|
+
|
|
173
191
|
Example `wup.yaml`:
|
|
174
192
|
|
|
175
193
|
```yaml
|
|
@@ -307,6 +325,39 @@ Visible in `wup status` as a "Visual DOM diffs" section.
|
|
|
307
325
|
|
|
308
326
|
If Playwright is not installed, the visual diff module logs a warning and skips scanning — it does **not** break the watcher.
|
|
309
327
|
|
|
328
|
+
## Web Dashboard (wupbro)
|
|
329
|
+
|
|
330
|
+
Optional FastAPI backend that receives events from WUP agents and renders a live dashboard.
|
|
331
|
+
|
|
332
|
+
### Run
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
pip install -e wupbro/
|
|
336
|
+
wupbro --reload --port 8000
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Open <http://localhost:8000/> to see regressions, passes, anomalies, visual diffs, and health transitions in real time.
|
|
340
|
+
|
|
341
|
+
### Configure agent → backend
|
|
342
|
+
|
|
343
|
+
```yaml
|
|
344
|
+
# wup.yaml
|
|
345
|
+
web:
|
|
346
|
+
enabled: true
|
|
347
|
+
endpoint: "http://localhost:8000"
|
|
348
|
+
timeout_s: 2.0
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Or via env:
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
export WUPBRO_ENDPOINT=http://localhost:8000
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
The agent fire-and-forgets `REGRESSION`, `PASS`, `ANOMALY`, `VISUAL_DIFF`, and `HEALTH_TRANSITION` events. Network errors never break the watcher (soft-fail).
|
|
358
|
+
|
|
359
|
+
See `wupbro/README.md` for full API reference and driver endpoints (DOM diff, browserless, anomaly).
|
|
360
|
+
|
|
310
361
|
## Project Structure
|
|
311
362
|
|
|
312
363
|
```
|
|
@@ -320,12 +371,21 @@ wup/
|
|
|
320
371
|
│ ├── testql_discovery.py # TestQLEndpointDiscovery: scenario parsing
|
|
321
372
|
│ ├── testql_watcher.py # TestQLWatcher: scenario runner + health tracking
|
|
322
373
|
│ ├── visual_diff.py # VisualDiffer: Playwright DOM snapshot + diff engine
|
|
374
|
+
│ ├── web_client.py # WebClient: async HTTP event sink → wupbro
|
|
323
375
|
│ └── models/
|
|
324
376
|
│ ├── __init__.py
|
|
325
|
-
│ └── config.py # Dataclasses: WupConfig, VisualDiffConfig,
|
|
377
|
+
│ └── config.py # Dataclasses: WupConfig, VisualDiffConfig, WebConfig...
|
|
378
|
+
├── wupbro/ # Optional FastAPI dashboard (separate package)
|
|
379
|
+
│ ├── wupbro/
|
|
380
|
+
│ │ ├── main.py # FastAPI app
|
|
381
|
+
│ │ ├── routers/ # events, drivers, dashboard
|
|
382
|
+
│ │ ├── storage.py # EventStore (in-memory + JSONL)
|
|
383
|
+
│ │ └── templates/ # index.html dashboard
|
|
384
|
+
│ └── tests/ # FastAPI endpoint tests (pytest + TestClient)
|
|
326
385
|
├── tests/
|
|
327
386
|
│ ├── test_wup.py # unit/integration tests (incl. VisualDiffer, config)
|
|
328
387
|
│ ├── test_testql_watcher.py # TestQLWatcher + VisualDiffer integration tests
|
|
388
|
+
│ ├── test_web_client.py # WebClient + WebConfig tests
|
|
329
389
|
│ └── test_e2e.py # end-to-end CLI tests
|
|
330
390
|
├── examples/
|
|
331
391
|
│ ├── fastapi-app/ # FastAPI example project
|
|
@@ -368,6 +428,18 @@ python3 examples/visual_diff_demo.py http://localhost:8100/health
|
|
|
368
428
|
|
|
369
429
|
# TestQL + visual diff integration
|
|
370
430
|
python3 examples/testql_integration.py /path/to/project
|
|
431
|
+
|
|
432
|
+
# Monorepo analysis (c2004-style large projects)
|
|
433
|
+
python3 examples/c2004_monorepo_demo.py /path/to/monorepo
|
|
434
|
+
|
|
435
|
+
# CI/CD integration patterns
|
|
436
|
+
python3 examples/ci_cd_integration.py
|
|
437
|
+
|
|
438
|
+
# Generate GitHub Actions workflow
|
|
439
|
+
python3 examples/ci_cd_integration.py --generate-github-actions
|
|
440
|
+
|
|
441
|
+
# Webhook notifications (Slack, Teams, Discord)
|
|
442
|
+
python3 examples/webhook_notifications.py
|
|
371
443
|
```
|
|
372
444
|
|
|
373
445
|
### Building & Publishing
|
|
@@ -376,6 +448,39 @@ python3 examples/testql_integration.py /path/to/project
|
|
|
376
448
|
python -m build
|
|
377
449
|
```
|
|
378
450
|
|
|
451
|
+
## Real-World Testing
|
|
452
|
+
|
|
453
|
+
WUP has been tested on production-scale projects:
|
|
454
|
+
|
|
455
|
+
- **c2004 Project** (maskservice/c2004): Large IoT platform with 21+ connect-* modules
|
|
456
|
+
- 29 services auto-detected by assistant
|
|
457
|
+
- 100+ YAML configuration files monitored
|
|
458
|
+
- Anomaly detection: 0.06s for 5 config files (~1ms/file)
|
|
459
|
+
- Framework: Custom Python/FastAPI hybrid
|
|
460
|
+
|
|
461
|
+
## Documentation
|
|
462
|
+
|
|
463
|
+
Comprehensive documentation is available in the `docs/` directory:
|
|
464
|
+
|
|
465
|
+
- **[Configuration Assistant](docs/WUP_ASSISTANT.md)** - Interactive setup guide for `wup.yaml`
|
|
466
|
+
- `wup assistant` - Interactive configuration wizard
|
|
467
|
+
- Auto-detects framework and services
|
|
468
|
+
- Intelligent suggestions and validation
|
|
469
|
+
|
|
470
|
+
- **[Anomaly Detection](docs/ANOMALY_DETECTION.md)** - Fast alternatives to Playwright
|
|
471
|
+
- Hash-based change detection (~1ms per file)
|
|
472
|
+
- YAML structure analysis
|
|
473
|
+
- Python AST diff for API changes
|
|
474
|
+
- Configure with `anomaly_detection:` in wup.yaml
|
|
475
|
+
|
|
476
|
+
- **[Browser Notifications](docs/NOTIFICATIONS.md)** - Real-time alerts in wupbro
|
|
477
|
+
- 7 notification types (regressions, status changes, recoveries)
|
|
478
|
+
- Configurable per-type with cooldown
|
|
479
|
+
- Server-Sent Events for instant delivery
|
|
480
|
+
- Browser Notifications API integration
|
|
481
|
+
|
|
482
|
+
- **[TestQL Integration](docs/TESTQL_INTEGRATION.md)** - TestQL scenario support
|
|
483
|
+
|
|
379
484
|
## License
|
|
380
485
|
|
|
381
486
|
Licensed under Apache-2.0.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""Tests for wup.web_client and WebConfig."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
import threading
|
|
8
|
+
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
9
|
+
from typing import List, Tuple
|
|
10
|
+
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
13
|
+
from wup.models.config import WebConfig
|
|
14
|
+
from wup.web_client import WebClient, resolve_endpoint
|
|
15
|
+
|
|
16
|
+
# Skip all tests in this module if httpx is not installed
|
|
17
|
+
pytest.importorskip("httpx")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
# Lightweight HTTP recorder server (no external dep)
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
class _Recorder:
|
|
25
|
+
def __init__(self):
|
|
26
|
+
self.requests: List[Tuple[str, str, dict]] = [] # (method, path, body)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _make_handler(recorder: _Recorder, status: int = 201):
|
|
30
|
+
class Handler(BaseHTTPRequestHandler):
|
|
31
|
+
def log_message(self, *_): pass
|
|
32
|
+
|
|
33
|
+
def do_POST(self):
|
|
34
|
+
length = int(self.headers.get("Content-Length") or 0)
|
|
35
|
+
raw = self.rfile.read(length).decode("utf-8") if length else "{}"
|
|
36
|
+
try:
|
|
37
|
+
body = json.loads(raw)
|
|
38
|
+
except json.JSONDecodeError:
|
|
39
|
+
body = {"_raw": raw}
|
|
40
|
+
recorder.requests.append((self.command, self.path, body))
|
|
41
|
+
self.send_response(status)
|
|
42
|
+
self.send_header("Content-Type", "application/json")
|
|
43
|
+
self.end_headers()
|
|
44
|
+
self.wfile.write(b'{"accepted": true}')
|
|
45
|
+
|
|
46
|
+
return Handler
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@pytest.fixture
|
|
50
|
+
def recorder_server():
|
|
51
|
+
rec = _Recorder()
|
|
52
|
+
server = HTTPServer(("127.0.0.1", 0), _make_handler(rec))
|
|
53
|
+
port = server.server_address[1]
|
|
54
|
+
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
|
55
|
+
thread.start()
|
|
56
|
+
try:
|
|
57
|
+
yield f"http://127.0.0.1:{port}", rec
|
|
58
|
+
finally:
|
|
59
|
+
server.shutdown()
|
|
60
|
+
server.server_close()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# ---------------------------------------------------------------------------
|
|
64
|
+
# Tests
|
|
65
|
+
# ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
def test_resolve_endpoint_from_config():
|
|
68
|
+
cfg = WebConfig(endpoint="http://localhost:8000/")
|
|
69
|
+
assert resolve_endpoint(cfg) == "http://localhost:8000"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_resolve_endpoint_from_env(monkeypatch):
|
|
73
|
+
cfg = WebConfig(endpoint="", endpoint_env="MY_WUP_WEB")
|
|
74
|
+
monkeypatch.setenv("MY_WUP_WEB", "http://my-host:9000/")
|
|
75
|
+
assert resolve_endpoint(cfg) == "http://my-host:9000"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_resolve_endpoint_empty(monkeypatch):
|
|
79
|
+
cfg = WebConfig(endpoint="", endpoint_env="WUP_WEB_NONE")
|
|
80
|
+
monkeypatch.delenv("WUP_WEB_NONE", raising=False)
|
|
81
|
+
assert resolve_endpoint(cfg) == ""
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_is_active_false_when_disabled():
|
|
85
|
+
cfg = WebConfig(enabled=False, endpoint="http://x")
|
|
86
|
+
assert WebClient(cfg).is_active is False
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_is_active_false_when_no_endpoint():
|
|
90
|
+
cfg = WebConfig(enabled=True, endpoint="", endpoint_env="WUP_WEB_NONE")
|
|
91
|
+
assert WebClient(cfg).is_active is False
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_send_event_disabled_returns_false():
|
|
95
|
+
client = WebClient(WebConfig(enabled=False))
|
|
96
|
+
assert asyncio.run(client.send_event({"type": "REGRESSION"})) is False
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_send_event_posts_to_recorder(recorder_server):
|
|
100
|
+
base, rec = recorder_server
|
|
101
|
+
cfg = WebConfig(enabled=True, endpoint=base, timeout_s=5.0)
|
|
102
|
+
client = WebClient(cfg)
|
|
103
|
+
assert client.is_active is True
|
|
104
|
+
|
|
105
|
+
ok = asyncio.run(client.send_event({
|
|
106
|
+
"type": "REGRESSION",
|
|
107
|
+
"service": "users-web",
|
|
108
|
+
"file": "app/users/routes.py",
|
|
109
|
+
"endpoint": "/api/users",
|
|
110
|
+
"status": "fail",
|
|
111
|
+
"reason": "TestQL exit code 1",
|
|
112
|
+
}))
|
|
113
|
+
assert ok is True
|
|
114
|
+
assert len(rec.requests) == 1
|
|
115
|
+
method, path, body = rec.requests[0]
|
|
116
|
+
assert method == "POST"
|
|
117
|
+
assert path == "/events"
|
|
118
|
+
assert body["type"] == "REGRESSION"
|
|
119
|
+
assert body["service"] == "users-web"
|
|
120
|
+
assert "timestamp" in body # auto-injected
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def test_send_event_with_api_key(recorder_server):
|
|
124
|
+
base, rec = recorder_server
|
|
125
|
+
cfg = WebConfig(enabled=True, endpoint=base, api_key="secret-token")
|
|
126
|
+
client = WebClient(cfg)
|
|
127
|
+
asyncio.run(client.send_event({"type": "PASS", "service": "x"}))
|
|
128
|
+
# body received OK; auth header is set on the client side (verified by no failure)
|
|
129
|
+
assert len(rec.requests) == 1
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_send_event_swallows_connection_error():
|
|
133
|
+
"""Unreachable host must NOT raise — soft-fail by design."""
|
|
134
|
+
cfg = WebConfig(
|
|
135
|
+
enabled=True,
|
|
136
|
+
endpoint="http://127.0.0.1:1", # almost certainly closed
|
|
137
|
+
timeout_s=0.5,
|
|
138
|
+
)
|
|
139
|
+
client = WebClient(cfg)
|
|
140
|
+
ok = asyncio.run(client.send_event({"type": "REGRESSION"}))
|
|
141
|
+
assert ok is False # no exception raised
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_send_regression_helper(recorder_server):
|
|
145
|
+
base, rec = recorder_server
|
|
146
|
+
client = WebClient(WebConfig(enabled=True, endpoint=base))
|
|
147
|
+
ok = asyncio.run(client.send_regression(
|
|
148
|
+
service="users", file="x.py", endpoint="/api/users", reason="500",
|
|
149
|
+
))
|
|
150
|
+
assert ok is True
|
|
151
|
+
body = rec.requests[0][2]
|
|
152
|
+
assert body["type"] == "REGRESSION"
|
|
153
|
+
assert body["service"] == "users"
|
|
154
|
+
assert body["status"] == "fail"
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def test_send_health_transition_helper(recorder_server):
|
|
158
|
+
base, rec = recorder_server
|
|
159
|
+
client = WebClient(WebConfig(enabled=True, endpoint=base))
|
|
160
|
+
ok = asyncio.run(client.send_health_transition(
|
|
161
|
+
service="users", from_status="up", to_status="down",
|
|
162
|
+
))
|
|
163
|
+
assert ok is True
|
|
164
|
+
body = rec.requests[0][2]
|
|
165
|
+
assert body["type"] == "HEALTH_TRANSITION"
|
|
166
|
+
assert body["from"] == "up"
|
|
167
|
+
assert body["to"] == "down"
|
|
@@ -7,7 +7,7 @@ WUP monitors file changes and runs intelligent regression tests using a 3-layer
|
|
|
7
7
|
3. Detail Layer: Full tests with blame reports (only on failure)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
__version__ = "0.2.
|
|
10
|
+
__version__ = "0.2.20"
|
|
11
11
|
__author__ = "Tom Sapletta"
|
|
12
12
|
|
|
13
13
|
from .config import load_config, save_config, get_default_config
|