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

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 (220) 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.config.ts +150 -0
  208. package/tests/jest/components/post-header.test.tsx +377 -0
  209. package/tests/jest/config/role-config.test.ts +529 -0
  210. package/tests/jest/jest.config.ts +81 -0
  211. package/tests/jest/langchain/COVERAGE.md +372 -0
  212. package/tests/jest/langchain/guardrails.test.ts +465 -0
  213. package/tests/jest/langchain/streaming.test.ts +367 -0
  214. package/tests/jest/langchain/token-tracker.test.ts +455 -0
  215. package/tests/jest/langchain/tracer-callbacks.test.ts +881 -0
  216. package/tests/jest/langchain/tracer.test.ts +823 -0
  217. package/tests/jest/user-roles/role-helpers.test.ts +432 -0
  218. package/tests/jest/validation/categories.test.ts +429 -0
  219. package/tests/jest/validation/posts.test.ts +546 -0
  220. package/tests/tsconfig.json +15 -0
@@ -0,0 +1,423 @@
1
+ // @ts-nocheck
2
+ /// <reference types="cypress" />
3
+
4
+ /**
5
+ * Billing API - Lifecycle Cron Endpoint Tests
6
+ *
7
+ * P1: Lifecycle Jobs - Tests for cron job endpoint
8
+ * Tests the /api/cron/billing/lifecycle endpoint that handles subscription lifecycle
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 - Lifecycle Cron Endpoint', () => {
19
+ let billingAPI: any
20
+
21
+ const BASE_URL = Cypress.config('baseUrl') || 'http://localhost:5173'
22
+ // CRON_SECRET from environment (fallback to test value)
23
+ const CRON_SECRET = Cypress.env('CRON_SECRET') || 'test-cron-secret-min-32-characters-long-for-security'
24
+
25
+ before(() => {
26
+ billingAPI = new BillingAPIController(BASE_URL, null, null)
27
+ cy.log('BillingAPIController initialized for Lifecycle tests')
28
+ cy.log(`CRON_SECRET configured: ${CRON_SECRET ? 'Yes' : 'No'}`)
29
+ })
30
+
31
+ beforeEach(() => {
32
+ allure.epic('API')
33
+ allure.feature('Billing')
34
+ allure.story('Lifecycle Cron Endpoint')
35
+ })
36
+
37
+ // ============================================================
38
+ // TEST 1: Authentication with CRON_SECRET
39
+ // ============================================================
40
+ describe('Authentication', () => {
41
+ it('LIFECYCLE_API_001: Should return 401 without CRON_SECRET', () => {
42
+ allure.severity('critical')
43
+
44
+ cy.request({
45
+ method: 'GET',
46
+ url: `${BASE_URL}/api/cron/billing/lifecycle`,
47
+ failOnStatusCode: false
48
+ }).then((response) => {
49
+ expect(response.status).to.eq(401)
50
+ expect(response.body.error).to.include('Unauthorized')
51
+
52
+ cy.log('✓ Returns 401 without CRON_SECRET')
53
+ })
54
+ })
55
+
56
+ it('LIFECYCLE_API_002: Should return 401 with invalid CRON_SECRET', () => {
57
+ allure.severity('critical')
58
+
59
+ billingAPI.triggerLifecycle('invalid-secret').then((response: any) => {
60
+ expect(response.status).to.eq(401)
61
+ expect(response.body.error).to.include('Unauthorized')
62
+
63
+ cy.log('✓ Returns 401 with invalid CRON_SECRET')
64
+ })
65
+ })
66
+
67
+ it('LIFECYCLE_API_003: Should accept valid CRON_SECRET via Bearer token', () => {
68
+ allure.severity('critical')
69
+
70
+ billingAPI.triggerLifecycle(CRON_SECRET).then((response: any) => {
71
+ // Should succeed with 200 or fail with 500 if DB/logic issue
72
+ expect([200, 500]).to.include(response.status)
73
+
74
+ if (response.status === 200) {
75
+ cy.log('✓ Accepts valid CRON_SECRET')
76
+ } else {
77
+ cy.log('✓ Auth passed, job execution may have failed (check logs)')
78
+ }
79
+ })
80
+ })
81
+
82
+ it('LIFECYCLE_API_004: Should require Bearer prefix in Authorization header', () => {
83
+ cy.request({
84
+ method: 'GET',
85
+ url: `${BASE_URL}/api/cron/billing/lifecycle`,
86
+ headers: {
87
+ 'Authorization': CRON_SECRET // Missing "Bearer " prefix
88
+ },
89
+ failOnStatusCode: false
90
+ }).then((response) => {
91
+ expect(response.status).to.eq(401)
92
+
93
+ cy.log('✓ Requires Bearer prefix')
94
+ })
95
+ })
96
+ })
97
+
98
+ // ============================================================
99
+ // TEST 2: Successful Job Execution
100
+ // ============================================================
101
+ describe('Job Execution', () => {
102
+ it('LIFECYCLE_API_010: Should execute lifecycle jobs successfully', () => {
103
+ allure.severity('critical')
104
+
105
+ billingAPI.triggerLifecycle(CRON_SECRET).then((response: any) => {
106
+ if (response.status === 200) {
107
+ billingAPI.validateLifecycleResponse(response)
108
+
109
+ expect(response.body.success).to.be.true
110
+ expect(response.body.processed).to.be.a('number')
111
+ expect(response.body.processed).to.be.gte(0)
112
+
113
+ cy.log('✓ Lifecycle jobs executed')
114
+ cy.log(` Processed: ${response.body.processed} items`)
115
+ } else {
116
+ cy.log('⚠ Job execution failed (check server logs)')
117
+ }
118
+ })
119
+ })
120
+
121
+ it('LIFECYCLE_API_011: Should return detailed results', () => {
122
+ billingAPI.triggerLifecycle(CRON_SECRET).then((response: any) => {
123
+ if (response.status === 200) {
124
+ expect(response.body.details).to.exist
125
+ expect(response.body.details).to.have.property('expireTrials')
126
+ expect(response.body.details).to.have.property('pastDueGrace')
127
+ expect(response.body.details).to.have.property('resetUsage')
128
+
129
+ // Each job should have processed count and errors array
130
+ expect(response.body.details.expireTrials).to.have.property('processed')
131
+ expect(response.body.details.expireTrials).to.have.property('errors')
132
+ expect(response.body.details.expireTrials.errors).to.be.an('array')
133
+
134
+ cy.log('✓ Detailed results returned')
135
+ cy.log(` Expire Trials: ${response.body.details.expireTrials.processed}`)
136
+ cy.log(` Past Due Grace: ${response.body.details.pastDueGrace.processed}`)
137
+ cy.log(` Reset Usage: ${response.body.details.resetUsage.processed}`)
138
+ }
139
+ })
140
+ })
141
+
142
+ it('LIFECYCLE_API_012: Should include timestamp in response', () => {
143
+ billingAPI.triggerLifecycle(CRON_SECRET).then((response: any) => {
144
+ if (response.status === 200) {
145
+ expect(response.body.timestamp).to.exist
146
+ expect(response.body.timestamp).to.be.a('string')
147
+
148
+ // Validate ISO 8601 timestamp format
149
+ const timestamp = new Date(response.body.timestamp)
150
+ expect(timestamp.toString()).to.not.eq('Invalid Date')
151
+
152
+ cy.log('✓ Timestamp included and valid')
153
+ cy.log(` Timestamp: ${response.body.timestamp}`)
154
+ }
155
+ })
156
+ })
157
+
158
+ it('LIFECYCLE_API_013: Should return errors array if jobs fail', () => {
159
+ billingAPI.triggerLifecycle(CRON_SECRET).then((response: any) => {
160
+ if (response.status === 200) {
161
+ expect(response.body.errors).to.be.an('array')
162
+
163
+ if (response.body.errors.length > 0) {
164
+ cy.log(`⚠ Found ${response.body.errors.length} errors:`)
165
+ response.body.errors.forEach((error: string, index: number) => {
166
+ cy.log(` ${index + 1}. ${error}`)
167
+ })
168
+ } else {
169
+ cy.log('✓ No errors in job execution')
170
+ }
171
+ }
172
+ })
173
+ })
174
+ })
175
+
176
+ // ============================================================
177
+ // TEST 3: Response Structure
178
+ // ============================================================
179
+ describe('Response Structure', () => {
180
+ it('LIFECYCLE_API_020: Should return correct success response structure', () => {
181
+ billingAPI.triggerLifecycle(CRON_SECRET).then((response: any) => {
182
+ if (response.status === 200) {
183
+ expect(response.body).to.have.property('success', true)
184
+ expect(response.body).to.have.property('processed')
185
+ expect(response.body).to.have.property('errors')
186
+ expect(response.body).to.have.property('details')
187
+ expect(response.body).to.have.property('timestamp')
188
+
189
+ cy.log('✓ Success response structure valid')
190
+ }
191
+ })
192
+ })
193
+
194
+ it('LIFECYCLE_API_021: Should return error response structure on failure', () => {
195
+ // Trigger with invalid secret to force error
196
+ billingAPI.triggerLifecycle('invalid').then((response: any) => {
197
+ expect(response.status).to.eq(401)
198
+ expect(response.body).to.have.property('error')
199
+ expect(response.body.error).to.be.a('string')
200
+
201
+ cy.log('✓ Error response structure valid')
202
+ })
203
+ })
204
+ })
205
+
206
+ // ============================================================
207
+ // TEST 4: HTTP Methods Support
208
+ // ============================================================
209
+ describe('HTTP Methods', () => {
210
+ it('LIFECYCLE_API_030: Should support GET method', () => {
211
+ allure.severity('normal')
212
+
213
+ cy.request({
214
+ method: 'GET',
215
+ url: `${BASE_URL}/api/cron/billing/lifecycle`,
216
+ headers: {
217
+ 'Authorization': `Bearer ${CRON_SECRET}`
218
+ },
219
+ failOnStatusCode: false
220
+ }).then((response) => {
221
+ expect([200, 500]).to.include(response.status)
222
+
223
+ cy.log('✓ GET method supported')
224
+ })
225
+ })
226
+
227
+ it('LIFECYCLE_API_031: Should support POST method for manual triggering', () => {
228
+ cy.request({
229
+ method: 'POST',
230
+ url: `${BASE_URL}/api/cron/billing/lifecycle`,
231
+ headers: {
232
+ 'Authorization': `Bearer ${CRON_SECRET}`
233
+ },
234
+ failOnStatusCode: false
235
+ }).then((response) => {
236
+ expect([200, 500]).to.include(response.status)
237
+
238
+ cy.log('✓ POST method supported (manual trigger)')
239
+ })
240
+ })
241
+
242
+ it('LIFECYCLE_API_032: Should reject unsupported HTTP methods', () => {
243
+ cy.request({
244
+ method: 'PUT',
245
+ url: `${BASE_URL}/api/cron/billing/lifecycle`,
246
+ headers: {
247
+ 'Authorization': `Bearer ${CRON_SECRET}`
248
+ },
249
+ failOnStatusCode: false
250
+ }).then((response) => {
251
+ expect(response.status).to.eq(405)
252
+
253
+ cy.log('✓ Rejects PUT method')
254
+ })
255
+ })
256
+ })
257
+
258
+ // ============================================================
259
+ // TEST 5: Job Functions
260
+ // ============================================================
261
+ describe('Job Functions', () => {
262
+ it('LIFECYCLE_API_040: Should execute expireTrials job', () => {
263
+ billingAPI.triggerLifecycle(CRON_SECRET).then((response: any) => {
264
+ if (response.status === 200) {
265
+ const expireTrialsResult = response.body.details.expireTrials
266
+
267
+ expect(expireTrialsResult).to.exist
268
+ expect(expireTrialsResult.processed).to.be.a('number')
269
+ expect(expireTrialsResult.errors).to.be.an('array')
270
+
271
+ cy.log('✓ expireTrials job executed')
272
+ cy.log(` Expired: ${expireTrialsResult.processed} trials`)
273
+ }
274
+ })
275
+ })
276
+
277
+ it('LIFECYCLE_API_041: Should execute handlePastDueGracePeriod job', () => {
278
+ billingAPI.triggerLifecycle(CRON_SECRET).then((response: any) => {
279
+ if (response.status === 200) {
280
+ const pastDueResult = response.body.details.pastDueGrace
281
+
282
+ expect(pastDueResult).to.exist
283
+ expect(pastDueResult.processed).to.be.a('number')
284
+ expect(pastDueResult.errors).to.be.an('array')
285
+
286
+ cy.log('✓ handlePastDueGracePeriod job executed')
287
+ cy.log(` Processed: ${pastDueResult.processed} past_due subscriptions`)
288
+ }
289
+ })
290
+ })
291
+
292
+ it('LIFECYCLE_API_042: Should execute resetMonthlyUsage job (only on day 1)', () => {
293
+ billingAPI.triggerLifecycle(CRON_SECRET).then((response: any) => {
294
+ if (response.status === 200) {
295
+ const resetUsageResult = response.body.details.resetUsage
296
+
297
+ expect(resetUsageResult).to.exist
298
+ expect(resetUsageResult.processed).to.be.a('number')
299
+ expect(resetUsageResult.errors).to.be.an('array')
300
+
301
+ const today = new Date().getDate()
302
+
303
+ if (today === 1) {
304
+ cy.log('✓ resetMonthlyUsage job executed (first day of month)')
305
+ cy.log(` Reset: ${resetUsageResult.processed} usage records`)
306
+ } else {
307
+ expect(resetUsageResult.processed).to.eq(0)
308
+ cy.log('✓ resetMonthlyUsage job skipped (not first day of month)')
309
+ }
310
+ }
311
+ })
312
+ })
313
+ })
314
+
315
+ // ============================================================
316
+ // TEST 6: Integration
317
+ // ============================================================
318
+ describe('Integration', () => {
319
+ it('LIFECYCLE_API_100: Should complete full lifecycle job execution', () => {
320
+ allure.severity('critical')
321
+
322
+ cy.log('1. Triggering lifecycle cron job...')
323
+
324
+ billingAPI.triggerLifecycle(CRON_SECRET).then((response: any) => {
325
+ cy.log(` Status: ${response.status}`)
326
+
327
+ if (response.status === 200) {
328
+ cy.log('2. Job executed successfully')
329
+ cy.log(` Total processed: ${response.body.processed}`)
330
+ cy.log(` Total errors: ${response.body.errors.length}`)
331
+
332
+ cy.log('3. Job details:')
333
+ cy.log(` - Expire Trials: ${response.body.details.expireTrials.processed}`)
334
+ cy.log(` - Past Due Grace: ${response.body.details.pastDueGrace.processed}`)
335
+ cy.log(` - Reset Usage: ${response.body.details.resetUsage.processed}`)
336
+
337
+ if (response.body.errors.length > 0) {
338
+ cy.log('4. Errors encountered:')
339
+ response.body.errors.forEach((error: string, index: number) => {
340
+ cy.log(` ${index + 1}. ${error}`)
341
+ })
342
+ } else {
343
+ cy.log('4. No errors')
344
+ }
345
+
346
+ cy.log('✓ Full lifecycle job flow completed')
347
+ } else {
348
+ cy.log('2. Job execution failed')
349
+ cy.log(' Check server logs for details')
350
+ }
351
+ })
352
+ })
353
+ })
354
+
355
+ // ============================================================
356
+ // TEST 7: Idempotency
357
+ // ============================================================
358
+ describe('Idempotency', () => {
359
+ it('LIFECYCLE_API_050: Should be safe to run multiple times', () => {
360
+ allure.severity('normal')
361
+
362
+ cy.log('Running job twice in succession...')
363
+
364
+ // First execution
365
+ billingAPI.triggerLifecycle(CRON_SECRET).then((firstResponse: any) => {
366
+ if (firstResponse.status === 200) {
367
+ cy.log(`First run: ${firstResponse.body.processed} processed`)
368
+
369
+ // Second execution immediately after
370
+ billingAPI.triggerLifecycle(CRON_SECRET).then((secondResponse: any) => {
371
+ if (secondResponse.status === 200) {
372
+ cy.log(`Second run: ${secondResponse.body.processed} processed`)
373
+
374
+ // Second run should process 0 or fewer items (idempotent)
375
+ expect(secondResponse.body.processed).to.be.lte(firstResponse.body.processed)
376
+
377
+ cy.log('✓ Job is idempotent (safe to run multiple times)')
378
+ }
379
+ })
380
+ }
381
+ })
382
+ })
383
+ })
384
+
385
+ // ============================================================
386
+ // TEST 8: Edge Cases
387
+ // ============================================================
388
+ describe('Edge Cases', () => {
389
+ it('LIFECYCLE_API_060: Should handle empty CRON_SECRET', () => {
390
+ billingAPI.triggerLifecycle('').then((response: any) => {
391
+ expect(response.status).to.eq(401)
392
+
393
+ cy.log('✓ Rejects empty CRON_SECRET')
394
+ })
395
+ })
396
+
397
+ it('LIFECYCLE_API_061: Should handle missing Authorization header', () => {
398
+ cy.request({
399
+ method: 'GET',
400
+ url: `${BASE_URL}/api/cron/billing/lifecycle`,
401
+ failOnStatusCode: false
402
+ }).then((response) => {
403
+ expect(response.status).to.eq(401)
404
+
405
+ cy.log('✓ Rejects missing Authorization header')
406
+ })
407
+ })
408
+
409
+ it('LIFECYCLE_API_062: Should handle case-sensitive CRON_SECRET', () => {
410
+ if (CRON_SECRET) {
411
+ const wrongCaseSecret = CRON_SECRET.toUpperCase()
412
+
413
+ billingAPI.triggerLifecycle(wrongCaseSecret).then((response: any) => {
414
+ // Should fail unless secret happens to be all uppercase
415
+ if (CRON_SECRET !== wrongCaseSecret) {
416
+ expect(response.status).to.eq(401)
417
+ cy.log('✓ CRON_SECRET is case-sensitive')
418
+ }
419
+ })
420
+ }
421
+ })
422
+ })
423
+ })