@digitaldefiance/express-suite-test-utils 1.0.9 → 1.0.11

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,261 @@ 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
+
160
+ ## Testing Approach
161
+
162
+ This package provides comprehensive testing utilities for Express Suite projects, including custom Jest matchers, console mocks, database helpers, and more.
163
+
164
+ ### Test Utilities Overview
165
+
166
+ **Custom Matchers**: `toThrowType` for type-safe error testing
167
+ **Console Mocks**: Mock and spy on console methods
168
+ **Direct Log Mocks**: Mock `fs.writeSync` for stdout/stderr testing
169
+ **Database Helpers**: MongoDB Memory Server integration
170
+ **React Mocks**: Mock React components and hooks
171
+
172
+ ### Usage Patterns
173
+
174
+ #### Using toThrowType Matcher
175
+
176
+ ```typescript
177
+ import '@digitaldefiance/express-suite-test-utils';
178
+
179
+ class CustomError extends Error {
180
+ constructor(public code: number) {
181
+ super('Custom error');
182
+ }
183
+ }
184
+
185
+ describe('Error Testing', () => {
186
+ it('should throw specific error type', () => {
187
+ expect(() => {
188
+ throw new CustomError(404);
189
+ }).toThrowType(CustomError);
190
+ });
191
+
192
+ it('should validate error properties', () => {
193
+ expect(() => {
194
+ throw new CustomError(404);
195
+ }).toThrowType(CustomError, (error) => {
196
+ expect(error.code).toBe(404);
197
+ });
198
+ });
199
+ });
200
+ ```
201
+
202
+ #### Using Console Mocks
203
+
204
+ ```typescript
205
+ import { withConsoleMocks, spyContains } from '@digitaldefiance/express-suite-test-utils';
206
+
207
+ describe('Console Output', () => {
208
+ it('should capture console.log', async () => {
209
+ await withConsoleMocks({ mute: true }, async (spies) => {
210
+ console.log('test message');
211
+
212
+ expect(spies.log).toHaveBeenCalledWith('test message');
213
+ expect(spyContains(spies.log, 'test', 'message')).toBe(true);
214
+ });
215
+ });
216
+
217
+ it('should capture console.error', async () => {
218
+ await withConsoleMocks({ mute: true }, async (spies) => {
219
+ console.error('error message');
220
+
221
+ expect(spies.error).toHaveBeenCalledWith('error message');
222
+ });
223
+ });
224
+ });
225
+ ```
226
+
227
+ #### Using Direct Log Mocks
228
+
229
+ ```typescript
230
+ import { withDirectLogMocks, directLogContains, getDirectLogMessages } from '@digitaldefiance/express-suite-test-utils';
231
+ import * as fs from 'fs';
232
+
233
+ // Mock fs at module level
234
+ jest.mock('fs', () => ({
235
+ ...jest.requireActual('fs'),
236
+ writeSync: jest.fn(),
237
+ }));
238
+
239
+ describe('Direct Logging', () => {
240
+ it('should capture stdout writes', async () => {
241
+ await withDirectLogMocks({ mute: true }, async (spies) => {
242
+ const buffer = Buffer.from('hello world\n', 'utf8');
243
+ fs.writeSync(1, buffer); // stdout
244
+
245
+ expect(directLogContains(spies.writeSync, 1, 'hello', 'world')).toBe(true);
246
+ expect(getDirectLogMessages(spies.writeSync, 1)).toEqual(['hello world\n']);
247
+ });
248
+ });
249
+ });
250
+ ```
251
+
252
+ #### Using MongoDB Memory Server
253
+
254
+ ```typescript
255
+ import { connectMemoryDB, disconnectMemoryDB, clearMemoryDB } from '@digitaldefiance/express-suite-test-utils';
256
+ import { User } from './models/user';
257
+
258
+ describe('Database Tests', () => {
259
+ beforeAll(async () => {
260
+ await connectMemoryDB();
261
+ });
262
+
263
+ afterAll(async () => {
264
+ await disconnectMemoryDB();
265
+ });
266
+
267
+ afterEach(async () => {
268
+ await clearMemoryDB();
269
+ });
270
+
271
+ it('should validate user schema', async () => {
272
+ const user = new User({
273
+ username: 'test',
274
+ email: 'test@example.com'
275
+ });
276
+
277
+ await user.validate(); // Real Mongoose validation!
278
+ await user.save();
279
+
280
+ const found = await User.findOne({ username: 'test' });
281
+ expect(found).toBeDefined();
282
+ });
283
+
284
+ it('should reject invalid data', async () => {
285
+ const invalid = new User({ username: 'ab' }); // too short
286
+
287
+ await expect(invalid.validate()).rejects.toThrow();
288
+ });
289
+ });
290
+ ```
291
+
292
+ ### Testing Best Practices
293
+
294
+ 1. **Always clean up** after tests (disconnect DB, restore mocks)
295
+ 2. **Use memory database** for fast, isolated tests
296
+ 3. **Mock external dependencies** to avoid side effects
297
+ 4. **Test error conditions** with `toThrowType` matcher
298
+ 5. **Capture console output** when testing logging behavior
299
+
300
+ ### Cross-Package Testing
301
+
302
+ These utilities are designed to work seamlessly with all Express Suite packages:
303
+
304
+ ```typescript
305
+ import { connectMemoryDB, disconnectMemoryDB } from '@digitaldefiance/express-suite-test-utils';
306
+ import { Application } from '@digitaldefiance/node-express-suite';
307
+ import { UserService } from '@digitaldefiance/node-express-suite';
308
+
309
+ describe('Integration Tests', () => {
310
+ let app: Application;
311
+
312
+ beforeAll(async () => {
313
+ await connectMemoryDB();
314
+ app = new Application({
315
+ mongoUri: global.__MONGO_URI__,
316
+ jwtSecret: 'test-secret'
317
+ });
318
+ });
319
+
320
+ afterAll(async () => {
321
+ await app.stop();
322
+ await disconnectMemoryDB();
323
+ });
324
+
325
+ it('should create and find user', async () => {
326
+ const userService = new UserService(app);
327
+ const user = await userService.create({
328
+ username: 'alice',
329
+ email: 'alice@example.com'
330
+ });
331
+
332
+ const found = await userService.findByUsername('alice');
333
+ expect(found).toBeDefined();
334
+ });
335
+ });
336
+ ```
337
+
100
338
  ## License
