@smartsoft001/mongo 1.1.91 → 1.3.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 (43) hide show
  1. package/.eslintrc +13 -0
  2. package/jest.config.ts +27 -0
  3. package/package.json +2 -26
  4. package/project.json +48 -0
  5. package/src/index.ts +3 -0
  6. package/src/lib/mongo.config.ts +10 -0
  7. package/src/lib/mongo.module.ts +28 -0
  8. package/src/lib/mongo.unitofwork.spec.ts +84 -0
  9. package/src/lib/mongo.unitofwork.ts +58 -0
  10. package/src/lib/mongo.utils.spec.ts +54 -0
  11. package/src/lib/mongo.utils.ts +17 -0
  12. package/src/lib/repositories/attachment.repository.spec.ts +249 -0
  13. package/src/lib/repositories/attachment.repository.ts +113 -0
  14. package/src/lib/repositories/interfaces.ts +28 -0
  15. package/src/lib/repositories/item.repository.spec.ts +1269 -0
  16. package/src/lib/repositories/item.repository.ts +582 -0
  17. package/tsconfig.json +13 -0
  18. package/tsconfig.lib.json +11 -0
  19. package/tsconfig.spec.json +20 -0
  20. package/src/index.d.ts +0 -3
  21. package/src/index.js +0 -7
  22. package/src/index.js.map +0 -1
  23. package/src/lib/mongo.config.d.ts +0 -9
  24. package/src/lib/mongo.config.js +0 -7
  25. package/src/lib/mongo.config.js.map +0 -1
  26. package/src/lib/mongo.module.d.ts +0 -5
  27. package/src/lib/mongo.module.js +0 -25
  28. package/src/lib/mongo.module.js.map +0 -1
  29. package/src/lib/mongo.unitofwork.d.ts +0 -12
  30. package/src/lib/mongo.unitofwork.js +0 -56
  31. package/src/lib/mongo.unitofwork.js.map +0 -1
  32. package/src/lib/mongo.utils.d.ts +0 -2
  33. package/src/lib/mongo.utils.js +0 -13
  34. package/src/lib/mongo.utils.js.map +0 -1
  35. package/src/lib/repositories/attachment.repository.d.ts +0 -28
  36. package/src/lib/repositories/attachment.repository.js +0 -88
  37. package/src/lib/repositories/attachment.repository.js.map +0 -1
  38. package/src/lib/repositories/item.repository.d.ts +0 -51
  39. package/src/lib/repositories/item.repository.js +0 -411
  40. package/src/lib/repositories/item.repository.js.map +0 -1
  41. package/test-setup.js +0 -1
  42. package/test-setup.js.map +0 -1
  43. /package/{test-setup.d.ts → test-setup.ts} +0 -0
