@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.
- package/package.json +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/support/e2e.ts +89 -0
- package/tests/cypress.config.ts +165 -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
|
@@ -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
|
+
})
|