101
339
 
102
340
  MIT
103
341
 
104
342
  ## ChangeLog
105
343
 
344
+ ### v1.0.11
345
+
346
+ - Fix mongoose to use @digitaldefiance/mongoose-types
347
+
348
+ ### v1.0.10
349
+
350
+ - Fix direct-log mocks to work with non-configurable fs.writeSync in newer Node.js versions
351
+ - Add comprehensive mongoose memory database testing utilities
352
+ - Fix memory mongoose connectMemoryDB
353
+
354
+
106
355
  ### v1.0.9
107
356
 
108
357
  - Add mongoose memory helpers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digitaldefiance/express-suite-test-utils",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "Test utilities for Digital Defiance Express Suite",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -1,8 +1,12 @@
1
- import { Connection } from 'mongoose';
1
+ import { Connection } from '@digitaldefiance/mongoose-types';
2
2
  /**
3
3
  * Connect to in-memory MongoDB for testing
4
+ * @returns Object with both the connection and URI
4
5
  */
5
- export declare function connectMemoryDB(): Promise<Connection>;
6
+ export declare function connectMemoryDB(): Promise<{
7
+ connection: Connection;
8
+ uri: string;
9
+ }>;
6
10
  /**
7
11
  * Drop all collections and disconnect
8
12
  */
@@ -1 +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;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,UAAU,CAAC,CAY3D;AAED;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAWxD;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAOnD"}
1
+ {"version":3,"file":"mongoose-memory.d.ts","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-express-suite-test-utils/src/lib/mongoose-memory.ts"],"names":[],"mappings":"AAAA,OAAiB,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAMvE;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC;IAC/C,UAAU,EAAE,UAAU,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC,CAsBD;AAED;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAWxD;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAOnD"}
@@ -4,22 +4,31 @@ exports.connectMemoryDB = connectMemoryDB;
4
4
  exports.disconnectMemoryDB = disconnectMemoryDB;
5
5
  exports.clearMemoryDB = clearMemoryDB;
6
6
  const tslib_1 = require("tslib");
7
+ const mongoose_types_1 = tslib_1.__importDefault(require("@digitaldefiance/mongoose-types"));
7
8
  const mongodb_memory_server_1 = require("mongodb-memory-server");
8
- const mongoose_1 = tslib_1.__importDefault(require("mongoose"));
9
9
  let mongoServer;
10
10
  let connection;
11
11
  /**
12
12
  * Connect to in-memory MongoDB for testing
13
+ * @returns Object with both the connection and URI
13
14
  */
14
15
  async function connectMemoryDB() {
15
- if (connection && connection.readyState === 1) {
16
- return connection;
16
+ // If mongoose is connected but we don't have our server, disconnect first
17
+ if (mongoose_types_1.default.connection.readyState !== 0 && !mongoServer) {
18
+ await mongoose_types_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();
17
24
  }
18
- mongoServer = await mongodb_memory_server_1.MongoMemoryServer.create();
19
25
  const uri = mongoServer.getUri();
20
- await mongoose_1.default.connect(uri);
21
- connection = mongoose_1.default.connection;
22
- return connection;
26
+ // Connect if not already connected
27
+ if (mongoose_types_1.default.connection.readyState === 0) {
28
+ await mongoose_types_1.default.connect(uri);
29
+ }
30
+ connection = mongoose_types_1.default.connection;
31
+ return { connection, uri };
23
32
  }
24
33
  /**
25
34
  * Drop all collections and disconnect
@@ -27,7 +36,7 @@ async function connectMemoryDB() {
27
36
  async function disconnectMemoryDB() {
28
37
  if (connection) {
29
38
  await connection.dropDatabase();
30
- await mongoose_1.default.disconnect();
39
+ await mongoose_types_1.default.disconnect();
31
40
  connection = undefined;
32
41
  }
33
42
  if (mongoServer) {