@sync-in/server 1.4.0 → 1.5.1

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 (229) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +2 -1
  3. package/package.json +5 -5
  4. package/server/applications/comments/comments.controller.spec.js +103 -4
  5. package/server/applications/comments/comments.controller.spec.js.map +1 -1
  6. package/server/applications/comments/services/comments-manager.service.spec.js +409 -9
  7. package/server/applications/comments/services/comments-manager.service.spec.js.map +1 -1
  8. package/server/applications/files/adapters/files-indexer-mysql.service.spec.js +333 -0
  9. package/server/applications/files/adapters/files-indexer-mysql.service.spec.js.map +1 -0
  10. package/server/applications/files/constants/routes.js +6 -1
  11. package/server/applications/files/constants/routes.js.map +1 -1
  12. package/server/applications/files/files-only-office.controller.js +11 -0
  13. package/server/applications/files/files-only-office.controller.js.map +1 -1
  14. package/server/applications/files/files-only-office.controller.spec.js +97 -3
  15. package/server/applications/files/files-only-office.controller.spec.js.map +1 -1
  16. package/server/applications/files/files-tasks.controller.spec.js +91 -1
  17. package/server/applications/files/files-tasks.controller.spec.js.map +1 -1
  18. package/server/applications/files/files.controller.spec.js +268 -46
  19. package/server/applications/files/files.controller.spec.js.map +1 -1
  20. package/server/applications/files/guards/files-only-office.guard.spec.js +77 -1
  21. package/server/applications/files/guards/files-only-office.guard.spec.js.map +1 -1
  22. package/server/applications/files/services/files-only-office-manager.service.js +5 -0
  23. package/server/applications/files/services/files-only-office-manager.service.js.map +1 -1
  24. package/server/applications/links/links.controller.spec.js +91 -58
  25. package/server/applications/links/links.controller.spec.js.map +1 -1
  26. package/server/applications/links/services/links-manager.service.js +4 -6
  27. package/server/applications/links/services/links-manager.service.js.map +1 -1
  28. package/server/applications/links/services/links-manager.service.spec.js +378 -14
  29. package/server/applications/links/services/links-manager.service.spec.js.map +1 -1
  30. package/server/applications/links/services/links-queries.service.js +1 -1
  31. package/server/applications/links/services/links-queries.service.js.map +1 -1
  32. package/server/applications/notifications/notifications.controller.spec.js +56 -1
  33. package/server/applications/notifications/notifications.controller.spec.js.map +1 -1
  34. package/server/applications/notifications/services/notifications-manager.service.spec.js +461 -5
  35. package/server/applications/notifications/services/notifications-manager.service.spec.js.map +1 -1
  36. package/server/applications/shares/services/shares-manager.service.spec.js +590 -14
  37. package/server/applications/shares/services/shares-manager.service.spec.js.map +1 -1
  38. package/server/applications/sync/interceptors/sync-diff-gzip-body.interceptor.spec.js +120 -0
  39. package/server/applications/sync/interceptors/sync-diff-gzip-body.interceptor.spec.js.map +1 -0
  40. package/server/applications/sync/services/sync-clients-manager.service.spec.js +548 -8
  41. package/server/applications/sync/services/sync-clients-manager.service.spec.js.map +1 -1
  42. package/server/applications/sync/services/sync-manager.service.spec.js +837 -5
  43. package/server/applications/sync/services/sync-manager.service.spec.js.map +1 -1
  44. package/server/applications/sync/services/sync-paths-manager.service.spec.js +900 -7
  45. package/server/applications/sync/services/sync-paths-manager.service.spec.js.map +1 -1
  46. package/server/applications/users/services/admin-users-manager.service.js +22 -24
  47. package/server/applications/users/services/admin-users-manager.service.js.map +1 -1
  48. package/server/applications/users/services/admin-users-manager.service.spec.js +763 -17
  49. package/server/applications/users/services/admin-users-manager.service.spec.js.map +1 -1
  50. package/server/applications/users/services/users-manager.service.js +1 -1
  51. package/server/applications/users/services/users-manager.service.js.map +1 -1
  52. package/server/applications/users/services/users-manager.service.spec.js +938 -49
  53. package/server/applications/users/services/users-manager.service.spec.js.map +1 -1
  54. package/server/applications/webdav/decorators/if-header.decorator.js +4 -1
  55. package/server/applications/webdav/decorators/if-header.decorator.js.map +1 -1
  56. package/server/applications/webdav/filters/webdav.filter.spec.js +77 -0
  57. package/server/applications/webdav/filters/webdav.filter.spec.js.map +1 -0
  58. package/server/applications/webdav/guards/webdav-protocol.guard.js +3 -7
  59. package/server/applications/webdav/guards/webdav-protocol.guard.js.map +1 -1
  60. package/server/applications/webdav/guards/webdav-protocol.guard.spec.js +580 -0
  61. package/server/applications/webdav/guards/webdav-protocol.guard.spec.js.map +1 -0
  62. package/server/applications/webdav/services/webdav-methods.service.spec.js +1582 -3
  63. package/server/applications/webdav/services/webdav-methods.service.spec.js.map +1 -1
  64. package/server/applications/webdav/services/webdav-spaces.service.spec.js +390 -2
  65. package/server/applications/webdav/services/webdav-spaces.service.spec.js.map +1 -1
  66. package/server/applications/webdav/webdav.controller.js +2 -2
  67. package/server/applications/webdav/webdav.controller.js.map +1 -1
  68. package/server/authentication/guards/auth-basic.guard.js.map +1 -1
  69. package/server/authentication/guards/auth-digest.guard.js +1 -2
  70. package/server/authentication/guards/auth-digest.guard.js.map +1 -1
  71. package/server/authentication/guards/auth-local.guard.js.map +1 -1
  72. package/server/infrastructure/context/interceptors/context.interceptor.spec.js +135 -0
  73. package/server/infrastructure/context/interceptors/context.interceptor.spec.js.map +1 -0
  74. package/static/3rdpartylicenses.txt +26 -26
  75. package/static/assets/pdfjs/build/pdf.mjs +1177 -255
  76. package/static/assets/pdfjs/build/pdf.mjs.map +1 -1
  77. package/static/assets/pdfjs/build/pdf.sandbox.mjs +25 -2
  78. package/static/assets/pdfjs/build/pdf.sandbox.mjs.map +1 -1
  79. package/static/assets/pdfjs/build/pdf.worker.mjs +140 -16
  80. package/static/assets/pdfjs/build/pdf.worker.mjs.map +1 -1
  81. package/static/assets/pdfjs/version +1 -1
  82. package/static/assets/pdfjs/web/debugger.css +31 -0
  83. package/static/assets/pdfjs/web/debugger.mjs +144 -2
  84. package/static/assets/pdfjs/web/images/comment-editButton.svg +6 -1
  85. package/static/assets/pdfjs/web/locale/ach/viewer.ftl +0 -63
  86. package/static/assets/pdfjs/web/locale/af/viewer.ftl +0 -71
  87. package/static/assets/pdfjs/web/locale/an/viewer.ftl +0 -63
  88. package/static/assets/pdfjs/web/locale/ast/viewer.ftl +0 -60
  89. package/static/assets/pdfjs/web/locale/az/viewer.ftl +0 -63
  90. package/static/assets/pdfjs/web/locale/be/viewer.ftl +38 -0
  91. package/static/assets/pdfjs/web/locale/bg/viewer.ftl +0 -37
  92. package/static/assets/pdfjs/web/locale/bn/viewer.ftl +0 -63
  93. package/static/assets/pdfjs/web/locale/bo/viewer.ftl +0 -63
  94. package/static/assets/pdfjs/web/locale/br/viewer.ftl +0 -37
  95. package/static/assets/pdfjs/web/locale/brx/viewer.ftl +0 -63
  96. package/static/assets/pdfjs/web/locale/bs/viewer.ftl +22 -0
  97. package/static/assets/pdfjs/web/locale/ca/viewer.ftl +0 -54
  98. package/static/assets/pdfjs/web/locale/cak/viewer.ftl +0 -54
  99. package/static/assets/pdfjs/web/locale/ckb/viewer.ftl +0 -63
  100. package/static/assets/pdfjs/web/locale/cs/viewer.ftl +38 -0
  101. package/static/assets/pdfjs/web/locale/cy/viewer.ftl +38 -0
  102. package/static/assets/pdfjs/web/locale/da/viewer.ftl +38 -0
  103. package/static/assets/pdfjs/web/locale/de/viewer.ftl +38 -0
  104. package/static/assets/pdfjs/web/locale/dsb/viewer.ftl +38 -0
  105. package/static/assets/pdfjs/web/locale/el/viewer.ftl +38 -0
  106. package/static/assets/pdfjs/web/locale/en-CA/viewer.ftl +38 -0
  107. package/static/assets/pdfjs/web/locale/en-GB/viewer.ftl +38 -0
  108. package/static/assets/pdfjs/web/locale/en-US/viewer.ftl +25 -0
  109. package/static/assets/pdfjs/web/locale/eo/viewer.ftl +38 -0
  110. package/static/assets/pdfjs/web/locale/es-AR/viewer.ftl +38 -0
  111. package/static/assets/pdfjs/web/locale/es-CL/viewer.ftl +38 -0
  112. package/static/assets/pdfjs/web/locale/es-MX/viewer.ftl +0 -6
  113. package/static/assets/pdfjs/web/locale/et/viewer.ftl +0 -57
  114. package/static/assets/pdfjs/web/locale/fa/viewer.ftl +0 -37
  115. package/static/assets/pdfjs/web/locale/ff/viewer.ftl +0 -63
  116. package/static/assets/pdfjs/web/locale/fi/viewer.ftl +38 -0
  117. package/static/assets/pdfjs/web/locale/fr/viewer.ftl +38 -0
  118. package/static/assets/pdfjs/web/locale/fy-NL/viewer.ftl +38 -0
  119. package/static/assets/pdfjs/web/locale/ga-IE/viewer.ftl +0 -71
  120. package/static/assets/pdfjs/web/locale/gd/viewer.ftl +0 -54
  121. package/static/assets/pdfjs/web/locale/gl/viewer.ftl +8 -0
  122. package/static/assets/pdfjs/web/locale/gn/viewer.ftl +38 -0
  123. package/static/assets/pdfjs/web/locale/gu-IN/viewer.ftl +0 -63
  124. package/static/assets/pdfjs/web/locale/he/viewer.ftl +38 -0
  125. package/static/assets/pdfjs/web/locale/hi-IN/viewer.ftl +0 -60
  126. package/static/assets/pdfjs/web/locale/hsb/viewer.ftl +38 -0
  127. package/static/assets/pdfjs/web/locale/hu/viewer.ftl +38 -0
  128. package/static/assets/pdfjs/web/locale/hy-AM/viewer.ftl +0 -49
  129. package/static/assets/pdfjs/web/locale/hye/viewer.ftl +0 -60
  130. package/static/assets/pdfjs/web/locale/ia/viewer.ftl +38 -0
  131. package/static/assets/pdfjs/web/locale/is/viewer.ftl +0 -3
  132. package/static/assets/pdfjs/web/locale/it/viewer.ftl +31 -0
  133. package/static/assets/pdfjs/web/locale/ja/viewer.ftl +8 -0
  134. package/static/assets/pdfjs/web/locale/ka/viewer.ftl +48 -10
  135. package/static/assets/pdfjs/web/locale/kab/viewer.ftl +5 -0
  136. package/static/assets/pdfjs/web/locale/kk/viewer.ftl +8 -0
  137. package/static/assets/pdfjs/web/locale/km/viewer.ftl +0 -63
  138. package/static/assets/pdfjs/web/locale/kn/viewer.ftl +0 -71
  139. package/static/assets/pdfjs/web/locale/ko/viewer.ftl +38 -0
  140. package/static/assets/pdfjs/web/locale/lij/viewer.ftl +0 -63
  141. package/static/assets/pdfjs/web/locale/lo/viewer.ftl +0 -54
  142. package/static/assets/pdfjs/web/locale/lt/viewer.ftl +0 -60
  143. package/static/assets/pdfjs/web/locale/ltg/viewer.ftl +0 -63
  144. package/static/assets/pdfjs/web/locale/lv/viewer.ftl +0 -63
  145. package/static/assets/pdfjs/web/locale/meh/viewer.ftl +0 -75
  146. package/static/assets/pdfjs/web/locale/mk/viewer.ftl +0 -63
  147. package/static/assets/pdfjs/web/locale/ml/viewer.ftl +0 -3
  148. package/static/assets/pdfjs/web/locale/mr/viewer.ftl +0 -63
  149. package/static/assets/pdfjs/web/locale/ms/viewer.ftl +0 -63
  150. package/static/assets/pdfjs/web/locale/my/viewer.ftl +0 -71
  151. package/static/assets/pdfjs/web/locale/nb-NO/viewer.ftl +44 -6
  152. package/static/assets/pdfjs/web/locale/ne-NP/viewer.ftl +0 -71
  153. package/static/assets/pdfjs/web/locale/nl/viewer.ftl +38 -0
  154. package/static/assets/pdfjs/web/locale/nn-NO/viewer.ftl +45 -1
  155. package/static/assets/pdfjs/web/locale/oc/viewer.ftl +0 -31
  156. package/static/assets/pdfjs/web/locale/pa-IN/viewer.ftl +38 -0
  157. package/static/assets/pdfjs/web/locale/pl/viewer.ftl +39 -1
  158. package/static/assets/pdfjs/web/locale/pt-BR/viewer.ftl +38 -0
  159. package/static/assets/pdfjs/web/locale/ro/viewer.ftl +355 -1
  160. package/static/assets/pdfjs/web/locale/ru/viewer.ftl +38 -0
  161. package/static/assets/pdfjs/web/locale/sat/viewer.ftl +0 -54
  162. package/static/assets/pdfjs/web/locale/sc/viewer.ftl +0 -38
  163. package/static/assets/pdfjs/web/locale/scn/viewer.ftl +0 -92
  164. package/static/assets/pdfjs/web/locale/sco/viewer.ftl +0 -60
  165. package/static/assets/pdfjs/web/locale/si/viewer.ftl +0 -51
  166. package/static/assets/pdfjs/web/locale/sk/viewer.ftl +38 -0
  167. package/static/assets/pdfjs/web/locale/skr/viewer.ftl +0 -27
  168. package/static/assets/pdfjs/web/locale/sl/viewer.ftl +8 -0
  169. package/static/assets/pdfjs/web/locale/son/viewer.ftl +0 -71
  170. package/static/assets/pdfjs/web/locale/sr/viewer.ftl +0 -33
  171. package/static/assets/pdfjs/web/locale/sv-SE/viewer.ftl +38 -0
  172. package/static/assets/pdfjs/web/locale/szl/viewer.ftl +0 -63
  173. package/static/assets/pdfjs/web/locale/ta/viewer.ftl +0 -63
  174. package/static/assets/pdfjs/web/locale/te/viewer.ftl +0 -60
  175. package/static/assets/pdfjs/web/locale/tg/viewer.ftl +38 -0
  176. package/static/assets/pdfjs/web/locale/tl/viewer.ftl +0 -63
  177. package/static/assets/pdfjs/web/locale/tr/viewer.ftl +40 -2
  178. package/static/assets/pdfjs/web/locale/trs/viewer.ftl +0 -72
  179. package/static/assets/pdfjs/web/locale/ur/viewer.ftl +0 -60
  180. package/static/assets/pdfjs/web/locale/uz/viewer.ftl +0 -71
  181. package/static/assets/pdfjs/web/locale/vi/viewer.ftl +38 -0
  182. package/static/assets/pdfjs/web/locale/wo/viewer.ftl +0 -77
  183. package/static/assets/pdfjs/web/locale/xh/viewer.ftl +0 -71
  184. package/static/assets/pdfjs/web/locale/zh-CN/viewer.ftl +38 -0
  185. package/static/assets/pdfjs/web/locale/zh-TW/viewer.ftl +38 -0
  186. package/static/assets/pdfjs/web/viewer.css +649 -120
  187. package/static/assets/pdfjs/web/viewer.html +19 -0
  188. package/static/assets/pdfjs/web/viewer.mjs +489 -38
  189. package/static/assets/pdfjs/web/viewer.mjs.map +1 -1
  190. package/static/chunk-22EANI6R.js +1 -0
  191. package/static/{chunk-N2LYWNTC.js → chunk-2456KVFZ.js} +1 -1
  192. package/static/{chunk-YCINY2YI.js → chunk-2LVCLKCK.js} +1 -1
  193. package/static/{chunk-5YKWZT33.js → chunk-2V5S7DWD.js} +1 -1
  194. package/static/{chunk-3GC2BQZD.js → chunk-44YDXGNZ.js} +1 -1
  195. package/static/{chunk-W3QXNDI5.js → chunk-4LSJLWYV.js} +1 -1
  196. package/static/{chunk-T55FAU2O.js → chunk-4UT5VH7R.js} +1 -1
  197. package/static/{chunk-VMQMD36Z.js → chunk-5GOMMRRE.js} +1 -1
  198. package/static/{chunk-TXPODW5Q.js → chunk-5J4VRDKB.js} +1 -1
  199. package/static/{chunk-FQ4AFNGE.js → chunk-6PVKNZ7Q.js} +1 -1
  200. package/static/{chunk-XE5YHU5J.js → chunk-BIUNUYZ5.js} +1 -1
  201. package/static/{chunk-X43VWRFP.js → chunk-DFQKHCDR.js} +1 -1
  202. package/static/{chunk-TDQAEVZN.js → chunk-EE2TDTY4.js} +1 -1
  203. package/static/{chunk-MOVWEZ7J.js → chunk-ESNDJ5T6.js} +1 -1
  204. package/static/{chunk-TCFKH6K6.js → chunk-GDKKLLEU.js} +1 -1
  205. package/static/{chunk-VHYIXL7R.js → chunk-GSR2MCQG.js} +1 -1
  206. package/static/{chunk-LJIGRUEF.js → chunk-HR7KS5BR.js} +1 -1
  207. package/static/chunk-HW2H3ISM.js +559 -0
  208. package/static/{chunk-HZA7R43P.js → chunk-IMB3C547.js} +1 -1
  209. package/static/{chunk-B2Y2RNFP.js → chunk-J4ALHUDX.js} +1 -1
  210. package/static/{chunk-PF4K7MVG.js → chunk-KP6LSQTK.js} +1 -1
  211. package/static/{chunk-C23BPTJZ.js → chunk-LUZCOHFN.js} +1 -1
  212. package/static/{chunk-GBCYYDCI.js → chunk-MHSCCXVL.js} +1 -1
  213. package/static/{chunk-PY3BGNJN.js → chunk-OMRQYBXV.js} +1 -1
  214. package/static/chunk-P7CTJ5BG.js +27 -0
  215. package/static/{chunk-Y2CDUS4J.js → chunk-P7PX67IR.js} +4 -4
  216. package/static/{chunk-VMUOUCEI.js → chunk-PPO7DBVO.js} +1 -1
  217. package/static/{chunk-TTQ37MUV.js → chunk-RSS6GYNE.js} +1 -1
  218. package/static/{chunk-WWIC7UW3.js → chunk-SLGGINMR.js} +1 -1
  219. package/static/{chunk-KZQCFEPT.js → chunk-UHD5XD3G.js} +1 -1
  220. package/static/{chunk-TNW2CGK6.js → chunk-UPYYAJCJ.js} +1 -1
  221. package/static/{chunk-6F55D74O.js → chunk-VHYPQ3D4.js} +1 -1
  222. package/static/{chunk-DGVNNICG.js → chunk-YQSDS6BO.js} +1 -1
  223. package/static/{chunk-MTRNPGS4.js → chunk-ZC5NIT55.js} +1 -1
  224. package/static/index.html +1 -1
  225. package/static/main-FYD34UEC.js +7 -0
  226. package/static/chunk-RSNLYAN6.js +0 -560
  227. package/static/chunk-WHMS3PJ3.js +0 -24
  228. package/static/chunk-ZZ3LHYOY.js +0 -1
  229. package/static/main-7LDKYVXO.js +0 -10
