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