@nextsparkjs/ai-workflow 0.1.0-beta.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +115 -0
  3. package/claude/_docs/workflows-optimizations.md +359 -0
  4. package/claude/agents/api-tester.md +634 -0
  5. package/claude/agents/architecture-supervisor.md +1351 -0
  6. package/claude/agents/backend-developer.md +997 -0
  7. package/claude/agents/backend-validator.md +417 -0
  8. package/claude/agents/bdd-docs-writer.md +737 -0
  9. package/claude/agents/block-developer.md +677 -0
  10. package/claude/agents/code-reviewer.md +1432 -0
  11. package/claude/agents/db-developer.md +721 -0
  12. package/claude/agents/db-validator.md +407 -0
  13. package/claude/agents/demo-video-generator.md +493 -0
  14. package/claude/agents/documentation-writer.md +1268 -0
  15. package/claude/agents/frontend-developer.md +1234 -0
  16. package/claude/agents/frontend-validator.md +777 -0
  17. package/claude/agents/functional-validator.md +630 -0
  18. package/claude/agents/mock-analyst.md +387 -0
  19. package/claude/agents/product-manager.md +963 -0
  20. package/claude/agents/qa-automation.md +1762 -0
  21. package/claude/agents/release-manager.md +634 -0
  22. package/claude/agents/selectors-translator.md +262 -0
  23. package/claude/agents/unit-test-writer.md +785 -0
  24. package/claude/agents/visual-comparator.md +329 -0
  25. package/claude/agents/workflow-maintainer.md +352 -0
  26. package/claude/commands/do/README.md +88 -0
  27. package/claude/commands/do/create-api.md +64 -0
  28. package/claude/commands/do/create-entity.md +66 -0
  29. package/claude/commands/do/create-migration.md +64 -0
  30. package/claude/commands/do/create-plugin.md +56 -0
  31. package/claude/commands/do/create-theme.md +70 -0
  32. package/claude/commands/do/mock-data.md +67 -0
  33. package/claude/commands/do/reset-db.md +71 -0
  34. package/claude/commands/do/setup-scheduled-action.md +75 -0
  35. package/claude/commands/do/sync-code-review.md +117 -0
  36. package/claude/commands/do/update-selectors.md +112 -0
  37. package/claude/commands/do/use-skills.md +90 -0
  38. package/claude/commands/do/validate-blocks.md +69 -0
  39. package/claude/commands/how-to/README.md +261 -0
  40. package/claude/commands/how-to/add-metadata.md +692 -0
  41. package/claude/commands/how-to/add-taxonomies.md +806 -0
  42. package/claude/commands/how-to/add-translations.md +571 -0
  43. package/claude/commands/how-to/create-api.md +577 -0
  44. package/claude/commands/how-to/create-block.md +575 -0
  45. package/claude/commands/how-to/create-child-entities.md +771 -0
  46. package/claude/commands/how-to/create-entity.md +597 -0
  47. package/claude/commands/how-to/create-migrations.md +605 -0
  48. package/claude/commands/how-to/create-plugin.md +654 -0
  49. package/claude/commands/how-to/customize-app.md +481 -0
  50. package/claude/commands/how-to/customize-dashboard.md +553 -0
  51. package/claude/commands/how-to/customize-theme.md +438 -0
  52. package/claude/commands/how-to/define-features-flows.md +632 -0
  53. package/claude/commands/how-to/deploy.md +507 -0
  54. package/claude/commands/how-to/handle-file-uploads.md +746 -0
  55. package/claude/commands/how-to/implement-search.md +1001 -0
  56. package/claude/commands/how-to/install-plugins.md +352 -0
  57. package/claude/commands/how-to/manage-test-coverage.md +984 -0
  58. package/claude/commands/how-to/run-tests.md +400 -0
  59. package/claude/commands/how-to/set-app-languages.md +601 -0
  60. package/claude/commands/how-to/set-plans-and-permissions.md +575 -0
  61. package/claude/commands/how-to/set-scheduled-actions.md +527 -0
  62. package/claude/commands/how-to/set-user-roles-and-permissions.md +550 -0
  63. package/claude/commands/how-to/setup-authentication.md +388 -0
  64. package/claude/commands/how-to/setup-claude-code.md +440 -0
  65. package/claude/commands/how-to/setup-database.md +274 -0
  66. package/claude/commands/how-to/setup-email-providers.md +598 -0
  67. package/claude/commands/how-to/setup-mobile-dev.md +627 -0
  68. package/claude/commands/how-to/start.md +500 -0
  69. package/claude/commands/how-to/use-devtools.md +639 -0
  70. package/claude/commands/how-to/use-superadmin.md +622 -0
  71. package/claude/commands/session/README.md +193 -0
  72. package/claude/commands/session/block-create.md +190 -0
  73. package/claude/commands/session/block-list.md +203 -0
  74. package/claude/commands/session/block-update.md +192 -0
  75. package/claude/commands/session/block-validate.md +218 -0
  76. package/claude/commands/session/changelog.md +115 -0
  77. package/claude/commands/session/close.md +225 -0
  78. package/claude/commands/session/commit.md +174 -0
  79. package/claude/commands/session/db-entity.md +206 -0
  80. package/claude/commands/session/db-fix.md +212 -0
  81. package/claude/commands/session/db-sample.md +206 -0
  82. package/claude/commands/session/demo.md +178 -0
  83. package/claude/commands/session/doc-bdd.md +207 -0
  84. package/claude/commands/session/doc-feature.md +218 -0
  85. package/claude/commands/session/doc-read.md +225 -0
  86. package/claude/commands/session/execute.md +204 -0
  87. package/claude/commands/session/explain.md +202 -0
  88. package/claude/commands/session/fix-bug.md +210 -0
  89. package/claude/commands/session/fix-build.md +182 -0
  90. package/claude/commands/session/fix-test.md +189 -0
  91. package/claude/commands/session/pending.md +232 -0
  92. package/claude/commands/session/refine.md +188 -0
  93. package/claude/commands/session/resume.md +192 -0
  94. package/claude/commands/session/review.md +192 -0
  95. package/claude/commands/session/scope-change.md +181 -0
  96. package/claude/commands/session/start-blocks.md +347 -0
  97. package/claude/commands/session/start.md +604 -0
  98. package/claude/commands/session/status.md +169 -0
  99. package/claude/commands/session/test-fix.md +221 -0
  100. package/claude/commands/session/test-run.md +203 -0
  101. package/claude/commands/session/test-write.md +242 -0
  102. package/claude/commands/session/validate.md +162 -0
  103. package/claude/config/context.json +40 -0
  104. package/claude/config/github.json +69 -0
  105. package/claude/config/github.schema.json +106 -0
  106. package/claude/config/team.json +46 -0
  107. package/claude/config/team.schema.json +106 -0
  108. package/claude/config/workspace.json +43 -0
  109. package/claude/config/workspace.schema.json +75 -0
  110. package/claude/skills/README.md +228 -0
  111. package/claude/skills/accessibility/SKILL.md +573 -0
  112. package/claude/skills/api-bypass-layers/SKILL.md +550 -0
  113. package/claude/skills/asana-integration/SKILL.md +499 -0
  114. package/claude/skills/better-auth/SKILL.md +666 -0
  115. package/claude/skills/billing-subscriptions/SKILL.md +660 -0
  116. package/claude/skills/block-decision-matrix/SKILL.md +359 -0
  117. package/claude/skills/clickup-integration/SKILL.md +434 -0
  118. package/claude/skills/core-theme-responsibilities/SKILL.md +485 -0
  119. package/claude/skills/create-plugin/SKILL.md +425 -0
  120. package/claude/skills/create-theme/SKILL.md +331 -0
  121. package/claude/skills/cypress-api/SKILL.md +511 -0
  122. package/claude/skills/cypress-api/scripts/generate-api-controller.py +329 -0
  123. package/claude/skills/cypress-api/scripts/generate-api-test.py +930 -0
  124. package/claude/skills/cypress-e2e/SKILL.md +526 -0
  125. package/claude/skills/cypress-e2e/scripts/extract-selectors.py +383 -0
  126. package/claude/skills/cypress-e2e/scripts/generate-uat-test.py +788 -0
  127. package/claude/skills/cypress-selectors/SKILL.md +309 -0
  128. package/claude/skills/cypress-selectors/scripts/extract-missing.py +243 -0
  129. package/claude/skills/cypress-selectors/scripts/generate-block-selectors.py +283 -0
  130. package/claude/skills/cypress-selectors/scripts/validate-selectors.py +145 -0
  131. package/claude/skills/database-migrations/SKILL.md +335 -0
  132. package/claude/skills/database-migrations/scripts/generate-sample-data.py +284 -0
  133. package/claude/skills/database-migrations/scripts/validate-migration.py +323 -0
  134. package/claude/skills/design-system/SKILL.md +682 -0
  135. package/claude/skills/documentation/SKILL.md +540 -0
  136. package/claude/skills/entity-api/SKILL.md +482 -0
  137. package/claude/skills/entity-system/SKILL.md +635 -0
  138. package/claude/skills/entity-system/scripts/generate-child-migration.py +298 -0
  139. package/claude/skills/entity-system/scripts/generate-metas-migration.py +233 -0
  140. package/claude/skills/entity-system/scripts/generate-migration.py +382 -0
  141. package/claude/skills/entity-system/scripts/generate-sample-data.py +418 -0
  142. package/claude/skills/entity-system/scripts/scaffold-entity.py +661 -0
  143. package/claude/skills/github/SKILL.md +467 -0
  144. package/claude/skills/i18n-nextintl/SKILL.md +302 -0
  145. package/claude/skills/i18n-nextintl/scripts/add-translation.py +243 -0
  146. package/claude/skills/i18n-nextintl/scripts/extract-hardcoded.py +246 -0
  147. package/claude/skills/i18n-nextintl/scripts/validate-translations.py +260 -0
  148. package/claude/skills/impact-analysis/SKILL.md +203 -0
  149. package/claude/skills/jest-unit/SKILL.md +306 -0
  150. package/claude/skills/jest-unit/references/component-testing.md +371 -0
  151. package/claude/skills/jest-unit/references/mocking-patterns.md +380 -0
  152. package/claude/skills/jest-unit/references/service-hook-testing.md +454 -0
  153. package/claude/skills/jira-integration/SKILL.md +539 -0
  154. package/claude/skills/media-library/SKILL.md +743 -0
  155. package/claude/skills/mock-analysis/SKILL.md +276 -0
  156. package/claude/skills/monorepo-architecture/SKILL.md +162 -0
  157. package/claude/skills/nextjs-api-development/SKILL.md +364 -0
  158. package/claude/skills/nextjs-api-development/scripts/generate-crud-tests.py +456 -0
  159. package/claude/skills/nextjs-api-development/scripts/scaffold-endpoint.py +481 -0
  160. package/claude/skills/nextjs-api-development/scripts/validate-api.py +283 -0
  161. package/claude/skills/notion-integration/SKILL.md +641 -0
  162. package/claude/skills/npm-development-workflow/SKILL.md +480 -0
  163. package/claude/skills/page-builder-blocks/SKILL.md +530 -0
  164. package/claude/skills/page-builder-blocks/scripts/scaffold-block.py +444 -0
  165. package/claude/skills/permissions-system/SKILL.md +619 -0
  166. package/claude/skills/plugins/SKILL.md +340 -0
  167. package/claude/skills/plugins/references/plugin-templates.md +414 -0
  168. package/claude/skills/plugins/references/plugin-testing.md +353 -0
  169. package/claude/skills/plugins/references/plugin-types.md +198 -0
  170. package/claude/skills/plugins/scripts/scaffold-plugin.py +443 -0
  171. package/claude/skills/pom-patterns/SKILL.md +452 -0
  172. package/claude/skills/pom-patterns/scripts/generate-pom.py +392 -0
  173. package/claude/skills/rate-limiting/SKILL.md +342 -0
  174. package/claude/skills/react-best-practices/AGENTS.md +2410 -0
  175. package/claude/skills/react-best-practices/README.md +123 -0
  176. package/claude/skills/react-best-practices/SKILL.md +125 -0
  177. package/claude/skills/react-best-practices/metadata.json +15 -0
  178. package/claude/skills/react-best-practices/rules/_sections.md +46 -0
  179. package/claude/skills/react-best-practices/rules/_template.md +28 -0
  180. package/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  181. package/claude/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
  182. package/claude/skills/react-best-practices/rules/async-api-routes.md +38 -0
  183. package/claude/skills/react-best-practices/rules/async-defer-await.md +80 -0
  184. package/claude/skills/react-best-practices/rules/async-dependencies.md +36 -0
  185. package/claude/skills/react-best-practices/rules/async-parallel.md +28 -0
  186. package/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  187. package/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
  188. package/claude/skills/react-best-practices/rules/bundle-conditional.md +31 -0
  189. package/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
  190. package/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  191. package/claude/skills/react-best-practices/rules/bundle-preload.md +50 -0
  192. package/claude/skills/react-best-practices/rules/client-event-listeners.md +74 -0
  193. package/claude/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
  194. package/claude/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  195. package/claude/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
  196. package/claude/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
  197. package/claude/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
  198. package/claude/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  199. package/claude/skills/react-best-practices/rules/js-cache-storage.md +70 -0
  200. package/claude/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  201. package/claude/skills/react-best-practices/rules/js-early-exit.md +50 -0
  202. package/claude/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  203. package/claude/skills/react-best-practices/rules/js-index-maps.md +37 -0
  204. package/claude/skills/react-best-practices/rules/js-length-check-first.md +49 -0
  205. package/claude/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  206. package/claude/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  207. package/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  208. package/claude/skills/react-best-practices/rules/rendering-activity.md +26 -0
  209. package/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  210. package/claude/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
  211. package/claude/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  212. package/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  213. package/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  214. package/claude/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  215. package/claude/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  216. package/claude/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  217. package/claude/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  218. package/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  219. package/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  220. package/claude/skills/react-best-practices/rules/rerender-memo.md +44 -0
  221. package/claude/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  222. package/claude/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
  223. package/claude/skills/react-best-practices/rules/server-cache-lru.md +41 -0
  224. package/claude/skills/react-best-practices/rules/server-cache-react.md +76 -0
  225. package/claude/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
  226. package/claude/skills/react-best-practices/rules/server-serialization.md +38 -0
  227. package/claude/skills/react-patterns/SKILL.md +688 -0
  228. package/claude/skills/registry-system/SKILL.md +331 -0
  229. package/claude/skills/scheduled-actions/SKILL.md +671 -0
  230. package/claude/skills/scope-enforcement/SKILL.md +542 -0
  231. package/claude/skills/scope-enforcement/scripts/validate-scope.py +357 -0
  232. package/claude/skills/server-actions/SKILL.md +493 -0
  233. package/claude/skills/service-layer/SKILL.md +587 -0
  234. package/claude/skills/session-management/SKILL.md +266 -0
  235. package/claude/skills/session-management/scripts/create-session.py +166 -0
  236. package/claude/skills/session-management/scripts/iteration-close.sh +105 -0
  237. package/claude/skills/session-management/scripts/iteration-init.sh +180 -0
  238. package/claude/skills/session-management/scripts/session-archive.sh +87 -0
  239. package/claude/skills/session-management/scripts/session-close.sh +133 -0
  240. package/claude/skills/session-management/scripts/session-init.sh +225 -0
  241. package/claude/skills/session-management/scripts/session-list.sh +163 -0
  242. package/claude/skills/session-management/scripts/split-plan.sh +116 -0
  243. package/claude/skills/shadcn-components/SKILL.md +586 -0
  244. package/claude/skills/shadcn-theming/SKILL.md +446 -0
  245. package/claude/skills/suspense-loading/SKILL.md +280 -0
  246. package/claude/skills/tailwind-theming/SKILL.md +507 -0
  247. package/claude/skills/tanstack-query/SKILL.md +608 -0
  248. package/claude/skills/test-coverage/SKILL.md +239 -0
  249. package/claude/skills/web-design-guidelines/SKILL.md +39 -0
  250. package/claude/skills/zod-validation/SKILL.md +537 -0
  251. package/claude/templates/blocks/progress.md +86 -0
  252. package/claude/templates/iteration/changes.md +61 -0
  253. package/claude/templates/iteration/progress.md +55 -0
  254. package/claude/templates/log.md +31 -0
  255. package/claude/templates/story/context.md +77 -0
  256. package/claude/templates/story/pendings.md +37 -0
  257. package/claude/templates/story/plan.md +299 -0
  258. package/claude/templates/story/requirements.md +109 -0
  259. package/claude/templates/story/scope.json +10 -0
  260. package/claude/templates/story/tests.md +91 -0
  261. package/claude/templates/task/progress.md +58 -0
  262. package/claude/templates/task/requirements.md +54 -0
  263. package/claude/workflows/README.md +154 -0
  264. package/claude/workflows/blocks.md +614 -0
  265. package/claude/workflows/story.md +1207 -0
  266. package/claude/workflows/task.md +927 -0
  267. package/claude/workflows/tweak.md +527 -0
  268. package/cursor/.gitkeep +0 -0
  269. package/package.json +35 -0
  270. package/scripts/postinstall.mjs +198 -0
  271. package/scripts/setup.mjs +282 -0
  272. package/scripts/sync.mjs +209 -0
