@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
@@ -13,7 +13,13 @@ const _filesmethodsservice = require("./services/files-methods.service");
13
13
  const _filestasksmanagerservice = require("./services/files-tasks-manager.service");
14
14
  describe(_filestaskscontroller.FilesTasksController.name, ()=>{
15
15
  let controller;
16
+ let filesTasksManager;
16
17
  beforeAll(async ()=>{
18
+ filesTasksManager = {
19
+ getTasks: jest.fn(),
20
+ deleteTasks: jest.fn(),
21
+ downloadArchive: jest.fn()
22
+ };
17
23
  const module = await _testing.Test.createTestingModule({
18
24
  controllers: [
19
25
  _filestaskscontroller.FilesTasksController
@@ -27,14 +33,98 @@ describe(_filestaskscontroller.FilesTasksController.name, ()=>{
27
33
  provide: _filesmethodsservice.FilesMethods,
28
34
  useValue: {}
29
35
  },
30
- _filestasksmanagerservice.FilesTasksManager
36
+ {
37
+ provide: _filestasksmanagerservice.FilesTasksManager,
38
+ useValue: filesTasksManager
39
+ }
31
40
  ]
32
41
  }).compile();
33
42
  controller = module.get(_filestaskscontroller.FilesTasksController);
34
43
  });
44
+ beforeEach(()=>{
45
+ jest.clearAllMocks();
46
+ });
35
47
  it('should be defined', ()=>{
36
48
  expect(controller).toBeDefined();
37
49
  });
50
+ it('getTasks should call FilesTasksManager.getTasks with user.id and taskId and return the value', ()=>{
51
+ const user = {
52
+ id: 'user-1'
53
+ };
54
+ const taskId = 'task-123';
55
+ const expected = [
56
+ {
57
+ id: 'task-123'
58
+ }
59
+ ];
60
+ filesTasksManager.getTasks.mockReturnValueOnce(expected);
61
+ const result = controller.getTasks(user, taskId);
62
+ expect(filesTasksManager.getTasks).toHaveBeenCalledTimes(1);
63
+ expect(filesTasksManager.getTasks).toHaveBeenCalledWith('user-1', 'task-123');
64
+ expect(result).toBe(expected);
65
+ });
66
+ it('getTasks without taskId should pass undefined', ()=>{
67
+ const user = {
68
+ id: 'user-2'
69
+ };
70
+ const expected = [
71
+ {
72
+ id: 'task-a'
73
+ },
74
+ {
75
+ id: 'task-b'
76
+ }
77
+ ];
78
+ filesTasksManager.getTasks.mockReturnValueOnce(expected);
79
+ const result = controller.getTasks(user);
80
+ expect(filesTasksManager.getTasks).toHaveBeenCalledTimes(1);
81
+ expect(filesTasksManager.getTasks).toHaveBeenCalledWith('user-2', undefined);
82
+ expect(result).toBe(expected);
83
+ });
84
+ it('deleteTasks should call FilesTasksManager.deleteTasks with user and taskId and return the value', ()=>{
85
+ const user = {
86
+ id: 'user-3',
87
+ name: 'Alice'
88
+ };
89
+ const taskId = 'task-del-1';
90
+ const expected = {
91
+ deleted: true
92
+ };
93
+ filesTasksManager.deleteTasks.mockReturnValueOnce(expected);
94
+ const result = controller.deleteTasks(user, taskId);
95
+ expect(filesTasksManager.deleteTasks).toHaveBeenCalledTimes(1);
96
+ expect(filesTasksManager.deleteTasks).toHaveBeenCalledWith(user, 'task-del-1');
97
+ expect(result).toBe(expected);
98
+ });
99
+ it('deleteTasks without taskId must pass undefined', ()=>{
100
+ const user = {
101
+ id: 'user-4'
102
+ };
103
+ const expected = {
104
+ deletedAll: true
105
+ };
106
+ filesTasksManager.deleteTasks.mockReturnValueOnce(expected);
107
+ const result = controller.deleteTasks(user);
108
+ expect(filesTasksManager.deleteTasks).toHaveBeenCalledTimes(1);
109
+ expect(filesTasksManager.deleteTasks).toHaveBeenCalledWith(user, undefined);
110
+ expect(result).toBe(expected);
111
+ });
112
+ it('downloadTaskFile should delegate to FilesTasksManager.downloadArchive and return the StreamableFile', async ()=>{
113
+ const user = {
114
+ id: 'user-5'
115
+ };
116
+ const taskId = 'task-dl-42';
117
+ const req = {};
118
+ const res = {};
119
+ const streamable = {
120
+ some: 'stream'
121
+ };
122
+ filesTasksManager.downloadArchive.mockResolvedValueOnce(streamable);
123
+ const result = await controller.downloadTaskFile(user, taskId, req, res);
124
+ expect(filesTasksManager.downloadArchive).toHaveBeenCalledTimes(1);
125
+ expect(filesTasksManager.downloadArchive).toHaveBeenCalledWith(user, 'task-dl-42', req, res);
126
+ expect(result).toBe(streamable);
127
+ });
38
128
  });
39
129
 
40
130
  //# sourceMappingURL=files-tasks.controller.spec.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../backend/src/applications/files/files-tasks.controller.spec.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { Test, TestingModule } from '@nestjs/testing'\nimport { Cache } from '../../infrastructure/cache/services/cache.service'\nimport { FilesTasksController } from './files-tasks.controller'\nimport { FilesMethods } from './services/files-methods.service'\nimport { FilesTasksManager } from './services/files-tasks-manager.service'\n\ndescribe(FilesTasksController.name, () => {\n let controller: FilesTasksController\n\n beforeAll(async () => {\n const module: TestingModule = await Test.createTestingModule({\n controllers: [FilesTasksController],\n providers: [\n {\n provide: Cache,\n useValue: {}\n },\n { provide: FilesMethods, useValue: {} },\n FilesTasksManager\n ]\n }).compile()\n\n controller = module.get<FilesTasksController>(FilesTasksController)\n })\n\n it('should be defined', () => {\n expect(controller).toBeDefined()\n })\n})\n"],"names":["describe","FilesTasksController","name","controller","beforeAll","module","Test","createTestingModule","controllers","providers","provide","Cache","useValue","FilesMethods","FilesTasksManager","compile","get","it","expect","toBeDefined"],"mappings":"AAAA;;;;CAIC;;;;yBAEmC;8BACd;sCACe;qCACR;0CACK;AAElCA,SAASC,0CAAoB,CAACC,IAAI,EAAE;IAClC,IAAIC;IAEJC,UAAU;QACR,MAAMC,SAAwB,MAAMC,aAAI,CAACC,mBAAmB,CAAC;YAC3DC,aAAa;gBAACP,0CAAoB;aAAC;YACnCQ,WAAW;gBACT;oBACEC,SAASC,mBAAK;oBACdC,UAAU,CAAC;gBACb;gBACA;oBAAEF,SAASG,iCAAY;oBAAED,UAAU,CAAC;gBAAE;gBACtCE,2CAAiB;aAClB;QACH,GAAGC,OAAO;QAEVZ,aAAaE,OAAOW,GAAG,CAAuBf,0CAAoB;IACpE;IAEAgB,GAAG,qBAAqB;QACtBC,OAAOf,YAAYgB,WAAW;IAChC;AACF"}
1
+ {"version":3,"sources":["../../../../backend/src/applications/files/files-tasks.controller.spec.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { Test, TestingModule } from '@nestjs/testing'\nimport { Cache } from '../../infrastructure/cache/services/cache.service'\nimport { FilesTasksController } from './files-tasks.controller'\nimport { FilesMethods } from './services/files-methods.service'\nimport { FilesTasksManager } from './services/files-tasks-manager.service'\n\ndescribe(FilesTasksController.name, () => {\n let controller: FilesTasksController\n let filesTasksManager: { getTasks: jest.Mock; deleteTasks: jest.Mock; downloadArchive: jest.Mock }\n\n beforeAll(async () => {\n filesTasksManager = {\n getTasks: jest.fn(),\n deleteTasks: jest.fn(),\n downloadArchive: jest.fn()\n }\n\n const module: TestingModule = await Test.createTestingModule({\n controllers: [FilesTasksController],\n providers: [\n {\n provide: Cache,\n useValue: {}\n },\n { provide: FilesMethods, useValue: {} },\n { provide: FilesTasksManager, useValue: filesTasksManager }\n ]\n }).compile()\n\n controller = module.get<FilesTasksController>(FilesTasksController)\n })\n\n beforeEach(() => {\n jest.clearAllMocks()\n })\n\n it('should be defined', () => {\n expect(controller).toBeDefined()\n })\n\n it('getTasks should call FilesTasksManager.getTasks with user.id and taskId and return the value', () => {\n const user = { id: 'user-1' } as any\n const taskId = 'task-123'\n const expected = [{ id: 'task-123' }]\n\n filesTasksManager.getTasks.mockReturnValueOnce(expected)\n\n const result = controller.getTasks(user, taskId)\n\n expect(filesTasksManager.getTasks).toHaveBeenCalledTimes(1)\n expect(filesTasksManager.getTasks).toHaveBeenCalledWith('user-1', 'task-123')\n expect(result).toBe(expected)\n })\n\n it('getTasks without taskId should pass undefined', () => {\n const user = { id: 'user-2' } as any\n const expected = [{ id: 'task-a' }, { id: 'task-b' }]\n\n filesTasksManager.getTasks.mockReturnValueOnce(expected)\n\n const result = controller.getTasks(user)\n\n expect(filesTasksManager.getTasks).toHaveBeenCalledTimes(1)\n expect(filesTasksManager.getTasks).toHaveBeenCalledWith('user-2', undefined)\n expect(result).toBe(expected)\n })\n\n it('deleteTasks should call FilesTasksManager.deleteTasks with user and taskId and return the value', () => {\n const user = { id: 'user-3', name: 'Alice' } as any\n const taskId = 'task-del-1'\n const expected = { deleted: true }\n\n filesTasksManager.deleteTasks.mockReturnValueOnce(expected)\n\n const result = controller.deleteTasks(user, taskId)\n\n expect(filesTasksManager.deleteTasks).toHaveBeenCalledTimes(1)\n expect(filesTasksManager.deleteTasks).toHaveBeenCalledWith(user, 'task-del-1')\n expect(result).toBe(expected)\n })\n\n it('deleteTasks without taskId must pass undefined', () => {\n const user = { id: 'user-4' } as any\n const expected = { deletedAll: true }\n\n filesTasksManager.deleteTasks.mockReturnValueOnce(expected)\n\n const result = controller.deleteTasks(user)\n\n expect(filesTasksManager.deleteTasks).toHaveBeenCalledTimes(1)\n expect(filesTasksManager.deleteTasks).toHaveBeenCalledWith(user, undefined)\n expect(result).toBe(expected)\n })\n\n it('downloadTaskFile should delegate to FilesTasksManager.downloadArchive and return the StreamableFile', async () => {\n const user = { id: 'user-5' } as any\n const taskId = 'task-dl-42'\n const req = {} as any\n const res = {} as any\n const streamable = { some: 'stream' } as any\n\n filesTasksManager.downloadArchive.mockResolvedValueOnce(streamable)\n\n const result = await controller.downloadTaskFile(user, taskId, req, res)\n\n expect(filesTasksManager.downloadArchive).toHaveBeenCalledTimes(1)\n expect(filesTasksManager.downloadArchive).toHaveBeenCalledWith(user, 'task-dl-42', req, res)\n expect(result).toBe(streamable)\n })\n})\n"],"names":["describe","FilesTasksController","name","controller","filesTasksManager","beforeAll","getTasks","jest","fn","deleteTasks","downloadArchive","module","Test","createTestingModule","controllers","providers","provide","Cache","useValue","FilesMethods","FilesTasksManager","compile","get","beforeEach","clearAllMocks","it","expect","toBeDefined","user","id","taskId","expected","mockReturnValueOnce","result","toHaveBeenCalledTimes","toHaveBeenCalledWith","toBe","undefined","deleted","deletedAll","req","res","streamable","some","mockResolvedValueOnce","downloadTaskFile"],"mappings":"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"}
@@ -6,79 +6,301 @@
6
6
  Object.defineProperty(exports, "__esModule", {
7
7
  value: true
8
8
  });
9
- const _axios = require("@nestjs/axios");
10
9
  const _testing = require("@nestjs/testing");
11
- const _cacheservice = require("../../infrastructure/cache/services/cache.service");
12
- const _contextmanagerservice = require("../../infrastructure/context/services/context-manager.service");
13
- const _constants = require("../../infrastructure/database/constants");
14
- const _linksqueriesservice = require("../links/services/links-queries.service");
15
- const _notificationsmanagerservice = require("../notifications/services/notifications-manager.service");
16
- const _sharesmanagerservice = require("../shares/services/shares-manager.service");
17
- const _sharesqueriesservice = require("../shares/services/shares-queries.service");
18
10
  const _spaceguard = require("../spaces/guards/space.guard");
19
- const _spacesmanagerservice = require("../spaces/services/spaces-manager.service");
20
- const _spacesqueriesservice = require("../spaces/services/spaces-queries.service");
21
- const _usersqueriesservice = require("../users/services/users-queries.service");
11
+ const _operations = require("./constants/operations");
22
12
  const _filescontroller = require("./files.controller");
23
- const _filesindexer = require("./models/files-indexer");
24
- const _fileslockmanagerservice = require("./services/files-lock-manager.service");
25
- const _filesmanagerservice = require("./services/files-manager.service");
26
13
  const _filesmethodsservice = require("./services/files-methods.service");
27
- const _filesparserservice = require("./services/files-parser.service");
28
- const _filesqueriesservice = require("./services/files-queries.service");
29
14
  const _filesrecentsservice = require("./services/files-recents.service");
30
15
  const _filessearchmanagerservice = require("./services/files-search-manager.service");
31
16
  const _filestasksmanagerservice = require("./services/files-tasks-manager.service");
32
17
  describe(_filescontroller.FilesController.name, ()=>{
33
18
  let filesController;
19
+ // Reusable fakes
20
+ const fakeUser = {
21
+ id: 1,
22
+ login: 'john',
23
+ role: 1
24
+ };
25
+ const fakeSpace = {
26
+ id: 42,
27
+ key: 'space-key',
28
+ url: '/space/a',
29
+ realPath: '/data/space/a',
30
+ realBasePath: '/data/space'
31
+ };
32
+ const fakeReq = {
33
+ user: fakeUser,
34
+ space: fakeSpace,
35
+ headers: {},
36
+ method: 'GET',
37
+ ip: '127.0.0.1'
38
+ };
39
+ const fakeRes = {
40
+ header: jest.fn().mockReturnThis(),
41
+ status: jest.fn().mockReturnThis(),
42
+ send: jest.fn()
43
+ };
44
+ // Mocks
45
+ const filesMethodsMock = {
46
+ headOrGet: jest.fn(),
47
+ make: jest.fn(),
48
+ upload: jest.fn(),
49
+ copy: jest.fn(),
50
+ move: jest.fn(),
51
+ delete: jest.fn(),
52
+ genThumbnail: jest.fn(),
53
+ downloadFromUrl: jest.fn(),
54
+ compress: jest.fn(),
55
+ decompress: jest.fn()
56
+ };
57
+ const filesTasksManagerMock = {
58
+ createTask: jest.fn()
59
+ };
60
+ const filesRecentsMock = {
61
+ getRecents: jest.fn()
62
+ };
63
+ const filesSearchMock = {
64
+ search: jest.fn()
65
+ };
34
66
  beforeAll(async ()=>{
35
- const module = await _testing.Test.createTestingModule({
36
- imports: [
37
- _axios.HttpModule
38
- ],
67
+ const testingModuleBuilder = _testing.Test.createTestingModule({
39
68
  controllers: [
40
69
  _filescontroller.FilesController
41
70
  ],
42
71
  providers: [
43
72
  {
44
- provide: _constants.DB_TOKEN_PROVIDER,
45
- useValue: {}
73
+ provide: _filesmethodsservice.FilesMethods,
74
+ useValue: filesMethodsMock
46
75
  },
47
76
  {
48
- provide: _cacheservice.Cache,
49
- useValue: {}
77
+ provide: _filestasksmanagerservice.FilesTasksManager,
78
+ useValue: filesTasksManagerMock
50
79
  },
51
80
  {
52
- provide: _notificationsmanagerservice.NotificationsManager,
53
- useValue: {}
81
+ provide: _filesrecentsservice.FilesRecents,
82
+ useValue: filesRecentsMock
54
83
  },
55
84
  {
56
- provide: _filesindexer.FilesIndexer,
57
- useValue: {}
58
- },
59
- _contextmanagerservice.ContextManager,
60
- _spaceguard.SpaceGuard,
61
- _spacesmanagerservice.SpacesManager,
62
- _filesmethodsservice.FilesMethods,
63
- _filesmanagerservice.FilesManager,
64
- _filestasksmanagerservice.FilesTasksManager,
65
- _fileslockmanagerservice.FilesLockManager,
66
- _filesqueriesservice.FilesQueries,
67
- _spacesqueriesservice.SpacesQueries,
68
- _sharesmanagerservice.SharesManager,
69
- _linksqueriesservice.LinksQueries,
70
- _sharesqueriesservice.SharesQueries,
71
- _usersqueriesservice.UsersQueries,
72
- _filesrecentsservice.FilesRecents,
73
- _filessearchmanagerservice.FilesSearchManager,
74
- _filesparserservice.FilesParser
85
+ provide: _filessearchmanagerservice.FilesSearchManager,
86
+ useValue: filesSearchMock
87
+ }
75
88
  ]
76
- }).compile();
89
+ });
90
+ // IMPORTANT: override the guard referenced by @UseGuards to avoid resolving its dependencies
91
+ testingModuleBuilder.overrideGuard(_spaceguard.SpaceGuard).useValue({
92
+ canActivate: jest.fn().mockReturnValue(true)
93
+ });
94
+ const module = await testingModuleBuilder.compile();
77
95
  filesController = module.get(_filescontroller.FilesController);
78
96
  });
79
97
  it('should be defined', ()=>{
80
98
  expect(filesController).toBeDefined();
81
99
  });
100
+ describe('Operations', ()=>{
101
+ beforeEach(()=>{
102
+ jest.clearAllMocks();
103
+ });
104
+ it('head() should delegate to filesMethods.headOrGet(req, res) and return its result', async ()=>{
105
+ const stream = {};
106
+ filesMethodsMock.headOrGet.mockResolvedValue(stream);
107
+ const result = await filesController.head(fakeReq, fakeRes);
108
+ expect(filesMethodsMock.headOrGet).toHaveBeenCalledWith(fakeReq, fakeRes);
109
+ expect(result).toBe(stream);
110
+ });
111
+ it('download() should delegate to filesMethods.headOrGet(req, res) and return its result', async ()=>{
112
+ const stream = {};
113
+ filesMethodsMock.headOrGet.mockResolvedValue(stream);
114
+ const result = await filesController.download(fakeReq, fakeRes);
115
+ expect(filesMethodsMock.headOrGet).toHaveBeenCalledWith(fakeReq, fakeRes);
116
+ expect(result).toBe(stream);
117
+ });
118
+ it('make() should call filesMethods.make(user, space, dto)', async ()=>{
119
+ const dto = {
120
+ path: '/a',
121
+ name: 'b',
122
+ type: 'directory'
123
+ };
124
+ await filesController.make(fakeUser, fakeSpace, dto);
125
+ expect(filesMethodsMock.make).toHaveBeenCalledWith(fakeUser, fakeSpace, dto);
126
+ });
127
+ it('upload() should call filesMethods.upload(req, res)', async ()=>{
128
+ await filesController.upload(fakeReq, fakeRes);
129
+ expect(filesMethodsMock.upload).toHaveBeenCalledWith(fakeReq, fakeRes);
130
+ });
131
+ it('copy() should call filesMethods.copy(user, space, dto) and return its result', async ()=>{
132
+ const dto = {
133
+ dstDirectory: '/dst',
134
+ dstName: 'b'
135
+ };
136
+ const expected = {
137
+ path: '/dst',
138
+ name: 'b'
139
+ };
140
+ filesMethodsMock.copy.mockResolvedValue(expected);
141
+ const result = await filesController.copy(fakeUser, fakeSpace, dto);
142
+ expect(filesMethodsMock.copy).toHaveBeenCalledWith(fakeUser, fakeSpace, dto);
143
+ expect(result).toEqual(expected);
144
+ });
145
+ it('move() should call filesMethods.move(user, space, dto) and return its result', async ()=>{
146
+ const dto = {
147
+ dstDirectory: '/dst',
148
+ dstName: 'c'
149
+ };
150
+ const expected = {
151
+ path: '/dst',
152
+ name: 'c'
153
+ };
154
+ filesMethodsMock.move.mockResolvedValue(expected);
155
+ const result = await filesController.move(fakeUser, fakeSpace, dto);
156
+ expect(filesMethodsMock.move).toHaveBeenCalledWith(fakeUser, fakeSpace, dto);
157
+ expect(result).toEqual(expected);
158
+ });
159
+ it('delete() should call filesMethods.delete(user, space)', async ()=>{
160
+ await filesController.delete(fakeUser, fakeSpace);
161
+ expect(filesMethodsMock.delete).toHaveBeenCalledWith(fakeUser, fakeSpace);
162
+ });
163
+ it('genThumbnail() should default size to 256 when not provided', async ()=>{
164
+ const stream = {};
165
+ filesMethodsMock.genThumbnail.mockResolvedValue(stream);
166
+ // pass undefined to exercise controller default parameter
167
+ const result = await filesController.genThumbnail(fakeSpace, undefined);
168
+ expect(filesMethodsMock.genThumbnail).toHaveBeenCalledWith(fakeSpace, 256);
169
+ expect(result).toBe(stream);
170
+ });
171
+ it('genThumbnail() should pass provided size', async ()=>{
172
+ const stream = {};
173
+ filesMethodsMock.genThumbnail.mockResolvedValue(stream);
174
+ const result = await filesController.genThumbnail(fakeSpace, 512);
175
+ expect(filesMethodsMock.genThumbnail).toHaveBeenCalledWith(fakeSpace, 512);
176
+ expect(result).toBe(stream);
177
+ });
178
+ });
179
+ describe('Tasks operations', ()=>{
180
+ beforeEach(()=>{
181
+ jest.clearAllMocks();
182
+ });
183
+ it('downloadFromUrlAsTask() should create DOWNLOAD task using method name "downloadFromUrl"', async ()=>{
184
+ const dto = {
185
+ url: 'http://x',
186
+ to: '/a'
187
+ };
188
+ const task = {
189
+ id: 1
190
+ };
191
+ filesTasksManagerMock.createTask.mockResolvedValue(task);
192
+ const result = await filesController.downloadFromUrlAsTask(fakeUser, fakeSpace, dto);
193
+ expect(filesTasksManagerMock.createTask).toHaveBeenCalledWith(_operations.FILE_OPERATION.DOWNLOAD, fakeUser, fakeSpace, dto, filesMethodsMock.downloadFromUrl.name);
194
+ expect(result).toBe(task);
195
+ });
196
+ it('compressAsTask() should call SpaceGuard.checkPermissions when compressInDirectory is true', async ()=>{
197
+ const dto = {
198
+ compressInDirectory: true
199
+ };
200
+ const spy = jest.spyOn(_spaceguard.SpaceGuard, 'checkPermissions').mockImplementation(()=>undefined);
201
+ filesTasksManagerMock.createTask.mockResolvedValue({});
202
+ await filesController.compressAsTask(fakeReq, dto);
203
+ expect(spy).toHaveBeenCalled();
204
+ spy.mockRestore();
205
+ });
206
+ it('compressAsTask() should create COMPRESS task with req.user and req.space and method name "compress"', async ()=>{
207
+ const dto = {
208
+ compressInDirectory: false
209
+ };
210
+ const task = {
211
+ id: 2
212
+ };
213
+ filesTasksManagerMock.createTask.mockResolvedValue(task);
214
+ const result = await filesController.compressAsTask(fakeReq, dto);
215
+ expect(filesTasksManagerMock.createTask).toHaveBeenCalledWith(_operations.FILE_OPERATION.COMPRESS, fakeReq.user, fakeReq.space, dto, filesMethodsMock.compress.name);
216
+ expect(result).toBe(task);
217
+ });
218
+ it('decompressAsTask() should create DECOMPRESS task with null dto and method name "decompress"', async ()=>{
219
+ const task = {
220
+ id: 3
221
+ };
222
+ filesTasksManagerMock.createTask.mockResolvedValue(task);
223
+ const result = await filesController.decompressAsTask(fakeUser, fakeSpace);
224
+ expect(filesTasksManagerMock.createTask).toHaveBeenCalledWith(_operations.FILE_OPERATION.DECOMPRESS, fakeUser, fakeSpace, null, filesMethodsMock.decompress.name);
225
+ expect(result).toBe(task);
226
+ });
227
+ it('copyAsTask() should create COPY task with method name "copy"', async ()=>{
228
+ const dto = {
229
+ from: '/a',
230
+ to: '/b'
231
+ };
232
+ const task = {
233
+ id: 4
234
+ };
235
+ filesTasksManagerMock.createTask.mockResolvedValue(task);
236
+ const result = await filesController.copyAsTask(fakeUser, fakeSpace, dto);
237
+ expect(filesTasksManagerMock.createTask).toHaveBeenCalledWith(_operations.FILE_OPERATION.COPY, fakeUser, fakeSpace, dto, filesMethodsMock.copy.name);
238
+ expect(result).toBe(task);
239
+ });
240
+ it('moveAsTask() should create MOVE task with method name "move"', async ()=>{
241
+ const dto = {
242
+ from: '/a',
243
+ to: '/c'
244
+ };
245
+ const task = {
246
+ id: 5
247
+ };
248
+ filesTasksManagerMock.createTask.mockResolvedValue(task);
249
+ const result = await filesController.moveAsTask(fakeUser, fakeSpace, dto);
250
+ expect(filesTasksManagerMock.createTask).toHaveBeenCalledWith(_operations.FILE_OPERATION.MOVE, fakeUser, fakeSpace, dto, filesMethodsMock.move.name);
251
+ expect(result).toBe(task);
252
+ });
253
+ it('deleteAsTask() should create DELETE task with null dto and method name "delete"', async ()=>{
254
+ const task = {
255
+ id: 6
256
+ };
257
+ filesTasksManagerMock.createTask.mockResolvedValue(task);
258
+ const result = await filesController.deleteAsTask(fakeUser, fakeSpace);
259
+ expect(filesTasksManagerMock.createTask).toHaveBeenCalledWith(_operations.FILE_OPERATION.DELETE, fakeUser, fakeSpace, null, filesMethodsMock.delete.name);
260
+ expect(result).toBe(task);
261
+ });
262
+ });
263
+ describe('Recents & Search', ()=>{
264
+ beforeEach(()=>{
265
+ jest.clearAllMocks();
266
+ });
267
+ it('getRecents() should use limit=10 by default', async ()=>{
268
+ const recents = [
269
+ {
270
+ path: '/a'
271
+ }
272
+ ];
273
+ filesRecentsMock.getRecents.mockResolvedValue(recents);
274
+ const result = await filesController.getRecents(fakeUser, undefined);
275
+ expect(filesRecentsMock.getRecents).toHaveBeenCalledWith(fakeUser, 10);
276
+ expect(result).toBe(recents);
277
+ });
278
+ it('getRecents() should forward provided limit', async ()=>{
279
+ const recents = [
280
+ {
281
+ path: '/b'
282
+ }
283
+ ];
284
+ filesRecentsMock.getRecents.mockResolvedValue(recents);
285
+ const result = await filesController.getRecents(fakeUser, 5);
286
+ expect(filesRecentsMock.getRecents).toHaveBeenCalledWith(fakeUser, 5);
287
+ expect(result).toBe(recents);
288
+ });
289
+ it('search() should delegate to filesSearch.search(user, dto)', async ()=>{
290
+ const dto = {
291
+ query: 'test'
292
+ };
293
+ const items = [
294
+ {
295
+ name: 'file'
296
+ }
297
+ ];
298
+ filesSearchMock.search.mockResolvedValue(items);
299
+ const result = await filesController.search(fakeUser, dto);
300
+ expect(filesSearchMock.search).toHaveBeenCalledWith(fakeUser, dto);
301
+ expect(result).toBe(items);
302
+ });
303
+ });
82
304
  });
