@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,913 @@
1
+ /// <reference types="cypress" />
2
+
3
+ /**
4
+ * Users API - Metadata Tests
5
+ *
6
+ * Comprehensive test suite for User API endpoints with metadata functionality.
7
+ * Covers GET, POST, PATCH, DELETE operations with various metadata scenarios.
8
+ * Tests metadata parameter handling, merge behavior, and upsert functionality.
9
+ */
10
+
11
+ import * as allure from 'allure-cypress'
12
+
13
+ const UsersAPIController = require('../../../src/controllers/UsersAPIController.js')
14
+
15
+ describe('Users API - Metadata Tests', {
16
+ tags: ['@api', '@feat-users', '@metas', '@regression']
17
+ }, () => {
18
+ let userAPI: any
19
+ let createdUsers: any[] = []
20
+
21
+ // Superadmin API key for testing
22
+ const SUPERADMIN_API_KEY = 'test_api_key_for_testing_purposes_only_not_a_real_secret_key_abc123'
23
+ const BASE_URL = Cypress.config('baseUrl') || 'http://localhost:5173'
24
+
25
+ before(() => {
26
+ // Initialize API controller with superadmin API key (global entity - no teamId required)
27
+ userAPI = new UsersAPIController(BASE_URL, SUPERADMIN_API_KEY)
28
+ cy.log('UsersAPIController initialized for Metadata tests')
29
+ })
30
+
31
+ beforeEach(() => {
32
+ allure.epic('API')
33
+ allure.feature('Users')
34
+ allure.story('Metadata Operations')
35
+ })
36
+
37
+ afterEach(() => {
38
+ // Cleanup: Delete users created during tests
39
+ if (createdUsers.length > 0) {
40
+ createdUsers.forEach((user: any) => {
41
+ if (user && user.id) {
42
+ userAPI.deleteUser(user.id)
43
+ }
44
+ })
45
+ createdUsers = []
46
+ }
47
+ })
48
+
49
+ // ============================================================
50
+ // GET /api/v1/users - List Users with Metadata
51
+ // ============================================================
52
+ describe('GET /api/v1/users - List Users with Metadata', () => {
53
+ let userWithMetas: any
54
+
55
+ beforeEach(() => {
56
+ // Create a user with metadata for testing
57
+ const userData = userAPI.generateRandomUserData({
58
+ firstName: 'MetaListTest'
59
+ })
60
+
61
+ userAPI.createUser(userData).then((response: any) => {
62
+ userWithMetas = response.body.data
63
+ createdUsers.push(userWithMetas)
64
+
65
+ // Add metadata to the user
66
+ const metaUpdate = {
67
+ metas: {
68
+ uiPreferences: { theme: 'dark', sidebarCollapsed: false },
69
+ securityPreferences: { twoFactorEnabled: true },
70
+ notificationPreferences: { emailEnabled: true, pushEnabled: false }
71
+ }
72
+ }
73
+ userAPI.updateUser(userWithMetas.id, metaUpdate)
74
+ })
75
+ })
76
+
77
+ it('USERS_META_001: Should list users without metas parameter', () => {
78
+ userAPI.getUsers().then((response: any) => {
79
+ userAPI.validateSuccessResponse(response, 200)
80
+ expect(response.body.data).to.be.an('array')
81
+
82
+ // Verify no metas property in user objects
83
+ response.body.data.forEach((user: any) => {
84
+ expect(user).to.not.have.property('metas')
85
+ })
86
+
87
+ cy.log('Users listed without metas property')
88
+ })
89
+ })
90
+
91
+ it('USERS_META_002: Should list users with metas=all', () => {
92
+ cy.then(() => {
93
+ userAPI.getUsers({ metas: 'all' }).then((response: any) => {
94
+ userAPI.validateSuccessResponse(response, 200)
95
+ expect(response.body.data).to.be.an('array')
96
+
97
+ // Find our user with metadata
98
+ const foundUser = response.body.data.find((u: any) => u.id === userWithMetas.id)
99
+ expect(foundUser).to.exist
100
+ expect(foundUser).to.have.property('metas')
101
+ expect(foundUser.metas).to.have.property('uiPreferences')
102
+ expect(foundUser.metas).to.have.property('securityPreferences')
103
+ expect(foundUser.metas).to.have.property('notificationPreferences')
104
+
105
+ cy.log('Users listed with all metas included')
106
+ })
107
+ })
108
+ })
109
+
110
+ it('USERS_META_003: Should list users with metas=key1 (single key)', () => {
111
+ cy.then(() => {
112
+ userAPI.getUsers({ metas: 'uiPreferences' }).then((response: any) => {
113
+ userAPI.validateSuccessResponse(response, 200)
114
+
115
+ const foundUser = response.body.data.find((u: any) => u.id === userWithMetas.id)
116
+ expect(foundUser).to.exist
117
+ expect(foundUser).to.have.property('metas')
118
+ expect(foundUser.metas).to.have.property('uiPreferences')
119
+ expect(foundUser.metas).to.not.have.property('securityPreferences')
120
+ expect(foundUser.metas).to.not.have.property('notificationPreferences')
121
+
122
+ cy.log('Users listed with only uiPreferences metas')
123
+ })
124
+ })
125
+ })
126
+
127
+ it('USERS_META_004: Should list users with metas=key1,key2 (multiple keys)', () => {
128
+ cy.then(() => {
129
+ userAPI.getUsers({ metas: 'uiPreferences,securityPreferences' }).then((response: any) => {
130
+ userAPI.validateSuccessResponse(response, 200)
131
+
132
+ const foundUser = response.body.data.find((u: any) => u.id === userWithMetas.id)
133
+ expect(foundUser).to.exist
134
+ expect(foundUser).to.have.property('metas')
135
+ expect(foundUser.metas).to.have.property('uiPreferences')
136
+ expect(foundUser.metas).to.have.property('securityPreferences')
137
+ expect(foundUser.metas).to.not.have.property('notificationPreferences')
138
+
139
+ cy.log('Users listed with uiPreferences and securityPreferences metas')
140
+ })
141
+ })
142
+ })
143
+
144
+ it('USERS_META_005: Should list users with non-existent metaKey', () => {
145
+ userAPI.getUsers({ metas: 'nonExistentKey' }).then((response: any) => {
146
+ userAPI.validateSuccessResponse(response, 200)
147
+
148
+ response.body.data.forEach((user: any) => {
149
+ expect(user).to.have.property('metas')
150
+ // Should return empty metas object for non-existent key
151
+ expect(user.metas).to.not.have.property('nonExistentKey')
152
+ })
153
+
154
+ cy.log('Users listed with non-existent metaKey returns empty metas')
155
+ })
156
+ })
157
+
158
+ it('USERS_META_006: Should list users with pagination and metas=all', () => {
159
+ cy.then(() => {
160
+ userAPI.getUsers({ page: 1, limit: 5, metas: 'all' }).then((response: any) => {
161
+ userAPI.validatePaginatedResponse(response)
162
+ expect(response.body.info.page).to.eq(1)
163
+ expect(response.body.info.limit).to.eq(5)
164
+ expect(response.body.data.length).to.be.at.most(5)
165
+
166
+ // Verify metas property exists for all users
167
+ response.body.data.forEach((user: any) => {
168
+ expect(user).to.have.property('metas')
169
+ })
170
+
171
+ cy.log('Paginated users listed with metas=all')
172
+ })
173
+ })
174
+ })
175
+ })
176
+
177
+ // ============================================================
178
+ // GET /api/v1/users/{id} - Get Single User with Metadata
179
+ // ============================================================
180
+ describe('GET /api/v1/users/{id} - Get Single User with Metadata', () => {
181
+ let testUser: any
182
+
183
+ beforeEach(() => {
184
+ // Create a test user with metadata
185
+ const userData = userAPI.generateRandomUserData({
186
+ firstName: 'MetaGetTest'
187
+ })
188
+
189
+ userAPI.createUser(userData).then((response: any) => {
190
+ testUser = response.body.data
191
+ createdUsers.push(testUser)
192
+
193
+ // Add metadata to the user
194
+ const metaUpdate = {
195
+ metas: {
196
+ uiPreferences: { theme: 'light', language: 'es' },
197
+ securityPreferences: { twoFactorEnabled: false, sessionTimeout: 3600 },
198
+ customPreferences: { feature1: 'enabled' }
199
+ }
200
+ }
201
+ userAPI.updateUser(testUser.id, metaUpdate)
202
+ })
203
+ })
204
+
205
+ it('USERS_META_010: Should get user without metas parameter', () => {
206
+ cy.then(() => {
207
+ userAPI.getUserById(testUser.id).then((response: any) => {
208
+ userAPI.validateSuccessResponse(response, 200)
209
+ userAPI.validateUserObject(response.body.data)
210
+ expect(response.body.data).to.not.have.property('metas')
211
+
212
+ cy.log('User retrieved without metas property')
213
+ })
214
+ })
215
+ })
216
+
217
+ it('USERS_META_011: Should get user with metas=all', () => {
218
+ cy.then(() => {
219
+ userAPI.getUserById(testUser.id, { metas: 'all' }).then((response: any) => {
220
+ userAPI.validateSuccessResponse(response, 200)
221
+ userAPI.validateUserObject(response.body.data, true)
222
+ expect(response.body.data).to.have.property('metas')
223
+ expect(response.body.data.metas).to.have.property('uiPreferences')
224
+ expect(response.body.data.metas).to.have.property('securityPreferences')
225
+ expect(response.body.data.metas).to.have.property('customPreferences')
226
+
227
+ cy.log('User retrieved with all metas')
228
+ })
229
+ })
230
+ })
231
+
232
+ it('USERS_META_012: Should get user with metas=key1 (single key)', () => {
233
+ cy.then(() => {
234
+ userAPI.getUserById(testUser.id, { metas: 'uiPreferences' }).then((response: any) => {
235
+ userAPI.validateSuccessResponse(response, 200)
236
+ expect(response.body.data).to.have.property('metas')
237
+ expect(response.body.data.metas).to.have.property('uiPreferences')
238
+ expect(response.body.data.metas).to.not.have.property('securityPreferences')
239
+ expect(response.body.data.metas).to.not.have.property('customPreferences')
240
+
241
+ cy.log('User retrieved with only uiPreferences metas')
242
+ })
243
+ })
244
+ })
245
+
246
+ it('USERS_META_013: Should get user with metas=key1,key2 (multiple keys)', () => {
247
+ cy.then(() => {
248
+ userAPI.getUserById(testUser.id, { metas: 'uiPreferences,securityPreferences' }).then((response: any) => {
249
+ userAPI.validateSuccessResponse(response, 200)
250
+ expect(response.body.data).to.have.property('metas')
251
+ expect(response.body.data.metas).to.have.property('uiPreferences')
252
+ expect(response.body.data.metas).to.have.property('securityPreferences')
253
+ expect(response.body.data.metas).to.not.have.property('customPreferences')
254
+
255
+ cy.log('User retrieved with uiPreferences and securityPreferences metas')
256
+ })
257
+ })
258
+ })
259
+
260
+ it('USERS_META_014: Should get user with metas=all when user has no metadata', () => {
261
+ // Create a user without metadata
262
+ const userData = userAPI.generateRandomUserData({
263
+ firstName: 'NoMetaUser'
264
+ })
265
+
266
+ userAPI.createUser(userData).then((response: any) => {
267
+ const noMetaUser = response.body.data
268
+ createdUsers.push(noMetaUser)
269
+
270
+ userAPI.getUserById(noMetaUser.id, { metas: 'all' }).then((getResponse: any) => {
271
+ userAPI.validateSuccessResponse(getResponse, 200)
272
+ expect(getResponse.body.data).to.have.property('metas')
273
+ expect(Object.keys(getResponse.body.data.metas)).to.have.lengthOf(0)
274
+
275
+ cy.log('User without metadata returns empty metas object')
276
+ })
277
+ })
278
+ })
279
+
280
+ it('USERS_META_015: Should get user by email with metas=all', () => {
281
+ cy.then(() => {
282
+ userAPI.getUserById(testUser.email, { metas: 'all' }).then((response: any) => {
283
+ userAPI.validateSuccessResponse(response, 200)
284
+ expect(response.body.data.id).to.eq(testUser.id)
285
+ expect(response.body.data).to.have.property('metas')
286
+ expect(response.body.data.metas).to.have.property('uiPreferences')
287
+
288
+ cy.log('User retrieved by email with metas=all')
289
+ })
290
+ })
291
+ })
292
+ })
293
+
294
+ // ============================================================
295
+ // POST /api/v1/users - Create User with Metadata
296
+ // ============================================================
297
+ describe('POST /api/v1/users - Create User with Metadata', () => {
298
+
299
+ it('USERS_META_020: Should create user without metas', () => {
300
+ const userData = userAPI.generateRandomUserData({
301
+ firstName: 'NoMetaCreate'
302
+ })
303
+
304
+ userAPI.createUser(userData).then((response: any) => {
305
+ userAPI.validateSuccessResponse(response, 201)
306
+ userAPI.validateUserObject(response.body.data)
307
+ expect(response.body.data).to.not.have.property('metas')
308
+
309
+ createdUsers.push(response.body.data)
310
+ cy.log('User created without metas in response')
311
+ })
312
+ })
313
+
314
+ it('USERS_META_021: Should create user with one metas group', () => {
315
+ const userData = userAPI.generateRandomUserData({
316
+ firstName: 'OneMetaCreate'
317
+ })
318
+ ;(userData as any).metas = {
319
+ uiPreferences: {
320
+ theme: 'dark',
321
+ sidebarCollapsed: true
322
+ }
323
+ }
324
+
325
+ userAPI.createUser(userData).then((response: any) => {
326
+ userAPI.validateSuccessResponse(response, 201)
327
+ expect(response.body.data).to.have.property('metas')
328
+ expect(response.body.data.metas).to.have.property('uiPreferences')
329
+ expect(response.body.data.metas.uiPreferences.theme).to.eq('dark')
330
+ expect(response.body.data.metas.uiPreferences.sidebarCollapsed).to.eq(true)
331
+
332
+ createdUsers.push(response.body.data)
333
+ cy.log('User created with one metas group')
334
+ })
335
+ })
336
+
337
+ it('USERS_META_022: Should create user with multiple metas groups', () => {
338
+ const userData = userAPI.generateRandomUserData({
339
+ firstName: 'MultiMetaCreate'
340
+ })
341
+ ;(userData as any).metas = {
342
+ uiPreferences: { theme: 'light' },
343
+ securityPreferences: { twoFactorEnabled: true },
344
+ notificationPreferences: { emailEnabled: false }
345
+ }
346
+
347
+ userAPI.createUser(userData).then((response: any) => {
348
+ userAPI.validateSuccessResponse(response, 201)
349
+ expect(response.body.data).to.have.property('metas')
350
+ expect(response.body.data.metas).to.have.property('uiPreferences')
351
+ expect(response.body.data.metas).to.have.property('securityPreferences')
352
+ expect(response.body.data.metas).to.have.property('notificationPreferences')
353
+
354
+ createdUsers.push(response.body.data)
355
+ cy.log('User created with multiple metas groups')
356
+ })
357
+ })
358
+
359
+ it('USERS_META_023: Should create user with nested metas structure', () => {
360
+ const userData = userAPI.generateRandomUserData({
361
+ firstName: 'NestedMetaCreate'
362
+ })
363
+ ;(userData as any).metas = {
364
+ workflowPreferences: {
365
+ stages: {
366
+ review: {
367
+ autoAssign: true,
368
+ notifications: {
369
+ email: true,
370
+ slack: false
371
+ }
372
+ },
373
+ testing: {
374
+ autoAssign: false
375
+ }
376
+ }
377
+ }
378
+ }
379
+
380
+ userAPI.createUser(userData).then((response: any) => {
381
+ userAPI.validateSuccessResponse(response, 201)
382
+ expect(response.body.data).to.have.property('metas')
383
+ const workflow = response.body.data.metas.workflowPreferences
384
+ expect(workflow.stages.review.autoAssign).to.eq(true)
385
+ expect(workflow.stages.review.notifications.email).to.eq(true)
386
+ expect(workflow.stages.review.notifications.slack).to.eq(false)
387
+ expect(workflow.stages.testing.autoAssign).to.eq(false)
388
+
389
+ createdUsers.push(response.body.data)
390
+ cy.log('User created with nested metas structure')
391
+ })
392
+ })
393
+
394
+ it('USERS_META_024: Should reject creation with only metas (no user fields)', () => {
395
+ const invalidData = {
396
+ metas: {
397
+ uiPreferences: { theme: 'dark' }
398
+ }
399
+ }
400
+
401
+ userAPI.createUser(invalidData).then((response: any) => {
402
+ expect(response.status).to.eq(400)
403
+ expect(response.body.success).to.be.false
404
+
405
+ cy.log('Creation rejected when only metas provided')
406
+ })
407
+ })
408
+
409
+ it('USERS_META_025: Should handle creation with invalid metas structure (string)', () => {
410
+ const userData = userAPI.generateRandomUserData({
411
+ firstName: 'InvalidMetaCreate'
412
+ })
413
+ ;(userData as any).metas = 'invalid_string'
414
+
415
+ userAPI.createUser(userData).then((response: any) => {
416
+ // API might accept and ignore invalid metas, or reject
417
+ if (response.status === 201) {
418
+ expect(response.body.data).to.not.have.property('metas')
419
+ createdUsers.push(response.body.data)
420
+ cy.log('Invalid metas ignored, user created without metas')
421
+ } else {
422
+ expect(response.status).to.eq(400)
423
+ cy.log('Invalid metas rejected')
424
+ }
425
+ })
426
+ })
427
+ })
428
+
429
+ // ============================================================
430
+ // PATCH /api/v1/users/{id} - Update Metadata
431
+ // ============================================================
432
+ describe('PATCH /api/v1/users/{id} - Update Metadata', () => {
433
+ let testUser: any
434
+
435
+ beforeEach(() => {
436
+ // Create a test user
437
+ const userData = userAPI.generateRandomUserData({
438
+ firstName: 'MetaUpdateTest'
439
+ })
440
+
441
+ userAPI.createUser(userData).then((response: any) => {
442
+ testUser = response.body.data
443
+ createdUsers.push(testUser)
444
+ })
445
+ })
446
+
447
+ it('USERS_META_030: Should update only user data (no metas in response)', () => {
448
+ // First add some metadata
449
+ const initialMeta = {
450
+ metas: {
451
+ uiPreferences: { theme: 'dark' }
452
+ }
453
+ }
454
+
455
+ cy.then(() => {
456
+ userAPI.updateUser(testUser.id, initialMeta).then(() => {
457
+ // Now update only user data
458
+ const updateData = { firstName: 'UpdatedNoMeta' }
459
+
460
+ userAPI.updateUser(testUser.id, updateData).then((response: any) => {
461
+ userAPI.validateSuccessResponse(response, 200)
462
+ expect(response.body.data.firstName).to.eq('UpdatedNoMeta')
463
+ // Response should NOT include metas when only user data is updated
464
+ expect(response.body.data).to.not.have.property('metas')
465
+
466
+ cy.log('User data updated without metas in response')
467
+ })
468
+ })
469
+ })
470
+ })
471
+
472
+ it('USERS_META_031: Should update user data and metas together', () => {
473
+ const updateData = {
474
+ firstName: 'UpdatedWithMeta',
475
+ metas: {
476
+ uiPreferences: { theme: 'light' }
477
+ }
478
+ }
479
+
480
+ cy.then(() => {
481
+ userAPI.updateUser(testUser.id, updateData).then((response: any) => {
482
+ userAPI.validateSuccessResponse(response, 200)
483
+ expect(response.body.data.firstName).to.eq('UpdatedWithMeta')
484
+ expect(response.body.data).to.have.property('metas')
485
+ expect(response.body.data.metas.uiPreferences.theme).to.eq('light')
486
+
487
+ cy.log('User data and metas updated together')
488
+ })
489
+ })
490
+ })
491
+
492
+ it('USERS_META_032: Should update only metas (no user data)', () => {
493
+ const updateData = {
494
+ metas: {
495
+ securityPreferences: { twoFactorEnabled: true }
496
+ }
497
+ }
498
+
499
+ cy.then(() => {
500
+ userAPI.updateUser(testUser.id, updateData).then((response: any) => {
501
+ userAPI.validateSuccessResponse(response, 200)
502
+ expect(response.body.data).to.have.property('metas')
503
+ expect(response.body.data.metas.securityPreferences.twoFactorEnabled).to.eq(true)
504
+
505
+ cy.log('Only metas updated successfully')
506
+ })
507
+ })
508
+ })
509
+
510
+ it('USERS_META_033: Should merge - add new key to existing metas group', () => {
511
+ // First set initial metadata
512
+ const initialMeta = {
513
+ metas: {
514
+ uiPreferences: {
515
+ theme: 'dark',
516
+ sidebar: true
517
+ }
518
+ }
519
+ }
520
+
521
+ cy.then(() => {
522
+ userAPI.updateUser(testUser.id, initialMeta).then(() => {
523
+ // Now add a new key to the same group
524
+ const addKeyUpdate = {
525
+ metas: {
526
+ uiPreferences: {
527
+ newSetting: 'value'
528
+ }
529
+ }
530
+ }
531
+
532
+ userAPI.updateUser(testUser.id, addKeyUpdate).then((response: any) => {
533
+ userAPI.validateSuccessResponse(response, 200)
534
+ const prefs = response.body.data.metas.uiPreferences
535
+ expect(prefs.theme).to.eq('dark') // Preserved
536
+ expect(prefs.sidebar).to.eq(true) // Preserved
537
+ expect(prefs.newSetting).to.eq('value') // Added
538
+
539
+ cy.log('New key added to existing metas group (merge)')
540
+ })
541
+ })
542
+ })
543
+ })
544
+
545
+ it('USERS_META_034: Should merge - modify existing key, preserve others', () => {
546
+ // First set initial metadata
547
+ const initialMeta = {
548
+ metas: {
549
+ uiPreferences: {
550
+ theme: 'dark',
551
+ language: 'en',
552
+ sidebar: true
553
+ }
554
+ }
555
+ }
556
+
557
+ cy.then(() => {
558
+ userAPI.updateUser(testUser.id, initialMeta).then(() => {
559
+ // Now modify one key
560
+ const modifyUpdate = {
561
+ metas: {
562
+ uiPreferences: {
563
+ theme: 'light' // Change this
564
+ }
565
+ }
566
+ }
567
+
568
+ userAPI.updateUser(testUser.id, modifyUpdate).then((response: any) => {
569
+ userAPI.validateSuccessResponse(response, 200)
570
+ const prefs = response.body.data.metas.uiPreferences
571
+ expect(prefs.theme).to.eq('light') // Modified
572
+ expect(prefs.language).to.eq('en') // Preserved
573
+ expect(prefs.sidebar).to.eq(true) // Preserved
574
+
575
+ cy.log('Existing key modified, others preserved (merge)')
576
+ })
577
+ })
578
+ })
579
+ })
580
+
581
+ it('USERS_META_035: Should upsert - create new metaKey if not exists', () => {
582
+ const createNewKeyUpdate = {
583
+ metas: {
584
+ customPreferences: {
585
+ feature1: 'enabled',
586
+ feature2: 'disabled'
587
+ }
588
+ }
589
+ }
590
+
591
+ cy.then(() => {
592
+ userAPI.updateUser(testUser.id, createNewKeyUpdate).then((response: any) => {
593
+ userAPI.validateSuccessResponse(response, 200)
594
+ expect(response.body.data).to.have.property('metas')
595
+ expect(response.body.data.metas).to.have.property('customPreferences')
596
+ expect(response.body.data.metas.customPreferences.feature1).to.eq('enabled')
597
+ expect(response.body.data.metas.customPreferences.feature2).to.eq('disabled')
598
+
599
+ cy.log('New metaKey created (upsert)')
600
+ })
601
+ })
602
+ })
603
+
604
+ it('USERS_META_036: Should update multiple metas groups simultaneously', () => {
605
+ // First set initial metadata
606
+ const initialMeta = {
607
+ metas: {
608
+ uiPreferences: { theme: 'dark' },
609
+ securityPreferences: { twoFactorEnabled: false }
610
+ }
611
+ }
612
+
613
+ cy.then(() => {
614
+ userAPI.updateUser(testUser.id, initialMeta).then(() => {
615
+ // Update both groups
616
+ const multiUpdate = {
617
+ metas: {
618
+ uiPreferences: { language: 'es' },
619
+ securityPreferences: { sessionTimeout: 7200 }
620
+ }
621
+ }
622
+
623
+ userAPI.updateUser(testUser.id, multiUpdate).then((response: any) => {
624
+ userAPI.validateSuccessResponse(response, 200)
625
+ const metas = response.body.data.metas
626
+
627
+ expect(metas.uiPreferences.theme).to.eq('dark') // Preserved
628
+ expect(metas.uiPreferences.language).to.eq('es') // Added
629
+ expect(metas.securityPreferences.twoFactorEnabled).to.eq(false) // Preserved
630
+ expect(metas.securityPreferences.sessionTimeout).to.eq(7200) // Added
631
+
632
+ cy.log('Multiple metas groups updated simultaneously')
633
+ })
634
+ })
635
+ })
636
+ })
637
+
638
+ it('USERS_META_037: Should handle nested objects update', () => {
639
+ // First set initial nested metadata
640
+ const initialMeta = {
641
+ metas: {
642
+ workflowPreferences: {
643
+ stages: {
644
+ review: {
645
+ autoAssign: true,
646
+ notifications: {
647
+ email: true
648
+ }
649
+ }
650
+ }
651
+ }
652
+ }
653
+ }
654
+
655
+ cy.then(() => {
656
+ userAPI.updateUser(testUser.id, initialMeta).then(() => {
657
+ // Update nested structure - Note: API may replace nested objects rather than deep merge
658
+ const nestedUpdate = {
659
+ metas: {
660
+ workflowPreferences: {
661
+ stages: {
662
+ review: {
663
+ notifications: {
664
+ slack: true,
665
+ email: true // Include existing to preserve
666
+ }
667
+ }
668
+ }
669
+ }
670
+ }
671
+ }
672
+
673
+ userAPI.updateUser(testUser.id, nestedUpdate).then((response: any) => {
674
+ userAPI.validateSuccessResponse(response, 200)
675
+ const workflow = response.body.data.metas.workflowPreferences
676
+ expect(workflow.stages.review.notifications.slack).to.eq(true)
677
+ expect(workflow.stages.review.notifications.email).to.eq(true)
678
+
679
+ cy.log('Nested objects updated correctly')
680
+ })
681
+ })
682
+ })
683
+ })
684
+
685
+ it('USERS_META_038: Should reject update with empty metas object', () => {
686
+ const emptyMetaUpdate = {
687
+ metas: {}
688
+ }
689
+
690
+ cy.then(() => {
691
+ userAPI.updateUser(testUser.id, emptyMetaUpdate).then((response: any) => {
692
+ expect(response.status).to.eq(400)
693
+ expect(response.body.success).to.be.false
694
+
695
+ cy.log('Empty metas object rejected')
696
+ })
697
+ })
698
+ })
699
+
700
+ it('USERS_META_039: Should reject update with invalid metas structure (string)', () => {
701
+ const invalidMetaUpdate = {
702
+ metas: 'invalid_string'
703
+ }
704
+
705
+ cy.then(() => {
706
+ userAPI.updateUser(testUser.id, invalidMetaUpdate).then((response: any) => {
707
+ expect(response.status).to.eq(400)
708
+ expect(response.body.success).to.be.false
709
+
710
+ cy.log('Invalid metas structure rejected')
711
+ })
712
+ })
713
+ })
714
+ })
715
+
716
+ // ============================================================
717
+ // DELETE /api/v1/users/{id} - Delete with Metadata
718
+ // ============================================================
719
+ describe('DELETE /api/v1/users/{id} - Delete with Metadata', () => {
720
+
721
+ it('USERS_META_050: Should delete user with metadata', () => {
722
+ // Create user with metadata
723
+ const userData = userAPI.generateRandomUserData({
724
+ firstName: 'DeleteWithMeta'
725
+ })
726
+ ;(userData as any).metas = {
727
+ uiPreferences: { theme: 'dark' },
728
+ securityPreferences: { twoFactorEnabled: true }
729
+ }
730
+
731
+ userAPI.createUser(userData).then((response: any) => {
732
+ const userToDelete = response.body.data
733
+
734
+ userAPI.deleteUser(userToDelete.id).then((deleteResponse: any) => {
735
+ userAPI.validateSuccessResponse(deleteResponse, 200)
736
+ expect(deleteResponse.body.data.deleted).to.eq(true)
737
+ expect(deleteResponse.body.data.id).to.eq(userToDelete.id)
738
+
739
+ cy.log('User with metadata deleted successfully')
740
+ })
741
+ })
742
+ })
743
+
744
+ it('USERS_META_051: Should verify cascade delete (GET returns 404)', () => {
745
+ // Create user with metadata
746
+ const userData = userAPI.generateRandomUserData({
747
+ firstName: 'CascadeDelete'
748
+ })
749
+ ;(userData as any).metas = {
750
+ uiPreferences: { theme: 'dark' }
751
+ }
752
+
753
+ userAPI.createUser(userData).then((response: any) => {
754
+ const userId = response.body.data.id
755
+
756
+ userAPI.deleteUser(userId).then(() => {
757
+ // Verify user is deleted
758
+ userAPI.getUserById(userId).then((getResponse: any) => {
759
+ expect(getResponse.status).to.eq(404)
760
+ expect(getResponse.body.success).to.be.false
761
+
762
+ cy.log('Cascade delete verified - user not found')
763
+ })
764
+ })
765
+ })
766
+ })
767
+
768
+ it('USERS_META_052: Should delete user without metadata normally', () => {
769
+ // Create user without metadata
770
+ const userData = userAPI.generateRandomUserData({
771
+ firstName: 'DeleteNoMeta'
772
+ })
773
+
774
+ userAPI.createUser(userData).then((response: any) => {
775
+ const userToDelete = response.body.data
776
+
777
+ userAPI.deleteUser(userToDelete.id).then((deleteResponse: any) => {
778
+ userAPI.validateSuccessResponse(deleteResponse, 200)
779
+ expect(deleteResponse.body.data.deleted).to.eq(true)
780
+
781
+ cy.log('User without metadata deleted successfully')
782
+ })
783
+ })
784
+ })
785
+ })
786
+
787
+ // ============================================================
788
+ // Integration - Complete CRUD Lifecycle with Metadata
789
+ // ============================================================
790
+ describe('Integration - Complete CRUD Lifecycle with Metadata', () => {
791
+
792
+ it('USERS_META_100: Should complete full lifecycle with metas', () => {
793
+ // 1. CREATE with metas
794
+ const userData = userAPI.generateRandomUserData({
795
+ firstName: 'LifecycleMeta',
796
+ lastName: 'TestUser'
797
+ })
798
+ ;(userData as any).metas = {
799
+ uiPreferences: { theme: 'dark', language: 'en' }
800
+ }
801
+
802
+ userAPI.createUser(userData).then((createResponse: any) => {
803
+ userAPI.validateSuccessResponse(createResponse, 201)
804
+ const createdUser = createResponse.body.data
805
+ expect(createdUser.metas.uiPreferences.theme).to.eq('dark')
806
+ cy.log('1. Created user with metas')
807
+
808
+ // 2. READ with metas=all
809
+ userAPI.getUserById(createdUser.id, { metas: 'all' }).then((readResponse: any) => {
810
+ userAPI.validateSuccessResponse(readResponse, 200)
811
+ expect(readResponse.body.data.metas.uiPreferences.theme).to.eq('dark')
812
+ cy.log('2. Read user with metas=all')
813
+
814
+ // 3. UPDATE metas
815
+ const updateData = {
816
+ metas: {
817
+ uiPreferences: { sidebar: true },
818
+ securityPreferences: { twoFactorEnabled: true }
819
+ }
820
+ }
821
+
822
+ userAPI.updateUser(createdUser.id, updateData).then((updateResponse: any) => {
823
+ userAPI.validateSuccessResponse(updateResponse, 200)
824
+ expect(updateResponse.body.data.metas.uiPreferences.theme).to.eq('dark') // Preserved
825
+ expect(updateResponse.body.data.metas.uiPreferences.sidebar).to.eq(true) // Added
826
+ expect(updateResponse.body.data.metas.securityPreferences.twoFactorEnabled).to.eq(true) // New group
827
+ cy.log('3. Updated metas (merge + new group)')
828
+
829
+ // 4. DELETE
830
+ userAPI.deleteUser(createdUser.id).then((deleteResponse: any) => {
831
+ userAPI.validateSuccessResponse(deleteResponse, 200)
832
+ expect(deleteResponse.body.data.deleted).to.eq(true)
833
+ cy.log('4. Deleted user')
834
+
835
+ // 5. VERIFY deletion
836
+ userAPI.getUserById(createdUser.id).then((finalResponse: any) => {
837
+ expect(finalResponse.status).to.eq(404)
838
+ cy.log('5. Verified deletion (404)')
839
+ cy.log('Full CRUD lifecycle with metas completed successfully')
840
+ })
841
+ })
842
+ })
843
+ })
844
+ })
845
+ })
846
+
847
+ it('USERS_META_101: Should handle multiple sequential updates (accumulative merge)', () => {
848
+ // Create user
849
+ const userData = userAPI.generateRandomUserData({
850
+ firstName: 'SequentialMeta'
851
+ })
852
+
853
+ userAPI.createUser(userData).then((createResponse: any) => {
854
+ const userId = createResponse.body.data.id
855
+ createdUsers.push(createResponse.body.data)
856
+
857
+ // Update 1: Add uiPreferences
858
+ const update1 = {
859
+ metas: {
860
+ uiPreferences: { theme: 'dark' }
861
+ }
862
+ }
863
+
864
+ userAPI.updateUser(userId, update1).then((response1: any) => {
865
+ expect(response1.body.data.metas.uiPreferences.theme).to.eq('dark')
866
+ cy.log('Update 1: Added uiPreferences.theme')
867
+
868
+ // Update 2: Add more to uiPreferences
869
+ const update2 = {
870
+ metas: {
871
+ uiPreferences: { language: 'es' }
872
+ }
873
+ }
874
+
875
+ userAPI.updateUser(userId, update2).then((response2: any) => {
876
+ expect(response2.body.data.metas.uiPreferences.theme).to.eq('dark') // Still there
877
+ expect(response2.body.data.metas.uiPreferences.language).to.eq('es') // Added
878
+ cy.log('Update 2: Added uiPreferences.language (theme preserved)')
879
+
880
+ // Update 3: Add new group
881
+ const update3 = {
882
+ metas: {
883
+ securityPreferences: { twoFactorEnabled: true }
884
+ }
885
+ }
886
+
887
+ userAPI.updateUser(userId, update3).then((response3: any) => {
888
+ expect(response3.body.data.metas.uiPreferences.theme).to.eq('dark')
889
+ expect(response3.body.data.metas.uiPreferences.language).to.eq('es')
890
+ expect(response3.body.data.metas.securityPreferences.twoFactorEnabled).to.eq(true)
891
+ cy.log('Update 3: Added securityPreferences (uiPreferences preserved)')
892
+
893
+ // Update 4: Modify existing
894
+ const update4 = {
895
+ metas: {
896
+ uiPreferences: { theme: 'light' }
897
+ }
898
+ }
899
+
900
+ userAPI.updateUser(userId, update4).then((response4: any) => {
901
+ expect(response4.body.data.metas.uiPreferences.theme).to.eq('light') // Modified
902
+ expect(response4.body.data.metas.uiPreferences.language).to.eq('es') // Preserved
903
+ expect(response4.body.data.metas.securityPreferences.twoFactorEnabled).to.eq(true) // Preserved
904
+ cy.log('Update 4: Modified uiPreferences.theme (all else preserved)')
905
+ cy.log('Sequential updates completed - accumulative merge verified')
906
+ })
907
+ })
908
+ })
909
+ })
910
+ })
911
+ })
912
+ })
913
+ })