superseed 0.2.0__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.
- superseed-0.2.0/.gitignore +11 -0
- superseed-0.2.0/LICENSE +21 -0
- superseed-0.2.0/PKG-INFO +460 -0
- superseed-0.2.0/README.md +437 -0
- superseed-0.2.0/pyproject.toml +70 -0
- superseed-0.2.0/src/superseed/__init__.py +21 -0
- superseed-0.2.0/src/superseed/cli.py +104 -0
- superseed-0.2.0/src/superseed/compiler.py +163 -0
- superseed-0.2.0/src/superseed/decorator.py +39 -0
- superseed-0.2.0/src/superseed/errors.py +10 -0
- superseed-0.2.0/src/superseed/plugin.py +123 -0
- superseed-0.2.0/src/superseed/py.typed +0 -0
- superseed-0.2.0/src/superseed/registry.py +229 -0
- superseed-0.2.0/src/superseed/scan/__init__.py +0 -0
- superseed-0.2.0/src/superseed/scan/cypher_hints.py +46 -0
- superseed-0.2.0/src/superseed/scan/python_repos.py +118 -0
- superseed-0.2.0/src/superseed/scan/runner.py +131 -0
- superseed-0.2.0/src/superseed/schema.py +107 -0
- superseed-0.2.0/src/superseed/seeder.py +29 -0
superseed-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SuperSeed contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
superseed-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: superseed
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Declarative Neo4j fixtures for pytest
|
|
5
|
+
Project-URL: Documentation, https://github.com/angelmota/superseed#readme
|
|
6
|
+
Project-URL: Source, https://github.com/angelmota/superseed
|
|
7
|
+
License: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Requires-Dist: neo4j>=5
|
|
11
|
+
Requires-Dist: pyyaml>=6
|
|
12
|
+
Requires-Dist: typer>=0.12
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: mypy>=1.13; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
17
|
+
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
18
|
+
Requires-Dist: testcontainers[neo4j]>=4; extra == 'dev'
|
|
19
|
+
Requires-Dist: types-pyyaml>=6; extra == 'dev'
|
|
20
|
+
Provides-Extra: neo4j
|
|
21
|
+
Requires-Dist: neo4j>=5; extra == 'neo4j'
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# SuperSeed
|
|
25
|
+
|
|
26
|
+
Declarative Neo4j fixtures for pytest. Seed **real graph data** before each test, run your services against it, then tear it down automatically.
|
|
27
|
+
|
|
28
|
+
> **This is not mocking.** SuperSeed does not patch repositories or stub the driver. It loads YAML scenarios, compiles them to Cypher, `MERGE`s nodes and relationships into Neo4j, and deletes everything tagged with a per-run ID when the test finishes.
|
|
29
|
+
|
|
30
|
+
**Requirements:** Python 3.11+, Neo4j 5+, pytest
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Table of contents
|
|
35
|
+
|
|
36
|
+
1. [How it works](#how-it-works)
|
|
37
|
+
2. [Quick start — PyPI install](#quick-start--pypi-install)
|
|
38
|
+
3. [Quick start — clone the monorepo demo](#quick-start--clone-the-monorepo-demo)
|
|
39
|
+
4. [Writing tests with `@super_seed`](#writing-tests-with-super_seed)
|
|
40
|
+
5. [`superseed.yaml` reference](#superseedyaml-reference)
|
|
41
|
+
6. [`*.schema.yaml` reference](#schemayaml-reference)
|
|
42
|
+
7. [Cleanup model (`testRunId`)](#cleanup-model-testrunid)
|
|
43
|
+
8. [CLI](#cli)
|
|
44
|
+
9. [Full walkthrough repo](#full-walkthrough-repo)
|
|
45
|
+
10. [Contributing](#contributing)
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## How it works
|
|
50
|
+
|
|
51
|
+
```mermaid
|
|
52
|
+
sequenceDiagram
|
|
53
|
+
autonumber
|
|
54
|
+
participant Dev as Your test
|
|
55
|
+
participant Plugin as SuperSeed plugin
|
|
56
|
+
participant YAML as superseed.yaml
|
|
57
|
+
participant Compiler as compiler
|
|
58
|
+
participant Neo4j as Neo4j
|
|
59
|
+
participant App as Service / repository
|
|
60
|
+
|
|
61
|
+
Dev->>Plugin: @super_seed("matrix_cast")
|
|
62
|
+
Plugin->>YAML: find config + load scenario
|
|
63
|
+
Plugin->>Compiler: compile_scenario(run_id)
|
|
64
|
+
Compiler-->>Plugin: CypherSeedPlan (MERGE statements)
|
|
65
|
+
Plugin->>Neo4j: seed — MERGE nodes & relationships
|
|
66
|
+
Note over Neo4j: every node tagged with testRunId
|
|
67
|
+
Plugin->>Dev: run test body
|
|
68
|
+
Dev->>App: call business logic
|
|
69
|
+
App->>Neo4j: real driver queries
|
|
70
|
+
Neo4j-->>App: graph results
|
|
71
|
+
App-->>Dev: assertions pass / fail
|
|
72
|
+
Plugin->>Neo4j: cleanup(testRunId)
|
|
73
|
+
Note over Neo4j: DETACH DELETE seeded nodes
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
superseed.yaml scenario
|
|
78
|
+
│
|
|
79
|
+
▼
|
|
80
|
+
compile to Cypher (MERGE nodes + relationships)
|
|
81
|
+
│
|
|
82
|
+
▼
|
|
83
|
+
seed Neo4j (tag every node with testRunId)
|
|
84
|
+
│
|
|
85
|
+
▼
|
|
86
|
+
your pytest runs (real service / repository code)
|
|
87
|
+
│
|
|
88
|
+
▼
|
|
89
|
+
cleanup (DELETE all nodes with that testRunId)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
1. You decorate a test with `@super_seed("scenario_name")`.
|
|
93
|
+
2. The pytest plugin finds `superseed.yaml` (searches upward from the test file).
|
|
94
|
+
3. It compiles the named scenario to parameterized Cypher statements.
|
|
95
|
+
4. Before the test body runs, it seeds Neo4j and tags created nodes with a unique `testRunId`.
|
|
96
|
+
5. After the test (pass or fail), it deletes every node with that `testRunId`.
|
|
97
|
+
|
|
98
|
+
Your tests talk to **real** Neo4j through your **real** application code — the same code you run in production.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Quick start — PyPI install
|
|
103
|
+
|
|
104
|
+
Use this path when SuperSeed is a dependency in your own project.
|
|
105
|
+
|
|
106
|
+
### 1. Install
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
pip install superseed
|
|
110
|
+
# or
|
|
111
|
+
uv add superseed
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
You also need a running Neo4j instance. Minimal Docker setup:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
docker run -d \
|
|
118
|
+
--name neo4j \
|
|
119
|
+
-p 7474:7474 -p 7687:7687 \
|
|
120
|
+
-e NEO4J_AUTH=neo4j/your-password \
|
|
121
|
+
neo4j:5
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 2. Add `superseed.yaml` beside your tests
|
|
125
|
+
|
|
126
|
+
```yaml
|
|
127
|
+
neo4j:
|
|
128
|
+
uri: ${NEO4J_URI:-bolt://localhost:7687}
|
|
129
|
+
user: neo4j
|
|
130
|
+
password: ${NEO4J_PASSWORD:-your-password}
|
|
131
|
+
|
|
132
|
+
defaults:
|
|
133
|
+
Movie:
|
|
134
|
+
released: 1999
|
|
135
|
+
|
|
136
|
+
scenarios:
|
|
137
|
+
matrix_cast:
|
|
138
|
+
description: "The Matrix with Neo and Trinity"
|
|
139
|
+
parameters:
|
|
140
|
+
movie_title: "The Matrix"
|
|
141
|
+
nodes:
|
|
142
|
+
- label: Movie
|
|
143
|
+
key: {title: "${movie_title}"}
|
|
144
|
+
props: {released: 1999}
|
|
145
|
+
- label: Actor
|
|
146
|
+
key: {name: "Keanu Reeves"}
|
|
147
|
+
- label: Actor
|
|
148
|
+
key: {name: "Carrie-Anne Moss"}
|
|
149
|
+
props: {born: 1967}
|
|
150
|
+
relationships:
|
|
151
|
+
- type: ACTED_IN
|
|
152
|
+
from: {label: Actor, key: {name: "Keanu Reeves"}}
|
|
153
|
+
to: {label: Movie, key: {title: "${movie_title}"}}
|
|
154
|
+
props: {role: "Neo"}
|
|
155
|
+
- type: ACTED_IN
|
|
156
|
+
from: {label: Actor, key: {name: "Carrie-Anne Moss"}}
|
|
157
|
+
to: {label: Movie, key: {title: "${movie_title}"}}
|
|
158
|
+
props: {role: "Trinity"}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Set environment variables for your Neo4j connection:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
export NEO4J_URI=bolt://localhost:7687
|
|
165
|
+
export NEO4J_PASSWORD=your-password
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 3. Write a test
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from superseed import super_seed
|
|
172
|
+
|
|
173
|
+
@super_seed("matrix_cast")
|
|
174
|
+
def test_get_cast(movie_service):
|
|
175
|
+
cast = movie_service.get_cast_for_movie("The Matrix")
|
|
176
|
+
assert len(cast) == 2
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The plugin loads automatically when pytest discovers SuperSeed (via the `pytest11` entry point). No `pytest_plugins` line required if `superseed` is installed.
|
|
180
|
+
|
|
181
|
+
### 4. Run
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
pytest tests/ -v
|
|
185
|
+
superseed validate -c tests/superseed.yaml # optional CI check
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Quick start — clone the monorepo demo
|
|
191
|
+
|
|
192
|
+
Use this path to explore the reference app and dogfood the library locally.
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
git clone <repo-url> superseed && cd superseed
|
|
196
|
+
cp .env.example .env
|
|
197
|
+
docker compose up --build -d # Neo4j + movie-api
|
|
198
|
+
|
|
199
|
+
# Package dev setup
|
|
200
|
+
uv sync --extra dev
|
|
201
|
+
|
|
202
|
+
# Run the demo integration tests (Neo4j only — movie-api container optional)
|
|
203
|
+
export NEO4J_URI=bolt://localhost:7687
|
|
204
|
+
export NEO4J_PASSWORD=superseed-dev
|
|
205
|
+
cd examples/movie-api && uv sync --extra dev && uv run pytest -v
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
| URL | What |
|
|
209
|
+
|-----|------|
|
|
210
|
+
| http://localhost:7474 | Neo4j Browser (`neo4j` / `superseed-dev`) |
|
|
211
|
+
| http://localhost:8000/docs | Movie API (FastAPI demo) |
|
|
212
|
+
| `bolt://localhost:7687` | Host pytest / CLI |
|
|
213
|
+
|
|
214
|
+
After running a seeded test you can inspect the graph in Neo4j Browser or hit the API:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
uv run pytest -k matrix -v
|
|
218
|
+
curl http://localhost:8000/movies/The%20Matrix/actors
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Writing tests with `@super_seed`
|
|
224
|
+
|
|
225
|
+
### Basic usage
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
from superseed import super_seed
|
|
229
|
+
|
|
230
|
+
@super_seed("matrix_cast")
|
|
231
|
+
def test_get_cast_for_matrix(movie_service):
|
|
232
|
+
cast = movie_service.get_cast_for_movie("The Matrix")
|
|
233
|
+
assert len(cast) == 2
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Parameter overrides
|
|
237
|
+
|
|
238
|
+
Override scenario parameters per test without duplicating YAML:
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
@super_seed("matrix_cast", movie_title="Inception")
|
|
242
|
+
def test_custom_title(movie_service):
|
|
243
|
+
...
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
In YAML, reference parameters with lowercase placeholders: `${movie_title}`.
|
|
247
|
+
|
|
248
|
+
### Config discovery
|
|
249
|
+
|
|
250
|
+
By default the plugin searches upward from the test file for `superseed.yaml`. Override with:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
pytest --superseed-config path/to/superseed.yaml
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Optional schema validation:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
pytest --superseed-schema path/to/movies.schema.yaml
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
If omitted, the plugin looks for `movies.schema.yaml` or `superseed.schema.yaml` next to the config file.
|
|
263
|
+
|
|
264
|
+
### What you should **not** do
|
|
265
|
+
|
|
266
|
+
```python
|
|
267
|
+
# Before SuperSeed — don't do this anymore
|
|
268
|
+
def test_old_way(neo4j_session, movie_service):
|
|
269
|
+
neo4j_session.run("CREATE (m:Movie ...)") # hand-written seed
|
|
270
|
+
...
|
|
271
|
+
neo4j_session.run("MATCH (n) DETACH DELETE n") # manual cleanup
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
SuperSeed replaces inline `CREATE` blocks and manual teardown.
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## `superseed.yaml` reference
|
|
279
|
+
|
|
280
|
+
Top-level keys:
|
|
281
|
+
|
|
282
|
+
| Key | Required | Description |
|
|
283
|
+
|-----|----------|-------------|
|
|
284
|
+
| `neo4j` | yes | Connection settings (`uri`, `user`, `password`) |
|
|
285
|
+
| `defaults` | no | Default properties per label, merged into every node |
|
|
286
|
+
| `scenarios` | yes | Named scenario definitions |
|
|
287
|
+
|
|
288
|
+
### Environment substitution
|
|
289
|
+
|
|
290
|
+
Use uppercase env vars in config values:
|
|
291
|
+
|
|
292
|
+
```yaml
|
|
293
|
+
neo4j:
|
|
294
|
+
uri: ${NEO4J_URI:-bolt://localhost:7687}
|
|
295
|
+
password: ${NEO4J_PASSWORD:-superseed-dev}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Syntax: `${VAR}` or `${VAR:-default}`.
|
|
299
|
+
|
|
300
|
+
### Scenario fields
|
|
301
|
+
|
|
302
|
+
| Field | Required | Description |
|
|
303
|
+
|-------|----------|-------------|
|
|
304
|
+
| `description` | no | Human-readable summary (shown by `superseed list`) |
|
|
305
|
+
| `linked_repository` | no | Dotted repo method path for future auto-detection, e.g. `MovieRepository.find_actors_for_movie` |
|
|
306
|
+
| `parameters` | no | Default parameter values; overridable via `@super_seed(..., param=value)` |
|
|
307
|
+
| `required_labels` | no | Documentation / scan metadata |
|
|
308
|
+
| `required_relationships` | no | Documentation / scan metadata |
|
|
309
|
+
| `nodes` | yes | Nodes to `MERGE` |
|
|
310
|
+
| `relationships` | no | Relationships to `MERGE` between matched nodes |
|
|
311
|
+
|
|
312
|
+
### Node entry
|
|
313
|
+
|
|
314
|
+
```yaml
|
|
315
|
+
- label: Movie
|
|
316
|
+
key: {title: "${movie_title}"} # MERGE identity — must be unique in the graph
|
|
317
|
+
props: {released: 1999} # additional SET properties
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
- **`key`** — business identity used in `MERGE (n:Label {key...})`
|
|
321
|
+
- **`props`** — extra properties merged after defaults and schema defaults
|
|
322
|
+
- **`${parameter}`** — lowercase placeholders resolved at compile time from `parameters` + decorator overrides
|
|
323
|
+
|
|
324
|
+
### Relationship entry
|
|
325
|
+
|
|
326
|
+
```yaml
|
|
327
|
+
- type: ACTED_IN
|
|
328
|
+
from: {label: Actor, key: {name: "Keanu Reeves"}}
|
|
329
|
+
to: {label: Movie, key: {title: "${movie_title}"}}
|
|
330
|
+
props: {role: "Neo"}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
The compiler `MATCH`es both endpoints by key, then `MERGE`s the relationship.
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## `*.schema.yaml` reference
|
|
338
|
+
|
|
339
|
+
Optional label contract validated before compile. Place beside `superseed.yaml` (e.g. `movies.schema.yaml`).
|
|
340
|
+
|
|
341
|
+
```yaml
|
|
342
|
+
Movie:
|
|
343
|
+
required: [title]
|
|
344
|
+
optional: [released, id]
|
|
345
|
+
defaults:
|
|
346
|
+
released: 1999
|
|
347
|
+
|
|
348
|
+
Actor:
|
|
349
|
+
required: [name]
|
|
350
|
+
optional: [born, id]
|
|
351
|
+
defaults:
|
|
352
|
+
born: 1964
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
| Key | Description |
|
|
356
|
+
|-----|-------------|
|
|
357
|
+
| `required` | Properties that must appear in `key` + `props` (after defaults) |
|
|
358
|
+
| `optional` | Allowed extra properties |
|
|
359
|
+
| `defaults` | Applied before scenario `props` |
|
|
360
|
+
|
|
361
|
+
Run `superseed validate -c tests/superseed.yaml` to catch schema violations in CI.
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## Cleanup model (`testRunId`)
|
|
366
|
+
|
|
367
|
+
Every seeded node gets a property:
|
|
368
|
+
|
|
369
|
+
```text
|
|
370
|
+
testRunId = "<uuid per test run>"
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Cleanup query (run automatically after each `@super_seed` test):
|
|
374
|
+
|
|
375
|
+
```cypher
|
|
376
|
+
MATCH (n {testRunId: $run_id}) DETACH DELETE n
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**Why this works:**
|
|
380
|
+
|
|
381
|
+
- Each test run gets a fresh UUID — no collisions between parallel or sequential tests.
|
|
382
|
+
- `DETACH DELETE` removes relationships automatically when nodes are deleted.
|
|
383
|
+
- Tests are isolated: one test's graph never leaks into the next.
|
|
384
|
+
- Failed tests still clean up (teardown runs regardless of pass/fail).
|
|
385
|
+
|
|
386
|
+
Relationships are not tagged; they disappear when their endpoint nodes are deleted.
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## CLI
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
superseed validate -c path/to/superseed.yaml # parse YAML + schema; exit 1 on error
|
|
394
|
+
superseed list -c path/to/superseed.yaml # print scenario names + descriptions
|
|
395
|
+
superseed scan path/to/repositories # suggest scenario stubs from repo Cypher
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### `superseed scan`
|
|
399
|
+
|
|
400
|
+
Walks `*repository*.py` files, extracts Cypher via AST, and writes:
|
|
401
|
+
|
|
402
|
+
```text
|
|
403
|
+
.superseed/
|
|
404
|
+
├── suggested/
|
|
405
|
+
│ ├── find_actors_for_movie.yaml # merge into superseed.yaml
|
|
406
|
+
│ └── ...
|
|
407
|
+
└── repo_index.json # machine-readable index for tooling
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Each suggested stub includes `linked_repository`, detected labels, and relationship types. Edit the stub, copy the scenario into `superseed.yaml`, and run `superseed validate`.
|
|
411
|
+
|
|
412
|
+
Example:
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
superseed scan app/repositories -o .superseed
|
|
416
|
+
superseed validate -c tests/superseed.yaml
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## Full walkthrough repo
|
|
422
|
+
|
|
423
|
+
The git monorepo includes a complete reference app that dogfoods SuperSeed:
|
|
424
|
+
|
|
425
|
+
```text
|
|
426
|
+
examples/movie-api/
|
|
427
|
+
├── app/
|
|
428
|
+
│ ├── repositories/movie_repository.py # real Cypher strings (scan targets these)
|
|
429
|
+
│ ├── services/movie_service.py
|
|
430
|
+
│ └── main.py # FastAPI demo
|
|
431
|
+
└── tests/
|
|
432
|
+
├── superseed.yaml # scenarios
|
|
433
|
+
├── movies.schema.yaml # label contract
|
|
434
|
+
└── test_movie_service.py # @super_seed tests — no inline CREATE
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
See [`examples/movie-api/tests/test_movie_service.py`](examples/movie-api/tests/test_movie_service.py) and [`examples/movie-api/tests/superseed.yaml`](examples/movie-api/tests/superseed.yaml) for a working end-to-end example.
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## Contributing
|
|
442
|
+
|
|
443
|
+
SuperSeed is developed in the open. Planning docs:
|
|
444
|
+
|
|
445
|
+
- [Future work (v0.3+ auto-detection)](docs/FUTURE.md)
|
|
446
|
+
|
|
447
|
+
Local development:
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
uv sync --extra dev
|
|
451
|
+
uv run pytest tests/ -v
|
|
452
|
+
uv run ruff check src tests
|
|
453
|
+
uv run mypy src
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
## License
|
|
459
|
+
|
|
460
|
+
MIT — see [LICENSE](LICENSE).
|