83
305
 
84
306
  //# sourceMappingURL=files.controller.spec.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../backend/src/applications/files/files.controller.spec.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { HttpModule } from '@nestjs/axios'\nimport { Test, TestingModule } from '@nestjs/testing'\nimport { Cache } from '../../infrastructure/cache/services/cache.service'\nimport { ContextManager } from '../../infrastructure/context/services/context-manager.service'\nimport { DB_TOKEN_PROVIDER } from '../../infrastructure/database/constants'\nimport { LinksQueries } from '../links/services/links-queries.service'\nimport { NotificationsManager } from '../notifications/services/notifications-manager.service'\nimport { SharesManager } from '../shares/services/shares-manager.service'\nimport { SharesQueries } from '../shares/services/shares-queries.service'\nimport { SpaceGuard } from '../spaces/guards/space.guard'\nimport { SpacesManager } from '../spaces/services/spaces-manager.service'\nimport { SpacesQueries } from '../spaces/services/spaces-queries.service'\nimport { UsersQueries } from '../users/services/users-queries.service'\nimport { FilesController } from './files.controller'\nimport { FilesIndexer } from './models/files-indexer'\nimport { FilesLockManager } from './services/files-lock-manager.service'\nimport { FilesManager } from './services/files-manager.service'\nimport { FilesMethods } from './services/files-methods.service'\nimport { FilesParser } from './services/files-parser.service'\nimport { FilesQueries } from './services/files-queries.service'\nimport { FilesRecents } from './services/files-recents.service'\nimport { FilesSearchManager } from './services/files-search-manager.service'\nimport { FilesTasksManager } from './services/files-tasks-manager.service'\n\ndescribe(FilesController.name, () => {\n let filesController: FilesController\n\n beforeAll(async () => {\n const module: TestingModule = await Test.createTestingModule({\n imports: [HttpModule],\n controllers: [FilesController],\n providers: [\n { provide: DB_TOKEN_PROVIDER, useValue: {} },\n {\n provide: Cache,\n useValue: {}\n },\n {\n provide: NotificationsManager,\n useValue: {}\n },\n {\n provide: FilesIndexer,\n useValue: {}\n },\n ContextManager,\n SpaceGuard,\n SpacesManager,\n FilesMethods,\n FilesManager,\n FilesTasksManager,\n FilesLockManager,\n FilesQueries,\n SpacesQueries,\n SharesManager,\n LinksQueries,\n SharesQueries,\n UsersQueries,\n FilesRecents,\n FilesSearchManager,\n FilesParser\n ]\n }).compile()\n\n filesController = module.get<FilesController>(FilesController)\n })\n\n it('should be defined', () => {\n expect(filesController).toBeDefined()\n })\n})\n"],"names":["describe","FilesController","name","filesController","beforeAll","module","Test","createTestingModule","imports","HttpModule","controllers","providers","provide","DB_TOKEN_PROVIDER","useValue","Cache","NotificationsManager","FilesIndexer","ContextManager","SpaceGuard","SpacesManager","FilesMethods","FilesManager","FilesTasksManager","FilesLockManager","FilesQueries","SpacesQueries","SharesManager","LinksQueries","SharesQueries","UsersQueries","FilesRecents","FilesSearchManager","FilesParser","compile","get","it","expect","toBeDefined"],"mappings":"AAAA;;;;CAIC;;;;uBAE0B;yBACS;8BACd;uCACS;2BACG;qCACL;6CACQ;sCACP;sCACA;4BACH;sCACG;sCACA;qCACD;iCACG;8BACH;yCACI;qCACJ;qCACA;oCACD;qCACC;qCACA;2CACM;0CACD;AAElCA,SAASC,gCAAe,CAACC,IAAI,EAAE;IAC7B,IAAIC;IAEJC,UAAU;QACR,MAAMC,SAAwB,MAAMC,aAAI,CAACC,mBAAmB,CAAC;YAC3DC,SAAS;gBAACC,iBAAU;aAAC;YACrBC,aAAa;gBAACT,gCAAe;aAAC;YAC9BU,WAAW;gBACT;oBAAEC,SAASC,4BAAiB;oBAAEC,UAAU,CAAC;gBAAE;gBAC3C;oBACEF,SAASG,mBAAK;oBACdD,UAAU,CAAC;gBACb;gBACA;oBACEF,SAASI,iDAAoB;oBAC7BF,UAAU,CAAC;gBACb;gBACA;oBACEF,SAASK,0BAAY;oBACrBH,UAAU,CAAC;gBACb;gBACAI,qCAAc;gBACdC,sBAAU;gBACVC,mCAAa;gBACbC,iCAAY;gBACZC,iCAAY;gBACZC,2CAAiB;gBACjBC,yCAAgB;gBAChBC,iCAAY;gBACZC,mCAAa;gBACbC,mCAAa;gBACbC,iCAAY;gBACZC,mCAAa;gBACbC,iCAAY;gBACZC,iCAAY;gBACZC,6CAAkB;gBAClBC,+BAAW;aACZ;QACH,GAAGC,OAAO;QAEV/B,kBAAkBE,OAAO8B,GAAG,CAAkBlC,gCAAe;IAC/D;IAEAmC,GAAG,qBAAqB;QACtBC,OAAOlC,iBAAiBmC,WAAW;IACrC;AACF"}