@@ -7,49 +7,589 @@ Object.defineProperty(exports, "__esModule", {
7
7
  value: true
8
8
  });
9
9
  const _axios = require("@nestjs/axios");
10
+ const _common = require("@nestjs/common");
10
11
  const _testing = require("@nestjs/testing");
12
+ const _nodecrypto = /*#__PURE__*/ _interop_require_default(require("node:crypto"));
13
+ const _promises = /*#__PURE__*/ _interop_require_default(require("node:fs/promises"));
11
14
  const _authmethod = require("../../../authentication/models/auth-method");
12
15
  const _authmanagerservice = require("../../../authentication/services/auth-manager.service");
16
+ const _functions = /*#__PURE__*/ _interop_require_wildcard(require("../../../common/functions"));
17
+ const _shared = /*#__PURE__*/ _interop_require_wildcard(require("../../../common/shared"));
18
+ const _configenvironment = require("../../../configuration/config.environment");
13
19
  const _cacheservice = require("../../../infrastructure/cache/services/cache.service");
20
+ const _files = require("../../files/utils/files");
14
21
  const _usersqueriesservice = require("../../users/services/users-queries.service");
22
+ const _auth = require("../constants/auth");
23
+ const _store = require("../constants/store");
24
+ const _sync = require("../constants/sync");
15
25
  const _syncclientsmanagerservice = require("./sync-clients-manager.service");
