@nextsparkjs/theme-default 0.1.0-beta.2 → 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.
- package/package.json +8 -4
- package/templates/(public)/page.tsx +1 -1
- package/tests/cypress/e2e/_devtools/access.bdd.md +262 -0
- package/tests/cypress/e2e/_devtools/access.cy.ts +171 -0
- package/tests/cypress/e2e/_devtools/navigation.bdd.md +261 -0
- package/tests/cypress/e2e/_devtools/navigation.cy.ts +157 -0
- package/tests/cypress/e2e/_devtools/pages.bdd.md +303 -0
- package/tests/cypress/e2e/_devtools/pages.cy.ts +184 -0
- package/tests/cypress/e2e/_docs/README.md +215 -0
- package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin-teams.narration.json +155 -0
- package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin.cy.ts +390 -0
- package/tests/cypress/e2e/_docs/tutorials/teams-system.doc.cy.ts +349 -0
- package/tests/cypress/e2e/_docs/tutorials/teams-system.narration.json +165 -0
- package/tests/cypress/e2e/_selectors/auth.cy.ts +306 -0
- package/tests/cypress/e2e/_selectors/billing.cy.ts +89 -0
- package/tests/cypress/e2e/_selectors/dashboard-mobile.cy.ts +113 -0
- package/tests/cypress/e2e/_selectors/dashboard-navigation.cy.ts +89 -0
- package/tests/cypress/e2e/_selectors/dashboard-sidebar.cy.ts +60 -0
- package/tests/cypress/e2e/_selectors/dashboard-topnav.cy.ts +146 -0
- package/tests/cypress/e2e/_selectors/devtools.cy.ts +210 -0
- package/tests/cypress/e2e/_selectors/global-search.cy.ts +88 -0
- package/tests/cypress/e2e/_selectors/pages-editor.cy.ts +179 -0
- package/tests/cypress/e2e/_selectors/posts-editor.cy.ts +282 -0
- package/tests/cypress/e2e/_selectors/public.cy.ts +112 -0
- package/tests/cypress/e2e/_selectors/settings-api-keys.cy.ts +228 -0
- package/tests/cypress/e2e/_selectors/settings-billing.cy.ts +105 -0
- package/tests/cypress/e2e/_selectors/settings-layout.cy.ts +119 -0
- package/tests/cypress/e2e/_selectors/settings-password.cy.ts +71 -0
- package/tests/cypress/e2e/_selectors/settings-profile.cy.ts +82 -0
- package/tests/cypress/e2e/_selectors/settings-teams.cy.ts +68 -0
- package/tests/cypress/e2e/_selectors/superadmin.cy.ts +185 -0
- package/tests/cypress/e2e/_selectors/tasks.cy.ts +242 -0
- package/tests/cypress/e2e/_selectors/taxonomies.cy.ts +126 -0
- package/tests/cypress/e2e/_selectors/teams.cy.ts +142 -0
- package/tests/cypress/e2e/_superadmin/all-teams.bdd.md +261 -0
- package/tests/cypress/e2e/_superadmin/all-teams.cy.ts +177 -0
- package/tests/cypress/e2e/_superadmin/all-users.bdd.md +406 -0
- package/tests/cypress/e2e/_superadmin/all-users.cy.ts +294 -0
- package/tests/cypress/e2e/_superadmin/dashboard.bdd.md +235 -0
- package/tests/cypress/e2e/_superadmin/dashboard.cy.ts +149 -0
- package/tests/cypress/e2e/_superadmin/subscriptions-overview.bdd.md +290 -0
- package/tests/cypress/e2e/_superadmin/subscriptions-overview.cy.ts +194 -0
- package/tests/cypress/e2e/ai/ai-usage.cy.ts +209 -0
- package/tests/cypress/e2e/ai/chat-api.cy.ts +107 -0
- package/tests/cypress/e2e/ai/guardrails.cy.ts +332 -0
- package/tests/cypress/e2e/api/billing/BillingAPIController.js +319 -0
- package/tests/cypress/e2e/api/billing/check-action.cy.ts +326 -0
- package/tests/cypress/e2e/api/billing/checkout.cy.ts +358 -0
- package/tests/cypress/e2e/api/billing/lifecycle.cy.ts +423 -0
- package/tests/cypress/e2e/api/billing/plans/README.md +345 -0
- package/tests/cypress/e2e/api/billing/plans/business.cy.ts +412 -0
- package/tests/cypress/e2e/api/billing/plans/downgrade.cy.ts +510 -0
- package/tests/cypress/e2e/api/billing/plans/fixtures/billing-plans.json +163 -0
- package/tests/cypress/e2e/api/billing/plans/free.cy.ts +500 -0
- package/tests/cypress/e2e/api/billing/plans/pro.cy.ts +497 -0
- package/tests/cypress/e2e/api/billing/plans/starter.cy.ts +342 -0
- package/tests/cypress/e2e/api/billing/portal.cy.ts +313 -0
- package/tests/cypress/e2e/api/devtools/registries.bdd.md +300 -0
- package/tests/cypress/e2e/api/devtools/registries.cy.ts +368 -0
- package/tests/cypress/e2e/api/entities/blocks-scope.cy.ts +396 -0
- package/tests/cypress/e2e/api/entities/customers-crud.cy.ts +648 -0
- package/tests/cypress/e2e/api/entities/customers-metas.cy.ts +839 -0
- package/tests/cypress/e2e/api/entities/pages-crud.cy.ts +425 -0
- package/tests/cypress/e2e/api/entities/pages-status.cy.ts +335 -0
- package/tests/cypress/e2e/api/entities/post-categories-crud.cy.ts +610 -0
- package/tests/cypress/e2e/api/entities/posts-crud.cy.ts +709 -0
- package/tests/cypress/e2e/api/entities/posts-status.cy.ts +396 -0
- package/tests/cypress/e2e/api/entities/tasks-crud.cy.ts +602 -0
- package/tests/cypress/e2e/api/entities/tasks-metas.cy.ts +878 -0
- package/tests/cypress/e2e/api/entities/users-crud.cy.ts +469 -0
- package/tests/cypress/e2e/api/entities/users-metas.cy.ts +913 -0
- package/tests/cypress/e2e/api/entities/users-security.cy.ts +375 -0
- package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.bdd.md +375 -0
- package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.cy.ts +346 -0
- package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.bdd.md +451 -0
- package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.cy.ts +447 -0
- package/tests/cypress/e2e/api/scheduled-actions/scheduling.bdd.md +649 -0
- package/tests/cypress/e2e/api/scheduled-actions/scheduling.cy.ts +333 -0
- package/tests/cypress/e2e/api/settings/api-keys.crud.cy.ts +923 -0
- package/tests/cypress/e2e/uat/auth/app-roles/developer-login.bdd.md +231 -0
- package/tests/cypress/e2e/uat/auth/app-roles/developer-login.cy.ts +144 -0
- package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.bdd.md +118 -0
- package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.cy.ts +84 -0
- package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.bdd.md +288 -0
- package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.cy.ts +188 -0
- package/tests/cypress/e2e/uat/auth/login-logout.bdd.md +160 -0
- package/tests/cypress/e2e/uat/auth/login-logout.cy.ts +116 -0
- package/tests/cypress/e2e/uat/auth/password-reset.bdd.md +289 -0
- package/tests/cypress/e2e/uat/auth/password-reset.cy.ts +200 -0
- package/tests/cypress/e2e/uat/auth/team-roles/admin-login.bdd.md +225 -0
- package/tests/cypress/e2e/uat/auth/team-roles/admin-login.cy.ts +148 -0
- package/tests/cypress/e2e/uat/auth/team-roles/member-login.bdd.md +251 -0
- package/tests/cypress/e2e/uat/auth/team-roles/member-login.cy.ts +163 -0
- package/tests/cypress/e2e/uat/auth/team-roles/owner-login.bdd.md +231 -0
- package/tests/cypress/e2e/uat/auth/team-roles/owner-login.cy.ts +141 -0
- package/tests/cypress/e2e/uat/billing/extended.bdd.md +273 -0
- package/tests/cypress/e2e/uat/billing/extended.cy.ts +209 -0
- package/tests/cypress/e2e/uat/billing/feature-gates.bdd.md +407 -0
- package/tests/cypress/e2e/uat/billing/feature-gates.cy.ts +307 -0
- package/tests/cypress/e2e/uat/billing/page.bdd.md +329 -0
- package/tests/cypress/e2e/uat/billing/page.cy.ts +250 -0
- package/tests/cypress/e2e/uat/billing/status.bdd.md +190 -0
- package/tests/cypress/e2e/uat/billing/status.cy.ts +145 -0
- package/tests/cypress/e2e/uat/billing/team-switch.bdd.md +156 -0
- package/tests/cypress/e2e/uat/billing/team-switch.cy.ts +122 -0
- package/tests/cypress/e2e/uat/billing/usage.bdd.md +218 -0
- package/tests/cypress/e2e/uat/billing/usage.cy.ts +176 -0
- package/tests/cypress/e2e/uat/blocks/hero.bdd.md +124 -0
- package/tests/cypress/e2e/uat/blocks/hero.cy.ts +56 -0
- package/tests/cypress/e2e/uat/devtools/api-tester.cy.ts +390 -0
- package/tests/cypress/e2e/uat/entities/customers/member.bdd.md +275 -0
- package/tests/cypress/e2e/uat/entities/customers/member.cy.ts +122 -0
- package/tests/cypress/e2e/uat/entities/customers/owner.bdd.md +243 -0
- package/tests/cypress/e2e/uat/entities/customers/owner.cy.ts +165 -0
- package/tests/cypress/e2e/uat/entities/pages/block-crud.bdd.md +476 -0
- package/tests/cypress/e2e/uat/entities/pages/block-crud.cy.ts +486 -0
- package/tests/cypress/e2e/uat/entities/pages/block-editor.bdd.md +460 -0
- package/tests/cypress/e2e/uat/entities/pages/block-editor.cy.ts +301 -0
- package/tests/cypress/e2e/uat/entities/pages/list.bdd.md +432 -0
- package/tests/cypress/e2e/uat/entities/pages/list.cy.ts +273 -0
- package/tests/cypress/e2e/uat/entities/pages/public-rendering.bdd.md +696 -0
- package/tests/cypress/e2e/uat/entities/pages/public-rendering.cy.ts +340 -0
- package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.bdd.md +161 -0
- package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.cy.ts +104 -0
- package/tests/cypress/e2e/uat/entities/posts/categories.bdd.md +375 -0
- package/tests/cypress/e2e/uat/entities/posts/categories.cy.ts +241 -0
- package/tests/cypress/e2e/uat/entities/posts/editor.bdd.md +429 -0
- package/tests/cypress/e2e/uat/entities/posts/editor.cy.ts +257 -0
- package/tests/cypress/e2e/uat/entities/posts/list.bdd.md +340 -0
- package/tests/cypress/e2e/uat/entities/posts/list.cy.ts +177 -0
- package/tests/cypress/e2e/uat/entities/posts/public.bdd.md +614 -0
- package/tests/cypress/e2e/uat/entities/posts/public.cy.ts +249 -0
- package/tests/cypress/e2e/uat/entities/tasks/member.bdd.md +222 -0
- package/tests/cypress/e2e/uat/entities/tasks/member.cy.ts +165 -0
- package/tests/cypress/e2e/uat/entities/tasks/owner.bdd.md +419 -0
- package/tests/cypress/e2e/uat/entities/tasks/owner.cy.ts +191 -0
- package/tests/cypress/e2e/uat/roles/editor-role.bdd.md +552 -0
- package/tests/cypress/e2e/uat/roles/editor-role.cy.ts +210 -0
- package/tests/cypress/e2e/uat/roles/member-restrictions.bdd.md +450 -0
- package/tests/cypress/e2e/uat/roles/member-restrictions.cy.ts +189 -0
- package/tests/cypress/e2e/uat/roles/owner-full-crud.bdd.md +530 -0
- package/tests/cypress/e2e/uat/roles/owner-full-crud.cy.ts +247 -0
- package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.bdd.md +736 -0
- package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.cy.ts +740 -0
- package/tests/cypress/e2e/uat/teams/roles-matrix.bdd.md +553 -0
- package/tests/cypress/e2e/uat/teams/roles-matrix.cy.ts +185 -0
- package/tests/cypress/e2e/uat/teams/switcher.bdd.md +1151 -0
- package/tests/cypress/e2e/uat/teams/switcher.cy.ts +497 -0
- package/tests/cypress/e2e/uat/teams/team-switcher.md +198 -0
- package/tests/cypress/fixtures/blocks.json +218 -0
- package/tests/cypress/fixtures/entities.json +78 -0
- package/tests/cypress/fixtures/page-builder.json +21 -0
- package/tests/cypress/src/components/CategoriesPOM.ts +382 -0
- package/tests/cypress/src/components/CustomersPOM.ts +439 -0
- package/tests/cypress/src/components/DevKeyringPOM.ts +160 -0
- package/tests/cypress/src/components/EntityForm.ts +375 -0
- package/tests/cypress/src/components/EntityList.ts +389 -0
- package/tests/cypress/src/components/PageBuilderPOM.ts +710 -0
- package/tests/cypress/src/components/PostEditorPOM.ts +370 -0
- package/tests/cypress/src/components/PostsListPOM.ts +223 -0
- package/tests/cypress/src/components/PublicPagePOM.ts +447 -0
- package/tests/cypress/src/components/PublicPostPOM.ts +146 -0
- package/tests/cypress/src/components/TasksPOM.ts +272 -0
- package/tests/cypress/src/components/TeamSwitcherPOM.ts +450 -0
- package/tests/cypress/src/components/index.ts +21 -0
- package/tests/cypress/src/controllers/ApiKeysAPIController.js +178 -0
- package/tests/cypress/src/controllers/BaseAPIController.js +317 -0
- package/tests/cypress/src/controllers/CustomerAPIController.js +251 -0
- package/tests/cypress/src/controllers/PagesAPIController.js +226 -0
- package/tests/cypress/src/controllers/PostsAPIController.js +250 -0
- package/tests/cypress/src/controllers/TaskAPIController.js +240 -0
- package/tests/cypress/src/controllers/UsersAPIController.js +242 -0
- package/tests/cypress/src/controllers/index.js +25 -0
- package/tests/cypress/src/core/AuthPOM.ts +450 -0
- package/tests/cypress/src/core/BasePOM.ts +86 -0
- package/tests/cypress/src/core/BlockEditorBasePOM.ts +576 -0
- package/tests/cypress/src/core/DashboardEntityPOM.ts +692 -0
- package/tests/cypress/src/core/index.ts +14 -0
- package/tests/cypress/src/entities/CustomersPOM.ts +172 -0
- package/tests/cypress/src/entities/PagesPOM.ts +137 -0
- package/tests/cypress/src/entities/PostsPOM.ts +137 -0
- package/tests/cypress/src/entities/TasksPOM.ts +176 -0
- package/tests/cypress/src/entities/index.ts +14 -0
- package/tests/cypress/src/features/BillingPOM.ts +385 -0
- package/tests/cypress/src/features/DashboardPOM.ts +245 -0
- package/tests/cypress/src/features/DevtoolsPOM.ts +739 -0
- package/tests/cypress/src/features/PageBuilderPOM.ts +263 -0
- package/tests/cypress/src/features/PostEditorPOM.ts +313 -0
- package/tests/cypress/src/features/ScheduledActionsPOM.ts +463 -0
- package/tests/cypress/src/features/SettingsPOM.ts +362 -0
- package/tests/cypress/src/features/SuperadminPOM.ts +331 -0
- package/tests/cypress/src/features/SuperadminTeamRolesPOM.ts +285 -0
- package/tests/cypress/src/features/index.ts +28 -0
- package/tests/cypress/src/helpers/ApiInterceptor.ts +177 -0
- package/tests/cypress/src/index.ts +101 -0
- package/tests/cypress/src/pages/dashboard/Dashboard.js +677 -0
- package/tests/cypress/src/pages/dashboard/DashboardPage.js +43 -0
- package/tests/cypress/src/pages/dashboard/DashboardStats.js +546 -0
- package/tests/cypress/src/pages/dashboard/index.js +6 -0
- package/tests/cypress/src/pages/index.js +5 -0
- package/tests/cypress/src/pages/public/FeaturesPage.js +28 -0
- package/tests/cypress/src/pages/public/LandingPage.js +69 -0
- package/tests/cypress/src/pages/public/PricingPage.js +33 -0
- package/tests/cypress/src/pages/public/index.js +6 -0
- package/tests/cypress/src/selectors.ts +46 -0
- package/tests/cypress/src/session-helpers.ts +500 -0
- package/tests/cypress/support/doc-commands.ts +260 -0
- package/tests/cypress.config.ts +150 -0
- package/tests/jest/components/post-header.test.tsx +377 -0
- package/tests/jest/config/role-config.test.ts +529 -0
- package/tests/jest/jest.config.ts +81 -0
- package/tests/jest/langchain/COVERAGE.md +372 -0
- package/tests/jest/langchain/guardrails.test.ts +465 -0
- package/tests/jest/langchain/streaming.test.ts +367 -0
- package/tests/jest/langchain/token-tracker.test.ts +455 -0
- package/tests/jest/langchain/tracer-callbacks.test.ts +881 -0
- package/tests/jest/langchain/tracer.test.ts +823 -0
- package/tests/jest/user-roles/role-helpers.test.ts +432 -0
- package/tests/jest/validation/categories.test.ts +429 -0
- package/tests/jest/validation/posts.test.ts +546 -0
- package/tests/tsconfig.json +15 -0
- package/LICENSE +0 -21
|
@@ -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
|
+
})
|