atdd 0.4.7__py3-none-any.whl → 0.6.0__py3-none-any.whl
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.
- atdd/coach/schemas/config.schema.json +63 -0
- atdd/coach/utils/coverage_phase.py +97 -0
- atdd/coach/validators/shared_fixtures.py +154 -0
- atdd/coder/conventions/coverage.convention.yaml +85 -0
- atdd/coder/validators/conftest.py +5 -0
- atdd/coder/validators/test_hierarchy_coverage.py +361 -0
- atdd/planner/conventions/coverage.convention.yaml +95 -0
- atdd/planner/validators/test_hierarchy_coverage.py +433 -0
- atdd/tester/conventions/coverage.convention.yaml +114 -0
- atdd/tester/validators/test_hierarchy_coverage.py +604 -0
- {atdd-0.4.7.dist-info → atdd-0.6.0.dist-info}/METADATA +1 -1
- {atdd-0.4.7.dist-info → atdd-0.6.0.dist-info}/RECORD +16 -8
- {atdd-0.4.7.dist-info → atdd-0.6.0.dist-info}/WHEEL +0 -0
- {atdd-0.4.7.dist-info → atdd-0.6.0.dist-info}/entry_points.txt +0 -0
- {atdd-0.4.7.dist-info → atdd-0.6.0.dist-info}/licenses/LICENSE +0 -0
- {atdd-0.4.7.dist-info → atdd-0.6.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Planner hierarchy coverage validation.
|
|
3
|
+
|
|
4
|
+
ATDD Hierarchy Coverage Spec v0.1 - Section 2: Planner Coverage Rules
|
|
5
|
+
|
|
6
|
+
Validates bidirectional coverage between:
|
|
7
|
+
- Trains <-> Wagons (COVERAGE-PLAN-2.1)
|
|
8
|
+
- Wagons <-> Features (COVERAGE-PLAN-2.2)
|
|
9
|
+
- Features <-> WMBTs (COVERAGE-PLAN-2.3)
|
|
10
|
+
- WMBTs <-> Acceptances (COVERAGE-PLAN-2.4)
|
|
11
|
+
|
|
12
|
+
Architecture:
|
|
13
|
+
- Uses shared fixtures from atdd.coach.validators.shared_fixtures
|
|
14
|
+
- Phased rollout via atdd.coach.utils.coverage_phase
|
|
15
|
+
- Exception handling via .atdd/config.yaml coverage.exceptions
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import pytest
|
|
19
|
+
import yaml
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Dict, List, Set, Tuple, Any
|
|
22
|
+
|
|
23
|
+
from atdd.coach.utils.repo import find_repo_root
|
|
24
|
+
from atdd.coach.utils.coverage_phase import (
|
|
25
|
+
CoveragePhase,
|
|
26
|
+
should_enforce,
|
|
27
|
+
emit_coverage_warning
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# Path constants
|
|
32
|
+
REPO_ROOT = find_repo_root()
|
|
33
|
+
PLAN_DIR = REPO_ROOT / "plan"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# ============================================================================
|
|
37
|
+
# COVERAGE-PLAN-2.1: Train <-> Wagon Coverage
|
|
38
|
+
# ============================================================================
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@pytest.mark.planner
|
|
42
|
+
def test_all_wagons_in_at_least_one_train(
|
|
43
|
+
wagon_manifests,
|
|
44
|
+
wagon_to_train_mapping,
|
|
45
|
+
coverage_exceptions
|
|
46
|
+
):
|
|
47
|
+
"""
|
|
48
|
+
COVERAGE-PLAN-2.1a: Every wagon appears in at least one train.
|
|
49
|
+
|
|
50
|
+
Given: All wagon manifests in plan/
|
|
51
|
+
When: Checking train participant references
|
|
52
|
+
Then: Every wagon slug appears in at least one train's participants
|
|
53
|
+
(unless in wagons_not_in_train allow-list or status:draft)
|
|
54
|
+
"""
|
|
55
|
+
allowed_wagons = set(coverage_exceptions.get("wagons_not_in_train", []))
|
|
56
|
+
violations = []
|
|
57
|
+
|
|
58
|
+
for path, manifest in wagon_manifests:
|
|
59
|
+
wagon_slug = manifest.get("wagon", "")
|
|
60
|
+
status = manifest.get("status", "")
|
|
61
|
+
|
|
62
|
+
# Skip draft wagons
|
|
63
|
+
if status == "draft":
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
# Skip allowed exceptions
|
|
67
|
+
if wagon_slug in allowed_wagons:
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
# Check if wagon is in any train
|
|
71
|
+
if wagon_slug not in wagon_to_train_mapping:
|
|
72
|
+
violations.append(
|
|
73
|
+
f"{wagon_slug}: not referenced by any train (path: {path.relative_to(REPO_ROOT)})"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
if violations:
|
|
77
|
+
if should_enforce(CoveragePhase.PLANNER_TESTER_ENFORCEMENT):
|
|
78
|
+
pytest.fail(
|
|
79
|
+
f"COVERAGE-PLAN-2.1a: Wagons not in any train:\n " +
|
|
80
|
+
"\n ".join(violations) +
|
|
81
|
+
"\n\nAdd to train participants or coverage.exceptions.wagons_not_in_train"
|
|
82
|
+
)
|
|
83
|
+
else:
|
|
84
|
+
for violation in violations:
|
|
85
|
+
emit_coverage_warning(
|
|
86
|
+
"COVERAGE-PLAN-2.1a",
|
|
87
|
+
violation,
|
|
88
|
+
CoveragePhase.PLANNER_TESTER_ENFORCEMENT
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@pytest.mark.planner
|
|
93
|
+
def test_all_train_wagon_refs_exist(train_files, wagon_manifests):
|
|
94
|
+
"""
|
|
95
|
+
COVERAGE-PLAN-2.1b: Every train wagon participant has manifest.
|
|
96
|
+
|
|
97
|
+
Given: Train YAML files with wagon: participants
|
|
98
|
+
When: Checking wagon references
|
|
99
|
+
Then: Every referenced wagon has a manifest in plan/
|
|
100
|
+
"""
|
|
101
|
+
# Build set of existing wagon slugs
|
|
102
|
+
wagon_slugs = {manifest.get("wagon", "") for _, manifest in wagon_manifests}
|
|
103
|
+
|
|
104
|
+
violations = []
|
|
105
|
+
|
|
106
|
+
for train_path, train_data in train_files:
|
|
107
|
+
train_id = train_data.get("train_id", train_path.stem)
|
|
108
|
+
participants = train_data.get("participants", [])
|
|
109
|
+
|
|
110
|
+
for participant in participants:
|
|
111
|
+
if isinstance(participant, str) and participant.startswith("wagon:"):
|
|
112
|
+
wagon_slug = participant.replace("wagon:", "")
|
|
113
|
+
if wagon_slug not in wagon_slugs:
|
|
114
|
+
violations.append(
|
|
115
|
+
f"{train_id}: references non-existent wagon '{wagon_slug}'"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if violations:
|
|
119
|
+
if should_enforce(CoveragePhase.PLANNER_TESTER_ENFORCEMENT):
|
|
120
|
+
pytest.fail(
|
|
121
|
+
f"COVERAGE-PLAN-2.1b: Train wagon references to non-existent wagons:\n " +
|
|
122
|
+
"\n ".join(violations)
|
|
123
|
+
)
|
|
124
|
+
else:
|
|
125
|
+
for violation in violations:
|
|
126
|
+
emit_coverage_warning(
|
|
127
|
+
"COVERAGE-PLAN-2.1b",
|
|
128
|
+
violation,
|
|
129
|
+
CoveragePhase.PLANNER_TESTER_ENFORCEMENT
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# ============================================================================
|
|
134
|
+
# COVERAGE-PLAN-2.2: Wagon <-> Feature Coverage
|
|
135
|
+
# ============================================================================
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@pytest.mark.planner
|
|
139
|
+
def test_all_features_in_wagon_manifest(feature_files, wagon_manifests, coverage_exceptions):
|
|
140
|
+
"""
|
|
141
|
+
COVERAGE-PLAN-2.2a: Every feature file referenced in wagon manifest.
|
|
142
|
+
|
|
143
|
+
Given: Feature files in plan/*/features/
|
|
144
|
+
When: Checking wagon manifest features[] references
|
|
145
|
+
Then: Every feature file is referenced by its wagon's manifest
|
|
146
|
+
"""
|
|
147
|
+
allowed_features = set(coverage_exceptions.get("features_orphaned", []))
|
|
148
|
+
|
|
149
|
+
# Build mapping of wagon_dir -> manifest features
|
|
150
|
+
wagon_features: Dict[str, Set[str]] = {}
|
|
151
|
+
for path, manifest in wagon_manifests:
|
|
152
|
+
wagon_dir = path.parent.name
|
|
153
|
+
features_list = manifest.get("features", [])
|
|
154
|
+
|
|
155
|
+
feature_slugs = set()
|
|
156
|
+
for feature in features_list:
|
|
157
|
+
if isinstance(feature, dict) and "urn" in feature:
|
|
158
|
+
# URN format: feature:wagon-slug:feature-slug
|
|
159
|
+
urn = feature["urn"]
|
|
160
|
+
parts = urn.split(":")
|
|
161
|
+
if len(parts) >= 3:
|
|
162
|
+
feature_slugs.add(parts[2])
|
|
163
|
+
elif isinstance(feature, str):
|
|
164
|
+
# Direct slug or URN
|
|
165
|
+
if feature.startswith("feature:"):
|
|
166
|
+
parts = feature.split(":")
|
|
167
|
+
if len(parts) >= 3:
|
|
168
|
+
feature_slugs.add(parts[2])
|
|
169
|
+
else:
|
|
170
|
+
feature_slugs.add(feature)
|
|
171
|
+
|
|
172
|
+
wagon_features[wagon_dir] = feature_slugs
|
|
173
|
+
|
|
174
|
+
violations = []
|
|
175
|
+
|
|
176
|
+
for path, feature_data in feature_files:
|
|
177
|
+
wagon_dir = path.parent.parent.name
|
|
178
|
+
# Feature filename (snake_case) -> slug (kebab-case)
|
|
179
|
+
feature_filename = path.stem
|
|
180
|
+
feature_slug = feature_filename.replace("_", "-")
|
|
181
|
+
|
|
182
|
+
# Also check for URN in feature data
|
|
183
|
+
feature_urn = feature_data.get("urn", "")
|
|
184
|
+
|
|
185
|
+
# Skip allowed exceptions
|
|
186
|
+
if feature_urn in allowed_features or feature_slug in allowed_features:
|
|
187
|
+
continue
|
|
188
|
+
|
|
189
|
+
# Check if feature is in wagon manifest
|
|
190
|
+
manifest_features = wagon_features.get(wagon_dir, set())
|
|
191
|
+
if feature_slug not in manifest_features:
|
|
192
|
+
violations.append(
|
|
193
|
+
f"{path.relative_to(REPO_ROOT)}: not in wagon manifest features[]"
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
if violations:
|
|
197
|
+
if should_enforce(CoveragePhase.PLANNER_TESTER_ENFORCEMENT):
|
|
198
|
+
pytest.fail(
|
|
199
|
+
f"COVERAGE-PLAN-2.2a: Features not in wagon manifest:\n " +
|
|
200
|
+
"\n ".join(violations) +
|
|
201
|
+
"\n\nAdd to wagon features[] or coverage.exceptions.features_orphaned"
|
|
202
|
+
)
|
|
203
|
+
else:
|
|
204
|
+
for violation in violations:
|
|
205
|
+
emit_coverage_warning(
|
|
206
|
+
"COVERAGE-PLAN-2.2a",
|
|
207
|
+
violation,
|
|
208
|
+
CoveragePhase.PLANNER_TESTER_ENFORCEMENT
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@pytest.mark.planner
|
|
213
|
+
def test_all_wagon_feature_refs_exist(wagon_manifests):
|
|
214
|
+
"""
|
|
215
|
+
COVERAGE-PLAN-2.2b: Every wagon features[] entry has YAML file.
|
|
216
|
+
|
|
217
|
+
Given: Wagon manifests with features[] references
|
|
218
|
+
When: Checking for corresponding files
|
|
219
|
+
Then: Every features[] entry has a YAML file in wagon/features/
|
|
220
|
+
"""
|
|
221
|
+
violations = []
|
|
222
|
+
|
|
223
|
+
for path, manifest in wagon_manifests:
|
|
224
|
+
wagon_dir = path.parent
|
|
225
|
+
features_dir = wagon_dir / "features"
|
|
226
|
+
features_list = manifest.get("features", [])
|
|
227
|
+
|
|
228
|
+
for feature in features_list:
|
|
229
|
+
feature_slug = None
|
|
230
|
+
|
|
231
|
+
if isinstance(feature, dict) and "urn" in feature:
|
|
232
|
+
# URN format: feature:wagon-slug:feature-slug
|
|
233
|
+
urn = feature["urn"]
|
|
234
|
+
parts = urn.split(":")
|
|
235
|
+
if len(parts) >= 3:
|
|
236
|
+
feature_slug = parts[2]
|
|
237
|
+
elif isinstance(feature, str):
|
|
238
|
+
if feature.startswith("feature:"):
|
|
239
|
+
parts = feature.split(":")
|
|
240
|
+
if len(parts) >= 3:
|
|
241
|
+
feature_slug = parts[2]
|
|
242
|
+
else:
|
|
243
|
+
feature_slug = feature
|
|
244
|
+
|
|
245
|
+
if not feature_slug:
|
|
246
|
+
continue
|
|
247
|
+
|
|
248
|
+
# Convert slug to filename (kebab-case -> snake_case)
|
|
249
|
+
feature_filename = feature_slug.replace("-", "_") + ".yaml"
|
|
250
|
+
feature_path = features_dir / feature_filename
|
|
251
|
+
|
|
252
|
+
if not feature_path.exists():
|
|
253
|
+
wagon_slug = manifest.get("wagon", wagon_dir.name)
|
|
254
|
+
violations.append(
|
|
255
|
+
f"{wagon_slug}: features[] references '{feature_slug}' but "
|
|
256
|
+
f"{feature_path.relative_to(REPO_ROOT)} does not exist"
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
if violations:
|
|
260
|
+
if should_enforce(CoveragePhase.PLANNER_TESTER_ENFORCEMENT):
|
|
261
|
+
pytest.fail(
|
|
262
|
+
f"COVERAGE-PLAN-2.2b: Wagon feature references without files:\n " +
|
|
263
|
+
"\n ".join(violations)
|
|
264
|
+
)
|
|
265
|
+
else:
|
|
266
|
+
for violation in violations:
|
|
267
|
+
emit_coverage_warning(
|
|
268
|
+
"COVERAGE-PLAN-2.2b",
|
|
269
|
+
violation,
|
|
270
|
+
CoveragePhase.PLANNER_TESTER_ENFORCEMENT
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
# ============================================================================
|
|
275
|
+
# COVERAGE-PLAN-2.3: WMBT <-> Feature Coverage
|
|
276
|
+
# ============================================================================
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@pytest.mark.planner
|
|
280
|
+
def test_all_wmbts_in_at_least_one_feature(wmbt_files, feature_files):
|
|
281
|
+
"""
|
|
282
|
+
COVERAGE-PLAN-2.3: Every WMBT appears in at least one feature's wmbts.
|
|
283
|
+
|
|
284
|
+
Given: WMBT files in plan/*/
|
|
285
|
+
When: Checking feature wmbts[] references
|
|
286
|
+
Then: Every WMBT file is referenced by at least one feature
|
|
287
|
+
"""
|
|
288
|
+
# Build set of all WMBT references from features
|
|
289
|
+
referenced_wmbts: Set[str] = set()
|
|
290
|
+
|
|
291
|
+
for path, feature_data in feature_files:
|
|
292
|
+
wmbts_list = feature_data.get("wmbts", [])
|
|
293
|
+
for wmbt_ref in wmbts_list:
|
|
294
|
+
if isinstance(wmbt_ref, dict) and "urn" in wmbt_ref:
|
|
295
|
+
referenced_wmbts.add(wmbt_ref["urn"])
|
|
296
|
+
elif isinstance(wmbt_ref, str):
|
|
297
|
+
referenced_wmbts.add(wmbt_ref)
|
|
298
|
+
|
|
299
|
+
violations = []
|
|
300
|
+
|
|
301
|
+
for path, wmbt_data in wmbt_files:
|
|
302
|
+
wmbt_urn = wmbt_data.get("urn", "")
|
|
303
|
+
status = wmbt_data.get("status", "")
|
|
304
|
+
|
|
305
|
+
# Skip draft WMBTs
|
|
306
|
+
if status == "draft":
|
|
307
|
+
continue
|
|
308
|
+
|
|
309
|
+
# Check both URN and file-based reference
|
|
310
|
+
wmbt_id = path.stem # e.g., "D001"
|
|
311
|
+
wagon_slug = path.parent.name.replace("_", "-")
|
|
312
|
+
|
|
313
|
+
# Try various reference formats
|
|
314
|
+
is_referenced = (
|
|
315
|
+
wmbt_urn in referenced_wmbts or
|
|
316
|
+
f"wmbt:{wagon_slug}:{wmbt_id}" in referenced_wmbts or
|
|
317
|
+
wmbt_id in referenced_wmbts
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
if not is_referenced:
|
|
321
|
+
violations.append(
|
|
322
|
+
f"{path.relative_to(REPO_ROOT)}: not referenced by any feature"
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
if violations:
|
|
326
|
+
if should_enforce(CoveragePhase.PLANNER_TESTER_ENFORCEMENT):
|
|
327
|
+
pytest.fail(
|
|
328
|
+
f"COVERAGE-PLAN-2.3: WMBTs not in any feature:\n " +
|
|
329
|
+
"\n ".join(violations) +
|
|
330
|
+
"\n\nAdd WMBT to a feature's wmbts[] list"
|
|
331
|
+
)
|
|
332
|
+
else:
|
|
333
|
+
for violation in violations:
|
|
334
|
+
emit_coverage_warning(
|
|
335
|
+
"COVERAGE-PLAN-2.3",
|
|
336
|
+
violation,
|
|
337
|
+
CoveragePhase.PLANNER_TESTER_ENFORCEMENT
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
# ============================================================================
|
|
342
|
+
# COVERAGE-PLAN-2.4: WMBT <-> Acceptance Coverage
|
|
343
|
+
# ============================================================================
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
@pytest.mark.planner
|
|
347
|
+
def test_all_wmbts_have_acceptances(wmbt_files, coverage_exceptions):
|
|
348
|
+
"""
|
|
349
|
+
COVERAGE-PLAN-2.4: Every non-draft WMBT has at least one acceptance.
|
|
350
|
+
|
|
351
|
+
Given: WMBT files in plan/*/
|
|
352
|
+
When: Checking acceptances[]
|
|
353
|
+
Then: Every WMBT (except draft) has at least one acceptance
|
|
354
|
+
"""
|
|
355
|
+
allowed_wmbts = set(coverage_exceptions.get("wmbts_without_acceptance", []))
|
|
356
|
+
violations = []
|
|
357
|
+
|
|
358
|
+
for path, wmbt_data in wmbt_files:
|
|
359
|
+
wmbt_urn = wmbt_data.get("urn", "")
|
|
360
|
+
status = wmbt_data.get("status", "")
|
|
361
|
+
acceptances = wmbt_data.get("acceptances", [])
|
|
362
|
+
|
|
363
|
+
# Skip draft WMBTs
|
|
364
|
+
if status == "draft":
|
|
365
|
+
continue
|
|
366
|
+
|
|
367
|
+
# Skip allowed exceptions
|
|
368
|
+
if wmbt_urn in allowed_wmbts:
|
|
369
|
+
continue
|
|
370
|
+
|
|
371
|
+
# Check for acceptances
|
|
372
|
+
if not acceptances or len(acceptances) == 0:
|
|
373
|
+
violations.append(
|
|
374
|
+
f"{path.relative_to(REPO_ROOT)}: no acceptances defined"
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
if violations:
|
|
378
|
+
if should_enforce(CoveragePhase.PLANNER_TESTER_ENFORCEMENT):
|
|
379
|
+
pytest.fail(
|
|
380
|
+
f"COVERAGE-PLAN-2.4: WMBTs without acceptances:\n " +
|
|
381
|
+
"\n ".join(violations) +
|
|
382
|
+
"\n\nAdd acceptances or coverage.exceptions.wmbts_without_acceptance"
|
|
383
|
+
)
|
|
384
|
+
else:
|
|
385
|
+
for violation in violations:
|
|
386
|
+
emit_coverage_warning(
|
|
387
|
+
"COVERAGE-PLAN-2.4",
|
|
388
|
+
violation,
|
|
389
|
+
CoveragePhase.PLANNER_TESTER_ENFORCEMENT
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
# ============================================================================
|
|
394
|
+
# COVERAGE SUMMARY
|
|
395
|
+
# ============================================================================
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
@pytest.mark.planner
|
|
399
|
+
def test_planner_coverage_summary(
|
|
400
|
+
wagon_manifests,
|
|
401
|
+
feature_files,
|
|
402
|
+
wmbt_files,
|
|
403
|
+
wagon_to_train_mapping
|
|
404
|
+
):
|
|
405
|
+
"""
|
|
406
|
+
COVERAGE-PLAN-SUMMARY: Report planner coverage statistics.
|
|
407
|
+
|
|
408
|
+
This test always passes but reports coverage metrics for visibility.
|
|
409
|
+
"""
|
|
410
|
+
# Count wagons in trains
|
|
411
|
+
wagons_in_trains = len(wagon_to_train_mapping)
|
|
412
|
+
total_wagons = len(wagon_manifests)
|
|
413
|
+
|
|
414
|
+
# Count features
|
|
415
|
+
total_features = len(feature_files)
|
|
416
|
+
|
|
417
|
+
# Count WMBTs with acceptances
|
|
418
|
+
wmbts_with_acceptances = sum(
|
|
419
|
+
1 for _, data in wmbt_files
|
|
420
|
+
if data.get("acceptances") and len(data.get("acceptances", [])) > 0
|
|
421
|
+
)
|
|
422
|
+
total_wmbts = len(wmbt_files)
|
|
423
|
+
|
|
424
|
+
# Report summary (always passes)
|
|
425
|
+
summary = (
|
|
426
|
+
f"\n\nPlanner Coverage Summary:\n"
|
|
427
|
+
f" Wagons in trains: {wagons_in_trains}/{total_wagons}\n"
|
|
428
|
+
f" Features defined: {total_features}\n"
|
|
429
|
+
f" WMBTs with acceptances: {wmbts_with_acceptances}/{total_wmbts}"
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
# This test always passes - it's informational
|
|
433
|
+
assert True, summary
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
version: "1.0"
|
|
2
|
+
name: "Tester Coverage Convention"
|
|
3
|
+
description: "Section 3 of ATDD Hierarchy Coverage Spec v0.1 - Ensures test artifacts have complete coverage"
|
|
4
|
+
|
|
5
|
+
rules:
|
|
6
|
+
- id: "COVERAGE-TEST-3.1"
|
|
7
|
+
name: "Acceptance - Tests Coverage"
|
|
8
|
+
description: "Every acceptance criterion must have corresponding test(s)"
|
|
9
|
+
bidirectional:
|
|
10
|
+
- direction: "acceptance -> test"
|
|
11
|
+
requirement: "Each acceptance URN must be referenced by at least one test"
|
|
12
|
+
exceptions: "acceptance_without_tests allow-list"
|
|
13
|
+
validator: "test_all_acceptances_have_tests"
|
|
14
|
+
|
|
15
|
+
- id: "COVERAGE-TEST-3.2"
|
|
16
|
+
name: "Contract - Wagon Coverage"
|
|
17
|
+
description: "Bidirectional coverage between contracts and wagon produce/consume"
|
|
18
|
+
bidirectional:
|
|
19
|
+
- direction: "contract -> wagon"
|
|
20
|
+
requirement: "Every contract schema must be referenced by produce/consume"
|
|
21
|
+
exceptions: "contracts_unreferenced allow-list or x-artifact-metadata.status: draft|external|deprecated"
|
|
22
|
+
validator: "test_all_contracts_referenced"
|
|
23
|
+
|
|
24
|
+
- direction: "wagon -> contract"
|
|
25
|
+
requirement: "Every produce/consume contract ref must have schema file"
|
|
26
|
+
validator: "test_all_contract_refs_exist"
|
|
27
|
+
|
|
28
|
+
- id: "COVERAGE-TEST-3.3"
|
|
29
|
+
name: "Telemetry - Wagon Coverage"
|
|
30
|
+
description: "Bidirectional coverage between telemetry signals and wagon produce"
|
|
31
|
+
bidirectional:
|
|
32
|
+
- direction: "telemetry -> wagon"
|
|
33
|
+
requirement: "Every telemetry signal must be referenced by wagon produce"
|
|
34
|
+
exceptions: "telemetry_unreferenced allow-list or status: draft|external|deprecated"
|
|
35
|
+
validator: "test_all_telemetry_referenced"
|
|
36
|
+
|
|
37
|
+
- direction: "wagon -> telemetry"
|
|
38
|
+
requirement: "Every produce telemetry ref must have signal files"
|
|
39
|
+
validator: "test_all_telemetry_refs_exist"
|
|
40
|
+
|
|
41
|
+
- id: "COVERAGE-TEST-3.4"
|
|
42
|
+
name: "Telemetry Tracking Manifest"
|
|
43
|
+
description: "Tracking manifest must be complete if present"
|
|
44
|
+
requirement: "If telemetry/_tracking_manifest.yaml exists, all signals must be present"
|
|
45
|
+
validator: "test_telemetry_manifest_complete"
|
|
46
|
+
|
|
47
|
+
coverage_graph:
|
|
48
|
+
description: "The tester coverage graph ensures acceptance-to-test traceability and artifact completeness"
|
|
49
|
+
levels:
|
|
50
|
+
- name: "Acceptance"
|
|
51
|
+
covered_by: "Tests (Python, TypeScript, Dart)"
|
|
52
|
+
validators: ["COVERAGE-TEST-3.1"]
|
|
53
|
+
|
|
54
|
+
- name: "Contract"
|
|
55
|
+
referenced_by: "Wagon produce/consume"
|
|
56
|
+
validators: ["COVERAGE-TEST-3.2"]
|
|
57
|
+
|
|
58
|
+
- name: "Telemetry"
|
|
59
|
+
referenced_by: "Wagon produce"
|
|
60
|
+
validators: ["COVERAGE-TEST-3.3", "COVERAGE-TEST-3.4"]
|
|
61
|
+
|
|
62
|
+
exception_handling:
|
|
63
|
+
description: "How exceptions are handled in tester coverage validation"
|
|
64
|
+
allow_lists:
|
|
65
|
+
acceptance_without_tests:
|
|
66
|
+
description: "Acceptance URNs that are allowed without test coverage"
|
|
67
|
+
use_case: "Future acceptances, integration-only criteria, or manual verification"
|
|
68
|
+
config_path: "coverage.exceptions.acceptance_without_tests"
|
|
69
|
+
|
|
70
|
+
contracts_unreferenced:
|
|
71
|
+
description: "Contract URNs that are allowed without produce/consume"
|
|
72
|
+
use_case: "External contracts, deprecated schemas, or system-level contracts"
|
|
73
|
+
config_path: "coverage.exceptions.contracts_unreferenced"
|
|
74
|
+
|
|
75
|
+
telemetry_unreferenced:
|
|
76
|
+
description: "Telemetry URNs that are allowed without references"
|
|
77
|
+
use_case: "System telemetry, deprecated signals, or external observability"
|
|
78
|
+
config_path: "coverage.exceptions.telemetry_unreferenced"
|
|
79
|
+
|
|
80
|
+
metadata_exemptions:
|
|
81
|
+
contracts:
|
|
82
|
+
field: "x-artifact-metadata.status"
|
|
83
|
+
values: ["draft", "external", "deprecated"]
|
|
84
|
+
reason: "Contract schemas with these statuses are exempt from coverage requirements"
|
|
85
|
+
|
|
86
|
+
telemetry:
|
|
87
|
+
field: "status"
|
|
88
|
+
values: ["draft", "external", "deprecated"]
|
|
89
|
+
reason: "Telemetry signals with these statuses are exempt from coverage requirements"
|
|
90
|
+
|
|
91
|
+
test_discovery:
|
|
92
|
+
description: "How tests are discovered and linked to acceptances"
|
|
93
|
+
patterns:
|
|
94
|
+
python:
|
|
95
|
+
location: "python/{wagon}/"
|
|
96
|
+
filename: "test_*.py"
|
|
97
|
+
reference_method: "Docstring URN reference or header comment"
|
|
98
|
+
example: "# URN: acc:maintain-ux:D001-UNIT-001"
|
|
99
|
+
|
|
100
|
+
typescript:
|
|
101
|
+
location: "supabase/functions/{wagon}/{feature}/test/"
|
|
102
|
+
filename: "*.test.ts"
|
|
103
|
+
reference_method: "Comment at top of file or JSDoc annotation"
|
|
104
|
+
example: "// urn: acc:maintain-ux:D001-UNIT-001"
|
|
105
|
+
|
|
106
|
+
dart:
|
|
107
|
+
location: "test/"
|
|
108
|
+
filename: "*_test.dart"
|
|
109
|
+
reference_method: "Comment at top of file"
|
|
110
|
+
example: "// urn: acc:maintain-ux:D001-UNIT-001"
|
|
111
|
+
|
|
112
|
+
rollout:
|
|
113
|
+
phase: "PLANNER_TESTER_ENFORCEMENT"
|
|
114
|
+
description: "Section 3 validators become strict in Phase 2"
|