@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.
Files changed (473) hide show
  1. package/package.json +2 -2
  2. package/dist/modules/auth/__integration__/TC-AUTH-001.spec.js +0 -9
  3. package/dist/modules/auth/__integration__/TC-AUTH-001.spec.js.map +0 -7
  4. package/dist/modules/auth/__integration__/TC-AUTH-002.spec.js +0 -22
  5. package/dist/modules/auth/__integration__/TC-AUTH-002.spec.js.map +0 -7
  6. package/dist/modules/auth/__integration__/TC-AUTH-003.spec.js +0 -21
  7. package/dist/modules/auth/__integration__/TC-AUTH-003.spec.js.map +0 -7
  8. package/dist/modules/auth/__integration__/TC-AUTH-004.spec.js +0 -15
  9. package/dist/modules/auth/__integration__/TC-AUTH-004.spec.js.map +0 -7
  10. package/dist/modules/auth/__integration__/TC-AUTH-005.spec.js +0 -11
  11. package/dist/modules/auth/__integration__/TC-AUTH-005.spec.js.map +0 -7
  12. package/dist/modules/auth/__integration__/TC-AUTH-006.spec.js +0 -11
  13. package/dist/modules/auth/__integration__/TC-AUTH-006.spec.js.map +0 -7
  14. package/dist/modules/auth/__integration__/TC-AUTH-007.spec.js +0 -14
  15. package/dist/modules/auth/__integration__/TC-AUTH-007.spec.js.map +0 -7
  16. package/dist/modules/auth/__integration__/TC-AUTH-008.spec.js +0 -26
  17. package/dist/modules/auth/__integration__/TC-AUTH-008.spec.js.map +0 -7
  18. package/dist/modules/auth/__integration__/TC-AUTH-009.spec.js +0 -16
  19. package/dist/modules/auth/__integration__/TC-AUTH-009.spec.js.map +0 -7
  20. package/dist/modules/auth/__integration__/TC-AUTH-010.spec.js +0 -32
  21. package/dist/modules/auth/__integration__/TC-AUTH-010.spec.js.map +0 -7
  22. package/dist/modules/auth/__integration__/TC-AUTH-011.spec.js +0 -26
  23. package/dist/modules/auth/__integration__/TC-AUTH-011.spec.js.map +0 -7
  24. package/dist/modules/auth/__integration__/TC-AUTH-012.spec.js +0 -30
  25. package/dist/modules/auth/__integration__/TC-AUTH-012.spec.js.map +0 -7
  26. package/dist/modules/auth/__integration__/TC-AUTH-013.spec.js +0 -41
  27. package/dist/modules/auth/__integration__/TC-AUTH-013.spec.js.map +0 -7
  28. package/dist/modules/auth/__integration__/TC-AUTH-014.spec.js +0 -25
  29. package/dist/modules/auth/__integration__/TC-AUTH-014.spec.js.map +0 -7
  30. package/dist/modules/auth/__integration__/TC-AUTH-015.spec.js +0 -20
  31. package/dist/modules/auth/__integration__/TC-AUTH-015.spec.js.map +0 -7
  32. package/dist/modules/auth/__integration__/TC-AUTH-016.spec.js +0 -76
  33. package/dist/modules/auth/__integration__/TC-AUTH-016.spec.js.map +0 -7
  34. package/dist/modules/catalog/__integration__/TC-CAT-001.spec.js +0 -20
  35. package/dist/modules/catalog/__integration__/TC-CAT-001.spec.js.map +0 -7
  36. package/dist/modules/catalog/__integration__/TC-CAT-002.spec.js +0 -12
  37. package/dist/modules/catalog/__integration__/TC-CAT-002.spec.js.map +0 -7
  38. package/dist/modules/catalog/__integration__/TC-CAT-003.spec.js +0 -28
  39. package/dist/modules/catalog/__integration__/TC-CAT-003.spec.js.map +0 -7
  40. package/dist/modules/catalog/__integration__/TC-CAT-004.spec.js +0 -33
  41. package/dist/modules/catalog/__integration__/TC-CAT-004.spec.js.map +0 -7
  42. package/dist/modules/catalog/__integration__/TC-CAT-005.spec.js +0 -30
  43. package/dist/modules/catalog/__integration__/TC-CAT-005.spec.js.map +0 -7
  44. package/dist/modules/catalog/__integration__/TC-CAT-006.spec.js +0 -31
  45. package/dist/modules/catalog/__integration__/TC-CAT-006.spec.js.map +0 -7
  46. package/dist/modules/catalog/__integration__/TC-CAT-007.spec.js +0 -31
  47. package/dist/modules/catalog/__integration__/TC-CAT-007.spec.js.map +0 -7
  48. package/dist/modules/catalog/__integration__/TC-CAT-008.spec.js +0 -65
  49. package/dist/modules/catalog/__integration__/TC-CAT-008.spec.js.map +0 -7
  50. package/dist/modules/catalog/__integration__/TC-CAT-009.spec.js +0 -31
  51. package/dist/modules/catalog/__integration__/TC-CAT-009.spec.js.map +0 -7
  52. package/dist/modules/catalog/__integration__/TC-CAT-010.spec.js +0 -28
  53. package/dist/modules/catalog/__integration__/TC-CAT-010.spec.js.map +0 -7
  54. package/dist/modules/catalog/__integration__/TC-CAT-011.spec.js +0 -34
  55. package/dist/modules/catalog/__integration__/TC-CAT-011.spec.js.map +0 -7
  56. package/dist/modules/catalog/__integration__/TC-CAT-012.spec.js +0 -27
  57. package/dist/modules/catalog/__integration__/TC-CAT-012.spec.js.map +0 -7
  58. package/dist/modules/core/__integration__/admin/TC-ADMIN-001.spec.js +0 -44
  59. package/dist/modules/core/__integration__/admin/TC-ADMIN-001.spec.js.map +0 -7
  60. package/dist/modules/core/__integration__/admin/TC-ADMIN-002.spec.js +0 -53
  61. package/dist/modules/core/__integration__/admin/TC-ADMIN-002.spec.js.map +0 -7
  62. package/dist/modules/core/__integration__/admin/TC-ADMIN-003.spec.js +0 -26
  63. package/dist/modules/core/__integration__/admin/TC-ADMIN-003.spec.js.map +0 -7
  64. package/dist/modules/core/__integration__/admin/TC-ADMIN-004.spec.js +0 -47
  65. package/dist/modules/core/__integration__/admin/TC-ADMIN-004.spec.js.map +0 -7
  66. package/dist/modules/core/__integration__/admin/TC-ADMIN-005.spec.js +0 -33
  67. package/dist/modules/core/__integration__/admin/TC-ADMIN-005.spec.js.map +0 -7
  68. package/dist/modules/core/__integration__/admin/TC-ADMIN-006.spec.js +0 -30
  69. package/dist/modules/core/__integration__/admin/TC-ADMIN-006.spec.js.map +0 -7
  70. package/dist/modules/core/__integration__/admin/TC-ADMIN-007.spec.js +0 -36
  71. package/dist/modules/core/__integration__/admin/TC-ADMIN-007.spec.js.map +0 -7
  72. package/dist/modules/core/__integration__/admin/TC-ADMIN-008.spec.js +0 -113
  73. package/dist/modules/core/__integration__/admin/TC-ADMIN-008.spec.js.map +0 -7
  74. package/dist/modules/core/__integration__/admin/TC-ADMIN-009.spec.js +0 -25
  75. package/dist/modules/core/__integration__/admin/TC-ADMIN-009.spec.js.map +0 -7
  76. package/dist/modules/core/__integration__/admin/TC-ADMIN-010.spec.js +0 -35
  77. package/dist/modules/core/__integration__/admin/TC-ADMIN-010.spec.js.map +0 -7
  78. package/dist/modules/core/__integration__/helpers/api.js +0 -64
  79. package/dist/modules/core/__integration__/helpers/api.js.map +0 -7
  80. package/dist/modules/core/__integration__/helpers/auth.js +0 -98
  81. package/dist/modules/core/__integration__/helpers/auth.js.map +0 -7
  82. package/dist/modules/core/__integration__/helpers/authUi.js +0 -31
  83. package/dist/modules/core/__integration__/helpers/authUi.js.map +0 -7
  84. package/dist/modules/core/__integration__/helpers/catalogFixtures.js +0 -49
  85. package/dist/modules/core/__integration__/helpers/catalogFixtures.js.map +0 -7
  86. package/dist/modules/core/__integration__/helpers/crmFixtures.js +0 -73
  87. package/dist/modules/core/__integration__/helpers/crmFixtures.js.map +0 -7
  88. package/dist/modules/core/__integration__/helpers/salesFixtures.js +0 -63
  89. package/dist/modules/core/__integration__/helpers/salesFixtures.js.map +0 -7
  90. package/dist/modules/core/__integration__/helpers/salesUi.js +0 -464
  91. package/dist/modules/core/__integration__/helpers/salesUi.js.map +0 -7
  92. package/dist/modules/core/__integration__/integration/TC-INT-001.spec.js +0 -26
  93. package/dist/modules/core/__integration__/integration/TC-INT-001.spec.js.map +0 -7
  94. package/dist/modules/core/__integration__/integration/TC-INT-002.spec.js +0 -60
  95. package/dist/modules/core/__integration__/integration/TC-INT-002.spec.js.map +0 -7
  96. package/dist/modules/core/__integration__/integration/TC-INT-003.spec.js +0 -36
  97. package/dist/modules/core/__integration__/integration/TC-INT-003.spec.js.map +0 -7
  98. package/dist/modules/core/__integration__/integration/TC-INT-004.spec.js +0 -74
  99. package/dist/modules/core/__integration__/integration/TC-INT-004.spec.js.map +0 -7
  100. package/dist/modules/core/__integration__/integration/TC-INT-005.spec.js +0 -21
  101. package/dist/modules/core/__integration__/integration/TC-INT-005.spec.js.map +0 -7
  102. package/dist/modules/customers/__integration__/TC-CRM-001.spec.js +0 -32
  103. package/dist/modules/customers/__integration__/TC-CRM-001.spec.js.map +0 -7
  104. package/dist/modules/customers/__integration__/TC-CRM-002.spec.js +0 -35
  105. package/dist/modules/customers/__integration__/TC-CRM-002.spec.js.map +0 -7
  106. package/dist/modules/customers/__integration__/TC-CRM-003.spec.js +0 -40
  107. package/dist/modules/customers/__integration__/TC-CRM-003.spec.js.map +0 -7
  108. package/dist/modules/customers/__integration__/TC-CRM-004.spec.js +0 -40
  109. package/dist/modules/customers/__integration__/TC-CRM-004.spec.js.map +0 -7
  110. package/dist/modules/customers/__integration__/TC-CRM-005.spec.js +0 -37
  111. package/dist/modules/customers/__integration__/TC-CRM-005.spec.js.map +0 -7
  112. package/dist/modules/customers/__integration__/TC-CRM-006.spec.js +0 -42
  113. package/dist/modules/customers/__integration__/TC-CRM-006.spec.js.map +0 -7
  114. package/dist/modules/customers/__integration__/TC-CRM-007.spec.js +0 -44
  115. package/dist/modules/customers/__integration__/TC-CRM-007.spec.js.map +0 -7
  116. package/dist/modules/customers/__integration__/TC-CRM-008.spec.js +0 -50
  117. package/dist/modules/customers/__integration__/TC-CRM-008.spec.js.map +0 -7
  118. package/dist/modules/customers/__integration__/TC-CRM-009.spec.js +0 -33
  119. package/dist/modules/customers/__integration__/TC-CRM-009.spec.js.map +0 -7
  120. package/dist/modules/customers/__integration__/TC-CRM-010.spec.js +0 -39
  121. package/dist/modules/customers/__integration__/TC-CRM-010.spec.js.map +0 -7
  122. package/dist/modules/customers/__integration__/TC-CRM-011.spec.js +0 -37
  123. package/dist/modules/customers/__integration__/TC-CRM-011.spec.js.map +0 -7
  124. package/dist/modules/customers/__integration__/TC-CRM-012.spec.js +0 -38
  125. package/dist/modules/customers/__integration__/TC-CRM-012.spec.js.map +0 -7
  126. package/dist/modules/customers/__integration__/TC-CRM-013.spec.js +0 -43
  127. package/dist/modules/customers/__integration__/TC-CRM-013.spec.js.map +0 -7
  128. package/dist/modules/customers/__integration__/TC-CRM-014.spec.js +0 -23
  129. package/dist/modules/customers/__integration__/TC-CRM-014.spec.js.map +0 -7
  130. package/dist/modules/customers/__integration__/TC-CRM-015.spec.js +0 -63
  131. package/dist/modules/customers/__integration__/TC-CRM-015.spec.js.map +0 -7
  132. package/dist/modules/customers/__integration__/TC-CRM-016.spec.js +0 -46
  133. package/dist/modules/customers/__integration__/TC-CRM-016.spec.js.map +0 -7
  134. package/dist/modules/customers/__integration__/TC-CRM-017.spec.js +0 -29
  135. package/dist/modules/customers/__integration__/TC-CRM-017.spec.js.map +0 -7
  136. package/dist/modules/customers/__integration__/TC-CRM-018.spec.js +0 -52
  137. package/dist/modules/customers/__integration__/TC-CRM-018.spec.js.map +0 -7
  138. package/dist/modules/customers/__integration__/TC-CRM-019.spec.js +0 -37
  139. package/dist/modules/customers/__integration__/TC-CRM-019.spec.js.map +0 -7
  140. package/dist/modules/customers/__integration__/TC-CRM-020.spec.js +0 -65
  141. package/dist/modules/customers/__integration__/TC-CRM-020.spec.js.map +0 -7
  142. package/dist/modules/progress/__integration__/TC-PROG-001.spec.js +0 -51
  143. package/dist/modules/progress/__integration__/TC-PROG-001.spec.js.map +0 -7
  144. package/dist/modules/resources/__integration__/TC-INT-007.spec.js +0 -88
  145. package/dist/modules/resources/__integration__/TC-INT-007.spec.js.map +0 -7
  146. package/dist/modules/resources/__integration__/helpers/resourcesFixtures.js +0 -45
  147. package/dist/modules/resources/__integration__/helpers/resourcesFixtures.js.map +0 -7
  148. package/dist/modules/sales/__integration__/TC-SALES-001.spec.js +0 -20
  149. package/dist/modules/sales/__integration__/TC-SALES-001.spec.js.map +0 -7
  150. package/dist/modules/sales/__integration__/TC-SALES-002.spec.js +0 -31
  151. package/dist/modules/sales/__integration__/TC-SALES-002.spec.js.map +0 -7
  152. package/dist/modules/sales/__integration__/TC-SALES-003.spec.js +0 -13
  153. package/dist/modules/sales/__integration__/TC-SALES-003.spec.js.map +0 -7
  154. package/dist/modules/sales/__integration__/TC-SALES-004.spec.js +0 -14
  155. package/dist/modules/sales/__integration__/TC-SALES-004.spec.js.map +0 -7
  156. package/dist/modules/sales/__integration__/TC-SALES-005.spec.js +0 -15
  157. package/dist/modules/sales/__integration__/TC-SALES-005.spec.js.map +0 -7
  158. package/dist/modules/sales/__integration__/TC-SALES-006.spec.js +0 -20
  159. package/dist/modules/sales/__integration__/TC-SALES-006.spec.js.map +0 -7
  160. package/dist/modules/sales/__integration__/TC-SALES-007.spec.js +0 -19
  161. package/dist/modules/sales/__integration__/TC-SALES-007.spec.js.map +0 -7
  162. package/dist/modules/sales/__integration__/TC-SALES-008.spec.js +0 -7
  163. package/dist/modules/sales/__integration__/TC-SALES-008.spec.js.map +0 -7
  164. package/dist/modules/sales/__integration__/TC-SALES-009.spec.js +0 -7
  165. package/dist/modules/sales/__integration__/TC-SALES-009.spec.js.map +0 -7
  166. package/dist/modules/sales/__integration__/TC-SALES-010.spec.js +0 -16
  167. package/dist/modules/sales/__integration__/TC-SALES-010.spec.js.map +0 -7
  168. package/dist/modules/sales/__integration__/TC-SALES-011.spec.js +0 -20
  169. package/dist/modules/sales/__integration__/TC-SALES-011.spec.js.map +0 -7
  170. package/dist/modules/sales/__integration__/TC-SALES-012.spec.js +0 -7
  171. package/dist/modules/sales/__integration__/TC-SALES-012.spec.js.map +0 -7
  172. package/dist/modules/sales/__integration__/TC-SALES-013.spec.js +0 -66
  173. package/dist/modules/sales/__integration__/TC-SALES-013.spec.js.map +0 -7
  174. package/dist/modules/sales/__integration__/TC-SALES-014.spec.js +0 -13
  175. package/dist/modules/sales/__integration__/TC-SALES-014.spec.js.map +0 -7
  176. package/dist/modules/sales/__integration__/TC-SALES-015.spec.js +0 -13
  177. package/dist/modules/sales/__integration__/TC-SALES-015.spec.js.map +0 -7
  178. package/dist/modules/sales/__integration__/TC-SALES-016.spec.js +0 -13
  179. package/dist/modules/sales/__integration__/TC-SALES-016.spec.js.map +0 -7
  180. package/dist/modules/sales/__integration__/TC-SALES-017.spec.js +0 -44
  181. package/dist/modules/sales/__integration__/TC-SALES-017.spec.js.map +0 -7
  182. package/dist/modules/sales/__integration__/TC-SALES-018.spec.js +0 -18
  183. package/dist/modules/sales/__integration__/TC-SALES-018.spec.js.map +0 -7
  184. package/dist/modules/sales/__integration__/TC-SALES-019.spec.js +0 -16
  185. package/dist/modules/sales/__integration__/TC-SALES-019.spec.js.map +0 -7
  186. package/dist/modules/sales/__integration__/TC-SALES-020.spec.js +0 -75
  187. package/dist/modules/sales/__integration__/TC-SALES-020.spec.js.map +0 -7
  188. package/dist/modules/staff/__integration__/TC-INT-006.spec.js +0 -64
  189. package/dist/modules/staff/__integration__/TC-INT-006.spec.js.map +0 -7
  190. package/dist/modules/translations/__integration__/TC-TRANS-001.spec.js +0 -48
  191. package/dist/modules/translations/__integration__/TC-TRANS-001.spec.js.map +0 -7
  192. package/dist/modules/translations/__integration__/TC-TRANS-002.spec.js +0 -94
  193. package/dist/modules/translations/__integration__/TC-TRANS-002.spec.js.map +0 -7
  194. package/dist/modules/translations/__integration__/TC-TRANS-003.spec.js +0 -61
  195. package/dist/modules/translations/__integration__/TC-TRANS-003.spec.js.map +0 -7
  196. package/dist/modules/translations/__integration__/TC-TRANS-004.spec.js +0 -52
  197. package/dist/modules/translations/__integration__/TC-TRANS-004.spec.js.map +0 -7
  198. package/dist/modules/translations/__integration__/TC-TRANS-005.spec.js +0 -106
  199. package/dist/modules/translations/__integration__/TC-TRANS-005.spec.js.map +0 -7
  200. package/dist/modules/translations/__integration__/TC-TRANS-006.spec.js +0 -94
  201. package/dist/modules/translations/__integration__/TC-TRANS-006.spec.js.map +0 -7
  202. package/dist/modules/translations/__integration__/TC-TRANS-007.spec.js +0 -62
  203. package/dist/modules/translations/__integration__/TC-TRANS-007.spec.js.map +0 -7
  204. package/dist/modules/translations/__integration__/TC-TRANS-008.spec.js +0 -168
  205. package/dist/modules/translations/__integration__/TC-TRANS-008.spec.js.map +0 -7
  206. package/dist/modules/translations/__integration__/helpers/translationFixtures.js +0 -63
  207. package/dist/modules/translations/__integration__/helpers/translationFixtures.js.map +0 -7
  208. package/dist/modules/workflows/__integration__/TC-WF-001.spec.js +0 -73
  209. package/dist/modules/workflows/__integration__/TC-WF-001.spec.js.map +0 -7
  210. package/src/__tests__/module-decoupling.test.ts +0 -356
  211. package/src/modules/api_keys/api/__tests__/keys.route.test.ts +0 -244
  212. package/src/modules/attachments/api/__tests__/attachments.api.test.ts +0 -240
  213. package/src/modules/attachments/components/__tests__/AttachmentContentPreview.test.tsx +0 -45
  214. package/src/modules/attachments/data/__tests__/entities-ocr.test.ts +0 -15
  215. package/src/modules/attachments/lib/__tests__/ocr-config.test.ts +0 -27
  216. package/src/modules/attachments/lib/__tests__/textExtraction.test.ts +0 -64
  217. package/src/modules/audit_logs/api/__tests__/access.route.test.ts +0 -118
  218. package/src/modules/audit_logs/api/__tests__/redo.route.test.ts +0 -131
  219. package/src/modules/audit_logs/api/__tests__/undo.route.test.ts +0 -103
  220. package/src/modules/audit_logs/services/__tests__/actionLogService.test.ts +0 -26
  221. package/src/modules/auth/__integration__/TC-AUTH-001.spec.ts +0 -13
  222. package/src/modules/auth/__integration__/TC-AUTH-002.spec.ts +0 -30
  223. package/src/modules/auth/__integration__/TC-AUTH-003.spec.ts +0 -28
  224. package/src/modules/auth/__integration__/TC-AUTH-004.spec.ts +0 -21
  225. package/src/modules/auth/__integration__/TC-AUTH-005.spec.ts +0 -17
  226. package/src/modules/auth/__integration__/TC-AUTH-006.spec.ts +0 -17
  227. package/src/modules/auth/__integration__/TC-AUTH-007.spec.ts +0 -19
  228. package/src/modules/auth/__integration__/TC-AUTH-008.spec.ts +0 -31
  229. package/src/modules/auth/__integration__/TC-AUTH-009.spec.ts +0 -22
  230. package/src/modules/auth/__integration__/TC-AUTH-010.spec.ts +0 -39
  231. package/src/modules/auth/__integration__/TC-AUTH-011.spec.ts +0 -35
  232. package/src/modules/auth/__integration__/TC-AUTH-012.spec.ts +0 -36
  233. package/src/modules/auth/__integration__/TC-AUTH-013.spec.ts +0 -48
  234. package/src/modules/auth/__integration__/TC-AUTH-014.spec.ts +0 -31
  235. package/src/modules/auth/__integration__/TC-AUTH-015.spec.ts +0 -28
  236. package/src/modules/auth/__integration__/TC-AUTH-016.spec.ts +0 -109
  237. package/src/modules/auth/__tests__/cli-rotate-encryption.test.ts +0 -97
  238. package/src/modules/auth/__tests__/cli-setup-acl.test.ts +0 -148
  239. package/src/modules/auth/api/__tests__/feature-check.test.ts +0 -65
  240. package/src/modules/auth/api/__tests__/login.test.ts +0 -47
  241. package/src/modules/auth/commands/__tests__/roles.custom-fields.test.ts +0 -126
  242. package/src/modules/auth/commands/__tests__/users.custom-fields.test.ts +0 -147
  243. package/src/modules/auth/lib/__tests__/rateLimitCheck.test.ts +0 -224
  244. package/src/modules/auth/services/__tests__/authService.test.ts +0 -32
  245. package/src/modules/auth/services/__tests__/rbacService.test.ts +0 -814
  246. package/src/modules/business_rules/api/__tests__/execute.route.test.ts +0 -311
  247. package/src/modules/business_rules/api/__tests__/logs-detail.route.test.ts +0 -181
  248. package/src/modules/business_rules/api/__tests__/logs.route.test.ts +0 -261
  249. package/src/modules/business_rules/api/__tests__/rules-detail.route.test.ts +0 -115
  250. package/src/modules/business_rules/api/__tests__/rules.route.test.ts +0 -746
  251. package/src/modules/business_rules/api/__tests__/sets-detail.route.test.ts +0 -169
  252. package/src/modules/business_rules/api/__tests__/sets-members.route.test.ts +0 -367
  253. package/src/modules/business_rules/api/__tests__/sets.route.test.ts +0 -361
  254. package/src/modules/business_rules/api/__tests__/test-helpers.ts +0 -42
  255. package/src/modules/business_rules/components/utils/__tests__/formHelpers.test.ts +0 -69
  256. package/src/modules/business_rules/data/__tests__/validators.test.ts +0 -637
  257. package/src/modules/business_rules/lib/__tests__/action-executor.test.ts +0 -728
  258. package/src/modules/business_rules/lib/__tests__/expression-evaluator.test.ts +0 -592
  259. package/src/modules/business_rules/lib/__tests__/rule-engine.test.ts +0 -805
  260. package/src/modules/business_rules/lib/__tests__/rule-evaluator.test.ts +0 -436
  261. package/src/modules/catalog/__integration__/TC-CAT-001.spec.ts +0 -32
  262. package/src/modules/catalog/__integration__/TC-CAT-002.spec.ts +0 -19
  263. package/src/modules/catalog/__integration__/TC-CAT-003.spec.ts +0 -39
  264. package/src/modules/catalog/__integration__/TC-CAT-004.spec.ts +0 -41
  265. package/src/modules/catalog/__integration__/TC-CAT-005.spec.ts +0 -37
  266. package/src/modules/catalog/__integration__/TC-CAT-006.spec.ts +0 -40
  267. package/src/modules/catalog/__integration__/TC-CAT-007.spec.ts +0 -37
  268. package/src/modules/catalog/__integration__/TC-CAT-008.spec.ts +0 -76
  269. package/src/modules/catalog/__integration__/TC-CAT-009.spec.ts +0 -39
  270. package/src/modules/catalog/__integration__/TC-CAT-010.spec.ts +0 -36
  271. package/src/modules/catalog/__integration__/TC-CAT-011.spec.ts +0 -44
  272. package/src/modules/catalog/__integration__/TC-CAT-012.spec.ts +0 -35
  273. package/src/modules/catalog/api/__tests__/offers.route.test.ts +0 -161
  274. package/src/modules/catalog/api/__tests__/prices.route.test.ts +0 -39
  275. package/src/modules/catalog/api/__tests__/products.route.test.ts +0 -91
  276. package/src/modules/catalog/api/__tests__/utils.test.ts +0 -36
  277. package/src/modules/catalog/api/__tests__/variants.route.test.ts +0 -44
  278. package/src/modules/catalog/backend/catalog/products/__tests__/ProductsDataTable.test.tsx +0 -172
  279. package/src/modules/catalog/commands/__tests__/products.delete.test.ts +0 -146
  280. package/src/modules/catalog/commands/__tests__/products.update.test.ts +0 -142
  281. package/src/modules/catalog/commands/__tests__/registration.test.ts +0 -54
  282. package/src/modules/catalog/commands/__tests__/shared.test.ts +0 -129
  283. package/src/modules/catalog/components/__tests__/catalogComponentsRender.test.tsx +0 -373
  284. package/src/modules/catalog/components/products/__tests__/ProductImageCell.test.tsx +0 -51
  285. package/src/modules/catalog/components/products/__tests__/productForm.test.ts +0 -32
  286. package/src/modules/catalog/lib/__tests__/pricing.test.ts +0 -150
  287. package/src/modules/catalog/services/__tests__/catalogPricingService.test.ts +0 -21
  288. package/src/modules/configs/components/__tests__/CachePanel.test.tsx +0 -134
  289. package/src/modules/configs/components/__tests__/SystemStatusPanel.test.tsx +0 -93
  290. package/src/modules/configs/lib/__tests__/system-status.test.ts +0 -55
  291. package/src/modules/configs/lib/__tests__/upgrade-actions.test.ts +0 -135
  292. package/src/modules/core/__integration__/admin/TC-ADMIN-001.spec.ts +0 -70
  293. package/src/modules/core/__integration__/admin/TC-ADMIN-002.spec.ts +0 -83
  294. package/src/modules/core/__integration__/admin/TC-ADMIN-003.spec.ts +0 -50
  295. package/src/modules/core/__integration__/admin/TC-ADMIN-004.spec.ts +0 -77
  296. package/src/modules/core/__integration__/admin/TC-ADMIN-005.spec.ts +0 -49
  297. package/src/modules/core/__integration__/admin/TC-ADMIN-006.spec.ts +0 -59
  298. package/src/modules/core/__integration__/admin/TC-ADMIN-007.spec.ts +0 -68
  299. package/src/modules/core/__integration__/admin/TC-ADMIN-008.spec.ts +0 -127
  300. package/src/modules/core/__integration__/admin/TC-ADMIN-009.spec.ts +0 -48
  301. package/src/modules/core/__integration__/admin/TC-ADMIN-010.spec.ts +0 -57
  302. package/src/modules/core/__integration__/helpers/api.ts +0 -84
  303. package/src/modules/core/__integration__/helpers/auth.ts +0 -110
  304. package/src/modules/core/__integration__/helpers/authUi.ts +0 -33
  305. package/src/modules/core/__integration__/helpers/catalogFixtures.ts +0 -73
  306. package/src/modules/core/__integration__/helpers/crmFixtures.ts +0 -101
  307. package/src/modules/core/__integration__/helpers/salesFixtures.ts +0 -89
  308. package/src/modules/core/__integration__/helpers/salesUi.ts +0 -528
  309. package/src/modules/core/__integration__/integration/TC-INT-001.spec.ts +0 -34
  310. package/src/modules/core/__integration__/integration/TC-INT-002.spec.ts +0 -74
  311. package/src/modules/core/__integration__/integration/TC-INT-003.spec.ts +0 -43
  312. package/src/modules/core/__integration__/integration/TC-INT-004.spec.ts +0 -82
  313. package/src/modules/core/__integration__/integration/TC-INT-005.spec.ts +0 -29
  314. package/src/modules/currencies/backend/exchange-rates/__tests__/formatDateTimeLocal.test.ts +0 -78
  315. package/src/modules/currencies/data/__tests__/validators.test.ts +0 -100
  316. package/src/modules/currencies/services/__tests__/exchangeRateService.test.ts +0 -666
  317. package/src/modules/currencies/services/__tests__/rateFetchingService.basic.test.ts +0 -398
  318. package/src/modules/currencies/services/__tests__/rateFetchingService.errors.test.ts +0 -296
  319. package/src/modules/currencies/services/__tests__/rateFetchingService.providers.test.ts +0 -350
  320. package/src/modules/currencies/services/__tests__/rateFetchingService.setup.ts +0 -188
  321. package/src/modules/customers/__integration__/TC-CRM-001.spec.ts +0 -42
  322. package/src/modules/customers/__integration__/TC-CRM-002.spec.ts +0 -47
  323. package/src/modules/customers/__integration__/TC-CRM-003.spec.ts +0 -55
  324. package/src/modules/customers/__integration__/TC-CRM-004.spec.ts +0 -57
  325. package/src/modules/customers/__integration__/TC-CRM-005.spec.ts +0 -50
  326. package/src/modules/customers/__integration__/TC-CRM-006.spec.ts +0 -60
  327. package/src/modules/customers/__integration__/TC-CRM-007.spec.ts +0 -57
  328. package/src/modules/customers/__integration__/TC-CRM-008.spec.ts +0 -62
  329. package/src/modules/customers/__integration__/TC-CRM-009.spec.ts +0 -46
  330. package/src/modules/customers/__integration__/TC-CRM-010.spec.ts +0 -49
  331. package/src/modules/customers/__integration__/TC-CRM-011.spec.ts +0 -47
  332. package/src/modules/customers/__integration__/TC-CRM-012.spec.ts +0 -49
  333. package/src/modules/customers/__integration__/TC-CRM-013.spec.ts +0 -61
  334. package/src/modules/customers/__integration__/TC-CRM-014.spec.ts +0 -31
  335. package/src/modules/customers/__integration__/TC-CRM-015.spec.ts +0 -89
  336. package/src/modules/customers/__integration__/TC-CRM-016.spec.ts +0 -55
  337. package/src/modules/customers/__integration__/TC-CRM-017.spec.ts +0 -37
  338. package/src/modules/customers/__integration__/TC-CRM-018.spec.ts +0 -62
  339. package/src/modules/customers/__integration__/TC-CRM-019.spec.ts +0 -44
  340. package/src/modules/customers/__integration__/TC-CRM-020.spec.ts +0 -73
  341. package/src/modules/customers/api/__tests__/utils.test.ts +0 -61
  342. package/src/modules/customers/api/dashboard/widgets/new-deals/__tests__/route.test.ts +0 -54
  343. package/src/modules/customers/commands/__tests__/shared.test.ts +0 -263
  344. package/src/modules/customers/commands/__tests__/undo.custom-fields.test.ts +0 -1184
  345. package/src/modules/customers/components/detail/__tests__/ActivityForm.validation.test.ts +0 -37
  346. package/src/modules/customers/components/detail/__tests__/DealForm.validation.test.ts +0 -45
  347. package/src/modules/customers/components/detail/__tests__/InlineEditors.test.tsx +0 -166
  348. package/src/modules/customers/components/detail/__tests__/TaskForm.submit.test.ts +0 -21
  349. package/src/modules/customers/components/detail/hooks/__tests__/useCustomerDictionary.test.ts +0 -97
  350. package/src/modules/customers/lib/__tests__/customFieldRouting.test.ts +0 -107
  351. package/src/modules/customers/utils/__tests__/addressFormat.test.ts +0 -105
  352. package/src/modules/customers/utils/__tests__/phoneDuplicates.test.ts +0 -98
  353. package/src/modules/dashboards/__tests__/widgets.test.ts +0 -70
  354. package/src/modules/dashboards/lib/__tests__/aggregations.test.ts +0 -328
  355. package/src/modules/dashboards/lib/__tests__/formatters.test.ts +0 -128
  356. package/src/modules/directory/backend/directory/organizations/__tests__/create-submit.test.ts +0 -46
  357. package/src/modules/directory/backend/directory/organizations/__tests__/edit-submit.test.ts +0 -49
  358. package/src/modules/directory/components/__tests__/OrganizationSelect.test.tsx +0 -71
  359. package/src/modules/directory/components/__tests__/TenantSelect.test.tsx +0 -75
  360. package/src/modules/entities/__tests__/cli-decrypt-database.test.ts +0 -534
  361. package/src/modules/entities/__tests__/cli-rotate-encryption.test.ts +0 -123
  362. package/src/modules/entities/api/__tests__/encryption.api.test.ts +0 -57
  363. package/src/modules/entities/api/__tests__/records.get.custom-entity.test.ts +0 -43
  364. package/src/modules/entities/api/__tests__/records.validation.test.ts +0 -53
  365. package/src/modules/entities/backend/entities/user/__tests__/create-entity-submit.test.ts +0 -47
  366. package/src/modules/entities/backend/entities/user/__tests__/records-submit.test.ts +0 -104
  367. package/src/modules/feature_toggles/commands/__tests__/global.test.ts +0 -325
  368. package/src/modules/feature_toggles/commands/__tests__/overrides.test.ts +0 -186
  369. package/src/modules/feature_toggles/lib/__tests__/feature-flag-check.test.ts +0 -365
  370. package/src/modules/feature_toggles/lib/__tests__/queries.test.ts +0 -130
  371. package/src/modules/inbox_ops/api/emails/[id]/reprocess/__tests__/route.test.ts +0 -194
  372. package/src/modules/inbox_ops/api/proposals/[id]/__tests__/route.test.ts +0 -124
  373. package/src/modules/inbox_ops/api/proposals/[id]/accept-all/__tests__/route.test.ts +0 -154
  374. package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/__tests__/route.test.ts +0 -200
  375. package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/accept/__tests__/route.test.ts +0 -261
  376. package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/reject/__tests__/route.test.ts +0 -201
  377. package/src/modules/inbox_ops/api/proposals/[id]/reject/__tests__/route.test.ts +0 -123
  378. package/src/modules/inbox_ops/api/proposals/[id]/replies/[replyId]/send/__tests__/route.test.ts +0 -232
  379. package/src/modules/inbox_ops/api/proposals/[id]/translate/__tests__/route.test.ts +0 -173
  380. package/src/modules/inbox_ops/api/proposals/__tests__/route.test.ts +0 -185
  381. package/src/modules/inbox_ops/api/webhook/__tests__/inbound.test.ts +0 -317
  382. package/src/modules/inbox_ops/data/__tests__/validators.test.ts +0 -463
  383. package/src/modules/inbox_ops/lib/__tests__/catalogLookup.test.ts +0 -143
  384. package/src/modules/inbox_ops/lib/__tests__/contactMatcher.test.ts +0 -158
  385. package/src/modules/inbox_ops/lib/__tests__/emailParser.test.ts +0 -191
  386. package/src/modules/inbox_ops/lib/__tests__/executionEngine.test.ts +0 -1419
  387. package/src/modules/inbox_ops/lib/__tests__/extractionPrompt.test.ts +0 -151
  388. package/src/modules/inbox_ops/lib/__tests__/priceValidator.test.ts +0 -259
  389. package/src/modules/inbox_ops/lib/__tests__/translationProvider.test.ts +0 -99
  390. package/src/modules/inbox_ops/subscribers/__tests__/extractionWorker.test.ts +0 -803
  391. package/src/modules/notifications/__tests__/deliver-notification.test.ts +0 -285
  392. package/src/modules/notifications/__tests__/deliveryStrategies.test.ts +0 -19
  393. package/src/modules/notifications/__tests__/notificationService.test.ts +0 -248
  394. package/src/modules/planner/__tests__/availabilityMerge.test.ts +0 -99
  395. package/src/modules/planner/__tests__/plannerAvailabilityService.test.ts +0 -89
  396. package/src/modules/planner/data/__tests__/validators.test.ts +0 -78
  397. package/src/modules/progress/__integration__/TC-PROG-001.spec.ts +0 -67
  398. package/src/modules/progress/__tests__/progressService.test.ts +0 -377
  399. package/src/modules/query_index/__tests__/hybrid-engine.test.ts +0 -365
  400. package/src/modules/query_index/__tests__/indexer.test.ts +0 -175
  401. package/src/modules/resources/__integration__/TC-INT-007.spec.ts +0 -110
  402. package/src/modules/resources/__integration__/helpers/resourcesFixtures.ts +0 -50
  403. package/src/modules/resources/data/__tests__/validators.test.ts +0 -65
  404. package/src/modules/sales/__integration__/TC-SALES-001.spec.ts +0 -26
  405. package/src/modules/sales/__integration__/TC-SALES-002.spec.ts +0 -38
  406. package/src/modules/sales/__integration__/TC-SALES-003.spec.ts +0 -18
  407. package/src/modules/sales/__integration__/TC-SALES-004.spec.ts +0 -19
  408. package/src/modules/sales/__integration__/TC-SALES-005.spec.ts +0 -21
  409. package/src/modules/sales/__integration__/TC-SALES-006.spec.ts +0 -26
  410. package/src/modules/sales/__integration__/TC-SALES-007.spec.ts +0 -23
  411. package/src/modules/sales/__integration__/TC-SALES-008.spec.ts +0 -11
  412. package/src/modules/sales/__integration__/TC-SALES-009.spec.ts +0 -12
  413. package/src/modules/sales/__integration__/TC-SALES-010.spec.ts +0 -20
  414. package/src/modules/sales/__integration__/TC-SALES-011.spec.ts +0 -26
  415. package/src/modules/sales/__integration__/TC-SALES-012.spec.ts +0 -12
  416. package/src/modules/sales/__integration__/TC-SALES-013.spec.ts +0 -73
  417. package/src/modules/sales/__integration__/TC-SALES-014.spec.ts +0 -17
  418. package/src/modules/sales/__integration__/TC-SALES-015.spec.ts +0 -17
  419. package/src/modules/sales/__integration__/TC-SALES-016.spec.ts +0 -17
  420. package/src/modules/sales/__integration__/TC-SALES-017.spec.ts +0 -60
  421. package/src/modules/sales/__integration__/TC-SALES-018.spec.ts +0 -25
  422. package/src/modules/sales/__integration__/TC-SALES-019.spec.ts +0 -22
  423. package/src/modules/sales/__integration__/TC-SALES-020.spec.ts +0 -107
  424. package/src/modules/sales/api/__tests__/channels.route.test.ts +0 -50
  425. package/src/modules/sales/api/__tests__/document-history.test.ts +0 -146
  426. package/src/modules/sales/api/__tests__/documents.factory.test.ts +0 -98
  427. package/src/modules/sales/api/__tests__/documents.routes.test.ts +0 -149
  428. package/src/modules/sales/api/__tests__/quotes.acceptance.test.ts +0 -209
  429. package/src/modules/sales/api/__tests__/timeline.test.tsx +0 -165
  430. package/src/modules/sales/api/dashboard/widgets/new-orders/__tests__/route.test.ts +0 -112
  431. package/src/modules/sales/api/dashboard/widgets/new-quotes/__tests__/route.test.ts +0 -116
  432. package/src/modules/sales/commands/__tests__/documents.cache.test.ts +0 -126
  433. package/src/modules/sales/commands/__tests__/documents.undo.test.ts +0 -170
  434. package/src/modules/sales/commands/__tests__/registration.test.ts +0 -141
  435. package/src/modules/sales/components/__tests__/salesComponentsRender.test.tsx +0 -456
  436. package/src/modules/sales/lib/__tests__/calculations.test.ts +0 -210
  437. package/src/modules/sales/services/__tests__/salesCalculationService.test.ts +0 -181
  438. package/src/modules/sales/services/__tests__/taxCalculationService.test.ts +0 -79
  439. package/src/modules/sales/widgets/dashboard/new-orders/__tests__/config.test.ts +0 -47
  440. package/src/modules/sales/widgets/dashboard/new-quotes/__tests__/config.test.ts +0 -47
  441. package/src/modules/staff/__integration__/TC-INT-006.spec.ts +0 -71
  442. package/src/modules/staff/data/__tests__/validators.test.ts +0 -60
  443. package/src/modules/translations/__integration__/TC-TRANS-001.spec.ts +0 -57
  444. package/src/modules/translations/__integration__/TC-TRANS-002.spec.ts +0 -114
  445. package/src/modules/translations/__integration__/TC-TRANS-003.spec.ts +0 -71
  446. package/src/modules/translations/__integration__/TC-TRANS-004.spec.ts +0 -66
  447. package/src/modules/translations/__integration__/TC-TRANS-005.spec.ts +0 -135
  448. package/src/modules/translations/__integration__/TC-TRANS-006.spec.ts +0 -113
  449. package/src/modules/translations/__integration__/TC-TRANS-007.spec.ts +0 -80
  450. package/src/modules/translations/__integration__/TC-TRANS-008.spec.ts +0 -209
  451. package/src/modules/translations/__integration__/helpers/translationFixtures.ts +0 -95
  452. package/src/modules/translations/api/__tests__/locales.test.ts +0 -67
  453. package/src/modules/translations/data/__tests__/validators.test.ts +0 -143
  454. package/src/modules/translations/lib/__tests__/extract-record-id.test.ts +0 -75
  455. package/src/modules/translations/lib/__tests__/helpers.test.ts +0 -215
  456. package/src/modules/translations/lib/__tests__/locale.test.ts +0 -115
  457. package/src/modules/translations/lib/__tests__/resolve-field-list.test.ts +0 -176
  458. package/src/modules/translations/lib/__tests__/translatable-fields.test.ts +0 -79
  459. package/src/modules/translations/widgets/__tests__/injection-table.test.ts +0 -83
  460. package/src/modules/workflows/__integration__/TC-WF-001.spec.ts +0 -114
  461. package/src/modules/workflows/api/__tests__/definitions.route.test.ts +0 -762
  462. package/src/modules/workflows/api/__tests__/instances.route.test.ts +0 -869
  463. package/src/modules/workflows/data/__tests__/validators.test.ts +0 -707
  464. package/src/modules/workflows/lib/__tests__/activity-executor.test.ts +0 -1230
  465. package/src/modules/workflows/lib/__tests__/call-api.test.ts +0 -421
  466. package/src/modules/workflows/lib/__tests__/compensation.test.ts +0 -713
  467. package/src/modules/workflows/lib/__tests__/event-logger.test.ts +0 -615
  468. package/src/modules/workflows/lib/__tests__/integration.test.ts +0 -693
  469. package/src/modules/workflows/lib/__tests__/signals.test.ts +0 -566
  470. package/src/modules/workflows/lib/__tests__/step-handler.test.ts +0 -670
  471. package/src/modules/workflows/lib/__tests__/sub-workflow.test.ts +0 -934
  472. package/src/modules/workflows/lib/__tests__/transition-handler.test.ts +0 -925
  473. 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
- })