atdd 0.1.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.
Files changed (183) hide show
  1. atdd/__init__.py +0 -0
  2. atdd/cli.py +404 -0
  3. atdd/coach/__init__.py +0 -0
  4. atdd/coach/commands/__init__.py +0 -0
  5. atdd/coach/commands/add_persistence_metadata.py +215 -0
  6. atdd/coach/commands/analyze_migrations.py +188 -0
  7. atdd/coach/commands/consumers.py +720 -0
  8. atdd/coach/commands/infer_governance_status.py +149 -0
  9. atdd/coach/commands/initializer.py +177 -0
  10. atdd/coach/commands/interface.py +1078 -0
  11. atdd/coach/commands/inventory.py +565 -0
  12. atdd/coach/commands/migration.py +240 -0
  13. atdd/coach/commands/registry.py +1560 -0
  14. atdd/coach/commands/session.py +430 -0
  15. atdd/coach/commands/sync.py +405 -0
  16. atdd/coach/commands/test_interface.py +399 -0
  17. atdd/coach/commands/test_runner.py +141 -0
  18. atdd/coach/commands/tests/__init__.py +1 -0
  19. atdd/coach/commands/tests/test_telemetry_array_validation.py +235 -0
  20. atdd/coach/commands/traceability.py +4264 -0
  21. atdd/coach/conventions/session.convention.yaml +754 -0
  22. atdd/coach/overlays/__init__.py +2 -0
  23. atdd/coach/overlays/claude.md +2 -0
  24. atdd/coach/schemas/config.schema.json +34 -0
  25. atdd/coach/schemas/manifest.schema.json +101 -0
  26. atdd/coach/templates/ATDD.md +282 -0
  27. atdd/coach/templates/SESSION-TEMPLATE.md +327 -0
  28. atdd/coach/utils/__init__.py +0 -0
  29. atdd/coach/utils/graph/__init__.py +0 -0
  30. atdd/coach/utils/graph/urn.py +875 -0
  31. atdd/coach/validators/__init__.py +0 -0
  32. atdd/coach/validators/shared_fixtures.py +365 -0
  33. atdd/coach/validators/test_enrich_wagon_registry.py +167 -0
  34. atdd/coach/validators/test_registry.py +575 -0
  35. atdd/coach/validators/test_session_validation.py +1183 -0
  36. atdd/coach/validators/test_traceability.py +448 -0
  37. atdd/coach/validators/test_update_feature_paths.py +108 -0
  38. atdd/coach/validators/test_validate_contract_consumers.py +297 -0
  39. atdd/coder/__init__.py +1 -0
  40. atdd/coder/conventions/adapter.recipe.yaml +88 -0
  41. atdd/coder/conventions/backend.convention.yaml +460 -0
  42. atdd/coder/conventions/boundaries.convention.yaml +666 -0
  43. atdd/coder/conventions/commons.convention.yaml +460 -0
  44. atdd/coder/conventions/complexity.recipe.yaml +109 -0
  45. atdd/coder/conventions/component-naming.convention.yaml +178 -0
  46. atdd/coder/conventions/design.convention.yaml +327 -0
  47. atdd/coder/conventions/design.recipe.yaml +273 -0
  48. atdd/coder/conventions/dto.convention.yaml +660 -0
  49. atdd/coder/conventions/frontend.convention.yaml +542 -0
  50. atdd/coder/conventions/green.convention.yaml +1012 -0
  51. atdd/coder/conventions/presentation.convention.yaml +587 -0
  52. atdd/coder/conventions/refactor.convention.yaml +535 -0
  53. atdd/coder/conventions/technology.convention.yaml +206 -0
  54. atdd/coder/conventions/tests/__init__.py +0 -0
  55. atdd/coder/conventions/tests/test_adapter_recipe.py +302 -0
  56. atdd/coder/conventions/tests/test_complexity_recipe.py +289 -0
  57. atdd/coder/conventions/tests/test_component_taxonomy.py +278 -0
  58. atdd/coder/conventions/tests/test_component_urn_naming.py +165 -0
  59. atdd/coder/conventions/tests/test_thinness_recipe.py +286 -0
  60. atdd/coder/conventions/thinness.recipe.yaml +82 -0
  61. atdd/coder/conventions/train.convention.yaml +325 -0
  62. atdd/coder/conventions/verification.protocol.yaml +53 -0
  63. atdd/coder/schemas/design_system.schema.json +361 -0
  64. atdd/coder/validators/__init__.py +0 -0
  65. atdd/coder/validators/test_commons_structure.py +485 -0
  66. atdd/coder/validators/test_complexity.py +416 -0
  67. atdd/coder/validators/test_cross_language_consistency.py +431 -0
  68. atdd/coder/validators/test_design_system_compliance.py +413 -0
  69. atdd/coder/validators/test_dto_testing_patterns.py +268 -0
  70. atdd/coder/validators/test_green_cross_stack_layers.py +168 -0
  71. atdd/coder/validators/test_green_layer_dependencies.py +148 -0
  72. atdd/coder/validators/test_green_python_layer_structure.py +103 -0
  73. atdd/coder/validators/test_green_supabase_layer_structure.py +103 -0
  74. atdd/coder/validators/test_import_boundaries.py +396 -0
  75. atdd/coder/validators/test_init_file_urns.py +593 -0
  76. atdd/coder/validators/test_preact_layer_boundaries.py +221 -0
  77. atdd/coder/validators/test_presentation_convention.py +260 -0
  78. atdd/coder/validators/test_python_architecture.py +674 -0
  79. atdd/coder/validators/test_quality_metrics.py +420 -0
  80. atdd/coder/validators/test_station_master_pattern.py +244 -0
  81. atdd/coder/validators/test_train_infrastructure.py +454 -0
  82. atdd/coder/validators/test_train_urns.py +293 -0
  83. atdd/coder/validators/test_typescript_architecture.py +616 -0
  84. atdd/coder/validators/test_usecase_structure.py +421 -0
  85. atdd/coder/validators/test_wagon_boundaries.py +586 -0
  86. atdd/conftest.py +126 -0
  87. atdd/planner/__init__.py +1 -0
  88. atdd/planner/conventions/acceptance.convention.yaml +538 -0
  89. atdd/planner/conventions/appendix.convention.yaml +187 -0
  90. atdd/planner/conventions/artifact-naming.convention.yaml +852 -0
  91. atdd/planner/conventions/component.convention.yaml +670 -0
  92. atdd/planner/conventions/criteria.convention.yaml +141 -0
  93. atdd/planner/conventions/feature.convention.yaml +371 -0
  94. atdd/planner/conventions/interface.convention.yaml +382 -0
  95. atdd/planner/conventions/steps.convention.yaml +141 -0
  96. atdd/planner/conventions/train.convention.yaml +552 -0
  97. atdd/planner/conventions/wagon.convention.yaml +275 -0
  98. atdd/planner/conventions/wmbt.convention.yaml +258 -0
  99. atdd/planner/schemas/acceptance.schema.json +336 -0
  100. atdd/planner/schemas/appendix.schema.json +78 -0
  101. atdd/planner/schemas/component.schema.json +114 -0
  102. atdd/planner/schemas/feature.schema.json +197 -0
  103. atdd/planner/schemas/train.schema.json +192 -0
  104. atdd/planner/schemas/wagon.schema.json +281 -0
  105. atdd/planner/schemas/wmbt.schema.json +59 -0
  106. atdd/planner/validators/__init__.py +0 -0
  107. atdd/planner/validators/conftest.py +5 -0
  108. atdd/planner/validators/test_draft_wagon_registry.py +374 -0
  109. atdd/planner/validators/test_plan_cross_refs.py +240 -0
  110. atdd/planner/validators/test_plan_uniqueness.py +224 -0
  111. atdd/planner/validators/test_plan_urn_resolution.py +268 -0
  112. atdd/planner/validators/test_plan_wagons.py +174 -0
  113. atdd/planner/validators/test_train_validation.py +514 -0
  114. atdd/planner/validators/test_wagon_urn_chain.py +648 -0
  115. atdd/planner/validators/test_wmbt_consistency.py +327 -0
  116. atdd/planner/validators/test_wmbt_vocabulary.py +632 -0
  117. atdd/tester/__init__.py +1 -0
  118. atdd/tester/conventions/artifact.convention.yaml +257 -0
  119. atdd/tester/conventions/contract.convention.yaml +1009 -0
  120. atdd/tester/conventions/filename.convention.yaml +555 -0
  121. atdd/tester/conventions/migration.convention.yaml +509 -0
  122. atdd/tester/conventions/red.convention.yaml +797 -0
  123. atdd/tester/conventions/routing.convention.yaml +51 -0
  124. atdd/tester/conventions/telemetry.convention.yaml +458 -0
  125. atdd/tester/schemas/a11y.tmpl.json +17 -0
  126. atdd/tester/schemas/artifact.schema.json +189 -0
  127. atdd/tester/schemas/contract.schema.json +591 -0
  128. atdd/tester/schemas/contract.tmpl.json +95 -0
  129. atdd/tester/schemas/db.tmpl.json +20 -0
  130. atdd/tester/schemas/e2e.tmpl.json +17 -0
  131. atdd/tester/schemas/edge_function.tmpl.json +17 -0
  132. atdd/tester/schemas/event.tmpl.json +17 -0
  133. atdd/tester/schemas/http.tmpl.json +19 -0
  134. atdd/tester/schemas/job.tmpl.json +18 -0
  135. atdd/tester/schemas/load.tmpl.json +21 -0
  136. atdd/tester/schemas/metric.tmpl.json +19 -0
  137. atdd/tester/schemas/pack.schema.json +139 -0
  138. atdd/tester/schemas/realtime.tmpl.json +20 -0
  139. atdd/tester/schemas/rls.tmpl.json +18 -0
  140. atdd/tester/schemas/script.tmpl.json +16 -0
  141. atdd/tester/schemas/sec.tmpl.json +18 -0
  142. atdd/tester/schemas/storage.tmpl.json +18 -0
  143. atdd/tester/schemas/telemetry.schema.json +128 -0
  144. atdd/tester/schemas/telemetry_tracking_manifest.schema.json +143 -0
  145. atdd/tester/schemas/test_filename.schema.json +194 -0
  146. atdd/tester/schemas/test_intent.schema.json +179 -0
  147. atdd/tester/schemas/unit.tmpl.json +18 -0
  148. atdd/tester/schemas/visual.tmpl.json +18 -0
  149. atdd/tester/schemas/ws.tmpl.json +17 -0
  150. atdd/tester/utils/__init__.py +0 -0
  151. atdd/tester/utils/filename.py +300 -0
  152. atdd/tester/validators/__init__.py +0 -0
  153. atdd/tester/validators/cleanup_duplicate_headers.py +116 -0
  154. atdd/tester/validators/cleanup_duplicate_headers_v2.py +135 -0
  155. atdd/tester/validators/conftest.py +5 -0
  156. atdd/tester/validators/coverage_gap_report.py +321 -0
  157. atdd/tester/validators/fix_dual_ac_references.py +179 -0
  158. atdd/tester/validators/remove_duplicate_lines.py +93 -0
  159. atdd/tester/validators/test_acceptance_urn_filename_mapping.py +359 -0
  160. atdd/tester/validators/test_acceptance_urn_separator.py +166 -0
  161. atdd/tester/validators/test_artifact_naming_category.py +307 -0
  162. atdd/tester/validators/test_contract_schema_compliance.py +706 -0
  163. atdd/tester/validators/test_contracts_structure.py +200 -0
  164. atdd/tester/validators/test_coverage_adequacy.py +797 -0
  165. atdd/tester/validators/test_dual_ac_reference.py +225 -0
  166. atdd/tester/validators/test_fixture_validity.py +372 -0
  167. atdd/tester/validators/test_isolation.py +487 -0
  168. atdd/tester/validators/test_migration_coverage.py +204 -0
  169. atdd/tester/validators/test_migration_criteria.py +276 -0
  170. atdd/tester/validators/test_migration_generation.py +116 -0
  171. atdd/tester/validators/test_python_test_naming.py +410 -0
  172. atdd/tester/validators/test_red_layer_validation.py +95 -0
  173. atdd/tester/validators/test_red_python_layer_structure.py +87 -0
  174. atdd/tester/validators/test_red_supabase_layer_structure.py +90 -0
  175. atdd/tester/validators/test_telemetry_structure.py +634 -0
  176. atdd/tester/validators/test_typescript_test_naming.py +301 -0
  177. atdd/tester/validators/test_typescript_test_structure.py +84 -0
  178. atdd-0.1.0.dist-info/METADATA +191 -0
  179. atdd-0.1.0.dist-info/RECORD +183 -0
  180. atdd-0.1.0.dist-info/WHEEL +5 -0
  181. atdd-0.1.0.dist-info/entry_points.txt +2 -0
  182. atdd-0.1.0.dist-info/licenses/LICENSE +674 -0
  183. atdd-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,276 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Test migration criteria decision algorithm.
