@open-mercato/core 0.4.5-develop-2e9903a57a → 0.4.5-develop-eeccf7adf4
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 +2 -2
- package/dist/modules/auth/__integration__/TC-AUTH-001.spec.js +0 -9
- package/dist/modules/auth/__integration__/TC-AUTH-001.spec.js.map +0 -7
- package/dist/modules/auth/__integration__/TC-AUTH-002.spec.js +0 -22
- package/dist/modules/auth/__integration__/TC-AUTH-002.spec.js.map +0 -7
- package/dist/modules/auth/__integration__/TC-AUTH-003.spec.js +0 -21
- package/dist/modules/auth/__integration__/TC-AUTH-003.spec.js.map +0 -7
- package/dist/modules/auth/__integration__/TC-AUTH-004.spec.js +0 -15
- package/dist/modules/auth/__integration__/TC-AUTH-004.spec.js.map +0 -7
- package/dist/modules/auth/__integration__/TC-AUTH-005.spec.js +0 -11
- package/dist/modules/auth/__integration__/TC-AUTH-005.spec.js.map +0 -7
- package/dist/modules/auth/__integration__/TC-AUTH-006.spec.js +0 -11
- package/dist/modules/auth/__integration__/TC-AUTH-006.spec.js.map +0 -7
- package/dist/modules/auth/__integration__/TC-AUTH-007.spec.js +0 -14
- package/dist/modules/auth/__integration__/TC-AUTH-007.spec.js.map +0 -7
- package/dist/modules/auth/__integration__/TC-AUTH-008.spec.js +0 -26
- package/dist/modules/auth/__integration__/TC-AUTH-008.spec.js.map +0 -7
- package/dist/modules/auth/__integration__/TC-AUTH-009.spec.js +0 -16
- package/dist/modules/auth/__integration__/TC-AUTH-009.spec.js.map +0 -7
- package/dist/modules/auth/__integration__/TC-AUTH-010.spec.js +0 -32
- package/dist/modules/auth/__integration__/TC-AUTH-010.spec.js.map +0 -7
- package/dist/modules/auth/__integration__/TC-AUTH-011.spec.js +0 -26
- package/dist/modules/auth/__integration__/TC-AUTH-011.spec.js.map +0 -7
- package/dist/modules/auth/__integration__/TC-AUTH-012.spec.js +0 -30
- package/dist/modules/auth/__integration__/TC-AUTH-012.spec.js.map +0 -7
- package/dist/modules/auth/__integration__/TC-AUTH-013.spec.js +0 -41
- package/dist/modules/auth/__integration__/TC-AUTH-013.spec.js.map +0 -7
- package/dist/modules/auth/__integration__/TC-AUTH-014.spec.js +0 -25
- package/dist/modules/auth/__integration__/TC-AUTH-014.spec.js.map +0 -7
- package/dist/modules/auth/__integration__/TC-AUTH-015.spec.js +0 -20
- package/dist/modules/auth/__integration__/TC-AUTH-015.spec.js.map +0 -7
- package/dist/modules/auth/__integration__/TC-AUTH-016.spec.js +0 -76
- package/dist/modules/auth/__integration__/TC-AUTH-016.spec.js.map +0 -7
- package/dist/modules/catalog/__integration__/TC-CAT-001.spec.js +0 -20
- package/dist/modules/catalog/__integration__/TC-CAT-001.spec.js.map +0 -7
- package/dist/modules/catalog/__integration__/TC-CAT-002.spec.js +0 -12
- package/dist/modules/catalog/__integration__/TC-CAT-002.spec.js.map +0 -7
- package/dist/modules/catalog/__integration__/TC-CAT-003.spec.js +0 -28
- package/dist/modules/catalog/__integration__/TC-CAT-003.spec.js.map +0 -7
- package/dist/modules/catalog/__integration__/TC-CAT-004.spec.js +0 -33
- package/dist/modules/catalog/__integration__/TC-CAT-004.spec.js.map +0 -7
- package/dist/modules/catalog/__integration__/TC-CAT-005.spec.js +0 -30
- package/dist/modules/catalog/__integration__/TC-CAT-005.spec.js.map +0 -7
- package/dist/modules/catalog/__integration__/TC-CAT-006.spec.js +0 -31
- package/dist/modules/catalog/__integration__/TC-CAT-006.spec.js.map +0 -7
- package/dist/modules/catalog/__integration__/TC-CAT-007.spec.js +0 -31
- package/dist/modules/catalog/__integration__/TC-CAT-007.spec.js.map +0 -7
- package/dist/modules/catalog/__integration__/TC-CAT-008.spec.js +0 -65
- package/dist/modules/catalog/__integration__/TC-CAT-008.spec.js.map +0 -7
- package/dist/modules/catalog/__integration__/TC-CAT-009.spec.js +0 -31
- package/dist/modules/catalog/__integration__/TC-CAT-009.spec.js.map +0 -7
- package/dist/modules/catalog/__integration__/TC-CAT-010.spec.js +0 -28
- package/dist/modules/catalog/__integration__/TC-CAT-010.spec.js.map +0 -7
- package/dist/modules/catalog/__integration__/TC-CAT-011.spec.js +0 -34
- package/dist/modules/catalog/__integration__/TC-CAT-011.spec.js.map +0 -7
- package/dist/modules/catalog/__integration__/TC-CAT-012.spec.js +0 -27
- package/dist/modules/catalog/__integration__/TC-CAT-012.spec.js.map +0 -7
- package/dist/modules/core/__integration__/admin/TC-ADMIN-001.spec.js +0 -44
- package/dist/modules/core/__integration__/admin/TC-ADMIN-001.spec.js.map +0 -7
- package/dist/modules/core/__integration__/admin/TC-ADMIN-002.spec.js +0 -53
- package/dist/modules/core/__integration__/admin/TC-ADMIN-002.spec.js.map +0 -7
- package/dist/modules/core/__integration__/admin/TC-ADMIN-003.spec.js +0 -26
- package/dist/modules/core/__integration__/admin/TC-ADMIN-003.spec.js.map +0 -7
- package/dist/modules/core/__integration__/admin/TC-ADMIN-004.spec.js +0 -47
- package/dist/modules/core/__integration__/admin/TC-ADMIN-004.spec.js.map +0 -7
- package/dist/modules/core/__integration__/admin/TC-ADMIN-005.spec.js +0 -33
- package/dist/modules/core/__integration__/admin/TC-ADMIN-005.spec.js.map +0 -7
- package/dist/modules/core/__integration__/admin/TC-ADMIN-006.spec.js +0 -30
- package/dist/modules/core/__integration__/admin/TC-ADMIN-006.spec.js.map +0 -7
- package/dist/modules/core/__integration__/admin/TC-ADMIN-007.spec.js +0 -36
- package/dist/modules/core/__integration__/admin/TC-ADMIN-007.spec.js.map +0 -7
- package/dist/modules/core/__integration__/admin/TC-ADMIN-008.spec.js +0 -113
- package/dist/modules/core/__integration__/admin/TC-ADMIN-008.spec.js.map +0 -7
- package/dist/modules/core/__integration__/admin/TC-ADMIN-009.spec.js +0 -25
- package/dist/modules/core/__integration__/admin/TC-ADMIN-009.spec.js.map +0 -7
- package/dist/modules/core/__integration__/admin/TC-ADMIN-010.spec.js +0 -35
- package/dist/modules/core/__integration__/admin/TC-ADMIN-010.spec.js.map +0 -7
- package/dist/modules/core/__integration__/helpers/api.js +0 -64
- package/dist/modules/core/__integration__/helpers/api.js.map +0 -7
- package/dist/modules/core/__integration__/helpers/auth.js +0 -98
- package/dist/modules/core/__integration__/helpers/auth.js.map +0 -7
- package/dist/modules/core/__integration__/helpers/authUi.js +0 -31
- package/dist/modules/core/__integration__/helpers/authUi.js.map +0 -7
- package/dist/modules/core/__integration__/helpers/catalogFixtures.js +0 -49
- package/dist/modules/core/__integration__/helpers/catalogFixtures.js.map +0 -7
- package/dist/modules/core/__integration__/helpers/crmFixtures.js +0 -73
- package/dist/modules/core/__integration__/helpers/crmFixtures.js.map +0 -7
- package/dist/modules/core/__integration__/helpers/salesFixtures.js +0 -63
- package/dist/modules/core/__integration__/helpers/salesFixtures.js.map +0 -7
- package/dist/modules/core/__integration__/helpers/salesUi.js +0 -464
- package/dist/modules/core/__integration__/helpers/salesUi.js.map +0 -7
- package/dist/modules/core/__integration__/integration/TC-INT-001.spec.js +0 -26
- package/dist/modules/core/__integration__/integration/TC-INT-001.spec.js.map +0 -7
- package/dist/modules/core/__integration__/integration/TC-INT-002.spec.js +0 -60
- package/dist/modules/core/__integration__/integration/TC-INT-002.spec.js.map +0 -7
- package/dist/modules/core/__integration__/integration/TC-INT-003.spec.js +0 -36
- package/dist/modules/core/__integration__/integration/TC-INT-003.spec.js.map +0 -7
- package/dist/modules/core/__integration__/integration/TC-INT-004.spec.js +0 -74
- package/dist/modules/core/__integration__/integration/TC-INT-004.spec.js.map +0 -7
- package/dist/modules/core/__integration__/integration/TC-INT-005.spec.js +0 -21
- package/dist/modules/core/__integration__/integration/TC-INT-005.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-001.spec.js +0 -32
- package/dist/modules/customers/__integration__/TC-CRM-001.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-002.spec.js +0 -35
- package/dist/modules/customers/__integration__/TC-CRM-002.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-003.spec.js +0 -40
- package/dist/modules/customers/__integration__/TC-CRM-003.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-004.spec.js +0 -40
- package/dist/modules/customers/__integration__/TC-CRM-004.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-005.spec.js +0 -37
- package/dist/modules/customers/__integration__/TC-CRM-005.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-006.spec.js +0 -42
- package/dist/modules/customers/__integration__/TC-CRM-006.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-007.spec.js +0 -44
- package/dist/modules/customers/__integration__/TC-CRM-007.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-008.spec.js +0 -50
- package/dist/modules/customers/__integration__/TC-CRM-008.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-009.spec.js +0 -33
- package/dist/modules/customers/__integration__/TC-CRM-009.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-010.spec.js +0 -39
- package/dist/modules/customers/__integration__/TC-CRM-010.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-011.spec.js +0 -37
- package/dist/modules/customers/__integration__/TC-CRM-011.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-012.spec.js +0 -38
- package/dist/modules/customers/__integration__/TC-CRM-012.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-013.spec.js +0 -43
- package/dist/modules/customers/__integration__/TC-CRM-013.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-014.spec.js +0 -23
- package/dist/modules/customers/__integration__/TC-CRM-014.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-015.spec.js +0 -63
- package/dist/modules/customers/__integration__/TC-CRM-015.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-016.spec.js +0 -46
- package/dist/modules/customers/__integration__/TC-CRM-016.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-017.spec.js +0 -29
- package/dist/modules/customers/__integration__/TC-CRM-017.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-018.spec.js +0 -52
- package/dist/modules/customers/__integration__/TC-CRM-018.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-019.spec.js +0 -37
- package/dist/modules/customers/__integration__/TC-CRM-019.spec.js.map +0 -7
- package/dist/modules/customers/__integration__/TC-CRM-020.spec.js +0 -65
- package/dist/modules/customers/__integration__/TC-CRM-020.spec.js.map +0 -7
- package/dist/modules/progress/__integration__/TC-PROG-001.spec.js +0 -51
- package/dist/modules/progress/__integration__/TC-PROG-001.spec.js.map +0 -7
- package/dist/modules/resources/__integration__/TC-INT-007.spec.js +0 -88
- package/dist/modules/resources/__integration__/TC-INT-007.spec.js.map +0 -7
- package/dist/modules/resources/__integration__/helpers/resourcesFixtures.js +0 -45
- package/dist/modules/resources/__integration__/helpers/resourcesFixtures.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-001.spec.js +0 -20
- package/dist/modules/sales/__integration__/TC-SALES-001.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-002.spec.js +0 -31
- package/dist/modules/sales/__integration__/TC-SALES-002.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-003.spec.js +0 -13
- package/dist/modules/sales/__integration__/TC-SALES-003.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-004.spec.js +0 -14
- package/dist/modules/sales/__integration__/TC-SALES-004.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-005.spec.js +0 -15
- package/dist/modules/sales/__integration__/TC-SALES-005.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-006.spec.js +0 -20
- package/dist/modules/sales/__integration__/TC-SALES-006.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-007.spec.js +0 -19
- package/dist/modules/sales/__integration__/TC-SALES-007.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-008.spec.js +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-008.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-009.spec.js +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-009.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-010.spec.js +0 -16
- package/dist/modules/sales/__integration__/TC-SALES-010.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-011.spec.js +0 -20
- package/dist/modules/sales/__integration__/TC-SALES-011.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-012.spec.js +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-012.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-013.spec.js +0 -66
- package/dist/modules/sales/__integration__/TC-SALES-013.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-014.spec.js +0 -13
- package/dist/modules/sales/__integration__/TC-SALES-014.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-015.spec.js +0 -13
- package/dist/modules/sales/__integration__/TC-SALES-015.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-016.spec.js +0 -13
- package/dist/modules/sales/__integration__/TC-SALES-016.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-017.spec.js +0 -44
- package/dist/modules/sales/__integration__/TC-SALES-017.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-018.spec.js +0 -18
- package/dist/modules/sales/__integration__/TC-SALES-018.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-019.spec.js +0 -16
- package/dist/modules/sales/__integration__/TC-SALES-019.spec.js.map +0 -7
- package/dist/modules/sales/__integration__/TC-SALES-020.spec.js +0 -75
- package/dist/modules/sales/__integration__/TC-SALES-020.spec.js.map +0 -7
- package/dist/modules/staff/__integration__/TC-INT-006.spec.js +0 -64
- package/dist/modules/staff/__integration__/TC-INT-006.spec.js.map +0 -7
- package/dist/modules/translations/__integration__/TC-TRANS-001.spec.js +0 -48
- package/dist/modules/translations/__integration__/TC-TRANS-001.spec.js.map +0 -7
- package/dist/modules/translations/__integration__/TC-TRANS-002.spec.js +0 -94
- package/dist/modules/translations/__integration__/TC-TRANS-002.spec.js.map +0 -7
- package/dist/modules/translations/__integration__/TC-TRANS-003.spec.js +0 -61
- package/dist/modules/translations/__integration__/TC-TRANS-003.spec.js.map +0 -7
- package/dist/modules/translations/__integration__/TC-TRANS-004.spec.js +0 -52
- package/dist/modules/translations/__integration__/TC-TRANS-004.spec.js.map +0 -7
- package/dist/modules/translations/__integration__/TC-TRANS-005.spec.js +0 -106
- package/dist/modules/translations/__integration__/TC-TRANS-005.spec.js.map +0 -7
- package/dist/modules/translations/__integration__/TC-TRANS-006.spec.js +0 -94
- package/dist/modules/translations/__integration__/TC-TRANS-006.spec.js.map +0 -7
- package/dist/modules/translations/__integration__/TC-TRANS-007.spec.js +0 -62
- package/dist/modules/translations/__integration__/TC-TRANS-007.spec.js.map +0 -7
- package/dist/modules/translations/__integration__/TC-TRANS-008.spec.js +0 -168
- package/dist/modules/translations/__integration__/TC-TRANS-008.spec.js.map +0 -7
- package/dist/modules/translations/__integration__/helpers/translationFixtures.js +0 -63
- package/dist/modules/translations/__integration__/helpers/translationFixtures.js.map +0 -7
- package/dist/modules/workflows/__integration__/TC-WF-001.spec.js +0 -73
- package/dist/modules/workflows/__integration__/TC-WF-001.spec.js.map +0 -7
- package/src/__tests__/module-decoupling.test.ts +0 -356
- package/src/modules/api_keys/api/__tests__/keys.route.test.ts +0 -244
- package/src/modules/attachments/api/__tests__/attachments.api.test.ts +0 -240
- package/src/modules/attachments/components/__tests__/AttachmentContentPreview.test.tsx +0 -45
- package/src/modules/attachments/data/__tests__/entities-ocr.test.ts +0 -15
- package/src/modules/attachments/lib/__tests__/ocr-config.test.ts +0 -27
- package/src/modules/attachments/lib/__tests__/textExtraction.test.ts +0 -64
- package/src/modules/audit_logs/api/__tests__/access.route.test.ts +0 -118
- package/src/modules/audit_logs/api/__tests__/redo.route.test.ts +0 -131
- package/src/modules/audit_logs/api/__tests__/undo.route.test.ts +0 -103
- package/src/modules/audit_logs/services/__tests__/actionLogService.test.ts +0 -26
- package/src/modules/auth/__integration__/TC-AUTH-001.spec.ts +0 -13
- package/src/modules/auth/__integration__/TC-AUTH-002.spec.ts +0 -30
- package/src/modules/auth/__integration__/TC-AUTH-003.spec.ts +0 -28
- package/src/modules/auth/__integration__/TC-AUTH-004.spec.ts +0 -21
- package/src/modules/auth/__integration__/TC-AUTH-005.spec.ts +0 -17
- package/src/modules/auth/__integration__/TC-AUTH-006.spec.ts +0 -17
- package/src/modules/auth/__integration__/TC-AUTH-007.spec.ts +0 -19
- package/src/modules/auth/__integration__/TC-AUTH-008.spec.ts +0 -31
- package/src/modules/auth/__integration__/TC-AUTH-009.spec.ts +0 -22
- package/src/modules/auth/__integration__/TC-AUTH-010.spec.ts +0 -39
- package/src/modules/auth/__integration__/TC-AUTH-011.spec.ts +0 -35
- package/src/modules/auth/__integration__/TC-AUTH-012.spec.ts +0 -36
- package/src/modules/auth/__integration__/TC-AUTH-013.spec.ts +0 -48
- package/src/modules/auth/__integration__/TC-AUTH-014.spec.ts +0 -31
- package/src/modules/auth/__integration__/TC-AUTH-015.spec.ts +0 -28
- package/src/modules/auth/__integration__/TC-AUTH-016.spec.ts +0 -109
- package/src/modules/auth/__tests__/cli-rotate-encryption.test.ts +0 -97
- package/src/modules/auth/__tests__/cli-setup-acl.test.ts +0 -148
- package/src/modules/auth/api/__tests__/feature-check.test.ts +0 -65
- package/src/modules/auth/api/__tests__/login.test.ts +0 -47
- package/src/modules/auth/commands/__tests__/roles.custom-fields.test.ts +0 -126
- package/src/modules/auth/commands/__tests__/users.custom-fields.test.ts +0 -147
- package/src/modules/auth/lib/__tests__/rateLimitCheck.test.ts +0 -224
- package/src/modules/auth/services/__tests__/authService.test.ts +0 -32
- package/src/modules/auth/services/__tests__/rbacService.test.ts +0 -814
- package/src/modules/business_rules/api/__tests__/execute.route.test.ts +0 -311
- package/src/modules/business_rules/api/__tests__/logs-detail.route.test.ts +0 -181
- package/src/modules/business_rules/api/__tests__/logs.route.test.ts +0 -261
- package/src/modules/business_rules/api/__tests__/rules-detail.route.test.ts +0 -115
- package/src/modules/business_rules/api/__tests__/rules.route.test.ts +0 -746
- package/src/modules/business_rules/api/__tests__/sets-detail.route.test.ts +0 -169
- package/src/modules/business_rules/api/__tests__/sets-members.route.test.ts +0 -367
- package/src/modules/business_rules/api/__tests__/sets.route.test.ts +0 -361
- package/src/modules/business_rules/api/__tests__/test-helpers.ts +0 -42
- package/src/modules/business_rules/components/utils/__tests__/formHelpers.test.ts +0 -69
- package/src/modules/business_rules/data/__tests__/validators.test.ts +0 -637
- package/src/modules/business_rules/lib/__tests__/action-executor.test.ts +0 -728
- package/src/modules/business_rules/lib/__tests__/expression-evaluator.test.ts +0 -592
- package/src/modules/business_rules/lib/__tests__/rule-engine.test.ts +0 -805
- package/src/modules/business_rules/lib/__tests__/rule-evaluator.test.ts +0 -436
- package/src/modules/catalog/__integration__/TC-CAT-001.spec.ts +0 -32
- package/src/modules/catalog/__integration__/TC-CAT-002.spec.ts +0 -19
- package/src/modules/catalog/__integration__/TC-CAT-003.spec.ts +0 -39
- package/src/modules/catalog/__integration__/TC-CAT-004.spec.ts +0 -41
- package/src/modules/catalog/__integration__/TC-CAT-005.spec.ts +0 -37
- package/src/modules/catalog/__integration__/TC-CAT-006.spec.ts +0 -40
- package/src/modules/catalog/__integration__/TC-CAT-007.spec.ts +0 -37
- package/src/modules/catalog/__integration__/TC-CAT-008.spec.ts +0 -76
- package/src/modules/catalog/__integration__/TC-CAT-009.spec.ts +0 -39
- package/src/modules/catalog/__integration__/TC-CAT-010.spec.ts +0 -36
- package/src/modules/catalog/__integration__/TC-CAT-011.spec.ts +0 -44
- package/src/modules/catalog/__integration__/TC-CAT-012.spec.ts +0 -35
- package/src/modules/catalog/api/__tests__/offers.route.test.ts +0 -161
- package/src/modules/catalog/api/__tests__/prices.route.test.ts +0 -39
- package/src/modules/catalog/api/__tests__/products.route.test.ts +0 -91
- package/src/modules/catalog/api/__tests__/utils.test.ts +0 -36
- package/src/modules/catalog/api/__tests__/variants.route.test.ts +0 -44
- package/src/modules/catalog/backend/catalog/products/__tests__/ProductsDataTable.test.tsx +0 -172
- package/src/modules/catalog/commands/__tests__/products.delete.test.ts +0 -146
- package/src/modules/catalog/commands/__tests__/products.update.test.ts +0 -142
- package/src/modules/catalog/commands/__tests__/registration.test.ts +0 -54
- package/src/modules/catalog/commands/__tests__/shared.test.ts +0 -129
- package/src/modules/catalog/components/__tests__/catalogComponentsRender.test.tsx +0 -373
- package/src/modules/catalog/components/products/__tests__/ProductImageCell.test.tsx +0 -51
- package/src/modules/catalog/components/products/__tests__/productForm.test.ts +0 -32
- package/src/modules/catalog/lib/__tests__/pricing.test.ts +0 -150
- package/src/modules/catalog/services/__tests__/catalogPricingService.test.ts +0 -21
- package/src/modules/configs/components/__tests__/CachePanel.test.tsx +0 -134
- package/src/modules/configs/components/__tests__/SystemStatusPanel.test.tsx +0 -93
- package/src/modules/configs/lib/__tests__/system-status.test.ts +0 -55
- package/src/modules/configs/lib/__tests__/upgrade-actions.test.ts +0 -135
- package/src/modules/core/__integration__/admin/TC-ADMIN-001.spec.ts +0 -70
- package/src/modules/core/__integration__/admin/TC-ADMIN-002.spec.ts +0 -83
- package/src/modules/core/__integration__/admin/TC-ADMIN-003.spec.ts +0 -50
- package/src/modules/core/__integration__/admin/TC-ADMIN-004.spec.ts +0 -77
- package/src/modules/core/__integration__/admin/TC-ADMIN-005.spec.ts +0 -49
- package/src/modules/core/__integration__/admin/TC-ADMIN-006.spec.ts +0 -59
- package/src/modules/core/__integration__/admin/TC-ADMIN-007.spec.ts +0 -68
- package/src/modules/core/__integration__/admin/TC-ADMIN-008.spec.ts +0 -127
- package/src/modules/core/__integration__/admin/TC-ADMIN-009.spec.ts +0 -48
- package/src/modules/core/__integration__/admin/TC-ADMIN-010.spec.ts +0 -57
- package/src/modules/core/__integration__/helpers/api.ts +0 -84
- package/src/modules/core/__integration__/helpers/auth.ts +0 -110
- package/src/modules/core/__integration__/helpers/authUi.ts +0 -33
- package/src/modules/core/__integration__/helpers/catalogFixtures.ts +0 -73
- package/src/modules/core/__integration__/helpers/crmFixtures.ts +0 -101
- package/src/modules/core/__integration__/helpers/salesFixtures.ts +0 -89
- package/src/modules/core/__integration__/helpers/salesUi.ts +0 -528
- package/src/modules/core/__integration__/integration/TC-INT-001.spec.ts +0 -34
- package/src/modules/core/__integration__/integration/TC-INT-002.spec.ts +0 -74
- package/src/modules/core/__integration__/integration/TC-INT-003.spec.ts +0 -43
- package/src/modules/core/__integration__/integration/TC-INT-004.spec.ts +0 -82
- package/src/modules/core/__integration__/integration/TC-INT-005.spec.ts +0 -29
- package/src/modules/currencies/backend/exchange-rates/__tests__/formatDateTimeLocal.test.ts +0 -78
- package/src/modules/currencies/data/__tests__/validators.test.ts +0 -100
- package/src/modules/currencies/services/__tests__/exchangeRateService.test.ts +0 -666
- package/src/modules/currencies/services/__tests__/rateFetchingService.basic.test.ts +0 -398
- package/src/modules/currencies/services/__tests__/rateFetchingService.errors.test.ts +0 -296
- package/src/modules/currencies/services/__tests__/rateFetchingService.providers.test.ts +0 -350
- package/src/modules/currencies/services/__tests__/rateFetchingService.setup.ts +0 -188
- package/src/modules/customers/__integration__/TC-CRM-001.spec.ts +0 -42
- package/src/modules/customers/__integration__/TC-CRM-002.spec.ts +0 -47
- package/src/modules/customers/__integration__/TC-CRM-003.spec.ts +0 -55
- package/src/modules/customers/__integration__/TC-CRM-004.spec.ts +0 -57
- package/src/modules/customers/__integration__/TC-CRM-005.spec.ts +0 -50
- package/src/modules/customers/__integration__/TC-CRM-006.spec.ts +0 -60
- package/src/modules/customers/__integration__/TC-CRM-007.spec.ts +0 -57
- package/src/modules/customers/__integration__/TC-CRM-008.spec.ts +0 -62
- package/src/modules/customers/__integration__/TC-CRM-009.spec.ts +0 -46
- package/src/modules/customers/__integration__/TC-CRM-010.spec.ts +0 -49
- package/src/modules/customers/__integration__/TC-CRM-011.spec.ts +0 -47
- package/src/modules/customers/__integration__/TC-CRM-012.spec.ts +0 -49
- package/src/modules/customers/__integration__/TC-CRM-013.spec.ts +0 -61
- package/src/modules/customers/__integration__/TC-CRM-014.spec.ts +0 -31
- package/src/modules/customers/__integration__/TC-CRM-015.spec.ts +0 -89
- package/src/modules/customers/__integration__/TC-CRM-016.spec.ts +0 -55
- package/src/modules/customers/__integration__/TC-CRM-017.spec.ts +0 -37
- package/src/modules/customers/__integration__/TC-CRM-018.spec.ts +0 -62
- package/src/modules/customers/__integration__/TC-CRM-019.spec.ts +0 -44
- package/src/modules/customers/__integration__/TC-CRM-020.spec.ts +0 -73
- package/src/modules/customers/api/__tests__/utils.test.ts +0 -61
- package/src/modules/customers/api/dashboard/widgets/new-deals/__tests__/route.test.ts +0 -54
- package/src/modules/customers/commands/__tests__/shared.test.ts +0 -263
- package/src/modules/customers/commands/__tests__/undo.custom-fields.test.ts +0 -1184
- package/src/modules/customers/components/detail/__tests__/ActivityForm.validation.test.ts +0 -37
- package/src/modules/customers/components/detail/__tests__/DealForm.validation.test.ts +0 -45
- package/src/modules/customers/components/detail/__tests__/InlineEditors.test.tsx +0 -166
- package/src/modules/customers/components/detail/__tests__/TaskForm.submit.test.ts +0 -21
- package/src/modules/customers/components/detail/hooks/__tests__/useCustomerDictionary.test.ts +0 -97
- package/src/modules/customers/lib/__tests__/customFieldRouting.test.ts +0 -107
- package/src/modules/customers/utils/__tests__/addressFormat.test.ts +0 -105
- package/src/modules/customers/utils/__tests__/phoneDuplicates.test.ts +0 -98
- package/src/modules/dashboards/__tests__/widgets.test.ts +0 -70
- package/src/modules/dashboards/lib/__tests__/aggregations.test.ts +0 -328
- package/src/modules/dashboards/lib/__tests__/formatters.test.ts +0 -128
- package/src/modules/directory/backend/directory/organizations/__tests__/create-submit.test.ts +0 -46
- package/src/modules/directory/backend/directory/organizations/__tests__/edit-submit.test.ts +0 -49
- package/src/modules/directory/components/__tests__/OrganizationSelect.test.tsx +0 -71
- package/src/modules/directory/components/__tests__/TenantSelect.test.tsx +0 -75
- package/src/modules/entities/__tests__/cli-decrypt-database.test.ts +0 -534
- package/src/modules/entities/__tests__/cli-rotate-encryption.test.ts +0 -123
- package/src/modules/entities/api/__tests__/encryption.api.test.ts +0 -57
- package/src/modules/entities/api/__tests__/records.get.custom-entity.test.ts +0 -43
- package/src/modules/entities/api/__tests__/records.validation.test.ts +0 -53
- package/src/modules/entities/backend/entities/user/__tests__/create-entity-submit.test.ts +0 -47
- package/src/modules/entities/backend/entities/user/__tests__/records-submit.test.ts +0 -104
- package/src/modules/feature_toggles/commands/__tests__/global.test.ts +0 -325
- package/src/modules/feature_toggles/commands/__tests__/overrides.test.ts +0 -186
- package/src/modules/feature_toggles/lib/__tests__/feature-flag-check.test.ts +0 -365
- package/src/modules/feature_toggles/lib/__tests__/queries.test.ts +0 -130
- package/src/modules/inbox_ops/api/emails/[id]/reprocess/__tests__/route.test.ts +0 -194
- package/src/modules/inbox_ops/api/proposals/[id]/__tests__/route.test.ts +0 -124
- package/src/modules/inbox_ops/api/proposals/[id]/accept-all/__tests__/route.test.ts +0 -154
- package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/__tests__/route.test.ts +0 -200
- package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/accept/__tests__/route.test.ts +0 -261
- package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/reject/__tests__/route.test.ts +0 -201
- package/src/modules/inbox_ops/api/proposals/[id]/reject/__tests__/route.test.ts +0 -123
- package/src/modules/inbox_ops/api/proposals/[id]/replies/[replyId]/send/__tests__/route.test.ts +0 -232
- package/src/modules/inbox_ops/api/proposals/[id]/translate/__tests__/route.test.ts +0 -173
- package/src/modules/inbox_ops/api/proposals/__tests__/route.test.ts +0 -185
- package/src/modules/inbox_ops/api/webhook/__tests__/inbound.test.ts +0 -317
- package/src/modules/inbox_ops/data/__tests__/validators.test.ts +0 -463
- package/src/modules/inbox_ops/lib/__tests__/catalogLookup.test.ts +0 -143
- package/src/modules/inbox_ops/lib/__tests__/contactMatcher.test.ts +0 -158
- package/src/modules/inbox_ops/lib/__tests__/emailParser.test.ts +0 -191
- package/src/modules/inbox_ops/lib/__tests__/executionEngine.test.ts +0 -1419
- package/src/modules/inbox_ops/lib/__tests__/extractionPrompt.test.ts +0 -151
- package/src/modules/inbox_ops/lib/__tests__/priceValidator.test.ts +0 -259
- package/src/modules/inbox_ops/lib/__tests__/translationProvider.test.ts +0 -99
- package/src/modules/inbox_ops/subscribers/__tests__/extractionWorker.test.ts +0 -803
- package/src/modules/notifications/__tests__/deliver-notification.test.ts +0 -285
- package/src/modules/notifications/__tests__/deliveryStrategies.test.ts +0 -19
- package/src/modules/notifications/__tests__/notificationService.test.ts +0 -248
- package/src/modules/planner/__tests__/availabilityMerge.test.ts +0 -99
- package/src/modules/planner/__tests__/plannerAvailabilityService.test.ts +0 -89
- package/src/modules/planner/data/__tests__/validators.test.ts +0 -78
- package/src/modules/progress/__integration__/TC-PROG-001.spec.ts +0 -67
- package/src/modules/progress/__tests__/progressService.test.ts +0 -377
- package/src/modules/query_index/__tests__/hybrid-engine.test.ts +0 -365
- package/src/modules/query_index/__tests__/indexer.test.ts +0 -175
- package/src/modules/resources/__integration__/TC-INT-007.spec.ts +0 -110
- package/src/modules/resources/__integration__/helpers/resourcesFixtures.ts +0 -50
- package/src/modules/resources/data/__tests__/validators.test.ts +0 -65
- package/src/modules/sales/__integration__/TC-SALES-001.spec.ts +0 -26
- package/src/modules/sales/__integration__/TC-SALES-002.spec.ts +0 -38
- package/src/modules/sales/__integration__/TC-SALES-003.spec.ts +0 -18
- package/src/modules/sales/__integration__/TC-SALES-004.spec.ts +0 -19
- package/src/modules/sales/__integration__/TC-SALES-005.spec.ts +0 -21
- package/src/modules/sales/__integration__/TC-SALES-006.spec.ts +0 -26
- package/src/modules/sales/__integration__/TC-SALES-007.spec.ts +0 -23
- package/src/modules/sales/__integration__/TC-SALES-008.spec.ts +0 -11
- package/src/modules/sales/__integration__/TC-SALES-009.spec.ts +0 -12
- package/src/modules/sales/__integration__/TC-SALES-010.spec.ts +0 -20
- package/src/modules/sales/__integration__/TC-SALES-011.spec.ts +0 -26
- package/src/modules/sales/__integration__/TC-SALES-012.spec.ts +0 -12
- package/src/modules/sales/__integration__/TC-SALES-013.spec.ts +0 -73
- package/src/modules/sales/__integration__/TC-SALES-014.spec.ts +0 -17
- package/src/modules/sales/__integration__/TC-SALES-015.spec.ts +0 -17
- package/src/modules/sales/__integration__/TC-SALES-016.spec.ts +0 -17
- package/src/modules/sales/__integration__/TC-SALES-017.spec.ts +0 -60
- package/src/modules/sales/__integration__/TC-SALES-018.spec.ts +0 -25
- package/src/modules/sales/__integration__/TC-SALES-019.spec.ts +0 -22
- package/src/modules/sales/__integration__/TC-SALES-020.spec.ts +0 -107
- package/src/modules/sales/api/__tests__/channels.route.test.ts +0 -50
- package/src/modules/sales/api/__tests__/document-history.test.ts +0 -146
- package/src/modules/sales/api/__tests__/documents.factory.test.ts +0 -98
- package/src/modules/sales/api/__tests__/documents.routes.test.ts +0 -149
- package/src/modules/sales/api/__tests__/quotes.acceptance.test.ts +0 -209
- package/src/modules/sales/api/__tests__/timeline.test.tsx +0 -165
- package/src/modules/sales/api/dashboard/widgets/new-orders/__tests__/route.test.ts +0 -112
- package/src/modules/sales/api/dashboard/widgets/new-quotes/__tests__/route.test.ts +0 -116
- package/src/modules/sales/commands/__tests__/documents.cache.test.ts +0 -126
- package/src/modules/sales/commands/__tests__/documents.undo.test.ts +0 -170
- package/src/modules/sales/commands/__tests__/registration.test.ts +0 -141
- package/src/modules/sales/components/__tests__/salesComponentsRender.test.tsx +0 -456
- package/src/modules/sales/lib/__tests__/calculations.test.ts +0 -210
- package/src/modules/sales/services/__tests__/salesCalculationService.test.ts +0 -181
- package/src/modules/sales/services/__tests__/taxCalculationService.test.ts +0 -79
- package/src/modules/sales/widgets/dashboard/new-orders/__tests__/config.test.ts +0 -47
- package/src/modules/sales/widgets/dashboard/new-quotes/__tests__/config.test.ts +0 -47
- package/src/modules/staff/__integration__/TC-INT-006.spec.ts +0 -71
- package/src/modules/staff/data/__tests__/validators.test.ts +0 -60
- package/src/modules/translations/__integration__/TC-TRANS-001.spec.ts +0 -57
- package/src/modules/translations/__integration__/TC-TRANS-002.spec.ts +0 -114
- package/src/modules/translations/__integration__/TC-TRANS-003.spec.ts +0 -71
- package/src/modules/translations/__integration__/TC-TRANS-004.spec.ts +0 -66
- package/src/modules/translations/__integration__/TC-TRANS-005.spec.ts +0 -135
- package/src/modules/translations/__integration__/TC-TRANS-006.spec.ts +0 -113
- package/src/modules/translations/__integration__/TC-TRANS-007.spec.ts +0 -80
- package/src/modules/translations/__integration__/TC-TRANS-008.spec.ts +0 -209
- package/src/modules/translations/__integration__/helpers/translationFixtures.ts +0 -95
- package/src/modules/translations/api/__tests__/locales.test.ts +0 -67
- package/src/modules/translations/data/__tests__/validators.test.ts +0 -143
- package/src/modules/translations/lib/__tests__/extract-record-id.test.ts +0 -75
- package/src/modules/translations/lib/__tests__/helpers.test.ts +0 -215
- package/src/modules/translations/lib/__tests__/locale.test.ts +0 -115
- package/src/modules/translations/lib/__tests__/resolve-field-list.test.ts +0 -176
- package/src/modules/translations/lib/__tests__/translatable-fields.test.ts +0 -79
- package/src/modules/translations/widgets/__tests__/injection-table.test.ts +0 -83
- package/src/modules/workflows/__integration__/TC-WF-001.spec.ts +0 -114
- package/src/modules/workflows/api/__tests__/definitions.route.test.ts +0 -762
- package/src/modules/workflows/api/__tests__/instances.route.test.ts +0 -869
- package/src/modules/workflows/data/__tests__/validators.test.ts +0 -707
- package/src/modules/workflows/lib/__tests__/activity-executor.test.ts +0 -1230
- package/src/modules/workflows/lib/__tests__/call-api.test.ts +0 -421
- package/src/modules/workflows/lib/__tests__/compensation.test.ts +0 -713
- package/src/modules/workflows/lib/__tests__/event-logger.test.ts +0 -615
- package/src/modules/workflows/lib/__tests__/integration.test.ts +0 -693
- package/src/modules/workflows/lib/__tests__/signals.test.ts +0 -566
- package/src/modules/workflows/lib/__tests__/step-handler.test.ts +0 -670
- package/src/modules/workflows/lib/__tests__/sub-workflow.test.ts +0 -934
- package/src/modules/workflows/lib/__tests__/transition-handler.test.ts +0 -925
- package/src/modules/workflows/lib/__tests__/workflow-executor.test.ts +0 -684
|
@@ -1,814 +0,0 @@
|
|
|
1
|
-
import { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
|
|
2
|
-
import { User, UserRole, RoleAcl, UserAcl, Role } from '@open-mercato/core/modules/auth/data/entities'
|
|
3
|
-
import { ApiKey } from '@open-mercato/core/modules/api_keys/data/entities'
|
|
4
|
-
import { createMemoryStrategy } from '@open-mercato/cache'
|
|
5
|
-
import type { CacheStrategy } from '@open-mercato/cache'
|
|
6
|
-
|
|
7
|
-
// Minimal mock of MikroORM EntityManager surface used by RbacService
|
|
8
|
-
type MockEm = {
|
|
9
|
-
findOne: jest.Mock<any, any>
|
|
10
|
-
find: jest.Mock<any, any>
|
|
11
|
-
fork: jest.Mock<any, any>
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function createMockEm(): MockEm {
|
|
15
|
-
const mockEm: MockEm = {
|
|
16
|
-
findOne: jest.fn(),
|
|
17
|
-
find: jest.fn(),
|
|
18
|
-
fork: jest.fn(),
|
|
19
|
-
}
|
|
20
|
-
// fork() should return the same mock instance for testing purposes
|
|
21
|
-
mockEm.fork.mockReturnValue(mockEm)
|
|
22
|
-
return mockEm
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
describe('RbacService', () => {
|
|
26
|
-
let em: MockEm
|
|
27
|
-
let service: RbacService
|
|
28
|
-
let cache: CacheStrategy
|
|
29
|
-
const callsForScopes = (scopeCount: number): number => {
|
|
30
|
-
if (scopeCount <= 0) return 0
|
|
31
|
-
return 3 + 2 * (scopeCount - 1)
|
|
32
|
-
}
|
|
33
|
-
const baseUser: Partial<User> = {
|
|
34
|
-
id: 'user-1',
|
|
35
|
-
tenantId: 'tenant-1',
|
|
36
|
-
organizationId: 'org-1',
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
beforeEach(() => {
|
|
40
|
-
em = createMockEm()
|
|
41
|
-
cache = createMemoryStrategy()
|
|
42
|
-
service = new RbacService(em as any, cache)
|
|
43
|
-
jest.clearAllMocks()
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
describe('loadAcl', () => {
|
|
47
|
-
it('returns empty ACL for unknown user', async () => {
|
|
48
|
-
em.findOne.mockImplementation(async (entity: any) => {
|
|
49
|
-
if (entity === User) return null
|
|
50
|
-
return null
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
const acl = await service.loadAcl('missing', { tenantId: null, organizationId: null })
|
|
54
|
-
expect(acl).toEqual({ isSuperAdmin: false, features: [], organizations: null })
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
it('prioritizes per-user ACL when present for tenant', async () => {
|
|
58
|
-
const uacl: Partial<UserAcl> = {
|
|
59
|
-
isSuperAdmin: false,
|
|
60
|
-
featuresJson: ['entities.records.view', 'example.*'],
|
|
61
|
-
organizationsJson: ['org-1', 'org-2'],
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
65
|
-
if (entity === User && where?.id === baseUser.id) return baseUser
|
|
66
|
-
if (entity === UserAcl && where?.user === baseUser.id && where?.tenantId === baseUser.tenantId) return uacl
|
|
67
|
-
return null
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
const acl = await service.loadAcl(baseUser.id!, { tenantId: null, organizationId: null })
|
|
71
|
-
expect(acl.isSuperAdmin).toBe(false)
|
|
72
|
-
expect(acl.features.sort()).toEqual(['entities.records.view', 'example.*'])
|
|
73
|
-
expect(acl.organizations).toEqual(['org-1', 'org-2'])
|
|
74
|
-
expect(em.find).toHaveBeenCalledTimes(1)
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
it('aggregates role ACLs when user ACL missing and tenant provided', async () => {
|
|
78
|
-
const roleA: Partial<Role> = { id: 'role-a', name: 'admin' }
|
|
79
|
-
const roleB: Partial<Role> = { id: 'role-b', name: 'employee' }
|
|
80
|
-
const links: Array<Partial<UserRole>> = [
|
|
81
|
-
{ role: roleA as any },
|
|
82
|
-
{ role: roleB as any },
|
|
83
|
-
]
|
|
84
|
-
const racls: Array<Partial<RoleAcl>> = [
|
|
85
|
-
{ role: roleA as any, tenantId: 'tenant-1', isSuperAdmin: false, featuresJson: ['entities.*'], organizationsJson: ['org-1'] },
|
|
86
|
-
{ role: roleB as any, tenantId: 'tenant-1', isSuperAdmin: false, featuresJson: ['example.todos.view'], organizationsJson: ['org-2'] },
|
|
87
|
-
]
|
|
88
|
-
|
|
89
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
90
|
-
if (entity === User && where?.id === baseUser.id) return baseUser
|
|
91
|
-
if (entity === UserAcl) return null
|
|
92
|
-
return null
|
|
93
|
-
})
|
|
94
|
-
em.find.mockImplementation(async (entity: any, where: any) => {
|
|
95
|
-
if (entity === UserRole && where?.user === baseUser.id) return links
|
|
96
|
-
if (entity === RoleAcl && where?.tenantId === 'tenant-1') return racls
|
|
97
|
-
return []
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
const acl = await service.loadAcl(baseUser.id!, { tenantId: null, organizationId: 'org-2' })
|
|
101
|
-
expect(acl.isSuperAdmin).toBe(false)
|
|
102
|
-
// de-duplicated and union of features
|
|
103
|
-
expect(acl.features.sort()).toEqual(['entities.*', 'example.todos.view'])
|
|
104
|
-
// organizations become union; since neither role had null, it remains an array
|
|
105
|
-
expect(acl.organizations && new Set(acl.organizations)).toEqual(new Set(['org-1', 'org-2']))
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
it('sets organizations to null if any role grants all-org visibility', async () => {
|
|
109
|
-
const roleA: Partial<Role> = { id: 'role-a' }
|
|
110
|
-
const links: Array<Partial<UserRole>> = [{ role: roleA as any }]
|
|
111
|
-
const racls: Array<Partial<RoleAcl>> = [
|
|
112
|
-
{ role: roleA as any, tenantId: 'tenant-1', featuresJson: ['entities.records.view'], organizationsJson: null },
|
|
113
|
-
]
|
|
114
|
-
|
|
115
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
116
|
-
if (entity === User && where?.id === baseUser.id) return baseUser
|
|
117
|
-
if (entity === UserAcl) return null
|
|
118
|
-
return null
|
|
119
|
-
})
|
|
120
|
-
em.find.mockImplementation(async (entity: any, where: any) => {
|
|
121
|
-
if (entity === UserRole && where?.user === baseUser.id) return links
|
|
122
|
-
if (entity === RoleAcl && where?.tenantId === 'tenant-1') return racls
|
|
123
|
-
return []
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
const acl = await service.loadAcl(baseUser.id!, { tenantId: null, organizationId: 'org-3' })
|
|
127
|
-
expect(acl.organizations).toBeNull()
|
|
128
|
-
expect(acl.features).toEqual(['entities.records.view'])
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
it('marks isSuperAdmin when any role ACL has isSuperAdmin=true', async () => {
|
|
132
|
-
const roleA: Partial<Role> = { id: 'role-a' }
|
|
133
|
-
const links: Array<Partial<UserRole>> = [{ role: roleA as any }]
|
|
134
|
-
const racls: Array<Partial<RoleAcl>> = [
|
|
135
|
-
{ role: roleA as any, tenantId: 'tenant-1', isSuperAdmin: true, featuresJson: [] },
|
|
136
|
-
]
|
|
137
|
-
|
|
138
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
139
|
-
if (entity === User && where?.id === baseUser.id) return baseUser
|
|
140
|
-
if (entity === UserAcl) return null
|
|
141
|
-
return null
|
|
142
|
-
})
|
|
143
|
-
em.find.mockImplementation(async (entity: any, where: any) => {
|
|
144
|
-
if (entity === UserRole && where?.user === baseUser.id) return links
|
|
145
|
-
if (entity === RoleAcl && where?.tenantId === 'tenant-1') return racls
|
|
146
|
-
return []
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
const acl = await service.loadAcl(baseUser.id!, { tenantId: null, organizationId: null })
|
|
150
|
-
expect(acl.isSuperAdmin).toBe(true)
|
|
151
|
-
})
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
describe('userHasAllFeatures', () => {
|
|
155
|
-
it('returns true when no required features', async () => {
|
|
156
|
-
const ok = await service.userHasAllFeatures('any', [], { tenantId: null, organizationId: null })
|
|
157
|
-
expect(ok).toBe(true)
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
it('returns true for super admin user', async () => {
|
|
161
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
162
|
-
if (entity === User && where?.id === baseUser.id) return baseUser
|
|
163
|
-
if (entity === UserAcl && where?.user === baseUser.id && where?.tenantId === baseUser.tenantId) {
|
|
164
|
-
const uacl: Partial<UserAcl> = { isSuperAdmin: true, featuresJson: [] }
|
|
165
|
-
return uacl
|
|
166
|
-
}
|
|
167
|
-
return null
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
const ok = await service.userHasAllFeatures(baseUser.id!, ['anything.here'], { tenantId: null, organizationId: null })
|
|
171
|
-
expect(ok).toBe(true)
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
it('checks wildcard "*" grants all', async () => {
|
|
175
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
176
|
-
if (entity === User && where?.id === baseUser.id) return baseUser
|
|
177
|
-
if (entity === UserAcl && where?.user === baseUser.id && where?.tenantId === baseUser.tenantId) {
|
|
178
|
-
const uacl: Partial<UserAcl> = { isSuperAdmin: false, featuresJson: ['*'] }
|
|
179
|
-
return uacl
|
|
180
|
-
}
|
|
181
|
-
return null
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
const ok = await service.userHasAllFeatures(baseUser.id!, ['entities.definitions.manage', 'other.feature'], { tenantId: null, organizationId: null })
|
|
185
|
-
expect(ok).toBe(true)
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
it('checks prefix wildcard like "entities.*"', async () => {
|
|
189
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
190
|
-
if (entity === User && where?.id === baseUser.id) return baseUser
|
|
191
|
-
if (entity === UserAcl && where?.user === baseUser.id && where?.tenantId === baseUser.tenantId) {
|
|
192
|
-
const uacl: Partial<UserAcl> = { isSuperAdmin: false, featuresJson: ['entities.*'] }
|
|
193
|
-
return uacl
|
|
194
|
-
}
|
|
195
|
-
return null
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
const ok1 = await service.userHasAllFeatures(baseUser.id!, ['entities.records.view'], { tenantId: null, organizationId: null })
|
|
199
|
-
const ok2 = await service.userHasAllFeatures(baseUser.id!, ['entities'], { tenantId: null, organizationId: null })
|
|
200
|
-
const ok3 = await service.userHasAllFeatures(baseUser.id!, ['auth.users.list'], { tenantId: null, organizationId: null })
|
|
201
|
-
expect(ok1).toBe(true)
|
|
202
|
-
expect(ok2).toBe(true)
|
|
203
|
-
expect(ok3).toBe(false)
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
it('returns false when organization not included in restricted list', async () => {
|
|
207
|
-
const roleA: Partial<Role> = { id: 'role-a' }
|
|
208
|
-
const links: Array<Partial<UserRole>> = [{ role: roleA as any }]
|
|
209
|
-
const racls: Array<Partial<RoleAcl>> = [
|
|
210
|
-
{ role: roleA as any, tenantId: 'tenant-1', isSuperAdmin: false, featuresJson: ['entities.records.view'], organizationsJson: ['org-1'] },
|
|
211
|
-
]
|
|
212
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
213
|
-
if (entity === User && where?.id === baseUser.id) return baseUser
|
|
214
|
-
if (entity === UserAcl) return null
|
|
215
|
-
return null
|
|
216
|
-
})
|
|
217
|
-
em.find.mockImplementation(async (entity: any, where: any) => {
|
|
218
|
-
if (entity === UserRole && where?.user === baseUser.id) return links
|
|
219
|
-
if (entity === RoleAcl && where?.tenantId === 'tenant-1') return racls
|
|
220
|
-
return []
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
const ok = await service.userHasAllFeatures(baseUser.id!, ['entities.records.view'], { tenantId: null, organizationId: 'org-2' })
|
|
224
|
-
expect(ok).toBe(false)
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
it('ignores organization restriction when any role grants all-org visibility (organizations=null)', async () => {
|
|
228
|
-
const roleA: Partial<Role> = { id: 'role-a' }
|
|
229
|
-
const links: Array<Partial<UserRole>> = [{ role: roleA as any }]
|
|
230
|
-
const racls: Array<Partial<RoleAcl>> = [
|
|
231
|
-
{ role: roleA as any, tenantId: 'tenant-1', isSuperAdmin: false, featuresJson: ['entities.records.view'], organizationsJson: null },
|
|
232
|
-
]
|
|
233
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
234
|
-
if (entity === User && where?.id === baseUser.id) return baseUser
|
|
235
|
-
if (entity === UserAcl) return null
|
|
236
|
-
return null
|
|
237
|
-
})
|
|
238
|
-
em.find.mockImplementation(async (entity: any, where: any) => {
|
|
239
|
-
if (entity === UserRole && where?.user === baseUser.id) return links
|
|
240
|
-
if (entity === RoleAcl && where?.tenantId === 'tenant-1') return racls
|
|
241
|
-
return []
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
const ok = await service.userHasAllFeatures(baseUser.id!, ['entities.records.view'], { tenantId: null, organizationId: 'org-unknown' })
|
|
245
|
-
expect(ok).toBe(true)
|
|
246
|
-
})
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
describe('Cache behavior', () => {
|
|
250
|
-
it('should cache ACL results and not query database on second call', async () => {
|
|
251
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
252
|
-
if (entity === User && where?.id === baseUser.id) return baseUser
|
|
253
|
-
if (entity === UserAcl && where?.user === baseUser.id && where?.tenantId === baseUser.tenantId) {
|
|
254
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature'], organizationsJson: null }
|
|
255
|
-
}
|
|
256
|
-
return null
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
const acl1 = await service.loadAcl(baseUser.id!, { tenantId: null, organizationId: null })
|
|
260
|
-
const acl2 = await service.loadAcl(baseUser.id!, { tenantId: null, organizationId: null })
|
|
261
|
-
|
|
262
|
-
expect(acl1).toEqual(acl2)
|
|
263
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsForScopes(1)) // First load triggers global + user + ACL lookups
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
it('should cache separately for different scopes (different tenants)', async () => {
|
|
267
|
-
const user = { id: 'user-1', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
268
|
-
|
|
269
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
270
|
-
if (entity === User && where?.id === user.id) return user
|
|
271
|
-
if (entity === UserAcl && where?.tenantId === 'tenant-1') {
|
|
272
|
-
return { isSuperAdmin: false, featuresJson: ['tenant1.feature'], organizationsJson: null }
|
|
273
|
-
}
|
|
274
|
-
if (entity === UserAcl && where?.tenantId === 'tenant-2') {
|
|
275
|
-
return { isSuperAdmin: false, featuresJson: ['tenant2.feature'], organizationsJson: null }
|
|
276
|
-
}
|
|
277
|
-
return null
|
|
278
|
-
})
|
|
279
|
-
|
|
280
|
-
const acl1 = await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: null })
|
|
281
|
-
const acl2 = await service.loadAcl(user.id, { tenantId: 'tenant-2', organizationId: null })
|
|
282
|
-
|
|
283
|
-
expect(acl1.features).toEqual(['tenant1.feature'])
|
|
284
|
-
expect(acl2.features).toEqual(['tenant2.feature'])
|
|
285
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsForScopes(2)) // 3 queries on first scope, then 2 on next
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
it('should cache separately for different scopes (different organizations)', async () => {
|
|
289
|
-
const user = { id: 'user-1', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
290
|
-
|
|
291
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
292
|
-
if (entity === User && where?.id === user.id) return user
|
|
293
|
-
if (entity === UserAcl) {
|
|
294
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature'], organizationsJson: null }
|
|
295
|
-
}
|
|
296
|
-
return null
|
|
297
|
-
})
|
|
298
|
-
|
|
299
|
-
const acl1 = await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: 'org-1' })
|
|
300
|
-
const acl2 = await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: 'org-2' })
|
|
301
|
-
|
|
302
|
-
expect(acl1).toEqual(acl2) // Same data
|
|
303
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsForScopes(2)) // Cached separately per scope
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
it('should maintain cache isolation between different users', async () => {
|
|
307
|
-
const user1 = { id: 'user-1', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
308
|
-
const user2 = { id: 'user-2', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
309
|
-
|
|
310
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
311
|
-
if (entity === User && where?.id === user1.id) return user1
|
|
312
|
-
if (entity === User && where?.id === user2.id) return user2
|
|
313
|
-
if (entity === UserAcl && where?.user === user1.id) {
|
|
314
|
-
return { isSuperAdmin: false, featuresJson: ['user1.feature'], organizationsJson: null }
|
|
315
|
-
}
|
|
316
|
-
if (entity === UserAcl && where?.user === user2.id) {
|
|
317
|
-
return { isSuperAdmin: true, featuresJson: ['user2.feature'], organizationsJson: null }
|
|
318
|
-
}
|
|
319
|
-
return null
|
|
320
|
-
})
|
|
321
|
-
|
|
322
|
-
const acl1 = await service.loadAcl(user1.id, { tenantId: 'tenant-1', organizationId: null })
|
|
323
|
-
const acl2 = await service.loadAcl(user2.id, { tenantId: 'tenant-1', organizationId: null })
|
|
324
|
-
|
|
325
|
-
expect(acl1.isSuperAdmin).toBe(false)
|
|
326
|
-
expect(acl1.features).toEqual(['user1.feature'])
|
|
327
|
-
expect(acl2.isSuperAdmin).toBe(true)
|
|
328
|
-
expect(acl2.features).toEqual(['*'])
|
|
329
|
-
})
|
|
330
|
-
|
|
331
|
-
it('should invalidate cache for specific user', async () => {
|
|
332
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
333
|
-
if (entity === User && where?.id === baseUser.id) return baseUser
|
|
334
|
-
if (entity === UserAcl) {
|
|
335
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature'], organizationsJson: null }
|
|
336
|
-
}
|
|
337
|
-
return null
|
|
338
|
-
})
|
|
339
|
-
|
|
340
|
-
await service.loadAcl(baseUser.id!, { tenantId: null, organizationId: null })
|
|
341
|
-
await service.invalidateUserCache(baseUser.id!)
|
|
342
|
-
await service.loadAcl(baseUser.id!, { tenantId: null, organizationId: null })
|
|
343
|
-
|
|
344
|
-
expect(em.findOne).toHaveBeenCalledTimes(6) // Two loads of same scope -> 3 queries each
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
it('should invalidate all scopes for a user when invalidating user cache', async () => {
|
|
348
|
-
const user = { id: 'user-1', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
349
|
-
|
|
350
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
351
|
-
if (entity === User && where?.id === user.id) return user
|
|
352
|
-
if (entity === UserAcl) {
|
|
353
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature'], organizationsJson: null }
|
|
354
|
-
}
|
|
355
|
-
return null
|
|
356
|
-
})
|
|
357
|
-
|
|
358
|
-
// Load multiple scopes for same user
|
|
359
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: 'org-1' })
|
|
360
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: 'org-2' })
|
|
361
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-2', organizationId: 'org-1' })
|
|
362
|
-
|
|
363
|
-
const callsAfterLoad = em.findOne.mock.calls.length
|
|
364
|
-
|
|
365
|
-
// Verify cache is working
|
|
366
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: 'org-1' })
|
|
367
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsAfterLoad) // No new calls
|
|
368
|
-
|
|
369
|
-
// Invalidate user cache
|
|
370
|
-
await service.invalidateUserCache(user.id)
|
|
371
|
-
|
|
372
|
-
// All scopes should require fresh queries
|
|
373
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: 'org-1' })
|
|
374
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: 'org-2' })
|
|
375
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-2', organizationId: 'org-1' })
|
|
376
|
-
|
|
377
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsAfterLoad + callsForScopes(3)) // 3 scopes reloaded for same user
|
|
378
|
-
})
|
|
379
|
-
|
|
380
|
-
it('should not affect other users when invalidating specific user cache', async () => {
|
|
381
|
-
const user1 = { id: 'user-1', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
382
|
-
const user2 = { id: 'user-2', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
383
|
-
|
|
384
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
385
|
-
if (entity === User && where?.id === user1.id) return user1
|
|
386
|
-
if (entity === User && where?.id === user2.id) return user2
|
|
387
|
-
if (entity === UserAcl) {
|
|
388
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature'], organizationsJson: null }
|
|
389
|
-
}
|
|
390
|
-
return null
|
|
391
|
-
})
|
|
392
|
-
|
|
393
|
-
await service.loadAcl(user1.id, { tenantId: 'tenant-1', organizationId: null })
|
|
394
|
-
await service.loadAcl(user2.id, { tenantId: 'tenant-1', organizationId: null })
|
|
395
|
-
|
|
396
|
-
const callsAfterLoad = em.findOne.mock.calls.length
|
|
397
|
-
|
|
398
|
-
await service.invalidateUserCache(user1.id)
|
|
399
|
-
|
|
400
|
-
// User1 should query again
|
|
401
|
-
await service.loadAcl(user1.id, { tenantId: 'tenant-1', organizationId: null })
|
|
402
|
-
expect(em.findOne.mock.calls.length).toBeGreaterThan(callsAfterLoad)
|
|
403
|
-
|
|
404
|
-
const callsAfterUser1 = em.findOne.mock.calls.length
|
|
405
|
-
|
|
406
|
-
// User2 should still be cached
|
|
407
|
-
await service.loadAcl(user2.id, { tenantId: 'tenant-1', organizationId: null })
|
|
408
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsAfterUser1) // No new calls
|
|
409
|
-
})
|
|
410
|
-
|
|
411
|
-
it('should invalidate cache for all users in a tenant', async () => {
|
|
412
|
-
const user1 = { id: 'user-1', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
413
|
-
const user2 = { id: 'user-2', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
414
|
-
|
|
415
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
416
|
-
if (entity === User && where?.id === user1.id) return user1
|
|
417
|
-
if (entity === User && where?.id === user2.id) return user2
|
|
418
|
-
if (entity === UserAcl) {
|
|
419
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature'], organizationsJson: null }
|
|
420
|
-
}
|
|
421
|
-
return null
|
|
422
|
-
})
|
|
423
|
-
|
|
424
|
-
await service.loadAcl(user1.id, { tenantId: 'tenant-1', organizationId: null })
|
|
425
|
-
await service.loadAcl(user2.id, { tenantId: 'tenant-1', organizationId: null })
|
|
426
|
-
|
|
427
|
-
const initialCalls = em.findOne.mock.calls.length
|
|
428
|
-
|
|
429
|
-
await service.invalidateTenantCache('tenant-1')
|
|
430
|
-
|
|
431
|
-
await service.loadAcl(user1.id, { tenantId: 'tenant-1', organizationId: null })
|
|
432
|
-
await service.loadAcl(user2.id, { tenantId: 'tenant-1', organizationId: null })
|
|
433
|
-
|
|
434
|
-
expect(em.findOne).toHaveBeenCalledTimes(initialCalls + callsForScopes(1) * 2) // Both users queried again (first load per user)
|
|
435
|
-
})
|
|
436
|
-
|
|
437
|
-
it('should not affect other tenants when invalidating specific tenant cache', async () => {
|
|
438
|
-
const user1 = { id: 'user-1', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
439
|
-
const user2 = { id: 'user-2', tenantId: 'tenant-2', organizationId: 'org-1' }
|
|
440
|
-
|
|
441
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
442
|
-
if (entity === User && where?.id === user1.id) return user1
|
|
443
|
-
if (entity === User && where?.id === user2.id) return user2
|
|
444
|
-
if (entity === UserAcl) {
|
|
445
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature'], organizationsJson: null }
|
|
446
|
-
}
|
|
447
|
-
return null
|
|
448
|
-
})
|
|
449
|
-
|
|
450
|
-
await service.loadAcl(user1.id, { tenantId: 'tenant-1', organizationId: null })
|
|
451
|
-
await service.loadAcl(user2.id, { tenantId: 'tenant-2', organizationId: null })
|
|
452
|
-
|
|
453
|
-
const callsAfterLoad = em.findOne.mock.calls.length
|
|
454
|
-
|
|
455
|
-
await service.invalidateTenantCache('tenant-1')
|
|
456
|
-
|
|
457
|
-
// Tenant-1 user should query again
|
|
458
|
-
await service.loadAcl(user1.id, { tenantId: 'tenant-1', organizationId: null })
|
|
459
|
-
expect(em.findOne.mock.calls.length).toBeGreaterThan(callsAfterLoad)
|
|
460
|
-
|
|
461
|
-
const callsAfterTenant1 = em.findOne.mock.calls.length
|
|
462
|
-
|
|
463
|
-
// Tenant-2 user should still be cached
|
|
464
|
-
await service.loadAcl(user2.id, { tenantId: 'tenant-2', organizationId: null })
|
|
465
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsAfterTenant1) // No new calls
|
|
466
|
-
})
|
|
467
|
-
|
|
468
|
-
it('should handle invalidating tenant cache with null tenant entries', async () => {
|
|
469
|
-
const user = { id: 'user-1', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
470
|
-
|
|
471
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
472
|
-
if (entity === User && where?.id === user.id) return user
|
|
473
|
-
if (entity === UserAcl) {
|
|
474
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature'], organizationsJson: null }
|
|
475
|
-
}
|
|
476
|
-
return null
|
|
477
|
-
})
|
|
478
|
-
|
|
479
|
-
// Load with explicit tenant and with null tenant
|
|
480
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: null })
|
|
481
|
-
await service.loadAcl(user.id, { tenantId: null, organizationId: null })
|
|
482
|
-
|
|
483
|
-
const callsAfterLoad = em.findOne.mock.calls.length
|
|
484
|
-
|
|
485
|
-
await service.invalidateTenantCache('tenant-1')
|
|
486
|
-
|
|
487
|
-
// Tenant-1 entry should be invalidated
|
|
488
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: null })
|
|
489
|
-
expect(em.findOne.mock.calls.length).toBeGreaterThan(callsAfterLoad)
|
|
490
|
-
|
|
491
|
-
const callsAfterInvalidation = em.findOne.mock.calls.length
|
|
492
|
-
|
|
493
|
-
// Null tenant entry should still be cached
|
|
494
|
-
await service.loadAcl(user.id, { tenantId: null, organizationId: null })
|
|
495
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsAfterInvalidation) // No new calls
|
|
496
|
-
})
|
|
497
|
-
|
|
498
|
-
it('should invalidate cache for all users in an organization', async () => {
|
|
499
|
-
const user1 = { id: 'user-1', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
500
|
-
|
|
501
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
502
|
-
if (entity === User && where?.id === user1.id) return user1
|
|
503
|
-
if (entity === UserAcl) {
|
|
504
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature'], organizationsJson: null }
|
|
505
|
-
}
|
|
506
|
-
return null
|
|
507
|
-
})
|
|
508
|
-
|
|
509
|
-
await service.loadAcl(user1.id, { tenantId: 'tenant-1', organizationId: 'org-1' })
|
|
510
|
-
|
|
511
|
-
const initialCalls = em.findOne.mock.calls.length
|
|
512
|
-
|
|
513
|
-
await service.invalidateOrganizationCache('org-1')
|
|
514
|
-
|
|
515
|
-
await service.loadAcl(user1.id, { tenantId: 'tenant-1', organizationId: 'org-1' })
|
|
516
|
-
|
|
517
|
-
expect(em.findOne).toHaveBeenCalledTimes(initialCalls + 2) // User queried again
|
|
518
|
-
})
|
|
519
|
-
|
|
520
|
-
it('should not affect other organizations when invalidating specific organization cache', async () => {
|
|
521
|
-
const user = { id: 'user-1', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
522
|
-
|
|
523
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
524
|
-
if (entity === User && where?.id === user.id) return user
|
|
525
|
-
if (entity === UserAcl) {
|
|
526
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature'], organizationsJson: null }
|
|
527
|
-
}
|
|
528
|
-
return null
|
|
529
|
-
})
|
|
530
|
-
|
|
531
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: 'org-1' })
|
|
532
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: 'org-2' })
|
|
533
|
-
|
|
534
|
-
const callsAfterLoad = em.findOne.mock.calls.length
|
|
535
|
-
|
|
536
|
-
await service.invalidateOrganizationCache('org-1')
|
|
537
|
-
|
|
538
|
-
// Org-1 entry should query again
|
|
539
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: 'org-1' })
|
|
540
|
-
expect(em.findOne.mock.calls.length).toBeGreaterThan(callsAfterLoad)
|
|
541
|
-
|
|
542
|
-
const callsAfterOrg1 = em.findOne.mock.calls.length
|
|
543
|
-
|
|
544
|
-
// Org-2 entry should still be cached
|
|
545
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: 'org-2' })
|
|
546
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsAfterOrg1) // No new calls
|
|
547
|
-
})
|
|
548
|
-
|
|
549
|
-
it('should handle invalidating organization cache with null organization entries', async () => {
|
|
550
|
-
const user = { id: 'user-1', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
551
|
-
|
|
552
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
553
|
-
if (entity === User && where?.id === user.id) return user
|
|
554
|
-
if (entity === UserAcl) {
|
|
555
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature'], organizationsJson: null }
|
|
556
|
-
}
|
|
557
|
-
return null
|
|
558
|
-
})
|
|
559
|
-
|
|
560
|
-
// Load with explicit org and with null org
|
|
561
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: 'org-1' })
|
|
562
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: null })
|
|
563
|
-
|
|
564
|
-
const callsAfterLoad = em.findOne.mock.calls.length
|
|
565
|
-
|
|
566
|
-
await service.invalidateOrganizationCache('org-1')
|
|
567
|
-
|
|
568
|
-
// Org-1 entry should be invalidated
|
|
569
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: 'org-1' })
|
|
570
|
-
expect(em.findOne.mock.calls.length).toBeGreaterThan(callsAfterLoad)
|
|
571
|
-
|
|
572
|
-
const callsAfterInvalidation = em.findOne.mock.calls.length
|
|
573
|
-
|
|
574
|
-
// Null org entry should still be cached
|
|
575
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: null })
|
|
576
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsAfterInvalidation) // No new calls
|
|
577
|
-
})
|
|
578
|
-
|
|
579
|
-
it('should invalidate all cache entries with invalidateAllCache', async () => {
|
|
580
|
-
const user1 = { id: 'user-1', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
581
|
-
const user2 = { id: 'user-2', tenantId: 'tenant-2', organizationId: 'org-2' }
|
|
582
|
-
|
|
583
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
584
|
-
if (entity === User && where?.id === user1.id) return user1
|
|
585
|
-
if (entity === User && where?.id === user2.id) return user2
|
|
586
|
-
if (entity === UserAcl) {
|
|
587
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature'], organizationsJson: null }
|
|
588
|
-
}
|
|
589
|
-
return null
|
|
590
|
-
})
|
|
591
|
-
|
|
592
|
-
// Load multiple cache entries
|
|
593
|
-
await service.loadAcl(user1.id, { tenantId: 'tenant-1', organizationId: 'org-1' })
|
|
594
|
-
await service.loadAcl(user1.id, { tenantId: 'tenant-1', organizationId: 'org-2' })
|
|
595
|
-
await service.loadAcl(user2.id, { tenantId: 'tenant-2', organizationId: 'org-1' })
|
|
596
|
-
await service.loadAcl(user2.id, { tenantId: 'tenant-2', organizationId: 'org-2' })
|
|
597
|
-
|
|
598
|
-
const callsAfterLoad = em.findOne.mock.calls.length
|
|
599
|
-
|
|
600
|
-
// Verify cache is working
|
|
601
|
-
await service.loadAcl(user1.id, { tenantId: 'tenant-1', organizationId: 'org-1' })
|
|
602
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsAfterLoad) // No new calls
|
|
603
|
-
|
|
604
|
-
// Invalidate all cache
|
|
605
|
-
await service.invalidateAllCache()
|
|
606
|
-
|
|
607
|
-
// All entries should require fresh queries
|
|
608
|
-
await service.loadAcl(user1.id, { tenantId: 'tenant-1', organizationId: 'org-1' })
|
|
609
|
-
await service.loadAcl(user1.id, { tenantId: 'tenant-1', organizationId: 'org-2' })
|
|
610
|
-
await service.loadAcl(user2.id, { tenantId: 'tenant-2', organizationId: 'org-1' })
|
|
611
|
-
await service.loadAcl(user2.id, { tenantId: 'tenant-2', organizationId: 'org-2' })
|
|
612
|
-
|
|
613
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsAfterLoad + callsForScopes(2) * 2) // Both users reload two scopes each
|
|
614
|
-
})
|
|
615
|
-
|
|
616
|
-
it('should handle invalidating non-existent user cache gracefully', async () => {
|
|
617
|
-
const user = { id: 'user-1', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
618
|
-
|
|
619
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
620
|
-
if (entity === User && where?.id === user.id) return user
|
|
621
|
-
if (entity === UserAcl) {
|
|
622
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature'], organizationsJson: null }
|
|
623
|
-
}
|
|
624
|
-
return null
|
|
625
|
-
})
|
|
626
|
-
|
|
627
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: null })
|
|
628
|
-
|
|
629
|
-
// Should not throw
|
|
630
|
-
await expect(service.invalidateUserCache('non-existent-user')).resolves.not.toThrow()
|
|
631
|
-
|
|
632
|
-
// Original cache should still work
|
|
633
|
-
const callsBeforeReload = em.findOne.mock.calls.length
|
|
634
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: null })
|
|
635
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsBeforeReload) // No new calls
|
|
636
|
-
})
|
|
637
|
-
|
|
638
|
-
it('should handle invalidating non-existent tenant cache gracefully', async () => {
|
|
639
|
-
const user = { id: 'user-1', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
640
|
-
|
|
641
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
642
|
-
if (entity === User && where?.id === user.id) return user
|
|
643
|
-
if (entity === UserAcl) {
|
|
644
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature'], organizationsJson: null }
|
|
645
|
-
}
|
|
646
|
-
return null
|
|
647
|
-
})
|
|
648
|
-
|
|
649
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: null })
|
|
650
|
-
|
|
651
|
-
// Should not throw
|
|
652
|
-
await expect(service.invalidateTenantCache('non-existent-tenant')).resolves.not.toThrow()
|
|
653
|
-
|
|
654
|
-
// Original cache should still work
|
|
655
|
-
const callsBeforeReload = em.findOne.mock.calls.length
|
|
656
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: null })
|
|
657
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsBeforeReload) // No new calls
|
|
658
|
-
})
|
|
659
|
-
|
|
660
|
-
it('should handle invalidating non-existent organization cache gracefully', async () => {
|
|
661
|
-
const user = { id: 'user-1', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
662
|
-
|
|
663
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
664
|
-
if (entity === User && where?.id === user.id) return user
|
|
665
|
-
if (entity === UserAcl) {
|
|
666
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature'], organizationsJson: null }
|
|
667
|
-
}
|
|
668
|
-
return null
|
|
669
|
-
})
|
|
670
|
-
|
|
671
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: 'org-1' })
|
|
672
|
-
|
|
673
|
-
// Should not throw
|
|
674
|
-
await expect(service.invalidateOrganizationCache('non-existent-org')).resolves.not.toThrow()
|
|
675
|
-
|
|
676
|
-
// Original cache should still work
|
|
677
|
-
const callsBeforeReload = em.findOne.mock.calls.length
|
|
678
|
-
await service.loadAcl(user.id, { tenantId: 'tenant-1', organizationId: 'org-1' })
|
|
679
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsBeforeReload) // No new calls
|
|
680
|
-
})
|
|
681
|
-
|
|
682
|
-
it('should respect cache TTL and refetch after expiration', async () => {
|
|
683
|
-
jest.useFakeTimers()
|
|
684
|
-
jest.setSystemTime(new Date('2025-01-01T00:00:00.000Z'))
|
|
685
|
-
const shortTtlCache = createMemoryStrategy()
|
|
686
|
-
const shortTtlService = new RbacService(em as any, shortTtlCache)
|
|
687
|
-
shortTtlService.setCacheTtl(100) // 100ms TTL
|
|
688
|
-
|
|
689
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
690
|
-
if (entity === User && where?.id === baseUser.id) return baseUser
|
|
691
|
-
if (entity === UserAcl) {
|
|
692
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature'], organizationsJson: null }
|
|
693
|
-
}
|
|
694
|
-
return null
|
|
695
|
-
})
|
|
696
|
-
|
|
697
|
-
await shortTtlService.loadAcl(baseUser.id!, { tenantId: null, organizationId: null })
|
|
698
|
-
const callsAfterFirst = em.findOne.mock.calls.length
|
|
699
|
-
|
|
700
|
-
await shortTtlService.loadAcl(baseUser.id!, { tenantId: null, organizationId: null })
|
|
701
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsAfterFirst) // Still cached
|
|
702
|
-
|
|
703
|
-
await jest.advanceTimersByTimeAsync(150) // Wait for cache to expire
|
|
704
|
-
|
|
705
|
-
await shortTtlService.loadAcl(baseUser.id!, { tenantId: null, organizationId: null })
|
|
706
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsAfterFirst + 2) // Refetched (user + ACL queries)
|
|
707
|
-
jest.useRealTimers()
|
|
708
|
-
})
|
|
709
|
-
|
|
710
|
-
it('should use custom TTL when configured via setCacheTtl', async () => {
|
|
711
|
-
jest.useFakeTimers()
|
|
712
|
-
jest.setSystemTime(new Date('2025-01-01T00:00:00.000Z'))
|
|
713
|
-
const customTtlCache = createMemoryStrategy()
|
|
714
|
-
const customTtlService = new RbacService(em as any, customTtlCache)
|
|
715
|
-
customTtlService.setCacheTtl(50) // 50ms TTL
|
|
716
|
-
|
|
717
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
718
|
-
if (entity === User && where?.id === baseUser.id) return baseUser
|
|
719
|
-
if (entity === UserAcl) {
|
|
720
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature'], organizationsJson: null }
|
|
721
|
-
}
|
|
722
|
-
return null
|
|
723
|
-
})
|
|
724
|
-
|
|
725
|
-
await customTtlService.loadAcl(baseUser.id!, { tenantId: null, organizationId: null })
|
|
726
|
-
const callsAfterFirst = em.findOne.mock.calls.length
|
|
727
|
-
|
|
728
|
-
// Should still be cached at 40ms
|
|
729
|
-
await jest.advanceTimersByTimeAsync(40)
|
|
730
|
-
await customTtlService.loadAcl(baseUser.id!, { tenantId: null, organizationId: null })
|
|
731
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsAfterFirst)
|
|
732
|
-
|
|
733
|
-
// Should expire after 60ms total
|
|
734
|
-
await jest.advanceTimersByTimeAsync(25)
|
|
735
|
-
await customTtlService.loadAcl(baseUser.id!, { tenantId: null, organizationId: null })
|
|
736
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsAfterFirst + 2)
|
|
737
|
-
jest.useRealTimers()
|
|
738
|
-
})
|
|
739
|
-
|
|
740
|
-
it('should cache empty ACL results for unknown users', async () => {
|
|
741
|
-
em.findOne.mockImplementation(async (entity: any) => {
|
|
742
|
-
if (entity === User) return null
|
|
743
|
-
return null
|
|
744
|
-
})
|
|
745
|
-
|
|
746
|
-
await service.loadAcl('unknown-user', { tenantId: null, organizationId: null })
|
|
747
|
-
const callsAfterFirst = em.findOne.mock.calls.length
|
|
748
|
-
|
|
749
|
-
// Second call should be cached
|
|
750
|
-
await service.loadAcl('unknown-user', { tenantId: null, organizationId: null })
|
|
751
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsAfterFirst) // No new calls
|
|
752
|
-
})
|
|
753
|
-
|
|
754
|
-
it('should properly cache complex role aggregations', async () => {
|
|
755
|
-
const user = { id: 'user-1', tenantId: 'tenant-1', organizationId: 'org-1' }
|
|
756
|
-
const roleA: Partial<Role> = { id: 'role-a' }
|
|
757
|
-
const roleB: Partial<Role> = { id: 'role-b' }
|
|
758
|
-
const links: Array<Partial<UserRole>> = [
|
|
759
|
-
{ role: roleA as any },
|
|
760
|
-
{ role: roleB as any },
|
|
761
|
-
]
|
|
762
|
-
const racls: Array<Partial<RoleAcl>> = [
|
|
763
|
-
{ role: roleA as any, tenantId: 'tenant-1', isSuperAdmin: false, featuresJson: ['feature1', 'feature2'], organizationsJson: ['org-1'] },
|
|
764
|
-
{ role: roleB as any, tenantId: 'tenant-1', isSuperAdmin: false, featuresJson: ['feature3'], organizationsJson: ['org-2'] },
|
|
765
|
-
]
|
|
766
|
-
|
|
767
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
768
|
-
if (entity === User && where?.id === user.id) return user
|
|
769
|
-
if (entity === UserAcl) return null
|
|
770
|
-
return null
|
|
771
|
-
})
|
|
772
|
-
em.find.mockImplementation(async (entity: any, where: any) => {
|
|
773
|
-
if (entity === UserRole && where?.user === user.id) return links
|
|
774
|
-
if (entity === RoleAcl && where?.tenantId === 'tenant-1') return racls
|
|
775
|
-
return []
|
|
776
|
-
})
|
|
777
|
-
|
|
778
|
-
const acl1 = await service.loadAcl(user.id, { tenantId: null, organizationId: null })
|
|
779
|
-
const callsAfterFirst = em.findOne.mock.calls.length + em.find.mock.calls.length
|
|
780
|
-
|
|
781
|
-
// Verify aggregation is correct
|
|
782
|
-
expect(acl1.features.sort()).toEqual(['feature1', 'feature2', 'feature3'])
|
|
783
|
-
expect(new Set(acl1.organizations || [])).toEqual(new Set(['org-1', 'org-2']))
|
|
784
|
-
|
|
785
|
-
// Second call should be fully cached
|
|
786
|
-
const acl2 = await service.loadAcl(user.id, { tenantId: null, organizationId: null })
|
|
787
|
-
const callsAfterSecond = em.findOne.mock.calls.length + em.find.mock.calls.length
|
|
788
|
-
|
|
789
|
-
expect(callsAfterSecond).toBe(callsAfterFirst) // No additional queries
|
|
790
|
-
expect(acl2).toEqual(acl1)
|
|
791
|
-
})
|
|
792
|
-
|
|
793
|
-
it('should handle userHasAllFeatures with cached results', async () => {
|
|
794
|
-
em.findOne.mockImplementation(async (entity: any, where: any) => {
|
|
795
|
-
if (entity === User && where?.id === baseUser.id) return baseUser
|
|
796
|
-
if (entity === UserAcl && where?.user === baseUser.id) {
|
|
797
|
-
return { isSuperAdmin: false, featuresJson: ['test.feature', 'another.feature'], organizationsJson: null }
|
|
798
|
-
}
|
|
799
|
-
return null
|
|
800
|
-
})
|
|
801
|
-
|
|
802
|
-
// First call loads and caches
|
|
803
|
-
const hasFeatures1 = await service.userHasAllFeatures(baseUser.id!, ['test.feature'], { tenantId: null, organizationId: null })
|
|
804
|
-
const callsAfterFirst = em.findOne.mock.calls.length
|
|
805
|
-
|
|
806
|
-
// Second call uses cache
|
|
807
|
-
const hasFeatures2 = await service.userHasAllFeatures(baseUser.id!, ['another.feature'], { tenantId: null, organizationId: null })
|
|
808
|
-
|
|
809
|
-
expect(hasFeatures1).toBe(true)
|
|
810
|
-
expect(hasFeatures2).toBe(true)
|
|
811
|
-
expect(em.findOne).toHaveBeenCalledTimes(callsAfterFirst) // No new queries
|
|
812
|
-
})
|
|
813
|
-
})
|
|
814
|
-
})
|