1
+ {"version":3,"sources":["../../../../backend/src/applications/files/files.controller.spec.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { Test, TestingModule } from '@nestjs/testing'\nimport { SpaceGuard } from '../spaces/guards/space.guard'\nimport { FILE_OPERATION } from './constants/operations'\nimport { FilesController } from './files.controller'\nimport { FilesMethods } from './services/files-methods.service'\nimport { FilesRecents } from './services/files-recents.service'\nimport { FilesSearchManager } from './services/files-search-manager.service'\nimport { FilesTasksManager } from './services/files-tasks-manager.service'\n\ndescribe(FilesController.name, () => {\n let filesController: FilesController\n\n // Reusable fakes\n const fakeUser: any = { id: 1, login: 'john', role: 1 }\n const fakeSpace: any = { id: 42, key: 'space-key', url: '/space/a', realPath: '/data/space/a', realBasePath: '/data/space' }\n const fakeReq: any = { user: fakeUser, space: fakeSpace, headers: {}, method: 'GET', ip: '127.0.0.1' }\n const fakeRes: any = { header: jest.fn().mockReturnThis(), status: jest.fn().mockReturnThis(), send: jest.fn() }\n\n // Mocks\n const filesMethodsMock = {\n headOrGet: jest.fn(),\n make: jest.fn(),\n upload: jest.fn(),\n copy: jest.fn(),\n move: jest.fn(),\n delete: jest.fn(),\n genThumbnail: jest.fn(),\n downloadFromUrl: jest.fn(),\n compress: jest.fn(),\n decompress: jest.fn()\n }\n\n const filesTasksManagerMock = {\n createTask: jest.fn()\n }\n\n const filesRecentsMock = {\n getRecents: jest.fn()\n }\n\n const filesSearchMock = {\n search: jest.fn()\n }\n\n beforeAll(async () => {\n const testingModuleBuilder = Test.createTestingModule({\n controllers: [FilesController],\n providers: [\n { provide: FilesMethods, useValue: filesMethodsMock },\n { provide: FilesTasksManager, useValue: filesTasksManagerMock },\n { provide: FilesRecents, useValue: filesRecentsMock },\n { provide: FilesSearchManager, useValue: filesSearchMock }\n ]\n })\n // IMPORTANT: override the guard referenced by @UseGuards to avoid resolving its dependencies\n testingModuleBuilder.overrideGuard(SpaceGuard).useValue({ canActivate: jest.fn().mockReturnValue(true) })\n\n const module: TestingModule = await testingModuleBuilder.compile()\n\n filesController = module.get<FilesController>(FilesController)\n })\n\n it('should be defined', () => {\n expect(filesController).toBeDefined()\n })\n\n describe('Operations', () => {\n beforeEach(() => {\n jest.clearAllMocks()\n })\n\n it('head() should delegate to filesMethods.headOrGet(req, res) and return its result', async () => {\n const stream = {} as any\n filesMethodsMock.headOrGet.mockResolvedValue(stream)\n\n const result = await filesController.head(fakeReq, fakeRes)\n\n expect(filesMethodsMock.headOrGet).toHaveBeenCalledWith(fakeReq, fakeRes)\n expect(result).toBe(stream)\n })\n\n it('download() should delegate to filesMethods.headOrGet(req, res) and return its result', async () => {\n const stream = {} as any\n filesMethodsMock.headOrGet.mockResolvedValue(stream)\n\n const result = await filesController.download(fakeReq, fakeRes)\n\n expect(filesMethodsMock.headOrGet).toHaveBeenCalledWith(fakeReq, fakeRes)\n expect(result).toBe(stream)\n })\n\n it('make() should call filesMethods.make(user, space, dto)', async () => {\n const dto = { path: '/a', name: 'b', type: 'directory' } as any\n\n await filesController.make(fakeUser, fakeSpace, dto)\n\n expect(filesMethodsMock.make).toHaveBeenCalledWith(fakeUser, fakeSpace, dto)\n })\n\n it('upload() should call filesMethods.upload(req, res)', async () => {\n await filesController.upload(fakeReq, fakeRes)\n\n expect(filesMethodsMock.upload).toHaveBeenCalledWith(fakeReq, fakeRes)\n })\n\n it('copy() should call filesMethods.copy(user, space, dto) and return its result', async () => {\n const dto = { dstDirectory: '/dst', dstName: 'b' } as any\n const expected = { path: '/dst', name: 'b' }\n filesMethodsMock.copy.mockResolvedValue(expected)\n\n const result = await filesController.copy(fakeUser, fakeSpace, dto)\n\n expect(filesMethodsMock.copy).toHaveBeenCalledWith(fakeUser, fakeSpace, dto)\n expect(result).toEqual(expected)\n })\n\n it('move() should call filesMethods.move(user, space, dto) and return its result', async () => {\n const dto = { dstDirectory: '/dst', dstName: 'c' } as any\n const expected = { path: '/dst', name: 'c' }\n filesMethodsMock.move.mockResolvedValue(expected)\n\n const result = await filesController.move(fakeUser, fakeSpace, dto)\n\n expect(filesMethodsMock.move).toHaveBeenCalledWith(fakeUser, fakeSpace, dto)\n expect(result).toEqual(expected)\n })\n\n it('delete() should call filesMethods.delete(user, space)', async () => {\n await filesController.delete(fakeUser, fakeSpace)\n\n expect(filesMethodsMock.delete).toHaveBeenCalledWith(fakeUser, fakeSpace)\n })\n\n it('genThumbnail() should default size to 256 when not provided', async () => {\n const stream = {} as any\n filesMethodsMock.genThumbnail.mockResolvedValue(stream)\n\n // pass undefined to exercise controller default parameter\n const result = await filesController.genThumbnail(fakeSpace, undefined as unknown as number)\n\n expect(filesMethodsMock.genThumbnail).toHaveBeenCalledWith(fakeSpace, 256)\n expect(result).toBe(stream)\n })\n\n it('genThumbnail() should pass provided size', async () => {\n const stream = {} as any\n filesMethodsMock.genThumbnail.mockResolvedValue(stream)\n\n const result = await filesController.genThumbnail(fakeSpace, 512)\n\n expect(filesMethodsMock.genThumbnail).toHaveBeenCalledWith(fakeSpace, 512)\n expect(result).toBe(stream)\n })\n })\n\n describe('Tasks operations', () => {\n beforeEach(() => {\n jest.clearAllMocks()\n })\n\n it('downloadFromUrlAsTask() should create DOWNLOAD task using method name \"downloadFromUrl\"', async () => {\n const dto = { url: 'http://x', to: '/a' } as any\n const task = { id: 1 } as any\n filesTasksManagerMock.createTask.mockResolvedValue(task)\n\n const result = await filesController.downloadFromUrlAsTask(fakeUser, fakeSpace, dto)\n\n expect(filesTasksManagerMock.createTask).toHaveBeenCalledWith(\n FILE_OPERATION.DOWNLOAD,\n fakeUser,\n fakeSpace,\n dto,\n filesMethodsMock.downloadFromUrl.name\n )\n expect(result).toBe(task)\n })\n\n it('compressAsTask() should call SpaceGuard.checkPermissions when compressInDirectory is true', async () => {\n const dto = { compressInDirectory: true } as any\n const spy = jest.spyOn(SpaceGuard as any, 'checkPermissions').mockImplementation(() => undefined)\n\n filesTasksManagerMock.createTask.mockResolvedValue({} as any)\n await filesController.compressAsTask(fakeReq, dto)\n\n expect(spy).toHaveBeenCalled()\n spy.mockRestore()\n })\n\n it('compressAsTask() should create COMPRESS task with req.user and req.space and method name \"compress\"', async () => {\n const dto = { compressInDirectory: false } as any\n const task = { id: 2 } as any\n filesTasksManagerMock.createTask.mockResolvedValue(task)\n\n const result = await filesController.compressAsTask(fakeReq, dto)\n\n expect(filesTasksManagerMock.createTask).toHaveBeenCalledWith(\n FILE_OPERATION.COMPRESS,\n fakeReq.user,\n fakeReq.space,\n dto,\n filesMethodsMock.compress.name\n )\n expect(result).toBe(task)\n })\n\n it('decompressAsTask() should create DECOMPRESS task with null dto and method name \"decompress\"', async () => {\n const task = { id: 3 } as any\n filesTasksManagerMock.createTask.mockResolvedValue(task)\n\n const result = await filesController.decompressAsTask(fakeUser, fakeSpace)\n\n expect(filesTasksManagerMock.createTask).toHaveBeenCalledWith(\n FILE_OPERATION.DECOMPRESS,\n fakeUser,\n fakeSpace,\n null,\n filesMethodsMock.decompress.name\n )\n expect(result).toBe(task)\n })\n\n it('copyAsTask() should create COPY task with method name \"copy\"', async () => {\n const dto = { from: '/a', to: '/b' } as any\n const task = { id: 4 } as any\n filesTasksManagerMock.createTask.mockResolvedValue(task)\n\n const result = await filesController.copyAsTask(fakeUser, fakeSpace, dto)\n\n expect(filesTasksManagerMock.createTask).toHaveBeenCalledWith(FILE_OPERATION.COPY, fakeUser, fakeSpace, dto, filesMethodsMock.copy.name)\n expect(result).toBe(task)\n })\n\n it('moveAsTask() should create MOVE task with method name \"move\"', async () => {\n const dto = { from: '/a', to: '/c' } as any\n const task = { id: 5 } as any\n filesTasksManagerMock.createTask.mockResolvedValue(task)\n\n const result = await filesController.moveAsTask(fakeUser, fakeSpace, dto)\n\n expect(filesTasksManagerMock.createTask).toHaveBeenCalledWith(FILE_OPERATION.MOVE, fakeUser, fakeSpace, dto, filesMethodsMock.move.name)\n expect(result).toBe(task)\n })\n\n it('deleteAsTask() should create DELETE task with null dto and method name \"delete\"', async () => {\n const task = { id: 6 } as any\n filesTasksManagerMock.createTask.mockResolvedValue(task)\n\n const result = await filesController.deleteAsTask(fakeUser, fakeSpace)\n\n expect(filesTasksManagerMock.createTask).toHaveBeenCalledWith(FILE_OPERATION.DELETE, fakeUser, fakeSpace, null, filesMethodsMock.delete.name)\n expect(result).toBe(task)\n })\n })\n\n describe('Recents & Search', () => {\n beforeEach(() => {\n jest.clearAllMocks()\n })\n\n it('getRecents() should use limit=10 by default', async () => {\n const recents = [{ path: '/a' }] as any\n filesRecentsMock.getRecents.mockResolvedValue(recents)\n\n const result = await filesController.getRecents(fakeUser, undefined as unknown as number)\n\n expect(filesRecentsMock.getRecents).toHaveBeenCalledWith(fakeUser, 10)\n expect(result).toBe(recents)\n })\n\n it('getRecents() should forward provided limit', async () => {\n const recents = [{ path: '/b' }] as any\n filesRecentsMock.getRecents.mockResolvedValue(recents)\n\n const result = await filesController.getRecents(fakeUser, 5)\n\n expect(filesRecentsMock.getRecents).toHaveBeenCalledWith(fakeUser, 5)\n expect(result).toBe(recents)\n })\n\n it('search() should delegate to filesSearch.search(user, dto)', async () => {\n const dto = { query: 'test' } as any\n const items = [{ name: 'file' }] as any\n filesSearchMock.search.mockResolvedValue(items)\n\n const result = await filesController.search(fakeUser, dto)\n\n expect(filesSearchMock.search).toHaveBeenCalledWith(fakeUser, dto)\n expect(result).toBe(items)\n })\n })\n})\n"],"names":["describe","FilesController","name","filesController","fakeUser","id","login","role","fakeSpace","key","url","realPath","realBasePath","fakeReq","user","space","headers","method","ip","fakeRes","header","jest","fn","mockReturnThis","status","send","filesMethodsMock","headOrGet","make","upload","copy","move","delete","genThumbnail","downloadFromUrl","compress","decompress","filesTasksManagerMock","createTask","filesRecentsMock","getRecents","filesSearchMock","search","beforeAll","testingModuleBuilder","Test","createTestingModule","controllers","providers","provide","FilesMethods","useValue","FilesTasksManager","FilesRecents","FilesSearchManager","overrideGuard","SpaceGuard","canActivate","mockReturnValue","module","compile","get","it","expect","toBeDefined","beforeEach","clearAllMocks","stream","mockResolvedValue","result","head","toHaveBeenCalledWith","toBe","download","dto","path","type","dstDirectory","dstName","expected","toEqual","undefined","to","task","downloadFromUrlAsTask","FILE_OPERATION","DOWNLOAD","compressInDirectory","spy","spyOn","mockImplementation","compressAsTask","toHaveBeenCalled","mockRestore","COMPRESS","decompressAsTask","DECOMPRESS","from","copyAsTask","COPY","moveAsTask","MOVE","deleteAsTask","DELETE","recents","query","items"],"mappings":"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"}
@@ -6,10 +6,86 @@
6
6
  Object.defineProperty(exports, "__esModule", {
7
7
  value: true
8
8
  });
