@digitaldefiance/express-suite-test-utils 1.0.8 → 1.0.10

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.
package/README.md CHANGED
@@ -97,12 +97,83 @@ it('should log message', async () => {
97
97
  });
98
98
  ```
99
99
 
100
+ ### Direct Log Mocks
101
+
102
+ Mock `fs.writeSync` for testing direct console output:
103
+
104
+ ```typescript
105
+ import { withDirectLogMocks, directLogContains, getDirectLogMessages } from '@digitaldefiance/express-suite-test-utils';
106
+ import * as fs from 'fs';
107
+
108
+ // Important: Mock fs module at module level before importing
109
+ jest.mock('fs', () => ({
110
+ ...jest.requireActual('fs'),
111
+ writeSync: jest.fn(),
112
+ }));
113
+
114
+ it('should capture direct writes to stdout', async () => {
115
+ await withDirectLogMocks({ mute: true }, async (spies) => {
116
+ const buffer = Buffer.from('hello world\n', 'utf8');
117
+ fs.writeSync(1, buffer); // stdout
118
+
119
+ expect(directLogContains(spies.writeSync, 1, 'hello', 'world')).toBe(true);
120
+ expect(getDirectLogMessages(spies.writeSync, 1)).toEqual(['hello world\n']);
121
+ });
122
+ });
123
+ ```
124
+
125
+ ### Mongoose Memory Database
126
+
127
+ In-memory MongoDB testing utilities using mongodb-memory-server:
128
+
129
+ ```typescript
130
+ import { connectMemoryDB, disconnectMemoryDB, clearMemoryDB } from '@digitaldefiance/express-suite-test-utils';
131
+ import { User } from './models/user';
132
+
133
+ describe('User model', () => {
134
+ beforeAll(async () => {
135
+ await connectMemoryDB();
136
+ });
137
+
138
+ afterAll(async () => {
139
+ await disconnectMemoryDB();
140
+ });
141
+
142
+ afterEach(async () => {
143
+ await clearMemoryDB();
144
+ });
145
+
146
+ it('should validate user schema', async () => {
147
+ const user = new User({ username: 'test', email: 'test@example.com' });
148
+ await user.validate(); // Real Mongoose validation!
149
+
150
+ await expect(async () => {
151
+ const invalid = new User({ username: 'ab' }); // too short
152
+ await invalid.validate();
153
+ }).rejects.toThrow();
154
+ });
155
+ });
156
+ ```
157
+
158
+ **Note:** Requires `mongoose` as a peer dependency and `mongodb-memory-server` as a dependency (already included).
159
+
100
160
  ## License
101
161
 
102
162
  MIT
103
163
 
104
164
  ## ChangeLog
105
165
 
166
+ ### v1.0.10
167
+
168
+ - Fix direct-log mocks to work with non-configurable fs.writeSync in newer Node.js versions
169
+ - Add comprehensive mongoose memory database testing utilities
170
+ - Fix memory mongoose connectMemoryDB
171
+
172
+
173
+ ### v1.0.9
174
+
175
+ - Add mongoose memory helpers
176
+
106
177
  ### v1.0.8
107
178
 
108
179
  - Add directLog mocks
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digitaldefiance/express-suite-test-utils",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Test utilities for Digital Defiance Express Suite",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -37,10 +37,15 @@
37
37
  "dependencies": {
38
38
  "@jest/globals": "^30.0.5",
39
39
  "@types/jest": "^30.0.0",
40
- "expect": "^30.0.5"
40
+ "expect": "^30.0.5",
41
+ "mongodb-memory-server": "^10.1.3"
42
+ },
43
+ "peerDependencies": {
44
+ "mongoose": "^8.0.0"
41
45
  },
42
46
  "devDependencies": {
43
- "@types/node": "^22.0.0"
47
+ "@types/node": "^22.0.0",
48
+ "mongoose": "^8.9.3"
44
49
  },
45
50
  "type": "commonjs"
46
51
  }
package/src/index.d.ts CHANGED
@@ -4,4 +4,5 @@ export * from './lib/direct-log';
4
4
  export * from './lib/localStorage-mock';
5
5
  export * from './lib/bson-mock';
6
6
  export * from './lib/react-mocks';
7
+ export * from './lib/mongoose-memory';
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../packages/digitaldefiance-express-suite-test-utils/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../packages/digitaldefiance-express-suite-test-utils/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC"}
package/src/index.js CHANGED
@@ -7,3 +7,4 @@ tslib_1.__exportStar(require("./lib/direct-log"), exports);
7
7
  tslib_1.__exportStar(require("./lib/localStorage-mock"), exports);
8
8
  tslib_1.__exportStar(require("./lib/bson-mock"), exports);
9
9
  tslib_1.__exportStar(require("./lib/react-mocks"), exports);
10
+ tslib_1.__exportStar(require("./lib/mongoose-memory"), exports);
@@ -2,7 +2,7 @@
2
2
  * Spies returned from withDirectLogMocks
3
3
  */
4
4
  export interface DirectLogSpies {
5
- writeSync: jest.SpyInstance;
5
+ writeSync: jest.Mock;
6
6
  }
7
7
  /**
8
8
  * Options for withDirectLogMocks
@@ -18,19 +18,21 @@ export interface WithDirectLogOptions {
18
18
  * Wrap a test body with fs.writeSync spy for directLog testing.
19
19
  * By default mutes output (does nothing on write).
20
20
  * The spy will capture calls with file descriptor and buffer.
21
+ *
22
+ * Note: Requires fs module to be mocked at module level with jest.mock('fs')
21
23
  */
22
24
  export declare function withDirectLogMocks<T = unknown>(options: WithDirectLogOptions, fn: (spies: DirectLogSpies) => Promise<T> | T): Promise<T>;
23
25
  /**
24
26
  * Helper to check if writeSync was called with a specific file descriptor and message.
25
- * @param spy The writeSync spy
27
+ * @param spy The writeSync mock
26
28
  * @param fd The file descriptor (1 for stdout, 2 for stderr)
27
29
  * @param needles Substrings to search for in the buffer content
28
30
  */
29
- export declare function directLogContains(spy: jest.SpyInstance, fd: number, ...needles: string[]): boolean;
31
+ export declare function directLogContains(spy: jest.Mock, fd: number, ...needles: string[]): boolean;
30
32
  /**
31
33
  * Get all messages written to a specific file descriptor.
32
- * @param spy The writeSync spy
34
+ * @param spy The writeSync mock
33
35
  * @param fd The file descriptor (1 for stdout, 2 for stderr)
34
36
  */
35
- export declare function getDirectLogMessages(spy: jest.SpyInstance, fd: number): string[];
37
+ export declare function getDirectLogMessages(spy: jest.Mock, fd: number): string[];
36
38
  //# sourceMappingURL=direct-log.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"direct-log.d.ts","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-express-suite-test-utils/src/lib/direct-log.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,GAAG,OAAO,EAClD,OAAO,EAAE,oBAAoB,EAC7B,EAAE,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAC5C,OAAO,CAAC,CAAC,CAAC,CAoBZ;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,IAAI,CAAC,WAAW,EACrB,EAAE,EAAE,MAAM,EACV,GAAG,OAAO,EAAE,MAAM,EAAE,GACnB,OAAO,CAWT;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,IAAI,CAAC,WAAW,EACrB,EAAE,EAAE,MAAM,GACT,MAAM,EAAE,CASV"}
1
+ {"version":3,"file":"direct-log.d.ts","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-express-suite-test-utils/src/lib/direct-log.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,GAAG,OAAO,EAClD,OAAO,EAAE,oBAAoB,EAC7B,EAAE,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GAC5C,OAAO,CAAC,CAAC,CAAC,CAgCZ;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,IAAI,CAAC,IAAI,EACd,EAAE,EAAE,MAAM,EACV,GAAG,OAAO,EAAE,MAAM,EAAE,GACnB,OAAO,CAWT;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,IAAI,CAAC,IAAI,EACd,EAAE,EAAE,MAAM,GACT,MAAM,EAAE,CASV"}
@@ -9,27 +9,42 @@ const fs = tslib_1.__importStar(require("fs"));
9
9
  * Wrap a test body with fs.writeSync spy for directLog testing.
10
10
  * By default mutes output (does nothing on write).
11
11
  * The spy will capture calls with file descriptor and buffer.
12
+ *
13
+ * Note: Requires fs module to be mocked at module level with jest.mock('fs')
12
14
  */
13
15
  async function withDirectLogMocks(options, fn) {
14
16
  const mute = options?.mute !== false; // default true
15
- const noop = () => undefined;
16
- const originalWriteSync = fs.writeSync;
17
- const writeSync = jest
18
- .spyOn(fs, 'writeSync')
19
- .mockImplementation(mute
20
- ? noop
21
- : originalWriteSync);
17
+ // Get the mocked writeSync function
18
+ const writeSync = fs.writeSync;
19
+ // Store previous mock implementation if any
20
+ const previousImpl = writeSync.getMockImplementation();
21
+ // Set implementation based on mute option
22
+ if (mute) {
23
+ writeSync.mockImplementation(() => undefined);
24
+ }
25
+ else {
26
+ // Pass through - requires original implementation to be available
27
+ writeSync.mockImplementation((...args) => {
28
+ // In test environment, we can't easily call the real fs.writeSync
29
+ // so we just no-op but track the calls
30
+ return args[1]?.length || 0;
31
+ });
32
+ }
22
33
  const spies = { writeSync };
23
34
  try {
24
35
  return await fn(spies);
25
36
  }
26
37
  finally {
27
- writeSync.mockRestore();
38
+ // Clear calls and restore previous implementation
39
+ writeSync.mockClear();
40
+ if (previousImpl) {
41
+ writeSync.mockImplementation(previousImpl);
42
+ }
28
43
  }
29
44
  }
30
45
  /**
31
46
  * Helper to check if writeSync was called with a specific file descriptor and message.
32
- * @param spy The writeSync spy
47
+ * @param spy The writeSync mock
33
48
  * @param fd The file descriptor (1 for stdout, 2 for stderr)
34
49
  * @param needles Substrings to search for in the buffer content
35
50
  */
@@ -46,7 +61,7 @@ function directLogContains(spy, fd, ...needles) {
46
61
  }
47
62
  /**
48
63
  * Get all messages written to a specific file descriptor.
49
- * @param spy The writeSync spy
64
+ * @param spy The writeSync mock
50
65
  * @param fd The file descriptor (1 for stdout, 2 for stderr)
51
66
  */
52
67
  function getDirectLogMessages(spy, fd) {
@@ -0,0 +1,18 @@
1
+ import { Connection } from 'mongoose';
2
+ /**
3
+ * Connect to in-memory MongoDB for testing
4
+ * @returns Object with both the connection and URI
5
+ */
6
+ export declare function connectMemoryDB(): Promise<{
7
+ connection: Connection;
8
+ uri: string;
9
+ }>;
10
+ /**
11
+ * Drop all collections and disconnect
12
+ */
13
+ export declare function disconnectMemoryDB(): Promise<void>;
14
+ /**
15
+ * Clear all collections without disconnecting
16
+ */
17
+ export declare function clearMemoryDB(): Promise<void>;
18
+ //# sourceMappingURL=mongoose-memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mongoose-memory.d.ts","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-express-suite-test-utils/src/lib/mongoose-memory.ts"],"names":[],"mappings":"AACA,OAAiB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAKhD;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC;IAAE,UAAU,EAAE,UAAU,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAsBxF;AAED;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAWxD;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAOnD"}
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.connectMemoryDB = connectMemoryDB;
4
+ exports.disconnectMemoryDB = disconnectMemoryDB;
5
+ exports.clearMemoryDB = clearMemoryDB;
6
+ const tslib_1 = require("tslib");
7
+ const mongodb_memory_server_1 = require("mongodb-memory-server");
8
+ const mongoose_1 = tslib_1.__importDefault(require("mongoose"));
9
+ let mongoServer;
10
+ let connection;
11
+ /**
12
+ * Connect to in-memory MongoDB for testing
13
+ * @returns Object with both the connection and URI
14
+ */
15
+ async function connectMemoryDB() {
16
+ // If mongoose is connected but we don't have our server, disconnect first
17
+ if (mongoose_1.default.connection.readyState !== 0 && !mongoServer) {
18
+ await mongoose_1.default.disconnect();
19
+ connection = undefined;
20
+ }
21
+ // Create new server if needed
22
+ if (!mongoServer) {
23
+ mongoServer = await mongodb_memory_server_1.MongoMemoryServer.create();
24
+ }
25
+ const uri = mongoServer.getUri();
26
+ // Connect if not already connected
27
+ if (mongoose_1.default.connection.readyState === 0) {
28
+ await mongoose_1.default.connect(uri);
29
+ }
30
+ connection = mongoose_1.default.connection;
31
+ return { connection, uri };
32
+ }
33
+ /**
34
+ * Drop all collections and disconnect
35
+ */
36
+ async function disconnectMemoryDB() {
37
+ if (connection) {
38
+ await connection.dropDatabase();
39
+ await mongoose_1.default.disconnect();
40
+ connection = undefined;
41
+ }
42
+ if (mongoServer) {
43
+ await mongoServer.stop();
44
+ mongoServer = undefined;
45
+ }
46
+ }
47
+ /**
48
+ * Clear all collections without disconnecting
49
+ */
50
+ async function clearMemoryDB() {
51
+ if (connection) {
52
+ const collections = connection.collections;
53
+ for (const key in collections) {
54
+ await collections[key].deleteMany({});
55
+ }
56
+ }
57
+ }