@drax/media-back 2.11.0 → 3.0.0

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 (66) hide show
  1. package/dist/controllers/FileController.js +38 -0
  2. package/dist/controllers/MediaController.js +42 -1
  3. package/dist/factory/services/FileServiceFactory.js +30 -0
  4. package/dist/index.js +13 -2
  5. package/dist/interfaces/IFile.js +1 -0
  6. package/dist/interfaces/IFileRepository.js +1 -0
  7. package/dist/models/FileModel.js +49 -0
  8. package/dist/permissions/FilePermissions.js +13 -0
  9. package/dist/repository/mongo/FileMongoRepository.js +13 -0
  10. package/dist/repository/sqlite/FileSqliteRepository.js +37 -0
  11. package/dist/routes/FileRoutes.js +20 -0
  12. package/dist/schemas/FileSchema.js +51 -0
  13. package/dist/services/FileService.js +22 -0
  14. package/package.json +4 -4
  15. package/src/controllers/FileController.ts +50 -0
  16. package/src/controllers/MediaController.ts +48 -4
  17. package/src/factory/services/FileServiceFactory.ts +41 -0
  18. package/src/index.ts +30 -3
  19. package/src/interfaces/IFile.ts +67 -0
  20. package/src/interfaces/IFileRepository.ts +11 -0
  21. package/src/models/FileModel.ts +65 -0
  22. package/src/permissions/FilePermissions.ts +17 -0
  23. package/src/repository/mongo/FileMongoRepository.ts +22 -0
  24. package/src/repository/sqlite/FileSqliteRepository.ts +46 -0
  25. package/src/routes/FileRoutes.ts +36 -0
  26. package/src/schemas/FileSchema.ts +55 -0
  27. package/src/services/FileService.ts +36 -0
  28. package/test/modules/media/File/File-endpoints.test.ts +573 -0
  29. package/test/setup/MongoInMemory.ts +56 -0
  30. package/test/setup/TestSetup.ts +347 -0
  31. package/test/setup/data/admin-role.ts +13 -0
  32. package/test/setup/data/basic-user.ts +14 -0
  33. package/test/setup/data/file-role.ts +16 -0
  34. package/test/setup/data/one-tenant.ts +6 -0
  35. package/test/setup/data/restricted-role.ts +11 -0
  36. package/test/setup/data/root-user.ts +14 -0
  37. package/test/setup/data/tenant-one-user.ts +13 -0
  38. package/test/setup/data/tenant-two-user.ts +14 -0
  39. package/test/setup/data/two-tenant.ts +6 -0
  40. package/test/utils/StoreManager.test.ts +1 -1
  41. package/tsconfig.tsbuildinfo +1 -1
  42. package/types/controllers/FileController.d.ts +10 -0
  43. package/types/controllers/FileController.d.ts.map +1 -0
  44. package/types/controllers/MediaController.d.ts.map +1 -1
  45. package/types/factory/services/FileServiceFactory.d.ts +8 -0
  46. package/types/factory/services/FileServiceFactory.d.ts.map +1 -0
  47. package/types/index.d.ts +13 -1
  48. package/types/index.d.ts.map +1 -1
  49. package/types/interfaces/IFile.d.ts +63 -0
  50. package/types/interfaces/IFile.d.ts.map +1 -0
  51. package/types/interfaces/IFileRepository.d.ts +6 -0
  52. package/types/interfaces/IFileRepository.d.ts.map +1 -0
  53. package/types/models/FileModel.d.ts +15 -0
  54. package/types/models/FileModel.d.ts.map +1 -0
  55. package/types/permissions/FilePermissions.d.ts +13 -0
  56. package/types/permissions/FilePermissions.d.ts.map +1 -0
  57. package/types/repository/mongo/FileMongoRepository.d.ts +9 -0
  58. package/types/repository/mongo/FileMongoRepository.d.ts.map +1 -0
  59. package/types/repository/sqlite/FileSqliteRepository.d.ts +18 -0
  60. package/types/repository/sqlite/FileSqliteRepository.d.ts.map +1 -0
  61. package/types/routes/FileRoutes.d.ts +4 -0
  62. package/types/routes/FileRoutes.d.ts.map +1 -0
  63. package/types/schemas/FileSchema.d.ts +65 -0
  64. package/types/schemas/FileSchema.d.ts.map +1 -0
  65. package/types/services/FileService.d.ts +12 -0
  66. package/types/services/FileService.d.ts.map +1 -0