9
+ const _tsjest = require("@golevelup/ts-jest");
10
+ const _jwt = require("@nestjs/jwt");
11
+ const _testing = require("@nestjs/testing");
12
+ const _nestjspino = require("nestjs-pino");
13
+ const _configenvironment = require("../../../configuration/config.environment");
14
+ const _onlyoffice = require("../constants/only-office");
15
+ const _routes = require("../constants/routes");
9
16
  const _filesonlyofficeguard = require("./files-only-office.guard");
17
+ const _filesonlyofficestrategy = require("./files-only-office.strategy");
10
18
  describe(_filesonlyofficeguard.FilesOnlyOfficeGuard.name, ()=>{
19
+ let jwtService;
20
+ let filesOnlyOfficeGuard;
21
+ let context;
22
+ let accessToken;
23
+ beforeAll(async ()=>{
24
+ const module = await _testing.Test.createTestingModule({
25
+ imports: [
26
+ _jwt.JwtModule.register({
27
+ global: true
28
+ })
29
+ ],
30
+ providers: [
31
+ _filesonlyofficeguard.FilesOnlyOfficeGuard,
32
+ _filesonlyofficestrategy.FilesOnlyOfficeStrategy,
33
+ {
34
+ provide: _nestjspino.PinoLogger,
35
+ useValue: {
36
+ assign: ()=>undefined
37
+ }
38
+ }
39
+ ]
40
+ }).compile();
41
+ jwtService = module.get(_jwt.JwtService);
42
+ filesOnlyOfficeGuard = module.get(_filesonlyofficeguard.FilesOnlyOfficeGuard);
43
+ context = (0, _tsjest.createMock)();
44
+ accessToken = await jwtService.signAsync({
45
+ identity: {
46
+ id: 1,
47
+ login: 'foo'
48
+ }
49
+ }, {
50
+ secret: _configenvironment.configuration.auth.token.access.secret,
51
+ expiresIn: 30
52
+ });
53
+ });
11
54
  it('should be defined', ()=>{
12
- expect(new _filesonlyofficeguard.FilesOnlyOfficeGuard()).toBeDefined();
55
+ expect(jwtService).toBeDefined();
56
+ expect(filesOnlyOfficeGuard).toBeDefined();
57
+ expect(accessToken).toBeDefined();
58
+ });
59
+ it('should not pass if enabled (or not) without a valid token', async ()=>{
60
+ _configenvironment.configuration.applications.files.onlyoffice.enabled = false;
61
+ context.switchToHttp().getRequest.mockReturnValue({
62
+ url: `${_routes.API_FILES_ONLY_OFFICE_CALLBACK}`,
63
+ raw: {
64
+ user: ''
65
+ }
66
+ });
67
+ expect(()=>filesOnlyOfficeGuard.canActivate(context)).toThrow(/feature not enabled/i);
68
+ _configenvironment.configuration.applications.files.onlyoffice.enabled = true;
69
+ await expect(filesOnlyOfficeGuard.canActivate(context)).rejects.toThrow('Unauthorized');
70
+ });
71
+ it('should pass if enabled (or not) with a valid token', async ()=>{
72
+ _configenvironment.configuration.applications.files.onlyoffice.enabled = false;
73
+ context.switchToHttp().getRequest.mockReturnValue({
74
+ url: `${_routes.API_FILES_ONLY_OFFICE_CALLBACK}?${_onlyoffice.ONLY_OFFICE_TOKEN_QUERY_PARAM_NAME}=${accessToken}`,
75
+ raw: {
76
+ user: ''
77
+ }
78
+ });
79
+ expect(()=>filesOnlyOfficeGuard.canActivate(context)).toThrow(/feature not enabled/i);
80
+ _configenvironment.configuration.applications.files.onlyoffice.enabled = true;
81
+ expect(await filesOnlyOfficeGuard.canActivate(context)).toBe(true);
82
+ context.switchToHttp().getRequest.mockReturnValue({
83
+ url: `${_routes.API_FILES_ONLY_OFFICE_CALLBACK}?${_onlyoffice.ONLY_OFFICE_TOKEN_QUERY_PARAM_NAME}=unvalidToken`,
84
+ raw: {
85
+ user: ''
86
+ }
87
+ });
88
+ await expect(filesOnlyOfficeGuard.canActivate(context)).rejects.toThrow('Unauthorized');
13
89
  });
14
90
  });