package/.eslintrc ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "../../../.eslintrc",
3
+ "rules": {
4
+ "import/order": "off"
5
+ },
6
+ "ignorePatterns": ["!**/*"],
7
+ "overrides": [
8
+ {
9
+ "files": ["*.json"],
10
+ "parser": "jsonc-eslint-parser"
11
+ }
12
+ ]
13
+ }
package/jest.config.ts ADDED
@@ -0,0 +1,27 @@
1
+ /* eslint-disable */
2
+ export default {
3
+ globals: {},
4
+ transform: {
5
+ '^.+\\.[tj]sx?$': [
6
+ 'ts-jest',
7
+ {
8
+ tsconfig: '<rootDir>/tsconfig.spec.json',
9
+ },
10
+ ],
11
+ },
12
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
13
+ coverageDirectory: '../../../coverage/packages/shared/mongo',
14
+ displayName: 'shared-mongo',
15
+ testEnvironment: 'node',
16
+ preset: '../../../jest.preset.js',
17
+ /* TODO: Update to latest Jest snapshotFormat
18
+ * By default Nx has kept the older style of Jest Snapshot formats
19
+ * to prevent breaking of any existing tests with snapshots.
20
+ * It's recommend you update to the latest format.
21
+ * You can do this by removing snapshotFormat property
22
+ * and running tests with --update-snapshot flag.
23
+ * Example: From within the project directory, run "nx test --update-snapshot"
24
+ * More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
25
+ */
26
+ snapshotFormat: { escapeString: true, printBasicPrototype: true },
27
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartsoft001/mongo",
3
- "version": "1.1.91",
3
+ "version": "1.3.0",
4
4
  "description": "Utils to mongo",
5
5
  "repository": {
6
6
  "type": "git",
@@ -16,29 +16,5 @@
16
16
  "bugs": {
17
17
  "url": "https://github.com/emiljuchnikowski/smartsoft/issues"
18
18
  },
19
- "homepage": "https://github.com/emiljuchnikowski/smartsoft#readme",
20
- "dependencies": {
21
- "@angular/common": "16.1.3",
22
- "@angular/core": "16.1.3",
23
- "@angular/platform-browser-dynamic": "16.1.3",
24
- "@nestjs/common": "^10.0.5",
25
- "flatted": "^3.2.4",
26
- "guid-typescript": "1.0.9",
27
- "lodash": "^4.17.20",
28
- "md5": "2.3.0",
29
- "mongodb": "5.6.0",
30
- "reflect-metadata": "0.1.13",
31
- "rxjs": "7.8.1"
32
- },
33
- "peerDependencies": {
34
- "@smartsoft001/crud-shell-dtos": "1.1.91",
35
- "@smartsoft001/domain-core": "1.1.91",
36
- "@smartsoft001/models": "1.1.91",
37
- "@smartsoft001/users": "1.1.91",
38
- "@smartsoft001/utils": "1.1.91",
39
- "util": "0.12.5",
40
- "tslib": "2.5.3"
41
- },
42
- "main": "./src/index.js",
43
- "types": "./src/index.d.ts"
19
+ "homepage": "https://github.com/emiljuchnikowski/smartsoft#readme"
44
20
  }
package/project.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "shared-mongo",
3
+ "$schema": "../../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "packages/shared/mongo/src",
5
+ "projectType": "library",
6
+ "generators": {},
7
+ "targets": {
8
+ "lint": {
9
+ "executor": "@nx/eslint:lint",
10
+ "outputs": ["{options.outputFile}"],
11
+ "options": {
12
+ "lintFilePatterns": [
13
+ "packages/shared/mongo/**/*.{ts,tsx,js,jsx}",
14
+ "packages/shared/mongo/package.json"
15
+ ]
16
+ }
17
+ },
18
+ "test": {
19
+ "executor": "@nx/jest:jest",
20
+ "options": {
21
+ "jestConfig": "packages/shared/mongo/jest.config.ts"
22
+ },
23
+ "outputs": ["{workspaceRoot}/coverage/packages/shared/mongo"]
24
+ },
25
+ "build": {
26
+ "executor": "@nx/esbuild:esbuild",
27
+ "outputs": ["{options.outputPath}"],
28
+ "options": {
29
+ "outputPath": "dist/packages/shared/mongo",
30
+ "tsConfig": "packages/shared/mongo/tsconfig.lib.json",
31
+ "packageJson": "packages/shared/mongo/package.json",
32
+ "main": "packages/shared/mongo/src/index.ts",
33
+ "assets": ["packages/shared/mongo/*.md"],
34
+ "generatePackageJson": true,
35
+ "format": ["cjs"]
36
+ }
37
+ },
38
+ "deploy": {
39
+ "executor": "ngx-deploy-npm:deploy",
40
+ "options": {
41
+ "access": "public",
42
+ "distFolderPath": "dist/packages/shared/mongo"
43
+ },
44
+ "dependsOn": ["build"]
45
+ }
46
+ },
47
+ "tags": ["scope:shared", "type:util"]
48
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './lib/mongo.module';
2
+ export * from './lib/repositories/item.repository';
3
+ export * from './lib/mongo.config';
@@ -0,0 +1,10 @@
1
+ export class MongoConfig {
2
+ host?: string;
3
+ port?: number;
4
+ database: string;
5
+ username?: string;
6
+ password?: string;
7
+ collection?: string;
8
+ url?: string;
9
+ type?: any;
10
+ }
@@ -0,0 +1,28 @@
1
+ import { DynamicModule } from '@nestjs/common';
2
+ import {
3
+ IAttachmentRepository,
4
+ IItemRepository,
5
+ IUnitOfWork,
6
+ } from '@smartsoft001/domain-core';
7
+
8
+ import { MongoConfig } from './mongo.config';
9
+ import { MongoUnitOfWork } from './mongo.unitofwork';
10
+ import { MongoAttachmentRepository } from './repositories/attachment.repository';
11
+ import { MongoItemRepository } from './repositories/item.repository';
12
+
13
+ export class MongoModule {
14
+ static forRoot(config: MongoConfig): DynamicModule {
15
+ const providers = [
16
+ { provide: MongoConfig, useValue: config },
17
+ { provide: IItemRepository, useClass: MongoItemRepository },
18
+ { provide: IAttachmentRepository, useClass: MongoAttachmentRepository },
19
+ { provide: IUnitOfWork, useClass: MongoUnitOfWork },
20
+ ];
21
+
22
+ return {
23
+ module: MongoModule,
24
+ providers: providers,
25
+ exports: providers,
26
+ };
27
+ }
28
+ }
@@ -0,0 +1,84 @@
1
+ import { jest } from '@jest/globals';
2
+ import { MongoConfig } from '@smartsoft001/mongo';
3
+ import { MongoClient, ClientSession, TransactionOptions } from 'mongodb';
4
+
5
+ import { IMongoTransaction, MongoUnitOfWork } from './mongo.unitofwork';
6
+
7
+ const mockData: {
8
+ config: MongoConfig;
9
+ url: string;
10
+ } = {
11
+ config: {
12
+ host: 'host',
13
+ port: 4200,
14
+ database: 'db-string',
15
+ },
16
+ url: 'mongodb://host:4200?authSource=db-string',
17
+ };
18
+
19
+ describe('shared-mongo: MongoUnitOfWork scope function', () => {
20
+ let mockClient: Partial<MongoClient>;
21
+ let mockSession: Partial<ClientSession>;
22
+ let model: MongoUnitOfWork;
23
+
24
+ beforeEach(() => {
25
+ model = new MongoUnitOfWork(mockData.config);
26
+
27
+ mockSession = {
28
+ withTransaction: jest.fn(
29
+ async <T>(
30
+ callback: (session: ClientSession) => Promise<T>,
31
+ options: TransactionOptions,
32
+ ): Promise<T> => {
33
+ return callback(mockSession as ClientSession); // Pass the mocked session
34
+ },
35
+ ) as ClientSession['withTransaction'],
36
+ endSession: jest.fn(() => Promise.resolve()),
37
+ };
38
+
39
+ mockClient = {
40
+ startSession: jest.fn(() => mockSession as ClientSession),
41
+ close: jest.fn(() => Promise.resolve()),
42
+ };
43
+
44
+ jest
45
+ .spyOn(MongoClient, 'connect')
46
+ .mockResolvedValue(mockClient as MongoClient);
47
+ });
48
+
49
+ afterEach(() => {
50
+ jest.restoreAllMocks();
51
+ });
52
+
53
+ it('should execute the definition within a transaction', async () => {
54
+ const mockDefinition = jest.fn(async (transaction: IMongoTransaction) => {
55
+ expect(transaction.session).toBe(mockSession);
56
+ expect(transaction.connection).toBe(mockClient);
57
+ });
58
+
59
+ await expect(model.scope(mockDefinition)).resolves.not.toThrow();
60
+
61
+ expect(MongoClient.connect).toHaveBeenCalledWith(mockData.url);
62
+ expect(mockClient.startSession).toHaveBeenCalled();
63
+ expect(mockSession.withTransaction).toHaveBeenCalled();
64
+ expect(mockDefinition).toHaveBeenCalled();
65
+ expect(mockSession.endSession).toHaveBeenCalled();
66
+ expect(mockClient.close).toHaveBeenCalled();
67
+ });
68
+
69
+ it('should throw an error if the definition throws an error', async () => {
70
+ const mockError = new Error('Test error');
71
+ const mockDefinition = jest.fn(async () => {
72
+ throw mockError;
73
+ });
74
+
75
+ await expect(model.scope(mockDefinition)).rejects.toThrow(mockError);
76
+
77
+ expect(MongoClient.connect).toHaveBeenCalledWith(mockData.url);
78
+ expect(mockClient.startSession).toHaveBeenCalled();
79
+ expect(mockSession.withTransaction).toHaveBeenCalled();
80
+ expect(mockDefinition).toHaveBeenCalled();
81
+ expect(mockSession.endSession).toHaveBeenCalled();
82
+ expect(mockClient.close).toHaveBeenCalled();
83
+ });
84
+ });
@@ -0,0 +1,58 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { ClientSession, MongoClient, TransactionOptions } from 'mongodb';
3
+
4
+ import { ITransaction, IUnitOfWork } from '@smartsoft001/domain-core';
5
+ import { MongoConfig } from '@smartsoft001/mongo';
6
+
7
+ import { getMongoUrl } from './mongo.utils';
8
+
9
+ export interface IMongoTransaction extends ITransaction {
10
+ session: ClientSession;
11
+ }
12
+
13
+ @Injectable()
14
+ export class MongoUnitOfWork extends IUnitOfWork {
15
+ constructor(private config: MongoConfig) {
16
+ super();
17
+ }
18
+
19
+ async scope(
20
+ definition: (transaction: ITransaction) => Promise<void>,
21
+ ): Promise<void> {
22
+ const client = await MongoClient.connect(this.getUrl());
23
+
24
+ // Step 1: Start a Client Session
25
+ const session = client.startSession();
26
+
27
+ // Step 2: Optional. Define options to use for the transaction
28
+ const transactionOptions: TransactionOptions = {
29
+ readPreference: 'primary',
30
+ readConcern: { level: 'local' },
31
+ writeConcern: { w: 'majority' },
32
+ };
33
+
34
+ // Step 3: Use withTransaction to start a transaction, execute the callback, and commit (or abort on error)
35
+ // Note: The callback for withTransaction MUST be async and/or return a Promise.
36
+
37
+ let error = null;
38
+ try {
39
+ await session.withTransaction(async () => {
40
+ await definition({
41
+ session,
42
+ connection: client,
43
+ } as IMongoTransaction);
44
+ }, transactionOptions);
45
+ } catch (e) {
46
+ error = e;
47
+ } finally {
48
+ await session.endSession();
49
+ await client.close();
50
+ }
51
+
52
+ if (error) throw error;
53
+ }
54
+
55
+ private getUrl(): string {
56
+ return getMongoUrl(this.config);
57
+ }
58
+ }
@@ -0,0 +1,54 @@
1
+ import { MongoConfig } from '@smartsoft001/mongo';
2
+
3
+ import { getMongoUrl } from './mongo.utils';
4
+
5
+ const mockData: (MongoConfig & { result: string; testName: string })[] = [
6
+ {
7
+ host: 'host',
8
+ port: 4200,
9
+ database: 'db-string',
10
+ result: 'mongodb://host:4200?authSource=db-string',
11
+ testName: 'Basic',
12
+ },
13
+ {
14
+ host: 'ondigitalocean.com',
15
+ port: 4200,
16
+ database: 'db-string',
17
+ result:
18
+ 'mongodb+srv://ondigitalocean.com:4200?authSource=db-string&tls=true',
19
+ testName: 'Specific host',
20
+ },
21
+ {
22
+ host: 'host',
23
+ port: 4200,
24
+ database: 'db-string',
25
+ result: 'mongodb://host:4200?authSource=db-string',
26
+ testName: 'Only username',
27
+ username: 'username',
28
+ },
29
+ {
30
+ host: 'host',
31
+ port: 4200,
32
+ database: 'db-string',
33
+ result: 'mongodb://host:4200?authSource=db-string',
34
+ testName: 'Only password',
35
+ password: 'password',
36
+ },
37
+ {
38
+ host: 'host',
39
+ port: 4200,
40
+ database: 'db-string',
41
+ result: 'mongodb://username:password@host:4200?authSource=db-string',
42
+ testName: 'Username and Password',
43
+ username: 'username',
44
+ password: 'password',
45
+ },
46
+ ];
47
+
48
+ describe('shared-mongo: utils getMongoUrl function', () => {
49
+ for (const data of mockData) {
50
+ it(data.testName, () => {
51
+ expect(getMongoUrl(data)).toBe(data.result);
52
+ });
53
+ }
54
+ });
@@ -0,0 +1,17 @@
1
+ import { MongoConfig } from '@smartsoft001/mongo';
2
+
3
+ export function getMongoUrl(config: MongoConfig): string {
4
+ let url;
5
+ if (config.username && config.password)
6
+ url = `mongodb://${config.username}:${config.password}@${config.host}:${config.port}`;
7
+ else url = `mongodb://${config.host}:${config.port}`;
8
+
9
+ url = url + '?authSource=' + config.database;
10
+
11
+ if (config.host.indexOf('ondigitalocean.com') > -1) {
12
+ url = url.replace('mongodb://', 'mongodb+srv://');
13
+ url = url + '&tls=true';
14
+ }
15
+
16
+ return url;
17
+ }
@@ -0,0 +1,249 @@
1
+ import { MongoClient, GridFSBucket } from 'mongodb';
2
+ import { Readable } from 'stream';
3
+
4
+ import { IEntity } from '@smartsoft001/domain-core';
5
+
6
+ import { MongoAttachmentRepository } from './attachment.repository';
7
+
8
+ jest.mock('mongodb', () => {
9
+ const mockOpenDownloadStream = jest.fn();
10
+ const mockDelete = jest.fn();
11
+ const mockClose = jest.fn();
12
+ const mockToArray = jest.fn();
13
+ const mockFind = jest.fn().mockReturnValue({
14
+ toArray: mockToArray, // Return a mock cursor with toArray method
15
+ });
16
+
17
+ const mockWriteStream = {
18
+ on: jest.fn((event, callback) => {
19
+ if (event === 'finish') {
20
+ setTimeout(callback, 0); // Simulate the 'finish' event after a timeout
21
+ } else if (event === 'error') {
22
+ // You can use this to simulate an error event in your test
23
+ mockWriteStream._errorCallback = callback;
24
+ }
25
+ return mockWriteStream; // Return this for chaining
26
+ }),
27
+ pipe: jest.fn(function () {
28
+ return this; // Make sure pipe is chainable
29
+ }),
30
+ once: jest.fn((event, callback) => {
31
+ // Handle 'once' event similarly to 'on'
32
+ if (event === 'finish') {
33
+ setTimeout(callback, 0); // Simulate the 'finish' event after a timeout
34
+ }
35
+ return mockWriteStream; // Chainable
36
+ }),
37
+ emit: jest.fn((event, ...args) => {
38
+ if (event === 'finish') {
39
+ setTimeout(() => {
40
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
41
+ mockWriteStream.on('finish', () => {
42
+ // Simulate the finish event
43
+ });
44
+ }, 0);
45
+ }
46
+ return true; // Return true for emit as a valid event
47
+ }),
48
+ removeListener: jest.fn((event, callback) => {
49
+ // Just return the mock itself for chaining
50
+ return mockWriteStream;
51
+ }),
52
+ _errorCallback: null, // Used to simulate an error when you trigger it manually
53
+ };
54
+ const mockOpenUploadStreamWithId = jest.fn().mockReturnValue(mockWriteStream);
55
+ const mockGridFSBucket = jest.fn().mockImplementation(() => ({
56
+ delete: mockDelete,
57
+ openDownloadStream: mockOpenDownloadStream,
58
+ find: mockFind,
59
+ openUploadStreamWithId: mockOpenUploadStreamWithId,
60
+ }));
61
+
62
+ const mockDb = jest.fn().mockReturnValue({
63
+ GridFSBucket: mockGridFSBucket,
64
+ });
65
+
66
+ const mockConnect = jest.fn().mockResolvedValue({
67
+ db: jest.fn(() => mockDb()),
68
+ close: mockClose,
69
+ });
70
+
71
+ return {
72
+ MongoClient: {
73
+ connect: mockConnect,
74
+ },
75
+ GridFSBucket: mockGridFSBucket,
76
+ __mockDelete: mockDelete, // Expose the mockDelete for assertions
77
+ __mockClose: mockClose, // Expose the mockClose for assertions
78
+ __mockOpenDownloadStream: mockOpenDownloadStream, // Expose the mockOpenDownloadStream for assertions
79
+ __mockToArray: mockToArray, // Expose the mockToArray for assertions
80
+ __mockWriteStream: mockWriteStream, // Expose the mockWriteStream for assertions
81
+ __mockOpenUploadStreamWithId: mockOpenUploadStreamWithId, // Expose the mockOpenUploadStreamWithId for assertions
82
+ };
83
+ });
84
+
85
+ describe('shared-mongo: MongoAttachmentRepository', () => {
86
+ let model: MongoAttachmentRepository<IEntity<string>>;
87
+ const mockConfig = {
88
+ database: 'testDB',
89
+ host: 'host',
90
+ port: 4200,
91
+ collection: 'testCollection',
92
+ };
93
+
94
+ const data = {
95
+ id: 'test-id',
96
+ fileName: 'test-file.txt',
97
+ stream: new Readable(),
98
+ mimeType: 'text/plain',
99
+ encoding: 'utf8',
100
+ };
101
+
102
+ const options = {
103
+ streamCallback: jest.fn(),
104
+ };
105
+
106
+ // Declare mockDelete here to access in the test scope
107
+ const mongodbMock = jest.requireMock('mongodb');
108
+ const mockDelete = mongodbMock.__mockDelete;
109
+ const mockClose = mongodbMock.__mockClose;
110
+ const mockOpenDownloadStream = mongodbMock.__mockOpenDownloadStream;
111
+ const mockToArray = mongodbMock.__mockToArray;
112
+ const mockWriteStream = mongodbMock.__mockWriteStream;
113
+ const mockOpenUploadStreamWithId = mongodbMock.__mockOpenUploadStreamWithId;
114
+ const mockStream = new Readable();
115
+ mockStream._read = jest.fn();
116
+
117
+ beforeEach(() => {
118
+ model = new MongoAttachmentRepository<IEntity<string>>(mockConfig); // Pass required config
119
+ });
120
+
121
+ it('delete() should delete a file from GridFSBucket and close the client', async () => {
122
+ const id = 'test-id';
123
+ const mockUrl = 'mock-url';
124
+ jest.spyOn(model as any, 'getUrl').mockImplementation(() => mockUrl);
125
+
126
+ await model.delete(id);
127
+
128
+ // Assertions
129
+ expect(MongoClient.connect).toHaveBeenCalledWith(mockUrl);
130
+ expect(GridFSBucket).toHaveBeenCalledWith(expect.anything(), {
131
+ bucketName: mockConfig.collection,
132
+ });
133
+ expect(mockDelete).toHaveBeenCalledWith(id);
134
+ expect(mockClose).toHaveBeenCalled();
135
+ });
136
+
137
+ it('getStream() should return a readable stream when getStream is called', async () => {
138
+ const id = 'test-id';
139
+ const options = { start: 0, end: 100 };
140
+ const mockUrl = 'mock-url';
141
+ jest.spyOn(model as any, 'getUrl').mockImplementation(() => mockUrl);
142
+
143
+ // Mock the return value of openDownloadStream to return a mock Readable stream
144
+ const mockStream = new Readable();
145
+ mockStream._read = jest.fn(); // Implement the _read method to make it a valid Readable stream
146
+ mockOpenDownloadStream.mockReturnValue(mockStream);
147
+
148
+ const stream = await model.getStream(id, options);
149
+
150
+ // Assertions
151
+ expect(MongoClient.connect).toHaveBeenCalledWith(mockUrl); // Ensure MongoClient.connect is called with the correct URL
152
+ expect(mockOpenDownloadStream).toHaveBeenCalledWith(id, options); // Check if openDownloadStream was called with the right parameters
153
+ expect(stream).toBe(mockStream); // Ensure the returned stream is the one we mocked
154
+ });
155
+
156
+ it('getInfo() should return file info if the file exists', async () => {
157
+ const id = 'test-id';
158
+ const mockFileData = [
159
+ {
160
+ filename: 'test-file.txt',
161
+ contentType: 'text/plain',
162
+ length: 1024,
163
+ },
164
+ ];
165
+ const mockUrl = 'mock-url';
166
+
167
+ mockToArray.mockResolvedValueOnce(mockFileData); // Mock toArray to return mock file data
168
+
169
+ const result = await model.getInfo(id);
170
+
171
+ expect(MongoClient.connect).toHaveBeenCalledWith(mockUrl); // Ensure MongoClient.connect is called with the correct URL
172
+ expect(result).toEqual({
173
+ fileName: 'test-file.txt',
174
+ contentType: 'text/plain',
175
+ length: 1024,
176
+ }); // Check if the correct file info is returned
177
+ });
178
+
179
+ it('getInfo() should return null if no file is found', async () => {
180
+ const id = 'non-existent-id';
181
+
182
+ // Mock the return value of toArray to return an empty array
183
+ (MongoClient.connect as jest.Mock).mockResolvedValueOnce({
184
+ db: jest.fn(() => ({
185
+ GridFSBucket: jest.fn().mockImplementation(() => ({
186
+ find: jest.fn().mockReturnValue({
187
+ toArray: jest.fn().mockResolvedValue([]), // Return mock file data
188
+ }),
189
+ })),
190
+ })),
191
+ close: jest.fn(),
192
+ });
193
+
194
+ const result = await model.getInfo(id);
195
+
196
+ expect(result).toBeNull(); // Expect null if no file is found
197
+ });
198
+
199
+ // it("upload() should upload file successfully", async () => {
200
+ // const mockUrl = "mock-url";
201
+ // jest.spyOn(model as any, "getUrl").mockImplementation(() => mockUrl);
202
+ //
203
+ // const mockStream = new Readable();
204
+ // mockStream._read = jest.fn();
205
+ //
206
+ // // Set up the stream to simulate data being piped
207
+ // data.stream = mockStream;
208
+ //
209
+ // const result = model.upload(data);
210
+ //
211
+ // // Ensure the MongoClient.connect method is called with the correct URL
212
+ // expect(MongoClient.connect).toHaveBeenCalledWith(mockUrl);
213
+ //
214
+ // // Wait for the upload promise to resolve
215
+ // await expect(result).resolves.toBeUndefined();
216
+ //
217
+ // // Ensure GridFSBucket and openUploadStreamWithId are called
218
+ // expect(GridFSBucket).toHaveBeenCalledWith(expect.anything(), {
219
+ // bucketName: mockConfig.collection,
220
+ // });
221
+ // expect(mockOpenUploadStreamWithId).toHaveBeenCalledWith(data.id, data.fileName, {
222
+ // contentType: data.mimeType,
223
+ // });
224
+ //
225
+ // // Ensure the stream callback is called if provided
226
+ // expect(options.streamCallback).toHaveBeenCalledWith(mockWriteStream);
227
+ //
228
+ // // Ensure data is piped into the write stream
229
+ // expect(mockStream.pipe).toHaveBeenCalledWith(mockWriteStream);
230
+ // });
231
+
232
+ // it("upload() should reject with error if upload fails", async () => {
233
+ // const mockUrl = "mock-url";
234
+ // jest.spyOn(model as any, "getUrl").mockImplementation(() => mockUrl);
235
+ //
236
+ // const result = model.upload({
237
+ // ...data,
238
+ // stream: mockStream
239
+ // });
240
+ //
241
+ // if (mockWriteStream._errorCallback) {
242
+ // mockWriteStream._errorCallback(new Error("Upload failed"));
243
+ // }
244
+ // // Ensure the upload rejects with the error
245
+ // // await expect(result.catch(e => e)).rejects.toThrowError("Upload failed");
246
+ //
247
+ // await expect(result).rejects.toThrowError("Upload failed");
248
+ // });
249
+ });