@@ -0,0 +1,930 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Generate API Test Script
4
+
5
+ Generates an API test file and optional BDD documentation for an entity.
6
+
7
+ Usage:
8
+ python generate-api-test.py --entity ENTITY [--theme THEME] [--session SESSION] [--with-bdd]
9
+
10
+ Options:
11
+ --entity ENTITY Entity name (e.g., tasks, customers)
12
+ --theme THEME Theme name (default: default)
13
+ --session SESSION Session name for @scope tag
14
+ --with-bdd Generate BDD documentation file
15
+ --dry-run Preview without writing to file
16
+ """
17
+
18
+ import os
19
+ import sys
20
+ import re
21
+ import argparse
22
+ from pathlib import Path
23
+ from datetime import datetime
24
+ from typing import Optional
25
+
26
+
27
+ def to_pascal_case(name: str) -> str:
28
+ """Convert kebab-case/snake_case to PascalCase."""
29
+ return ''.join(x.title() for x in re.split(r'[-_]', name))
30
+
31
+
32
+ def to_singular(name: str) -> str:
33
+ """Convert plural to singular (simple English rules)."""
34
+ if name.endswith('ies'):
35
+ return name[:-3] + 'y'
36
+ elif name.endswith('es'):
37
+ return name[:-2]
38
+ elif name.endswith('s'):
39
+ return name[:-1]
40
+ return name
41
+
42
+
43
+ def generate_test_content(
44
+ entity: str,
45
+ theme: str,
46
+ session: Optional[str] = None
47
+ ) -> str:
48
+ """Generate the API test file content."""
49
+ singular = to_singular(entity)
50
+ pascal_singular = to_pascal_case(singular)
51
+ pascal_plural = to_pascal_case(entity)
52
+ entity_upper = entity.upper()
53
+ timestamp = datetime.now().strftime('%Y-%m-%d')
54
+ session_name = session or 'manual'
55
+
56
+ # Build tags
57
+ tags = [f"'@api'", f"'@feat-{entity}'", "'@crud'", "'@regression'"]
58
+ if session:
59
+ tags.append(f"'@scope-{session}'")
60
+ tags_str = ', '.join(tags)
61
+
62
+ return f'''/// <reference types="cypress" />
63
+
64
+ /**
65
+ * {pascal_plural} API - CRUD Tests
66
+ *
67
+ * Generated: {timestamp}
68
+ * Session: {session_name}
69
+ *
70
+ * Basic CRUD operations for /api/v1/{entity} endpoints
71
+ * Uses superadmin API key for full access with team context
72
+ */
73
+
74
+ import * as allure from 'allure-cypress'
75
+
76
+ const {pascal_plural}APIController = require('../../src/controllers/{pascal_plural}APIController.js')
77
+
78
+ describe('{pascal_plural} API - CRUD Operations', {{
79
+ tags: [{tags_str}]
80
+ }}, () => {{
81
+ let api: any
82
+ let createdEntities: any[] = []
83
+
84
+ // Superadmin API key for testing
85
+ // TODO: Replace with your test API key from fixtures or environment
86
+ const SUPERADMIN_API_KEY = Cypress.env('SUPERADMIN_API_KEY') || 'sk_test_...'
87
+ const TEAM_ID = Cypress.env('TEAM_ID') || 'team-tmt-001'
88
+ const BASE_URL = Cypress.config('baseUrl') || 'http://localhost:5173'
89
+
90
+ before(() => {{
91
+ // Initialize API controller with superadmin API key and team context
92
+ api = new {pascal_plural}APIController(BASE_URL, SUPERADMIN_API_KEY, TEAM_ID)
93
+ cy.log('{pascal_plural}APIController initialized')
94
+ cy.log(`Base URL: ${{BASE_URL}}`)
95
+ cy.log(`Team ID: ${{TEAM_ID}}`)
96
+ }})
97
+
98
+ beforeEach(() => {{
99
+ allure.epic('API')
100
+ allure.feature('{pascal_plural}')
101
+ }})
102
+
103
+ afterEach(() => {{
104
+ // Cleanup: Delete entities created during tests
105
+ if (createdEntities.length > 0) {{
106
+ createdEntities.forEach((entity: any) => {{
107
+ if (entity && entity.id) {{
108
+ api.delete{pascal_singular}(entity.id)
109
+ }}
110
+ }})
111
+ createdEntities = []
112
+ }}
113
+ }})
114
+
115
+ // ============================================================
116
+ // GET /api/v1/{entity} - List {pascal_plural}
117
+ // ============================================================
118
+ describe('GET /api/v1/{entity} - List {pascal_plural}', () => {{
119
+ it('{entity_upper}_API_001: Should list {entity} with valid API key', {{ tags: '@smoke' }}, () => {{
120
+ allure.story('CRUD Operations')
121
+ allure.severity('critical')
122
+ api.get{pascal_plural}().then((response: any) => {{
123
+ api.validateSuccessResponse(response, 200)
124
+ api.validatePaginatedResponse(response)
125
+ expect(response.body.data).to.be.an('array')
126
+
127
+ cy.log(`Found ${{response.body.data.length}} {entity}`)
128
+ cy.log(`Total: ${{response.body.info.total}}`)
129
+ }})
130
+ }})
131
+
132
+ it('{entity_upper}_API_002: Should list {entity} with pagination', () => {{
133
+ api.get{pascal_plural}({{ page: 1, limit: 5 }}).then((response: any) => {{
134
+ api.validatePaginatedResponse(response)
135
+ expect(response.body.info.page).to.eq(1)
136
+ expect(response.body.info.limit).to.eq(5)
137
+ expect(response.body.data.length).to.be.at.most(5)
138
+
139
+ cy.log(`Page 1, Limit 5: Got ${{response.body.data.length}} {entity}`)
140
+ }})
141
+ }})
142
+
143
+ it('{entity_upper}_API_003: Should reject request without API key', () => {{
144
+ const originalApiKey = api.apiKey
145
+ api.setApiKey(null)
146
+
147
+ api.get{pascal_plural}().then((response: any) => {{
148
+ expect(response.status).to.eq(401)
149
+ expect(response.body.success).to.be.false
150
+ }})
151
+
152
+ api.setApiKey(originalApiKey)
153
+ }})
154
+
155
+ it('{entity_upper}_API_004: Should reject request without x-team-id', () => {{
156
+ const originalTeamId = api.teamId
157
+ api.setTeamId(null)
158
+
159
+ api.get{pascal_plural}().then((response: any) => {{
160
+ expect(response.status).to.eq(400)
161
+ expect(response.body.success).to.be.false
162
+ expect(response.body.code).to.eq('TEAM_CONTEXT_REQUIRED')
163
+ }})
164
+
165
+ api.setTeamId(originalTeamId)
166
+ }})
167
+ }})
168
+
169
+ // ============================================================
170
+ // POST /api/v1/{entity} - Create {pascal_singular}
171
+ // ============================================================
172
+ describe('POST /api/v1/{entity} - Create {pascal_singular}', () => {{
173
+ it('{entity_upper}_API_010: Should create {singular} with valid data', {{ tags: '@smoke' }}, () => {{
174
+ allure.story('CRUD Operations')
175
+ allure.severity('critical')
176
+
177
+ // TODO: Customize test data based on entity schema
178
+ const data = api.generateRandom{pascal_singular}Data({{
179
+ // Add required fields here
180
+ }})
181
+
182
+ api.create{pascal_singular}(data).then((response: any) => {{
183
+ api.validateSuccessResponse(response, 201)
184
+ api.validate{pascal_singular}Object(response.body.data)
185
+
186
+ // TODO: Add entity-specific validations
187
+ // expect(response.body.data.title).to.eq(data.title)
188
+
189
+ createdEntities.push(response.body.data)
190
+ cy.log(`Created {singular}: ${{response.body.data.id}}`)
191
+ }})
192
+ }})
193
+
194
+ it('{entity_upper}_API_011: Should create {singular} with minimal data', () => {{
195
+ // TODO: Customize with minimal required fields
196
+ const data = api.generateRandom{pascal_singular}Data()
197
+
198
+ api.create{pascal_singular}(data).then((response: any) => {{
199
+ api.validateSuccessResponse(response, 201)
200
+ api.validate{pascal_singular}Object(response.body.data)
201
+
202
+ createdEntities.push(response.body.data)
203
+ cy.log(`Created {singular} with defaults: ${{response.body.data.id}}`)
204
+ }})
205
+ }})
206
+
207
+ it('{entity_upper}_API_012: Should reject creation without x-team-id', () => {{
208
+ const originalTeamId = api.teamId
209
+ api.setTeamId(null)
210
+
211
+ const data = api.generateRandom{pascal_singular}Data()
212
+
213
+ api.create{pascal_singular}(data).then((response: any) => {{
214
+ expect(response.status).to.eq(400)
215
+ expect(response.body.success).to.be.false
216
+ expect(response.body.code).to.eq('TEAM_CONTEXT_REQUIRED')
217
+ }})
218
+
219
+ api.setTeamId(originalTeamId)
220
+ }})
221
+ }})
222
+
223
+ // ============================================================
224
+ // GET /api/v1/{entity}/{{id}} - Get {pascal_singular} by ID
225
+ // ============================================================
226
+ describe('GET /api/v1/{entity}/{{id}} - Get {pascal_singular} by ID', () => {{
227
+ let testEntity: any
228
+
229
+ beforeEach(() => {{
230
+ const data = api.generateRandom{pascal_singular}Data()
231
+
232
+ api.create{pascal_singular}(data).then((response: any) => {{
233
+ testEntity = response.body.data
234
+ createdEntities.push(testEntity)
235
+ }})
236
+ }})
237
+
238
+ it('{entity_upper}_API_020: Should get {singular} by valid ID', () => {{
239
+ cy.then(() => {{
240
+ api.get{pascal_singular}ById(testEntity.id).then((response: any) => {{
241
+ api.validateSuccessResponse(response, 200)
242
+ api.validate{pascal_singular}Object(response.body.data)
243
+
244
+ expect(response.body.data.id).to.eq(testEntity.id)
245
+
246
+ cy.log(`Got {singular} by ID: ${{testEntity.id}}`)
247
+ }})
248
+ }})
249
+ }})
250
+
251
+ it('{entity_upper}_API_021: Should return 404 for non-existent {singular}', () => {{
252
+ const nonExistentId = 'non-existent-id-12345'
253
+
254
+ api.get{pascal_singular}ById(nonExistentId).then((response: any) => {{
255
+ expect(response.status).to.eq(404)
256
+ expect(response.body.success).to.be.false
257
+ }})
258
+ }})
259
+ }})
260
+
261
+ // ============================================================
262
+ // PATCH /api/v1/{entity}/{{id}} - Update {pascal_singular}
263
+ // ============================================================
264
+ describe('PATCH /api/v1/{entity}/{{id}} - Update {pascal_singular}', () => {{
265
+ let testEntity: any
266
+
267
+ beforeEach(() => {{
268
+ const data = api.generateRandom{pascal_singular}Data()
269
+
270
+ api.create{pascal_singular}(data).then((response: any) => {{
271
+ testEntity = response.body.data
272
+ createdEntities.push(testEntity)
273
+ }})
274
+ }})
275
+
276
+ it('{entity_upper}_API_030: Should update {singular} with valid data', () => {{
277
+ // TODO: Customize update data based on entity schema
278
+ const updateData = {{
279
+ // title: 'Updated Title'
280
+ }}
281
+
282
+ cy.then(() => {{
283
+ api.update{pascal_singular}(testEntity.id, updateData).then((response: any) => {{
284
+ api.validateSuccessResponse(response, 200)
285
+ api.validate{pascal_singular}Object(response.body.data)
286
+
287
+ // TODO: Add entity-specific validations
288
+ // expect(response.body.data.title).to.eq(updateData.title)
289
+ expect(response.body.data.id).to.eq(testEntity.id)
290
+
291
+ cy.log(`Updated {singular}: ${{testEntity.id}}`)
292
+ }})
293
+ }})
294
+ }})
295
+
296
+ it('{entity_upper}_API_031: Should return 404 for non-existent {singular}', () => {{
297
+ const nonExistentId = 'non-existent-id-12345'
298
+ const updateData = {{ /* minimal update */ }}
299
+
300
+ api.update{pascal_singular}(nonExistentId, updateData).then((response: any) => {{
301
+ expect(response.status).to.eq(404)
302
+ expect(response.body.success).to.be.false
303
+ }})
304
+ }})
305
+
306
+ it('{entity_upper}_API_032: Should reject empty update body', () => {{
307
+ cy.then(() => {{
308
+ api.update{pascal_singular}(testEntity.id, {{}}).then((response: any) => {{
309
+ expect(response.status).to.eq(400)
310
+ expect(response.body.success).to.be.false
311
+ }})
312
+ }})
313
+ }})
314
+ }})
315
+
316
+ // ============================================================
317
+ // DELETE /api/v1/{entity}/{{id}} - Delete {pascal_singular}
318
+ // ============================================================
319
+ describe('DELETE /api/v1/{entity}/{{id}} - Delete {pascal_singular}', () => {{
320
+ let testEntity: any
321
+
322
+ beforeEach(() => {{
323
+ const data = api.generateRandom{pascal_singular}Data()
324
+
325
+ api.create{pascal_singular}(data).then((response: any) => {{
326
+ testEntity = response.body.data
327
+ // Don't add to createdEntities - we'll delete manually
328
+ }})
329
+ }})
330
+
331
+ it('{entity_upper}_API_040: Should delete {singular} by valid ID', () => {{
332
+ cy.then(() => {{
333
+ api.delete{pascal_singular}(testEntity.id).then((response: any) => {{
334
+ api.validateSuccessResponse(response, 200)
335
+ expect(response.body.data.success).to.be.true
336
+ expect(response.body.data.id).to.exist
337
+
338
+ cy.log(`Deleted {singular}: ${{testEntity.id}}`)
339
+
340
+ // Verify deletion
341
+ api.get{pascal_singular}ById(testEntity.id).then((getResponse: any) => {{
342
+ expect(getResponse.status).to.eq(404)
343
+ }})
344
+ }})
345
+ }})
346
+ }})
347
+
348
+ it('{entity_upper}_API_041: Should return 404 for non-existent {singular}', () => {{
349
+ const nonExistentId = 'non-existent-id-12345'
350
+
351
+ api.delete{pascal_singular}(nonExistentId).then((response: any) => {{
352
+ expect(response.status).to.eq(404)
353
+ expect(response.body.success).to.be.false
354
+ }})
355
+
356
+ // Add testEntity to cleanup since we didn't delete it
357
+ createdEntities.push(testEntity)
358
+ }})
359
+ }})
360
+
361
+ // ============================================================
362
+ // Integration Test - Complete CRUD Lifecycle
363
+ // ============================================================
364
+ describe('Integration - Complete CRUD Lifecycle', () => {{
365
+ it('{entity_upper}_API_100: Should complete full lifecycle: Create -> Read -> Update -> Delete', () => {{
366
+ // TODO: Customize test data
367
+ const createData = api.generateRandom{pascal_singular}Data()
368
+
369
+ // 1. CREATE
370
+ api.create{pascal_singular}(createData).then((createResponse: any) => {{
371
+ api.validateSuccessResponse(createResponse, 201)
372
+ const created = createResponse.body.data
373
+ cy.log(`1. Created: ${{created.id}}`)
374
+
375
+ // 2. READ
376
+ api.get{pascal_singular}ById(created.id).then((readResponse: any) => {{
377
+ api.validateSuccessResponse(readResponse, 200)
378
+ expect(readResponse.body.data.id).to.eq(created.id)
379
+ cy.log(`2. Read: ${{created.id}}`)
380
+
381
+ // 3. UPDATE
382
+ const updateData = {{
383
+ // TODO: Add update fields
384
+ }}
385
+ api.update{pascal_singular}(created.id, updateData).then((updateResponse: any) => {{
386
+ api.validateSuccessResponse(updateResponse, 200)
387
+ cy.log(`3. Updated: ${{created.id}}`)
388
+
389
+ // 4. DELETE
390
+ api.delete{pascal_singular}(created.id).then((deleteResponse: any) => {{
391
+ api.validateSuccessResponse(deleteResponse, 200)
392
+ expect(deleteResponse.body.data.success).to.be.true
393
+ cy.log(`4. Deleted: ${{created.id}}`)
394
+
395
+ // 5. VERIFY DELETION
396
+ api.get{pascal_singular}ById(created.id).then((finalResponse: any) => {{
397
+ expect(finalResponse.status).to.eq(404)
398
+ cy.log(`5. Verified deletion: 404`)
399
+ cy.log('Full CRUD lifecycle completed successfully')
400
+ }})
401
+ }})
402
+ }})
403
+ }})
404
+ }})
405
+ }})
406
+ }})
407
+ }})
408
+ '''
409
+
410
+
411
+ def generate_bdd_content(entity: str, theme: str) -> str:
412
+ """Generate the BDD documentation file content."""
413
+ singular = to_singular(entity)
414
+ pascal_singular = to_pascal_case(singular)
415
+ pascal_plural = to_pascal_case(entity)
416
+ entity_upper = entity.upper()
417
+
418
+ return f'''---
419
+ feature: {pascal_plural} API
420
+ priority: high
421
+ tags: [api, feat-{entity}, crud, regression]
422
+ grepTags: ["@api", "@feat-{entity}"]
423
+ coverage: 14 tests
424
+ ---
425
+
426
+ # {pascal_plural} API
427
+
428
+ > API tests for /api/v1/{entity} CRUD endpoints. Uses superadmin API key with team context.
429
+
430
+ ## Endpoints Covered
431
+
432
+ | Endpoint | Method | Description |
433
+ |----------|--------|-------------|
434
+ | `/api/v1/{entity}` | GET | List {entity} (paginated) |
435
+ | `/api/v1/{entity}` | POST | Create new {singular} |
436
+ | `/api/v1/{entity}/{{id}}` | GET | Get {singular} by ID |
437
+ | `/api/v1/{entity}/{{id}}` | PATCH | Update {singular} |
438
+ | `/api/v1/{entity}/{{id}}` | DELETE | Delete {singular} |
439
+
440
+ ---
441
+
442
+ ## @test {entity_upper}_API_001: List {entity} with valid API key
443
+
444
+ ### Metadata
445
+ - **Priority:** Critical
446
+ - **Type:** Smoke
447
+ - **Tags:** api, {entity}, list, authentication
448
+ - **Grep:** `@smoke @feat-{entity}`
449
+
450
+ ```gherkin:en
451
+ Scenario: List {entity} with valid authentication
452
+
453
+ Given I have a valid superadmin API key
454
+ And I have x-team-id header set to a valid team
455
+ When I make a GET request to /api/v1/{entity}
456
+ Then the response status should be 200
457
+ And the response body should have success true
458
+ And the data should be an array of {entity}
459
+ And the info should contain total, page, and limit
460
+ ```
461
+
462
+ ```gherkin:es
463
+ Scenario: Listar {entity} con autenticacion valida
464
+
465
+ Given tengo una API key de superadmin valida
466
+ And tengo el header x-team-id configurado con un team valido
467
+ When hago una solicitud GET a /api/v1/{entity}
468
+ Then el status de respuesta deberia ser 200
469
+ And el body deberia tener success true
470
+ And los datos deberian ser un array de {entity}
471
+ And info deberia contener total, page y limit
472
+ ```
473
+
474
+ ---
475
+
476
+ ## @test {entity_upper}_API_002: List {entity} with pagination
477
+
478
+ ### Metadata
479
+ - **Priority:** Normal
480
+ - **Type:** Functional
481
+ - **Tags:** api, {entity}, pagination
482
+
483
+ ```gherkin:en
484
+ Scenario: List {entity} with pagination parameters
485
+
486
+ Given I have valid authentication
487
+ When I make a GET request to /api/v1/{entity}?page=1&limit=5
488
+ Then the response status should be 200
489
+ And info.page should be 1
490
+ And info.limit should be 5
491
+ And data array length should be at most 5
492
+ ```
493
+
494
+ ```gherkin:es
495
+ Scenario: Listar {entity} con parametros de paginacion
496
+
497
+ Given tengo autenticacion valida
498
+ When hago una solicitud GET a /api/v1/{entity}?page=1&limit=5
499
+ Then el status de respuesta deberia ser 200
500
+ And info.page deberia ser 1
501
+ And info.limit deberia ser 5
502
+ And la longitud del array data deberia ser maximo 5
503
+ ```
504
+
505
+ ---
506
+
507
+ ## @test {entity_upper}_API_003: Reject request without API key
508
+
509
+ ### Metadata
510
+ - **Priority:** Critical
511
+ - **Type:** Security
512
+ - **Tags:** api, {entity}, authentication, 401
513
+
514
+ ```gherkin:en
515
+ Scenario: Request without API key returns 401
516
+
517
+ Given I make a request without Authorization header
518
+ When I make a GET request to /api/v1/{entity}
519
+ Then the response status should be 401
520
+ And the response body should have success false
521
+ ```
522
+
523
+ ```gherkin:es
524
+ Scenario: Solicitud sin API key retorna 401
525
+
526
+ Given hago una solicitud sin header Authorization
527
+ When hago una solicitud GET a /api/v1/{entity}
528
+ Then el status de respuesta deberia ser 401
529
+ And el body deberia tener success false
530
+ ```
531
+
532
+ ---
533
+
534
+ ## @test {entity_upper}_API_004: Reject request without x-team-id
535
+
536
+ ### Metadata
537
+ - **Priority:** Critical
538
+ - **Type:** Security
539
+ - **Tags:** api, {entity}, team-context, 400
540
+
541
+ ```gherkin:en
542
+ Scenario: Request without x-team-id returns 400
543
+
544
+ Given I have a valid API key
545
+ But I do not include x-team-id header
546
+ When I make a GET request to /api/v1/{entity}
547
+ Then the response status should be 400
548
+ And the error code should be TEAM_CONTEXT_REQUIRED
549
+ ```
550
+
551
+ ```gherkin:es
552
+ Scenario: Solicitud sin x-team-id retorna 400
553
+
554
+ Given tengo una API key valida
555
+ But no incluyo el header x-team-id
556
+ When hago una solicitud GET a /api/v1/{entity}
557
+ Then el status de respuesta deberia ser 400
558
+ And el codigo de error deberia ser TEAM_CONTEXT_REQUIRED
559
+ ```
560
+
561
+ ---
562
+
563
+ ## @test {entity_upper}_API_010: Create {singular} with valid data
564
+
565
+ ### Metadata
566
+ - **Priority:** Critical
567
+ - **Type:** Smoke
568
+ - **Tags:** api, {entity}, create, 201
569
+
570
+ ```gherkin:en
571
+ Scenario: Create new {singular} successfully
572
+
573
+ Given I have valid authentication and team context
574
+ And I have valid {singular} data
575
+ When I make a POST request to /api/v1/{entity}
576
+ Then the response status should be 201
577
+ And the response body should have success true
578
+ And the data should contain the created {singular} with an ID
579
+ ```
580
+
581
+ ```gherkin:es
582
+ Scenario: Crear nuevo/a {singular} exitosamente
583
+
584
+ Given tengo autenticacion y contexto de team validos
585
+ And tengo datos de {singular} validos
586
+ When hago una solicitud POST a /api/v1/{entity}
587
+ Then el status de respuesta deberia ser 201
588
+ And el body deberia tener success true
589
+ And los datos deberian contener el/la {singular} creado/a con un ID
590
+ ```
591
+
592
+ ---
593
+
594
+ ## @test {entity_upper}_API_020: Get {singular} by valid ID
595
+
596
+ ### Metadata
597
+ - **Priority:** Normal
598
+ - **Type:** Functional
599
+ - **Tags:** api, {entity}, read
600
+
601
+ ```gherkin:en
602
+ Scenario: Get {singular} by valid ID
603
+
604
+ Given I have valid authentication
605
+ And a {singular} exists with a known ID
606
+ When I make a GET request to /api/v1/{entity}/{{id}}
607
+ Then the response status should be 200
608
+ And the data should contain the {singular} details
609
+ And data.id should match the requested ID
610
+ ```
611
+
612
+ ```gherkin:es
613
+ Scenario: Obtener {singular} por ID valido
614
+
615
+ Given tengo autenticacion valida
616
+ And existe un/a {singular} con un ID conocido
617
+ When hago una solicitud GET a /api/v1/{entity}/{{id}}
618
+ Then el status de respuesta deberia ser 200
619
+ And los datos deberian contener los detalles del/la {singular}
620
+ And data.id deberia coincidir con el ID solicitado
621
+ ```
622
+
623
+ ---
624
+
625
+ ## @test {entity_upper}_API_021: Return 404 for non-existent {singular}
626
+
627
+ ### Metadata
628
+ - **Priority:** Normal
629
+ - **Type:** Functional
630
+ - **Tags:** api, {entity}, 404
631
+
632
+ ```gherkin:en
633
+ Scenario: Get non-existent {singular} returns 404
634
+
635
+ Given I have valid authentication
636
+ When I make a GET request to /api/v1/{entity}/non-existent-id
637
+ Then the response status should be 404
638
+ And the response body should have success false
639
+ ```
640
+
641
+ ```gherkin:es
642
+ Scenario: Obtener {singular} inexistente retorna 404
643
+
644
+ Given tengo autenticacion valida
645
+ When hago una solicitud GET a /api/v1/{entity}/id-inexistente
646
+ Then el status de respuesta deberia ser 404
647
+ And el body deberia tener success false
648
+ ```
649
+
650
+ ---
651
+
652
+ ## @test {entity_upper}_API_030: Update {singular} with valid data
653
+
654
+ ### Metadata
655
+ - **Priority:** Normal
656
+ - **Type:** Functional
657
+ - **Tags:** api, {entity}, update
658
+
659
+ ```gherkin:en
660
+ Scenario: Update {singular} successfully
661
+
662
+ Given I have valid authentication
663
+ And a {singular} exists with a known ID
664
+ And I have valid update data
665
+ When I make a PATCH request to /api/v1/{entity}/{{id}}
666
+ Then the response status should be 200
667
+ And the data should contain the updated {singular}
668
+ And the updated fields should reflect my changes
669
+ ```
670
+
671
+ ```gherkin:es
672
+ Scenario: Actualizar {singular} exitosamente
673
+
674
+ Given tengo autenticacion valida
675
+ And existe un/a {singular} con un ID conocido
676
+ And tengo datos de actualizacion validos
677
+ When hago una solicitud PATCH a /api/v1/{entity}/{{id}}
678
+ Then el status de respuesta deberia ser 200
679
+ And los datos deberian contener el/la {singular} actualizado/a
680
+ And los campos actualizados deberian reflejar mis cambios
681
+ ```
682
+
683
+ ---
684
+
685
+ ## @test {entity_upper}_API_040: Delete {singular} by valid ID
686
+
687
+ ### Metadata
688
+ - **Priority:** Normal
689
+ - **Type:** Functional
690
+ - **Tags:** api, {entity}, delete
691
+
692
+ ```gherkin:en
693
+ Scenario: Delete {singular} successfully
694
+
695
+ Given I have valid authentication
696
+ And a {singular} exists with a known ID
697
+ When I make a DELETE request to /api/v1/{entity}/{{id}}
698
+ Then the response status should be 200
699
+ And data.success should be true
700
+ And subsequent GET for the same ID should return 404
701
+ ```
702
+
703
+ ```gherkin:es
704
+ Scenario: Eliminar {singular} exitosamente
705
+
706
+ Given tengo autenticacion valida
707
+ And existe un/a {singular} con un ID conocido
708
+ When hago una solicitud DELETE a /api/v1/{entity}/{{id}}
709
+ Then el status de respuesta deberia ser 200
710
+ And data.success deberia ser true
711
+ And un GET posterior para el mismo ID deberia retornar 404
712
+ ```
713
+
714
+ ---
715
+
716
+ ## @test {entity_upper}_API_100: Complete CRUD Lifecycle
717
+
718
+ ### Metadata
719
+ - **Priority:** Critical
720
+ - **Type:** Integration
721
+ - **Tags:** api, {entity}, integration, lifecycle
722
+
723
+ ```gherkin:en
724
+ Scenario: Complete CRUD lifecycle (Create -> Read -> Update -> Delete)
725
+
726
+ Given I have valid authentication and team context
727
+ When I create a new {singular}
728
+ Then the {singular} should be created with status 201
729
+
730
+ When I read the created {singular} by ID
731
+ Then I should get the {singular} details with status 200
732
+
733
+ When I update the {singular} with new data
734
+ Then the {singular} should be updated with status 200
735
+
736
+ When I delete the {singular}
737
+ Then the {singular} should be deleted with status 200
738
+
739
+ When I try to read the deleted {singular}
740
+ Then I should get a 404 response
741
+ ```
742
+
743
+ ```gherkin:es
744
+ Scenario: Ciclo de vida CRUD completo (Crear -> Leer -> Actualizar -> Eliminar)
745
+
746
+ Given tengo autenticacion y contexto de team validos
747
+ When creo un/a nuevo/a {singular}
748
+ Then el/la {singular} deberia crearse con status 201
749
+
750
+ When leo el/la {singular} creado/a por ID
751
+ Then deberia obtener los detalles con status 200
752
+
753
+ When actualizo el/la {singular} con nuevos datos
754
+ Then el/la {singular} deberia actualizarse con status 200
755
+
756
+ When elimino el/la {singular}
757
+ Then el/la {singular} deberia eliminarse con status 200
758
+
759
+ When intento leer el/la {singular} eliminado/a
760
+ Then deberia obtener una respuesta 404
761
+ ```
762
+
763
+ ---
764
+
765
+ ## Response Formats
766
+
767
+ ### Success Response (200/201)
768
+
769
+ ```json
770
+ {{
771
+ "success": true,
772
+ "data": {{ ... }},
773
+ "info": {{
774
+ "total": 10,
775
+ "page": 1,
776
+ "limit": 10
777
+ }}
778
+ }}
779
+ ```
780
+
781
+ ### Error Response (400 - TEAM_CONTEXT_REQUIRED)
782
+
783
+ ```json
784
+ {{
785
+ "success": false,
786
+ "error": {{
787
+ "message": "Team context required",
788
+ "code": "TEAM_CONTEXT_REQUIRED",
789
+ "details": {{
790
+ "hint": "Include x-team-id header with valid team ID"
791
+ }}
792
+ }}
793
+ }}
794
+ ```
795
+
796
+ ### Error Response (401 - Unauthorized)
797
+
798
+ ```json
799
+ {{
800
+ "success": false,
801
+ "error": {{
802
+ "message": "Authentication required",
803
+ "code": "AUTHENTICATION_REQUIRED",
804
+ "details": {{
805
+ "hint": "Provide valid API key via Authorization or x-api-key header"
806
+ }}
807
+ }}
808
+ }}
809
+ ```
810
+
811
+ ### Error Response (404 - Not Found)
812
+
813
+ ```json
814
+ {{
815
+ "success": false,
816
+ "error": {{
817
+ "message": "{pascal_singular} not found",
818
+ "code": "NOT_FOUND"
819
+ }}
820
+ }}
821
+ ```
822
+
823
+ ---
824
+
825
+ ## Test Summary
826
+
827
+ | Test ID | Endpoint | Method | Description | Tags |
828
+ |---------|----------|--------|-------------|------|
829
+ | {entity_upper}_API_001 | /{entity} | GET | List with valid auth | `@smoke` |
830
+ | {entity_upper}_API_002 | /{entity} | GET | List with pagination | |
831
+ | {entity_upper}_API_003 | /{entity} | GET | 401 without API key | |
832
+ | {entity_upper}_API_004 | /{entity} | GET | 400 without x-team-id | |
833
+ | {entity_upper}_API_010 | /{entity} | POST | Create with valid data | `@smoke` |
834
+ | {entity_upper}_API_011 | /{entity} | POST | Create with minimal data | |
835
+ | {entity_upper}_API_012 | /{entity} | POST | 400 without x-team-id | |
836
+ | {entity_upper}_API_020 | /{entity}/{{id}} | GET | Get by valid ID | |
837
+ | {entity_upper}_API_021 | /{entity}/{{id}} | GET | 404 non-existent | |
838
+ | {entity_upper}_API_030 | /{entity}/{{id}} | PATCH | Update with valid data | |
839
+ | {entity_upper}_API_031 | /{entity}/{{id}} | PATCH | 404 non-existent | |
840
+ | {entity_upper}_API_032 | /{entity}/{{id}} | PATCH | 400 empty body | |
841
+ | {entity_upper}_API_040 | /{entity}/{{id}} | DELETE | Delete by valid ID | |
842
+ | {entity_upper}_API_041 | /{entity}/{{id}} | DELETE | 404 non-existent | |
843
+ | {entity_upper}_API_100 | All | All | Full CRUD lifecycle | |
844
+ '''
845
+
846
+
847
+ def main():
848
+ parser = argparse.ArgumentParser(description='Generate API test file')
849
+ parser.add_argument('--entity', required=True, help='Entity name (e.g., tasks)')
850
+ parser.add_argument('--theme', default='default', help='Theme name')
851
+ parser.add_argument('--session', default=None, help='Session name for @scope tag')
852
+ parser.add_argument('--with-bdd', action='store_true', help='Generate BDD documentation')
853
+ parser.add_argument('--dry-run', action='store_true', help='Preview without writing')
854
+ parser.add_argument('--output', default=None, help='Output directory path')
855
+
856
+ args = parser.parse_args()
857
+
858
+ entity = args.entity.lower()
859
+ theme = args.theme
860
+
861
+ print(f"\n{'=' * 60}")
862
+ print("GENERATING API TEST")
863
+ print(f"{'=' * 60}")
864
+ print(f"Entity: {entity}")
865
+ print(f"Theme: {theme}")
866
+ print(f"Session: {args.session or '(none)'}")
867
+ print(f"With BDD: {args.with_bdd}")
868
+ print(f"{'=' * 60}\n")
869
+
870
+ # Generate test content
871
+ test_content = generate_test_content(entity, theme, args.session)
872
+
873
+ # Determine output paths
874
+ if args.output:
875
+ output_dir = Path(args.output)
876
+ else:
877
+ output_dir = Path(f'contents/themes/{theme}/tests/cypress/e2e/api/entities')
878
+
879
+ test_file = output_dir / f'{entity}-crud.cy.ts'
880
+ bdd_file = output_dir / f'{entity}-crud.bdd.md'
881
+
882
+ if args.dry_run:
883
+ print("DRY RUN - Generated test content:\n")
884
+ print("-" * 60)
885
+ print(test_content[:2000] + "\n... (truncated)")
886
+ print("-" * 60)
887
+
888
+ if args.with_bdd:
889
+ bdd_content = generate_bdd_content(entity, theme)
890
+ print("\nDRY RUN - Generated BDD content:\n")
891
+ print("-" * 60)
892
+ print(bdd_content[:2000] + "\n... (truncated)")
893
+ print("-" * 60)
894
+
895
+ print("\nRun without --dry-run to write files.")
896
+ return 0
897
+
898
+ # Create directories
899
+ output_dir.mkdir(parents=True, exist_ok=True)
900
+
901
+ # Write test file
902
+ with open(test_file, 'w', encoding='utf-8') as f:
903
+ f.write(test_content)
904
+ print(f"Test file generated: {test_file}")
905
+
906
+ # Write BDD file if requested
907
+ if args.with_bdd:
908
+ bdd_content = generate_bdd_content(entity, theme)
909
+ with open(bdd_file, 'w', encoding='utf-8') as f:
910
+ f.write(bdd_content)
911
+ print(f"BDD file generated: {bdd_file}")
912
+
913
+ pascal_plural = to_pascal_case(entity)
914
+ pascal_singular = to_pascal_case(to_singular(entity))
915
+
916
+ print(f"\n{'=' * 60}")
917
+ print("NEXT STEPS:")
918
+ print("=" * 60)
919
+ print(f"1. Review generated file(s)")
920
+ print(f"2. Ensure {pascal_plural}APIController exists in src/controllers/")
921
+ print(f"3. Customize generateRandom{pascal_singular}Data() with entity fields")
922
+ print(f"4. Customize TODO sections in test file")
923
+ print(f"5. Run tests: pnpm cy:run --spec \"{test_file}\"")
924
+ print("=" * 60 + "\n")
925
+
926
+ return 0
927
+
928
+
929
+ if __name__ == '__main__':
930
+ sys.exit(main())