15
91
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../backend/src/applications/files/guards/files-only-office.guard.spec.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { FilesOnlyOfficeGuard } from './files-only-office.guard'\n\ndescribe(FilesOnlyOfficeGuard.name, () => {\n it('should be defined', () => {\n expect(new FilesOnlyOfficeGuard()).toBeDefined()\n })\n})\n"],"names":["describe","FilesOnlyOfficeGuard","name","it","expect","toBeDefined"],"mappings":"AAAA;;;;CAIC;;;;sCAEoC;AAErCA,SAASC,0CAAoB,CAACC,IAAI,EAAE;IAClCC,GAAG,qBAAqB;QACtBC,OAAO,IAAIH,0CAAoB,IAAII,WAAW;IAChD;AACF"}
1
+ {"version":3,"sources":["../../../../../backend/src/applications/files/guards/files-only-office.guard.spec.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { createMock, DeepMocked } from '@golevelup/ts-jest'\nimport { ExecutionContext } from '@nestjs/common'\nimport { JwtModule, JwtService } from '@nestjs/jwt'\nimport { Test, TestingModule } from '@nestjs/testing'\nimport { PinoLogger } from 'nestjs-pino'\nimport { JwtPayload } from '../../../authentication/interfaces/jwt-payload.interface'\nimport { configuration } from '../../../configuration/config.environment'\nimport { ONLY_OFFICE_TOKEN_QUERY_PARAM_NAME } from '../constants/only-office'\nimport { API_FILES_ONLY_OFFICE_CALLBACK } from '../constants/routes'\nimport { FilesOnlyOfficeGuard } from './files-only-office.guard'\nimport { FilesOnlyOfficeStrategy } from './files-only-office.strategy'\n\ndescribe(FilesOnlyOfficeGuard.name, () => {\n let jwtService: JwtService\n let filesOnlyOfficeGuard: FilesOnlyOfficeGuard\n let context: DeepMocked<ExecutionContext>\n let accessToken: string\n\n beforeAll(async () => {\n const module: TestingModule = await Test.createTestingModule({\n imports: [JwtModule.register({ global: true })],\n providers: [\n FilesOnlyOfficeGuard,\n FilesOnlyOfficeStrategy,\n {\n provide: PinoLogger,\n useValue: {\n assign: () => undefined\n }\n }\n ]\n }).compile()\n\n jwtService = module.get<JwtService>(JwtService)\n filesOnlyOfficeGuard = module.get<FilesOnlyOfficeGuard>(FilesOnlyOfficeGuard)\n context = createMock<ExecutionContext>()\n accessToken = await jwtService.signAsync({ identity: { id: 1, login: 'foo' } } as JwtPayload, {\n secret: configuration.auth.token.access.secret,\n expiresIn: 30\n })\n })\n\n it('should be defined', () => {\n expect(jwtService).toBeDefined()\n expect(filesOnlyOfficeGuard).toBeDefined()\n expect(accessToken).toBeDefined()\n })\n\n it('should not pass if enabled (or not) without a valid token', async () => {\n configuration.applications.files.onlyoffice.enabled = false\n context.switchToHttp().getRequest.mockReturnValue({\n url: `${API_FILES_ONLY_OFFICE_CALLBACK}`,\n raw: { user: '' }\n })\n expect(() => filesOnlyOfficeGuard.canActivate(context)).toThrow(/feature not enabled/i)\n configuration.applications.files.onlyoffice.enabled = true\n await expect(filesOnlyOfficeGuard.canActivate(context)).rejects.toThrow('Unauthorized')\n })\n\n it('should pass if enabled (or not) with a valid token', async () => {\n configuration.applications.files.onlyoffice.enabled = false\n context.switchToHttp().getRequest.mockReturnValue({\n url: `${API_FILES_ONLY_OFFICE_CALLBACK}?${ONLY_OFFICE_TOKEN_QUERY_PARAM_NAME}=${accessToken}`,\n raw: { user: '' }\n })\n expect(() => filesOnlyOfficeGuard.canActivate(context)).toThrow(/feature not enabled/i)\n configuration.applications.files.onlyoffice.enabled = true\n expect(await filesOnlyOfficeGuard.canActivate(context)).toBe(true)\n context.switchToHttp().getRequest.mockReturnValue({\n url: `${API_FILES_ONLY_OFFICE_CALLBACK}?${ONLY_OFFICE_TOKEN_QUERY_PARAM_NAME}=unvalidToken`,\n raw: { user: '' }\n })\n await expect(filesOnlyOfficeGuard.canActivate(context)).rejects.toThrow('Unauthorized')\n })\n})\n"],"names":["describe","FilesOnlyOfficeGuard","name","jwtService","filesOnlyOfficeGuard","context","accessToken","beforeAll","module","Test","createTestingModule","imports","JwtModule","register","global","providers","FilesOnlyOfficeStrategy","provide","PinoLogger","useValue","assign","undefined","compile","get","JwtService","createMock","signAsync","identity","id","login","secret","configuration","auth","token","access","expiresIn","it","expect","toBeDefined","applications","files","onlyoffice","enabled","switchToHttp","getRequest","mockReturnValue","url","API_FILES_ONLY_OFFICE_CALLBACK","raw","user","canActivate","toThrow","rejects","ONLY_OFFICE_TOKEN_QUERY_PARAM_NAME","toBe"],"mappings":"AAAA;;;;CAIC;;;;wBAEsC;qBAED;yBACF;4BACT;mCAEG;4BACqB;wBACJ;sCACV;yCACG;AAExCA,SAASC,0CAAoB,CAACC,IAAI,EAAE;IAClC,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IAEJC,UAAU;QACR,MAAMC,SAAwB,MAAMC,aAAI,CAACC,mBAAmB,CAAC;YAC3DC,SAAS;gBAACC,cAAS,CAACC,QAAQ,CAAC;oBAAEC,QAAQ;gBAAK;aAAG;YAC/CC,WAAW;gBACTd,0CAAoB;gBACpBe,gDAAuB;gBACvB;oBACEC,SAASC,sBAAU;oBACnBC,UAAU;wBACRC,QAAQ,IAAMC;oBAChB;gBACF;aACD;QACH,GAAGC,OAAO;QAEVnB,aAAaK,OAAOe,GAAG,CAAaC,eAAU;QAC9CpB,uBAAuBI,OAAOe,GAAG,CAAuBtB,0CAAoB;QAC5EI,UAAUoB,IAAAA,kBAAU;QACpBnB,cAAc,MAAMH,WAAWuB,SAAS,CAAC;YAAEC,UAAU;gBAAEC,IAAI;gBAAGC,OAAO;YAAM;QAAE,GAAiB;YAC5FC,QAAQC,gCAAa,CAACC,IAAI,CAACC,KAAK,CAACC,MAAM,CAACJ,MAAM;YAC9CK,WAAW;QACb;IACF;IAEAC,GAAG,qBAAqB;QACtBC,OAAOlC,YAAYmC,WAAW;QAC9BD,OAAOjC,sBAAsBkC,WAAW;QACxCD,OAAO/B,aAAagC,WAAW;IACjC;IAEAF,GAAG,6DAA6D;QAC9DL,gCAAa,CAACQ,YAAY,CAACC,KAAK,CAACC,UAAU,CAACC,OAAO,GAAG;QACtDrC,QAAQsC,YAAY,GAAGC,UAAU,CAACC,eAAe,CAAC;YAChDC,KAAK,GAAGC,sCAA8B,EAAE;YACxCC,KAAK;gBAAEC,MAAM;YAAG;QAClB;QACAZ,OAAO,IAAMjC,qBAAqB8C,WAAW,CAAC7C,UAAU8C,OAAO,CAAC;QAChEpB,gCAAa,CAACQ,YAAY,CAACC,KAAK,CAACC,UAAU,CAACC,OAAO,GAAG;QACtD,MAAML,OAAOjC,qBAAqB8C,WAAW,CAAC7C,UAAU+C,OAAO,CAACD,OAAO,CAAC;IAC1E;IAEAf,GAAG,sDAAsD;QACvDL,gCAAa,CAACQ,YAAY,CAACC,KAAK,CAACC,UAAU,CAACC,OAAO,GAAG;QACtDrC,QAAQsC,YAAY,GAAGC,UAAU,CAACC,eAAe,CAAC;YAChDC,KAAK,GAAGC,sCAA8B,CAAC,CAAC,EAAEM,8CAAkC,CAAC,CAAC,EAAE/C,aAAa;YAC7F0C,KAAK;gBAAEC,MAAM;YAAG;QAClB;QACAZ,OAAO,IAAMjC,qBAAqB8C,WAAW,CAAC7C,UAAU8C,OAAO,CAAC;QAChEpB,gCAAa,CAACQ,YAAY,CAACC,KAAK,CAACC,UAAU,CAACC,OAAO,GAAG;QACtDL,OAAO,MAAMjC,qBAAqB8C,WAAW,CAAC7C,UAAUiD,IAAI,CAAC;QAC7DjD,QAAQsC,YAAY,GAAGC,UAAU,CAACC,eAAe,CAAC;YAChDC,KAAK,GAAGC,sCAA8B,CAAC,CAAC,EAAEM,8CAAkC,CAAC,aAAa,CAAC;YAC3FL,KAAK;gBAAEC,MAAM;YAAG;QAClB;QACA,MAAMZ,OAAOjC,qBAAqB8C,WAAW,CAAC7C,UAAU+C,OAAO,CAACD,OAAO,CAAC;IAC1E;AACF"}