wup 0.2.47__tar.gz → 0.2.48__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.47/wup.egg-info → wup-0.2.48}/PKG-INFO +6 -6
- {wup-0.2.47 → wup-0.2.48}/README.md +5 -5
- {wup-0.2.47 → wup-0.2.48}/pyproject.toml +1 -1
- wup-0.2.48/tests/test_visual_diff_periodic_skip.py +40 -0
- wup-0.2.48/tests/test_visual_diff_progress.py +39 -0
- {wup-0.2.47 → wup-0.2.48}/wup/__init__.py +1 -1
- {wup-0.2.47 → wup-0.2.48}/wup/models/config.py +3 -0
- {wup-0.2.47 → wup-0.2.48}/wup/testing/handlers/event_handlers.py +7 -1
- {wup-0.2.47 → wup-0.2.48}/wup/testql_watcher.py +39 -12
- {wup-0.2.47 → wup-0.2.48}/wup/visual_diff.py +42 -4
- {wup-0.2.47 → wup-0.2.48/wup.egg-info}/PKG-INFO +6 -6
- {wup-0.2.47 → wup-0.2.48}/wup.egg-info/SOURCES.txt +2 -0
- {wup-0.2.47 → wup-0.2.48}/LICENSE +0 -0
- {wup-0.2.47 → wup-0.2.48}/setup.cfg +0 -0
- {wup-0.2.47 → wup-0.2.48}/tests/test_auto_detection.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/tests/test_cli_filtering.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/tests/test_e2e.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/tests/test_monitoring_manifest.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/tests/test_service_inference.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/tests/test_testql_monitor.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/tests/test_testql_watcher.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/tests/test_web_client.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/tests/test_wup.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/_ast_detector.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/_base_detector.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/_hash_detector.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/_yaml_detector.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/anomaly_detector.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/anomaly_models.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/assistant.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/bus.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/cli.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/cli_config_generator.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/cli_scanner.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/config.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/core.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/dependency_mapper.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/event_store.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/file_watcher/events/file_events.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/models/__init__.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/monitoring_manifest.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/planfile_reporter.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/testing/events/health_events.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/testing/events/test_results.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/testing/handlers/health_handlers.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/testing/queries/health_queries.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/testql_cli_generator.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/testql_discovery.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/testql_monitor.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup/web_client.py +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup.egg-info/dependency_links.txt +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup.egg-info/entry_points.txt +0 -0
- {wup-0.2.47 → wup-0.2.48}/wup.egg-info/requires.txt +0 -0
- {wup-0.2.47 → wup-0.2.48}/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.48
|
|
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-Expression: Apache-2.0
|
|
@@ -31,17 +31,17 @@ Dynamic: license-file
|
|
|
31
31
|
|
|
32
32
|
## AI Cost Tracking
|
|
33
33
|
|
|
34
|
-
    
|
|
35
|
+
  
|
|
36
36
|
|
|
37
|
-
- 🤖 **LLM usage:** $3.
|
|
38
|
-
- 👤 **Human dev:** ~$
|
|
37
|
+
- 🤖 **LLM usage:** $3.3508 (60 commits)
|
|
38
|
+
- 👤 **Human dev:** ~$2573 (25.7h @ $100/h, 30min dedup)
|
|
39
39
|
|
|
40
40
|
Generated on 2026-05-24 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
41
41
|
|
|
42
42
|
---
|
|
43
43
|
|
|
44
|
-
    
