@nextsparkjs/theme-default 0.1.0-beta.20 → 0.1.0-beta.22

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 (221) hide show
  1. package/package.json +1 -1
  2. package/tests/cypress/e2e/_devtools/access.bdd.md +262 -0
  3. package/tests/cypress/e2e/_devtools/access.cy.ts +171 -0
  4. package/tests/cypress/e2e/_devtools/navigation.bdd.md +261 -0
  5. package/tests/cypress/e2e/_devtools/navigation.cy.ts +157 -0
  6. package/tests/cypress/e2e/_devtools/pages.bdd.md +303 -0
  7. package/tests/cypress/e2e/_devtools/pages.cy.ts +184 -0
  8. package/tests/cypress/e2e/_docs/README.md +215 -0
  9. package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin-teams.narration.json +155 -0
  10. package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin.cy.ts +390 -0
  11. package/tests/cypress/e2e/_docs/tutorials/teams-system.doc.cy.ts +349 -0
  12. package/tests/cypress/e2e/_docs/tutorials/teams-system.narration.json +165 -0
  13. package/tests/cypress/e2e/_selectors/auth.cy.ts +306 -0
  14. package/tests/cypress/e2e/_selectors/billing.cy.ts +89 -0
  15. package/tests/cypress/e2e/_selectors/dashboard-mobile.cy.ts +113 -0
  16. package/tests/cypress/e2e/_selectors/dashboard-navigation.cy.ts +89 -0
  17. package/tests/cypress/e2e/_selectors/dashboard-sidebar.cy.ts +60 -0
  18. package/tests/cypress/e2e/_selectors/dashboard-topnav.cy.ts +146 -0
  19. package/tests/cypress/e2e/_selectors/devtools.cy.ts +210 -0
  20. package/tests/cypress/e2e/_selectors/global-search.cy.ts +88 -0
  21. package/tests/cypress/e2e/_selectors/pages-editor.cy.ts +179 -0
  22. package/tests/cypress/e2e/_selectors/posts-editor.cy.ts +282 -0
  23. package/tests/cypress/e2e/_selectors/public.cy.ts +112 -0
  24. package/tests/cypress/e2e/_selectors/settings-api-keys.cy.ts +228 -0
  25. package/tests/cypress/e2e/_selectors/settings-billing.cy.ts +105 -0
  26. package/tests/cypress/e2e/_selectors/settings-layout.cy.ts +119 -0
  27. package/tests/cypress/e2e/_selectors/settings-password.cy.ts +71 -0
  28. package/tests/cypress/e2e/_selectors/settings-profile.cy.ts +82 -0
  29. package/tests/cypress/e2e/_selectors/settings-teams.cy.ts +68 -0
  30. package/tests/cypress/e2e/_selectors/superadmin.cy.ts +185 -0
  31. package/tests/cypress/e2e/_selectors/tasks.cy.ts +242 -0
  32. package/tests/cypress/e2e/_selectors/taxonomies.cy.ts +126 -0
  33. package/tests/cypress/e2e/_selectors/teams.cy.ts +142 -0
  34. package/tests/cypress/e2e/_superadmin/all-teams.bdd.md +261 -0
  35. package/tests/cypress/e2e/_superadmin/all-teams.cy.ts +177 -0
  36. package/tests/cypress/e2e/_superadmin/all-users.bdd.md +406 -0
  37. package/tests/cypress/e2e/_superadmin/all-users.cy.ts +294 -0
  38. package/tests/cypress/e2e/_superadmin/dashboard.bdd.md +235 -0
  39. package/tests/cypress/e2e/_superadmin/dashboard.cy.ts +149 -0
  40. package/tests/cypress/e2e/_superadmin/subscriptions-overview.bdd.md +290 -0
  41. package/tests/cypress/e2e/_superadmin/subscriptions-overview.cy.ts +194 -0
  42. package/tests/cypress/e2e/ai/ai-usage.cy.ts +209 -0
  43. package/tests/cypress/e2e/ai/chat-api.cy.ts +107 -0
  44. package/tests/cypress/e2e/ai/guardrails.cy.ts +332 -0
  45. package/tests/cypress/e2e/api/billing/BillingAPIController.js +319 -0
  46. package/tests/cypress/e2e/api/billing/check-action.cy.ts +326 -0
  47. package/tests/cypress/e2e/api/billing/checkout.cy.ts +358 -0
  48. package/tests/cypress/e2e/api/billing/lifecycle.cy.ts +423 -0
  49. package/tests/cypress/e2e/api/billing/plans/README.md +345 -0
  50. package/tests/cypress/e2e/api/billing/plans/business.cy.ts +412 -0
  51. package/tests/cypress/e2e/api/billing/plans/downgrade.cy.ts +510 -0
  52. package/tests/cypress/e2e/api/billing/plans/fixtures/billing-plans.json +163 -0
  53. package/tests/cypress/e2e/api/billing/plans/free.cy.ts +500 -0
  54. package/tests/cypress/e2e/api/billing/plans/pro.cy.ts +497 -0
  55. package/tests/cypress/e2e/api/billing/plans/starter.cy.ts +342 -0
  56. package/tests/cypress/e2e/api/billing/portal.cy.ts +313 -0
  57. package/tests/cypress/e2e/api/devtools/registries.bdd.md +300 -0
  58. package/tests/cypress/e2e/api/devtools/registries.cy.ts +368 -0
  59. package/tests/cypress/e2e/api/entities/blocks-scope.cy.ts +396 -0
  60. package/tests/cypress/e2e/api/entities/customers-crud.cy.ts +648 -0
  61. package/tests/cypress/e2e/api/entities/customers-metas.cy.ts +839 -0
  62. package/tests/cypress/e2e/api/entities/pages-crud.cy.ts +425 -0
  63. package/tests/cypress/e2e/api/entities/pages-status.cy.ts +335 -0
  64. package/tests/cypress/e2e/api/entities/post-categories-crud.cy.ts +610 -0
  65. package/tests/cypress/e2e/api/entities/posts-crud.cy.ts +709 -0
  66. package/tests/cypress/e2e/api/entities/posts-status.cy.ts +396 -0
  67. package/tests/cypress/e2e/api/entities/tasks-crud.cy.ts +602 -0
  68. package/tests/cypress/e2e/api/entities/tasks-metas.cy.ts +878 -0
  69. package/tests/cypress/e2e/api/entities/users-crud.cy.ts +469 -0
  70. package/tests/cypress/e2e/api/entities/users-metas.cy.ts +913 -0
  71. package/tests/cypress/e2e/api/entities/users-security.cy.ts +375 -0
  72. package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.bdd.md +375 -0
  73. package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.cy.ts +346 -0
  74. package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.bdd.md +451 -0
  75. package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.cy.ts +447 -0
  76. package/tests/cypress/e2e/api/scheduled-actions/scheduling.bdd.md +649 -0
  77. package/tests/cypress/e2e/api/scheduled-actions/scheduling.cy.ts +333 -0
  78. package/tests/cypress/e2e/api/settings/api-keys.crud.cy.ts +923 -0
  79. package/tests/cypress/e2e/uat/auth/app-roles/developer-login.bdd.md +231 -0
  80. package/tests/cypress/e2e/uat/auth/app-roles/developer-login.cy.ts +144 -0
  81. package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.bdd.md +118 -0
  82. package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.cy.ts +84 -0
  83. package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.bdd.md +288 -0
  84. package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.cy.ts +188 -0
  85. package/tests/cypress/e2e/uat/auth/login-logout.bdd.md +160 -0
  86. package/tests/cypress/e2e/uat/auth/login-logout.cy.ts +116 -0
  87. package/tests/cypress/e2e/uat/auth/password-reset.bdd.md +289 -0
  88. package/tests/cypress/e2e/uat/auth/password-reset.cy.ts +200 -0
  89. package/tests/cypress/e2e/uat/auth/team-roles/admin-login.bdd.md +225 -0
  90. package/tests/cypress/e2e/uat/auth/team-roles/admin-login.cy.ts +148 -0
  91. package/tests/cypress/e2e/uat/auth/team-roles/member-login.bdd.md +251 -0
  92. package/tests/cypress/e2e/uat/auth/team-roles/member-login.cy.ts +163 -0
  93. package/tests/cypress/e2e/uat/auth/team-roles/owner-login.bdd.md +231 -0
  94. package/tests/cypress/e2e/uat/auth/team-roles/owner-login.cy.ts +141 -0
  95. package/tests/cypress/e2e/uat/billing/extended.bdd.md +273 -0
  96. package/tests/cypress/e2e/uat/billing/extended.cy.ts +209 -0
  97. package/tests/cypress/e2e/uat/billing/feature-gates.bdd.md +407 -0
  98. package/tests/cypress/e2e/uat/billing/feature-gates.cy.ts +307 -0
  99. package/tests/cypress/e2e/uat/billing/page.bdd.md +329 -0
  100. package/tests/cypress/e2e/uat/billing/page.cy.ts +250 -0
  101. package/tests/cypress/e2e/uat/billing/status.bdd.md +190 -0
  102. package/tests/cypress/e2e/uat/billing/status.cy.ts +145 -0
  103. package/tests/cypress/e2e/uat/billing/team-switch.bdd.md +156 -0
  104. package/tests/cypress/e2e/uat/billing/team-switch.cy.ts +122 -0
  105. package/tests/cypress/e2e/uat/billing/usage.bdd.md +218 -0
  106. package/tests/cypress/e2e/uat/billing/usage.cy.ts +176 -0
  107. package/tests/cypress/e2e/uat/blocks/hero.bdd.md +124 -0
  108. package/tests/cypress/e2e/uat/blocks/hero.cy.ts +56 -0
  109. package/tests/cypress/e2e/uat/devtools/api-tester.cy.ts +390 -0
  110. package/tests/cypress/e2e/uat/entities/customers/member.bdd.md +275 -0
  111. package/tests/cypress/e2e/uat/entities/customers/member.cy.ts +122 -0
  112. package/tests/cypress/e2e/uat/entities/customers/owner.bdd.md +243 -0
  113. package/tests/cypress/e2e/uat/entities/customers/owner.cy.ts +165 -0
  114. package/tests/cypress/e2e/uat/entities/pages/block-crud.bdd.md +476 -0
  115. package/tests/cypress/e2e/uat/entities/pages/block-crud.cy.ts +486 -0
  116. package/tests/cypress/e2e/uat/entities/pages/block-editor.bdd.md +460 -0
  117. package/tests/cypress/e2e/uat/entities/pages/block-editor.cy.ts +301 -0
  118. package/tests/cypress/e2e/uat/entities/pages/list.bdd.md +432 -0
  119. package/tests/cypress/e2e/uat/entities/pages/list.cy.ts +273 -0
  120. package/tests/cypress/e2e/uat/entities/pages/public-rendering.bdd.md +696 -0
  121. package/tests/cypress/e2e/uat/entities/pages/public-rendering.cy.ts +340 -0
  122. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.bdd.md +161 -0
  123. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.cy.ts +104 -0
  124. package/tests/cypress/e2e/uat/entities/posts/categories.bdd.md +375 -0
  125. package/tests/cypress/e2e/uat/entities/posts/categories.cy.ts +241 -0
  126. package/tests/cypress/e2e/uat/entities/posts/editor.bdd.md +429 -0
  127. package/tests/cypress/e2e/uat/entities/posts/editor.cy.ts +257 -0
  128. package/tests/cypress/e2e/uat/entities/posts/list.bdd.md +340 -0
  129. package/tests/cypress/e2e/uat/entities/posts/list.cy.ts +177 -0
  130. package/tests/cypress/e2e/uat/entities/posts/public.bdd.md +614 -0
  131. package/tests/cypress/e2e/uat/entities/posts/public.cy.ts +249 -0
  132. package/tests/cypress/e2e/uat/entities/tasks/member.bdd.md +222 -0
  133. package/tests/cypress/e2e/uat/entities/tasks/member.cy.ts +165 -0
  134. package/tests/cypress/e2e/uat/entities/tasks/owner.bdd.md +419 -0
  135. package/tests/cypress/e2e/uat/entities/tasks/owner.cy.ts +191 -0
  136. package/tests/cypress/e2e/uat/roles/editor-role.bdd.md +552 -0
  137. package/tests/cypress/e2e/uat/roles/editor-role.cy.ts +210 -0
  138. package/tests/cypress/e2e/uat/roles/member-restrictions.bdd.md +450 -0
  139. package/tests/cypress/e2e/uat/roles/member-restrictions.cy.ts +189 -0
  140. package/tests/cypress/e2e/uat/roles/owner-full-crud.bdd.md +530 -0
  141. package/tests/cypress/e2e/uat/roles/owner-full-crud.cy.ts +247 -0
  142. package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.bdd.md +736 -0
  143. package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.cy.ts +740 -0
  144. package/tests/cypress/e2e/uat/teams/roles-matrix.bdd.md +553 -0
  145. package/tests/cypress/e2e/uat/teams/roles-matrix.cy.ts +185 -0
  146. package/tests/cypress/e2e/uat/teams/switcher.bdd.md +1151 -0
  147. package/tests/cypress/e2e/uat/teams/switcher.cy.ts +497 -0
  148. package/tests/cypress/e2e/uat/teams/team-switcher.md +198 -0
  149. package/tests/cypress/fixtures/blocks.json +218 -0
  150. package/tests/cypress/fixtures/entities.json +78 -0
  151. package/tests/cypress/fixtures/page-builder.json +21 -0
  152. package/tests/cypress/src/components/CategoriesPOM.ts +382 -0
  153. package/tests/cypress/src/components/CustomersPOM.ts +439 -0
  154. package/tests/cypress/src/components/DevKeyringPOM.ts +160 -0
  155. package/tests/cypress/src/components/EntityForm.ts +375 -0
  156. package/tests/cypress/src/components/EntityList.ts +389 -0
  157. package/tests/cypress/src/components/PageBuilderPOM.ts +710 -0
  158. package/tests/cypress/src/components/PostEditorPOM.ts +370 -0
  159. package/tests/cypress/src/components/PostsListPOM.ts +223 -0
  160. package/tests/cypress/src/components/PublicPagePOM.ts +447 -0
  161. package/tests/cypress/src/components/PublicPostPOM.ts +146 -0
  162. package/tests/cypress/src/components/TasksPOM.ts +272 -0
  163. package/tests/cypress/src/components/TeamSwitcherPOM.ts +450 -0
  164. package/tests/cypress/src/components/index.ts +21 -0
  165. package/tests/cypress/src/controllers/ApiKeysAPIController.js +178 -0
  166. package/tests/cypress/src/controllers/BaseAPIController.js +317 -0
  167. package/tests/cypress/src/controllers/CustomerAPIController.js +251 -0
  168. package/tests/cypress/src/controllers/PagesAPIController.js +226 -0
  169. package/tests/cypress/src/controllers/PostsAPIController.js +250 -0
  170. package/tests/cypress/src/controllers/TaskAPIController.js +240 -0
  171. package/tests/cypress/src/controllers/UsersAPIController.js +242 -0
  172. package/tests/cypress/src/controllers/index.js +25 -0
  173. package/tests/cypress/src/core/AuthPOM.ts +450 -0
  174. package/tests/cypress/src/core/BasePOM.ts +86 -0
  175. package/tests/cypress/src/core/BlockEditorBasePOM.ts +576 -0
  176. package/tests/cypress/src/core/DashboardEntityPOM.ts +692 -0
  177. package/tests/cypress/src/core/index.ts +14 -0
  178. package/tests/cypress/src/entities/CustomersPOM.ts +172 -0
  179. package/tests/cypress/src/entities/PagesPOM.ts +137 -0
  180. package/tests/cypress/src/entities/PostsPOM.ts +137 -0
  181. package/tests/cypress/src/entities/TasksPOM.ts +176 -0
  182. package/tests/cypress/src/entities/index.ts +14 -0
  183. package/tests/cypress/src/features/BillingPOM.ts +385 -0
  184. package/tests/cypress/src/features/DashboardPOM.ts +245 -0
  185. package/tests/cypress/src/features/DevtoolsPOM.ts +739 -0
  186. package/tests/cypress/src/features/PageBuilderPOM.ts +263 -0
  187. package/tests/cypress/src/features/PostEditorPOM.ts +313 -0
  188. package/tests/cypress/src/features/ScheduledActionsPOM.ts +463 -0
  189. package/tests/cypress/src/features/SettingsPOM.ts +362 -0
  190. package/tests/cypress/src/features/SuperadminPOM.ts +331 -0
  191. package/tests/cypress/src/features/SuperadminTeamRolesPOM.ts +285 -0
  192. package/tests/cypress/src/features/index.ts +28 -0
  193. package/tests/cypress/src/helpers/ApiInterceptor.ts +177 -0
  194. package/tests/cypress/src/index.ts +101 -0
  195. package/tests/cypress/src/pages/dashboard/Dashboard.js +677 -0
  196. package/tests/cypress/src/pages/dashboard/DashboardPage.js +43 -0
  197. package/tests/cypress/src/pages/dashboard/DashboardStats.js +546 -0
  198. package/tests/cypress/src/pages/dashboard/index.js +6 -0
  199. package/tests/cypress/src/pages/index.js +5 -0
  200. package/tests/cypress/src/pages/public/FeaturesPage.js +28 -0
  201. package/tests/cypress/src/pages/public/LandingPage.js +69 -0
  202. package/tests/cypress/src/pages/public/PricingPage.js +33 -0
  203. package/tests/cypress/src/pages/public/index.js +6 -0
  204. package/tests/cypress/src/selectors.ts +46 -0
  205. package/tests/cypress/src/session-helpers.ts +500 -0
  206. package/tests/cypress/support/doc-commands.ts +260 -0
  207. package/tests/cypress/support/e2e.ts +89 -0
  208. package/tests/cypress.config.ts +165 -0
  209. package/tests/jest/components/post-header.test.tsx +377 -0
  210. package/tests/jest/config/role-config.test.ts +529 -0
  211. package/tests/jest/jest.config.ts +81 -0
  212. package/tests/jest/langchain/COVERAGE.md +372 -0
  213. package/tests/jest/langchain/guardrails.test.ts +465 -0
  214. package/tests/jest/langchain/streaming.test.ts +367 -0
  215. package/tests/jest/langchain/token-tracker.test.ts +455 -0
  216. package/tests/jest/langchain/tracer-callbacks.test.ts +881 -0
  217. package/tests/jest/langchain/tracer.test.ts +823 -0
  218. package/tests/jest/user-roles/role-helpers.test.ts +432 -0
  219. package/tests/jest/validation/categories.test.ts +429 -0
  220. package/tests/jest/validation/posts.test.ts +546 -0
  221. package/tests/tsconfig.json +15 -0
