@tailor-platform/erp-kit 0.5.1 → 0.6.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/CHANGELOG.md +15 -0
- package/dist/cli.mjs +103 -23
- package/package.json +1 -1
- package/skills/erp-kit-app-5-impl-backend/SKILL.md +7 -4
- package/skills/erp-kit-app-7-impl-review/SKILL.md +1 -1
- package/skills/erp-kit-module-6-impl-review/SKILL.md +39 -17
- package/src/commands/generate-doc.ts +1 -1
- package/src/commands/lib/discovery.test.ts +13 -3
- package/src/commands/lib/discovery.ts +10 -2
- package/src/commands/lib/paths.ts +4 -2
- package/src/commands/lib/sync-check-tests.test.ts +84 -6
- package/src/commands/lib/sync-check-tests.ts +63 -3
- package/src/commands/sync-check.ts +7 -3
- package/src/generator/generate-app-code.ts +51 -16
- package/src/generator/generate-stubs.ts +4 -0
- package/src/generator/stub-templates.test.ts +11 -0
- package/src/generator/stub-templates.ts +22 -1
- package/src/modules/inventory/docs/features/inventory-adjustment.md +2 -1
- package/src/modules/inventory/docs/features/scrap-management.md +39 -1
- package/src/modules/manufacturing/README.md +63 -0
- package/src/modules/manufacturing/command/.gitkeep +0 -0
- package/src/modules/manufacturing/command/activateBillOfMaterial.generated.ts +6 -0
- package/src/modules/manufacturing/command/activateBillOfMaterial.test.ts +166 -0
- package/src/modules/manufacturing/command/activateBillOfMaterial.ts +173 -0
- package/src/modules/manufacturing/command/activateRouting.generated.ts +6 -0
- package/src/modules/manufacturing/command/activateRouting.test.ts +152 -0
- package/src/modules/manufacturing/command/activateRouting.ts +92 -0
- package/src/modules/manufacturing/command/activateWorkCenter.generated.ts +6 -0
- package/src/modules/manufacturing/command/activateWorkCenter.test.ts +135 -0
- package/src/modules/manufacturing/command/activateWorkCenter.ts +91 -0
- package/src/modules/manufacturing/command/cancelProductionOrder.generated.ts +6 -0
- package/src/modules/manufacturing/command/cancelProductionOrder.test.ts +151 -0
- package/src/modules/manufacturing/command/cancelProductionOrder.ts +114 -0
- package/src/modules/manufacturing/command/closeProductionOrder.generated.ts +6 -0
- package/src/modules/manufacturing/command/closeProductionOrder.test.ts +126 -0
- package/src/modules/manufacturing/command/closeProductionOrder.ts +87 -0
- package/src/modules/manufacturing/command/completeProductionOrder.generated.ts +6 -0
- package/src/modules/manufacturing/command/completeProductionOrder.test.ts +132 -0
- package/src/modules/manufacturing/command/completeProductionOrder.ts +97 -0
- package/src/modules/manufacturing/command/completeWorkOrder.generated.ts +6 -0
- package/src/modules/manufacturing/command/completeWorkOrder.test.ts +369 -0
- package/src/modules/manufacturing/command/completeWorkOrder.ts +212 -0
- package/src/modules/manufacturing/command/createBillOfMaterial.generated.ts +6 -0
- package/src/modules/manufacturing/command/createBillOfMaterial.test.ts +210 -0
- package/src/modules/manufacturing/command/createBillOfMaterial.ts +176 -0
- package/src/modules/manufacturing/command/createProductionOrder.generated.ts +6 -0
- package/src/modules/manufacturing/command/createProductionOrder.test.ts +160 -0
- package/src/modules/manufacturing/command/createProductionOrder.ts +129 -0
- package/src/modules/manufacturing/command/createRouting.generated.ts +6 -0
- package/src/modules/manufacturing/command/createRouting.test.ts +168 -0
- package/src/modules/manufacturing/command/createRouting.ts +128 -0
- package/src/modules/manufacturing/command/createWorkCenter.generated.ts +6 -0
- package/src/modules/manufacturing/command/createWorkCenter.test.ts +148 -0
- package/src/modules/manufacturing/command/createWorkCenter.ts +131 -0
- package/src/modules/manufacturing/command/deactivateBillOfMaterial.generated.ts +6 -0
- package/src/modules/manufacturing/command/deactivateBillOfMaterial.test.ts +103 -0
- package/src/modules/manufacturing/command/deactivateBillOfMaterial.ts +78 -0
- package/src/modules/manufacturing/command/deactivateRouting.generated.ts +6 -0
- package/src/modules/manufacturing/command/deactivateRouting.test.ts +112 -0
- package/src/modules/manufacturing/command/deactivateRouting.ts +76 -0
- package/src/modules/manufacturing/command/deactivateWorkCenter.generated.ts +6 -0
- package/src/modules/manufacturing/command/deactivateWorkCenter.test.ts +113 -0
- package/src/modules/manufacturing/command/deactivateWorkCenter.ts +85 -0
- package/src/modules/manufacturing/command/pauseWorkOrder.generated.ts +6 -0
- package/src/modules/manufacturing/command/pauseWorkOrder.test.ts +118 -0
- package/src/modules/manufacturing/command/pauseWorkOrder.ts +82 -0
- package/src/modules/manufacturing/command/recordInventoryIssueOutcome.generated.ts +6 -0
- package/src/modules/manufacturing/command/recordInventoryIssueOutcome.test.ts +183 -0
- package/src/modules/manufacturing/command/recordInventoryIssueOutcome.ts +139 -0
- package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.generated.ts +6 -0
- package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.test.ts +120 -0
- package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.ts +110 -0
- package/src/modules/manufacturing/command/releaseProductionOrder.generated.ts +6 -0
- package/src/modules/manufacturing/command/releaseProductionOrder.test.ts +220 -0
- package/src/modules/manufacturing/command/releaseProductionOrder.ts +450 -0
- package/src/modules/manufacturing/command/reopenProductionOrder.generated.ts +6 -0
- package/src/modules/manufacturing/command/reopenProductionOrder.test.ts +196 -0
- package/src/modules/manufacturing/command/reopenProductionOrder.ts +98 -0
- package/src/modules/manufacturing/command/reportWorkOrderProgress.generated.ts +6 -0
- package/src/modules/manufacturing/command/reportWorkOrderProgress.test.ts +204 -0
- package/src/modules/manufacturing/command/reportWorkOrderProgress.ts +129 -0
- package/src/modules/manufacturing/command/rescheduleProductionOrder.generated.ts +6 -0
- package/src/modules/manufacturing/command/rescheduleProductionOrder.test.ts +185 -0
- package/src/modules/manufacturing/command/rescheduleProductionOrder.ts +95 -0
- package/src/modules/manufacturing/command/resumeWorkOrder.generated.ts +6 -0
- package/src/modules/manufacturing/command/resumeWorkOrder.test.ts +122 -0
- package/src/modules/manufacturing/command/resumeWorkOrder.ts +94 -0
- package/src/modules/manufacturing/command/reviewManufacturingCostSummary.generated.ts +6 -0
- package/src/modules/manufacturing/command/reviewManufacturingCostSummary.test.ts +231 -0
- package/src/modules/manufacturing/command/reviewManufacturingCostSummary.ts +137 -0
- package/src/modules/manufacturing/command/startWorkOrder.generated.ts +6 -0
- package/src/modules/manufacturing/command/startWorkOrder.test.ts +118 -0
- package/src/modules/manufacturing/command/startWorkOrder.ts +126 -0
- package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.generated.ts +6 -0
- package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.test.ts +153 -0
- package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.ts +106 -0
- package/src/modules/manufacturing/command/unreleaseProductionOrder.generated.ts +6 -0
- package/src/modules/manufacturing/command/unreleaseProductionOrder.test.ts +140 -0
- package/src/modules/manufacturing/command/unreleaseProductionOrder.ts +131 -0
- package/src/modules/manufacturing/command/updateBillOfMaterial.generated.ts +6 -0
- package/src/modules/manufacturing/command/updateBillOfMaterial.test.ts +149 -0
- package/src/modules/manufacturing/command/updateBillOfMaterial.ts +174 -0
- package/src/modules/manufacturing/command/updateProductionOrder.generated.ts +6 -0
- package/src/modules/manufacturing/command/updateProductionOrder.test.ts +112 -0
- package/src/modules/manufacturing/command/updateProductionOrder.ts +145 -0
- package/src/modules/manufacturing/command/updateRouting.generated.ts +6 -0
- package/src/modules/manufacturing/command/updateRouting.test.ts +211 -0
- package/src/modules/manufacturing/command/updateRouting.ts +124 -0
- package/src/modules/manufacturing/command/updateWorkCenter.generated.ts +6 -0
- package/src/modules/manufacturing/command/updateWorkCenter.test.ts +152 -0
- package/src/modules/manufacturing/command/updateWorkCenter.ts +137 -0
- package/src/modules/manufacturing/db/.gitkeep +0 -0
- package/src/modules/manufacturing/db/billOfMaterial.ts +70 -0
- package/src/modules/manufacturing/db/billOfMaterialLine.ts +49 -0
- package/src/modules/manufacturing/db/costVarianceLine.ts +53 -0
- package/src/modules/manufacturing/db/manufacturingCostLine.ts +35 -0
- package/src/modules/manufacturing/db/manufacturingCostSettlementRecord.ts +39 -0
- package/src/modules/manufacturing/db/manufacturingCostSummary.ts +59 -0
- package/src/modules/manufacturing/db/productionOrder.ts +83 -0
- package/src/modules/manufacturing/db/productionOrderBomSnapshot.ts +44 -0
- package/src/modules/manufacturing/db/productionOrderCostBaseline.ts +44 -0
- package/src/modules/manufacturing/db/productionOrderMaterialRequirement.ts +57 -0
- package/src/modules/manufacturing/db/productionOrderRoutingSnapshot.ts +43 -0
- package/src/modules/manufacturing/db/routing.ts +63 -0
- package/src/modules/manufacturing/db/routingOperation.ts +57 -0
- package/src/modules/manufacturing/db/workCenter.ts +87 -0
- package/src/modules/manufacturing/db/workOrder.ts +65 -0
- package/src/modules/manufacturing/db/workOrderExecutionEvent.ts +54 -0
- package/src/modules/manufacturing/docs/commands/ActivateBillOfMaterial.md +50 -0
- package/src/modules/manufacturing/docs/commands/ActivateRouting.md +48 -0
- package/src/modules/manufacturing/docs/commands/ActivateWorkCenter.md +49 -0
- package/src/modules/manufacturing/docs/commands/CancelProductionOrder.md +48 -0
- package/src/modules/manufacturing/docs/commands/CloseProductionOrder.md +46 -0
- package/src/modules/manufacturing/docs/commands/CompleteProductionOrder.md +48 -0
- package/src/modules/manufacturing/docs/commands/CompleteWorkOrder.md +66 -0
- package/src/modules/manufacturing/docs/commands/CreateBillOfMaterial.md +54 -0
- package/src/modules/manufacturing/docs/commands/CreateProductionOrder.md +49 -0
- package/src/modules/manufacturing/docs/commands/CreateRouting.md +50 -0
- package/src/modules/manufacturing/docs/commands/CreateWorkCenter.md +51 -0
- package/src/modules/manufacturing/docs/commands/DeactivateBillOfMaterial.md +45 -0
- package/src/modules/manufacturing/docs/commands/DeactivateRouting.md +45 -0
- package/src/modules/manufacturing/docs/commands/DeactivateWorkCenter.md +45 -0
- package/src/modules/manufacturing/docs/commands/PauseWorkOrder.md +44 -0
- package/src/modules/manufacturing/docs/commands/RecordInventoryIssueOutcome.md +59 -0
- package/src/modules/manufacturing/docs/commands/RecordManufacturingCostSettlementAcknowledgment.md +49 -0
- package/src/modules/manufacturing/docs/commands/ReleaseProductionOrder.md +57 -0
- package/src/modules/manufacturing/docs/commands/ReopenProductionOrder.md +54 -0
- package/src/modules/manufacturing/docs/commands/ReportWorkOrderProgress.md +53 -0
- package/src/modules/manufacturing/docs/commands/RescheduleProductionOrder.md +45 -0
- package/src/modules/manufacturing/docs/commands/ResumeWorkOrder.md +44 -0
- package/src/modules/manufacturing/docs/commands/ReviewManufacturingCostSummary.md +52 -0
- package/src/modules/manufacturing/docs/commands/StartWorkOrder.md +46 -0
- package/src/modules/manufacturing/docs/commands/TechnicallyCompleteProductionOrder.md +51 -0
- package/src/modules/manufacturing/docs/commands/UnreleaseProductionOrder.md +46 -0
- package/src/modules/manufacturing/docs/commands/UpdateBillOfMaterial.md +48 -0
- package/src/modules/manufacturing/docs/commands/UpdateProductionOrder.md +48 -0
- package/src/modules/manufacturing/docs/commands/UpdateRouting.md +52 -0
- package/src/modules/manufacturing/docs/commands/UpdateWorkCenter.md +48 -0
- package/src/modules/manufacturing/docs/features/bill-of-material-management.md +83 -0
- package/src/modules/manufacturing/docs/features/manufacturing-cost-and-variance.md +191 -0
- package/src/modules/manufacturing/docs/features/production-order-lifecycle.md +103 -0
- package/src/modules/manufacturing/docs/features/routing-and-work-center-definition.md +63 -0
- package/src/modules/manufacturing/docs/features/work-order-execution.md +115 -0
- package/src/modules/manufacturing/docs/models/BillOfMaterial.md +60 -0
- package/src/modules/manufacturing/docs/models/ManufacturingCostSummary.md +66 -0
- package/src/modules/manufacturing/docs/models/ProductionOrder.md +76 -0
- package/src/modules/manufacturing/docs/models/Routing.md +58 -0
- package/src/modules/manufacturing/docs/models/WorkCenter.md +56 -0
- package/src/modules/manufacturing/docs/models/WorkOrder.md +63 -0
- package/src/modules/manufacturing/docs/queries/DetectBillOfMaterialCircularReference.md +39 -0
- package/src/modules/manufacturing/docs/queries/ExplodeBillOfMaterial.md +56 -0
- package/src/modules/manufacturing/docs/queries/GetBillOfMaterial.md +37 -0
- package/src/modules/manufacturing/docs/queries/GetManufacturingCostSummary.md +39 -0
- package/src/modules/manufacturing/docs/queries/GetProductionOrder.md +37 -0
- package/src/modules/manufacturing/docs/queries/GetRouting.md +39 -0
- package/src/modules/manufacturing/docs/queries/GetWorkCenter.md +35 -0
- package/src/modules/manufacturing/docs/queries/GetWorkOrder.md +38 -0
- package/src/modules/manufacturing/docs/queries/ListBillOfMaterialsByItem.md +42 -0
- package/src/modules/manufacturing/docs/queries/ListManufacturingCostSummariesByStatus.md +41 -0
- package/src/modules/manufacturing/docs/queries/ListProductionOrdersByStatus.md +41 -0
- package/src/modules/manufacturing/docs/queries/ListRoutingsByItem.md +42 -0
- package/src/modules/manufacturing/docs/queries/ListWorkCentersBySite.md +38 -0
- package/src/modules/manufacturing/docs/queries/ListWorkOrdersByProductionOrder.md +39 -0
- package/src/modules/manufacturing/docs/queries/ListWorkOrdersByWorkCenter.md +43 -0
- package/src/modules/manufacturing/executor/.gitkeep +0 -0
- package/src/modules/manufacturing/generated/enums.ts +113 -0
- package/src/modules/manufacturing/generated/kysely-tailordb.ts +247 -0
- package/src/modules/manufacturing/index.ts +2 -0
- package/src/modules/manufacturing/lib/_db_deps.ts +22 -0
- package/src/modules/manufacturing/lib/errors.generated.ts +592 -0
- package/src/modules/manufacturing/lib/permissions.generated.ts +35 -0
- package/src/modules/manufacturing/lib/types.ts +111 -0
- package/src/modules/manufacturing/module.ts +226 -0
- package/src/modules/manufacturing/permissions.ts +3 -0
- package/src/modules/manufacturing/query/.gitkeep +0 -0
- package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.generated.ts +5 -0
- package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.test.ts +115 -0
- package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.ts +79 -0
- package/src/modules/manufacturing/query/explodeBillOfMaterial.generated.ts +5 -0
- package/src/modules/manufacturing/query/explodeBillOfMaterial.test.ts +445 -0
- package/src/modules/manufacturing/query/explodeBillOfMaterial.ts +306 -0
- package/src/modules/manufacturing/query/getBillOfMaterial.generated.ts +5 -0
- package/src/modules/manufacturing/query/getBillOfMaterial.test.ts +64 -0
- package/src/modules/manufacturing/query/getBillOfMaterial.ts +27 -0
- package/src/modules/manufacturing/query/getManufacturingCostSummary.generated.ts +5 -0
- package/src/modules/manufacturing/query/getManufacturingCostSummary.test.ts +147 -0
- package/src/modules/manufacturing/query/getManufacturingCostSummary.ts +46 -0
- package/src/modules/manufacturing/query/getProductionOrder.generated.ts +5 -0
- package/src/modules/manufacturing/query/getProductionOrder.test.ts +139 -0
- package/src/modules/manufacturing/query/getProductionOrder.ts +84 -0
- package/src/modules/manufacturing/query/getRouting.generated.ts +5 -0
- package/src/modules/manufacturing/query/getRouting.test.ts +71 -0
- package/src/modules/manufacturing/query/getRouting.ts +34 -0
- package/src/modules/manufacturing/query/getWorkCenter.generated.ts +5 -0
- package/src/modules/manufacturing/query/getWorkCenter.test.ts +37 -0
- package/src/modules/manufacturing/query/getWorkCenter.ts +21 -0
- package/src/modules/manufacturing/query/getWorkOrder.generated.ts +5 -0
- package/src/modules/manufacturing/query/getWorkOrder.test.ts +73 -0
- package/src/modules/manufacturing/query/getWorkOrder.ts +28 -0
- package/src/modules/manufacturing/query/listBillOfMaterialsByItem.generated.ts +5 -0
- package/src/modules/manufacturing/query/listBillOfMaterialsByItem.test.ts +107 -0
- package/src/modules/manufacturing/query/listBillOfMaterialsByItem.ts +58 -0
- package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.generated.ts +5 -0
- package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.test.ts +96 -0
- package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.ts +77 -0
- package/src/modules/manufacturing/query/listProductionOrdersByStatus.generated.ts +5 -0
- package/src/modules/manufacturing/query/listProductionOrdersByStatus.test.ts +121 -0
- package/src/modules/manufacturing/query/listProductionOrdersByStatus.ts +83 -0
- package/src/modules/manufacturing/query/listRoutingsByItem.generated.ts +5 -0
- package/src/modules/manufacturing/query/listRoutingsByItem.test.ts +110 -0
- package/src/modules/manufacturing/query/listRoutingsByItem.ts +54 -0
- package/src/modules/manufacturing/query/listWorkCentersBySite.generated.ts +5 -0
- package/src/modules/manufacturing/query/listWorkCentersBySite.test.ts +81 -0
- package/src/modules/manufacturing/query/listWorkCentersBySite.ts +70 -0
- package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.generated.ts +5 -0
- package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.test.ts +102 -0
- package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.ts +53 -0
- package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.generated.ts +5 -0
- package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.test.ts +143 -0
- package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.ts +56 -0
- package/src/modules/manufacturing/seed/index.ts +19 -0
- package/src/modules/manufacturing/tailor.config.ts +13 -0
- package/src/modules/manufacturing/tailor.d.ts +13 -0
- package/src/modules/manufacturing/testing/commandTestUtils.ts +29 -0
- package/src/modules/manufacturing/testing/fixtures.ts +402 -0
- package/templates/scaffold/app/backend/package.json +9 -2
- package/templates/scaffold/app/backend/src/tests/utils/graphql-client.ts +66 -0
- package/templates/scaffold/app/backend/src/tests/utils/setup.ts +21 -0
- package/templates/scaffold/app/backend/tsconfig.json +9 -2
- package/templates/scaffold/app/backend/vitest.config.ts +35 -0
- package/templates/scaffold/app/frontend/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @tailor-platform/erp-kit
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0cb94dc: - Add manufacturing module with BOM, work orders, and production tracking
|
|
8
|
+
- Add story test case sync-check and stub generation for `erp-kit test` command
|
|
9
|
+
- Update workspace creation instructions and deployment steps in skills
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 0cb94dc: - Fix relative shared imports to use package self-reference for consistent module resolution
|
|
14
|
+
- Fix priority calibration in requirements review skill to improve accuracy
|
|
15
|
+
- Fix severity validation in module-2 requirements review skill
|
|
16
|
+
- Update module-4-plan-review skill and regenerate .agents/skills
|
|
17
|
+
|
|
3
18
|
## 0.5.1
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/dist/cli.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
import { arg, defineCommand, runMain } from "politty";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import chalk from "chalk";
|
|
6
|
-
import fs, { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
6
|
+
import fs, { existsSync, globSync, readFileSync, readdirSync } from "node:fs";
|
|
7
7
|
import path, { basename, dirname, join, relative, resolve } from "node:path";
|
|
8
8
|
import { execFile, execSync } from "node:child_process";
|
|
9
9
|
import fg from "fast-glob";
|
|
@@ -452,11 +452,12 @@ const MODULE_PATHS = {
|
|
|
452
452
|
}
|
|
453
453
|
};
|
|
454
454
|
const APP_PATHS = {
|
|
455
|
-
|
|
455
|
+
tests: { stories: "backend/src/tests/stories" },
|
|
456
456
|
docs: {
|
|
457
457
|
resolver: "docs/resolver",
|
|
458
458
|
actors: "docs/actors",
|
|
459
459
|
businessFlow: "docs/business-flow",
|
|
460
|
+
story: "story",
|
|
460
461
|
screen: "docs/screen"
|
|
461
462
|
}
|
|
462
463
|
};
|
|
@@ -492,7 +493,14 @@ function appComposeCategories(root) {
|
|
|
492
493
|
exclusions: [/\.test\.ts$/, /^index\.ts$/]
|
|
493
494
|
}];
|
|
494
495
|
}
|
|
495
|
-
function
|
|
496
|
+
function appTestCaseCategories(root) {
|
|
497
|
+
return {
|
|
498
|
+
name: "story-test-case",
|
|
499
|
+
docPattern: `${root}/*/${APP_PATHS.docs.businessFlow}/*/${APP_PATHS.docs.story}/*.md`,
|
|
500
|
+
testDir: APP_PATHS.tests.stories
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
function moduleTestCaseCategories(root) {
|
|
496
504
|
return [{
|
|
497
505
|
name: "command-test-case",
|
|
498
506
|
docPattern: `${root}/*/${MODULE_PATHS.docs.commands}/*.md`,
|
|
@@ -536,7 +544,7 @@ function buildCheckTargets(config) {
|
|
|
536
544
|
glob: `${a}/[a-zA-Z]*/${APP_PATHS.docs.businessFlow}/*/README.md`,
|
|
537
545
|
schemaKey: "business-flow"
|
|
538
546
|
}, {
|
|
539
|
-
glob: `${a}/[a-zA-Z]*/${APP_PATHS.docs.businessFlow}/*/${APP_PATHS.
|
|
547
|
+
glob: `${a}/[a-zA-Z]*/${APP_PATHS.docs.businessFlow}/*/${APP_PATHS.docs.story}/*.md`,
|
|
540
548
|
schemaKey: "story"
|
|
541
549
|
}, {
|
|
542
550
|
glob: `${a}/[a-zA-Z]*/${APP_PATHS.docs.screen}/*.md`,
|
|
@@ -742,9 +750,9 @@ function parseItDescriptionsFromTest(content) {
|
|
|
742
750
|
function toCamelCase(pascalCase) {
|
|
743
751
|
return pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1);
|
|
744
752
|
}
|
|
745
|
-
async function
|
|
753
|
+
async function runModuleTestCaseSyncCheck(root, cwd) {
|
|
746
754
|
const errors = [];
|
|
747
|
-
const categories =
|
|
755
|
+
const categories = moduleTestCaseCategories(root);
|
|
748
756
|
for (const category of categories) {
|
|
749
757
|
const docPaths = await fg(category.docPattern, { cwd });
|
|
750
758
|
for (const docPath of docPaths) {
|
|
@@ -780,15 +788,57 @@ async function runTestCaseSyncCheck(root, cwd) {
|
|
|
780
788
|
}
|
|
781
789
|
return errors;
|
|
782
790
|
}
|
|
791
|
+
function resolveStoryTestPath(docPath, testDir) {
|
|
792
|
+
const match = /^(.+)\/docs\/business-flow\/([^/]+)\/story\/([^/]+)\.md$/.exec(docPath);
|
|
793
|
+
if (!match) return null;
|
|
794
|
+
const [, appPath, flow, name] = match;
|
|
795
|
+
return `${appPath}/${testDir}/${flow}/${name}.test.ts`;
|
|
796
|
+
}
|
|
797
|
+
async function runAppTestCaseSyncCheck(appRoot, cwd) {
|
|
798
|
+
const errors = [];
|
|
799
|
+
const config = appTestCaseCategories(appRoot);
|
|
800
|
+
const docPaths = await fg(config.docPattern, { cwd });
|
|
801
|
+
for (const docPath of docPaths) {
|
|
802
|
+
const docFullPath = path.join(cwd, docPath);
|
|
803
|
+
const docTestCases = parseTestCasesFromDoc(fs.readFileSync(docFullPath, "utf-8"));
|
|
804
|
+
if (docTestCases.length === 0) continue;
|
|
805
|
+
const testPath = resolveStoryTestPath(docPath, config.testDir);
|
|
806
|
+
if (!testPath) continue;
|
|
807
|
+
const testFullPath = path.join(cwd, testPath);
|
|
808
|
+
if (!fs.existsSync(testFullPath)) continue;
|
|
809
|
+
const itDescriptions = parseItDescriptionsFromTest(fs.readFileSync(testFullPath, "utf-8"));
|
|
810
|
+
const docSet = new Set(docTestCases);
|
|
811
|
+
const testSet = new Set(itDescriptions);
|
|
812
|
+
for (const docCase of docSet) if (!testSet.has(docCase)) errors.push({
|
|
813
|
+
type: "missing-test-case",
|
|
814
|
+
category: config.name,
|
|
815
|
+
docPath,
|
|
816
|
+
sourcePath: testPath,
|
|
817
|
+
expectedBasename: docCase
|
|
818
|
+
});
|
|
819
|
+
for (const testCase of testSet) if (!docSet.has(testCase)) errors.push({
|
|
820
|
+
type: "extra-test-case",
|
|
821
|
+
category: config.name,
|
|
822
|
+
docPath,
|
|
823
|
+
sourcePath: testPath,
|
|
824
|
+
expectedBasename: testCase
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
return errors;
|
|
828
|
+
}
|
|
783
829
|
//#endregion
|
|
784
830
|
//#region src/commands/sync-check.ts
|
|
785
831
|
async function runSyncCheck(config, cwd) {
|
|
786
832
|
const sourceResult = await runSourceSyncCheck(config, cwd);
|
|
787
833
|
if (config.modulesRoot) {
|
|
788
|
-
const testCaseErrors = await
|
|
834
|
+
const testCaseErrors = await runModuleTestCaseSyncCheck(config.modulesRoot, cwd);
|
|
789
835
|
sourceResult.errors.push(...testCaseErrors);
|
|
790
|
-
sourceResult.exitCode = sourceResult.errors.length > 0 ? 1 : 0;
|
|
791
836
|
}
|
|
837
|
+
if (config.appRoot) {
|
|
838
|
+
const storyErrors = await runAppTestCaseSyncCheck(config.appRoot, cwd);
|
|
839
|
+
sourceResult.errors.push(...storyErrors);
|
|
840
|
+
}
|
|
841
|
+
sourceResult.exitCode = sourceResult.errors.length > 0 ? 1 : 0;
|
|
792
842
|
return sourceResult;
|
|
793
843
|
}
|
|
794
844
|
function formatSyncCheckReport(result) {
|
|
@@ -1092,10 +1142,22 @@ describe("${name}", () => {
|
|
|
1092
1142
|
expect(resolver.default.name).toBe("${name}");
|
|
1093
1143
|
});
|
|
1094
1144
|
});
|
|
1145
|
+
`;
|
|
1146
|
+
} },
|
|
1147
|
+
storyTest: { render: (name, testCases) => {
|
|
1148
|
+
return `import { describe, expect, inject, it } from "vitest";
|
|
1149
|
+
import { createGraphQLClient } from "@/tests/utils/graphql-client";
|
|
1150
|
+
|
|
1151
|
+
describe("${name}", () => {
|
|
1152
|
+
const graphQLClient = createGraphQLClient(inject("url"), inject("token"));
|
|
1153
|
+
|
|
1154
|
+
${testCases.map((tc) => ` it("${tc}", async () => {\n // TODO: implement\n });`).join("\n\n")}
|
|
1155
|
+
});
|
|
1095
1156
|
`;
|
|
1096
1157
|
} }
|
|
1097
1158
|
};
|
|
1098
|
-
function renderStub(type, name) {
|
|
1159
|
+
function renderStub(type, name, testCases) {
|
|
1160
|
+
if (type === "storyTest") return templates.storyTest.render(name, testCases);
|
|
1099
1161
|
return templates[type].render(name);
|
|
1100
1162
|
}
|
|
1101
1163
|
//#endregion
|
|
@@ -1121,6 +1183,9 @@ function generateResolverStub(resolverName) {
|
|
|
1121
1183
|
function generateResolverTestStub(resolverName) {
|
|
1122
1184
|
return renderStub("resolverTest", resolverName);
|
|
1123
1185
|
}
|
|
1186
|
+
function generateStoryTestStub(name, testCases) {
|
|
1187
|
+
return renderStub("storyTest", name, testCases);
|
|
1188
|
+
}
|
|
1124
1189
|
//#endregion
|
|
1125
1190
|
//#region src/generator/scaffold.ts
|
|
1126
1191
|
function copyTemplateDir(srcDir, destDir, replacements, placeholderFiles) {
|
|
@@ -1314,7 +1379,7 @@ function resolveDocPath(type, name, modulePath) {
|
|
|
1314
1379
|
if (type === "story") {
|
|
1315
1380
|
const parts = name.split("/");
|
|
1316
1381
|
if (parts.length !== 2) throw new Error(`Story name must be "<flow>/<story>" (e.g., "onboarding/admin--create-user")`);
|
|
1317
|
-
return path.join(modulePath, APP_PATHS.docs.businessFlow, parts[0], APP_PATHS.
|
|
1382
|
+
return path.join(modulePath, APP_PATHS.docs.businessFlow, parts[0], APP_PATHS.docs.story, `${parts[1]}.md`);
|
|
1318
1383
|
}
|
|
1319
1384
|
if (MODULE_DIR_MAP[type]) return path.join(modulePath, MODULE_DIR_MAP[type], `${name}.md`);
|
|
1320
1385
|
if (APP_DIR_MAP[type]) return path.join(modulePath, APP_DIR_MAP[type], `${name}.md`);
|
|
@@ -1540,19 +1605,16 @@ function parseExceptionHandlingFromRaw(markdown) {
|
|
|
1540
1605
|
function runGenerateAppCode(appPath) {
|
|
1541
1606
|
const appName = path.basename(appPath);
|
|
1542
1607
|
scaffoldAppBoilerplate(appPath, appName);
|
|
1608
|
+
generateResolverStubs(appPath, appName);
|
|
1609
|
+
generateStoryTestStubs(appPath, appName);
|
|
1610
|
+
return 0;
|
|
1611
|
+
}
|
|
1612
|
+
function generateResolverStubs(appPath, appName) {
|
|
1543
1613
|
const docsDir = path.join(appPath, "docs", "resolver");
|
|
1544
|
-
|
|
1545
|
-
if (!fs.existsSync(docsDir)) {
|
|
1546
|
-
console.log(`No docs/resolver/ directory found — skipping resolver generation`);
|
|
1547
|
-
console.log(`Generated boilerplate for ${appName}`);
|
|
1548
|
-
return 0;
|
|
1549
|
-
}
|
|
1614
|
+
if (!fs.existsSync(docsDir)) return;
|
|
1550
1615
|
const mdFiles = fs.readdirSync(docsDir).filter((f) => f.endsWith(".md"));
|
|
1551
|
-
if (mdFiles.length === 0)
|
|
1552
|
-
|
|
1553
|
-
console.log(`Generated boilerplate for ${appName}`);
|
|
1554
|
-
return 0;
|
|
1555
|
-
}
|
|
1616
|
+
if (mdFiles.length === 0) return;
|
|
1617
|
+
const resolverDir = path.join(appPath, "backend", "src", "resolvers");
|
|
1556
1618
|
fs.mkdirSync(resolverDir, { recursive: true });
|
|
1557
1619
|
let generated = 0;
|
|
1558
1620
|
for (const file of mdFiles) {
|
|
@@ -1570,8 +1632,26 @@ function runGenerateAppCode(appPath) {
|
|
|
1570
1632
|
console.log(` scaffolded ${path.relative(appPath, testFile)}`);
|
|
1571
1633
|
}
|
|
1572
1634
|
}
|
|
1573
|
-
console.log(`Scaffolded ${generated} resolver(s) for ${appName}`);
|
|
1574
|
-
|
|
1635
|
+
if (generated > 0) console.log(`Scaffolded ${generated} resolver(s) for ${appName}`);
|
|
1636
|
+
}
|
|
1637
|
+
function generateStoryTestStubs(appPath, appName) {
|
|
1638
|
+
const storyDocs = globSync(path.join(appPath, "docs/business-flow/*/story/*.md"));
|
|
1639
|
+
let generated = 0;
|
|
1640
|
+
for (const storyDocPath of storyDocs) {
|
|
1641
|
+
const testCases = parseTestCasesFromDoc(fs.readFileSync(storyDocPath, "utf-8"));
|
|
1642
|
+
if (testCases.length === 0) continue;
|
|
1643
|
+
const match = /\/docs\/business-flow\/([^/]+)\/story\/([^/]+)\.md$/.exec(storyDocPath);
|
|
1644
|
+
if (!match) continue;
|
|
1645
|
+
const [, flow, storyName] = match;
|
|
1646
|
+
const testDir = path.join(appPath, APP_PATHS.tests.stories, flow);
|
|
1647
|
+
const testFile = path.join(testDir, `${storyName}.test.ts`);
|
|
1648
|
+
if (fs.existsSync(testFile)) continue;
|
|
1649
|
+
fs.mkdirSync(testDir, { recursive: true });
|
|
1650
|
+
fs.writeFileSync(testFile, generateStoryTestStub(storyName, testCases));
|
|
1651
|
+
console.log(` scaffolded ${path.relative(appPath, testFile)}`);
|
|
1652
|
+
generated++;
|
|
1653
|
+
}
|
|
1654
|
+
if (generated > 0) console.log(`Scaffolded ${generated} story test(s) for ${appName}`);
|
|
1575
1655
|
}
|
|
1576
1656
|
//#endregion
|
|
1577
1657
|
//#region src/shared/uuidv5.ts
|
package/package.json
CHANGED
|
@@ -90,7 +90,10 @@ For each resolver, check whether its spec (`docs/resolver/*.md`) documents error
|
|
|
90
90
|
|
|
91
91
|
Do not directly mutate module-owned tables via Kysely — always use module commands.
|
|
92
92
|
|
|
93
|
+
After resolvers are done, implement the story integration test stubs generated in Phase 1b (`backend/src/tests/stories/`). Each stub has `it()` descriptions from the story doc's `## Test Cases`. Use the scaffolded GraphQL client and `inject("url")` / `inject("token")` to call resolvers. Run `pnpm test:integration` and `pnpm erp-kit app sync-check` to verify.
|
|
94
|
+
|
|
93
95
|
> **Parallelize if possible:** Dispatch one agent per resolver. Each agent writes only to its own files:
|
|
96
|
+
>
|
|
94
97
|
> - `src/resolvers/<resolverName>.ts`
|
|
95
98
|
> - `src/executors/<resolverName>.ts`
|
|
96
99
|
>
|
|
@@ -107,10 +110,10 @@ Do not directly mutate module-owned tables via Kysely — always use module comm
|
|
|
107
110
|
|
|
108
111
|
Frontend implementation requires a deployed backend (for GraphQL schema generation). Prompt the user to:
|
|
109
112
|
|
|
110
|
-
1. Create a workspace and configure `.env` (including `TAILOR_PLATFORM_WORKSPACE_ID`)
|
|
111
|
-
2. Run `pnpm deploy` and `pnpm generate`
|
|
112
|
-
3. Run `pnpm seed` if seed data was created
|
|
113
|
-
4. Retrieve OAuth2 client ID via `tailor-sdk oauth2client get default --json` for frontend `.env`
|
|
113
|
+
1. Create a workspace via `tailor-sdk workspace create --name <NAME> --region <us-west|asia-northeast>` and configure `.env` (including `TAILOR_PLATFORM_WORKSPACE_ID`)
|
|
114
|
+
2. Run `pnpm run deploy` and `pnpm run generate`
|
|
115
|
+
3. Run `pnpm run seed` if seed data was created
|
|
116
|
+
4. Retrieve OAuth2 client ID via `tailor-sdk oauth2client get default --json --env-file-if-exists .env` for frontend `.env`
|
|
114
117
|
|
|
115
118
|
Wait for the user to confirm deployment is complete before proceeding.
|
|
116
119
|
|
|
@@ -63,7 +63,7 @@ cd <APP_ROOT>/frontend && pnpm lint && pnpm typecheck && pnpm build
|
|
|
63
63
|
erp-kit app sync-check -p <APP_ROOT>/..
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
Report pass/fail for each check. `sync-check` detects missing resolver implementations
|
|
66
|
+
Report pass/fail for each check. `sync-check` detects missing resolver implementations, orphaned docs, and story doc ↔ integration test case mismatches, so groups A-C agents can skip file-existence checks and focus on content parity.
|
|
67
67
|
|
|
68
68
|
## Step 3: Aggregate Results
|
|
69
69
|
|
|
@@ -31,35 +31,57 @@ Define shared context for all agents:
|
|
|
31
31
|
|
|
32
32
|
Verify at least `MODEL_DOCS`, `COMMAND_DOCS`, or `QUERY_DOCS` is non-empty. If no docs exist, stop with: "No docs found for module <MODULE_NAME>."
|
|
33
33
|
|
|
34
|
-
## Step 2: Dispatch Agents (parallelize
|
|
34
|
+
## Step 2: Dispatch Agents (parallelize ALL agents in a single message)
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
Each agent receives: MODULE_NAME, all relevant doc/code/test file paths for its domain.
|
|
38
|
-
Each agent runs all parity perspectives (doc→code, error, doc→test) internally and returns: structured JSON per [references/impl-parity-report-format.md](references/impl-parity-report-format.md).
|
|
39
|
-
If a doc or code directory is empty, the agent reports "no files found" and skips.
|
|
36
|
+
Split each parity check into its own agent, then further split by file batches when there are many files. Target **~5 agents per domain** (model, command, query) for maximum parallelism. Launch **ALL agents in a single message** — do NOT wait for one domain to finish before starting another.
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
If a doc or code directory is empty, skip that agent entirely.
|
|
39
|
+
|
|
40
|
+
### Splitting Strategy
|
|
41
|
+
|
|
42
|
+
1. **Each parity check type = separate agent** (never combine different check types in one agent)
|
|
43
|
+
2. **File batching**: If a check has more than 5 doc files, split docs into batches of 3-5 files each. Each batch becomes its own agent running the same check type on a subset of files.
|
|
44
|
+
3. **Corresponding code/test files**: When batching by doc files, include only the matching code and test files for those specific docs (match by filename stem, e.g., `docs/commands/createFoo.md` → `command/createFoo.ts` + `command/createFoo.test.ts`).
|
|
45
|
+
|
|
46
|
+
### Agent Table
|
|
47
|
+
|
|
48
|
+
| Check Type | Domain | Prompt Template | Inputs per agent |
|
|
49
|
+
| ---------- | ------- | ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
50
|
+
| M-1 | Model | [references/model-doc-code-parity.md](references/model-doc-code-parity.md) | MODEL_DOCS (batch), MODEL_CODE (matching) |
|
|
51
|
+
| C-1 | Command | [references/command-doc-code-parity.md](references/command-doc-code-parity.md) | COMMAND_DOCS (batch), COMMAND_CODE (matching) |
|
|
52
|
+
| C-2 | Command | [references/command-error-implementation-parity.md](references/command-error-implementation-parity.md) | COMMAND_DOCS (batch), COMMAND_CODE (matching), ERROR_DEFS |
|
|
53
|
+
| C-3 | Command | [references/command-doc-test-parity.md](references/command-doc-test-parity.md) | COMMAND_DOCS (batch), COMMAND_TEST_CODE (matching) |
|
|
54
|
+
| Q-1 | Query | [references/query-doc-code-parity.md](references/query-doc-code-parity.md) | QUERY_DOCS (batch), QUERY_CODE (matching) |
|
|
55
|
+
| Q-2 | Query | [references/query-error-implementation-parity.md](references/query-error-implementation-parity.md) | QUERY_DOCS (batch), QUERY_CODE (matching), ERROR_DEFS |
|
|
56
|
+
| Q-3 | Query | [references/query-doc-test-parity.md](references/query-doc-test-parity.md) | QUERY_DOCS (batch), QUERY_TEST_CODE (matching) |
|
|
57
|
+
|
|
58
|
+
### Batching Example
|
|
59
|
+
|
|
60
|
+
If a module has 8 command docs: split into 3 batches (3+3+2). Each batch spawns 3 agents (C-1, C-2, C-3) → 9 command agents total. If it has 4 command docs: no batching needed, 3 agents (C-1, C-2, C-3).
|
|
61
|
+
|
|
62
|
+
For model docs: if 6 models → 2 batches (3+3) → 2 agents (M-1 × 2). If 3 models → 1 agent.
|
|
63
|
+
|
|
64
|
+
### Agent Dispatch
|
|
46
65
|
|
|
47
66
|
For each agent:
|
|
48
67
|
|
|
49
|
-
1. Read
|
|
68
|
+
1. Read the prompt template file for that check type
|
|
50
69
|
2. Replace `{{MODULE_NAME}}` with the resolved module name
|
|
51
|
-
3. Replace `{{MODEL_DOCS}}`, `{{COMMAND_DOCS}}`, `{{QUERY_DOCS}}`, `{{MODEL_CODE}}`, `{{COMMAND_CODE}}`, `{{QUERY_CODE}}`, `{{COMMAND_TEST_CODE}}`, `{{QUERY_TEST_CODE}}`, `{{ERROR_DEFS}}` with the actual file paths
|
|
52
|
-
4.
|
|
70
|
+
3. Replace template placeholders (`{{MODEL_DOCS}}`, `{{COMMAND_DOCS}}`, `{{QUERY_DOCS}}`, `{{MODEL_CODE}}`, `{{COMMAND_CODE}}`, `{{QUERY_CODE}}`, `{{COMMAND_TEST_CODE}}`, `{{QUERY_TEST_CODE}}`, `{{ERROR_DEFS}}`) with the actual file paths **for this batch only**
|
|
71
|
+
4. Dispatch the agent with the filled prompt
|
|
72
|
+
|
|
73
|
+
**IMPORTANT**: Launch ALL agents across ALL domains in a single parallel message. Do not serialize by domain.
|
|
53
74
|
|
|
54
75
|
## Step 3: Aggregate Results
|
|
55
76
|
|
|
56
77
|
After ALL agents return:
|
|
57
78
|
|
|
58
79
|
1. Collect the JSON results from each agent
|
|
59
|
-
2. Merge
|
|
60
|
-
3. Merge all `
|
|
61
|
-
4.
|
|
62
|
-
5.
|
|
80
|
+
2. **Merge batches**: If a check type was split across multiple batches (e.g., C-1 batch 1 + C-1 batch 2), merge their `gaps[]` and `inconsistencies[]` into a single result per check type
|
|
81
|
+
3. Merge all `gaps[]` arrays across all check types into a single list
|
|
82
|
+
4. Merge all `inconsistencies[]` arrays into a single list
|
|
83
|
+
5. Deduplicate: if two gaps share the same `source + target + check`, keep only one
|
|
84
|
+
6. Calculate totals across all summaries
|
|
63
85
|
|
|
64
86
|
## Step 4: Severity Validation
|
|
65
87
|
|
|
@@ -2,7 +2,8 @@ import { describe, it, expect } from "vitest";
|
|
|
2
2
|
import {
|
|
3
3
|
moduleCategories,
|
|
4
4
|
appComposeCategories,
|
|
5
|
-
|
|
5
|
+
moduleTestCaseCategories,
|
|
6
|
+
appTestCaseCategories,
|
|
6
7
|
buildCheckTargets,
|
|
7
8
|
} from "./discovery";
|
|
8
9
|
|
|
@@ -35,15 +36,24 @@ describe("appComposeCategories", () => {
|
|
|
35
36
|
});
|
|
36
37
|
});
|
|
37
38
|
|
|
38
|
-
describe("
|
|
39
|
+
describe("moduleTestCaseCategories", () => {
|
|
39
40
|
it("produces command and query test case categories", () => {
|
|
40
|
-
const cats =
|
|
41
|
+
const cats = moduleTestCaseCategories("modules");
|
|
41
42
|
expect(cats).toHaveLength(2);
|
|
42
43
|
expect(cats[0].testDir).toBe("command");
|
|
43
44
|
expect(cats[1].testDir).toBe("query");
|
|
44
45
|
});
|
|
45
46
|
});
|
|
46
47
|
|
|
48
|
+
describe("appTestCaseCategories", () => {
|
|
49
|
+
it("produces story test case category with correct pattern", () => {
|
|
50
|
+
const cats = appTestCaseCategories("apps");
|
|
51
|
+
expect(cats.name).toBe("story-test-case");
|
|
52
|
+
expect(cats.docPattern).toBe("apps/*/docs/business-flow/*/story/*.md");
|
|
53
|
+
expect(cats.testDir).toBe("backend/src/tests/stories");
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
47
57
|
describe("buildCheckTargets", () => {
|
|
48
58
|
it("builds module check targets", () => {
|
|
49
59
|
const targets = buildCheckTargets({ modulesRoot: "modules" });
|
|
@@ -52,7 +52,15 @@ export function appComposeCategories(root: string): CategoryConfig[] {
|
|
|
52
52
|
];
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
export function
|
|
55
|
+
export function appTestCaseCategories(root: string): TestCaseCategoryConfig {
|
|
56
|
+
return {
|
|
57
|
+
name: "story-test-case",
|
|
58
|
+
docPattern: `${root}/*/${APP_PATHS.docs.businessFlow}/*/${APP_PATHS.docs.story}/*.md`,
|
|
59
|
+
testDir: APP_PATHS.tests.stories,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function moduleTestCaseCategories(root: string): TestCaseCategoryConfig[] {
|
|
56
64
|
return [
|
|
57
65
|
{
|
|
58
66
|
name: "command-test-case",
|
|
@@ -94,7 +102,7 @@ export function buildCheckTargets(config: {
|
|
|
94
102
|
schemaKey: "business-flow",
|
|
95
103
|
},
|
|
96
104
|
{
|
|
97
|
-
glob: `${a}/[a-zA-Z]*/${APP_PATHS.docs.businessFlow}/*/${APP_PATHS.
|
|
105
|
+
glob: `${a}/[a-zA-Z]*/${APP_PATHS.docs.businessFlow}/*/${APP_PATHS.docs.story}/*.md`,
|
|
98
106
|
schemaKey: "story",
|
|
99
107
|
},
|
|
100
108
|
{ glob: `${a}/[a-zA-Z]*/${APP_PATHS.docs.screen}/*.md`, schemaKey: "screen" },
|
|
@@ -11,12 +11,14 @@ export const MODULE_PATHS = {
|
|
|
11
11
|
} as const;
|
|
12
12
|
|
|
13
13
|
export const APP_PATHS = {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
tests: {
|
|
15
|
+
stories: "backend/src/tests/stories",
|
|
16
|
+
},
|
|
16
17
|
docs: {
|
|
17
18
|
resolver: "docs/resolver",
|
|
18
19
|
actors: "docs/actors",
|
|
19
20
|
businessFlow: "docs/business-flow",
|
|
21
|
+
story: "story",
|
|
20
22
|
screen: "docs/screen",
|
|
21
23
|
},
|
|
22
24
|
} as const;
|
|
@@ -3,7 +3,7 @@ import os from "node:os";
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
5
5
|
import { parseTestCasesFromDoc, parseItDescriptionsFromTest } from "../parse-doc-test-cases";
|
|
6
|
-
import {
|
|
6
|
+
import { runModuleTestCaseSyncCheck, runAppTestCaseSyncCheck } from "./sync-check-tests";
|
|
7
7
|
|
|
8
8
|
describe("parseTestCasesFromDoc", () => {
|
|
9
9
|
it("extracts test cases from ## Test Cases section", () => {
|
|
@@ -67,7 +67,7 @@ describe("createProduct", () => {
|
|
|
67
67
|
});
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
describe("
|
|
70
|
+
describe("runModuleTestCaseSyncCheck", () => {
|
|
71
71
|
let tmpDir: string;
|
|
72
72
|
|
|
73
73
|
beforeEach(() => {
|
|
@@ -94,7 +94,7 @@ describe("runTestCaseSyncCheck", () => {
|
|
|
94
94
|
'describe("createOrder", () => { it("creates order", async () => {}); });',
|
|
95
95
|
);
|
|
96
96
|
|
|
97
|
-
const errors = await
|
|
97
|
+
const errors = await runModuleTestCaseSyncCheck("modules", tmpDir);
|
|
98
98
|
expect(errors).toContainEqual(
|
|
99
99
|
expect.objectContaining({
|
|
100
100
|
type: "missing-test-case",
|
|
@@ -120,7 +120,7 @@ describe("runTestCaseSyncCheck", () => {
|
|
|
120
120
|
'describe("createOrder", () => { it("creates order", async () => {}); it("extra case", async () => {}); });',
|
|
121
121
|
);
|
|
122
122
|
|
|
123
|
-
const errors = await
|
|
123
|
+
const errors = await runModuleTestCaseSyncCheck("modules", tmpDir);
|
|
124
124
|
expect(errors).toContainEqual(
|
|
125
125
|
expect.objectContaining({
|
|
126
126
|
type: "extra-test-case",
|
|
@@ -146,7 +146,7 @@ describe("runTestCaseSyncCheck", () => {
|
|
|
146
146
|
'describe("createOrder", () => { it("creates order", async () => {}); it("validates input", async () => {}); });',
|
|
147
147
|
);
|
|
148
148
|
|
|
149
|
-
const errors = await
|
|
149
|
+
const errors = await runModuleTestCaseSyncCheck("modules", tmpDir);
|
|
150
150
|
const tcErrors = errors.filter(
|
|
151
151
|
(e) => e.type === "missing-test-case" || e.type === "extra-test-case",
|
|
152
152
|
);
|
|
@@ -169,10 +169,88 @@ describe("runTestCaseSyncCheck", () => {
|
|
|
169
169
|
'describe("createOrder", () => { it("creates order", async () => {}); });',
|
|
170
170
|
);
|
|
171
171
|
|
|
172
|
-
const errors = await
|
|
172
|
+
const errors = await runModuleTestCaseSyncCheck("modules", tmpDir);
|
|
173
173
|
const tcErrors = errors.filter(
|
|
174
174
|
(e) => e.type === "missing-test-case" || e.type === "extra-test-case",
|
|
175
175
|
);
|
|
176
176
|
expect(tcErrors).toHaveLength(0);
|
|
177
177
|
});
|
|
178
178
|
});
|
|
179
|
+
|
|
180
|
+
describe("runAppTestCaseSyncCheck", () => {
|
|
181
|
+
let tmpDir: string;
|
|
182
|
+
|
|
183
|
+
beforeEach(() => {
|
|
184
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "sync-check-story-"));
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
afterEach(() => {
|
|
188
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
function setupStoryFixture(storyName: string, storyContent: string, testContent: string) {
|
|
192
|
+
const flow = "item-lifecycle";
|
|
193
|
+
const storyDir = path.join(tmpDir, "apps", "my-app", "docs", "business-flow", flow, "story");
|
|
194
|
+
const testDir = path.join(tmpDir, "apps", "my-app", "backend", "src", "tests", "stories", flow);
|
|
195
|
+
fs.mkdirSync(storyDir, { recursive: true });
|
|
196
|
+
fs.mkdirSync(testDir, { recursive: true });
|
|
197
|
+
fs.writeFileSync(path.join(storyDir, `${storyName}.md`), storyContent);
|
|
198
|
+
fs.writeFileSync(path.join(testDir, `${storyName}.test.ts`), testContent);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
it("reports missing-test-case when story doc has case not in test", async () => {
|
|
202
|
+
setupStoryFixture(
|
|
203
|
+
"admin--create-item",
|
|
204
|
+
"# Create Item\n\n## Test Cases\n\n- creates item\n- validates input\n",
|
|
205
|
+
'describe("admin--create-item", () => { it("creates item", async () => {}); });',
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
const errors = await runAppTestCaseSyncCheck("apps", tmpDir);
|
|
209
|
+
expect(errors).toContainEqual(
|
|
210
|
+
expect.objectContaining({
|
|
211
|
+
type: "missing-test-case",
|
|
212
|
+
category: "story-test-case",
|
|
213
|
+
expectedBasename: "validates input",
|
|
214
|
+
}),
|
|
215
|
+
);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("reports extra-test-case when test has case not in story doc", async () => {
|
|
219
|
+
setupStoryFixture(
|
|
220
|
+
"admin--create-item",
|
|
221
|
+
"# Create Item\n\n## Test Cases\n\n- creates item\n",
|
|
222
|
+
'describe("admin--create-item", () => { it("creates item", async () => {}); it("extra case", async () => {}); });',
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
const errors = await runAppTestCaseSyncCheck("apps", tmpDir);
|
|
226
|
+
expect(errors).toContainEqual(
|
|
227
|
+
expect.objectContaining({
|
|
228
|
+
type: "extra-test-case",
|
|
229
|
+
category: "story-test-case",
|
|
230
|
+
expectedBasename: "extra case",
|
|
231
|
+
}),
|
|
232
|
+
);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("returns no errors when story doc and test match", async () => {
|
|
236
|
+
setupStoryFixture(
|
|
237
|
+
"admin--create-item",
|
|
238
|
+
"# Create Item\n\n## Test Cases\n\n- creates item\n- validates input\n",
|
|
239
|
+
'describe("admin--create-item", () => { it("creates item", async () => {}); it("validates input", async () => {}); });',
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
const errors = await runAppTestCaseSyncCheck("apps", tmpDir);
|
|
243
|
+
expect(errors).toHaveLength(0);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("skips when story doc has no Test Cases section", async () => {
|
|
247
|
+
setupStoryFixture(
|
|
248
|
+
"admin--view-items",
|
|
249
|
+
"# View Items\n\n## Scenario Patterns\n\n- Browse items\n",
|
|
250
|
+
'describe("admin--view-items", () => { it("some test", async () => {}); });',
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
const errors = await runAppTestCaseSyncCheck("apps", tmpDir);
|
|
254
|
+
expect(errors).toHaveLength(0);
|
|
255
|
+
});
|
|
256
|
+
});
|