|
|
45
45
|
|
|
46
46
|
**WUP (What's Up)** - Intelligent file watcher for regression testing in large projects.
|
|
47
47
|
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
|
|
4
4
|
## AI Cost Tracking
|
|
5
5
|
|
|
6
|
-
    
|
|
7
|
+
  
|
|
8
8
|
|
|
9
|
-
- 🤖 **LLM usage:** $3.
|
|
10
|
-
- 👤 **Human dev:** ~$
|
|
9
|
+
- 🤖 **LLM usage:** $3.3508 (60 commits)
|
|
10
|
+
- 👤 **Human dev:** ~$2573 (25.7h @ $100/h, 30min dedup)
|
|
11
11
|
|
|
12
12
|
Generated on 2026-05-24 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
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Regression: visual_diff is skipped during periodic probe cycles by default."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from unittest.mock import MagicMock
|
|
6
|
+
|
|
7
|
+
from wup.models.config import VisualDiffConfig
|
|
8
|
+
from wup.testql_watcher import TestQLWatcher
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _make_watcher(tmp_path, *, run_on_periodic_probe: bool) -> TestQLWatcher:
|
|
12
|
+
watcher = TestQLWatcher.__new__(TestQLWatcher)
|
|
13
|
+
differ = MagicMock()
|
|
14
|
+
differ.cfg = VisualDiffConfig(enabled=True, run_on_periodic_probe=run_on_periodic_probe)
|
|
15
|
+
watcher.visual_differ = differ
|
|
16
|
+
watcher._periodic_probe_in_progress = False
|
|
17
|
+
return watcher
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_visual_diff_runs_on_file_change_cycles(tmp_path) -> None:
|
|
21
|
+
watcher = _make_watcher(tmp_path, run_on_periodic_probe=False)
|
|
22
|
+
assert watcher._should_run_visual_diff() is True
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_visual_diff_skipped_on_periodic_probe_by_default(tmp_path) -> None:
|
|
26
|
+
watcher = _make_watcher(tmp_path, run_on_periodic_probe=False)
|
|
27
|
+
watcher._periodic_probe_in_progress = True
|
|
28
|
+
assert watcher._should_run_visual_diff() is False
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_visual_diff_runs_on_periodic_probe_when_opted_in(tmp_path) -> None:
|
|
32
|
+
watcher = _make_watcher(tmp_path, run_on_periodic_probe=True)
|
|
33
|
+
watcher._periodic_probe_in_progress = True
|
|
34
|
+
assert watcher._should_run_visual_diff() is True
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_visual_diff_skipped_when_disabled(tmp_path) -> None:
|
|
38
|
+
watcher = _make_watcher(tmp_path, run_on_periodic_probe=True)
|
|
39
|
+
watcher.visual_differ.cfg.enabled = False
|
|
40
|
+
assert watcher._should_run_visual_diff() is False
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Regression: visual_diff shows a progress bar for large scans."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
from wup.models.config import VisualDiffConfig
|
|
8
|
+
from wup.visual_diff import VisualDiffer
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _make_differ(tmp_path) -> VisualDiffer:
|
|
12
|
+
cfg = VisualDiffConfig(
|
|
13
|
+
enabled=True,
|
|
14
|
+
base_url="http://localhost:8100",
|
|
15
|
+
snapshot_dir=str(tmp_path / "snap"),
|
|
16
|
+
diff_dir=str(tmp_path / "diff"),
|
|
17
|
+
pages_from_endpoints=True,
|
|
18
|
+
max_pages=200,
|
|
19
|
+
)
|
|
20
|
+
return VisualDiffer(str(tmp_path), cfg)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_progress_returned_for_big_scans(tmp_path, monkeypatch) -> None:
|
|
24
|
+
monkeypatch.delenv("WUP_VISUAL_DIFF_PROGRESS", raising=False)
|
|
25
|
+
differ = _make_differ(tmp_path)
|
|
26
|
+
progress = differ._build_progress("frontend", total=20)
|
|
27
|
+
assert progress is not None
|
|
28
|
+
assert progress.live.transient is True
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_progress_skipped_for_small_scans(tmp_path) -> None:
|
|
32
|
+
differ = _make_differ(tmp_path)
|
|
33
|
+
assert differ._build_progress("frontend", total=2) is None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_progress_can_be_disabled_via_env(tmp_path, monkeypatch) -> None:
|
|
37
|
+
monkeypatch.setenv("WUP_VISUAL_DIFF_PROGRESS", "0")
|
|
38
|
+
differ = _make_differ(tmp_path)
|
|
39
|
+
assert differ._build_progress("frontend", total=50) is None
|
|
@@ -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.48"
|
|
11
11
|
__author__ = "Tom Sapletta"
|
|
12
12
|
|
|
13
13
|
from .config import load_config, save_config, get_default_config
|
|
@@ -100,6 +100,9 @@ class VisualDiffConfig:
|
|
|
100
100
|
"[class*='error'][class*='container']",
|
|
101
101
|
])
|
|
102
102
|
headless: bool = True
|
|
103
|
+
# Run visual_diff during periodic probe cycles too. Default false: visual_diff
|
|
104
|
+
# only runs when something on disk actually changed (or on first cycle).
|
|
105
|
+
run_on_periodic_probe: bool = False
|
|
103
106
|
|
|
104
107
|
|
|
105
108
|
@dataclass
|
|
@@ -35,7 +35,13 @@ class TestResultEventHandler:
|
|
|
35
35
|
)
|
|
36
36
|
)
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
reason = (event.reason or "TestQL failed").strip().splitlines()[-1]
|
|
39
|
+
if len(reason) > 160:
|
|
40
|
+
reason = reason[:157] + "..."
|
|
41
|
+
self.console.print(
|
|
42
|
+
f"[red]✗ {event.stage.capitalize()} failed: {event.scenario.name} — {reason}[/red]\n"
|
|
43
|
+
f"[dim] track: {event.track_file}[/dim]"
|
|
44
|
+
)
|
|
39
45
|
|
|
40
46
|
def handle_test_passed(self, event: ScenarioPassed) -> None:
|
|
41
47
|
"""Handle scenario pass."""
|
|
@@ -117,6 +117,7 @@ class TestQLWatcher(WupWatcher):
|
|
|
117
117
|
)
|
|
118
118
|
|
|
119
119
|
self._probe_thread = None
|
|
120
|
+
self._periodic_probe_in_progress = False
|
|
120
121
|
self._normalize_fleet_health_entry()
|
|
121
122
|
|
|
122
123
|
def _normalize_fleet_health_entry(self) -> None:
|
|
@@ -419,8 +420,10 @@ class TestQLWatcher(WupWatcher):
|
|
|
419
420
|
ts = int(time.time())
|
|
420
421
|
safe_service = service.replace("/", "_").replace("\\", "_")
|
|
421
422
|
scenario_name = scenario.name if scenario else "unknown"
|
|
422
|
-
|
|
423
|
-
|
|
423
|
+
stderr_lines = (result.stderr or "").strip().splitlines()
|
|
424
|
+
stdout_lines = (result.stdout or "").strip().splitlines()
|
|
425
|
+
stderr_tail = stderr_lines[-3:] if stderr_lines else []
|
|
426
|
+
stdout_tail = stdout_lines[-5:] if stdout_lines else []
|
|
424
427
|
|
|
425
428
|
payload = {
|
|
426
429
|
"service": service,
|
|
@@ -428,8 +431,11 @@ class TestQLWatcher(WupWatcher):
|
|
|
428
431
|
"scenario": str(scenario) if scenario else None,
|
|
429
432
|
"command": result.args,
|
|
430
433
|
"returncode": result.returncode,
|
|
431
|
-
"stderr_head":
|
|
432
|
-
"stdout_head":
|
|
434
|
+
"stderr_head": stderr_lines[0] if stderr_lines else "",
|
|
435
|
+
"stdout_head": stdout_lines[0] if stdout_lines else "",
|
|
436
|
+
"stderr_tail": stderr_tail,
|
|
437
|
+
"stdout_tail": stdout_tail,
|
|
438
|
+
"failure_summary": self._summarize_testql_failure(result),
|
|
433
439
|
"track": {
|
|
434
440
|
"file": str(scenario) if scenario else "",
|
|
435
441
|
"line": 1,
|
|
@@ -478,7 +484,7 @@ class TestQLWatcher(WupWatcher):
|
|
|
478
484
|
if self._is_interrupted_result(result):
|
|
479
485
|
raise KeyboardInterrupt
|
|
480
486
|
|
|
481
|
-
reason =
|
|
487
|
+
reason = self._summarize_testql_failure(result)
|
|
482
488
|
track_path = self._write_track(service=service, stage="quick",
|
|
483
489
|
scenario=scenario, result=result)
|
|
484
490
|
|
|
@@ -497,6 +503,13 @@ class TestQLWatcher(WupWatcher):
|
|
|
497
503
|
|
|
498
504
|
return False
|
|
499
505
|
|
|
506
|
+
def _should_run_visual_diff(self) -> bool:
|
|
507
|
+
if not (self.visual_differ and self.visual_differ.cfg.enabled):
|
|
508
|
+
return False
|
|
509
|
+
if not getattr(self, "_periodic_probe_in_progress", False):
|
|
510
|
+
return True
|
|
511
|
+
return bool(getattr(self.visual_differ.cfg, "run_on_periodic_probe", False))
|
|
512
|
+
|
|
500
513
|
async def _quick_pass_actions(self, service: str, merged_endpoints: List[str]) -> None:
|
|
501
514
|
"""Actions to perform after all quick scenarios pass."""
|
|
502
515
|
self._record_health_transition(service=service, status="up", stage="quick",
|
|
@@ -504,7 +517,7 @@ class TestQLWatcher(WupWatcher):
|
|
|
504
517
|
if self.web_client.is_active:
|
|
505
518
|
await self.web_client.send_pass(service=service, stage="quick")
|
|
506
519
|
self.console.print(f"[green]✓ Quick TestQL passed for {service}[/green]")
|
|
507
|
-
if self.
|
|
520
|
+
if self._should_run_visual_diff():
|
|
508
521
|
visual_endpoints = self._get_config_endpoints_for_service(service) or merged_endpoints
|
|
509
522
|
visual_results = await self.visual_differ.run_for_service(service, visual_endpoints)
|
|
510
523
|
visual_issues = [
|
|
@@ -605,6 +618,16 @@ class TestQLWatcher(WupWatcher):
|
|
|
605
618
|
return stripped
|
|
606
619
|
return None
|
|
607
620
|
|
|
621
|
+
@staticmethod
|
|
622
|
+
def _summarize_testql_failure(result: subprocess.CompletedProcess) -> str:
|
|
623
|
+
"""Short failure line for tracks and console (last lines of testql output)."""
|
|
624
|
+
if int(result.returncode) == 124:
|
|
625
|
+
return "TestQL subprocess timed out"
|
|
626
|
+
summary = TestQLWatcher._summarize_health_scenario_failure(result)
|
|
627
|
+
if summary != "health_scenario failed":
|
|
628
|
+
return summary
|
|
629
|
+
return "TestQL command failed"
|
|
630
|
+
|
|
608
631
|
@staticmethod
|
|
609
632
|
def _summarize_health_scenario_failure(result: subprocess.CompletedProcess) -> str:
|
|
610
633
|
"""Extract a short human summary from TestQL --output json (avoid trailing '}')."""
|
|
@@ -846,15 +869,19 @@ class TestQLWatcher(WupWatcher):
|
|
|
846
869
|
if not self.config.services:
|
|
847
870
|
return
|
|
848
871
|
self.console.print("[cyan]⟳ Periodic live probe cycle[/cyan]")
|
|
872
|
+
self._periodic_probe_in_progress = True
|
|
849
873
|
try:
|
|
850
|
-
asyncio.run(self._run_fleet_health_scenario())
|
|
851
|
-
except Exception as exc: # noqa: BLE001
|
|
852
|
-
self.console.print(f"[red]Fleet health scenario error: {exc}[/red]")
|
|
853
|
-
for svc in self.config.services:
|
|
854
874
|
try:
|
|
855
|
-
asyncio.run(self.
|
|
875
|
+
asyncio.run(self._run_fleet_health_scenario())
|
|
856
876
|
except Exception as exc: # noqa: BLE001
|
|
857
|
-
self.console.print(f"[red]
|
|
877
|
+
self.console.print(f"[red]Fleet health scenario error: {exc}[/red]")
|
|
878
|
+
for svc in self.config.services:
|
|
879
|
+
try:
|
|
880
|
+
asyncio.run(self.run_quick_test(svc.name, []))
|
|
881
|
+
except Exception as exc: # noqa: BLE001
|
|
882
|
+
self.console.print(f"[red]Probe error for {svc.name}: {exc}[/red]")
|
|
883
|
+
finally:
|
|
884
|
+
self._periodic_probe_in_progress = False
|
|
858
885
|
|
|
859
886
|
def _start_periodic_probe_thread(self) -> None:
|
|
860
887
|
import threading
|
|
@@ -24,6 +24,14 @@ from typing import Any, Dict, List, Optional, Tuple
|
|
|
24
24
|
from urllib.parse import urlparse
|
|
25
25
|
|
|
26
26
|
from rich.console import Console
|
|
27
|
+
from rich.progress import (
|
|
28
|
+
BarColumn,
|
|
29
|
+
MofNCompleteColumn,
|
|
30
|
+
Progress,
|
|
31
|
+
TextColumn,
|
|
32
|
+
TimeElapsedColumn,
|
|
33
|
+
TimeRemainingColumn,
|
|
34
|
+
)
|
|
27
35
|
|
|
28
36
|
from .models.config import VisualDiffConfig
|
|
29
37
|
|
|
@@ -442,14 +450,44 @@ class VisualDiffer:
|
|
|
442
450
|
new_urls: List[str] = []
|
|
443
451
|
error_results: List[Tuple[str, str]] = []
|
|
444
452
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
453
|
+
progress = self._build_progress(service, len(pages))
|
|
454
|
+
if progress is None:
|
|
455
|
+
for url in pages:
|
|
456
|
+
result = await self._check_page(service, url)
|
|
457
|
+
results.append(result)
|
|
458
|
+
self._categorize_page_result(service, url, result, ok_urls, new_urls, error_results)
|
|
459
|
+
else:
|
|
460
|
+
with progress:
|
|
461
|
+
task_id = progress.add_task(
|
|
462
|
+
f"[cyan]🔍 Visual diff {service}", total=len(pages), url=""
|
|
463
|
+
)
|
|
464
|
+
for url in pages:
|
|
465
|
+
progress.update(task_id, url=_short_url(url))
|
|
466
|
+
result = await self._check_page(service, url)
|
|
467
|
+
results.append(result)
|
|
468
|
+
self._categorize_page_result(service, url, result, ok_urls, new_urls, error_results)
|
|
469
|
+
progress.advance(task_id)
|
|
449
470
|
|
|
450
471
|
self._print_scan_summary(service, ok_urls, new_urls, error_results)
|
|
451
472
|
return results
|
|
452
473
|
|
|
474
|
+
def _build_progress(self, service: str, total: int) -> Optional[Progress]:
|
|
475
|
+
"""Return a rich Progress for big scans; None for tiny ones (avoid noise)."""
|
|
476
|
+
if total < 5 or os.environ.get("WUP_VISUAL_DIFF_PROGRESS", "1") == "0":
|
|
477
|
+
return None
|
|
478
|
+
return Progress(
|
|
479
|
+
TextColumn("[bold blue]{task.description}"),
|
|
480
|
+
BarColumn(bar_width=None),
|
|
481
|
+
MofNCompleteColumn(),
|
|
482
|
+
TextColumn("[dim]{task.fields[url]}"),
|
|
483
|
+
TimeElapsedColumn(),
|
|
484
|
+
TextColumn("eta"),
|
|
485
|
+
TimeRemainingColumn(),
|
|
486
|
+
console=console,
|
|
487
|
+
transient=True,
|
|
488
|
+
refresh_per_second=4,
|
|
489
|
+
)
|
|
490
|
+
|
|
453
491
|
async def _check_page(self, service: str, url: str) -> Dict[str, Any]:
|
|
454
492
|
snap_path = _snapshot_path(self.snapshot_dir, service, url)
|
|
455
493
|
old_snapshot = _load_snapshot(snap_path)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wup
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.48
|
|
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-Expression: Apache-2.0
|
|
@@ -31,17 +31,17 @@ Dynamic: license-file
|
|
|
31
31
|
|
|
32
32
|
## AI Cost Tracking
|
|
33
33
|
|
|
34
|
-
    
|
|
35
|
+
  
|
|
36
36
|
|
|
37
|
-
- 🤖 **LLM usage:** $3.
|
|
38
|
-
- 👤 **Human dev:** ~$
|
|
37
|
+
- 🤖 **LLM usage:** $3.3508 (60 commits)
|
|
38
|
+
- 👤 **Human dev:** ~$2573 (25.7h @ $100/h, 30min dedup)
|
|
39
39
|
|
|
40
40
|
Generated on 2026-05-24 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
|
|
41
41
|
|
|
42
42
|
---
|
|
43
43
|
|
|
44
|
-
    
|
|
45
45
|
|
|
46
46
|
**WUP (What's Up)** - Intelligent file watcher for regression testing in large projects.
|
|
47
47
|
|
|
@@ -8,6 +8,8 @@ tests/test_monitoring_manifest.py
|
|
|
8
8
|
tests/test_service_inference.py
|
|
9
9
|
tests/test_testql_monitor.py
|
|
10
10
|
tests/test_testql_watcher.py
|
|
11
|
+
tests/test_visual_diff_periodic_skip.py
|
|
12
|
+
tests/test_visual_diff_progress.py
|
|
11
13
|
tests/test_web_client.py
|
|
12
14
|
tests/test_wup.py
|
|
13
15
|
wup/__init__.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|