@@ -0,0 +1,326 @@
1
+ // @ts-nocheck
2
+ /// <reference types="cypress" />
3
+
4
+ /**
5
+ * Billing API - check-action Endpoint Tests
6
+ *
7
+ * FIX1: Tests for the check-action endpoint that validates:
8
+ * - RBAC permissions (role-based access control)
9
+ * - Plan features (subscription-based access)
10
+ * - Quota limits (usage-based access)
11
+ *
12
+ * Session: 2025-12-20-subscriptions-system-v2
13
+ * Phase: 9 (api-tester)
14
+ */
15
+
16
+ import * as allure from 'allure-cypress'
17
+
18
+ const BillingAPIController = require('./BillingAPIController.js')
19
+
20
+ describe('Billing API - check-action Endpoint', () => {
21
+ let billingAPI: any
22
+
23
+ const SUPERADMIN_API_KEY = 'test_api_key_for_testing_purposes_only_not_a_real_secret_key_abc123'
24
+ const TEAM_ID = 'team-tmt-001'
25
+ const BASE_URL = Cypress.config('baseUrl') || 'http://localhost:5173'
26
+
27
+ before(() => {
28
+ billingAPI = new BillingAPIController(BASE_URL, SUPERADMIN_API_KEY, TEAM_ID)
29
+ cy.log('BillingAPIController initialized')
30
+ cy.log(`Base URL: ${BASE_URL}`)
31
+ cy.log(`Team ID: ${TEAM_ID}`)
32
+ })
33
+
34
+ beforeEach(() => {
35
+ allure.epic('API')
36
+ allure.feature('Billing')
37
+ allure.story('check-action Endpoint')
38
+ })
39
+
40
+ // ============================================================
41
+ // TEST 1: Authentication
42
+ // ============================================================
43
+ describe('Authentication', () => {
44
+ it('BILLING_API_001: Should return 401 without authentication', () => {
45
+ allure.severity('critical')
46
+
47
+ // Remove API key
48
+ const originalApiKey = billingAPI.apiKey
49
+ billingAPI.setApiKey(null)
50
+
51
+ billingAPI.checkAction('tasks.create').then((response: any) => {
52
+ expect(response.status).to.eq(401)
53
+ expect(response.body.success).to.be.false
54
+
55
+ cy.log('✓ Returns 401 without auth')
56
+ })
57
+
58
+ // Restore API key
59
+ billingAPI.setApiKey(originalApiKey)
60
+ })
61
+
62
+ it('BILLING_API_002: Should accept API key authentication', () => {
63
+ allure.severity('critical')
64
+
65
+ billingAPI.checkAction('tasks.create').then((response: any) => {
66
+ expect(response.status).to.eq(200)
67
+ expect(response.body.success).to.be.true
68
+
69
+ cy.log('✓ Accepts API key authentication')
70
+ })
71
+ })
72
+
73
+ it('BILLING_API_003: Should require team context', () => {
74
+ allure.severity('critical')
75
+
76
+ // Remove team ID
77
+ const originalTeamId = billingAPI.teamId
78
+ billingAPI.setTeamId(null)
79
+
80
+ billingAPI.checkAction('tasks.create').then((response: any) => {
81
+ expect(response.status).to.eq(400)
82
+ expect(response.body.success).to.be.false
83
+ expect(response.body.error).to.include('team context')
84
+
85
+ cy.log('✓ Requires team context')
86
+ })
87
+
88
+ // Restore team ID
89
+ billingAPI.setTeamId(originalTeamId)
90
+ })
91
+ })
92
+
93
+ // ============================================================
94
+ // TEST 2: Allowed Actions (RBAC + Feature + Quota Pass)
95
+ // ============================================================
96
+ describe('Allowed Actions', () => {
97
+ it('BILLING_API_010: Should allow action with valid permissions', () => {
98
+ allure.severity('critical')
99
+
100
+ // Superadmin has all permissions
101
+ billingAPI.checkAction('tasks.create').then((response: any) => {
102
+ billingAPI.validateCheckActionResponse(response, true)
103
+
104
+ expect(response.body.data.allowed).to.be.true
105
+ expect(response.body.data.reason).to.be.undefined
106
+
107
+ cy.log('✓ Action allowed for superadmin')
108
+ cy.log(`Action: tasks.create`)
109
+ })
110
+ })
111
+
112
+ it('BILLING_API_011: Should allow multiple different actions', () => {
113
+ const actions = [
114
+ 'tasks.create',
115
+ 'customers.create',
116
+ 'analytics.view_advanced',
117
+ 'tasks.automate'
118
+ ]
119
+
120
+ actions.forEach((action) => {
121
+ billingAPI.checkAction(action).then((response: any) => {
122
+ expect(response.status).to.eq(200)
123
+ expect(response.body.data.allowed).to.be.true
124
+
125
+ cy.log(`✓ ${action} allowed`)
126
+ })
127
+ })
128
+ })
129
+ })
130
+
131
+ // ============================================================
132
+ // TEST 3: Feature Not in Plan (Subscription-based Access)
133
+ // ============================================================
134
+ describe('Feature Restrictions', () => {
135
+ it('BILLING_API_020: Should return feature_not_in_plan for missing feature', () => {
136
+ allure.severity('critical')
137
+
138
+ // This test assumes there's a feature that free plan doesn't have
139
+ // For now, we'll test the endpoint structure
140
+ // In a real scenario, you'd create a test user with free plan
141
+
142
+ cy.log('Note: This test checks endpoint structure')
143
+ cy.log('Full test requires multi-team setup with different plans')
144
+
145
+ // Using a hypothetical enterprise-only feature
146
+ billingAPI.checkAction('auth.configure_sso').then((response: any) => {
147
+ expect(response.status).to.eq(200)
148
+ expect(response.body.success).to.be.true
149
+ expect(response.body.data).to.have.property('allowed')
150
+
151
+ // With superadmin, this might be allowed or restricted
152
+ // The key is the endpoint works correctly
153
+ cy.log(`✓ Endpoint responds for feature check`)
154
+ cy.log(`Allowed: ${response.body.data.allowed}`)
155
+
156
+ if (!response.body.data.allowed) {
157
+ expect(response.body.data.reason).to.be.oneOf([
158
+ 'feature_not_in_plan',
159
+ 'no_permission'
160
+ ])
161
+ }
162
+ })
163
+ })
164
+
165
+ it('BILLING_API_021: Should check wildcard feature access', () => {
166
+ // Test wildcard feature support (*)
167
+ billingAPI.checkAction('custom.any_action').then((response: any) => {
168
+ expect(response.status).to.eq(200)
169
+ expect(response.body.success).to.be.true
170
+
171
+ // Superadmin with * should allow any action
172
+ cy.log(`✓ Wildcard feature check works`)
173
+ cy.log(`Allowed: ${response.body.data.allowed}`)
174
+ })
175
+ })
176
+ })
177
+
178
+ // ============================================================
179
+ // TEST 4: Quota Exceeded (Usage-based Access)
180
+ // ============================================================
181
+ describe('Quota Limits', () => {
182
+ it('BILLING_API_030: Should return quota info when checking limits', () => {
183
+ allure.severity('normal')
184
+
185
+ // Check an action that has quota limits
186
+ billingAPI.checkAction('tasks.create').then((response: any) => {
187
+ expect(response.status).to.eq(200)
188
+ expect(response.body.success).to.be.true
189
+ expect(response.body.data).to.have.property('allowed')
190
+
191
+ // If there's a quota check, it should include quota info
192
+ if (response.body.data.quota) {
193
+ expect(response.body.data.quota).to.have.property('current')
194
+ expect(response.body.data.quota).to.have.property('max')
195
+ expect(response.body.data.quota).to.have.property('allowed')
196
+
197
+ cy.log(`✓ Quota info included`)
198
+ cy.log(`Current: ${response.body.data.quota.current}/${response.body.data.quota.max}`)
199
+ } else {
200
+ cy.log('Note: No quota limit for this action')
201
+ }
202
+ })
203
+ })
204
+
205
+ it('BILLING_API_031: Should handle quota_exceeded scenario', () => {
206
+ // This test would require setting up a user at quota limit
207
+ // For now, we verify the endpoint handles the check correctly
208
+
209
+ cy.log('Note: Full quota_exceeded test requires quota setup')
210
+ cy.log('This verifies endpoint response structure')
211
+
212
+ billingAPI.checkAction('tasks.create').then((response: any) => {
213
+ expect(response.status).to.eq(200)
214
+ expect(response.body.data).to.have.property('allowed')
215
+
216
+ if (!response.body.data.allowed && response.body.data.reason === 'quota_exceeded') {
217
+ expect(response.body.data.quota).to.exist
218
+ expect(response.body.data.quota.current).to.be.gte(response.body.data.quota.max)
219
+
220
+ cy.log(`✓ Quota exceeded response correct`)
221
+ } else {
222
+ cy.log(`✓ User not at quota limit (allowed: ${response.body.data.allowed})`)
223
+ }
224
+ })
225
+ })
226
+ })
227
+
228
+ // ============================================================
229
+ // TEST 5: Validation & Error Handling
230
+ // ============================================================
231
+ describe('Validation', () => {
232
+ it('BILLING_API_040: Should reject request without action', () => {
233
+ allure.severity('normal')
234
+
235
+ cy.request({
236
+ method: 'POST',
237
+ url: `${BASE_URL}/api/v1/billing/check-action`,
238
+ headers: billingAPI.getHeaders(),
239
+ body: {},
240
+ failOnStatusCode: false
241
+ }).then((response) => {
242
+ expect(response.status).to.eq(400)
243
+ expect(response.body.success).to.be.false
244
+ expect(response.body.error).to.include('Validation')
245
+
246
+ cy.log('✓ Rejects empty action')
247
+ })
248
+ })
249
+
250
+ it('BILLING_API_041: Should reject invalid JSON', () => {
251
+ cy.request({
252
+ method: 'POST',
253
+ url: `${BASE_URL}/api/v1/billing/check-action`,
254
+ headers: {
255
+ ...billingAPI.getHeaders(),
256
+ 'Content-Type': 'application/json'
257
+ },
258
+ body: 'invalid json',
259
+ failOnStatusCode: false
260
+ }).then((response) => {
261
+ expect(response.status).to.eq(400)
262
+ expect(response.body.success).to.be.false
263
+
264
+ cy.log('✓ Rejects invalid JSON')
265
+ })
266
+ })
267
+
268
+ it('BILLING_API_042: Should validate action is non-empty string', () => {
269
+ cy.request({
270
+ method: 'POST',
271
+ url: `${BASE_URL}/api/v1/billing/check-action`,
272
+ headers: billingAPI.getHeaders(),
273
+ body: { action: '' },
274
+ failOnStatusCode: false
275
+ }).then((response) => {
276
+ expect(response.status).to.eq(400)
277
+ expect(response.body.success).to.be.false
278
+
279
+ cy.log('✓ Rejects empty action string')
280
+ })
281
+ })
282
+ })
283
+
284
+ // ============================================================
285
+ // TEST 6: Integration - Complete Flow
286
+ // ============================================================
287
+ describe('Integration', () => {
288
+ it('BILLING_API_100: Should complete full permission check flow', () => {
289
+ allure.severity('critical')
290
+
291
+ const testAction = 'tasks.create'
292
+
293
+ // 1. Check action permission
294
+ billingAPI.checkAction(testAction).then((checkResponse: any) => {
295
+ expect(checkResponse.status).to.eq(200)
296
+ expect(checkResponse.body.success).to.be.true
297
+
298
+ cy.log(`1. Permission check: ${checkResponse.body.data.allowed ? 'ALLOWED' : 'DENIED'}`)
299
+
300
+ if (checkResponse.body.data.allowed) {
301
+ cy.log(` ✓ User can perform: ${testAction}`)
302
+
303
+ if (checkResponse.body.data.quota) {
304
+ cy.log(` Quota: ${checkResponse.body.data.quota.current}/${checkResponse.body.data.quota.max}`)
305
+ }
306
+ } else {
307
+ cy.log(` ✗ Reason: ${checkResponse.body.data.reason}`)
308
+
309
+ if (checkResponse.body.data.quota) {
310
+ cy.log(` Quota exceeded: ${checkResponse.body.data.quota.current}/${checkResponse.body.data.quota.max}`)
311
+ }
312
+ }
313
+
314
+ // 2. Verify response structure is consistent
315
+ expect(checkResponse.body.data).to.have.property('allowed')
316
+
317
+ if (!checkResponse.body.data.allowed) {
318
+ expect(checkResponse.body.data).to.have.property('reason')
319
+ }
320
+
321
+ cy.log('2. Response structure validated')
322
+ cy.log('✓ Full permission check flow completed')
323
+ })
324
+ })
325
+ })
326
+ })
@@ -0,0 +1,358 @@
1
+ // @ts-nocheck
2
+ /// <reference types="cypress" />
3
+
4
+ /**
5
+ * Billing API - Checkout Endpoint Tests
6
+ *
7
+ * P2: Stripe Integration - Tests for checkout session creation
8
+ * Tests the /api/v1/billing/checkout endpoint that creates Stripe Checkout sessions
9
+ *
10
+ * Session: 2025-12-20-subscriptions-system-v2
11
+ * Phase: 9 (api-tester)
12
+ */
13
+
14
+ import * as allure from 'allure-cypress'
15
+
16
+ const BillingAPIController = require('./BillingAPIController.js')
17
+
18
+ describe('Billing API - Checkout Endpoint', () => {
19
+ let billingAPI: any
20
+
21
+ const SUPERADMIN_API_KEY = 'test_api_key_for_testing_purposes_only_not_a_real_secret_key_abc123'
22
+ const TEAM_ID = 'team-tmt-001'
23
+ const BASE_URL = Cypress.config('baseUrl') || 'http://localhost:5173'
24
+
25
+ before(() => {
26
+ billingAPI = new BillingAPIController(BASE_URL, SUPERADMIN_API_KEY, TEAM_ID)
27
+ cy.log('BillingAPIController initialized for Checkout tests')
28
+ })
29
+
30
+ beforeEach(() => {
31
+ allure.epic('API')
32
+ allure.feature('Billing')
33
+ allure.story('Checkout Endpoint')
34
+ })
35
+
36
+ // ============================================================
37
+ // TEST 1: Authentication
38
+ // ============================================================
39
+ describe('Authentication', () => {
40
+ it('CHECKOUT_API_001: Should return 401 without authentication', () => {
41
+ allure.severity('critical')
42
+
43
+ const originalApiKey = billingAPI.apiKey
44
+ billingAPI.setApiKey(null)
45
+
46
+ billingAPI.createCheckout({
47
+ planSlug: 'pro',
48
+ billingPeriod: 'monthly'
49
+ }).then((response: any) => {
50
+ expect(response.status).to.eq(401)
51
+ expect(response.body.success).to.be.false
52
+
53
+ cy.log('✓ Returns 401 without auth')
54
+ })
55
+
56
+ billingAPI.setApiKey(originalApiKey)
57
+ })
58
+
59
+ it('CHECKOUT_API_002: Should accept API key authentication', () => {
60
+ allure.severity('critical')
61
+
62
+ billingAPI.createCheckout({
63
+ planSlug: 'pro',
64
+ billingPeriod: 'monthly'
65
+ }).then((response: any) => {
66
+ // Should succeed (200) or fail gracefully (500) if Stripe not configured
67
+ expect([200, 500]).to.include(response.status)
68
+
69
+ if (response.status === 200) {
70
+ cy.log('✓ Checkout created successfully')
71
+ } else {
72
+ cy.log('✓ Auth passed, Stripe config may be missing (expected in test env)')
73
+ }
74
+ })
75
+ })
76
+ })
77
+
78
+ // ============================================================
79
+ // TEST 2: Validation
80
+ // ============================================================
81
+ describe('Validation', () => {
82
+ it('CHECKOUT_API_010: Should reject request without planSlug', () => {
83
+ allure.severity('critical')
84
+
85
+ billingAPI.createCheckout({
86
+ billingPeriod: 'monthly'
87
+ }).then((response: any) => {
88
+ expect(response.status).to.eq(400)
89
+ expect(response.body.success).to.be.false
90
+ expect(response.body.error).to.include('Validation')
91
+
92
+ cy.log('✓ Rejects missing planSlug')
93
+ })
94
+ })
95
+
96
+ it('CHECKOUT_API_011: Should reject invalid billingPeriod', () => {
97
+ billingAPI.createCheckout({
98
+ planSlug: 'pro',
99
+ billingPeriod: 'invalid-period'
100
+ }).then((response: any) => {
101
+ expect(response.status).to.eq(400)
102
+ expect(response.body.success).to.be.false
103
+
104
+ cy.log('✓ Rejects invalid billingPeriod')
105
+ })
106
+ })
107
+
108
+ it('CHECKOUT_API_012: Should default billingPeriod to monthly', () => {
109
+ billingAPI.createCheckout({
110
+ planSlug: 'pro'
111
+ // No billingPeriod specified
112
+ }).then((response: any) => {
113
+ // Should accept and default to 'monthly'
114
+ // May fail with 500 if Stripe not configured, which is OK
115
+ expect([200, 500]).to.include(response.status)
116
+
117
+ if (response.status === 500 && response.body.error) {
118
+ // Error might mention monthly in the context
119
+ cy.log('✓ Request accepted, defaults to monthly (Stripe config issue)')
120
+ } else if (response.status === 200) {
121
+ cy.log('✓ Defaults to monthly and creates session')
122
+ }
123
+ })
124
+ })
125
+
126
+ it('CHECKOUT_API_013: Should require team context', () => {
127
+ const originalTeamId = billingAPI.teamId
128
+ billingAPI.setTeamId(null)
129
+
130
+ billingAPI.createCheckout({
131
+ planSlug: 'pro',
132
+ billingPeriod: 'monthly'
133
+ }).then((response: any) => {
134
+ expect(response.status).to.eq(400)
135
+ expect(response.body.success).to.be.false
136
+ expect(response.body.error).to.include('team context')
137
+
138
+ cy.log('✓ Requires team context')
139
+ })
140
+
141
+ billingAPI.setTeamId(originalTeamId)
142
+ })
143
+
144
+ it('CHECKOUT_API_014: Should reject invalid JSON', () => {
145
+ cy.request({
146
+ method: 'POST',
147
+ url: `${BASE_URL}/api/v1/billing/checkout`,
148
+ headers: {
149
+ ...billingAPI.getHeaders(),
150
+ 'Content-Type': 'application/json'
151
+ },
152
+ body: 'invalid json',
153
+ failOnStatusCode: false
154
+ }).then((response) => {
155
+ expect(response.status).to.eq(400)
156
+ expect(response.body.success).to.be.false
157
+
158
+ cy.log('✓ Rejects invalid JSON')
159
+ })
160
+ })
161
+ })
162
+
163
+ // ============================================================
164
+ // TEST 3: Successful Checkout Creation
165
+ // ============================================================
166
+ describe('Checkout Session Creation', () => {
167
+ it('CHECKOUT_API_020: Should create checkout for pro plan monthly', () => {
168
+ allure.severity('critical')
169
+
170
+ billingAPI.createCheckout({
171
+ planSlug: 'pro',
172
+ billingPeriod: 'monthly'
173
+ }).then((response: any) => {
174
+ if (response.status === 200) {
175
+ billingAPI.validateCheckoutResponse(response)
176
+
177
+ expect(response.body.data.url).to.be.a('string')
178
+ expect(response.body.data.url.length).to.be.greaterThan(0)
179
+ expect(response.body.data.sessionId).to.be.a('string')
180
+
181
+ cy.log('✓ Checkout session created for pro monthly')
182
+ cy.log(`Session ID: ${response.body.data.sessionId}`)
183
+ } else if (response.status === 500) {
184
+ // Stripe not configured in test environment
185
+ expect(response.body.error).to.exist
186
+ cy.log('⚠ Stripe not configured (expected in test env)')
187
+ cy.log('✓ Endpoint structure correct')
188
+ } else {
189
+ throw new Error(`Unexpected status: ${response.status}`)
190
+ }
191
+ })
192
+ })
193
+
194
+ it('CHECKOUT_API_021: Should create checkout for pro plan yearly', () => {
195
+ billingAPI.createCheckout({
196
+ planSlug: 'pro',
197
+ billingPeriod: 'yearly'
198
+ }).then((response: any) => {
199
+ if (response.status === 200) {
200
+ expect(response.body.data.url).to.be.a('string')
201
+ expect(response.body.data.sessionId).to.be.a('string')
202
+
203
+ cy.log('✓ Checkout session created for pro yearly')
204
+ } else if (response.status === 500) {
205
+ cy.log('⚠ Stripe not configured (expected in test env)')
206
+ }
207
+ })
208
+ })
209
+
210
+ it('CHECKOUT_API_022: Should handle different plan slugs', () => {
211
+ const plans = ['free', 'pro', 'enterprise']
212
+
213
+ plans.forEach((planSlug) => {
214
+ billingAPI.createCheckout({
215
+ planSlug,
216
+ billingPeriod: 'monthly'
217
+ }).then((response: any) => {
218
+ // free plan might return error (no Stripe price)
219
+ // pro/enterprise might succeed or fail on Stripe config
220
+ expect([200, 400, 500]).to.include(response.status)
221
+
222
+ if (response.status === 200) {
223
+ cy.log(`✓ ${planSlug} plan checkout created`)
224
+ } else if (response.status === 400) {
225
+ cy.log(`✓ ${planSlug} plan rejected (might not have Stripe price)`)
226
+ } else {
227
+ cy.log(`✓ ${planSlug} plan handled (Stripe config issue)`)
228
+ }
229
+ })
230
+ })
231
+ })
232
+ })
233
+
234
+ // ============================================================
235
+ // TEST 4: Response Structure
236
+ // ============================================================
237
+ describe('Response Structure', () => {
238
+ it('CHECKOUT_API_030: Should return correct success response structure', () => {
239
+ billingAPI.createCheckout({
240
+ planSlug: 'pro',
241
+ billingPeriod: 'monthly'
242
+ }).then((response: any) => {
243
+ if (response.status === 200) {
244
+ expect(response.body).to.have.property('success', true)
245
+ expect(response.body).to.have.property('data')
246
+ expect(response.body.data).to.have.property('url')
247
+ expect(response.body.data).to.have.property('sessionId')
248
+
249
+ cy.log('✓ Success response structure valid')
250
+ } else {
251
+ cy.log('⚠ Skipped - Stripe not configured')
252
+ }
253
+ })
254
+ })
255
+
256
+ it('CHECKOUT_API_031: Should return error response structure on failure', () => {
257
+ billingAPI.createCheckout({
258
+ planSlug: 'nonexistent-plan',
259
+ billingPeriod: 'monthly'
260
+ }).then((response: any) => {
261
+ // Should fail with error
262
+ expect([400, 500]).to.include(response.status)
263
+ expect(response.body).to.have.property('success', false)
264
+ expect(response.body).to.have.property('error')
265
+ expect(response.body.error).to.be.a('string')
266
+
267
+ cy.log('✓ Error response structure valid')
268
+ })
269
+ })
270
+ })
271
+
272
+ // ============================================================
273
+ // TEST 5: Integration
274
+ // ============================================================
275
+ describe('Integration', () => {
276
+ it('CHECKOUT_API_100: Should complete checkout creation flow', () => {
277
+ allure.severity('critical')
278
+
279
+ const checkoutData = {
280
+ planSlug: 'pro',
281
+ billingPeriod: 'monthly'
282
+ }
283
+
284
+ cy.log('1. Creating checkout session...')
285
+
286
+ billingAPI.createCheckout(checkoutData).then((response: any) => {
287
+ cy.log(` Status: ${response.status}`)
288
+
289
+ if (response.status === 200) {
290
+ cy.log('2. Checkout session created successfully')
291
+ cy.log(` URL: ${response.body.data.url}`)
292
+ cy.log(` Session ID: ${response.body.data.sessionId}`)
293
+
294
+ // In a real E2E test, you would:
295
+ // 3. Visit the checkout URL
296
+ // 4. Fill in test card details
297
+ // 5. Complete checkout
298
+ // 6. Verify webhook received
299
+ // 7. Verify subscription updated
300
+
301
+ cy.log('✓ Checkout creation flow completed')
302
+ } else {
303
+ cy.log('2. Stripe not configured in test environment')
304
+ cy.log(' This is expected - full E2E requires Stripe test mode')
305
+ cy.log('✓ Endpoint structure validated')
306
+ }
307
+ })
308
+ })
309
+ })
310
+
311
+ // ============================================================
312
+ // TEST 6: Edge Cases
313
+ // ============================================================
314
+ describe('Edge Cases', () => {
315
+ it('CHECKOUT_API_040: Should handle empty planSlug string', () => {
316
+ billingAPI.createCheckout({
317
+ planSlug: '',
318
+ billingPeriod: 'monthly'
319
+ }).then((response: any) => {
320
+ expect(response.status).to.eq(400)
321
+ expect(response.body.success).to.be.false
322
+
323
+ cy.log('✓ Rejects empty planSlug')
324
+ })
325
+ })
326
+
327
+ it('CHECKOUT_API_041: Should handle plan with no Stripe price configured', () => {
328
+ // Free plan typically has no Stripe price
329
+ billingAPI.createCheckout({
330
+ planSlug: 'free',
331
+ billingPeriod: 'monthly'
332
+ }).then((response: any) => {
333
+ // Should fail gracefully
334
+ expect([400, 500]).to.include(response.status)
335
+ expect(response.body.success).to.be.false
336
+ expect(response.body.error).to.exist
337
+
338
+ cy.log('✓ Handles plan without Stripe price')
339
+ })
340
+ })
341
+
342
+ it('CHECKOUT_API_042: Should accept both monthly and yearly periods', () => {
343
+ const periods = ['monthly', 'yearly']
344
+
345
+ periods.forEach((period) => {
346
+ billingAPI.createCheckout({
347
+ planSlug: 'pro',
348
+ billingPeriod: period
349
+ }).then((response: any) => {
350
+ // Should either succeed or fail on Stripe config
351
+ expect([200, 500]).to.include(response.status)
352
+
353
+ cy.log(`✓ ${period} period handled correctly`)
354
+ })
355
+ })
356
+ })
357
+ })
358
+ })