atdd 0.2.1__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/__init__.py +6 -0
- atdd/__main__.py +4 -0
- atdd/cli.py +404 -0
- atdd/coach/__init__.py +0 -0
- atdd/coach/commands/__init__.py +0 -0
- atdd/coach/commands/add_persistence_metadata.py +215 -0
- atdd/coach/commands/analyze_migrations.py +188 -0
- atdd/coach/commands/consumers.py +720 -0
- atdd/coach/commands/infer_governance_status.py +149 -0
- atdd/coach/commands/initializer.py +177 -0
- atdd/coach/commands/interface.py +1078 -0
- atdd/coach/commands/inventory.py +565 -0
- atdd/coach/commands/migration.py +240 -0
- atdd/coach/commands/registry.py +1560 -0
- atdd/coach/commands/session.py +430 -0
- atdd/coach/commands/sync.py +405 -0
- atdd/coach/commands/test_interface.py +399 -0
- atdd/coach/commands/test_runner.py +141 -0
- atdd/coach/commands/tests/__init__.py +1 -0
- atdd/coach/commands/tests/test_telemetry_array_validation.py +235 -0
- atdd/coach/commands/traceability.py +4264 -0
- atdd/coach/conventions/session.convention.yaml +754 -0
- atdd/coach/overlays/__init__.py +2 -0
- atdd/coach/overlays/claude.md +2 -0
- atdd/coach/schemas/config.schema.json +34 -0
- atdd/coach/schemas/manifest.schema.json +101 -0
- atdd/coach/templates/ATDD.md +282 -0
- atdd/coach/templates/SESSION-TEMPLATE.md +327 -0
- atdd/coach/utils/__init__.py +0 -0
- atdd/coach/utils/graph/__init__.py +0 -0
- atdd/coach/utils/graph/urn.py +875 -0
- atdd/coach/validators/__init__.py +0 -0
- atdd/coach/validators/shared_fixtures.py +365 -0
- atdd/coach/validators/test_enrich_wagon_registry.py +167 -0
- atdd/coach/validators/test_registry.py +575 -0
- atdd/coach/validators/test_session_validation.py +1183 -0
- atdd/coach/validators/test_traceability.py +448 -0
- atdd/coach/validators/test_update_feature_paths.py +108 -0
- atdd/coach/validators/test_validate_contract_consumers.py +297 -0
- atdd/coder/__init__.py +1 -0
- atdd/coder/conventions/adapter.recipe.yaml +88 -0
- atdd/coder/conventions/backend.convention.yaml +460 -0
- atdd/coder/conventions/boundaries.convention.yaml +666 -0
- atdd/coder/conventions/commons.convention.yaml +460 -0
- atdd/coder/conventions/complexity.recipe.yaml +109 -0
- atdd/coder/conventions/component-naming.convention.yaml +178 -0
- atdd/coder/conventions/design.convention.yaml +327 -0
- atdd/coder/conventions/design.recipe.yaml +273 -0
- atdd/coder/conventions/dto.convention.yaml +660 -0
- atdd/coder/conventions/frontend.convention.yaml +542 -0
- atdd/coder/conventions/green.convention.yaml +1012 -0
- atdd/coder/conventions/presentation.convention.yaml +587 -0
- atdd/coder/conventions/refactor.convention.yaml +535 -0
- atdd/coder/conventions/technology.convention.yaml +206 -0
- atdd/coder/conventions/tests/__init__.py +0 -0
- atdd/coder/conventions/tests/test_adapter_recipe.py +302 -0
- atdd/coder/conventions/tests/test_complexity_recipe.py +289 -0
- atdd/coder/conventions/tests/test_component_taxonomy.py +278 -0
- atdd/coder/conventions/tests/test_component_urn_naming.py +165 -0
- atdd/coder/conventions/tests/test_thinness_recipe.py +286 -0
- atdd/coder/conventions/thinness.recipe.yaml +82 -0
- atdd/coder/conventions/train.convention.yaml +325 -0
- atdd/coder/conventions/verification.protocol.yaml +53 -0
- atdd/coder/schemas/design_system.schema.json +361 -0
- atdd/coder/validators/__init__.py +0 -0
- atdd/coder/validators/test_commons_structure.py +485 -0
- atdd/coder/validators/test_complexity.py +416 -0
- atdd/coder/validators/test_cross_language_consistency.py +431 -0
- atdd/coder/validators/test_design_system_compliance.py +413 -0
- atdd/coder/validators/test_dto_testing_patterns.py +268 -0
- atdd/coder/validators/test_green_cross_stack_layers.py +168 -0
- atdd/coder/validators/test_green_layer_dependencies.py +148 -0
- atdd/coder/validators/test_green_python_layer_structure.py +103 -0
- atdd/coder/validators/test_green_supabase_layer_structure.py +103 -0
- atdd/coder/validators/test_import_boundaries.py +396 -0
- atdd/coder/validators/test_init_file_urns.py +593 -0
- atdd/coder/validators/test_preact_layer_boundaries.py +221 -0
- atdd/coder/validators/test_presentation_convention.py +260 -0
- atdd/coder/validators/test_python_architecture.py +674 -0
- atdd/coder/validators/test_quality_metrics.py +420 -0
- atdd/coder/validators/test_station_master_pattern.py +244 -0
- atdd/coder/validators/test_train_infrastructure.py +454 -0
- atdd/coder/validators/test_train_urns.py +293 -0
- atdd/coder/validators/test_typescript_architecture.py +616 -0
- atdd/coder/validators/test_usecase_structure.py +421 -0
- atdd/coder/validators/test_wagon_boundaries.py +586 -0
- atdd/conftest.py +126 -0
- atdd/planner/__init__.py +1 -0
- atdd/planner/conventions/acceptance.convention.yaml +538 -0
- atdd/planner/conventions/appendix.convention.yaml +187 -0
- atdd/planner/conventions/artifact-naming.convention.yaml +852 -0
- atdd/planner/conventions/component.convention.yaml +670 -0
- atdd/planner/conventions/criteria.convention.yaml +141 -0
- atdd/planner/conventions/feature.convention.yaml +371 -0
- atdd/planner/conventions/interface.convention.yaml +382 -0
- atdd/planner/conventions/steps.convention.yaml +141 -0
- atdd/planner/conventions/train.convention.yaml +552 -0
- atdd/planner/conventions/wagon.convention.yaml +275 -0
- atdd/planner/conventions/wmbt.convention.yaml +258 -0
- atdd/planner/schemas/acceptance.schema.json +336 -0
- atdd/planner/schemas/appendix.schema.json +78 -0
- atdd/planner/schemas/component.schema.json +114 -0
- atdd/planner/schemas/feature.schema.json +197 -0
- atdd/planner/schemas/train.schema.json +192 -0
- atdd/planner/schemas/wagon.schema.json +281 -0
- atdd/planner/schemas/wmbt.schema.json +59 -0
- atdd/planner/validators/__init__.py +0 -0
- atdd/planner/validators/conftest.py +5 -0
- atdd/planner/validators/test_draft_wagon_registry.py +374 -0
- atdd/planner/validators/test_plan_cross_refs.py +240 -0
- atdd/planner/validators/test_plan_uniqueness.py +224 -0
- atdd/planner/validators/test_plan_urn_resolution.py +268 -0
- atdd/planner/validators/test_plan_wagons.py +174 -0
- atdd/planner/validators/test_train_validation.py +514 -0
- atdd/planner/validators/test_wagon_urn_chain.py +648 -0
- atdd/planner/validators/test_wmbt_consistency.py +327 -0
- atdd/planner/validators/test_wmbt_vocabulary.py +632 -0
- atdd/tester/__init__.py +1 -0
- atdd/tester/conventions/artifact.convention.yaml +257 -0
- atdd/tester/conventions/contract.convention.yaml +1009 -0
- atdd/tester/conventions/filename.convention.yaml +555 -0
- atdd/tester/conventions/migration.convention.yaml +509 -0
- atdd/tester/conventions/red.convention.yaml +797 -0
- atdd/tester/conventions/routing.convention.yaml +51 -0
- atdd/tester/conventions/telemetry.convention.yaml +458 -0
- atdd/tester/schemas/a11y.tmpl.json +17 -0
- atdd/tester/schemas/artifact.schema.json +189 -0
- atdd/tester/schemas/contract.schema.json +591 -0
- atdd/tester/schemas/contract.tmpl.json +95 -0
- atdd/tester/schemas/db.tmpl.json +20 -0
- atdd/tester/schemas/e2e.tmpl.json +17 -0
- atdd/tester/schemas/edge_function.tmpl.json +17 -0
- atdd/tester/schemas/event.tmpl.json +17 -0
- atdd/tester/schemas/http.tmpl.json +19 -0
- atdd/tester/schemas/job.tmpl.json +18 -0
- atdd/tester/schemas/load.tmpl.json +21 -0
- atdd/tester/schemas/metric.tmpl.json +19 -0
- atdd/tester/schemas/pack.schema.json +139 -0
- atdd/tester/schemas/realtime.tmpl.json +20 -0
- atdd/tester/schemas/rls.tmpl.json +18 -0
- atdd/tester/schemas/script.tmpl.json +16 -0
- atdd/tester/schemas/sec.tmpl.json +18 -0
- atdd/tester/schemas/storage.tmpl.json +18 -0
- atdd/tester/schemas/telemetry.schema.json +128 -0
- atdd/tester/schemas/telemetry_tracking_manifest.schema.json +143 -0
- atdd/tester/schemas/test_filename.schema.json +194 -0
- atdd/tester/schemas/test_intent.schema.json +179 -0
- atdd/tester/schemas/unit.tmpl.json +18 -0
- atdd/tester/schemas/visual.tmpl.json +18 -0
- atdd/tester/schemas/ws.tmpl.json +17 -0
- atdd/tester/utils/__init__.py +0 -0
- atdd/tester/utils/filename.py +300 -0
- atdd/tester/validators/__init__.py +0 -0
- atdd/tester/validators/cleanup_duplicate_headers.py +116 -0
- atdd/tester/validators/cleanup_duplicate_headers_v2.py +135 -0
- atdd/tester/validators/conftest.py +5 -0
- atdd/tester/validators/coverage_gap_report.py +321 -0
- atdd/tester/validators/fix_dual_ac_references.py +179 -0
- atdd/tester/validators/remove_duplicate_lines.py +93 -0
- atdd/tester/validators/test_acceptance_urn_filename_mapping.py +359 -0
- atdd/tester/validators/test_acceptance_urn_separator.py +166 -0
- atdd/tester/validators/test_artifact_naming_category.py +307 -0
- atdd/tester/validators/test_contract_schema_compliance.py +706 -0
- atdd/tester/validators/test_contracts_structure.py +200 -0
- atdd/tester/validators/test_coverage_adequacy.py +797 -0
- atdd/tester/validators/test_dual_ac_reference.py +225 -0
- atdd/tester/validators/test_fixture_validity.py +372 -0
- atdd/tester/validators/test_isolation.py +487 -0
- atdd/tester/validators/test_migration_coverage.py +204 -0
- atdd/tester/validators/test_migration_criteria.py +276 -0
- atdd/tester/validators/test_migration_generation.py +116 -0
- atdd/tester/validators/test_python_test_naming.py +410 -0
- atdd/tester/validators/test_red_layer_validation.py +95 -0
- atdd/tester/validators/test_red_python_layer_structure.py +87 -0
- atdd/tester/validators/test_red_supabase_layer_structure.py +90 -0
- atdd/tester/validators/test_telemetry_structure.py +634 -0
- atdd/tester/validators/test_typescript_test_naming.py +301 -0
- atdd/tester/validators/test_typescript_test_structure.py +84 -0
- atdd-0.2.1.dist-info/METADATA +221 -0
- atdd-0.2.1.dist-info/RECORD +184 -0
- atdd-0.2.1.dist-info/WHEEL +5 -0
- atdd-0.2.1.dist-info/entry_points.txt +2 -0
- atdd-0.2.1.dist-info/licenses/LICENSE +674 -0
- atdd-0.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Platform validation: Test filename generation from acceptance URNs.
|
|
3
|
+
|
|
4
|
+
Validates that actual test files in the repository follow the URN-based
|
|
5
|
+
naming convention for all supported languages.
|
|
6
|
+
|
|
7
|
+
Spec: SPEC-TESTER-CONV-0069 through SPEC-TESTER-CONV-0078
|
|
8
|
+
URN: acc:coach:platform-validation.filename-mapping
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import re
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
import pytest
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
REPO_ROOT = Path(__file__).parent.parent.parent
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# SPEC-TESTER-CONV-0069: Dart URN to filename mapping
|
|
21
|
+
def test_dart_urn_to_filename():
|
|
22
|
+
"""Test Dart filename generation from URN."""
|
|
23
|
+
from atdd.tester.utils.filename import dart_filename
|
|
24
|
+
|
|
25
|
+
test_cases = [
|
|
26
|
+
("acc:maintain-ux:C004-E2E-019-user-connection", "C004_E2E_019_user_connection_test.dart"),
|
|
27
|
+
("acc:maintain-ux:C004-UNIT-001", "C004_UNIT_001_test.dart"),
|
|
28
|
+
("acc:stage-characters:K005-WIDGET-012-avatar-display", "K005_WIDGET_012_avatar_display_test.dart"),
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
for urn, expected in test_cases:
|
|
32
|
+
assert dart_filename(urn) == expected
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# SPEC-TESTER-CONV-0070: TypeScript URN to filename mapping
|
|
36
|
+
def test_typescript_urn_to_filename():
|
|
37
|
+
"""Test TypeScript filename generation from URN."""
|
|
38
|
+
from atdd.tester.utils.filename import typescript_filename
|
|
39
|
+
|
|
40
|
+
test_cases = [
|
|
41
|
+
("acc:maintain-ux:C004-E2E-019-user-connection", "c004-e2e-019-user-connection.test.ts"),
|
|
42
|
+
("acc:maintain-ux:C004-HTTP-001", "c004-http-001.test.ts"),
|
|
43
|
+
("acc:resolve-dilemmas:R007-EVENT-005-choice-made", "r007-event-005-choice-made.test.ts"),
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
for urn, expected in test_cases:
|
|
47
|
+
assert typescript_filename(urn) == expected
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# SPEC-TESTER-CONV-0071: Python URN to filename mapping
|
|
51
|
+
def test_python_urn_to_filename():
|
|
52
|
+
"""Test Python filename generation from URN."""
|
|
53
|
+
from atdd.tester.utils.filename import python_filename
|
|
54
|
+
|
|
55
|
+
test_cases = [
|
|
56
|
+
("acc:maintain-ux:C004-E2E-019-user-connection", "test_c004_e2e_019_user_connection.py"),
|
|
57
|
+
("acc:commit-state:D001-UNIT-042", "test_d001_unit_042.py"),
|
|
58
|
+
("acc:construct-graph:M008-INTEGRATION-007-graph-build", "test_m008_integration_007_graph_build.py"),
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
for urn, expected in test_cases:
|
|
62
|
+
assert python_filename(urn) == expected
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# SPEC-TESTER-CONV-0072: Go URN to filename mapping
|
|
66
|
+
def test_go_urn_to_filename():
|
|
67
|
+
"""Test Go filename generation from URN."""
|
|
68
|
+
from atdd.tester.utils.filename import go_filename
|
|
69
|
+
|
|
70
|
+
test_cases = [
|
|
71
|
+
("acc:maintain-ux:C004-E2E-019-user-connection", "c004_e2e_019_user_connection_test.go"),
|
|
72
|
+
("acc:commit-state:D001-UNIT-042", "d001_unit_042_test.go"),
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
for urn, expected in test_cases:
|
|
76
|
+
assert go_filename(urn) == expected
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# SPEC-TESTER-CONV-0073: Java/Kotlin URN to classname mapping
|
|
80
|
+
def test_java_urn_to_classname():
|
|
81
|
+
"""Test Java/Kotlin classname generation from URN."""
|
|
82
|
+
from atdd.tester.utils.filename import java_classname
|
|
83
|
+
|
|
84
|
+
test_cases = [
|
|
85
|
+
("acc:maintain-ux:C004-E2E-019-user-connection", "C004E2E019UserConnectionTest"),
|
|
86
|
+
("acc:commit-state:D001-UNIT-042", "D001UNIT042Test"),
|
|
87
|
+
("acc:stage-characters:K005-INTEGRATION-003-full-flow", "K005INTEGRATION003FullFlowTest"),
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
for urn, expected in test_cases:
|
|
91
|
+
assert java_classname(urn) == expected
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# SPEC-TESTER-CONV-0074: Handle URNs without slug
|
|
95
|
+
def test_no_slug_handling():
|
|
96
|
+
"""Test filename generation for URNs without optional slug."""
|
|
97
|
+
from atdd.tester.utils.filename import dart_filename, typescript_filename, python_filename
|
|
98
|
+
|
|
99
|
+
urn = "acc:maintain-ux:C004-E2E-019"
|
|
100
|
+
|
|
101
|
+
assert dart_filename(urn) == "C004_E2E_019_test.dart"
|
|
102
|
+
assert typescript_filename(urn) == "c004-e2e-019.test.ts"
|
|
103
|
+
assert python_filename(urn) == "test_c004_e2e_019.py"
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_typescript_preact_urn_to_filename():
|
|
107
|
+
"""Test Preact TypeScript filename generation from URN."""
|
|
108
|
+
from atdd.tester.utils.filename import typescript_preact_filename
|
|
109
|
+
|
|
110
|
+
test_cases = [
|
|
111
|
+
("acc:maintain-ux:C004-E2E-019-user-connection", "C004_E2E_019_user_connection.test.ts"),
|
|
112
|
+
("acc:maintain-ux:C004-HTTP-001", "C004_HTTP_001.test.ts"),
|
|
113
|
+
("acc:maintain-ux:C001-WIDGET-001-button", "C001_WIDGET_001_button.test.tsx"),
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
for urn, expected in test_cases:
|
|
117
|
+
assert typescript_preact_filename(urn) == expected
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# SPEC-TESTER-CONV-0076: URN pattern validation
|
|
121
|
+
def test_urn_pattern_validation():
|
|
122
|
+
"""Test URN regex pattern matches valid acceptance URNs."""
|
|
123
|
+
from atdd.tester.utils.filename import URN_PATTERN
|
|
124
|
+
|
|
125
|
+
valid_urns = [
|
|
126
|
+
"acc:maintain-ux:C004-E2E-019-user-connection",
|
|
127
|
+
"acc:pace-dilemmas:P003-UNIT-042",
|
|
128
|
+
"acc:predict-cascade:L009-HTTP-001-api-endpoint",
|
|
129
|
+
"acc:commit-state:D001-EVENT-005",
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
for urn in valid_urns:
|
|
133
|
+
assert re.match(URN_PATTERN, urn), f"Should match: {urn}"
|
|
134
|
+
|
|
135
|
+
invalid_urns = [
|
|
136
|
+
"acc:maintain_ux:C004-E2E-019", # Underscore not allowed
|
|
137
|
+
"wmbt:maintain-ux:C004-E2E-019", # Wrong prefix
|
|
138
|
+
"acc:maintain-ux:C4-E2E-019", # WMBT not zero-padded
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
for urn in invalid_urns:
|
|
142
|
+
assert not re.match(URN_PATTERN, urn), f"Should not match: {urn}"
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# SPEC-TESTER-CONV-0078: Platform validation enforces filename convention compliance
|
|
146
|
+
def test_dart_files_match_convention():
|
|
147
|
+
"""Validate Dart test files follow URN-based naming convention."""
|
|
148
|
+
from atdd.tester.utils.filename import parse_acceptance_urn, dart_filename
|
|
149
|
+
|
|
150
|
+
# Pattern: {WMBT}_{HARNESS}_{NNN}[_{slug}]_test.dart
|
|
151
|
+
dart_pattern = re.compile(r'^([A-Z][0-9]{3})_([A-Z0-9]+)_([0-9]{3})(?:_([a-z0-9_]+))?_test\.dart$')
|
|
152
|
+
|
|
153
|
+
test_dirs = [
|
|
154
|
+
REPO_ROOT / "test",
|
|
155
|
+
REPO_ROOT / "integration_test",
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
violations = []
|
|
159
|
+
|
|
160
|
+
for test_dir in test_dirs:
|
|
161
|
+
if not test_dir.exists():
|
|
162
|
+
continue
|
|
163
|
+
|
|
164
|
+
for dart_file in test_dir.rglob("*_test.dart"):
|
|
165
|
+
filename = dart_file.name
|
|
166
|
+
|
|
167
|
+
# Check pattern compliance
|
|
168
|
+
match = dart_pattern.match(filename)
|
|
169
|
+
if not match:
|
|
170
|
+
# Check if there's a URN comment to determine expected filename
|
|
171
|
+
content = dart_file.read_text()
|
|
172
|
+
urn_match = re.search(r'// urn: (acc:[a-z][a-z0-9-]*:[A-Z][0-9]{3}-[A-Z0-9]+-[0-9]{3}(?:-[a-z0-9-]+)?)', content)
|
|
173
|
+
|
|
174
|
+
if urn_match:
|
|
175
|
+
urn = urn_match.group(1)
|
|
176
|
+
expected = dart_filename(urn)
|
|
177
|
+
violations.append({
|
|
178
|
+
'file': str(dart_file.relative_to(REPO_ROOT)),
|
|
179
|
+
'actual': filename,
|
|
180
|
+
'expected': expected,
|
|
181
|
+
'urn': urn
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
# Allow some violations during migration, but report them
|
|
185
|
+
if violations:
|
|
186
|
+
report = "\n".join([
|
|
187
|
+
f" {v['file']}: {v['actual']} → {v['expected']} (URN: {v['urn']})"
|
|
188
|
+
for v in violations[:5] # Show first 5
|
|
189
|
+
])
|
|
190
|
+
print(f"\nFilename convention violations found:\n{report}")
|
|
191
|
+
|
|
192
|
+
# For now, just log violations without failing
|
|
193
|
+
# In the future, this should assert len(violations) == 0
|
|
194
|
+
assert True, "Validation complete (violations logged)"
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def test_typescript_files_match_convention():
|
|
198
|
+
"""Validate TypeScript test files follow URN-based naming convention."""
|
|
199
|
+
from atdd.tester.utils.filename import typescript_filename
|
|
200
|
+
|
|
201
|
+
# Pattern: {wmbt}-{harness}-{nnn}[-{slug}].test.ts
|
|
202
|
+
ts_pattern = re.compile(r'^([a-z][0-9]{3})-([a-z0-9]+)-([0-9]{3})(?:-([a-z0-9-]+))?\.test\.ts$')
|
|
203
|
+
|
|
204
|
+
test_dirs = [
|
|
205
|
+
REPO_ROOT / "supabase" / "functions",
|
|
206
|
+
REPO_ROOT / "e2e",
|
|
207
|
+
]
|
|
208
|
+
|
|
209
|
+
violations = []
|
|
210
|
+
|
|
211
|
+
for test_dir in test_dirs:
|
|
212
|
+
if not test_dir.exists():
|
|
213
|
+
continue
|
|
214
|
+
|
|
215
|
+
for ts_file in test_dir.rglob("*.test.ts"):
|
|
216
|
+
filename = ts_file.name
|
|
217
|
+
match = ts_pattern.match(filename)
|
|
218
|
+
|
|
219
|
+
if not match:
|
|
220
|
+
content = ts_file.read_text()
|
|
221
|
+
urn_match = re.search(r'// urn: (acc:[a-z][a-z0-9-]*:[A-Z][0-9]{3}-[A-Z0-9]+-[0-9]{3}(?:-[a-z0-9-]+)?)', content)
|
|
222
|
+
|
|
223
|
+
if urn_match:
|
|
224
|
+
urn = urn_match.group(1)
|
|
225
|
+
expected = typescript_filename(urn)
|
|
226
|
+
violations.append({
|
|
227
|
+
'file': str(ts_file.relative_to(REPO_ROOT)),
|
|
228
|
+
'actual': filename,
|
|
229
|
+
'expected': expected,
|
|
230
|
+
'urn': urn
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
if violations:
|
|
234
|
+
report = "\n".join([
|
|
235
|
+
f" {v['file']}: {v['actual']} → {v['expected']} (URN: {v['urn']})"
|
|
236
|
+
for v in violations[:5]
|
|
237
|
+
])
|
|
238
|
+
print(f"\nFilename convention violations found:\n{report}")
|
|
239
|
+
|
|
240
|
+
assert True, "Validation complete (violations logged)"
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def test_typescript_preact_files_match_convention():
|
|
244
|
+
"""Validate Preact TypeScript test files follow URN-based naming convention."""
|
|
245
|
+
from atdd.tester.utils.filename import typescript_preact_filename
|
|
246
|
+
|
|
247
|
+
ts_pattern = re.compile(r'^([A-Z][0-9]{3})_([A-Z0-9]+)_([0-9]{3})(?:_([a-z0-9_]+))?\.test\.ts(x)?$')
|
|
248
|
+
|
|
249
|
+
test_dirs = [
|
|
250
|
+
REPO_ROOT / "web" / "tests",
|
|
251
|
+
]
|
|
252
|
+
|
|
253
|
+
violations = []
|
|
254
|
+
|
|
255
|
+
for test_dir in test_dirs:
|
|
256
|
+
if not test_dir.exists():
|
|
257
|
+
continue
|
|
258
|
+
|
|
259
|
+
ts_files = list(test_dir.rglob("*.test.ts")) + list(test_dir.rglob("*.test.tsx"))
|
|
260
|
+
for ts_file in ts_files:
|
|
261
|
+
filename = ts_file.name
|
|
262
|
+
match = ts_pattern.match(filename)
|
|
263
|
+
|
|
264
|
+
if not match:
|
|
265
|
+
content = ts_file.read_text()
|
|
266
|
+
urn_match = re.search(
|
|
267
|
+
r'//\s*(?:urn|URN):\s*(acc:[a-z][a-z0-9-]*:[A-Z][0-9]{3}-[A-Z0-9]+-[0-9]{3}(?:-[a-z0-9-]+)?)',
|
|
268
|
+
content
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
if urn_match:
|
|
272
|
+
urn = urn_match.group(1)
|
|
273
|
+
expected = typescript_preact_filename(urn)
|
|
274
|
+
violations.append({
|
|
275
|
+
'file': str(ts_file.relative_to(REPO_ROOT)),
|
|
276
|
+
'actual': filename,
|
|
277
|
+
'expected': expected,
|
|
278
|
+
'urn': urn
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
if violations:
|
|
282
|
+
report = "\n".join([
|
|
283
|
+
f" {v['file']}: {v['actual']} → {v['expected']} (URN: {v['urn']})"
|
|
284
|
+
for v in violations[:5]
|
|
285
|
+
])
|
|
286
|
+
print(f"\nFilename convention violations found:\n{report}")
|
|
287
|
+
|
|
288
|
+
assert True, "Validation complete (violations logged)"
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def test_python_files_match_convention():
|
|
292
|
+
"""Validate Python test files follow URN-based naming convention."""
|
|
293
|
+
from atdd.tester.utils.filename import python_filename
|
|
294
|
+
|
|
295
|
+
# Pattern: test_{wmbt}_{harness}_{nnn}[_{slug}].py
|
|
296
|
+
py_pattern = re.compile(r'^test_([a-z][0-9]{3})_([a-z0-9]+)_([0-9]{3})(?:_([a-z0-9_]+))?\.py$')
|
|
297
|
+
|
|
298
|
+
test_dirs = [
|
|
299
|
+
REPO_ROOT / "tests",
|
|
300
|
+
REPO_ROOT / "atdd" / "coach" / "validators",
|
|
301
|
+
REPO_ROOT / "atdd" / "tester" / "validators",
|
|
302
|
+
]
|
|
303
|
+
|
|
304
|
+
violations = []
|
|
305
|
+
|
|
306
|
+
for test_dir in test_dirs:
|
|
307
|
+
if not test_dir.exists():
|
|
308
|
+
continue
|
|
309
|
+
|
|
310
|
+
for py_file in test_dir.rglob("test_*.py"):
|
|
311
|
+
filename = py_file.name
|
|
312
|
+
match = py_pattern.match(filename)
|
|
313
|
+
|
|
314
|
+
if not match:
|
|
315
|
+
try:
|
|
316
|
+
content = py_file.read_text()
|
|
317
|
+
urn_match = re.search(r'# urn: (acc:[a-z][a-z0-9-]*:[A-Z][0-9]{3}-[A-Z0-9]+-[0-9]{3}(?:-[a-z0-9-]+)?)', content)
|
|
318
|
+
|
|
319
|
+
if urn_match:
|
|
320
|
+
urn = urn_match.group(1)
|
|
321
|
+
expected = python_filename(urn)
|
|
322
|
+
violations.append({
|
|
323
|
+
'file': str(py_file.relative_to(REPO_ROOT)),
|
|
324
|
+
'actual': filename,
|
|
325
|
+
'expected': expected,
|
|
326
|
+
'urn': urn
|
|
327
|
+
})
|
|
328
|
+
except Exception:
|
|
329
|
+
pass # Skip files that can't be read
|
|
330
|
+
|
|
331
|
+
if violations:
|
|
332
|
+
report = "\n".join([
|
|
333
|
+
f" {v['file']}: {v['actual']} → {v['expected']} (URN: {v['urn']})"
|
|
334
|
+
for v in violations[:5]
|
|
335
|
+
])
|
|
336
|
+
print(f"\nFilename convention violations found:\n{report}")
|
|
337
|
+
|
|
338
|
+
assert True, "Validation complete (violations logged)"
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def test_urn_comment_extraction():
|
|
342
|
+
"""Test extraction of URN from test file comments."""
|
|
343
|
+
# Dart style: // urn: acc:...
|
|
344
|
+
dart_content = """
|
|
345
|
+
// urn: acc:maintain-ux:C004-E2E-019-user-connection
|
|
346
|
+
import 'package:flutter_test/flutter_test.dart';
|
|
347
|
+
"""
|
|
348
|
+
urn_match = re.search(r'// urn: (acc:[^\s]+)', dart_content)
|
|
349
|
+
assert urn_match
|
|
350
|
+
assert urn_match.group(1) == "acc:maintain-ux:C004-E2E-019-user-connection"
|
|
351
|
+
|
|
352
|
+
# Python style: # urn: acc:...
|
|
353
|
+
python_content = """
|
|
354
|
+
# urn: acc:commit-state:D001-UNIT-042
|
|
355
|
+
import pytest
|
|
356
|
+
"""
|
|
357
|
+
urn_match = re.search(r'# urn: (acc:[^\s]+)', python_content)
|
|
358
|
+
assert urn_match
|
|
359
|
+
assert urn_match.group(1) == "acc:commit-state:D001-UNIT-042"
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Platform validation for Acceptance URN format (Updated for SPEC-COACH-UTILS-0282).
|
|
3
|
+
|
|
4
|
+
SPEC-COACH-UTILS-0282: Acceptance URN refactored to include WMBT ID and harness
|
|
5
|
+
SPEC-PLANNER-CONV-0052: Acceptance URN separator follows hierarchy-facet rule
|
|
6
|
+
SPEC-PLANNER-CONV-0057: URNBuilder.acceptance updated for new separator pattern
|
|
7
|
+
SPEC-PLANNER-CONV-0058: Acceptance schema updated to reflect new URN pattern
|
|
8
|
+
SPEC-TESTER-CONV-0068 through SPEC-TESTER-CONV-0078: Test filename generation from URNs
|
|
9
|
+
|
|
10
|
+
Background:
|
|
11
|
+
- URN rule: colon = hierarchy, dash = facet grouping
|
|
12
|
+
- New Acceptance URN structure: acc:{wagon}:{wmbt_id}-{harness}-{NNN}[-{slug}]
|
|
13
|
+
- Wagon is hierarchical (use colon)
|
|
14
|
+
- wmbt_id, harness, sequence, and optional slug are facets (use dash)
|
|
15
|
+
|
|
16
|
+
Filename Generation:
|
|
17
|
+
- URN-based filename generation implemented in atdd.tester.utils.filename
|
|
18
|
+
- Convention documented in .claude/conventions/tester/filename.convention.yaml
|
|
19
|
+
- See test_acceptance_urn_filename_mapping.py for filename validation tests
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import pytest
|
|
23
|
+
import yaml
|
|
24
|
+
import re
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
# Path constants
|
|
28
|
+
REPO_ROOT = Path(__file__).resolve().parents[4]
|
|
29
|
+
CONVENTIONS_DIR = REPO_ROOT / "atdd" / "planner" / "conventions"
|
|
30
|
+
SCHEMAS_DIR = REPO_ROOT / "atdd" / "planner" / "schemas"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# SPEC-COACH-UTILS-0282
|
|
34
|
+
def test_acceptance_urn_format_updated():
|
|
35
|
+
"""
|
|
36
|
+
Given: Current format in acceptance.convention.yaml
|
|
37
|
+
When: Applying new format with WMBT ID and harness
|
|
38
|
+
Then: URN examples follow pattern acc:{wagon}:{wmbt_id}-{harness}-{NNN}[-{slug}]
|
|
39
|
+
Example: 'acc:maintain-ux:C004-E2E-019-user-connection'
|
|
40
|
+
"""
|
|
41
|
+
acceptance_convention_path = CONVENTIONS_DIR / "acceptance.convention.yaml"
|
|
42
|
+
|
|
43
|
+
with open(acceptance_convention_path, 'r') as f:
|
|
44
|
+
convention = yaml.safe_load(f)
|
|
45
|
+
|
|
46
|
+
# Check urn_generation section for example
|
|
47
|
+
urn_generation = convention.get('urn_generation', {})
|
|
48
|
+
|
|
49
|
+
# Get pattern
|
|
50
|
+
pattern = urn_generation.get('pattern', '')
|
|
51
|
+
assert pattern == "acc:{wagon}:{wmbt_id}-{harness}-{NNN}[-{slug}]", \
|
|
52
|
+
f"Expected new pattern format, got: {pattern}"
|
|
53
|
+
|
|
54
|
+
# Get example (multi-line string with code)
|
|
55
|
+
example_code = urn_generation.get('example', '')
|
|
56
|
+
assert len(example_code) > 0, "Should have example code"
|
|
57
|
+
|
|
58
|
+
# Extract URN examples from comments in the code
|
|
59
|
+
# Look for patterns like: # Returns: "acc:authenticate-user:C004-E2E-019"
|
|
60
|
+
urn_matches = re.findall(r'acc:[a-z][a-z0-9-]*:[DLPCEMYRK]\d{3}-[A-Z0-9]+-\d{3}(?:-[a-z0-9-]+)?', example_code)
|
|
61
|
+
assert len(urn_matches) > 0, f"Should have at least one URN example in code, got: {example_code}"
|
|
62
|
+
|
|
63
|
+
# Verify at least one URN matches new format
|
|
64
|
+
for urn in urn_matches:
|
|
65
|
+
# Verify structure
|
|
66
|
+
parts = urn.split(':')
|
|
67
|
+
assert len(parts) == 3, f"URN should have 3 colon-separated parts, got: {urn}"
|
|
68
|
+
assert parts[0] == 'acc', f"URN kind should be 'acc', got: {parts[0]}"
|
|
69
|
+
|
|
70
|
+
# Parts[2] should have format {wmbt_id}-{harness}-{seq}[-{slug}]
|
|
71
|
+
facets = parts[2]
|
|
72
|
+
assert '-' in facets, f"WMBT facets should be dash-separated, got: {facets}"
|
|
73
|
+
|
|
74
|
+
facet_parts = facets.split('-')
|
|
75
|
+
assert len(facet_parts) >= 3, f"Should have at least wmbt_id, harness, seq, got: {facets}"
|
|
76
|
+
|
|
77
|
+
# Check WMBT ID format: [DLPCEMYRK]\d{3}
|
|
78
|
+
wmbt_id = facet_parts[0]
|
|
79
|
+
assert re.match(r'^[DLPCEMYRK]\d{3}$', wmbt_id), \
|
|
80
|
+
f"WMBT ID should match step-coded format, got: {wmbt_id}"
|
|
81
|
+
|
|
82
|
+
# At least one valid URN found
|
|
83
|
+
break
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# SPEC-COACH-UTILS-0282
|
|
87
|
+
def test_urn_builder_acceptance_separator(monkeypatch):
|
|
88
|
+
"""
|
|
89
|
+
Given: URNBuilder utility exists
|
|
90
|
+
When: Using new format with WMBT ID and harness
|
|
91
|
+
Then: acceptance() method generates: acc:{wagon}:{wmbt_id}-{harness}-{NNN}[-{slug}]
|
|
92
|
+
"""
|
|
93
|
+
# Try to import URNBuilder from current location
|
|
94
|
+
try:
|
|
95
|
+
from atdd.coach.utils.graph.urn import URNBuilder
|
|
96
|
+
|
|
97
|
+
# Test URNBuilder.acceptance method with new signature
|
|
98
|
+
# New signature: acceptance(wagon_id, wmbt_id, harness_code, seq, slug=None)
|
|
99
|
+
urn = URNBuilder.acceptance("authenticate-user", "E005", "UNIT", "201")
|
|
100
|
+
|
|
101
|
+
assert urn == "acc:authenticate-user:E005-UNIT-201", \
|
|
102
|
+
f"Expected 'acc:authenticate-user:E005-UNIT-201', got '{urn}'"
|
|
103
|
+
|
|
104
|
+
# Verify structure
|
|
105
|
+
assert urn.count(':') == 2, \
|
|
106
|
+
f"URN should have 2 colons (hierarchy separators), got: {urn}"
|
|
107
|
+
assert urn.count('-') >= 2, \
|
|
108
|
+
f"URN should have at least 2 dashes (facet separators), got: {urn}"
|
|
109
|
+
|
|
110
|
+
# Test with optional slug
|
|
111
|
+
urn_with_slug = URNBuilder.acceptance("maintain-ux", "C004", "E2E", "019", "user-connection")
|
|
112
|
+
assert urn_with_slug == "acc:maintain-ux:C004-E2E-019-user-connection", \
|
|
113
|
+
f"Expected 'acc:maintain-ux:C004-E2E-019-user-connection', got '{urn_with_slug}'"
|
|
114
|
+
|
|
115
|
+
except ImportError as e:
|
|
116
|
+
pytest.skip(f"URNBuilder not yet implemented or location changed: {e}")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# SPEC-PLANNER-CONV-0058
|
|
120
|
+
def test_schema_validates_new_urn_format():
|
|
121
|
+
"""
|
|
122
|
+
Given: Acceptance schema validates URN format
|
|
123
|
+
When: Updating schema to match new convention
|
|
124
|
+
Then: URN pattern updated to match acc:{wagon}:{nnn}.{acceptance_id}
|
|
125
|
+
Schema validation accepts new format
|
|
126
|
+
"""
|
|
127
|
+
# Find acceptance schema
|
|
128
|
+
acceptance_schema_files = list(SCHEMAS_DIR.glob("acceptance*.json"))
|
|
129
|
+
|
|
130
|
+
if not acceptance_schema_files:
|
|
131
|
+
pytest.skip("No acceptance schema file found")
|
|
132
|
+
|
|
133
|
+
import json
|
|
134
|
+
schema_path = acceptance_schema_files[0]
|
|
135
|
+
|
|
136
|
+
with open(schema_path, 'r') as f:
|
|
137
|
+
schema = json.load(f)
|
|
138
|
+
|
|
139
|
+
# Look for URN pattern in schema
|
|
140
|
+
# This could be in various locations, so we'll search recursively
|
|
141
|
+
def find_urn_pattern(obj, path=""):
|
|
142
|
+
"""Recursively search for URN pattern or examples"""
|
|
143
|
+
if isinstance(obj, dict):
|
|
144
|
+
# Check if this is a URN field
|
|
145
|
+
if 'pattern' in obj and path.endswith('urn'):
|
|
146
|
+
pattern = obj['pattern']
|
|
147
|
+
# Pattern should allow colon between wagon and sequence
|
|
148
|
+
# Pattern example: ^acc:[a-z-]+:\d{3}\.[A-Z-]+$
|
|
149
|
+
if 'acc:' in pattern:
|
|
150
|
+
# Verify pattern uses colon separator
|
|
151
|
+
# Should NOT have pattern like acc:[a-z-]+\.\d{3}
|
|
152
|
+
assert r'\.' not in pattern.split(':')[1] if len(pattern.split(':')) > 1 else True, \
|
|
153
|
+
f"Pattern should use colon between wagon and sequence, got: {pattern}"
|
|
154
|
+
|
|
155
|
+
for key, value in obj.items():
|
|
156
|
+
find_urn_pattern(value, f"{path}.{key}")
|
|
157
|
+
|
|
158
|
+
elif isinstance(obj, list):
|
|
159
|
+
for item in obj:
|
|
160
|
+
find_urn_pattern(item, path)
|
|
161
|
+
|
|
162
|
+
find_urn_pattern(schema)
|
|
163
|
+
|
|
164
|
+
# Test would pass if schema is updated correctly
|
|
165
|
+
# For now, we're just checking structure exists
|
|
166
|
+
assert True, "Schema structure validated"
|