16
26
  const _syncqueriesservice = require("./sync-queries.service");
27
+ function _interop_require_default(obj) {
28
+ return obj && obj.__esModule ? obj : {
29
+ default: obj
30
+ };
31
+ }
32
+ function _getRequireWildcardCache(nodeInterop) {
33
+ if (typeof WeakMap !== "function") return null;
34
+ var cacheBabelInterop = new WeakMap();
35
+ var cacheNodeInterop = new WeakMap();
36
+ return (_getRequireWildcardCache = function(nodeInterop) {
37
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
38
+ })(nodeInterop);
39
+ }
40
+ function _interop_require_wildcard(obj, nodeInterop) {
41
+ if (!nodeInterop && obj && obj.__esModule) {
42
+ return obj;
43
+ }
44
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
45
+ return {
46
+ default: obj
47
+ };
48
+ }
49
+ var cache = _getRequireWildcardCache(nodeInterop);
50
+ if (cache && cache.has(obj)) {
51
+ return cache.get(obj);
52
+ }
53
+ var newObj = {
54
+ __proto__: null
55
+ };
56
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
57
+ for(var key in obj){
58
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
59
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
60
+ if (desc && (desc.get || desc.set)) {
61
+ Object.defineProperty(newObj, key, desc);
62
+ } else {
63
+ newObj[key] = obj[key];
64
+ }
65
+ }
66
+ }
67
+ newObj.default = obj;
68
+ if (cache) {
69
+ cache.set(obj, newObj);
70
+ }
71
+ return newObj;
72
+ }
73
+ // Pilotage permission via UserModel
74
+ let mockHavePermission = true;
75
+ jest.mock('../../users/models/user.model', ()=>({
76
+ UserModel: jest.fn().mockImplementation((props)=>({
77
+ ...props,
78
+ havePermission: ()=>mockHavePermission
79
+ }))
80
+ }));
81
+ // Mock ciblé de convertHumanTimeToSeconds
82
+ jest.mock('../../../common/functions', ()=>{
83
+ const actual = jest.requireActual('../../../common/functions');
84
+ return {
85
+ ...actual,
86
+ convertHumanTimeToSeconds: jest.fn()
87
+ };
88
+ });
89
+ // Mock currentTimeStamp
90
+ jest.mock('../../../common/shared', ()=>({
91
+ currentTimeStamp: jest.fn()
92
+ }));
93
+ // Mock FS et helper d'existence
94
+ jest.mock('node:fs/promises', ()=>({
95
+ readFile: jest.fn()
96
+ }));
97
+ jest.mock('../../files/utils/files', ()=>({
98
+ isPathExists: jest.fn()
99
+ }));
17
100
  describe(_syncclientsmanagerservice.SyncClientsManager.name, ()=>{
18
101
  let service;
102
+ // Mocks
103
+ let http;
104
+ let authManager;
105
+ let authMethod;
106
+ let usersQueries;
107
+ let syncQueries;
108
+ let cacheMock;
109
+ // Helpers
110
+ const setRepo = (repo)=>{
111
+ ;
112
+ _configenvironment.configuration.applications.appStore.repository = repo;
113
+ };
114
+ const makeClient = (overrides = {})=>({
115
+ id: 'cid',
116
+ ownerId: 1,
117
+ tokenExpiration: 2000,
118
+ enabled: true,
119
+ info: {
120
+ type: 'desktop'
121
+ },
122
+ ...overrides
123
+ });
124
+ const makeUser = (overrides = {})=>({
125
+ id: 1,
126
+ isActive: true,
127
+ login: 'u',
128
+ email: 'u@x',
129
+ firstName: 'U',
130
+ lastName: 'X',
131
+ role: 1,
132
+ permissions: 'desktop',
133
+ ...overrides
134
+ });
19
135
  beforeAll(async ()=>{
136
+ http = {
137
+ axiosRef: jest.fn()
138
+ };
139
+ authManager = {
140
+ setCookies: jest.fn(),
141
+ getTokens: jest.fn()
142
+ };
143
+ authMethod = {
144
+ validateUser: jest.fn()
145
+ };
146
+ usersQueries = {
147
+ from: jest.fn(),
148
+ updateUserOrGuest: jest.fn()
149
+ };
150
+ syncQueries = {
151
+ getOrCreateClient: jest.fn(),
152
+ deleteClient: jest.fn(),
153
+ getClient: jest.fn(),
154
+ updateClientInfo: jest.fn(),
155
+ renewClientTokenAndExpiration: jest.fn(),
156
+ getClients: jest.fn()
157
+ };
158
+ cacheMock = {
159
+ genSlugKey: jest.fn().mockReturnValue('syncclientsmanager:checkappstore'),
160
+ get: jest.fn().mockResolvedValue(undefined),
161
+ set: jest.fn().mockResolvedValue(undefined),
162
+ del: jest.fn().mockResolvedValue(undefined)
163
+ };
20
164
  const module = await _testing.Test.createTestingModule({
21
165
  providers: [
22
166
  _syncclientsmanagerservice.SyncClientsManager,
23
167
  {
24
168
  provide: _cacheservice.Cache,
25
- useValue: {}
169
+ useValue: cacheMock
26
170
  },
27
171
  {
28
172
  provide: _axios.HttpService,
29
- useValue: {}
173
+ useValue: http
30
174
  },
31
175
  {
32
176
  provide: _syncqueriesservice.SyncQueries,
33
- useValue: {}
177
+ useValue: syncQueries
34
178
  },
35
179
  {
36
180
  provide: _usersqueriesservice.UsersQueries,
37
- useValue: {}
181
+ useValue: usersQueries
38
182
  },
39
183
  {
40
184
  provide: _authmanagerservice.AuthManager,
41
- useValue: {}
185
+ useValue: authManager
42
186
  },
43
187
  {
44
188
  provide: _authmethod.AuthMethod,
45
- useValue: {}
189
+ useValue: authMethod
46
190
  }
47
191
  ]
48
192
  }).compile();
193
+ module.useLogger([
194
+ 'fatal'
195
+ ]);
49
196
  service = module.get(_syncclientsmanagerservice.SyncClientsManager);
197
+ service.cache = cacheMock;
198
+ });
199
+ beforeEach(()=>{
200
+ jest.restoreAllMocks();
201
+ jest.clearAllMocks();
202
+ mockHavePermission = true;
203
+ _shared.currentTimeStamp.mockReturnValue(1_000);
204
+ _functions.convertHumanTimeToSeconds.mockImplementation((v)=>{
205
+ if (v === '90d') return 90 * 24 * 3600;
206
+ if (v === '180d') return 180 * 24 * 3600;
207
+ if (typeof v === 'number') return v;
208
+ return 0;
209
+ });
210
+ _files.isPathExists.mockReset();
211
+ _promises.default.readFile.mockReset();
212
+ syncQueries.updateClientInfo.mockResolvedValue(undefined);
213
+ usersQueries.updateUserOrGuest.mockResolvedValue(undefined);
214
+ service.cache = cacheMock;
215
+ cacheMock.get.mockResolvedValue(undefined);
216
+ cacheMock.get.mockClear();
217
+ cacheMock.set.mockClear();
218
+ cacheMock.del.mockClear();
219
+ cacheMock.genSlugKey.mockClear();
220
+ setRepo(_store.APP_STORE_REPOSITORY.PUBLIC);
221
+ });
222
+ it('should be defined', ()=>expect(service).toBeDefined());
223
+ describe('register', ()=>{
224
+ const baseDto = {
225
+ login: 'john',
226
+ password: 'secret',
227
+ clientId: 'client-1',
228
+ info: {
229
+ type: 'desktop',
230
+ version: '1.0.0'
231
+ }
232
+ };
233
+ test.each([
234
+ [
235
+ 'Unauthorized when credentials are invalid',
236
+ null,
237
+ _common.HttpStatus.UNAUTHORIZED
238
+ ],
239
+ [
240
+ 'Forbidden when user lacks DESKTOP_APP permission',
241
+ {
242
+ id: 10,
243
+ login: 'john',
244
+ havePermission: ()=>false
245
+ },
246
+ _common.HttpStatus.FORBIDDEN
247
+ ]
248
+ ])('should throw %s', async (_label, user, status)=>{
249
+ authMethod.validateUser.mockResolvedValue(user);
250
+ await expect(service.register(baseDto, '1.2.3.4')).rejects.toMatchObject({
251
+ status
252
+ });
253
+ });
254
+ it('should return client token when registration succeeds', async ()=>{
255
+ authMethod.validateUser.mockResolvedValue({
256
+ id: 10,
257
+ login: 'john',
258
+ havePermission: ()=>true
259
+ });
260
+ syncQueries.getOrCreateClient.mockResolvedValue('token-abc');
261
+ const r = await service.register(baseDto, '1.2.3.4');
262
+ expect(r).toEqual({
263
+ clientToken: 'token-abc'
264
+ });
265
+ expect(syncQueries.getOrCreateClient).toHaveBeenCalledWith(10, 'client-1', baseDto.info, '1.2.3.4');
266
+ });
267
+ it('should throw Internal Server Error when persistence fails', async ()=>{
268
+ authMethod.validateUser.mockResolvedValue({
269
+ id: 10,
270
+ login: 'john',
271
+ havePermission: ()=>true
272
+ });
273
+ syncQueries.getOrCreateClient.mockRejectedValue(new Error('db error'));
274
+ await expect(service.register(baseDto, '1.2.3.4')).rejects.toMatchObject({
275
+ status: _common.HttpStatus.INTERNAL_SERVER_ERROR
276
+ });
277
+ });
278
+ });
279
+ describe('unregister', ()=>{
280
+ it('should delete client without error', async ()=>{
281
+ syncQueries.deleteClient.mockResolvedValue(undefined);
282
+ await expect(service.unregister({
283
+ id: 1,
284
+ clientId: 'c1'
285
+ })).resolves.toBeUndefined();
286
+ expect(syncQueries.deleteClient).toHaveBeenCalledWith(1, 'c1');
287
+ });
288
+ it('should throw Internal Server Error when deletion fails', async ()=>{
289
+ syncQueries.deleteClient.mockRejectedValue(new Error('db error'));
290
+ await expect(service.unregister({
291
+ id: 1,
292
+ clientId: 'c1'
293
+ })).rejects.toMatchObject({
294
+ status: _common.HttpStatus.INTERNAL_SERVER_ERROR
295
+ });
296
+ });
297
+ });
298
+ describe('authenticate', ()=>{
299
+ const ip = '9.9.9.9';
300
+ const dto = {
301
+ clientId: 'cid',
302
+ token: 'ctok'
303
+ };
304
+ it('should forbid when client is unknown', async ()=>{
305
+ syncQueries.getClient.mockResolvedValue(undefined);
306
+ await expect(service.authenticate(_auth.CLIENT_AUTH_TYPE.TOKEN, dto, ip, {})).rejects.toMatchObject({
307
+ status: _common.HttpStatus.FORBIDDEN,
308
+ response: 'Client is unknown'
309
+ });
310
+ });
311
+ it('should forbid when client is disabled', async ()=>{
312
+ syncQueries.getClient.mockResolvedValue(makeClient({
313
+ enabled: false,
314
+ tokenExpiration: 5000
315
+ }));
316
+ await expect(service.authenticate(_auth.CLIENT_AUTH_TYPE.TOKEN, dto, ip, {})).rejects.toMatchObject({
317
+ status: _common.HttpStatus.FORBIDDEN,
318
+ response: 'Client is disabled'
319
+ });
320
+ });
321
+ it('should forbid when client token is expired', async ()=>{
322
+ ;
323
+ _shared.currentTimeStamp.mockReturnValue(1000);
324
+ syncQueries.getClient.mockResolvedValue(makeClient({
325
+ tokenExpiration: 1000
326
+ }));
327
+ await expect(service.authenticate(_auth.CLIENT_AUTH_TYPE.TOKEN, dto, ip, {})).rejects.toMatchObject({
328
+ status: _common.HttpStatus.FORBIDDEN,
329
+ response: _auth.CLIENT_TOKEN_EXPIRED_ERROR
330
+ });
331
+ });
332
+ it('should forbid when owner user does not exist', async ()=>{
333
+ syncQueries.getClient.mockResolvedValue(makeClient());
334
+ syncQueries.updateClientInfo.mockRejectedValueOnce(new Error('update-fails')); // silence expected
335
+ usersQueries.from.mockResolvedValue(null);
336
+ await expect(service.authenticate(_auth.CLIENT_AUTH_TYPE.TOKEN, dto, ip, {})).rejects.toMatchObject({
337
+ status: _common.HttpStatus.FORBIDDEN,
338
+ response: 'User does not exist'
339
+ });
340
+ });
341
+ it('should forbid when owner account is inactive', async ()=>{
342
+ syncQueries.getClient.mockResolvedValue(makeClient());
343
+ usersQueries.from.mockResolvedValue(makeUser({
344
+ isActive: false
345
+ }));
346
+ await expect(service.authenticate(_auth.CLIENT_AUTH_TYPE.TOKEN, dto, ip, {})).rejects.toMatchObject({
347
+ status: _common.HttpStatus.FORBIDDEN,
348
+ response: 'Account suspended or not authorized'
349
+ });
350
+ });
351
+ it('should forbid when owner lacks DESKTOP_APP permission', async ()=>{
352
+ mockHavePermission = false;
353
+ syncQueries.getClient.mockResolvedValue(makeClient());
354
+ usersQueries.from.mockResolvedValue(makeUser({
355
+ permissions: '',
356
+ role: 999
357
+ }));
358
+ await expect(service.authenticate(_auth.CLIENT_AUTH_TYPE.TOKEN, dto, ip, {})).rejects.toMatchObject({
359
+ status: _common.HttpStatus.FORBIDDEN,
360
+ response: 'Missing permission'
361
+ });
362
+ });
363
+ it('should perform COOKIE authentication and renew client token when needed', async ()=>{
364
+ syncQueries.getClient.mockResolvedValue(makeClient({
365
+ ownerId: 7
366
+ }));
367
+ usersQueries.from.mockResolvedValue(makeUser({
368
+ id: 7,
369
+ login: 'john',
370
+ email: 'john@doe',
371
+ firstName: 'John',
372
+ lastName: 'Doe'
373
+ }));
374
+ usersQueries.updateUserOrGuest.mockRejectedValueOnce(new Error('update-access-fail')); // silence expected
375
+ authManager.setCookies.mockResolvedValue({
376
+ access_token: 'a',
377
+ refresh_token: 'b'
378
+ });
379
+ jest.spyOn(service, 'renewTokenAndExpiration').mockResolvedValue('new-client-token');
380
+ const reply = {};
381
+ const r = await service.authenticate(_auth.CLIENT_AUTH_TYPE.COOKIE, dto, ip, reply);
382
+ expect(authManager.setCookies).toHaveBeenCalledTimes(1);
383
+ expect(service.renewTokenAndExpiration).toHaveBeenCalledTimes(1);
384
+ expect(r.client_token_update).toBe('new-client-token');
385
+ });
386
+ it('should perform TOKEN authentication and not renew when not needed', async ()=>{
387
+ syncQueries.getClient.mockResolvedValue(makeClient({
388
+ ownerId: 8
389
+ }));
390
+ usersQueries.from.mockResolvedValue(makeUser({
391
+ id: 8,
392
+ login: 'alice',
393
+ email: 'alice@doe',
394
+ firstName: 'Alice'
395
+ }));
396
+ authManager.getTokens.mockResolvedValue({
397
+ access_token: 'x',
398
+ refresh_token: 'y'
399
+ });
400
+ jest.spyOn(service, 'renewTokenAndExpiration').mockResolvedValue(undefined);
401
+ const r = await service.authenticate(_auth.CLIENT_AUTH_TYPE.TOKEN, dto, ip, {});
402
+ expect(authManager.getTokens).toHaveBeenCalledTimes(1);
403
+ expect(r.client_token_update).toBeUndefined();
404
+ });
405
+ it('should throw when auth type is unknown (else branch)', async ()=>{
406
+ syncQueries.getClient.mockResolvedValue(makeClient({
407
+ ownerId: 9
408
+ }));
409
+ usersQueries.from.mockResolvedValue(makeUser({
410
+ id: 9,
411
+ login: 'bob',
412
+ email: 'bob@doe',
413
+ firstName: 'Bob'
414
+ }));
415
+ jest.spyOn(service, 'renewTokenAndExpiration').mockResolvedValue(undefined);
416
+ await expect(service.authenticate('unknown', {
417
+ clientId: 'cid',
418
+ token: 'ctok'
419
+ }, ip, {})).rejects.toBeInstanceOf(TypeError);
420
+ });
50
421
  });
51
- it('should be defined', ()=>{
52
- expect(service).toBeDefined();
422
+ describe('getClients', ()=>{
423
+ it('should proxy to SyncQueries.getClients', async ()=>{
424
+ const fake = [
425
+ {
426
+ id: 'c1',
427
+ paths: []
428
+ }
429
+ ];
430
+ syncQueries.getClients.mockResolvedValue(fake);
431
+ const r = await service.getClients({
432
+ id: 1,
433
+ clientId: 'c1'
434
+ });
435
+ expect(r).toBe(fake);
436
+ expect(syncQueries.getClients).toHaveBeenCalledWith({
437
+ id: 1,
438
+ clientId: 'c1'
439
+ });
440
+ });
441
+ });
442
+ describe('renewTokenAndExpiration', ()=>{
443
+ const owner = {
444
+ id: 1,
445
+ login: 'bob'
446
+ };
447
+ it('should return undefined when token expiration is far enough', async ()=>{
448
+ ;
449
+ _shared.currentTimeStamp.mockReturnValue(1_000);
450
+ _functions.convertHumanTimeToSeconds.mockImplementation((v)=>v === '90d' ? 90 * 24 * 3600 : 0);
451
+ const client = {
452
+ id: 'cid',
453
+ tokenExpiration: 1_000 + 90 * 24 * 3600 + 1
454
+ };
455
+ expect(await service.renewTokenAndExpiration(client, owner)).toBeUndefined();
456
+ });
457
+ it('should renew token and return new value when close to expiration', async ()=>{
458
+ ;
459
+ _shared.currentTimeStamp.mockReturnValue(1_000);
460
+ _functions.convertHumanTimeToSeconds.mockImplementation((v)=>v === '90d' ? 90 * 24 * 3600 : v === '180d' ? 180 * 24 * 3600 : 0);
461
+ const client = {
462
+ id: 'cid',
463
+ tokenExpiration: 1_000 + 90 * 24 * 3600 - 1
464
+ };
465
+ syncQueries.renewClientTokenAndExpiration.mockResolvedValue(undefined);
466
+ const r = await service.renewTokenAndExpiration(client, owner);
467
+ expect(typeof r).toBe('string');
468
+ expect(r).toBeTruthy();
469
+ expect(syncQueries.renewClientTokenAndExpiration).toHaveBeenCalledWith('cid', r, expect.any(Number));
470
+ });
471
+ it('should throw Bad Request when renewal persistence fails', async ()=>{
472
+ ;
473
+ _shared.currentTimeStamp.mockReturnValue(1_000);
474
+ const client = {
475
+ id: 'cid',
476
+ tokenExpiration: 1_000
477
+ };
478
+ jest.spyOn(_nodecrypto.default, 'randomUUID').mockReturnValue('uuid-err');
479
+ syncQueries.renewClientTokenAndExpiration.mockRejectedValue(new Error('db fail'));
480
+ await expect(service.renewTokenAndExpiration(client, owner)).rejects.toMatchObject({
481
+ status: _common.HttpStatus.BAD_REQUEST
482
+ });
483
+ });
484
+ });
485
+ describe('deleteClient', ()=>{
486
+ it('should delete client successfully', async ()=>{
487
+ syncQueries.deleteClient.mockResolvedValue(undefined);
488
+ await expect(service.deleteClient({
489
+ id: 5
490
+ }, 'cid')).resolves.toBeUndefined();
491
+ expect(syncQueries.deleteClient).toHaveBeenCalledWith(5, 'cid');
492
+ });
493
+ it('should throw Internal Server Error when deletion fails', async ()=>{
494
+ syncQueries.deleteClient.mockRejectedValue(new Error('db error'));
495
+ await expect(service.deleteClient({
496
+ id: 5
497
+ }, 'cid')).rejects.toMatchObject({
498
+ status: _common.HttpStatus.INTERNAL_SERVER_ERROR
499
+ });
500
+ });
501
+ });
502
+ describe('checkAppStore', ()=>{
503
+ it('should return PUBLIC manifest when HTTP fetch succeeds', async ()=>{
504
+ setRepo(_store.APP_STORE_REPOSITORY.PUBLIC);
505
+ http.axiosRef.mockResolvedValue({
506
+ data: {
507
+ platform: {
508
+ win: []
509
+ }
510
+ }
511
+ });
512
+ const manifest = await service.checkAppStore();
513
+ expect(manifest).toBeTruthy();
514
+ expect(manifest.repository).toBe(_store.APP_STORE_REPOSITORY.PUBLIC);
515
+ expect(http.axiosRef).toHaveBeenCalled();
516
+ });
517
+ it('should return null when PUBLIC manifest fetch fails', async ()=>{
518
+ setRepo(_store.APP_STORE_REPOSITORY.PUBLIC);
519
+ http.axiosRef.mockRejectedValue(new Error('network'));
520
+ expect(await service.checkAppStore()).toBeNull();
521
+ });
522
+ it('should return null when LOCAL manifest file does not exist', async ()=>{
523
+ setRepo(_store.APP_STORE_REPOSITORY.LOCAL);
524
+ _files.isPathExists.mockResolvedValue(false);
525
+ expect(await service.checkAppStore()).toBeNull();
526
+ });
527
+ it('should return LOCAL manifest with rewritten URLs when file is valid', async ()=>{
528
+ setRepo(_store.APP_STORE_REPOSITORY.LOCAL);
529
+ _files.isPathExists.mockResolvedValue(true);
530
+ const raw = {
531
+ platform: {
532
+ win: [
533
+ {
534
+ package: 'desktop-win.exe'
535
+ },
536
+ {
537
+ package: 'cli-win.zip'
538
+ }
539
+ ],
540
+ linux: [
541
+ {
542
+ package: 'desktop-linux.AppImage'
543
+ }
544
+ ]
545
+ }
546
+ };
547
+ _promises.default.readFile.mockResolvedValue(JSON.stringify(raw));
548
+ const manifest = await service.checkAppStore();
549
+ expect(manifest.repository).toBe(_store.APP_STORE_REPOSITORY.LOCAL);
550
+ expect(manifest.platform.win[0].url.startsWith(_store.APP_STORE_DIRNAME)).toBe(true);
551
+ expect(manifest.platform.win[0].url.endsWith('desktop-win.exe')).toBe(true);
552
+ expect(manifest.platform.win[1].url.startsWith(_store.APP_STORE_DIRNAME)).toBe(true);
553
+ expect(manifest.platform.win[1].url.endsWith('cli-win.zip')).toBe(true);
554
+ expect(manifest.platform.linux[0].url.startsWith(_store.APP_STORE_DIRNAME)).toBe(true);
555
+ expect(manifest.platform.linux[0].url.endsWith('desktop-linux.AppImage')).toBe(true);
556
+ });
557
+ it('should return null when LOCAL manifest cannot be parsed', async ()=>{
558
+ setRepo(_store.APP_STORE_REPOSITORY.LOCAL);
559
+ _files.isPathExists.mockResolvedValue(true);
560
+ _promises.default.readFile.mockRejectedValue(new Error('fs error'));
561
+ expect(await service.checkAppStore()).toBeNull();
562
+ });
563
+ it('should rewrite desktop packages under desktop/os when package starts with "desktop"', async ()=>{
564
+ setRepo(_store.APP_STORE_REPOSITORY.LOCAL);
565
+ _files.isPathExists.mockResolvedValue(true);
566
+ const raw = {
567
+ platform: {
568
+ win: [
569
+ {
570
+ package: `${_sync.SYNC_CLIENT_TYPE.DESKTOP}-win.exe`
571
+ }
572
+ ],
573
+ mac: [
574
+ {
575
+ package: `${_sync.SYNC_CLIENT_TYPE.DESKTOP}-mac.dmg`
576
+ }
577
+ ],
578
+ linux: [
579
+ {
580
+ package: `${_sync.SYNC_CLIENT_TYPE.DESKTOP}-linux.AppImage`
581
+ }
582
+ ]
583
+ }
584
+ };
585
+ _promises.default.readFile.mockResolvedValue(JSON.stringify(raw));
586
+ const manifest = await service.checkAppStore();
587
+ expect(manifest).toBeTruthy();
588
+ expect(manifest.repository).toBe(_store.APP_STORE_REPOSITORY.LOCAL);
589
+ expect(manifest.platform.win[0].url).toBe(`${_store.APP_STORE_DIRNAME}/${_sync.SYNC_CLIENT_TYPE.DESKTOP}/win/${_sync.SYNC_CLIENT_TYPE.DESKTOP}-win.exe`);
590
+ expect(manifest.platform.mac[0].url).toBe(`${_store.APP_STORE_DIRNAME}/${_sync.SYNC_CLIENT_TYPE.DESKTOP}/mac/${_sync.SYNC_CLIENT_TYPE.DESKTOP}-mac.dmg`);
591
+ expect(manifest.platform.linux[0].url).toBe(`${_store.APP_STORE_DIRNAME}/${_sync.SYNC_CLIENT_TYPE.DESKTOP}/linux/${_sync.SYNC_CLIENT_TYPE.DESKTOP}-linux.AppImage`);
592
+ });
53
593
  });
54
594
  });
55
595