4
+
5
+ SPEC-TESTER-CONV-0034 through SPEC-TESTER-CONV-0043
6
+
7
+ Validates the contract_needs_migration() function correctly applies
8
+ all decision rules in the proper order.
9
+ """
10
+
11
+ import json
12
+ import pytest
13
+ from pathlib import Path
14
+ from tempfile import NamedTemporaryFile
15
+
16
+ # pytest pythonpath = . handles imports from repo root
17
+
18
+ from atdd.coach.commands.migration import contract_needs_migration
19
+
20
+
21
+ def create_test_contract(properties: dict, metadata: dict = None, description: str = "", aspect: str = "test") -> Path:
22
+ """Create a temporary contract file for testing."""
23
+ contract = {
24
+ "$id": f"urn:contract:test:domain:{aspect}",
25
+ "properties": properties,
26
+ "description": description
27
+ }
28
+
29
+ if metadata:
30
+ contract["x-artifact-metadata"] = metadata
31
+
32
+ # Create temp file with proper naming: {aspect}.schema.json
33
+ temp = NamedTemporaryFile(mode='w', suffix=f'{aspect}.schema.json', delete=False)
34
+ json.dump(contract, temp, indent=2)
35
+ temp.close()
36
+
37
+ return Path(temp.name)
38
+
39
+
40
+ # SPEC-TESTER-CONV-0034
41
+ def test_entity_with_id_requires_migration():
42
+ """Entity with id field requires migration."""
43
+ contract_path = create_test_contract(
44
+ properties={
45
+ "id": {"type": "string", "pattern": "^[0-9a-f]{8}"},
46
+ "player_id": {"type": "string"},
47
+ "status": {"type": "string"}
48
+ },
49
+ aspect="session"
50
+ )
51
+
52
+ try:
53
+ assert contract_needs_migration(contract_path) == True
54
+ finally:
55
+ contract_path.unlink()
56
+
57
+
58
+ # SPEC-TESTER-CONV-0035
59
+ def test_event_signals_no_migration():
60
+ """Event signals (past tense, no id) do not require migration."""
61
+ # Test multiple event patterns
62
+ event_aspects = ["exhausted", "succeeded", "failed", "registered", "terminated"]
63
+
64
+ for aspect in event_aspects:
65
+ contract_path = create_test_contract(
66
+ properties={
67
+ "timestamp": {"type": "string", "format": "date-time"},
68
+ "player_id": {"type": "string"}
69
+ },
70
+ aspect=aspect
71
+ )
72
+
73
+ try:
74
+ assert contract_needs_migration(contract_path) == False, f"{aspect} should not require migration"
75
+ finally:
76
+ contract_path.unlink()
77
+
78
+
79
+ # SPEC-TESTER-CONV-0036
80
+ def test_empty_contracts_no_migration():
81
+ """Empty contracts (pure signals) do not require migration."""
82
+ contract_path = create_test_contract(
83
+ properties={},
84
+ aspect="exhausted"
85
+ )
86
+
87
+ try:
88
+ assert contract_needs_migration(contract_path) == False
89
+ finally:
90
+ contract_path.unlink()
91
+
92
+
93
+ # SPEC-TESTER-CONV-0037
94
+ def test_value_objects_no_migration():
95
+ """Value objects without id do not require migration."""
96
+ # Test uuid value object
97
+ contract_path = create_test_contract(
98
+ properties={
99
+ "value": {"type": "string", "pattern": "^[0-9a-f]{8}"}
100
+ },
101
+ aspect="uuid"
102
+ )
103
+
104
+ try:
105
+ assert contract_needs_migration(contract_path) == False
106
+ finally:
107
+ contract_path.unlink()
108
+
109
+ # Test evaluation-score value object
110
+ contract_path = create_test_contract(
111
+ properties={
112
+ "score": {"type": "number"},
113
+ "confidence": {"type": "number"}
114
+ },
115
+ aspect="evaluation-score"
116
+ )
117
+
118
+ try:
119
+ assert contract_needs_migration(contract_path) == False
120
+ finally:
121
+ contract_path.unlink()
122
+
123
+
124
+ # SPEC-TESTER-CONV-0038
125
+ def test_internal_contracts_no_migration():
126
+ """Internal contracts (transient DTOs) do not require migration."""
127
+ contract_path = create_test_contract(
128
+ properties={
129
+ "player_id": {"type": "string"},
130
+ "data": {"type": "object"}
131
+ },
132
+ metadata={"to": "internal"},
133
+ aspect="dto"
134
+ )
135
+
136
+ try:
137
+ assert contract_needs_migration(contract_path) == False
138
+ finally:
139
+ contract_path.unlink()
140
+
141
+
142
+ # SPEC-TESTER-CONV-0039
143
+ def test_explicit_persistent_flag():
144
+ """Explicit persistence.strategy: jsonb overrides all heuristics."""
145
+ # Event pattern BUT persistence.strategy: jsonb → needs migration
146
+ contract_path = create_test_contract(
147
+ properties={
148
+ "timestamp": {"type": "string"}
149
+ },
150
+ metadata={"persistence": {"strategy": "jsonb"}},
151
+ aspect="completed" # Event pattern
152
+ )
153
+
154
+ try:
155
+ assert contract_needs_migration(contract_path) == True
156
+ finally:
157
+ contract_path.unlink()
158
+
159
+
160
+ # SPEC-TESTER-CONV-0040
161
+ def test_explicit_non_persistent_flag():
162
+ """Explicit persistence.strategy: none prevents migration."""
163
+ # Has id BUT persistence.strategy: none → no migration
164
+ contract_path = create_test_contract(
165
+ properties={
166
+ "id": {"type": "string"},
167
+ "data": {"type": "object"}
168
+ },
169
+ metadata={"persistence": {"strategy": "none"}},
170
+ aspect="entity"
171
+ )
172
+
173
+ try:
174
+ assert contract_needs_migration(contract_path) == False
175
+ finally:
176
+ contract_path.unlink()
177
+
178
+
179
+ # SPEC-TESTER-CONV-0041
180
+ def test_id_overrides_event_pattern():
181
+ """Entity with id overrides event naming pattern."""
182
+ # Aspect is "paused" (event pattern) BUT has id → needs migration
183
+ contract_path = create_test_contract(
184
+ properties={
185
+ "id": {"type": "string"},
186
+ "session_id": {"type": "string"},
187
+ "paused_at": {"type": "string", "format": "date-time"}
188
+ },
189
+ aspect="paused"
190
+ )
191
+
192
+ try:
193
+ assert contract_needs_migration(contract_path) == True
194
+ finally:
195
+ contract_path.unlink()
196
+
197
+
198
+ # SPEC-TESTER-CONV-0042
199
+ def test_computed_aggregates_no_migration():
200
+ """Computed aggregates without id do not require migration."""
201
+ computed_keywords = ["computed", "calculated", "derived", "aggregated"]
202
+
203
+ for keyword in computed_keywords:
204
+ contract_path = create_test_contract(
205
+ properties={
206
+ "total": {"type": "number"},
207
+ "count": {"type": "integer"}
208
+ },
209
+ description=f"This is a {keyword} result",
210
+ aspect="stats"
211
+ )
212
+
213
+ try:
214
+ assert contract_needs_migration(contract_path) == False, f"{keyword} should not require migration"
215
+ finally:
216
+ contract_path.unlink()
217
+
218
+
219
+ # SPEC-TESTER-CONV-0043
220
+ def test_conservative_default_external():
221
+ """Conservative default: external contracts with properties need migration."""
222
+ contract_path = create_test_contract(
223
+ properties={
224
+ "player_id": {"type": "string"},
225
+ "score": {"type": "number"}
226
+ },
227
+ metadata={"to": "external"},
228
+ aspect="profile"
229
+ )
230
+
231
+ try:
232
+ assert contract_needs_migration(contract_path) == True
233
+ finally:
234
+ contract_path.unlink()
235
+
236
+
237
+ def test_decision_algorithm_order():
238
+ """Test that decision rules are evaluated in correct order."""
239
+ # Rule 1 (explicit persistence.strategy) beats Rule 5 (has id)
240
+ contract_path = create_test_contract(
241
+ properties={"id": {"type": "string"}},
242
+ metadata={"persistence": {"strategy": "none"}},
243
+ aspect="entity"
244
+ )
245
+
246
+ try:
247
+ assert contract_needs_migration(contract_path) == False
248
+ finally:
249
+ contract_path.unlink()
250
+
251
+ # Rule 5 (has id) beats Rule 3 (event pattern)
252
+ contract_path = create_test_contract(
253
+ properties={"id": {"type": "string"}},
254
+ aspect="paused"
255
+ )
256
+
257
+ try:
258
+ assert contract_needs_migration(contract_path) == True
259
+ finally:
260
+ contract_path.unlink()
261
+
262
+ # Rule 2 (empty) beats everything except Rule 1
263
+ contract_path = create_test_contract(
264
+ properties={},
265
+ metadata={"to": "external"},
266
+ aspect="signal"
267
+ )
268
+
269
+ try:
270
+ assert contract_needs_migration(contract_path) == False
271
+ finally:
272
+ contract_path.unlink()
273
+
274
+
275
+ if __name__ == "__main__":
276
+ pytest.main([__file__, "-v"])
@@ -0,0 +1,116 @@
1
+ """
2
+ Platform tests: Migration generation from contracts.
3
+
4
+ SPEC-TESTER-CONV-0030: Generate migration template from contract schema
5
+ SPEC-TESTER-CONV-0033: Map JSON Schema types to PostgreSQL types
6
+ """
7
+ import pytest
8
+ import json
9
+ import tempfile
10
+ from pathlib import Path
11
+
12
+ # Path constants
13
+ REPO_ROOT = Path(__file__).resolve().parents[4]
14
+ CONTRACTS_DIR = REPO_ROOT / "contracts"
15
+
16
+
17
+ @pytest.mark.platform
18
+ def test_generate_migration_from_contract():
19
+ """
20
+ SPEC-TESTER-CONV-0030: Generate migration template from contract schema
21
+
22
+ Given: Contract schema at contracts/match/dilemma/current.schema.json
23
+ When: Running migration generator
24
+ Then: Migration file created with correct structure
25
+ Contains CREATE TABLE statement
26
+ Includes TODO markers for review
27
+ """
28
+ # This test will pass once the generator is implemented
29
+ # For now, it documents the expected behavior
30
+ pytest.skip("Migration generator not yet implemented - RED test placeholder")
31
+
32
+
33
+ @pytest.mark.platform
34
+ def test_table_naming_convention():
35
+ """
36
+ SPEC-TESTER-CONV-0030: Table naming follows convention
37
+
38
+ Given: Contract at contracts/{theme}/{domain}/{aspect}.schema.json
39
+ When: Generating migration
40
+ Then: Table name is {theme}_{domain}_{aspect}
41
+ Uses snake_case
42
+ Matches PostgreSQL naming rules
43
+ """
44
+ # Example test data
45
+ test_cases = [
46
+ {
47
+ "path": "contracts/match/dilemma/current.schema.json",
48
+ "expected_table": "match_dilemma_current"
49
+ },
50
+ {
51
+ "path": "contracts/commons/ux/foundations.schema.json",
52
+ "expected_table": "commons_ux_foundations"
53
+ },
54
+ {
55
+ "path": "contracts/mechanic/timebank/remaining.schema.json",
56
+ "expected_table": "mechanic_timebank_remaining"
57
+ }
58
+ ]
59
+
60
+ for case in test_cases:
61
+ path = Path(case["path"])
62
+ parts = path.parts
63
+
64
+ theme = parts[1] # contracts/{theme}/...
65
+ domain = parts[2] # contracts/{theme}/{domain}/...
66
+ aspect = path.stem.replace(".schema", "")
67
+
68
+ actual_table = f"{theme}_{domain}_{aspect}"
69
+
70
+ assert actual_table == case["expected_table"], \
71
+ f"Table name mismatch for {case['path']}: expected {case['expected_table']}, got {actual_table}"
72
+
73
+
74
+ @pytest.mark.platform
75
+ def test_json_to_postgres_type_mapping():
76
+ """
77
+ SPEC-TESTER-CONV-0033: Map JSON Schema types to PostgreSQL types
78
+
79
+ Given: Contract properties with various JSON Schema types
80
+ When: Mapping to PostgreSQL types
81
+ Then: Correct type mapping applied
82
+ string → TEXT
83
+ string with UUID pattern → UUID
84
+ string with date-time format → TIMESTAMPTZ
85
+ integer → INTEGER
86
+ number → NUMERIC
87
+ boolean → BOOLEAN
88
+ object → JSONB (with TODO)
89
+ array → JSONB (with TODO)
90
+ """
91
+ # This will be implemented by the migration generator utility
92
+ # Testing the type mapping logic
93
+
94
+ type_mappings = {
95
+ # Primitives
96
+ ("string", None, None): "TEXT",
97
+ ("string", "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", None): "UUID",
98
+ ("string", None, "date-time"): "TIMESTAMPTZ",
99
+ ("string", None, "date"): "DATE",
100
+ ("string", None, "email"): "TEXT",
101
+ ("string", None, "uri"): "TEXT",
102
+ ("integer", None, None): "INTEGER",
103
+ ("number", None, None): "NUMERIC",
104
+ ("boolean", None, None): "BOOLEAN",
105
+
106
+ # Complex (require review)
107
+ ("object", None, None): "JSONB",
108
+ ("array", None, None): "JSONB",
109
+ }
110
+
111
+ # Verify mapping exists for common types
112
+ for (json_type, pattern, format_type), expected_pg_type in type_mappings.items():
113
+ # This will call the actual mapper once implemented
114
+ # For now, just verify the expected mappings are defined
115
+ assert expected_pg_type in ["TEXT", "UUID", "TIMESTAMPTZ", "DATE", "INTEGER", "NUMERIC", "BOOLEAN", "JSONB"], \
116
+ f"Invalid PostgreSQL type: {expected_pg_type}"