@@ -0,0 +1,573 @@
1
+ import { describe, it, beforeAll, afterAll, expect } from "vitest"
2
+ import FileRoutes from "../../../../src/routes/FileRoutes.js"
3
+ import FilePermissions from "../../../../src/permissions/FilePermissions.js"
4
+ import TestSetup from "../../../setup/TestSetup.js"
5
+ import type { IFileBase } from "../../../../src/interfaces/IFile.js"
6
+
7
+ describe("File Endpoints Test", function () {
8
+
9
+ let testSetup = new TestSetup({
10
+ routes: [FileRoutes],
11
+ permissions: [FilePermissions]
12
+ })
13
+
14
+ beforeAll(async () => {
15
+ await testSetup.setup()
16
+ })
17
+
18
+ afterAll(async () => {
19
+ await testSetup.dropAndClose()
20
+ return
21
+ })
22
+
23
+ it("should create a new File and find by id", async () => {
24
+ const { accessToken } = await testSetup.rootUserLogin()
25
+ expect(accessToken).toBeTruthy()
26
+ await testSetup.dropCollection('File')
27
+
28
+ const newFile: IFileBase = {
29
+ filename: "test-file.txt",
30
+ relativePath: "/docs/test-file.txt",
31
+ absolutePath: "/var/www/docs/test-file.txt",
32
+ url: "http://example.com/test-file.txt",
33
+ description: "Test description",
34
+ tags: ["test", "test2"],
35
+ mimetype: "text/plain",
36
+ encoding: "utf-8",
37
+ extension: "txt",
38
+ size: 1024,
39
+ type: "document",
40
+ lastAccess: new Date(),
41
+ ttlSeconds: 3600,
42
+ isPublic: true,
43
+ hits: 0
44
+ }
45
+
46
+ const resp = await testSetup.fastifyInstance.inject({
47
+ method: 'POST',
48
+ url: '/api/file',
49
+ payload: newFile,
50
+ headers: { Authorization: `Bearer ${accessToken}` }
51
+ })
52
+
53
+ const entity = await resp.json()
54
+ expect(resp.statusCode).toBe(200)
55
+ expect(entity.filename).toBe("test-file.txt")
56
+ expect(entity._id).toBeDefined()
57
+
58
+ const getResp = await testSetup.fastifyInstance.inject({
59
+ method: 'GET',
60
+ url: '/api/file/' + entity._id,
61
+ headers: { Authorization: `Bearer ${accessToken}` }
62
+ })
63
+
64
+ const getEntity = await getResp.json()
65
+ expect(getResp.statusCode).toBe(200)
66
+ expect(getEntity.filename).toBe("test-file.txt")
67
+ })
68
+
69
+ it("should create and update a File and finally find by id", async () => {
70
+ const { accessToken } = await testSetup.rootUserLogin()
71
+ expect(accessToken).toBeTruthy()
72
+ await testSetup.dropCollection('File')
73
+
74
+ const newFile: IFileBase = {
75
+ filename: "test-file.txt",
76
+ relativePath: "/docs/test-file.txt",
77
+ absolutePath: "/var/www/docs/test-file.txt",
78
+ url: "http://example.com/test-file.txt",
79
+ description: "Original description",
80
+ tags: ["test", "test2"],
81
+ mimetype: "text/plain",
82
+ encoding: "utf-8",
83
+ extension: "txt",
84
+ size: 1024,
85
+ type: "document",
86
+ lastAccess: new Date(),
87
+ ttlSeconds: 3600,
88
+ isPublic: true,
89
+ hits: 0
90
+ }
91
+
92
+ const resp = await testSetup.fastifyInstance.inject({
93
+ method: 'POST',
94
+ url: '/api/file',
95
+ payload: newFile,
96
+ headers: { Authorization: `Bearer ${accessToken}` }
97
+ })
98
+
99
+ const entity = await resp.json()
100
+ expect(resp.statusCode).toBe(200)
101
+ expect(entity._id).toBeDefined()
102
+
103
+ const updateData: IFileBase = {
104
+ ...newFile,
105
+ filename: "Updated File",
106
+ description: "Updated description"
107
+ }
108
+
109
+ const updateResp = await testSetup.fastifyInstance.inject({
110
+ method: 'PUT',
111
+ url: `/api/file/${entity._id}`,
112
+ payload: updateData,
113
+ headers: { Authorization: `Bearer ${accessToken}` }
114
+ })
115
+
116
+ expect(updateResp.statusCode).toBe(200)
117
+ const updatedEntity = await updateResp.json()
118
+ expect(updatedEntity.filename).toBe("Updated File")
119
+
120
+ const verifyResp = await testSetup.fastifyInstance.inject({
121
+ method: 'GET',
122
+ url: `/api/file/${updatedEntity._id}`,
123
+ headers: { Authorization: `Bearer ${accessToken}` }
124
+ })
125
+
126
+ const verifiedEntity = await verifyResp.json()
127
+ expect(verifyResp.statusCode).toBe(200)
128
+ expect(verifiedEntity.filename).toBe("Updated File")
129
+ })
130
+
131
+ it("should create and update partial a File and finally find by id", async () => {
132
+ const { accessToken } = await testSetup.rootUserLogin()
133
+ expect(accessToken).toBeTruthy()
134
+ await testSetup.dropCollection('File')
135
+
136
+ const newFile: IFileBase = {
137
+ filename: "test-file.txt",
138
+ relativePath: "/docs/test-file.txt",
139
+ absolutePath: "/var/www/docs/test-file.txt",
140
+ url: "http://example.com/test-file.txt",
141
+ description: "Original description",
142
+ tags: ["test"],
143
+ mimetype: "text/plain",
144
+ encoding: "utf-8",
145
+ extension: "txt",
146
+ size: 1024,
147
+ type: "document",
148
+ lastAccess: new Date(),
149
+ ttlSeconds: 3600,
150
+ isPublic: true,
151
+ hits: 0
152
+ }
153
+
154
+ const resp = await testSetup.fastifyInstance.inject({
155
+ method: 'POST',
156
+ url: '/api/file',
157
+ payload: newFile,
158
+ headers: { Authorization: `Bearer ${accessToken}` }
159
+ })
160
+
161
+ const entity = await resp.json()
162
+ expect(resp.statusCode).toBe(200)
163
+
164
+ const updateData: any = {
165
+ filename: "Updated Partial"
166
+ }
167
+
168
+ const updateResp = await testSetup.fastifyInstance.inject({
169
+ method: 'PATCH',
170
+ url: `/api/file/${entity._id}`,
171
+ payload: updateData,
172
+ headers: { Authorization: `Bearer ${accessToken}` }
173
+ })
174
+
175
+ expect(updateResp.statusCode).toBe(200)
176
+ const updatedEntity = await updateResp.json()
177
+ expect(updatedEntity.filename).toBe("Updated Partial")
178
+
179
+ const verifyResp = await testSetup.fastifyInstance.inject({
180
+ method: 'GET',
181
+ url: `/api/file/${updatedEntity._id}`,
182
+ headers: { Authorization: `Bearer ${accessToken}` }
183
+ })
184
+
185
+ const verifiedEntity = await verifyResp.json()
186
+ expect(verifyResp.statusCode).toBe(200)
187
+ expect(verifiedEntity.filename).toBe("Updated Partial")
188
+ })
189
+
190
+ it("should create and delete a File", async () => {
191
+ const { accessToken } = await testSetup.rootUserLogin()
192
+ expect(accessToken).toBeTruthy()
193
+ await testSetup.dropCollection('File')
194
+
195
+ const newFile: IFileBase = {
196
+ filename: "test-file-delete.txt",
197
+ relativePath: "/docs/test-file.txt",
198
+ absolutePath: "/var/www/docs/test-file.txt",
199
+ url: "http://example.com/test-file.txt",
200
+ description: "To Delete",
201
+ tags: [],
202
+ mimetype: "text/plain",
203
+ encoding: "utf-8",
204
+ extension: "txt",
205
+ size: 1024,
206
+ type: "document",
207
+ lastAccess: new Date(),
208
+ ttlSeconds: 3600,
209
+ isPublic: true,
210
+ hits: 0
211
+ }
212
+
213
+ const createResp = await testSetup.fastifyInstance.inject({
214
+ method: 'POST',
215
+ url: '/api/file',
216
+ payload: newFile,
217
+ headers: { Authorization: `Bearer ${accessToken}` }
218
+ })
219
+
220
+ const createdEntity = await createResp.json()
221
+ expect(createResp.statusCode).toBe(200)
222
+ const entityId = createdEntity._id
223
+
224
+ const deleteResp = await testSetup.fastifyInstance.inject({
225
+ method: 'DELETE',
226
+ url: `/api/file/${entityId}`,
227
+ headers: { Authorization: `Bearer ${accessToken}` }
228
+ })
229
+
230
+ expect(deleteResp.statusCode).toBe(200)
231
+ const deleteResult = await deleteResp.json()
232
+ expect(deleteResult.deleted).toBe(true);
233
+
234
+ const verifyResp = await testSetup.fastifyInstance.inject({
235
+ method: 'GET',
236
+ url: `/api/file/${entityId}`,
237
+ headers: { Authorization: `Bearer ${accessToken}` }
238
+ })
239
+
240
+ expect(verifyResp.statusCode).toBe(404)
241
+ })
242
+
243
+ it("Should create and paginate File", async () => {
244
+ const { accessToken } = await testSetup.rootUserLogin()
245
+ expect(accessToken).toBeTruthy()
246
+ await testSetup.dropCollection('File')
247
+
248
+ const entityData = [
249
+ {
250
+ filename: "File 1", relativePath: "f1", absolutePath: "f1", url: "url1", mimetype: "txt", encoding: "1", extension: "1", size: 1, type: "1", lastAccess: new Date(), ttlSeconds: 1
251
+ },
252
+ {
253
+ filename: "File 2", relativePath: "f2", absolutePath: "f2", url: "url2", mimetype: "txt", encoding: "1", extension: "1", size: 1, type: "1", lastAccess: new Date(), ttlSeconds: 1
254
+ }
255
+ ]
256
+
257
+ for (const data of entityData) {
258
+ const respCreate = await testSetup.fastifyInstance.inject({
259
+ method: 'POST',
260
+ url: '/api/file',
261
+ payload: data,
262
+ headers: { Authorization: `Bearer ${accessToken}` }
263
+ })
264
+
265
+ const resultCreate = await respCreate.json()
266
+ console.log("resultCreate",resultCreate)
267
+ }
268
+
269
+ const resp = await testSetup.fastifyInstance.inject({
270
+ method: 'GET',
271
+ url: '/api/file',
272
+ headers: { Authorization: `Bearer ${accessToken}` }
273
+ })
274
+
275
+ const result = await resp.json()
276
+ expect(resp.statusCode).toBe(200)
277
+ console.log("result",result)
278
+ expect(result.items.length).toBe(2)
279
+ expect(result.total).toBe(2)
280
+ expect(result.page).toBe(1)
281
+ expect(result.limit).toBe(10)
282
+ })
283
+
284
+ it("should create and search for File", async () => {
285
+ const { accessToken } = await testSetup.rootUserLogin()
286
+ expect(accessToken).toBeTruthy()
287
+ await testSetup.dropCollection('File')
288
+
289
+ const entityData = [
290
+ { filename: "Search Entity 1", relativePath: "s1", absolutePath: "s1", url: "url1", mimetype: "txt", encoding: "1", extension: "1", size: 1, type: "1", lastAccess: new Date(), ttlSeconds: 1 },
291
+ { filename: "Search Entity 2", relativePath: "s2", absolutePath: "s2", url: "url2", mimetype: "txt", encoding: "1", extension: "1", size: 1, type: "2", lastAccess: new Date(), ttlSeconds: 1 },
292
+ { filename: "Other Entity", relativePath: "o1", absolutePath: "o1", url: "urlo", mimetype: "txt", encoding: "1", extension: "1", size: 1, type: "3", lastAccess: new Date(), ttlSeconds: 1 }
293
+ ]
294
+
295
+ for (const data of entityData) {
296
+ await testSetup.fastifyInstance.inject({
297
+ method: 'POST',
298
+ url: '/api/file',
299
+ payload: data,
300
+ headers: { Authorization: `Bearer ${accessToken}` }
301
+ })
302
+ }
303
+
304
+ const searchResp = await testSetup.fastifyInstance.inject({
305
+ method: 'GET',
306
+ url: '/api/file/search?search=Search',
307
+ headers: { Authorization: `Bearer ${accessToken}` }
308
+ })
309
+
310
+ const searchResult = await searchResp.json()
311
+ expect(searchResp.statusCode).toBe(200)
312
+ expect(searchResult.length).toBe(2)
313
+ expect(searchResult.some(entity => entity.filename === "Search Entity 1")).toBe(true)
314
+ expect(searchResult.some(entity => entity.filename === "Search Entity 2")).toBe(true)
315
+ })
316
+
317
+ it("should create and find File with filters", async () => {
318
+ const { accessToken } = await testSetup.rootUserLogin()
319
+ expect(accessToken).toBeTruthy()
320
+ await testSetup.dropCollection('File')
321
+
322
+ const entityData = [
323
+ { filename: "Entity 1", type: "document", relativePath: "e1", absolutePath: "e1", url: "u1", mimetype: "txt", encoding: "u", extension: "1", size: 1, lastAccess: new Date(), ttlSeconds: 1 },
324
+ { filename: "Entity 2", type: "image", relativePath: "e2", absolutePath: "e2", url: "u2", mimetype: "img", encoding: "u", extension: "1", size: 1, lastAccess: new Date(), ttlSeconds: 1 }
325
+ ]
326
+
327
+ for (const data of entityData) {
328
+ await testSetup.fastifyInstance.inject({
329
+ method: 'POST',
330
+ url: '/api/file',
331
+ payload: data,
332
+ headers: { Authorization: `Bearer ${accessToken}` }
333
+ })
334
+ }
335
+
336
+ const findByResp = await testSetup.fastifyInstance.inject({
337
+ method: 'GET',
338
+ url: '/api/file/find?filters=type;eq;document',
339
+ headers: { Authorization: `Bearer ${accessToken}` }
340
+ })
341
+
342
+ const findByResult = await findByResp.json()
343
+ expect(findByResp.statusCode).toBe(200)
344
+ expect(Array.isArray(findByResult)).toBe(true)
345
+ expect(findByResult.length).toBe(1)
346
+ expect(findByResult[0].filename).toBe("Entity 1")
347
+ })
348
+
349
+ it("should create and groupBy for File", async () => {
350
+ const { accessToken } = await testSetup.rootUserLogin()
351
+ expect(accessToken).toBeTruthy()
352
+ await testSetup.dropCollection('File')
353
+
354
+ const entityData = [
355
+ { filename: "Entity 1", type: "typeA", mimetype: "active", relativePath: "g1", absolutePath: "g1", url: "g1", encoding: "u", extension: "1", size: 1, lastAccess: new Date(), ttlSeconds: 1 },
356
+ { filename: "Entity 2", type: "typeB", mimetype: "active", relativePath: "g2", absolutePath: "g2", url: "g2", encoding: "u", extension: "1", size: 1, lastAccess: new Date(), ttlSeconds: 1 },
357
+ { filename: "Entity 3", type: "typeB", mimetype: "active", relativePath: "g3", absolutePath: "g3", url: "g3", encoding: "u", extension: "1", size: 1, lastAccess: new Date(), ttlSeconds: 1 }
358
+ ]
359
+
360
+ for (const data of entityData) {
361
+ await testSetup.fastifyInstance.inject({
362
+ method: 'POST',
363
+ url: '/api/file',
364
+ payload: data,
365
+ headers: { Authorization: `Bearer ${accessToken}` }
366
+ })
367
+ }
368
+
369
+ const groupResp = await testSetup.fastifyInstance.inject({
370
+ method: 'GET',
371
+ url: '/api/file/group-by?fields=type,mimetype',
372
+ headers: { Authorization: `Bearer ${accessToken}` }
373
+ })
374
+
375
+ const groupResult = await groupResp.json()
376
+ expect(groupResp.statusCode).toBe(200)
377
+ console.log("groupResult",groupResult)
378
+ expect(groupResult[0].count).toBe(2)
379
+ expect(groupResult[0].type).toBe('typeB')
380
+ expect(groupResult[1].count).toBe(1)
381
+ expect(groupResult[1].type).toBe('typeA')
382
+ })
383
+
384
+ it("should return 401 when accessing endpoints without token", async () => {
385
+ const resp = await testSetup.fastifyInstance.inject({
386
+ method: 'GET',
387
+ url: '/api/file'
388
+ });
389
+ expect(resp.statusCode).toBe(401);
390
+ });
391
+
392
+ it("should return 403 when creating with restricted user", async () => {
393
+ const { accessToken } = await testSetup.basicUserLogin();
394
+
395
+ const resp = await testSetup.fastifyInstance.inject({
396
+ method: 'POST',
397
+ url: '/api/file',
398
+ payload: { filename: "Forbidden" },
399
+ headers: { Authorization: `Bearer ${accessToken}` }
400
+ });
401
+ expect(resp.statusCode).toBe(403);
402
+ });
403
+
404
+ it("should return 422 when creating with missing mandatory fields", async () => {
405
+ const { accessToken } = await testSetup.rootUserLogin();
406
+
407
+ const resp = await testSetup.fastifyInstance.inject({
408
+ method: 'POST',
409
+ url: '/api/file',
410
+ payload: {},
411
+ headers: { Authorization: `Bearer ${accessToken}` }
412
+ });
413
+
414
+ expect(resp.statusCode).toBe(422);
415
+ });
416
+
417
+ it("should handle error responses correctly when File is not found", async () => {
418
+ const { accessToken } = await testSetup.rootUserLogin()
419
+ expect(accessToken).toBeTruthy()
420
+
421
+ const nonExistentId = "123456789012345678901234"
422
+
423
+ const resp = await testSetup.fastifyInstance.inject({
424
+ method: 'GET',
425
+ url: `/api/file/${nonExistentId}`,
426
+ headers: { Authorization: `Bearer ${accessToken}` }
427
+ })
428
+
429
+ expect(resp.statusCode).toBe(404)
430
+ const result = await resp.json()
431
+ expect(result.error).toBeDefined()
432
+ })
433
+
434
+ it("should return 400 when providing invalid ID format", async () => {
435
+ const { accessToken } = await testSetup.rootUserLogin();
436
+
437
+ const resp = await testSetup.fastifyInstance.inject({
438
+ method: 'GET',
439
+ url: '/api/file/invalid-id-format',
440
+ headers: { Authorization: `Bearer ${accessToken}` }
441
+ });
442
+ expect(resp.statusCode).toBe(400);
443
+ });
444
+
445
+ it("should return 404 when updating non-existent resource", async () => {
446
+ const { accessToken } = await testSetup.rootUserLogin();
447
+ const nonExistentId = "123456789012345678901234";
448
+
449
+ const resp = await testSetup.fastifyInstance.inject({
450
+ method: 'PUT',
451
+ url: `/api/file/${nonExistentId}`,
452
+ payload: {
453
+ filename: "Non-existent", relativePath: "s1", absolutePath: "s1", url: "url1", mimetype: "txt", encoding: "1", extension: "1", size: 1, type: "1", lastAccess: new Date(), ttlSeconds: 1
454
+ },
455
+ headers: { Authorization: `Bearer ${accessToken}` }
456
+ });
457
+ expect(resp.statusCode).toBe(404);
458
+ });
459
+
460
+ // ==========================================
461
+ // Multi-Tenancy Tests
462
+ // ==========================================
463
+
464
+ it("Tenant One files cannot be accessed by Tenant Two", async () => {
465
+ await testSetup.dropCollection('File')
466
+
467
+ const { accessToken: tokenOne } = await testSetup.tenantOneUserLogin()
468
+ const { accessToken: tokenTwo } = await testSetup.tenantTwoUserLogin()
469
+ expect(tokenOne).toBeTruthy()
470
+ expect(tokenTwo).toBeTruthy()
471
+
472
+ const tenantOneFile: IFileBase = {
473
+ filename: "tenant-one-file.txt",
474
+ relativePath: "/docs/tenant-one-file.txt",
475
+ absolutePath: "/var/www/docs/tenant-one-file.txt",
476
+ url: "http://example.com/tenant-one-file.txt",
477
+ description: "Tenant One File",
478
+ tags: ["tenant-one"],
479
+ mimetype: "text/plain",
480
+ encoding: "utf-8",
481
+ extension: "txt",
482
+ size: 1024,
483
+ type: "document",
484
+ lastAccess: new Date(),
485
+ ttlSeconds: 3600,
486
+ isPublic: true,
487
+ hits: 0
488
+ }
489
+
490
+ const createResp = await testSetup.fastifyInstance.inject({
491
+ method: 'POST',
492
+ url: '/api/file',
493
+ payload: tenantOneFile,
494
+ headers: { Authorization: `Bearer ${tokenOne}` }
495
+ })
496
+ expect(createResp.statusCode).toBe(200)
497
+ const createdFile = await createResp.json()
498
+ const tenantOneFileId = createdFile._id
499
+ expect(createdFile.filename).toBe("tenant-one-file.txt")
500
+
501
+
502
+ const getT1Resp = await testSetup.fastifyInstance.inject({
503
+ method: 'GET',
504
+ url: `/api/file/${tenantOneFileId}`,
505
+ headers: { Authorization: `Bearer ${tokenOne}` }
506
+ })
507
+ expect(getT1Resp.statusCode).toBe(200)
508
+ expect((await getT1Resp.json()).filename).toBe("tenant-one-file.txt")
509
+
510
+ const getT2Resp = await testSetup.fastifyInstance.inject({
511
+ method: 'GET',
512
+ url: `/api/file/${tenantOneFileId}`,
513
+ headers: { Authorization: `Bearer ${tokenTwo}` }
514
+ })
515
+ expect(getT2Resp.statusCode).not.toBe(200)
516
+
517
+ const paginateT2Resp = await testSetup.fastifyInstance.inject({
518
+ method: 'GET',
519
+ url: '/api/file?search=tenant-one-file.txt',
520
+ headers: { Authorization: `Bearer ${tokenTwo}` }
521
+ })
522
+ expect(paginateT2Resp.statusCode).toBe(200)
523
+ const paginateT2Body = await paginateT2Resp.json()
524
+ expect(paginateT2Body.items).toEqual([])
525
+ expect(paginateT2Body.total).toBe(0)
526
+
527
+ const searchT2Resp = await testSetup.fastifyInstance.inject({
528
+ method: 'GET',
529
+ url: '/api/file/search?search=tenant-one-file.txt',
530
+ headers: { Authorization: `Bearer ${tokenTwo}` }
531
+ })
532
+ expect(searchT2Resp.statusCode).toBe(200)
533
+ expect(await searchT2Resp.json()).toEqual([])
534
+
535
+ const findT2Resp = await testSetup.fastifyInstance.inject({
536
+ method: 'GET',
537
+ url: '/api/file/find?filters=filename;eq;tenant-one-file.txt',
538
+ headers: { Authorization: `Bearer ${tokenTwo}` }
539
+ })
540
+ expect(findT2Resp.statusCode).toBe(200)
541
+ expect(await findT2Resp.json()).toEqual([])
542
+
543
+ const updateT2Resp = await testSetup.fastifyInstance.inject({
544
+ method: 'PUT',
545
+ url: `/api/file/${tenantOneFileId}`,
546
+ payload: {
547
+ ...tenantOneFile,
548
+ filename: "hacked-by-tenant-two.txt",
549
+ description: "Hacked by Tenant Two"
550
+ },
551
+ headers: { Authorization: `Bearer ${tokenTwo}` }
552
+ })
553
+ expect(updateT2Resp.statusCode).not.toBe(200)
554
+
555
+ const updatePartialT2Resp = await testSetup.fastifyInstance.inject({
556
+ method: 'PATCH',
557
+ url: `/api/file/${tenantOneFileId}`,
558
+ payload: { filename: "patched-by-tenant-two.txt" },
559
+ headers: { Authorization: `Bearer ${tokenTwo}` }
560
+ })
561
+ expect(updatePartialT2Resp.statusCode).not.toBe(200)
562
+
563
+ const deleteT2Resp = await testSetup.fastifyInstance.inject({
564
+ method: 'DELETE',
565
+ url: `/api/file/${tenantOneFileId}`,
566
+ headers: { Authorization: `Bearer ${tokenTwo}` }
567
+ })
568
+ expect(deleteT2Resp.statusCode).not.toBe(200)
569
+
570
+
571
+ })
572
+
573
+ })
@@ -0,0 +1,56 @@
1
+ import {mongoose} from '@drax/common-back';
2
+ import {MongoMemoryServer} from 'mongodb-memory-server';
3
+
4
+ class MongoInMemory {
5
+
6
+ mongoServer: MongoMemoryServer
7
+
8
+ async connect() {
9
+ this.mongoServer = await MongoMemoryServer.create();
10
+ if (this.mongoServer.state == "new") {
11
+ await this.mongoServer.start()
12
+ }
13
+ if (!mongoose.connection.readyState) {
14
+ await mongoose.connect(this.mongoServer.getUri(), {dbName: "verifyMASTER"});
15
+ }
16
+ return
17
+ }
18
+
19
+ get mongooseStatus() {
20
+ return mongoose.connection.readyState
21
+ }
22
+
23
+ get serverStatus() {
24
+ return this.mongoServer.state
25
+ }
26
+
27
+ get status() {
28
+ return mongoose.connection.readyState
29
+ }
30
+
31
+ async disconnect() {
32
+ await mongoose.disconnect();
33
+ }
34
+
35
+ async dropCollections() {
36
+ const collections = await mongoose.connection.listCollections()
37
+ for (let collection of collections) {
38
+ console.log(`Dropping collection: ${collection.name}`)
39
+ await mongoose.connection.dropCollection(collection.name)
40
+ }
41
+ }
42
+
43
+ async dropCollection(name: string) {
44
+ await mongoose.connection.dropCollection(name)
45
+ }
46
+
47
+ async dropAndClose() {
48
+ if (this.mongoServer) {
49
+ await mongoose.connection.dropDatabase();
50
+ await mongoose.connection.close();
51
+ await this.mongoServer.stop();
52
+ }
53
+ }
54
+ }
55
+
56
+ export default MongoInMemory