@atlashub/smartstack-cli 3.23.0 → 3.25.0
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/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +96 -24
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/mcp-scaffolding/component.tsx.hbs +21 -1
- package/templates/skills/apex/references/smartstack-api.md +174 -5
- package/templates/skills/apex/references/smartstack-frontend.md +1101 -0
- package/templates/skills/apex/references/smartstack-layers.md +81 -5
- package/templates/skills/apex/steps/step-01-analyze.md +27 -3
- package/templates/skills/apex/steps/step-02-plan.md +5 -1
- package/templates/skills/apex/steps/step-03-execute.md +47 -5
- package/templates/skills/apex/steps/step-04-validate.md +300 -0
- package/templates/skills/apex/steps/step-05-examine.md +7 -0
- package/templates/skills/apex/steps/step-07-tests.md +19 -0
- package/templates/skills/business-analyse/_shared.md +6 -6
- package/templates/skills/business-analyse/patterns/suggestion-catalog.md +1 -1
- package/templates/skills/business-analyse/questionnaire/07-ui.md +3 -3
- package/templates/skills/business-analyse/references/cadrage-pre-analysis.md +1 -1
- package/templates/skills/business-analyse/references/entity-architecture-decision.md +3 -3
- package/templates/skills/business-analyse/references/handoff-file-templates.md +13 -5
- package/templates/skills/business-analyse/references/spec-auto-inference.md +14 -14
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +2 -2
- package/templates/skills/business-analyse/steps/step-02-decomposition.md +1 -1
- package/templates/skills/business-analyse/steps/step-03a1-setup.md +2 -2
- package/templates/skills/business-analyse/steps/step-03b-ui.md +2 -1
- package/templates/skills/business-analyse/steps/step-05a-handoff.md +15 -4
- package/templates/skills/business-analyse/templates/tpl-frd.md +2 -2
- package/templates/skills/business-analyse/templates-frd.md +2 -2
- package/templates/skills/efcore/steps/migration/step-02-create.md +14 -1
- package/templates/skills/ralph-loop/references/category-rules.md +71 -9
- package/templates/skills/ralph-loop/references/compact-loop.md +3 -3
- package/templates/skills/ralph-loop/references/core-seed-data.md +10 -0
- package/templates/skills/ralph-loop/steps/step-02-execute.md +190 -1
- package/templates/skills/validate-feature/steps/step-01-compile.md +4 -1
- package/templates/skills/validate-feature/steps/step-05-db-validation.md +86 -1
|
@@ -63,7 +63,7 @@ Key category triggers:
|
|
|
63
63
|
|----------|---------------|
|
|
64
64
|
| `infrastructure` with `_migrationMeta` | Migration sequence: MCP suggest → add → update → build |
|
|
65
65
|
| `infrastructure` with seed data keywords | **MANDATORY**: Read `references/core-seed-data.md` |
|
|
66
|
-
| `frontend` | MCP-first: scaffold_api_client → scaffold_routes → pages |
|
|
66
|
+
| `frontend` | MCP-first: scaffold_api_client → scaffold_routes → `/ui-components` skill (pages) → form pages (create/edit = full pages, ZERO modals) → form tests → i18n (4 langues) |
|
|
67
67
|
| `test` | Generate via MCP → run → fix loop → 100% pass required |
|
|
68
68
|
| `validation` | Clean build → full test suite → MCP validate |
|
|
69
69
|
|
|
@@ -164,6 +164,43 @@ fi
|
|
|
164
164
|
- If `auth_Permissions` is empty → all authorization checks reject → 403 on every endpoint
|
|
165
165
|
- These are **foundational** — without them, ALL subsequent features (frontend, API, tests) are useless
|
|
166
166
|
|
|
167
|
+
**POST-CHECK: DefaultTenantId cross-schema FK validation (WARNING)**
|
|
168
|
+
|
|
169
|
+
After generating SeedConstants.cs and DevDataSeeder, verify that `DefaultTenantId` is not a phantom GUID that doesn't exist in `core.tenant_Tenants`:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# Find SeedConstants.cs
|
|
173
|
+
SEED_CONSTANTS=$(find . -path "*/SeedConstants.cs" 2>/dev/null | head -1)
|
|
174
|
+
if [ -n "$SEED_CONSTANTS" ]; then
|
|
175
|
+
# Extract the DefaultTenantId GUID value
|
|
176
|
+
TENANT_GUID=$(grep -oP 'DefaultTenantId\s*=\s*Guid\.Parse\("([^"]+)"\)' "$SEED_CONSTANTS" | grep -oP '"[^"]+"' | tr -d '"')
|
|
177
|
+
|
|
178
|
+
if [ -z "$TENANT_GUID" ]; then
|
|
179
|
+
echo "⚠️ WARNING: SeedConstants.cs found but DefaultTenantId not defined"
|
|
180
|
+
echo "DevDataSeeder requires SeedConstants.DefaultTenantId for business seed data"
|
|
181
|
+
echo "Fix: Add 'public static readonly Guid DefaultTenantId = Guid.Parse(\"...\");' to SeedConstants.cs"
|
|
182
|
+
else
|
|
183
|
+
# Verify the GUID is referenced somewhere in platform config (not an invented value)
|
|
184
|
+
GUID_FOUND=$(grep -r "$TENANT_GUID" appsettings*.json src/ 2>/dev/null | grep -v SeedConstants)
|
|
185
|
+
|
|
186
|
+
if [ -z "$GUID_FOUND" ]; then
|
|
187
|
+
echo "⚠️ WARNING: SeedConstants.DefaultTenantId ($TENANT_GUID) not found in platform config"
|
|
188
|
+
echo "This GUID must exist in core.tenant_Tenants at runtime"
|
|
189
|
+
echo "Verify: The SmartStack platform seeds this tenant during InitializeSmartStackAsync()"
|
|
190
|
+
echo "If using a custom TenantId, ensure it's created before DevDataSeeder runs (Order >= 200)"
|
|
191
|
+
echo "validate-feature step-05 will verify this FK exists in real database"
|
|
192
|
+
else
|
|
193
|
+
echo "✓ SeedConstants.DefaultTenantId ($TENANT_GUID) found in platform config"
|
|
194
|
+
fi
|
|
195
|
+
fi
|
|
196
|
+
fi
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Why this matters:**
|
|
200
|
+
- If `DefaultTenantId` references a GUID not in `core.tenant_Tenants`, DevDataSeeder inserts orphan rows
|
|
201
|
+
- Business entities with invalid `TenantId` FK cause runtime 500 errors
|
|
202
|
+
- The tenant must be seeded by `InitializeSmartStackAsync()` BEFORE DevDataSeeder (Order >= 200) runs
|
|
203
|
+
|
|
167
204
|
### 6. Validate with MCP
|
|
168
205
|
|
|
169
206
|
```
|
|
@@ -206,6 +243,158 @@ npm run lint # ESLint — MUST exit 0
|
|
|
206
243
|
```
|
|
207
244
|
If FAIL → read errors → fix TSX/TS files → re-run → loop until pass.
|
|
208
245
|
|
|
246
|
+
**Frontend POST-CHECKs (BLOCKING for frontend category):**
|
|
247
|
+
```bash
|
|
248
|
+
# POST-CHECK: Forms must be full pages — ZERO modals/popups/drawers
|
|
249
|
+
PAGE_FILES=$(find src/pages/ -name "*.tsx" 2>/dev/null)
|
|
250
|
+
if [ -n "$PAGE_FILES" ]; then
|
|
251
|
+
MODAL_IMPORTS=$(grep -Pn "import.*(?:Modal|Dialog|Drawer|Popup|Sheet)" $PAGE_FILES 2>/dev/null)
|
|
252
|
+
if [ -n "$MODAL_IMPORTS" ]; then
|
|
253
|
+
echo "BLOCKING: Form pages must NOT use Modal/Dialog/Drawer/Popup components"
|
|
254
|
+
echo "Create/Edit forms MUST be full pages with own URL routes (/create, /:id/edit)"
|
|
255
|
+
echo "$MODAL_IMPORTS"
|
|
256
|
+
exit 1
|
|
257
|
+
fi
|
|
258
|
+
fi
|
|
259
|
+
|
|
260
|
+
# POST-CHECK: Create/Edit pages must exist for each module with a list page
|
|
261
|
+
LIST_PAGES=$(find src/pages/ -name "*ListPage.tsx" -o -name "*sPage.tsx" 2>/dev/null | grep -v test)
|
|
262
|
+
if [ -n "$LIST_PAGES" ]; then
|
|
263
|
+
for LP in $LIST_PAGES; do
|
|
264
|
+
DIR=$(dirname "$LP"); MOD=$(basename "$DIR")
|
|
265
|
+
if [ -z "$(find "$DIR" -name "*CreatePage.tsx" 2>/dev/null)" ]; then
|
|
266
|
+
echo "WARNING: Module $MOD has list page but no CreatePage"
|
|
267
|
+
fi
|
|
268
|
+
if [ -z "$(find "$DIR" -name "*EditPage.tsx" 2>/dev/null)" ]; then
|
|
269
|
+
echo "WARNING: Module $MOD has list page but no EditPage"
|
|
270
|
+
fi
|
|
271
|
+
done
|
|
272
|
+
fi
|
|
273
|
+
|
|
274
|
+
# First check: at least one test file must exist in pages/
|
|
275
|
+
PAGE_FILES=$(find src/pages/ -name "*.tsx" ! -name "*.test.tsx" 2>/dev/null)
|
|
276
|
+
if [ -n "$PAGE_FILES" ]; then
|
|
277
|
+
TEST_FILES=$(find src/pages/ -name "*.test.tsx" 2>/dev/null)
|
|
278
|
+
if [ -z "$TEST_FILES" ]; then
|
|
279
|
+
echo "BLOCKING: No frontend test files found in src/pages/"
|
|
280
|
+
echo "Every module with pages MUST have at least one .test.tsx file"
|
|
281
|
+
exit 1
|
|
282
|
+
fi
|
|
283
|
+
fi
|
|
284
|
+
|
|
285
|
+
# POST-CHECK: Form pages must have companion test files
|
|
286
|
+
FORM_PAGES=$(find src/pages/ -name "*CreatePage.tsx" -o -name "*EditPage.tsx" 2>/dev/null | grep -v test)
|
|
287
|
+
if [ -n "$FORM_PAGES" ]; then
|
|
288
|
+
for FP in $FORM_PAGES; do
|
|
289
|
+
TEST="${FP%.tsx}.test.tsx"
|
|
290
|
+
if [ ! -f "$TEST" ]; then
|
|
291
|
+
echo "BLOCKING: Form page missing test file: $FP → expected $TEST"
|
|
292
|
+
exit 1
|
|
293
|
+
fi
|
|
294
|
+
done
|
|
295
|
+
fi
|
|
296
|
+
|
|
297
|
+
# POST-CHECK: FK fields must NOT be plain text inputs — use EntityLookup
|
|
298
|
+
FORM_PAGES=$(find src/pages/ -name "*CreatePage.tsx" -o -name "*EditPage.tsx" 2>/dev/null | grep -v test | grep -v node_modules)
|
|
299
|
+
if [ -n "$FORM_PAGES" ]; then
|
|
300
|
+
# Match any input with name ending in "Id" (except hidden inputs)
|
|
301
|
+
FK_TEXT_INPUTS=$(grep -Pn '<input[^>]*name=["\x27][a-zA-Z]*Id["\x27]' $FORM_PAGES 2>/dev/null | grep -v 'type=["\x27]hidden["\x27]')
|
|
302
|
+
if [ -n "$FK_TEXT_INPUTS" ]; then
|
|
303
|
+
echo "BLOCKING: FK Guid fields rendered as plain text inputs — MUST use EntityLookup"
|
|
304
|
+
echo "See smartstack-frontend.md section 6 for the EntityLookup pattern"
|
|
305
|
+
echo "$FK_TEXT_INPUTS"
|
|
306
|
+
exit 1
|
|
307
|
+
fi
|
|
308
|
+
FK_PLACEHOLDER=$(grep -Pn 'placeholder=["\x27].*[Ee]nter.*[Ii][Dd]|placeholder=["\x27].*[Gg][Uu][Ii][Dd]' $FORM_PAGES 2>/dev/null)
|
|
309
|
+
if [ -n "$FK_PLACEHOLDER" ]; then
|
|
310
|
+
echo "BLOCKING: Form placeholder asks user to enter ID/GUID — use EntityLookup instead"
|
|
311
|
+
echo "$FK_PLACEHOLDER"
|
|
312
|
+
exit 1
|
|
313
|
+
fi
|
|
314
|
+
fi
|
|
315
|
+
|
|
316
|
+
# POST-CHECK: Backend GetAll endpoints must support ?search= for EntityLookup
|
|
317
|
+
CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
|
|
318
|
+
if [ -n "$CTRL_FILES" ]; then
|
|
319
|
+
for f in $CTRL_FILES; do
|
|
320
|
+
if grep -q "\[HttpGet\]" "$f" && grep -q "GetAll" "$f"; then
|
|
321
|
+
if ! grep -q "search" "$f"; then
|
|
322
|
+
echo "WARNING: Controller missing search parameter on GetAll: $f"
|
|
323
|
+
echo "GetAll MUST accept ?search= to enable EntityLookup on frontend"
|
|
324
|
+
fi
|
|
325
|
+
fi
|
|
326
|
+
done
|
|
327
|
+
fi
|
|
328
|
+
|
|
329
|
+
# POST-CHECK: Route seed data vs frontend cross-validation
|
|
330
|
+
SEED_NAV_FILES=$(find src/ -path "*/Seeding/Data/*" -name "*NavigationSeedData.cs" 2>/dev/null)
|
|
331
|
+
APP_TSX=$(find src/ -name "App.tsx" -not -path "*/node_modules/*" 2>/dev/null | head -1)
|
|
332
|
+
if [ -n "$SEED_NAV_FILES" ] && [ -n "$APP_TSX" ]; then
|
|
333
|
+
SEED_ROUTES=$(grep -ohP 'Route\s*=\s*"([^"]+)"' $SEED_NAV_FILES | sed 's/Route\s*=\s*"//' | sed 's/"//' | sort -u)
|
|
334
|
+
CLIENT_PATHS=$(grep -ohP "path:\s*['\"]([^'\"]+)['\"]" "$APP_TSX" | sed "s/path:\s*['\"]//;s/['\"]//" | sort -u)
|
|
335
|
+
if [ -n "$SEED_ROUTES" ] && [ -z "$CLIENT_PATHS" ]; then
|
|
336
|
+
echo "WARNING: Seed data has navigation routes but App.tsx has no client routes defined"
|
|
337
|
+
fi
|
|
338
|
+
fi
|
|
339
|
+
|
|
340
|
+
# POST-CHECK: HasQueryFilter anti-pattern
|
|
341
|
+
CONFIG_FILES=$(find src/ -path "*/Configurations/*" -name "*Configuration.cs" 2>/dev/null)
|
|
342
|
+
if [ -n "$CONFIG_FILES" ]; then
|
|
343
|
+
BAD_FILTER=$(grep -Pn 'HasQueryFilter.*Guid\.Empty' $CONFIG_FILES 2>/dev/null)
|
|
344
|
+
if [ -n "$BAD_FILTER" ]; then
|
|
345
|
+
echo "BLOCKING: HasQueryFilter uses Guid.Empty — must use runtime tenant filter"
|
|
346
|
+
echo "$BAD_FILTER"
|
|
347
|
+
exit 1
|
|
348
|
+
fi
|
|
349
|
+
fi
|
|
350
|
+
|
|
351
|
+
# POST-CHECK: PaginatedResult<T> for GetAll
|
|
352
|
+
SERVICE_FILES=$(find src/ -path "*/Services/*" -name "*Service.cs" ! -name "I*Service.cs" 2>/dev/null)
|
|
353
|
+
if [ -n "$SERVICE_FILES" ]; then
|
|
354
|
+
BAD_GETALL=$(grep -Pn 'Task<(List|IEnumerable|IList|ICollection)<' $SERVICE_FILES 2>/dev/null | grep -i "getall")
|
|
355
|
+
if [ -n "$BAD_GETALL" ]; then
|
|
356
|
+
echo "BLOCKING: GetAll methods must return PaginatedResult<T>, not List/IEnumerable"
|
|
357
|
+
echo "$BAD_GETALL"
|
|
358
|
+
exit 1
|
|
359
|
+
fi
|
|
360
|
+
fi
|
|
361
|
+
|
|
362
|
+
# POST-CHECK: i18n required key structure
|
|
363
|
+
FR_I18N_FILES=$(find src/i18n/locales/fr/ -name "*.json" 2>/dev/null)
|
|
364
|
+
if [ -n "$FR_I18N_FILES" ]; then
|
|
365
|
+
for f in $FR_I18N_FILES; do
|
|
366
|
+
BASENAME=$(basename "$f")
|
|
367
|
+
case "$BASENAME" in common.json|navigation.json) continue;; esac
|
|
368
|
+
for KEY in "actions" "labels" "errors"; do
|
|
369
|
+
if ! grep -q "\"$KEY\"" "$f"; then
|
|
370
|
+
echo "WARNING: i18n file missing required key '$KEY': $f"
|
|
371
|
+
fi
|
|
372
|
+
done
|
|
373
|
+
done
|
|
374
|
+
fi
|
|
375
|
+
|
|
376
|
+
# POST-CHECK: IAuditableEntity + Validator pairing
|
|
377
|
+
ENTITY_FILES=$(find src/ -path "*/Domain/Entities/Business/*" -name "*.cs" 2>/dev/null)
|
|
378
|
+
if [ -n "$ENTITY_FILES" ]; then
|
|
379
|
+
for f in $ENTITY_FILES; do
|
|
380
|
+
if grep -q "ITenantEntity" "$f" && ! grep -q "IAuditableEntity" "$f"; then
|
|
381
|
+
echo "WARNING: Entity has ITenantEntity but missing IAuditableEntity: $f"
|
|
382
|
+
fi
|
|
383
|
+
done
|
|
384
|
+
fi
|
|
385
|
+
CREATE_VALIDATORS=$(find src/ -path "*/Validators/*" -name "Create*Validator.cs" 2>/dev/null)
|
|
386
|
+
if [ -n "$CREATE_VALIDATORS" ]; then
|
|
387
|
+
for CV in $CREATE_VALIDATORS; do
|
|
388
|
+
UV=$(echo "$CV" | sed 's/Create/Update/')
|
|
389
|
+
if [ ! -f "$UV" ]; then
|
|
390
|
+
echo "BLOCKING: CreateValidator without matching UpdateValidator: $CV"
|
|
391
|
+
echo "Expected: $UV"
|
|
392
|
+
exit 1
|
|
393
|
+
fi
|
|
394
|
+
done
|
|
395
|
+
fi
|
|
396
|
+
```
|
|
397
|
+
|
|
209
398
|
**Error resolution cycle (ALL categories):**
|
|
210
399
|
1. Read the FULL error output (not just first line)
|
|
211
400
|
2. Identify root cause: file path + line number
|
|
@@ -15,9 +15,12 @@ next_step: steps/step-02-unit-tests.md
|
|
|
15
15
|
ls *.sln 2>/dev/null || find . -maxdepth 2 -name "*.sln" -type f
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
### 2.
|
|
18
|
+
### 2. Cleanup & Build
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
+
# Cleanup corrupted EF Core design-time artifacts (Roslyn BuildHost bug on Windows)
|
|
22
|
+
for d in src/*/bin?Debug; do [ -d "$d" ] && echo "Removing corrupted artifact: $d" && rm -rf "$d"; done
|
|
23
|
+
|
|
21
24
|
dotnet build {SolutionFile} --verbosity minimal
|
|
22
25
|
```
|
|
23
26
|
|
|
@@ -132,6 +132,65 @@ fi
|
|
|
132
132
|
|
|
133
133
|
---
|
|
134
134
|
|
|
135
|
+
## 5b. Verify Dev Seeding Configuration
|
|
136
|
+
|
|
137
|
+
Before checking seed data, verify that dev seeding is actually enabled:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
# Check 1: EnableDevSeeding in appsettings
|
|
141
|
+
APPSETTINGS_DEV=$(ls appsettings.Development.json 2>/dev/null || ls src/*Api*/appsettings.Development.json 2>/dev/null)
|
|
142
|
+
APPSETTINGS_BASE=$(ls appsettings.json 2>/dev/null || ls src/*Api*/appsettings.json 2>/dev/null)
|
|
143
|
+
|
|
144
|
+
SEEDING_EXPLICIT_TRUE=false
|
|
145
|
+
SEEDING_EXPLICIT_FALSE=false
|
|
146
|
+
|
|
147
|
+
# Check Development appsettings first (overrides base)
|
|
148
|
+
if [ -n "$APPSETTINGS_DEV" ]; then
|
|
149
|
+
if grep -q '"EnableDevSeeding".*true' "$APPSETTINGS_DEV" 2>/dev/null; then
|
|
150
|
+
SEEDING_EXPLICIT_TRUE=true
|
|
151
|
+
fi
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
# Check base appsettings
|
|
155
|
+
if [ -n "$APPSETTINGS_BASE" ] && [ "$SEEDING_EXPLICIT_TRUE" = "false" ]; then
|
|
156
|
+
if grep -q '"EnableDevSeeding".*false' "$APPSETTINGS_BASE" 2>/dev/null; then
|
|
157
|
+
SEEDING_EXPLICIT_FALSE=true
|
|
158
|
+
fi
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
# Check 2: Program.cs override pattern
|
|
162
|
+
PROGRAM_CS=$(find . -name "Program.cs" -path "*/Api/*" 2>/dev/null | head -1)
|
|
163
|
+
PROGRAM_OVERRIDE=false
|
|
164
|
+
if [ -n "$PROGRAM_CS" ]; then
|
|
165
|
+
if grep -q 'EnableDevSeeding.*IsDevelopment\|IsDevelopment.*EnableDevSeeding' "$PROGRAM_CS" 2>/dev/null; then
|
|
166
|
+
PROGRAM_OVERRIDE=true
|
|
167
|
+
fi
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
# Evaluate
|
|
171
|
+
if [ "$SEEDING_EXPLICIT_FALSE" = "true" ] && [ "$PROGRAM_OVERRIDE" = "false" ]; then
|
|
172
|
+
echo "⚠️ WARNING: EnableDevSeeding is FALSE in appsettings.json and NO Program.cs override detected"
|
|
173
|
+
echo "DevDataSeeder will NOT execute — test data will be empty"
|
|
174
|
+
echo "Fix option 1: Add to appsettings.Development.json: \"EnableDevSeeding\": true"
|
|
175
|
+
echo "Fix option 2: Add to Program.cs: options.EnableDevSeeding = builder.Environment.IsDevelopment();"
|
|
176
|
+
SEEDING_ENABLED=false
|
|
177
|
+
elif [ "$PROGRAM_OVERRIDE" = "true" ]; then
|
|
178
|
+
echo "✓ EnableDevSeeding: Program.cs overrides to IsDevelopment() — active in Development"
|
|
179
|
+
SEEDING_ENABLED=true
|
|
180
|
+
else
|
|
181
|
+
echo "✓ EnableDevSeeding: explicitly enabled"
|
|
182
|
+
SEEDING_ENABLED=true
|
|
183
|
+
fi
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
| Result | Meaning | Action |
|
|
187
|
+
|--------|---------|--------|
|
|
188
|
+
| Program.cs override detected | PASS | Dev seeding active in Development environment |
|
|
189
|
+
| Explicitly enabled | PASS | Dev seeding active |
|
|
190
|
+
| FALSE + no override | **WARNING** | DevDataSeeder won't run — seed data checks in section 6 will likely fail |
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
135
194
|
## 6. Verify Seed Data Execution
|
|
136
195
|
|
|
137
196
|
```bash
|
|
@@ -182,6 +241,28 @@ if [ "$STARTED" = "true" ]; then
|
|
|
182
241
|
fi
|
|
183
242
|
fi
|
|
184
243
|
|
|
244
|
+
# --- DefaultTenantId cross-schema FK validation ---
|
|
245
|
+
# Verify that SeedConstants.DefaultTenantId exists in core.tenant_Tenants
|
|
246
|
+
SEED_CONSTANTS=$(find . -path "*/SeedConstants.cs" 2>/dev/null | head -1)
|
|
247
|
+
if [ -n "$SEED_CONSTANTS" ]; then
|
|
248
|
+
TENANT_GUID=$(grep -oP 'DefaultTenantId\s*=\s*Guid\.Parse\("([^"]+)"\)' "$SEED_CONSTANTS" | grep -oP '"[^"]+"' | tr -d '"')
|
|
249
|
+
|
|
250
|
+
if [ -n "$TENANT_GUID" ] && [ -n "$DB_NAME" ]; then
|
|
251
|
+
TENANT_EXISTS=$(sqlcmd -S "(localdb)\MSSQLLocalDB" -d "$DB_NAME" -Q "
|
|
252
|
+
SET NOCOUNT ON;
|
|
253
|
+
SELECT COUNT(*) FROM core.tenant_Tenants WHERE Id = '$TENANT_GUID'
|
|
254
|
+
" -h -1 2>/dev/null | tr -d ' ')
|
|
255
|
+
|
|
256
|
+
if [ "$TENANT_EXISTS" = "0" ]; then
|
|
257
|
+
echo "❌ FAIL: SeedConstants.DefaultTenantId ($TENANT_GUID) does NOT exist in core.tenant_Tenants"
|
|
258
|
+
echo "DevDataSeeder inserts rows with an invalid FK — causes runtime 500 errors"
|
|
259
|
+
echo "Fix: Use a TenantId that is seeded by InitializeSmartStackAsync()"
|
|
260
|
+
else
|
|
261
|
+
echo "✓ DefaultTenantId ($TENANT_GUID) exists in core.tenant_Tenants"
|
|
262
|
+
fi
|
|
263
|
+
fi
|
|
264
|
+
fi
|
|
265
|
+
|
|
185
266
|
# Cleanup
|
|
186
267
|
kill $SEED_PID 2>/dev/null
|
|
187
268
|
wait $SEED_PID 2>/dev/null
|
|
@@ -224,6 +305,8 @@ fi
|
|
|
224
305
|
| Pending model changes | PASS/FAIL | dotnet ef migrations has-pending |
|
|
225
306
|
| Migrations apply cleanly | PASS/FAIL | dotnet ef database update on temp DB |
|
|
226
307
|
| Integration tests (SQL Server) | PASS/FAIL | dotnet test --filter Integration |
|
|
308
|
+
| EnableDevSeeding active | PASS/WARN | Program.cs override or appsettings check |
|
|
309
|
+
| DefaultTenantId FK valid | PASS/FAIL | SeedConstants GUID exists in tenant_Tenants|
|
|
227
310
|
| Seed data accessible | PASS/WARN | API endpoints return seeded data |
|
|
228
311
|
| Temp DB cleanup | PASS/WARN | Database dropped |
|
|
229
312
|
|
|
@@ -231,7 +314,7 @@ DB Validation Result: {PASS / FAIL / SKIPPED}
|
|
|
231
314
|
```
|
|
232
315
|
|
|
233
316
|
**Interpretation:**
|
|
234
|
-
- **PASS**: All
|
|
317
|
+
- **PASS**: All 8 checks covered — migrations, SQL Server, LINQ→SQL, isolation, seeding config, tenant FK, seed data verified
|
|
235
318
|
- **FAIL**: One or more checks failed — fix before committing
|
|
236
319
|
- **SKIPPED**: LocalDB not available — tests ran on SQLite only (partial coverage)
|
|
237
320
|
|
|
@@ -248,3 +331,5 @@ DB Validation Result: {PASS / FAIL / SKIPPED}
|
|
|
248
331
|
| `Cannot insert the value NULL into column` | Missing NOT NULL | Add default value or make column nullable |
|
|
249
332
|
| `Login failed for user` | LocalDB auth | Run `sqllocaldb start MSSQLLocalDB` |
|
|
250
333
|
| `A network-related or instance-specific error` | LocalDB not running | Run `sqllocaldb start MSSQLLocalDB` |
|
|
334
|
+
| `DefaultTenantId does NOT exist in core.tenant_Tenants` | Phantom FK | Use a TenantId seeded by `InitializeSmartStackAsync()` |
|
|
335
|
+
| `EnableDevSeeding is FALSE` + empty tables | Seeding disabled | Add `options.EnableDevSeeding = builder.Environment.IsDevelopment()` in Program.cs |
|