speculast 1.0.1__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.
Files changed (44) hide show
  1. speculast-1.0.1/.gitignore +8 -0
  2. speculast-1.0.1/PKG-INFO +292 -0
  3. speculast-1.0.1/README.md +264 -0
  4. speculast-1.0.1/demo_shop/__init__.py +22 -0
  5. speculast-1.0.1/demo_shop/database.py +89 -0
  6. speculast-1.0.1/demo_shop/logic.py +50 -0
  7. speculast-1.0.1/demo_shop/models.py +94 -0
  8. speculast-1.0.1/demo_shop/service.py +88 -0
  9. speculast-1.0.1/engine/__init__.py +64 -0
  10. speculast-1.0.1/engine/analyzer/__init__.py +12 -0
  11. speculast-1.0.1/engine/analyzer/engine.py +414 -0
  12. speculast-1.0.1/engine/analyzer/visitors.py +304 -0
  13. speculast-1.0.1/engine/core/__init__.py +47 -0
  14. speculast-1.0.1/engine/core/i18n.py +635 -0
  15. speculast-1.0.1/engine/core/models.py +225 -0
  16. speculast-1.0.1/engine/core/protocols.py +71 -0
  17. speculast-1.0.1/engine/generator/__init__.py +5 -0
  18. speculast-1.0.1/engine/generator/engine.py +267 -0
  19. speculast-1.0.1/engine/generator/templates/__init__.py +1 -0
  20. speculast-1.0.1/engine/generator/templates/integration_service_template.jinja2 +77 -0
  21. speculast-1.0.1/engine/generator/templates/pytest_template.jinja2 +690 -0
  22. speculast-1.0.1/engine/generator/templates/tests_conftest_template.jinja2 +75 -0
  23. speculast-1.0.1/engine/generator/templates/tests_pyproject_template.jinja2 +7 -0
  24. speculast-1.0.1/engine/generator/templates/unit_logic_template.jinja2 +45 -0
  25. speculast-1.0.1/engine/infra/__init__.py +6 -0
  26. speculast-1.0.1/engine/infra/manager.py +111 -0
  27. speculast-1.0.1/engine/infra/registry.py +52 -0
  28. speculast-1.0.1/engine/visualizer/__init__.py +6 -0
  29. speculast-1.0.1/engine/visualizer/dashboard.py +260 -0
  30. speculast-1.0.1/engine/visualizer/renderer.py +1373 -0
  31. speculast-1.0.1/engine/visualizer/templates/__init__.py +1 -0
  32. speculast-1.0.1/engine/visualizer/templates/report.html +2689 -0
  33. speculast-1.0.1/main.py +1107 -0
  34. speculast-1.0.1/pyproject.toml +81 -0
  35. speculast-1.0.1/tests/__init__.py +0 -0
  36. speculast-1.0.1/tests/conftest.py +75 -0
  37. speculast-1.0.1/tests/integration/__init__.py +0 -0
  38. speculast-1.0.1/tests/integration/test_service.py +72 -0
  39. speculast-1.0.1/tests/pyproject.toml +7 -0
  40. speculast-1.0.1/tests/unit/__init__.py +0 -0
  41. speculast-1.0.1/tests/unit/test_cli_defaults.py +76 -0
  42. speculast-1.0.1/tests/unit/test_dashboard_builder.py +10 -0
  43. speculast-1.0.1/tests/unit/test_logic.py +37 -0
  44. speculast-1.0.1/tests/unit/test_reporting_storage.py +177 -0
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ .pytest_cache/
3
+ .coverage
4
+ .speculast/
5
+ .browser-check-profile*
6
+ .tmp_chrome_report_test/
7
+ *.tmp*
8
+
@@ -0,0 +1,292 @@
1
+ Metadata-Version: 2.4
2
+ Name: speculast
3
+ Version: 1.0.1
4
+ Summary: Semantic-first framework for Python architecture analysis, automated test generation, and infrastructure-aware reporting.
5
+ Author: speculast team
6
+ License: Proprietary
7
+ Keywords: architecture-analysis,ast,automation,docker,pytest,python,reporting,semantic-analysis,testing
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Classifier: Programming Language :: Python :: 3.14
12
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
13
+ Classifier: Topic :: Software Development :: Testing
14
+ Requires-Python: >=3.12
15
+ Requires-Dist: asyncpg<1.0.0,>=0.29.0
16
+ Requires-Dist: jinja2<4.0.0,>=3.1.4
17
+ Requires-Dist: pydantic<3.0.0,>=2.7.0
18
+ Requires-Dist: pytest-asyncio<1.0.0,>=0.23.0
19
+ Requires-Dist: pytest-cov<7.0.0,>=5.0.0
20
+ Requires-Dist: pytest-json-report<2.0.0,>=1.5.0
21
+ Requires-Dist: pytest<9.0.0,>=8.2.0
22
+ Requires-Dist: rich<15.0.0,>=13.7.0
23
+ Requires-Dist: sqlalchemy<3.0.0,>=2.0.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: ruff<1.0.0,>=0.5.0; extra == 'dev'
26
+ Requires-Dist: testcontainers[postgresql,redis]<5.0.0,>=4.8.0; extra == 'dev'
27
+ Description-Content-Type: text/markdown
28
+
29
+ # speculast
30
+
31
+ `speculast` is an installable Python framework for semantic code analysis,
32
+ automatic pytest generation, infrastructure detection, and visual reporting.
33
+
34
+ speculast parses Python projects through AST analysis, tracks semantic changes,
35
+ detects external dependencies, prepares test suites, can orchestrate Docker-backed
36
+ integration runs, and produces a self-contained HTML report.
37
+
38
+ ## What speculast Does
39
+
40
+ - analyzes Python modules and projects at the semantic level;
41
+ - extracts functions, methods, imports, and external calls;
42
+ - detects infrastructure requirements such as PostgreSQL and Redis;
43
+ - generates production-style pytest suites;
44
+ - runs a full cycle with Docker, pytest, coverage, and JSON reports;
45
+ - builds timestamped HTML dashboards grouped by day under `reports/`.
46
+
47
+ ## Installation
48
+
49
+ Use Python `3.12+`. The project is packaged through `pyproject.toml`.
50
+
51
+ ```bash
52
+ python -m venv .venv
53
+ ```
54
+
55
+ Windows:
56
+
57
+ ```bash
58
+ .venv\Scripts\activate
59
+ python -m pip install --upgrade pip
60
+ python -m pip install .
61
+ ```
62
+
63
+ Linux / macOS:
64
+
65
+ ```bash
66
+ source .venv/bin/activate
67
+ python -m pip install --upgrade pip
68
+ python -m pip install .
69
+ ```
70
+
71
+ After installation, the CLI command `speculast` becomes available in the active environment.
72
+
73
+ ## CLI Quick Reference
74
+
75
+ ```bash
76
+ speculast [PATH] [--no-tests] [--no-viz] [--no-real-db] [--no-cleanup] [--lang ru|en] [--cleanup-reports] [--report-retention-days N]
77
+ ```
78
+
79
+ Examples:
80
+
81
+ ```bash
82
+ speculast
83
+ speculast path/to/module.py
84
+ speculast . --cleanup-reports --report-retention-days 7
85
+ speculast . --no-tests
86
+ speculast . --no-viz --lang en
87
+ ```
88
+
89
+ By default, `speculast` runs in Full Auto mode:
90
+
91
+ - uses the current directory when `PATH` is omitted;
92
+ - cleans temporary root-level technical noise automatically;
93
+ - enables real DB mode by default;
94
+ - runs generated tests;
95
+ - builds the HTML report;
96
+ - opens the fresh report in the browser automatically.
97
+
98
+ `--cleanup-reports` additionally prunes aged HTML reports from `reports/`.
99
+
100
+ The default full cycle performs:
101
+
102
+ 1. semantic analysis;
103
+ 2. test generation;
104
+ 3. infrastructure planning;
105
+ 4. `docker compose up -d` when services are required;
106
+ 5. pytest execution with coverage and JSON reports;
107
+ 6. HTML report generation;
108
+ 7. `docker compose down --remove-orphans`.
109
+
110
+ ## Template and Report Portability
111
+
112
+ speculast resolves Jinja and HTML templates through `importlib.resources`.
113
+ This means the package can locate bundled templates correctly after `pip install .`,
114
+ even when `speculast` is started from an unrelated working directory.
115
+
116
+ Bundled resources include:
117
+
118
+ - `engine/generator/templates/*.jinja2`
119
+ - `engine/visualizer/templates/report.html`
120
+
121
+ ## Scenario A: Quick Start on Any OS
122
+
123
+ Install the package and run the full autonomous cycle against the current project:
124
+
125
+ ```bash
126
+ python -m pip install .
127
+ speculast
128
+ ```
129
+
130
+ Expected outputs:
131
+
132
+ - generated test suite under `tests/`;
133
+ - `docker-compose.yaml` when infrastructure is required;
134
+ - date-grouped reports such as `reports/2026-05-14/report_131530.html`.
135
+
136
+ If you also want to keep raw pytest and coverage JSON artifacts:
137
+
138
+ ```bash
139
+ speculast . --no-cleanup
140
+ ```
141
+
142
+ If you only need the report without pytest execution:
143
+
144
+ ```bash
145
+ speculast . --no-tests
146
+ ```
147
+
148
+ ## Scenario B: Windows + Docker Desktop
149
+
150
+ Recommended stack:
151
+
152
+ - Python `3.12+`
153
+ - Docker Desktop
154
+ - PowerShell or Windows Terminal
155
+
156
+ Typical workflow:
157
+
158
+ ```powershell
159
+ .venv\Scripts\activate
160
+ python -m pip install .
161
+ speculast
162
+ ```
163
+
164
+ Notes for Windows:
165
+
166
+ - `speculast` can start and stop Docker-backed infrastructure for integration scenarios.
167
+ - If PostgreSQL-backed tests are generated, make sure Docker Desktop is installed and available in PATH.
168
+ - speculast writes final diagnostics into timestamped files inside `reports/`, while terminal output stays compact.
169
+
170
+ ## Scenario C: Linux / macOS Server Mode
171
+
172
+ speculast does not require a desktop UI. The report is generated as a plain HTML file
173
+ that can be opened locally or archived as a build artifact.
174
+
175
+ Typical workflow:
176
+
177
+ ```bash
178
+ source .venv/bin/activate
179
+ python -m pip install .
180
+ speculast /opt/projects/my-service --lang en
181
+ ```
182
+
183
+ Recommended environment preparation:
184
+
185
+ - install Docker Engine or Docker Desktop for container-backed tests;
186
+ - ensure the current user can run `docker compose`;
187
+ - run inside a CI worker, VM, or server shell without GUI if needed.
188
+
189
+ For analysis-only mode on a server:
190
+
191
+ ```bash
192
+ speculast /opt/projects/my-service --no-tests --no-viz
193
+ ```
194
+
195
+ ## Scenario D: CI/CD Integration
196
+
197
+ The CLI is designed to be called directly from automated validation pipelines.
198
+
199
+ Minimal pipeline step:
200
+
201
+ ```bash
202
+ python -m pip install .
203
+ speculast . --lang en
204
+ ```
205
+
206
+ Recommended artifacts to publish from the job:
207
+
208
+ - `reports/`
209
+ - generated `tests/` directory if you want to inspect emitted suites
210
+
211
+ If the pipeline should also retain raw pytest and coverage JSON files, run with `--no-cleanup` and publish:
212
+
213
+ - `.speculast/pytest-report.json`
214
+ - `.speculast/coverage.json`
215
+
216
+ Example GitHub Actions step:
217
+
218
+ ```yaml
219
+ - name: Install framework
220
+ run: python -m pip install .
221
+
222
+ - name: Run semantic analysis and tests
223
+ run: speculast . --lang en
224
+ ```
225
+
226
+ Example GitLab CI job:
227
+
228
+ ```yaml
229
+ speculast:
230
+ stage: test
231
+ script:
232
+ - python -m pip install .
233
+ - speculast . --lang en
234
+ artifacts:
235
+ when: always
236
+ paths:
237
+ - reports/
238
+ ```
239
+
240
+ ## Project Layout
241
+
242
+ ```text
243
+ engine/
244
+ analyzer/ Semantic analysis and AST visitors
245
+ core/ Pydantic models, i18n, protocols
246
+ generator/ Jinja-based pytest generation templates and engine
247
+ infra/ Infrastructure detection and Docker Compose helpers
248
+ visualizer/ HTML dashboard rendering
249
+ main.py CLI entry module
250
+ pyproject.toml Packaging metadata and script entry points
251
+ ```
252
+
253
+ ## Installed CLI Entry Point
254
+
255
+ The package registers:
256
+
257
+ ```toml
258
+ [project.scripts]
259
+ speculast = "main:main"
260
+ ```
261
+
262
+ So after installation, `speculast` runs the same autonomous CLI cycle exposed by `main.py`.
263
+
264
+ ## Output Artifacts
265
+
266
+ Depending on the mode, the framework can generate:
267
+
268
+ - `tests/`
269
+ - `docker-compose.yaml`
270
+ - `reports/`
271
+
272
+ Temporary technical artifacts such as `.speculast/`, cache folders, and browser-check profiles are removed automatically unless `--no-cleanup` is used.
273
+
274
+ ## Development Notes
275
+
276
+ Runtime dependencies are declared in `pyproject.toml` and include:
277
+
278
+ - `pydantic`
279
+ - `jinja2`
280
+ - `sqlalchemy`
281
+ - `asyncpg`
282
+ - `pytest`
283
+ - `pytest-asyncio`
284
+ - `pytest-cov`
285
+ - `pytest-json-report`
286
+ - `rich`
287
+
288
+ Optional development extras:
289
+
290
+ ```bash
291
+ python -m pip install .[dev]
292
+ ```
@@ -0,0 +1,264 @@
1
+ # speculast
2
+
3
+ `speculast` is an installable Python framework for semantic code analysis,
4
+ automatic pytest generation, infrastructure detection, and visual reporting.
5
+
6
+ speculast parses Python projects through AST analysis, tracks semantic changes,
7
+ detects external dependencies, prepares test suites, can orchestrate Docker-backed
8
+ integration runs, and produces a self-contained HTML report.
9
+
10
+ ## What speculast Does
11
+
12
+ - analyzes Python modules and projects at the semantic level;
13
+ - extracts functions, methods, imports, and external calls;
14
+ - detects infrastructure requirements such as PostgreSQL and Redis;
15
+ - generates production-style pytest suites;
16
+ - runs a full cycle with Docker, pytest, coverage, and JSON reports;
17
+ - builds timestamped HTML dashboards grouped by day under `reports/`.
18
+
19
+ ## Installation
20
+
21
+ Use Python `3.12+`. The project is packaged through `pyproject.toml`.
22
+
23
+ ```bash
24
+ python -m venv .venv
25
+ ```
26
+
27
+ Windows:
28
+
29
+ ```bash
30
+ .venv\Scripts\activate
31
+ python -m pip install --upgrade pip
32
+ python -m pip install .
33
+ ```
34
+
35
+ Linux / macOS:
36
+
37
+ ```bash
38
+ source .venv/bin/activate
39
+ python -m pip install --upgrade pip
40
+ python -m pip install .
41
+ ```
42
+
43
+ After installation, the CLI command `speculast` becomes available in the active environment.
44
+
45
+ ## CLI Quick Reference
46
+
47
+ ```bash
48
+ speculast [PATH] [--no-tests] [--no-viz] [--no-real-db] [--no-cleanup] [--lang ru|en] [--cleanup-reports] [--report-retention-days N]
49
+ ```
50
+
51
+ Examples:
52
+
53
+ ```bash
54
+ speculast
55
+ speculast path/to/module.py
56
+ speculast . --cleanup-reports --report-retention-days 7
57
+ speculast . --no-tests
58
+ speculast . --no-viz --lang en
59
+ ```
60
+
61
+ By default, `speculast` runs in Full Auto mode:
62
+
63
+ - uses the current directory when `PATH` is omitted;
64
+ - cleans temporary root-level technical noise automatically;
65
+ - enables real DB mode by default;
66
+ - runs generated tests;
67
+ - builds the HTML report;
68
+ - opens the fresh report in the browser automatically.
69
+
70
+ `--cleanup-reports` additionally prunes aged HTML reports from `reports/`.
71
+
72
+ The default full cycle performs:
73
+
74
+ 1. semantic analysis;
75
+ 2. test generation;
76
+ 3. infrastructure planning;
77
+ 4. `docker compose up -d` when services are required;
78
+ 5. pytest execution with coverage and JSON reports;
79
+ 6. HTML report generation;
80
+ 7. `docker compose down --remove-orphans`.
81
+
82
+ ## Template and Report Portability
83
+
84
+ speculast resolves Jinja and HTML templates through `importlib.resources`.
85
+ This means the package can locate bundled templates correctly after `pip install .`,
86
+ even when `speculast` is started from an unrelated working directory.
87
+
88
+ Bundled resources include:
89
+
90
+ - `engine/generator/templates/*.jinja2`
91
+ - `engine/visualizer/templates/report.html`
92
+
93
+ ## Scenario A: Quick Start on Any OS
94
+
95
+ Install the package and run the full autonomous cycle against the current project:
96
+
97
+ ```bash
98
+ python -m pip install .
99
+ speculast
100
+ ```
101
+
102
+ Expected outputs:
103
+
104
+ - generated test suite under `tests/`;
105
+ - `docker-compose.yaml` when infrastructure is required;
106
+ - date-grouped reports such as `reports/2026-05-14/report_131530.html`.
107
+
108
+ If you also want to keep raw pytest and coverage JSON artifacts:
109
+
110
+ ```bash
111
+ speculast . --no-cleanup
112
+ ```
113
+
114
+ If you only need the report without pytest execution:
115
+
116
+ ```bash
117
+ speculast . --no-tests
118
+ ```
119
+
120
+ ## Scenario B: Windows + Docker Desktop
121
+
122
+ Recommended stack:
123
+
124
+ - Python `3.12+`
125
+ - Docker Desktop
126
+ - PowerShell or Windows Terminal
127
+
128
+ Typical workflow:
129
+
130
+ ```powershell
131
+ .venv\Scripts\activate
132
+ python -m pip install .
133
+ speculast
134
+ ```
135
+
136
+ Notes for Windows:
137
+
138
+ - `speculast` can start and stop Docker-backed infrastructure for integration scenarios.
139
+ - If PostgreSQL-backed tests are generated, make sure Docker Desktop is installed and available in PATH.
140
+ - speculast writes final diagnostics into timestamped files inside `reports/`, while terminal output stays compact.
141
+
142
+ ## Scenario C: Linux / macOS Server Mode
143
+
144
+ speculast does not require a desktop UI. The report is generated as a plain HTML file
145
+ that can be opened locally or archived as a build artifact.
146
+
147
+ Typical workflow:
148
+
149
+ ```bash
150
+ source .venv/bin/activate
151
+ python -m pip install .
152
+ speculast /opt/projects/my-service --lang en
153
+ ```
154
+
155
+ Recommended environment preparation:
156
+
157
+ - install Docker Engine or Docker Desktop for container-backed tests;
158
+ - ensure the current user can run `docker compose`;
159
+ - run inside a CI worker, VM, or server shell without GUI if needed.
160
+
161
+ For analysis-only mode on a server:
162
+
163
+ ```bash
164
+ speculast /opt/projects/my-service --no-tests --no-viz
165
+ ```
166
+
167
+ ## Scenario D: CI/CD Integration
168
+
169
+ The CLI is designed to be called directly from automated validation pipelines.
170
+
171
+ Minimal pipeline step:
172
+
173
+ ```bash
174
+ python -m pip install .
175
+ speculast . --lang en
176
+ ```
177
+
178
+ Recommended artifacts to publish from the job:
179
+
180
+ - `reports/`
181
+ - generated `tests/` directory if you want to inspect emitted suites
182
+
183
+ If the pipeline should also retain raw pytest and coverage JSON files, run with `--no-cleanup` and publish:
184
+
185
+ - `.speculast/pytest-report.json`
186
+ - `.speculast/coverage.json`
187
+
188
+ Example GitHub Actions step:
189
+
190
+ ```yaml
191
+ - name: Install framework
192
+ run: python -m pip install .
193
+
194
+ - name: Run semantic analysis and tests
195
+ run: speculast . --lang en
196
+ ```
197
+
198
+ Example GitLab CI job:
199
+
200
+ ```yaml
201
+ speculast:
202
+ stage: test
203
+ script:
204
+ - python -m pip install .
205
+ - speculast . --lang en
206
+ artifacts:
207
+ when: always
208
+ paths:
209
+ - reports/
210
+ ```
211
+
212
+ ## Project Layout
213
+
214
+ ```text
215
+ engine/
216
+ analyzer/ Semantic analysis and AST visitors
217
+ core/ Pydantic models, i18n, protocols
218
+ generator/ Jinja-based pytest generation templates and engine
219
+ infra/ Infrastructure detection and Docker Compose helpers
220
+ visualizer/ HTML dashboard rendering
221
+ main.py CLI entry module
222
+ pyproject.toml Packaging metadata and script entry points
223
+ ```
224
+
225
+ ## Installed CLI Entry Point
226
+
227
+ The package registers:
228
+
229
+ ```toml
230
+ [project.scripts]
231
+ speculast = "main:main"
232
+ ```
233
+
234
+ So after installation, `speculast` runs the same autonomous CLI cycle exposed by `main.py`.
235
+
236
+ ## Output Artifacts
237
+
238
+ Depending on the mode, the framework can generate:
239
+
240
+ - `tests/`
241
+ - `docker-compose.yaml`
242
+ - `reports/`
243
+
244
+ Temporary technical artifacts such as `.speculast/`, cache folders, and browser-check profiles are removed automatically unless `--no-cleanup` is used.
245
+
246
+ ## Development Notes
247
+
248
+ Runtime dependencies are declared in `pyproject.toml` and include:
249
+
250
+ - `pydantic`
251
+ - `jinja2`
252
+ - `sqlalchemy`
253
+ - `asyncpg`
254
+ - `pytest`
255
+ - `pytest-asyncio`
256
+ - `pytest-cov`
257
+ - `pytest-json-report`
258
+ - `rich`
259
+
260
+ Optional development extras:
261
+
262
+ ```bash
263
+ python -m pip install .[dev]
264
+ ```
@@ -0,0 +1,22 @@
1
+ """Demo e-commerce application used by speculast."""
2
+
3
+ from .logic import (
4
+ calculate_discount,
5
+ calculate_line_total,
6
+ calculate_order_total,
7
+ validate_stock,
8
+ )
9
+ from .models import OrderCreateRequest, OrderItemRequest, OrderResult, Product
10
+ from .service import create_order
11
+
12
+ __all__ = [
13
+ "OrderCreateRequest",
14
+ "OrderItemRequest",
15
+ "OrderResult",
16
+ "Product",
17
+ "calculate_discount",
18
+ "calculate_line_total",
19
+ "calculate_order_total",
20
+ "create_order",
21
+ "validate_stock",
22
+ ]
@@ -0,0 +1,89 @@
1
+ """SQLAlchemy models and helpers for the demo shop."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from decimal import Decimal
7
+
8
+ from sqlalchemy import ForeignKey, Numeric, String
9
+ from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker, create_async_engine
10
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
11
+
12
+
13
+ DEFAULT_DATABASE_URL = "postgresql+asyncpg://app:app@127.0.0.1:55432/app"
14
+
15
+
16
+ class Base(DeclarativeBase):
17
+ """Shared SQLAlchemy declarative base."""
18
+
19
+
20
+ class ProductRecord(Base):
21
+ """Catalog inventory row."""
22
+
23
+ __tablename__ = "shop_products"
24
+
25
+ id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
26
+ sku: Mapped[str] = mapped_column(String(64), unique=True, index=True)
27
+ title: Mapped[str] = mapped_column(String(255))
28
+ unit_price: Mapped[Decimal] = mapped_column(Numeric(10, 2))
29
+ stock: Mapped[int] = mapped_column()
30
+
31
+
32
+ class OrderRecord(Base):
33
+ """Order header row."""
34
+
35
+ __tablename__ = "shop_orders"
36
+
37
+ id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
38
+ customer_email: Mapped[str] = mapped_column(String(255), index=True)
39
+ promo_code: Mapped[str | None] = mapped_column(String(64), nullable=True)
40
+ subtotal: Mapped[Decimal] = mapped_column(Numeric(10, 2))
41
+ discount_total: Mapped[Decimal] = mapped_column(Numeric(10, 2))
42
+ total: Mapped[Decimal] = mapped_column(Numeric(10, 2))
43
+
44
+ items: Mapped[list["OrderItemRecord"]] = relationship(
45
+ back_populates="order",
46
+ cascade="all, delete-orphan",
47
+ )
48
+
49
+
50
+ class OrderItemRecord(Base):
51
+ """Order line row."""
52
+
53
+ __tablename__ = "shop_order_items"
54
+
55
+ id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
56
+ order_id: Mapped[int] = mapped_column(ForeignKey("shop_orders.id", ondelete="CASCADE"))
57
+ product_id: Mapped[int] = mapped_column(ForeignKey("shop_products.id"))
58
+ sku: Mapped[str] = mapped_column(String(64))
59
+ quantity: Mapped[int] = mapped_column()
60
+ unit_price: Mapped[Decimal] = mapped_column(Numeric(10, 2))
61
+ line_total: Mapped[Decimal] = mapped_column(Numeric(10, 2))
62
+
63
+ order: Mapped[OrderRecord] = relationship(back_populates="items")
64
+
65
+
66
+ def resolve_database_url() -> str:
67
+ """Return the connection string used by integration tests and services."""
68
+
69
+ return os.getenv("DEMO_SHOP_DATABASE_URL", DEFAULT_DATABASE_URL)
70
+
71
+
72
+ def create_engine(database_url: str | None = None) -> AsyncEngine:
73
+ """Build an async SQLAlchemy engine."""
74
+
75
+ return create_async_engine(
76
+ database_url or resolve_database_url(),
77
+ future=True,
78
+ connect_args={"ssl": False},
79
+ )
80
+
81
+
82
+ def create_session_factory(database_url: str | None = None) -> async_sessionmaker[AsyncSession]:
83
+ """Build a reusable async session factory."""
84
+
85
+ return async_sessionmaker(
86
+ create_engine(database_url),
87
+ class_=AsyncSession,
88
+ expire_on_commit=False,
89
+ )