@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,923 @@
1
+ /// <reference types="cypress" />
2
+
3
+ /**
4
+ * API Keys API - CRUD Tests
5
+ *
6
+ * Comprehensive test suite for the API Keys management endpoints.
7
+ * Tests CRUD operations with superadmin API key authentication.
8
+ *
9
+ * @endpoint /api/v1/api-keys
10
+ * @see api-keys-crud.md for documentation
11
+ */
12
+
13
+ import * as allure from 'allure-cypress'
14
+
15
+ const ApiKeysAPIController = require('../../../src/controllers/ApiKeysAPIController.js')
16
+
17
+ describe('API Keys API - CRUD Operations', {
18
+ tags: ['@api', '@feat-api-keys', '@crud', '@security', '@regression']
19
+ }, () => {
20
+ let apiKeysAPI: any
21
+ const testApiKeys: string[] = [] // Track created API keys for cleanup
22
+
23
+ const SUPERADMIN_API_KEY = 'test_api_key_for_testing_purposes_only_not_a_real_secret_key_abc123'
24
+ const BASE_URL = Cypress.config('baseUrl') || 'http://localhost:5173'
25
+
26
+ before(() => {
27
+ apiKeysAPI = new ApiKeysAPIController(BASE_URL, SUPERADMIN_API_KEY)
28
+ cy.log('Initialized ApiKeysAPIController with superadmin API key')
29
+
30
+ // Cleanup existing test API keys before running tests
31
+ cy.request({
32
+ method: 'GET',
33
+ url: `${BASE_URL}/api/v1/api-keys`,
34
+ headers: {
35
+ 'Authorization': `Bearer ${SUPERADMIN_API_KEY}`,
36
+ 'Content-Type': 'application/json'
37
+ },
38
+ failOnStatusCode: false
39
+ }).then((response: any) => {
40
+ if (response.status === 200 && response.body.data) {
41
+ const cypressKeys = response.body.data.filter((apiKey: any) =>
42
+ apiKey.name && apiKey.name.toLowerCase().includes('cypress')
43
+ )
44
+
45
+ cypressKeys.forEach((apiKey: any) => {
46
+ cy.request({
47
+ method: 'DELETE',
48
+ url: `${BASE_URL}/api/v1/api-keys/${apiKey.id}`,
49
+ headers: {
50
+ 'Authorization': `Bearer ${SUPERADMIN_API_KEY}`,
51
+ 'Content-Type': 'application/json'
52
+ },
53
+ failOnStatusCode: false
54
+ })
55
+ })
56
+ }
57
+ })
58
+ })
59
+
60
+ afterEach(() => {
61
+ // Cleanup API keys created during each test
62
+ testApiKeys.forEach((apiKeyId: string) => {
63
+ if (apiKeyId) {
64
+ cy.request({
65
+ method: 'DELETE',
66
+ url: `${BASE_URL}/api/v1/api-keys/${apiKeyId}`,
67
+ headers: apiKeysAPI.getHeaders(),
68
+ failOnStatusCode: false
69
+ })
70
+ }
71
+ })
72
+ testApiKeys.length = 0
73
+ })
74
+
75
+ beforeEach(() => {
76
+ allure.epic('API')
77
+ allure.feature('API Keys')
78
+ allure.story('CRUD Operations')
79
+ })
80
+
81
+ after(() => {
82
+ // Final cleanup - use simple request-based cleanup to avoid async issues
83
+ cy.request({
84
+ method: 'GET',
85
+ url: `${BASE_URL}/api/v1/api-keys`,
86
+ headers: {
87
+ 'Authorization': `Bearer ${SUPERADMIN_API_KEY}`,
88
+ 'Content-Type': 'application/json'
89
+ },
90
+ failOnStatusCode: false
91
+ }).then((response: any) => {
92
+ if (response.status === 200 && response.body.data) {
93
+ const cypressKeys = response.body.data.filter((apiKey: any) =>
94
+ apiKey.name && apiKey.name.toLowerCase().includes('cypress')
95
+ )
96
+
97
+ cypressKeys.forEach((apiKey: any) => {
98
+ cy.request({
99
+ method: 'DELETE',
100
+ url: `${BASE_URL}/api/v1/api-keys/${apiKey.id}`,
101
+ headers: {
102
+ 'Authorization': `Bearer ${SUPERADMIN_API_KEY}`,
103
+ 'Content-Type': 'application/json'
104
+ },
105
+ failOnStatusCode: false
106
+ })
107
+ })
108
+ }
109
+ })
110
+ })
111
+
112
+ // ============================================
113
+ // GET /api/v1/api-keys - List API Keys
114
+ // ============================================
115
+
116
+ describe('GET /api/v1/api-keys - List API Keys', () => {
117
+
118
+ it('APIKEY_001: Should list API keys with valid superadmin API key', { tags: '@smoke' }, () => {
119
+ allure.severity('critical')
120
+ apiKeysAPI.getApiKeys().then((response: any) => {
121
+ expect(response.status).to.eq(200)
122
+ expect(response.body).to.have.property('success', true)
123
+ expect(response.body).to.have.property('data')
124
+ expect(response.body.data).to.be.an('array')
125
+ expect(response.body).to.have.property('info')
126
+ expect(response.body.info).to.have.property('timestamp')
127
+ })
128
+ })
129
+
130
+ it('APIKEY_002: Should accept pagination parameters', () => {
131
+ apiKeysAPI.getApiKeys({ page: 1, limit: 5 }).then((response: any) => {
132
+ expect(response.status).to.eq(200)
133
+ expect(response.body).to.have.property('success', true)
134
+ expect(response.body.data).to.be.an('array')
135
+ })
136
+ })
137
+
138
+ it('APIKEY_003: Should reject request without API key', () => {
139
+ const unauthenticatedAPI = new ApiKeysAPIController(BASE_URL, null)
140
+
141
+ unauthenticatedAPI.getApiKeys().then((response: any) => {
142
+ expect(response.status).to.eq(401)
143
+ expect(response.body).to.have.property('success', false)
144
+ expect(response.body).to.have.property('code')
145
+ expect(['MISSING_API_KEY', 'AUTHENTICATION_FAILED']).to.include(response.body.code)
146
+ })
147
+ })
148
+
149
+ it('APIKEY_004: Should reject request with invalid API key', () => {
150
+ const invalidAPI = new ApiKeysAPIController(BASE_URL, 'test_invalid_key_for_testing_purposes_only_not_real_12345678901234567')
151
+
152
+ invalidAPI.getApiKeys().then((response: any) => {
153
+ expect(response.status).to.eq(401)
154
+ expect(response.body).to.have.property('success', false)
155
+ expect(['INVALID_API_KEY', 'AUTHENTICATION_FAILED']).to.include(response.body.code)
156
+ })
157
+ })
158
+
159
+ it('APIKEY_005: Should reject request with malformed API key', () => {
160
+ const malformedAPI = new ApiKeysAPIController(BASE_URL, 'not_a_valid_key')
161
+
162
+ malformedAPI.getApiKeys().then((response: any) => {
163
+ expect(response.status).to.eq(401)
164
+ expect(response.body).to.have.property('success', false)
165
+ expect(['INVALID_API_KEY', 'INVALID_API_KEY_FORMAT', 'AUTHENTICATION_FAILED']).to.include(response.body.code)
166
+ })
167
+ })
168
+
169
+ it('APIKEY_006: Should validate response structure', () => {
170
+ apiKeysAPI.getApiKeys().then((response: any) => {
171
+ expect(response.status).to.eq(200)
172
+ expect(response.body).to.have.property('success', true)
173
+ expect(response.body).to.have.property('data')
174
+ expect(response.body).to.have.property('info')
175
+ expect(response.body.info).to.have.property('timestamp')
176
+ expect(response.body.info.timestamp).to.be.a('string')
177
+ })
178
+ })
179
+
180
+ it('APIKEY_007: Should validate API key object structure in list', () => {
181
+ apiKeysAPI.getApiKeys().then((response: any) => {
182
+ expect(response.status).to.eq(200)
183
+
184
+ if (response.body.data.length > 0) {
185
+ const apiKey = response.body.data[0]
186
+ expect(apiKey).to.have.property('id')
187
+ expect(apiKey).to.have.property('name')
188
+ expect(apiKey).to.have.property('keyPrefix')
189
+ expect(apiKey).to.have.property('scopes')
190
+ expect(apiKey).to.have.property('status')
191
+ expect(apiKey).to.have.property('createdAt')
192
+ expect(apiKey.scopes).to.be.an('array')
193
+ expect(apiKey.status).to.be.oneOf(['active', 'inactive', 'expired'])
194
+ }
195
+ })
196
+ })
197
+
198
+ it('APIKEY_008: Should handle large limit values', () => {
199
+ apiKeysAPI.getApiKeys({ limit: 100 }).then((response: any) => {
200
+ expect(response.status).to.eq(200)
201
+ expect(response.body).to.have.property('success', true)
202
+ expect(response.body.data).to.be.an('array')
203
+ })
204
+ })
205
+ })
206
+
207
+ // ============================================
208
+ // POST /api/v1/api-keys - Create API Key
209
+ // ============================================
210
+
211
+ describe('POST /api/v1/api-keys - Create API Key', () => {
212
+
213
+ it('APIKEY_010: Should create API key with valid data', { tags: '@smoke' }, () => {
214
+ allure.severity('critical')
215
+ const apiKeyData = {
216
+ name: 'Cypress Test Key APIKEY_010',
217
+ scopes: ['users:read', 'tasks:read'],
218
+ expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString()
219
+ }
220
+
221
+ apiKeysAPI.createApiKey(apiKeyData).then((response: any) => {
222
+ if (response.status === 429) {
223
+ cy.log('Rate limit reached, skipping test')
224
+ return
225
+ }
226
+
227
+ expect(response.status).to.eq(201)
228
+ expect(response.body).to.have.property('success', true)
229
+ expect(response.body.data).to.have.property('id')
230
+ expect(response.body.data).to.have.property('key')
231
+ expect(response.body.data).to.have.property('name', apiKeyData.name)
232
+ expect(response.body.data).to.have.property('scopes')
233
+ expect(response.body.data.scopes).to.deep.eq(apiKeyData.scopes)
234
+ expect(response.body.data).to.have.property('warning')
235
+
236
+ testApiKeys.push(response.body.data.id)
237
+ })
238
+ })
239
+
240
+ it('APIKEY_011: Should create API key with minimal data', () => {
241
+ const apiKeyData = {
242
+ name: 'Cypress Minimal Key APIKEY_011',
243
+ scopes: ['users:read']
244
+ }
245
+
246
+ apiKeysAPI.createApiKey(apiKeyData).then((response: any) => {
247
+ if (response.status === 429) {
248
+ cy.log('Rate limit reached, skipping test')
249
+ return
250
+ }
251
+
252
+ expect(response.status).to.eq(201)
253
+ expect(response.body).to.have.property('success', true)
254
+ expect(response.body.data).to.have.property('name', apiKeyData.name)
255
+ expect(response.body.data.scopes).to.deep.eq(apiKeyData.scopes)
256
+
257
+ testApiKeys.push(response.body.data.id)
258
+ })
259
+ })
260
+
261
+ it('APIKEY_012: Should create API key with expiresAt', () => {
262
+ const futureDate = new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString()
263
+ const apiKeyData = {
264
+ name: 'Cypress Expiring Key APIKEY_012',
265
+ scopes: ['users:read'],
266
+ expiresAt: futureDate
267
+ }
268
+
269
+ apiKeysAPI.createApiKey(apiKeyData).then((response: any) => {
270
+ if (response.status === 429) {
271
+ cy.log('Rate limit reached, skipping test')
272
+ return
273
+ }
274
+
275
+ expect(response.status).to.eq(201)
276
+ expect(response.body.data).to.have.property('expiresAt')
277
+
278
+ testApiKeys.push(response.body.data.id)
279
+ })
280
+ })
281
+
282
+ it('APIKEY_013: Should reject creation without name', () => {
283
+ const apiKeyData = {
284
+ scopes: ['users:read']
285
+ }
286
+
287
+ apiKeysAPI.createApiKey(apiKeyData).then((response: any) => {
288
+ expect(response.status).to.eq(400)
289
+ expect(response.body).to.have.property('success', false)
290
+ expect(response.body).to.have.property('code', 'VALIDATION_ERROR')
291
+ })
292
+ })
293
+
294
+ it('APIKEY_014: Should reject creation with empty name', () => {
295
+ const apiKeyData = {
296
+ name: '',
297
+ scopes: ['users:read']
298
+ }
299
+
300
+ apiKeysAPI.createApiKey(apiKeyData).then((response: any) => {
301
+ expect(response.status).to.eq(400)
302
+ expect(response.body).to.have.property('success', false)
303
+ expect(response.body).to.have.property('code', 'VALIDATION_ERROR')
304
+ })
305
+ })
306
+
307
+ it('APIKEY_015: Should reject name longer than 100 characters', () => {
308
+ const apiKeyData = {
309
+ name: 'A'.repeat(101),
310
+ scopes: ['users:read']
311
+ }
312
+
313
+ apiKeysAPI.createApiKey(apiKeyData).then((response: any) => {
314
+ expect(response.status).to.eq(400)
315
+ expect(response.body).to.have.property('success', false)
316
+ expect(response.body).to.have.property('code', 'VALIDATION_ERROR')
317
+ })
318
+ })
319
+
320
+ it('APIKEY_016: Should reject creation without scopes', () => {
321
+ const apiKeyData = {
322
+ name: 'Cypress No Scopes Key'
323
+ }
324
+
325
+ apiKeysAPI.createApiKey(apiKeyData).then((response: any) => {
326
+ expect(response.status).to.eq(400)
327
+ expect(response.body).to.have.property('success', false)
328
+ expect(response.body).to.have.property('code', 'VALIDATION_ERROR')
329
+ })
330
+ })
331
+
332
+ it('APIKEY_017: Should reject creation with empty scopes array', () => {
333
+ const apiKeyData = {
334
+ name: 'Cypress Empty Scopes Key',
335
+ scopes: []
336
+ }
337
+
338
+ apiKeysAPI.createApiKey(apiKeyData).then((response: any) => {
339
+ expect(response.status).to.eq(400)
340
+ expect(response.body).to.have.property('success', false)
341
+ expect(response.body).to.have.property('code', 'VALIDATION_ERROR')
342
+ })
343
+ })
344
+
345
+ it('APIKEY_018: Should reject creation with invalid scopes', () => {
346
+ const apiKeyData = {
347
+ name: 'Cypress Invalid Scopes Key',
348
+ scopes: ['invalid:scope', 'another:invalid']
349
+ }
350
+
351
+ apiKeysAPI.createApiKey(apiKeyData).then((response: any) => {
352
+ expect(response.status).to.eq(400)
353
+ expect(response.body).to.have.property('success', false)
354
+ expect(response.body).to.have.property('code', 'INVALID_SCOPES')
355
+ })
356
+ })
357
+
358
+ it('APIKEY_019: Should reject creation with mixed valid/invalid scopes', () => {
359
+ const apiKeyData = {
360
+ name: 'Cypress Mixed Scopes Key',
361
+ scopes: ['users:read', 'invalid:scope']
362
+ }
363
+
364
+ apiKeysAPI.createApiKey(apiKeyData).then((response: any) => {
365
+ expect(response.status).to.eq(400)
366
+ expect(response.body).to.have.property('success', false)
367
+ expect(response.body).to.have.property('code', 'INVALID_SCOPES')
368
+ })
369
+ })
370
+
371
+ it('APIKEY_020: Should reject creation with invalid expiresAt format', () => {
372
+ const apiKeyData = {
373
+ name: 'Cypress Invalid Date Key',
374
+ scopes: ['users:read'],
375
+ expiresAt: 'not-a-valid-date'
376
+ }
377
+
378
+ apiKeysAPI.createApiKey(apiKeyData).then((response: any) => {
379
+ expect(response.status).to.eq(400)
380
+ expect(response.body).to.have.property('success', false)
381
+ expect(response.body).to.have.property('code', 'VALIDATION_ERROR')
382
+ })
383
+ })
384
+
385
+ it('APIKEY_021: Should reject without authorization', () => {
386
+ const unauthenticatedAPI = new ApiKeysAPIController(BASE_URL, null)
387
+ const apiKeyData = {
388
+ name: 'Cypress Unauth Key',
389
+ scopes: ['users:read']
390
+ }
391
+
392
+ unauthenticatedAPI.createApiKey(apiKeyData).then((response: any) => {
393
+ expect(response.status).to.eq(401)
394
+ expect(response.body).to.have.property('success', false)
395
+ })
396
+ })
397
+ })
398
+
399
+ // ============================================
400
+ // GET /api/v1/api-keys/{id} - Get by ID
401
+ // ============================================
402
+
403
+ describe('GET /api/v1/api-keys/{id} - Get API Key by ID', () => {
404
+ let testApiKeyId: string
405
+
406
+ beforeEach(() => {
407
+ // Create a test API key for GET tests
408
+ const apiKeyData = {
409
+ name: 'Cypress Test Key for GET',
410
+ scopes: ['users:read', 'tasks:read']
411
+ }
412
+
413
+ cy.then(() => {
414
+ return apiKeysAPI.createApiKey(apiKeyData)
415
+ }).then((response: any) => {
416
+ if (response.status === 201) {
417
+ testApiKeyId = response.body.data.id
418
+ testApiKeys.push(testApiKeyId)
419
+ } else {
420
+ cy.log('Could not create test API key, some tests may fail')
421
+ }
422
+ })
423
+ })
424
+
425
+ it('APIKEY_030: Should get API key by valid ID with usage stats', () => {
426
+ cy.then(() => testApiKeyId).then((id) => {
427
+ if (!id) {
428
+ cy.log('No test API key available, skipping')
429
+ return
430
+ }
431
+
432
+ apiKeysAPI.getApiKeyById(id).then((response: any) => {
433
+ expect(response.status).to.eq(200)
434
+ expect(response.body).to.have.property('success', true)
435
+ expect(response.body.data).to.have.property('id', id)
436
+ expect(response.body.data).to.have.property('name')
437
+ expect(response.body.data).to.have.property('usage_stats')
438
+ expect(response.body.data.usage_stats).to.be.an('object')
439
+ })
440
+ })
441
+ })
442
+
443
+ it('APIKEY_031: Should return 404 for non-existent API key ID', () => {
444
+ const nonExistentId = 'non-existent-id-12345'
445
+
446
+ apiKeysAPI.getApiKeyById(nonExistentId).then((response: any) => {
447
+ expect(response.status).to.eq(404)
448
+ expect(response.body).to.have.property('success', false)
449
+ expect(response.body).to.have.property('code', 'API_KEY_NOT_FOUND')
450
+ })
451
+ })
452
+
453
+ it('APIKEY_032: Should reject request without authorization', () => {
454
+ const unauthenticatedAPI = new ApiKeysAPIController(BASE_URL, null)
455
+
456
+ cy.then(() => testApiKeyId).then((id) => {
457
+ if (!id) {
458
+ cy.log('No test API key available, skipping')
459
+ return
460
+ }
461
+
462
+ unauthenticatedAPI.getApiKeyById(id).then((response: any) => {
463
+ expect(response.status).to.eq(401)
464
+ expect(response.body).to.have.property('success', false)
465
+ })
466
+ })
467
+ })
468
+
469
+ it('APIKEY_033: Should reject request with invalid API key', () => {
470
+ const invalidAPI = new ApiKeysAPIController(BASE_URL, 'invalid_key')
471
+
472
+ cy.then(() => testApiKeyId).then((id) => {
473
+ if (!id) {
474
+ cy.log('No test API key available, skipping')
475
+ return
476
+ }
477
+
478
+ invalidAPI.getApiKeyById(id).then((response: any) => {
479
+ expect(response.status).to.eq(401)
480
+ expect(response.body).to.have.property('success', false)
481
+ })
482
+ })
483
+ })
484
+
485
+ it('APIKEY_034: Should validate usage_stats structure', () => {
486
+ cy.then(() => testApiKeyId).then((id) => {
487
+ if (!id) {
488
+ cy.log('No test API key available, skipping')
489
+ return
490
+ }
491
+
492
+ apiKeysAPI.getApiKeyById(id).then((response: any) => {
493
+ expect(response.status).to.eq(200)
494
+ expect(response.body.data).to.have.property('usage_stats')
495
+
496
+ const stats = response.body.data.usage_stats
497
+ expect(stats).to.have.property('total_requests')
498
+ expect(stats).to.have.property('last_24h')
499
+ })
500
+ })
501
+ })
502
+ })
503
+
504
+ // ============================================
505
+ // PATCH /api/v1/api-keys/{id} - Update
506
+ // ============================================
507
+
508
+ describe('PATCH /api/v1/api-keys/{id} - Update API Key', () => {
509
+ let testApiKeyId: string
510
+
511
+ beforeEach(() => {
512
+ // Create a test API key for PATCH tests
513
+ const apiKeyData = {
514
+ name: 'Cypress Test Key for PATCH',
515
+ scopes: ['users:read']
516
+ }
517
+
518
+ cy.then(() => {
519
+ return apiKeysAPI.createApiKey(apiKeyData)
520
+ }).then((response: any) => {
521
+ if (response.status === 201) {
522
+ testApiKeyId = response.body.data.id
523
+ testApiKeys.push(testApiKeyId)
524
+ }
525
+ })
526
+ })
527
+
528
+ it('APIKEY_040: Should update API key name', () => {
529
+ cy.then(() => testApiKeyId).then((id) => {
530
+ if (!id) {
531
+ cy.log('No test API key available, skipping')
532
+ return
533
+ }
534
+
535
+ const updateData = { name: 'Cypress Updated Name APIKEY_040' }
536
+
537
+ apiKeysAPI.updateApiKey(id, updateData).then((response: any) => {
538
+ expect(response.status).to.eq(200)
539
+ expect(response.body).to.have.property('success', true)
540
+ expect(response.body.data).to.have.property('name', updateData.name)
541
+ expect(response.body.data).to.have.property('updatedAt')
542
+ })
543
+ })
544
+ })
545
+
546
+ it('APIKEY_041: Should update API key status to inactive', () => {
547
+ cy.then(() => testApiKeyId).then((id) => {
548
+ if (!id) {
549
+ cy.log('No test API key available, skipping')
550
+ return
551
+ }
552
+
553
+ const updateData = { status: 'inactive' }
554
+
555
+ apiKeysAPI.updateApiKey(id, updateData).then((response: any) => {
556
+ expect(response.status).to.eq(200)
557
+ expect(response.body).to.have.property('success', true)
558
+ expect(response.body.data).to.have.property('status', 'inactive')
559
+ })
560
+ })
561
+ })
562
+
563
+ it('APIKEY_042: Should update multiple fields at once', () => {
564
+ cy.then(() => testApiKeyId).then((id) => {
565
+ if (!id) {
566
+ cy.log('No test API key available, skipping')
567
+ return
568
+ }
569
+
570
+ const updateData = {
571
+ name: 'Cypress Multi Update APIKEY_042',
572
+ status: 'active'
573
+ }
574
+
575
+ apiKeysAPI.updateApiKey(id, updateData).then((response: any) => {
576
+ expect(response.status).to.eq(200)
577
+ expect(response.body.data).to.have.property('name', updateData.name)
578
+ expect(response.body.data).to.have.property('status', updateData.status)
579
+ })
580
+ })
581
+ })
582
+
583
+ it('APIKEY_043: Should return 404 for non-existent API key ID', () => {
584
+ const nonExistentId = 'non-existent-id-12345'
585
+ const updateData = { name: 'Updated Name' }
586
+
587
+ apiKeysAPI.updateApiKey(nonExistentId, updateData).then((response: any) => {
588
+ expect(response.status).to.eq(404)
589
+ expect(response.body).to.have.property('success', false)
590
+ expect(response.body).to.have.property('code', 'API_KEY_NOT_FOUND')
591
+ })
592
+ })
593
+
594
+ it('APIKEY_044: Should reject empty update body', () => {
595
+ cy.then(() => testApiKeyId).then((id) => {
596
+ if (!id) {
597
+ cy.log('No test API key available, skipping')
598
+ return
599
+ }
600
+
601
+ apiKeysAPI.updateApiKey(id, {}).then((response: any) => {
602
+ expect(response.status).to.eq(400)
603
+ expect(response.body).to.have.property('success', false)
604
+ expect(response.body).to.have.property('code', 'NO_FIELDS')
605
+ })
606
+ })
607
+ })
608
+
609
+ it('APIKEY_045: Should reject invalid name type', () => {
610
+ cy.then(() => testApiKeyId).then((id) => {
611
+ if (!id) {
612
+ cy.log('No test API key available, skipping')
613
+ return
614
+ }
615
+
616
+ const updateData = { name: 123 }
617
+
618
+ apiKeysAPI.updateApiKey(id, updateData).then((response: any) => {
619
+ expect(response.status).to.eq(400)
620
+ expect(response.body).to.have.property('success', false)
621
+ })
622
+ })
623
+ })
624
+
625
+ it('APIKEY_046: Should reject invalid status value', () => {
626
+ cy.then(() => testApiKeyId).then((id) => {
627
+ if (!id) {
628
+ cy.log('No test API key available, skipping')
629
+ return
630
+ }
631
+
632
+ const updateData = { status: 'invalid_status' }
633
+
634
+ apiKeysAPI.updateApiKey(id, updateData).then((response: any) => {
635
+ expect(response.status).to.eq(400)
636
+ expect(response.body).to.have.property('success', false)
637
+ })
638
+ })
639
+ })
640
+
641
+ it('APIKEY_047: Should reject update without authorization', () => {
642
+ const unauthenticatedAPI = new ApiKeysAPIController(BASE_URL, null)
643
+
644
+ cy.then(() => testApiKeyId).then((id) => {
645
+ if (!id) {
646
+ cy.log('No test API key available, skipping')
647
+ return
648
+ }
649
+
650
+ const updateData = { name: 'Unauthorized Update' }
651
+
652
+ unauthenticatedAPI.updateApiKey(id, updateData).then((response: any) => {
653
+ expect(response.status).to.eq(401)
654
+ expect(response.body).to.have.property('success', false)
655
+ })
656
+ })
657
+ })
658
+ })
659
+
660
+ // ============================================
661
+ // DELETE /api/v1/api-keys/{id} - Revoke
662
+ // ============================================
663
+
664
+ describe('DELETE /api/v1/api-keys/{id} - Revoke API Key', () => {
665
+ let testApiKeyId: string
666
+
667
+ beforeEach(() => {
668
+ // Create a test API key for DELETE tests
669
+ const apiKeyData = {
670
+ name: 'Cypress Test Key for DELETE',
671
+ scopes: ['users:read']
672
+ }
673
+
674
+ cy.then(() => {
675
+ return apiKeysAPI.createApiKey(apiKeyData)
676
+ }).then((response: any) => {
677
+ if (response.status === 201) {
678
+ testApiKeyId = response.body.data.id
679
+ // Don't add to testApiKeys since we're deleting it in the test
680
+ }
681
+ })
682
+ })
683
+
684
+ it('APIKEY_050: Should revoke API key by valid ID', () => {
685
+ cy.then(() => testApiKeyId).then((id) => {
686
+ if (!id) {
687
+ cy.log('No test API key available, skipping')
688
+ return
689
+ }
690
+
691
+ apiKeysAPI.deleteApiKey(id).then((response: any) => {
692
+ expect(response.status).to.eq(200)
693
+ expect(response.body).to.have.property('success', true)
694
+ expect(response.body.data).to.have.property('revoked', true)
695
+ expect(response.body.data).to.have.property('id', id)
696
+ })
697
+ })
698
+ })
699
+
700
+ it('APIKEY_051: Should return 404 for non-existent API key ID', () => {
701
+ const nonExistentId = 'non-existent-id-12345'
702
+
703
+ apiKeysAPI.deleteApiKey(nonExistentId).then((response: any) => {
704
+ expect(response.status).to.eq(404)
705
+ expect(response.body).to.have.property('success', false)
706
+ expect(response.body).to.have.property('code', 'API_KEY_NOT_FOUND')
707
+ })
708
+
709
+ // Cleanup the created key since we didn't delete it
710
+ cy.then(() => testApiKeyId).then((id) => {
711
+ if (id) {
712
+ testApiKeys.push(id)
713
+ }
714
+ })
715
+ })
716
+
717
+ it('APIKEY_052: Should reject revoke without authorization', () => {
718
+ const unauthenticatedAPI = new ApiKeysAPIController(BASE_URL, null)
719
+
720
+ cy.then(() => testApiKeyId).then((id) => {
721
+ if (!id) {
722
+ cy.log('No test API key available, skipping')
723
+ return
724
+ }
725
+
726
+ unauthenticatedAPI.deleteApiKey(id).then((response: any) => {
727
+ expect(response.status).to.eq(401)
728
+ expect(response.body).to.have.property('success', false)
729
+
730
+ // Cleanup since the delete failed
731
+ testApiKeys.push(id)
732
+ })
733
+ })
734
+ })
735
+
736
+ it('APIKEY_053: Should reject revoke with invalid API key', () => {
737
+ const invalidAPI = new ApiKeysAPIController(BASE_URL, 'invalid_key')
738
+
739
+ cy.then(() => testApiKeyId).then((id) => {
740
+ if (!id) {
741
+ cy.log('No test API key available, skipping')
742
+ return
743
+ }
744
+
745
+ invalidAPI.deleteApiKey(id).then((response: any) => {
746
+ expect(response.status).to.eq(401)
747
+ expect(response.body).to.have.property('success', false)
748
+
749
+ // Cleanup since the delete failed
750
+ testApiKeys.push(id)
751
+ })
752
+ })
753
+ })
754
+
755
+ it('APIKEY_054: Should verify revoked key status after deletion', () => {
756
+ cy.then(() => testApiKeyId).then((id) => {
757
+ if (!id) {
758
+ cy.log('No test API key available, skipping')
759
+ return
760
+ }
761
+
762
+ // First revoke the key
763
+ apiKeysAPI.deleteApiKey(id).then((response: any) => {
764
+ expect(response.status).to.eq(200)
765
+ expect(response.body.data).to.have.property('revoked', true)
766
+
767
+ // Then verify it's inactive
768
+ apiKeysAPI.getApiKeyById(id).then((getResponse: any) => {
769
+ if (getResponse.status === 200) {
770
+ expect(getResponse.body.data.status).to.eq('inactive')
771
+ }
772
+ // If 404, that's also acceptable (hard delete)
773
+ })
774
+ })
775
+ })
776
+ })
777
+ })
778
+
779
+ // ============================================
780
+ // Integration Tests
781
+ // ============================================
782
+
783
+ describe('Integration - Complete API Key Lifecycle', () => {
784
+
785
+ it('APIKEY_100: Should complete full lifecycle: Create -> Read -> Update -> Revoke -> Verify', () => {
786
+ let createdApiKeyId: string
787
+
788
+ // 1. Create API key
789
+ const initialData = {
790
+ name: 'Cypress Lifecycle Test Key',
791
+ scopes: ['users:read', 'tasks:read'],
792
+ expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString()
793
+ }
794
+
795
+ apiKeysAPI.createApiKey(initialData).then((createResponse: any) => {
796
+ if (createResponse.status === 429) {
797
+ cy.log('Rate limit reached, skipping integration test')
798
+ return
799
+ }
800
+
801
+ expect(createResponse.status).to.eq(201)
802
+ expect(createResponse.body).to.have.property('success', true)
803
+ expect(createResponse.body.data).to.have.property('key')
804
+
805
+ createdApiKeyId = createResponse.body.data.id
806
+ expect(createResponse.body.data.name).to.eq(initialData.name)
807
+
808
+ // 2. Read API key
809
+ return apiKeysAPI.getApiKeyById(createdApiKeyId)
810
+ }).then((readResponse: any) => {
811
+ if (!readResponse) return
812
+
813
+ expect(readResponse.status).to.eq(200)
814
+ expect(readResponse.body.data.id).to.eq(createdApiKeyId)
815
+ expect(readResponse.body.data.name).to.eq(initialData.name)
816
+ expect(readResponse.body.data).to.have.property('usage_stats')
817
+
818
+ // 3. Update API key
819
+ const updateData = {
820
+ name: 'Cypress Updated Lifecycle Key',
821
+ status: 'active'
822
+ }
823
+ return apiKeysAPI.updateApiKey(createdApiKeyId, updateData)
824
+ }).then((updateResponse: any) => {
825
+ if (!updateResponse) return
826
+
827
+ expect(updateResponse.status).to.eq(200)
828
+ expect(updateResponse.body.data.name).to.eq('Cypress Updated Lifecycle Key')
829
+ expect(updateResponse.body.data.status).to.eq('active')
830
+
831
+ // 4. Revoke API key
832
+ return apiKeysAPI.deleteApiKey(createdApiKeyId)
833
+ }).then((deleteResponse: any) => {
834
+ if (!deleteResponse) return
835
+
836
+ expect(deleteResponse.status).to.eq(200)
837
+ expect(deleteResponse.body.data.revoked).to.be.true
838
+
839
+ // 5. Verify revoked status
840
+ return apiKeysAPI.getApiKeyById(createdApiKeyId)
841
+ }).then((finalResponse: any) => {
842
+ if (!finalResponse) return
843
+
844
+ if (finalResponse.status === 200) {
845
+ expect(finalResponse.body.data.status).to.eq('inactive')
846
+ }
847
+ // 404 is also acceptable if hard delete
848
+ })
849
+ })
850
+
851
+ it('APIKEY_101: Should handle sequential operations correctly', () => {
852
+ const apiKeysCreated: string[] = []
853
+
854
+ // Create multiple API keys
855
+ const createKey = (name: string) => {
856
+ return apiKeysAPI.createApiKey({
857
+ name: name,
858
+ scopes: ['users:read']
859
+ }).then((response: any) => {
860
+ if (response.status === 201) {
861
+ apiKeysCreated.push(response.body.data.id)
862
+ return response.body.data.id
863
+ }
864
+ return null
865
+ })
866
+ }
867
+
868
+ createKey('Cypress Sequential Key 1').then(() => {
869
+ return createKey('Cypress Sequential Key 2')
870
+ }).then(() => {
871
+ // List should contain our keys
872
+ return apiKeysAPI.getApiKeys()
873
+ }).then((listResponse: any) => {
874
+ expect(listResponse.status).to.eq(200)
875
+ expect(listResponse.body.data.length).to.be.greaterThan(0)
876
+
877
+ // Cleanup
878
+ apiKeysCreated.forEach(id => {
879
+ testApiKeys.push(id)
880
+ })
881
+ })
882
+ })
883
+ })
884
+
885
+ // ============================================
886
+ // CORS Preflight Tests
887
+ // ============================================
888
+
889
+ describe('OPTIONS - CORS Preflight', () => {
890
+
891
+ it('APIKEY_110: Should handle CORS preflight requests for API keys list', () => {
892
+ cy.request({
893
+ method: 'OPTIONS',
894
+ url: `${BASE_URL}/api/v1/api-keys`,
895
+ failOnStatusCode: false
896
+ }).then((response: any) => {
897
+ expect(response.status).to.eq(200)
898
+
899
+ // Verify CORS headers
900
+ expect(response.headers).to.have.property('access-control-allow-origin')
901
+ expect(response.headers).to.have.property('access-control-allow-methods')
902
+ expect(response.headers).to.have.property('access-control-allow-headers')
903
+ })
904
+ })
905
+
906
+ it('APIKEY_111: Should handle CORS preflight requests for specific API key', () => {
907
+ const testId = 'test-id-for-options'
908
+
909
+ cy.request({
910
+ method: 'OPTIONS',
911
+ url: `${BASE_URL}/api/v1/api-keys/${testId}`,
912
+ failOnStatusCode: false
913
+ }).then((response: any) => {
914
+ expect(response.status).to.eq(200)
915
+
916
+ // Verify CORS headers
917
+ expect(response.headers).to.have.property('access-control-allow-origin')
918
+ expect(response.headers).to.have.property('access-control-allow-methods')
919
+ expect(response.headers).to.have.property('access-control-allow-headers')
920
+ })
921
+ })
922
+ })
923
+ })