sonamu 0.10.7 → 0.10.9
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.
- package/dist/api/decorators.d.ts.map +1 -1
- package/dist/api/decorators.js +5 -12
- package/dist/bin/cli.js +10 -253
- package/dist/database/base-model.d.ts +1 -0
- package/dist/database/base-model.d.ts.map +1 -1
- package/dist/database/base-model.js +14 -8
- package/dist/database/db.d.ts +2 -1
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/db.js +13 -3
- package/dist/database/puri-wrapper.d.ts.map +1 -1
- package/dist/database/puri-wrapper.js +4 -11
- package/dist/database/transaction-context.d.ts +8 -3
- package/dist/database/transaction-context.d.ts.map +1 -1
- package/dist/database/transaction-context.js +12 -7
- package/dist/migration/code-generation.js +2 -2
- package/dist/naite/naite-reporter.d.ts +8 -3
- package/dist/naite/naite-reporter.d.ts.map +1 -1
- package/dist/naite/naite-reporter.js +9 -4
- package/dist/ui-web/assets/{index-D0MHYbxl.js → index-CJf8uJYf.js} +47 -41
- package/dist/ui-web/assets/index-GMMIVGja.css +1 -0
- package/dist/ui-web/index.html +2 -2
- package/package.json +3 -3
- package/src/api/decorators.ts +11 -22
- package/src/bin/cli.ts +12 -375
- package/src/database/__tests__/transaction-scope.test.ts +227 -0
- package/src/database/base-model.ts +31 -6
- package/src/database/db.ts +17 -2
- package/src/database/puri-wrapper.ts +5 -22
- package/src/database/transaction-context.ts +14 -6
- package/src/migration/code-generation.ts +1 -1
- package/src/naite/naite-reporter.ts +8 -3
- package/dist/ui-web/assets/index-Dx_JX4aQ.css +0 -1
- package/src/skills/AGENTS.md +0 -108
- package/src/skills/sonamu/SKILL.md +0 -75
- package/src/skills/sonamu-ai-agents/SKILL.md +0 -205
- package/src/skills/sonamu-api/SKILL.md +0 -480
- package/src/skills/sonamu-auth/SKILL.md +0 -327
- package/src/skills/sonamu-auth/references/plugins.md +0 -310
- package/src/skills/sonamu-auth/references/user-id-migration-followups.md +0 -192
- package/src/skills/sonamu-auth/references/user-id-migration.md +0 -455
- package/src/skills/sonamu-config/SKILL.md +0 -203
- package/src/skills/sonamu-config/references/database.md +0 -452
- package/src/skills/sonamu-config/references/environments.md +0 -178
- package/src/skills/sonamu-config/references/server-options.md +0 -400
- package/src/skills/sonamu-entity/SKILL.md +0 -180
- package/src/skills/sonamu-entity/references/creation-workflow.md +0 -581
- package/src/skills/sonamu-entity/references/design-guides.md +0 -243
- package/src/skills/sonamu-entity/references/field-types.md +0 -170
- package/src/skills/sonamu-entity/references/relations-detail.md +0 -245
- package/src/skills/sonamu-entity/references/relations.md +0 -463
- package/src/skills/sonamu-entity/references/subset.md +0 -156
- package/src/skills/sonamu-fixture/SKILL.md +0 -180
- package/src/skills/sonamu-fixture/references/cli-usage.md +0 -439
- package/src/skills/sonamu-fixture/references/cone.md +0 -298
- package/src/skills/sonamu-frontend/SKILL.md +0 -142
- package/src/skills/sonamu-frontend/references/components.md +0 -323
- package/src/skills/sonamu-frontend/references/examples.md +0 -64
- package/src/skills/sonamu-frontend/references/hooks.md +0 -273
- package/src/skills/sonamu-frontend/references/runtime.md +0 -165
- package/src/skills/sonamu-frontend/references/scaffolding.md +0 -439
- package/src/skills/sonamu-i18n/SKILL.md +0 -287
- package/src/skills/sonamu-migration/SKILL.md +0 -316
- package/src/skills/sonamu-naite/SKILL.md +0 -266
- package/src/skills/sonamu-query/SKILL.md +0 -48
- package/src/skills/sonamu-query/references/model-patterns.md +0 -390
- package/src/skills/sonamu-query/references/model.md +0 -366
- package/src/skills/sonamu-query/references/puri.md +0 -413
- package/src/skills/sonamu-query/references/search.md +0 -238
- package/src/skills/sonamu-query/references/upsert.md +0 -324
- package/src/skills/sonamu-tasks/SKILL.md +0 -236
- package/src/skills/sonamu-testing/SKILL.md +0 -251
- package/src/skills/sonamu-testing/references/devrunner.md +0 -405
- package/src/skills/sonamu-testing/references/helpers.md +0 -185
- package/src/skills/sonamu-testing/references/patterns.md +0 -263
- package/src/skills/sonamu-testing/references/pitfalls.md +0 -588
- package/src/skills/sonamu-testing/references/quick-start.md +0 -285
- package/src/skills/sonamu-testing/references/type-safety.md +0 -172
- package/src/skills/sonamu-testing/references/writing-plan.md +0 -375
- package/src/skills/sonamu-vector/SKILL.md +0 -222
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sonamu-testing
|
|
3
|
-
description: Writes and runs Sonamu Vitest tests. Use when authoring a Model or API test, asserting on query behaviour, mocking a dependency, or when sonamu test fails to run. Covers bootstrap, test and testAs, createFixtureLoader, Naite.get assertions, expectQuery, expectUB, DevRunner, HMR integration, and parallel workers.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Sonamu Test System
|
|
7
|
-
|
|
8
|
-
Sonamu provides a Vitest-based test environment. Each test is isolated in a transaction and automatically rolled back.
|
|
9
|
-
|
|
10
|
-
**Example project**: `sonamu/examples/miomock` — reference for real test code
|
|
11
|
-
|
|
12
|
-
**WARNING: Projects with 10 or more entities must use a batch strategy** — see `references/writing-plan.md`.
|
|
13
|
-
|
|
14
|
-
## Reference Map
|
|
15
|
-
|
|
16
|
-
| Need | Read |
|
|
17
|
-
| --- | --- |
|
|
18
|
-
| First test in a new project, end-to-end | `references/quick-start.md` |
|
|
19
|
-
| Planning what to test, order, batching for 10+ entities | `references/writing-plan.md` |
|
|
20
|
-
| bootstrap / test / testAs, CRUD, mocks, file structure, enum values | `references/patterns.md` |
|
|
21
|
-
| expectQuery, expectUB, Naite assertions, createFixtureLoader | `references/helpers.md` |
|
|
22
|
-
| Type errors in test code, nullable handling, partial/extend | `references/type-safety.md` |
|
|
23
|
-
| Failing for a non-obvious reason, complex entity graphs | `references/pitfalls.md` |
|
|
24
|
-
| `sonamu test` itself fails, HMR, parallel workers, vitest/sonamu config | `references/devrunner.md` |
|
|
25
|
-
|
|
26
|
-
Fixture generation via CLI (`fixture gen/fetch/explore`) and the 3-Tier DB structure live in the
|
|
27
|
-
`sonamu-fixture` skill, not here.
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## Running Tests
|
|
32
|
-
|
|
33
|
-
**Use `pnpm sonamu test` during development.** It reuses a Vitest instance living inside the
|
|
34
|
-
`sonamu dev` process, so it is roughly 3.2x faster than a cold start and picks up source changes
|
|
35
|
-
through HMR. Assume the dev server is running; start it first if it is down. `pnpm test` is for CI.
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
# Start dev server if it's down
|
|
39
|
-
pnpm sonamu dev
|
|
40
|
-
|
|
41
|
-
# Tests during development (default)
|
|
42
|
-
pnpm sonamu test
|
|
43
|
-
pnpm sonamu test user.model
|
|
44
|
-
pnpm sonamu test user.model -p "findMany" # filter by test name
|
|
45
|
-
pnpm sonamu test user.model -t # print Naite traces
|
|
46
|
-
|
|
47
|
-
# CI environments only
|
|
48
|
-
pnpm test
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Requires `test.devRunner.enabled: true` in `sonamu.config.ts`:
|
|
52
|
-
|
|
53
|
-
```typescript
|
|
54
|
-
export default defineConfig({
|
|
55
|
-
test: {
|
|
56
|
-
devRunner: { enabled: true }, // required for `pnpm sonamu test`
|
|
57
|
-
// parallel: true, // optional: separate DB per worker
|
|
58
|
-
// maxWorkers: 4,
|
|
59
|
-
},
|
|
60
|
-
});
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
**→ Prerequisites, CLI argument rules, HMR integration, HTTP API, internal architecture, parallel
|
|
64
|
-
DB flow, vitest.config.ts/global.ts, troubleshooting: `references/devrunner.md`**
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## Pre-Test Writing Checklist
|
|
69
|
-
|
|
70
|
-
- [ ] **Confirm entity design is complete** - `pnpm db:migration` and `pnpm scaffolding` completed without errors
|
|
71
|
-
- [ ] **Plan test writing** - group entities by business process (→ see "Test Writing Plan" below)
|
|
72
|
-
- [ ] **Handle nullable fields in types.ts (FIRST!)** - immediately after entity creation, apply partial + extend handling for nullable fields (→ see "Tasks to Do Immediately After Entity Creation" below)
|
|
73
|
-
- [ ] **Prepare Seed Data** - base data required due to FK constraints (→ see "minimum seed data" in database.md)
|
|
74
|
-
- [ ] **Test helper functions** - prepare helpers for handling complex entity dependencies
|
|
75
|
-
- [ ] **For 10 or more entities** - plan batch strategy (see "Large-Scale Project Strategy" below)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
## Core Test Writing Principles
|
|
79
|
-
|
|
80
|
-
### 1. Verify Actual Structure First
|
|
81
|
-
|
|
82
|
-
**CRITICAL: Always verify the actual entity structure before planning tests.**
|
|
83
|
-
|
|
84
|
-
Before writing tests, you must verify the following:
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
// STEP 1: Check entity.json
|
|
88
|
-
// - actual field names and types
|
|
89
|
-
// - nullable status
|
|
90
|
-
// - enum value list
|
|
91
|
-
// - relation structure
|
|
92
|
-
|
|
93
|
-
// STEP 2: Check types.ts
|
|
94
|
-
// - partial settings in SaveParams
|
|
95
|
-
// - nullish handling for nullable fields
|
|
96
|
-
// - _ids arrays for ManyToMany relations
|
|
97
|
-
|
|
98
|
-
// STEP 3: Check sonamu.generated.ts
|
|
99
|
-
// - Enum type definitions
|
|
100
|
-
// - Subset type structure
|
|
101
|
-
// - BaseSchema structure
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
**Wrong approach:**
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
// BAD - writing tests based on guesses
|
|
108
|
-
test("create user", async () => {
|
|
109
|
-
const [userId] = await UserModel.save([
|
|
110
|
-
{
|
|
111
|
-
name: "Test",
|
|
112
|
-
status: "active", // may actually be "normal"
|
|
113
|
-
role: "user", // may actually be "normal"
|
|
114
|
-
},
|
|
115
|
-
]);
|
|
116
|
-
});
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
**Correct approach:**
|
|
120
|
-
|
|
121
|
-
```typescript
|
|
122
|
-
// GOOD - write after checking entity.json
|
|
123
|
-
// 1. Check user.entity.json:
|
|
124
|
-
// - role: enum ["admin", "normal", "guest"]
|
|
125
|
-
// - status: enum ["active", "inactive"] with dbDefault: "active"
|
|
126
|
-
// - name: string (required)
|
|
127
|
-
// - email: string (nullable)
|
|
128
|
-
|
|
129
|
-
// 2. Check user.types.ts:
|
|
130
|
-
// - status, email are partial in SaveParams
|
|
131
|
-
|
|
132
|
-
// 3. Write test
|
|
133
|
-
test("create user", async () => {
|
|
134
|
-
const [userId] = await UserModel.save([
|
|
135
|
-
{
|
|
136
|
-
name: "Test",
|
|
137
|
-
role: "normal", // exact enum value from entity.json
|
|
138
|
-
// status can be omitted since it has dbDefault
|
|
139
|
-
// email can be omitted since it's nullable
|
|
140
|
-
},
|
|
141
|
-
]);
|
|
142
|
-
});
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### 2. Understanding Subset Structure
|
|
146
|
-
|
|
147
|
-
**Access nested relations using dot notation.**
|
|
148
|
-
|
|
149
|
-
```typescript
|
|
150
|
-
// Check Subset definition in entity.json
|
|
151
|
-
{
|
|
152
|
-
"subsets": {
|
|
153
|
-
"A": [
|
|
154
|
-
"id",
|
|
155
|
-
"title",
|
|
156
|
-
"evaluation_form.id", // BelongsToOne relation
|
|
157
|
-
"evaluation_form.title",
|
|
158
|
-
"evaluation_form.category.id", // nested relation
|
|
159
|
-
"evaluation_form.category.name"
|
|
160
|
-
]
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Access in tests
|
|
165
|
-
test("fetch evaluation item", async () => {
|
|
166
|
-
const { itemId } = await createTestEvaluationItemWithDeps();
|
|
167
|
-
|
|
168
|
-
const item = await EvaluationItemModel.findById("A", itemId);
|
|
169
|
-
|
|
170
|
-
// CORRECT - nested access via dot notation
|
|
171
|
-
expect(item.evaluation_form.id).toBe(formId);
|
|
172
|
-
expect(item.evaluation_form.category.name).toBe("Competency Evaluation");
|
|
173
|
-
|
|
174
|
-
// WRONG - attempting direct FK access
|
|
175
|
-
// expect(item.evaluation_form_id).toBe(formId); // type error!
|
|
176
|
-
});
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
**Important rules:**
|
|
180
|
-
|
|
181
|
-
- FK of BelongsToOne relation is defined as `relation.id` form in Subset
|
|
182
|
-
- Access in tests as `entity.relation.field` form
|
|
183
|
-
- Direct `entity.relation_id` access is not possible (not included in Subset)
|
|
184
|
-
|
|
185
|
-
### 3. Handling DECIMAL Types
|
|
186
|
-
|
|
187
|
-
**DECIMAL types are returned from PostgreSQL with a `.00` suffix.**
|
|
188
|
-
|
|
189
|
-
```typescript
|
|
190
|
-
// entity.json
|
|
191
|
-
{
|
|
192
|
-
"props": [
|
|
193
|
-
{ "name": "salary", "type": "number", "precision": 10, "scale": 2 }
|
|
194
|
-
]
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Generated in migration
|
|
198
|
-
table.decimal("salary", 10, 2); // DECIMAL(10,2)
|
|
199
|
-
|
|
200
|
-
// Writing tests
|
|
201
|
-
test("fetch salary info", async () => {
|
|
202
|
-
const [userId] = await UserModel.save([{
|
|
203
|
-
name: "Test",
|
|
204
|
-
salary: 75000, // input: number
|
|
205
|
-
}]);
|
|
206
|
-
|
|
207
|
-
const user = await UserModel.findById("A", userId);
|
|
208
|
-
|
|
209
|
-
// WRONG - exact comparison may fail
|
|
210
|
-
// expect(user.salary).toBe(75000); // DB may return "75000.00"
|
|
211
|
-
|
|
212
|
-
// CORRECT - pattern matching with toMatch()
|
|
213
|
-
expect(String(user.salary)).toMatch(/^75000(\.00)?$/);
|
|
214
|
-
|
|
215
|
-
// Or convert to number and compare
|
|
216
|
-
expect(Number(user.salary)).toBe(75000);
|
|
217
|
-
|
|
218
|
-
// Or range check
|
|
219
|
-
expect(user.salary).toBeGreaterThanOrEqual(74999.99);
|
|
220
|
-
expect(user.salary).toBeLessThanOrEqual(75000.01);
|
|
221
|
-
});
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
**DECIMAL type comparison patterns:**
|
|
225
|
-
|
|
226
|
-
```typescript
|
|
227
|
-
// Pattern 1: string pattern matching
|
|
228
|
-
expect(String(value)).toMatch(/^1234\.56$/);
|
|
229
|
-
expect(String(value)).toMatch(/^1234(\.56)?$/); // .56 optional
|
|
230
|
-
|
|
231
|
-
// Pattern 2: convert to number and compare
|
|
232
|
-
expect(Number(value)).toBe(1234.56);
|
|
233
|
-
|
|
234
|
-
// Pattern 3: range check (considering floating point errors)
|
|
235
|
-
expect(value).toBeCloseTo(1234.56, 2); // up to 2 decimal places
|
|
236
|
-
|
|
237
|
-
// Pattern 4: toMatchObject (when comparing objects)
|
|
238
|
-
expect(result).toMatchObject({
|
|
239
|
-
salary: expect.any(Number), // type check only
|
|
240
|
-
});
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
## Rules
|
|
244
|
-
|
|
245
|
-
- `bootstrap(vi)` call required in all test files
|
|
246
|
-
- Each test is automatically rolled back (test isolation)
|
|
247
|
-
- Use `test` for unauthenticated tests, `testAs` for authenticated tests
|
|
248
|
-
- Define fixtures with `createFixtureLoader` and load with `loadFixtures`
|
|
249
|
-
- Use Naite to track and validate query/UpsertBuilder behavior
|
|
250
|
-
- Recommend snapshot tests using `toMatchInlineSnapshot()`
|
|
251
|
-
- Configure Mocks globally in `setup-mocks.ts` or use `vi.spyOn` within tests
|
|
@@ -1,405 +0,0 @@
|
|
|
1
|
-
# DevRunner and Test Configuration
|
|
2
|
-
|
|
3
|
-
## Running Tests
|
|
4
|
-
|
|
5
|
-
**Principle: Use `pnpm sonamu test` during development.** Assume the dev server is always running. If the dev server is down, start it first with `pnpm sonamu dev`, then run tests. Use `pnpm test` only in CI environments.
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# Check dev server (start if it's down)
|
|
9
|
-
pnpm sonamu dev
|
|
10
|
-
|
|
11
|
-
# Tests during development (default)
|
|
12
|
-
pnpm sonamu test
|
|
13
|
-
pnpm sonamu test user.model
|
|
14
|
-
pnpm sonamu test user.model -p "findMany"
|
|
15
|
-
|
|
16
|
-
# CI environments only
|
|
17
|
-
pnpm test
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## DevRunner — `sonamu test` (Default Test Execution Method)
|
|
23
|
-
|
|
24
|
-
`sonamu test` runs tests through a Vitest Node API instance that resides inside the `sonamu dev` process. Instead of starting Vitest fresh each time, it reuses an already-initialized instance, making execution 3.2x faster, and it integrates with HMR so tests always run against the latest code immediately after source changes.
|
|
25
|
-
|
|
26
|
-
### Prerequisites
|
|
27
|
-
|
|
28
|
-
**1. Enable devRunner in sonamu.config.ts:**
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
export default defineConfig({
|
|
32
|
-
test: {
|
|
33
|
-
devRunner: {
|
|
34
|
-
enabled: true,
|
|
35
|
-
// routePrefix: "/__test__", // optional, default value
|
|
36
|
-
// vitestConfigPath: undefined, // optional, default: vitest.config.ts (relative to api-root)
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Configuration type (`SonamuDevRunnerConfig`):
|
|
43
|
-
|
|
44
|
-
- `enabled: boolean` — Whether to enable DevRunner (default: false)
|
|
45
|
-
- `routePrefix?: string` — Test endpoint path prefix (default: `/__test__`)
|
|
46
|
-
- `vitestConfigPath?: string` — vitest.config.ts path (relative to api-root)
|
|
47
|
-
|
|
48
|
-
**2. Start the dev server:**
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
sonamu dev # or pnpm dev
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
When the dev server starts, `DevVitestManager` is automatically initialized under the `isLocal() && devRunner.enabled` condition, and test endpoints are registered with Fastify.
|
|
55
|
-
|
|
56
|
-
### CLI Usage
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
# Run all tests
|
|
60
|
-
sonamu test
|
|
61
|
-
|
|
62
|
-
# Specify file (matched by partial filename — uses globTestSpecifications)
|
|
63
|
-
sonamu test user.model
|
|
64
|
-
|
|
65
|
-
# Multiple files
|
|
66
|
-
sonamu test user.model order.model
|
|
67
|
-
|
|
68
|
-
# Run specific test cases only (test name pattern)
|
|
69
|
-
sonamu test user.model --pattern "findMany"
|
|
70
|
-
sonamu test user.model -p "findMany"
|
|
71
|
-
|
|
72
|
-
# Print Naite traces
|
|
73
|
-
sonamu test user.model --traces
|
|
74
|
-
sonamu test user.model -t
|
|
75
|
-
|
|
76
|
-
# Combine file + pattern + trace
|
|
77
|
-
sonamu test user.model -p "findMany" -t
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Argument processing rules:
|
|
81
|
-
|
|
82
|
-
- `--pattern` / `-p`: test name string filter (`setGlobalTestNamePattern` → `resetGlobalTestNamePattern` after execution)
|
|
83
|
-
- `--traces` / `-t`: boolean flag, enables Naite trace output
|
|
84
|
-
- Arguments not starting with `-`: treated as file list
|
|
85
|
-
- Multiple files allowed
|
|
86
|
-
- `ok: false` in server response is reflected as exit code 1
|
|
87
|
-
|
|
88
|
-
### Naite Trace Output
|
|
89
|
-
|
|
90
|
-
The `--traces` / `-t` flag lets you view `Naite.t(key, data)` records from test code in the CLI:
|
|
91
|
-
|
|
92
|
-
```
|
|
93
|
-
Tests: 5 passed, 0 failed, 5 total
|
|
94
|
-
Duration: 791ms
|
|
95
|
-
|
|
96
|
-
Traces:
|
|
97
|
-
|
|
98
|
-
UserModel > BaseModel basic functionality check > Model.findMany() with num = 0
|
|
99
|
-
user.model.test.ts
|
|
100
|
-
|
|
101
|
-
[esq-query] user.model.ts:113
|
|
102
|
-
select "users"."id" as "id", ...
|
|
103
|
-
|
|
104
|
-
[puri:executed-query] puri.ts:1349
|
|
105
|
-
select COUNT(*)::integer as "total" from "users" limit 1
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
Without the `--traces` flag, traces are not printed. The API response (`POST /__test__/run`) always includes a `traces` field, so external tools can make use of it.
|
|
109
|
-
|
|
110
|
-
For trace data details: see `sonamu-naite`
|
|
111
|
-
|
|
112
|
-
### HMR Integration — Automatic Vitest Module Graph Invalidation on Source Changes
|
|
113
|
-
|
|
114
|
-
When a source file is modified, `hmrAndSync` in the syncer triggers both of the following simultaneously:
|
|
115
|
-
|
|
116
|
-
1. Server HMR cache invalidation (`hot.invalidateFile`)
|
|
117
|
-
2. Vitest module graph invalidation (`Sonamu.devVitestManager.invalidateFiles([filePath])`)
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
-
Modify user.model.ts → save
|
|
121
|
-
Server log: "Test invalidated: src/application/user/user.model.ts"
|
|
122
|
-
sonamu test user.model ← runs with latest code
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
Because Vite's `moduleGraph.invalidateModule()` recursively cascades in the importer direction, invalidating a single source file automatically invalidates any test files that import it. No restart is needed.
|
|
126
|
-
|
|
127
|
-
**Transitive dependency example:**
|
|
128
|
-
|
|
129
|
-
- Change `utils.ts` → `user.model.ts` (imports utils) → `user.model.test.ts` all automatically invalidated
|
|
130
|
-
|
|
131
|
-
### Direct HTTP API Calls
|
|
132
|
-
|
|
133
|
-
You can call the HTTP API directly instead of using the CLI:
|
|
134
|
-
|
|
135
|
-
```bash
|
|
136
|
-
# Run tests
|
|
137
|
-
curl -X POST http://localhost:3000/__test__/run \
|
|
138
|
-
-H "Content-Type: application/json" \
|
|
139
|
-
-d '{"files": ["user.model"], "pattern": "findMany"}'
|
|
140
|
-
|
|
141
|
-
# Check status
|
|
142
|
-
curl http://localhost:3000/__test__/status
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
**POST `/__test__/run`** request:
|
|
146
|
-
|
|
147
|
-
```json
|
|
148
|
-
{ "files": ["src/user/user.model.test.ts"], "pattern": "should create user" }
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
Response (success):
|
|
152
|
-
|
|
153
|
-
```json
|
|
154
|
-
{
|
|
155
|
-
"ok": true,
|
|
156
|
-
"summary": { "total": 12, "passed": 11, "failed": 1, "skipped": 0, "durationMs": 842 },
|
|
157
|
-
"failed": [
|
|
158
|
-
{
|
|
159
|
-
"file": "src/user/user.model.test.ts",
|
|
160
|
-
"name": "UserModel > should create user",
|
|
161
|
-
"error": "Expected ..."
|
|
162
|
-
}
|
|
163
|
-
],
|
|
164
|
-
"traces": [
|
|
165
|
-
{
|
|
166
|
-
"testName": "UserModel > should create user",
|
|
167
|
-
"file": "src/user/user.model.test.ts",
|
|
168
|
-
"traces": [
|
|
169
|
-
{
|
|
170
|
-
"key": "esq-query",
|
|
171
|
-
"value": "select ...",
|
|
172
|
-
"filePath": "user.model.ts",
|
|
173
|
-
"lineNumber": 113,
|
|
174
|
-
"at": "2026-02-23T14:51:35+09:00"
|
|
175
|
-
}
|
|
176
|
-
]
|
|
177
|
-
}
|
|
178
|
-
]
|
|
179
|
-
}
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
**GET `/__test__/status`** response:
|
|
183
|
-
|
|
184
|
-
```json
|
|
185
|
-
{ "ready": true, "running": false, "lastRunAt": "2026-02-13T12:34:56.000Z" }
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### Internal Architecture
|
|
189
|
-
|
|
190
|
-
**DevVitestManager** (`testing/dev-vitest-manager.ts`):
|
|
191
|
-
|
|
192
|
-
- Creates a resident instance with `createVitest('test', cliOptions, viteOverrides)`
|
|
193
|
-
- Configured with `watch: true, standalone: true`, but blocks automatic re-runs:
|
|
194
|
-
- `forceRerunTriggers: []`
|
|
195
|
-
- `server.watch: null` (chokidar not created)
|
|
196
|
-
- `onFilterWatchedSpecification(() => false)`
|
|
197
|
-
- Explicitly sets `env: { NODE_ENV: "test" }` so worker processes use the test DB
|
|
198
|
-
- **Queue-based sequential execution**: prevents result mixing on concurrent requests, processes in order and responds to each
|
|
199
|
-
- **Result aggregation**: filters only the modules requested for execution using `specModuleIds` (works around the issue where `testModules` includes all modules in standalone mode)
|
|
200
|
-
- Keeps loading the project's `vitest.config.ts` (reuses existing sequencer/reporter/globalSetup)
|
|
201
|
-
|
|
202
|
-
**Fastify integration** (`testing/dev-test-routes.ts`):
|
|
203
|
-
|
|
204
|
-
- Registers routes with `registerDevTestRoutes(server, config)`
|
|
205
|
-
- Stores manager instance in `Sonamu.devVitestManager`
|
|
206
|
-
- Ensures `shutdown()` is called in `server.addHook('onClose')`
|
|
207
|
-
|
|
208
|
-
**CLI** (`bin/test-command.ts`):
|
|
209
|
-
|
|
210
|
-
- Calls `testCommand()` directly before `tsicli` matching (to handle variadic arguments)
|
|
211
|
-
- Included in `bootstrap()`'s `notToInit` list (no need for `Sonamu.init` since it only makes HTTP calls)
|
|
212
|
-
- Server port/host read from `server.listen` in `sonamu.config.ts`
|
|
213
|
-
|
|
214
|
-
### Performance Comparison (miomock baseline)
|
|
215
|
-
|
|
216
|
-
Single file (`user.model.test.ts`):
|
|
217
|
-
|
|
218
|
-
| Item | `vitest run` | `sonamu test` | Difference |
|
|
219
|
-
| ------------------------------- | ------------ | ------------- | ----------- |
|
|
220
|
-
| Test execution time | 2,610ms | 823ms | 3.2x faster |
|
|
221
|
-
| Total elapsed time (wall clock) | 4,118ms | 1,907ms | 2.2x faster |
|
|
222
|
-
|
|
223
|
-
Multiple files (puri tests, 4 files 147 tests):
|
|
224
|
-
|
|
225
|
-
| Item | `vitest run` | `sonamu test` | Difference |
|
|
226
|
-
| ------------------------------- | ------------ | ------------- | ----------- |
|
|
227
|
-
| Test execution time | 5,740ms | 1,774ms | 3.2x faster |
|
|
228
|
-
| Total elapsed time (wall clock) | 7,035ms | 2,673ms | 2.6x faster |
|
|
229
|
-
|
|
230
|
-
Speed difference reason: `vitest run` requires process boot (~1.5s) + module transform (~400ms) on every execution, whereas `sonamu test` reuses an already-initialized instance and avoids this overhead.
|
|
231
|
-
|
|
232
|
-
### `pnpm sonamu test` vs `pnpm test` Comparison
|
|
233
|
-
|
|
234
|
-
| | `pnpm sonamu test` (default) | `pnpm test` (CI/fallback) |
|
|
235
|
-
| ------------------- | --------------------------------------------- | --------------------------- |
|
|
236
|
-
| Execution method | Resident instance inside dev server | Independent Vitest process |
|
|
237
|
-
| Initialization cost | None (already initialized) | Initialization on every run |
|
|
238
|
-
| HMR integration | Source changes reflected immediately | Not applicable |
|
|
239
|
-
| Naite trace | CLI output via `--traces` flag | Available through reporter |
|
|
240
|
-
| Use case | **Default test execution during development** | CI environments |
|
|
241
|
-
| Prerequisite | `sonamu dev` running (assumed always running) | None |
|
|
242
|
-
|
|
243
|
-
### Troubleshooting
|
|
244
|
-
|
|
245
|
-
**"Cannot connect to dev server"**
|
|
246
|
-
→ Check that `sonamu dev` is running. The CLI sends HTTP requests to `config.server.listen.port`.
|
|
247
|
-
|
|
248
|
-
**"devRunner is not enabled" (or 404 response)**
|
|
249
|
-
→ `test.devRunner.enabled: true` must be set in `sonamu.config.ts`. DevRunner is only activated in `isLocal()` environments.
|
|
250
|
-
|
|
251
|
-
**"Vitest instance is not ready yet" (500 response)**
|
|
252
|
-
→ Request was made before Vitest initialization completed immediately after dev server startup. Check `GET /__test__/status` for `ready: true` before running.
|
|
253
|
-
|
|
254
|
-
**Tests running against old code**
|
|
255
|
-
→ After saving a source file, check whether a `"Test invalidated: ..."` log appears. If not, verify that `devRunner.enabled` is `true` and that the syncer is working correctly.
|
|
256
|
-
|
|
257
|
-
---
|
|
258
|
-
|
|
259
|
-
## Complete Map of Test-Related Settings in sonamu.config.ts
|
|
260
|
-
|
|
261
|
-
Both `pnpm sonamu test` (DevRunner, default) and `pnpm test` (direct Vitest execution, CI) reference settings from `sonamu.config.ts`.
|
|
262
|
-
|
|
263
|
-
### Configuration Type Definition (SonamuTestConfig)
|
|
264
|
-
|
|
265
|
-
```typescript
|
|
266
|
-
// config.ts
|
|
267
|
-
export type SonamuTestConfig = {
|
|
268
|
-
/** Enable parallel testing (default: false) */
|
|
269
|
-
parallel?: boolean;
|
|
270
|
-
/** Number of parallel execution workers (default: 4) */
|
|
271
|
-
maxWorkers?: number;
|
|
272
|
-
/** Resident Vitest instance settings inside dev server */
|
|
273
|
-
devRunner?: SonamuDevRunnerConfig;
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
export type SonamuDevRunnerConfig = {
|
|
277
|
-
/** Whether to enable DevRunner (default: false) */
|
|
278
|
-
enabled: boolean;
|
|
279
|
-
/** Test endpoint path prefix (default: /__test__) */
|
|
280
|
-
routePrefix?: string;
|
|
281
|
-
/** vitest.config.ts path (relative to api-root) */
|
|
282
|
-
vitestConfigPath?: string;
|
|
283
|
-
};
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
### Parallel Test Configuration (Optional)
|
|
287
|
-
|
|
288
|
-
Both `pnpm sonamu test` and `pnpm test` share the parallel configuration. Only configure this if parallel execution is needed:
|
|
289
|
-
|
|
290
|
-
```typescript
|
|
291
|
-
export default defineConfig({
|
|
292
|
-
test: {
|
|
293
|
-
parallel: true, // Separate DB execution per worker
|
|
294
|
-
maxWorkers: 4, // Number of workers (default: 4)
|
|
295
|
-
},
|
|
296
|
-
});
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
When `parallel: true`, `getSonamuTestConfig()` (vitest-helpers.ts) injects `pool: "forks"`, `maxWorkers`, and `env: { SONAMU_WORKER_DB: "true" }` into vitest, and `globalSetup` (global-setup.ts) creates DBs `{database.name}_test_1` through `{database.name}_test_{maxWorkers}` by cloning from the template (`{database.name}_test`).
|
|
300
|
-
|
|
301
|
-
### DevRunner Configuration (Required)
|
|
302
|
-
|
|
303
|
-
To use `pnpm sonamu test`, DevRunner must be enabled. Just add `test.devRunner.enabled: true`:
|
|
304
|
-
|
|
305
|
-
```typescript
|
|
306
|
-
export default defineConfig({
|
|
307
|
-
server: {
|
|
308
|
-
listen: {
|
|
309
|
-
port: 3000, // CLI sends HTTP requests to this port
|
|
310
|
-
host: "localhost", // CLI connects to this host (default: localhost)
|
|
311
|
-
},
|
|
312
|
-
},
|
|
313
|
-
|
|
314
|
-
test: {
|
|
315
|
-
devRunner: {
|
|
316
|
-
enabled: true, // Required: enable DevRunner
|
|
317
|
-
// routePrefix: "/__test__", // Optional: endpoint path (default value)
|
|
318
|
-
// vitestConfigPath: undefined, // Optional: vitest.config.ts path (default: vitest.config.ts in api-root)
|
|
319
|
-
},
|
|
320
|
-
// parallel: true, // Optional: independent of DevRunner
|
|
321
|
-
// maxWorkers: 4, // Optional: when using parallel
|
|
322
|
-
},
|
|
323
|
-
});
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
### Source Code Reference Locations per Setting
|
|
327
|
-
|
|
328
|
-
| Setting path | Default | Reference location | Purpose |
|
|
329
|
-
| --------------------------------- | ------------- | ----------------------------------------------- | -------------------------------------------------- |
|
|
330
|
-
| `test.devRunner.enabled` | `false` | `sonamu.ts` L394, `test-command.ts` | DevRunner activation condition |
|
|
331
|
-
| `test.devRunner.routePrefix` | `"/__test__"` | `dev-test-routes.ts`, `test-command.ts` | HTTP endpoint path |
|
|
332
|
-
| `test.devRunner.vitestConfigPath` | `undefined` | `dev-vitest-manager.ts` `start()` | Vitest config file location |
|
|
333
|
-
| `test.parallel` | `false` | `vitest-helpers.ts` `getSonamuTestConfig()` | `pool: "forks"` + `SONAMU_WORKER_DB` env injection |
|
|
334
|
-
| `test.maxWorkers` | `4` | `vitest-helpers.ts`, `global-setup.ts` | Vitest maxWorkers + number of worker DBs |
|
|
335
|
-
| `server.listen.port` | `3000` | `test-command.ts` | CLI → dev server HTTP connection port |
|
|
336
|
-
| `server.listen.host` | `"localhost"` | `test-command.ts` | CLI → dev server HTTP connection host |
|
|
337
|
-
| `database.name` | — | `db.ts` `generateDBConfig()`, `global-setup.ts` | Test DB name (`{name}_test`) |
|
|
338
|
-
|
|
339
|
-
### Activation Conditions
|
|
340
|
-
|
|
341
|
-
DevRunner is registered in `sonamu.ts` under the condition `isLocal() && config.test?.devRunner?.enabled`. `isLocal()` returns true when `NODE_ENV` is `development` or `test` (`controller.ts`). It works in local development and test environments and is disabled in staging/production environments.
|
|
342
|
-
|
|
343
|
-
### Parallel Test DB Flow
|
|
344
|
-
|
|
345
|
-
DB flow when `test.parallel: true` is set:
|
|
346
|
-
|
|
347
|
-
1. **globalSetup** (`global-setup.ts`): Clones `{database.name}_test_1` through `_test_{maxWorkers}` from `{database.name}_test` as template
|
|
348
|
-
2. **getSonamuTestConfig** (`vitest-helpers.ts`): Injects `env: { SONAMU_WORKER_DB: "true" }` into vitest
|
|
349
|
-
3. **DB.getDB** (`db.ts`): When `SONAMU_WORKER_DB=true`, selects worker-specific DB using `VITEST_POOL_ID` environment variable (`{database.name}_test_{workerId}`)
|
|
350
|
-
4. **globalTeardown**: Deletes all worker DBs
|
|
351
|
-
|
|
352
|
-
When `test.parallel: false` (default), all tests run sequentially on the single `{database.name}_test` DB.
|
|
353
|
-
|
|
354
|
-
---
|
|
355
|
-
|
|
356
|
-
## Configuration Files
|
|
357
|
-
|
|
358
|
-
### vitest.config.ts
|
|
359
|
-
|
|
360
|
-
```typescript
|
|
361
|
-
import { getSonamuTestConfig, NaiteVitestReporter } from "sonamu/test";
|
|
362
|
-
import { defineConfig } from "vitest/config";
|
|
363
|
-
|
|
364
|
-
export default defineConfig(async () => ({
|
|
365
|
-
test: await getSonamuTestConfig({
|
|
366
|
-
include: ["src/**/*.test.ts"],
|
|
367
|
-
exclude: ["src/**/*.test-hold.ts"],
|
|
368
|
-
globals: true,
|
|
369
|
-
globalSetup: ["./src/testing/global.ts"],
|
|
370
|
-
setupFiles: ["./src/testing/setup-mocks.ts"],
|
|
371
|
-
reporters: ["default", NaiteVitestReporter],
|
|
372
|
-
restoreMocks: true,
|
|
373
|
-
}),
|
|
374
|
-
}));
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
### global.ts
|
|
378
|
-
|
|
379
|
-
```typescript
|
|
380
|
-
import dotenv from "dotenv";
|
|
381
|
-
dotenv.config();
|
|
382
|
-
export { setup } from "sonamu/test";
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
### sonamu.config.ts (test configuration)
|
|
386
|
-
|
|
387
|
-
```typescript
|
|
388
|
-
export default defineConfig({
|
|
389
|
-
test: {
|
|
390
|
-
parallel: true, // Enable parallel testing
|
|
391
|
-
maxWorkers: 4, // Number of workers (default: 4)
|
|
392
|
-
devRunner: {
|
|
393
|
-
enabled: true, // Enable DevRunner
|
|
394
|
-
},
|
|
395
|
-
},
|
|
396
|
-
});
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
---
|
|
400
|
-
|
|
401
|
-
## References
|
|
402
|
-
|
|
403
|
-
- **Test writing guide**: `sonamu-testing`
|
|
404
|
-
- **Naite tracing system**: `sonamu-naite`
|
|
405
|
-
- **Fixture CLI**: `sonamu-fixture`
|