@friggframework/test 2.0.0--canary.548.c8ae0ca.0 → 2.0.0--canary.545.dba001a.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.
- package/index.js +20 -2
- package/jest-global-setup.js +13 -3
- package/jest-global-teardown.js +7 -2
- package/package.json +16 -5
- package/router-test-utils/index.js +535 -0
- package/router-test-utils/index.test.js +350 -0
package/index.js
CHANGED
|
@@ -5,7 +5,22 @@ const {
|
|
|
5
5
|
} = require('./override-environment');
|
|
6
6
|
const globalTeardown = require('./jest-global-teardown');
|
|
7
7
|
const globalSetup = require('./jest-global-setup');
|
|
8
|
-
const Authenticator = require('./Authenticator')
|
|
8
|
+
const Authenticator = require('./Authenticator');
|
|
9
|
+
|
|
10
|
+
// Router test utilities are loaded lazily to avoid jest.fn() errors during
|
|
11
|
+
// global setup (when Jest globals aren't available yet)
|
|
12
|
+
let _routerTestUtils = null;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Lazily load router test utilities (only when called from test context)
|
|
16
|
+
* @returns {Object} Router test utilities
|
|
17
|
+
*/
|
|
18
|
+
const getRouterTestUtils = () => {
|
|
19
|
+
if (!_routerTestUtils) {
|
|
20
|
+
_routerTestUtils = require('./router-test-utils');
|
|
21
|
+
}
|
|
22
|
+
return _routerTestUtils;
|
|
23
|
+
};
|
|
9
24
|
|
|
10
25
|
module.exports = {
|
|
11
26
|
TestMongo,
|
|
@@ -13,5 +28,8 @@ module.exports = {
|
|
|
13
28
|
restoreEnvironment,
|
|
14
29
|
globalTeardown,
|
|
15
30
|
globalSetup,
|
|
16
|
-
Authenticator
|
|
31
|
+
Authenticator,
|
|
32
|
+
// Router test utilities - use getRouterTestUtils() for lazy loading
|
|
33
|
+
getRouterTestUtils,
|
|
34
|
+
// Direct import path is available: require('@friggframework/test/router-test-utils')
|
|
17
35
|
};
|
package/jest-global-setup.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
const { TestMongo } = require('./mongodb');
|
|
2
|
-
const {overrideEnvironment} = require('./override-environment');
|
|
2
|
+
const { overrideEnvironment } = require('./override-environment');
|
|
3
3
|
|
|
4
4
|
module.exports = async function () {
|
|
5
5
|
if (!process.env.STAGE) {
|
|
6
6
|
overrideEnvironment({ STAGE: 'dev' });
|
|
7
7
|
}
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
|
|
9
|
+
// Only start MongoDB for integration tests
|
|
10
|
+
// Unit tests should not depend on MongoDB
|
|
11
|
+
const isIntegrationTest =
|
|
12
|
+
process.argv.includes('--group=integration') || process.env.TEST_TYPE === 'integration';
|
|
13
|
+
|
|
14
|
+
if (isIntegrationTest || (!process.argv.includes('--group=unit') && !process.env.TEST_TYPE)) {
|
|
15
|
+
global.testMongo = new TestMongo();
|
|
16
|
+
await global.testMongo.start();
|
|
17
|
+
} else {
|
|
18
|
+
console.log('Skipping MongoDB setup for unit tests');
|
|
19
|
+
}
|
|
10
20
|
};
|
package/jest-global-teardown.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
const { restoreEnvironment } = require('./override-environment')
|
|
1
|
+
const { restoreEnvironment } = require('./override-environment');
|
|
2
|
+
|
|
2
3
|
module.exports = async function () {
|
|
3
4
|
restoreEnvironment();
|
|
4
|
-
|
|
5
|
+
|
|
6
|
+
// Only stop MongoDB if it was started
|
|
7
|
+
if (global.testMongo) {
|
|
8
|
+
await global.testMongo.stop();
|
|
9
|
+
}
|
|
5
10
|
};
|
package/package.json
CHANGED
|
@@ -1,24 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/test",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0--canary.
|
|
4
|
+
"version": "2.0.0--canary.545.dba001a.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@babel/eslint-parser": "^7.18.9",
|
|
7
|
+
"@hapi/boom": "^10.0.1",
|
|
7
8
|
"eslint": "^8.22.0",
|
|
8
9
|
"eslint-config-prettier": "^8.5.0",
|
|
9
10
|
"eslint-plugin-json": "^3.1.0",
|
|
10
11
|
"eslint-plugin-markdown": "^3.0.0",
|
|
11
12
|
"eslint-plugin-no-only-tests": "^3.0.0",
|
|
12
13
|
"eslint-plugin-yaml": "^0.5.0",
|
|
14
|
+
"express": "^4.21.2",
|
|
13
15
|
"jest-runner-groups": "^2.2.0",
|
|
14
16
|
"mongodb-memory-server": "^8.9.0",
|
|
15
17
|
"open": "^8.4.2"
|
|
16
18
|
},
|
|
17
19
|
"devDependencies": {
|
|
18
|
-
"@friggframework/eslint-config": "2.0.0--canary.
|
|
19
|
-
"@friggframework/prettier-config": "2.0.0--canary.
|
|
20
|
+
"@friggframework/eslint-config": "2.0.0--canary.545.dba001a.0",
|
|
21
|
+
"@friggframework/prettier-config": "2.0.0--canary.545.dba001a.0",
|
|
20
22
|
"jest": "^29.7.0",
|
|
21
|
-
"prettier": "^2.7.1"
|
|
23
|
+
"prettier": "^2.7.1",
|
|
24
|
+
"supertest": "^7.2.2"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"supertest": ">=7.0.0"
|
|
28
|
+
},
|
|
29
|
+
"peerDependenciesMeta": {
|
|
30
|
+
"supertest": {
|
|
31
|
+
"optional": true
|
|
32
|
+
}
|
|
22
33
|
},
|
|
23
34
|
"scripts": {
|
|
24
35
|
"lint:fix": "prettier --write --loglevel error . && eslint . --fix",
|
|
@@ -39,5 +50,5 @@
|
|
|
39
50
|
"publishConfig": {
|
|
40
51
|
"access": "public"
|
|
41
52
|
},
|
|
42
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "dba001affb699d55b0684fa236af8a5f67ddf852"
|
|
43
54
|
}
|
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Router Test Utilities
|
|
3
|
+
* @description Shared test utilities for Express router testing in Frigg Framework
|
|
4
|
+
*
|
|
5
|
+
* These utilities reduce boilerplate and ensure consistency across router tests.
|
|
6
|
+
* Use these helpers to:
|
|
7
|
+
* - Create mock repositories with standard interfaces
|
|
8
|
+
* - Set up Express apps with authentication middleware
|
|
9
|
+
* - Handle Boom errors properly in test environments
|
|
10
|
+
* - Generate consistent test data
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const { createTestApp, createMockRepositories, mockData } = require('@friggframework/test/router-test-utils');
|
|
14
|
+
*
|
|
15
|
+
* describe('My Router', () => {
|
|
16
|
+
* let app, mocks;
|
|
17
|
+
*
|
|
18
|
+
* beforeEach(() => {
|
|
19
|
+
* mocks = createMockRepositories();
|
|
20
|
+
* app = createTestApp({ router: myRouter, mocks });
|
|
21
|
+
* });
|
|
22
|
+
* });
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const express = require('express');
|
|
26
|
+
const Boom = require('@hapi/boom');
|
|
27
|
+
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// Mock Data Generators
|
|
30
|
+
// ============================================================================
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Helper to safely get jest.fn() - returns a real mock in test context, noop otherwise
|
|
34
|
+
* @returns {Function} Jest mock function or noop
|
|
35
|
+
*/
|
|
36
|
+
const createMockFn = (implementation) => {
|
|
37
|
+
if (typeof jest !== 'undefined' && jest.fn) {
|
|
38
|
+
return implementation ? jest.fn(implementation) : jest.fn();
|
|
39
|
+
}
|
|
40
|
+
// Return a noop function with mock properties for non-Jest environments
|
|
41
|
+
const fn = implementation || (() => {});
|
|
42
|
+
fn.mockReturnValue = (val) => {
|
|
43
|
+
fn._mockReturnValue = val;
|
|
44
|
+
return fn;
|
|
45
|
+
};
|
|
46
|
+
fn.mockResolvedValue = (val) => {
|
|
47
|
+
fn._mockResolvedValue = val;
|
|
48
|
+
return fn;
|
|
49
|
+
};
|
|
50
|
+
fn.mockImplementation = (impl) => {
|
|
51
|
+
fn._mockImplementation = impl;
|
|
52
|
+
return fn;
|
|
53
|
+
};
|
|
54
|
+
return fn;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Default mock user object
|
|
59
|
+
* @type {Object}
|
|
60
|
+
*/
|
|
61
|
+
const createMockUser = (overrides = {}) => ({
|
|
62
|
+
id: 'user-123',
|
|
63
|
+
appUserId: 'app-user-123',
|
|
64
|
+
username: 'testuser',
|
|
65
|
+
email: 'test@example.com',
|
|
66
|
+
getId: createMockFn(() => overrides.id || 'user-123'),
|
|
67
|
+
...overrides,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Default mock credential object
|
|
72
|
+
* @type {Object}
|
|
73
|
+
*/
|
|
74
|
+
const createMockCredential = (overrides = {}) => ({
|
|
75
|
+
id: 'cred-123',
|
|
76
|
+
type: 'hubspot',
|
|
77
|
+
userId: 'user-123',
|
|
78
|
+
authIsValid: true,
|
|
79
|
+
status: 'AUTHORIZED',
|
|
80
|
+
externalId: 'ext-123',
|
|
81
|
+
entityCount: 2,
|
|
82
|
+
createdAt: '2025-01-25T10:00:00.000Z',
|
|
83
|
+
updatedAt: '2025-01-25T10:00:00.000Z',
|
|
84
|
+
data: {
|
|
85
|
+
access_token: 'test-access-token',
|
|
86
|
+
refresh_token: 'test-refresh-token',
|
|
87
|
+
},
|
|
88
|
+
...overrides,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Default mock entity object
|
|
93
|
+
* @type {Object}
|
|
94
|
+
*/
|
|
95
|
+
const createMockEntity = (overrides = {}) => ({
|
|
96
|
+
id: 'entity-123',
|
|
97
|
+
entityType: 'ACCOUNT',
|
|
98
|
+
credential: 'cred-123',
|
|
99
|
+
credentialId: 'cred-123',
|
|
100
|
+
userId: 'user-123',
|
|
101
|
+
externalId: 'ext-account-123',
|
|
102
|
+
name: 'Test Account',
|
|
103
|
+
authIsValid: true,
|
|
104
|
+
type: 'hubspot',
|
|
105
|
+
...overrides,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Default mock integration object
|
|
110
|
+
* @type {Object}
|
|
111
|
+
*/
|
|
112
|
+
const createMockIntegration = (overrides = {}) => ({
|
|
113
|
+
id: 'integration-123',
|
|
114
|
+
name: 'Test Integration',
|
|
115
|
+
userId: 'user-123',
|
|
116
|
+
config: {},
|
|
117
|
+
entities: ['entity-123'],
|
|
118
|
+
createdAt: '2025-01-25T10:00:00.000Z',
|
|
119
|
+
updatedAt: '2025-01-25T10:00:00.000Z',
|
|
120
|
+
...overrides,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Default mock module definition object
|
|
125
|
+
* @type {Object}
|
|
126
|
+
*/
|
|
127
|
+
const createMockModuleDefinition = (overrides = {}) => {
|
|
128
|
+
const defaults = {
|
|
129
|
+
moduleName: 'hubspot',
|
|
130
|
+
definition: {
|
|
131
|
+
getDisplayName: () => overrides.displayName || 'HubSpot',
|
|
132
|
+
getDescription: () => overrides.description || 'Connect to HubSpot CRM',
|
|
133
|
+
getAuthType: () => overrides.authType || 'oauth2',
|
|
134
|
+
getAuthStepCount: () => overrides.stepCount || 1,
|
|
135
|
+
getCapabilities: () => overrides.capabilities || ['contacts', 'companies', 'deals'],
|
|
136
|
+
getAuthRequirementsForStep: jest.fn().mockResolvedValue({
|
|
137
|
+
type: overrides.authType || 'oauth2',
|
|
138
|
+
data: {
|
|
139
|
+
url: 'https://app.hubspot.com/oauth/authorize?client_id=test',
|
|
140
|
+
scopes: ['crm.objects.contacts.read', 'crm.objects.companies.read'],
|
|
141
|
+
},
|
|
142
|
+
}),
|
|
143
|
+
processAuthorizationStep: jest.fn(),
|
|
144
|
+
},
|
|
145
|
+
apiClass: jest.fn(),
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
...defaults,
|
|
150
|
+
...overrides,
|
|
151
|
+
definition: {
|
|
152
|
+
...defaults.definition,
|
|
153
|
+
...(overrides.definition || {}),
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Collection of mock data generators
|
|
160
|
+
*/
|
|
161
|
+
const mockData = {
|
|
162
|
+
createMockUser,
|
|
163
|
+
createMockCredential,
|
|
164
|
+
createMockEntity,
|
|
165
|
+
createMockIntegration,
|
|
166
|
+
createMockModuleDefinition,
|
|
167
|
+
|
|
168
|
+
// Pre-created instances for quick use
|
|
169
|
+
user: createMockUser(),
|
|
170
|
+
credential: createMockCredential(),
|
|
171
|
+
entity: createMockEntity(),
|
|
172
|
+
integration: createMockIntegration(),
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// ============================================================================
|
|
176
|
+
// Mock Repository Factories
|
|
177
|
+
// ============================================================================
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Create a mock user repository with standard interface
|
|
181
|
+
* @param {Object} overrides - Method overrides
|
|
182
|
+
* @returns {Object} Mock user repository
|
|
183
|
+
*/
|
|
184
|
+
const createMockUserRepository = (overrides = {}) => ({
|
|
185
|
+
findById: jest.fn().mockResolvedValue(mockData.user),
|
|
186
|
+
findOne: jest.fn().mockResolvedValue(mockData.user),
|
|
187
|
+
findByToken: jest.fn().mockResolvedValue(mockData.user),
|
|
188
|
+
getSessionToken: jest.fn().mockResolvedValue({ user: 'user-123', token: 'valid-token' }),
|
|
189
|
+
findIndividualUserById: jest.fn().mockResolvedValue(mockData.user),
|
|
190
|
+
findOrganizationUserById: jest.fn().mockResolvedValue(null),
|
|
191
|
+
findByEmail: jest.fn().mockResolvedValue(mockData.user),
|
|
192
|
+
findUserById: jest.fn().mockResolvedValue(mockData.user),
|
|
193
|
+
save: jest.fn(),
|
|
194
|
+
update: jest.fn(),
|
|
195
|
+
...overrides,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Create a mock credential repository with standard interface
|
|
200
|
+
* @param {Object} overrides - Method overrides
|
|
201
|
+
* @returns {Object} Mock credential repository
|
|
202
|
+
*/
|
|
203
|
+
const createMockCredentialRepository = (overrides = {}) => ({
|
|
204
|
+
findById: jest.fn(),
|
|
205
|
+
findByIdForUser: jest.fn(),
|
|
206
|
+
findCredential: jest.fn().mockResolvedValue([]),
|
|
207
|
+
findCredentialById: jest.fn(),
|
|
208
|
+
save: jest.fn(),
|
|
209
|
+
update: jest.fn(),
|
|
210
|
+
updateCredential: jest.fn(),
|
|
211
|
+
deleteCredentialById: jest.fn().mockResolvedValue({ deletedCount: 1 }),
|
|
212
|
+
...overrides,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Create a mock module repository with standard interface
|
|
217
|
+
* @param {Object} overrides - Method overrides
|
|
218
|
+
* @returns {Object} Mock module repository
|
|
219
|
+
*/
|
|
220
|
+
const createMockModuleRepository = (overrides = {}) => ({
|
|
221
|
+
findById: jest.fn(),
|
|
222
|
+
findByIdForUser: jest.fn(),
|
|
223
|
+
findByUserId: jest.fn().mockResolvedValue([]),
|
|
224
|
+
findByUserIdAndType: jest.fn(),
|
|
225
|
+
findModuleById: jest.fn(),
|
|
226
|
+
save: jest.fn(),
|
|
227
|
+
update: jest.fn(),
|
|
228
|
+
...overrides,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Create a mock integration repository with standard interface
|
|
233
|
+
* @param {Object} overrides - Method overrides
|
|
234
|
+
* @returns {Object} Mock integration repository
|
|
235
|
+
*/
|
|
236
|
+
const createMockIntegrationRepository = (overrides = {}) => ({
|
|
237
|
+
findById: jest.fn(),
|
|
238
|
+
findByIdForUser: jest.fn(),
|
|
239
|
+
findByUserId: jest.fn().mockResolvedValue([]),
|
|
240
|
+
save: jest.fn(),
|
|
241
|
+
update: jest.fn(),
|
|
242
|
+
delete: jest.fn(),
|
|
243
|
+
...overrides,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Create a mock authorization session repository
|
|
248
|
+
* @param {Object} overrides - Method overrides
|
|
249
|
+
* @returns {Object} Mock authorization session repository
|
|
250
|
+
*/
|
|
251
|
+
const createMockAuthorizationSessionRepository = (overrides = {}) => ({
|
|
252
|
+
findBySessionId: jest.fn(),
|
|
253
|
+
create: jest.fn(),
|
|
254
|
+
update: jest.fn(),
|
|
255
|
+
delete: jest.fn(),
|
|
256
|
+
...overrides,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Create all mock repositories at once
|
|
261
|
+
* @param {Object} overrides - Per-repository overrides
|
|
262
|
+
* @returns {Object} All mock repositories
|
|
263
|
+
*/
|
|
264
|
+
const createMockRepositories = (overrides = {}) => ({
|
|
265
|
+
userRepository: createMockUserRepository(overrides.user),
|
|
266
|
+
credentialRepository: createMockCredentialRepository(overrides.credential),
|
|
267
|
+
moduleRepository: createMockModuleRepository(overrides.module),
|
|
268
|
+
integrationRepository: createMockIntegrationRepository(overrides.integration),
|
|
269
|
+
authorizationSessionRepository: createMockAuthorizationSessionRepository(overrides.authorizationSession),
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// ============================================================================
|
|
273
|
+
// Mock API Requester
|
|
274
|
+
// ============================================================================
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Create a mock API requester for proxy tests
|
|
278
|
+
* @param {Object} overrides - Method overrides
|
|
279
|
+
* @returns {Object} Mock API requester
|
|
280
|
+
*/
|
|
281
|
+
const createMockApiRequester = (overrides = {}) => ({
|
|
282
|
+
request: jest.fn().mockResolvedValue({
|
|
283
|
+
status: 200,
|
|
284
|
+
headers: { 'content-type': 'application/json' },
|
|
285
|
+
data: { success: true },
|
|
286
|
+
}),
|
|
287
|
+
_get: jest.fn(),
|
|
288
|
+
_post: jest.fn(),
|
|
289
|
+
_put: jest.fn(),
|
|
290
|
+
_patch: jest.fn(),
|
|
291
|
+
_delete: jest.fn(),
|
|
292
|
+
addAuthHeaders: jest.fn().mockResolvedValue({}),
|
|
293
|
+
...overrides,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Create a mock module factory
|
|
298
|
+
* @param {Object} apiRequester - Mock API requester to use
|
|
299
|
+
* @returns {Object} Mock module factory
|
|
300
|
+
*/
|
|
301
|
+
const createMockModuleFactory = (apiRequester = createMockApiRequester()) => ({
|
|
302
|
+
getModuleInstance: jest.fn().mockResolvedValue({
|
|
303
|
+
api: apiRequester,
|
|
304
|
+
}),
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// ============================================================================
|
|
308
|
+
// Express App Setup
|
|
309
|
+
// ============================================================================
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Boom error handler middleware for Express
|
|
313
|
+
* Converts Boom errors to proper HTTP responses
|
|
314
|
+
* @param {Error} err - Error object
|
|
315
|
+
* @param {Object} req - Express request
|
|
316
|
+
* @param {Object} res - Express response
|
|
317
|
+
* @param {Function} next - Next middleware
|
|
318
|
+
*/
|
|
319
|
+
const boomErrorHandler = (err, req, res, next) => {
|
|
320
|
+
if (Boom.isBoom(err)) {
|
|
321
|
+
const { statusCode, payload } = err.output;
|
|
322
|
+
return res.status(statusCode).json({
|
|
323
|
+
error: payload.message,
|
|
324
|
+
message: payload.message,
|
|
325
|
+
statusCode: payload.statusCode,
|
|
326
|
+
...err.data,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Handle non-Boom errors
|
|
331
|
+
console.error('Unhandled error:', err);
|
|
332
|
+
return res.status(500).json({
|
|
333
|
+
error: 'Internal Server Error',
|
|
334
|
+
message: err.message,
|
|
335
|
+
statusCode: 500,
|
|
336
|
+
});
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Create authentication middleware for testing
|
|
341
|
+
* @param {Object} options - Configuration options
|
|
342
|
+
* @param {Object} options.mockUser - User to inject on successful auth
|
|
343
|
+
* @param {string} options.validToken - Token that will be considered valid (default: 'valid-token')
|
|
344
|
+
* @returns {Function} Express middleware
|
|
345
|
+
*/
|
|
346
|
+
const createAuthMiddleware = ({ mockUser = mockData.user, validToken = 'valid-token' } = {}) => {
|
|
347
|
+
return (req, res, next) => {
|
|
348
|
+
const authHeader = req.headers.authorization;
|
|
349
|
+
|
|
350
|
+
if (!authHeader) {
|
|
351
|
+
return next(Boom.unauthorized('No authentication provided'));
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const token = authHeader.replace('Bearer ', '');
|
|
355
|
+
|
|
356
|
+
if (token === validToken) {
|
|
357
|
+
req.user = mockUser;
|
|
358
|
+
return next();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return next(Boom.unauthorized('Invalid token'));
|
|
362
|
+
};
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Create a test Express app with common configuration
|
|
367
|
+
* @param {Object} options - Configuration options
|
|
368
|
+
* @param {Object} options.router - Express router to mount
|
|
369
|
+
* @param {string} options.basePath - Base path for router (default: '/')
|
|
370
|
+
* @param {Object} options.mockUser - User to inject on auth (default: mockData.user)
|
|
371
|
+
* @param {boolean} options.useAuth - Whether to use auth middleware (default: true)
|
|
372
|
+
* @param {string} options.validToken - Valid token string (default: 'valid-token')
|
|
373
|
+
* @returns {Object} Express app instance
|
|
374
|
+
*/
|
|
375
|
+
const createTestApp = ({
|
|
376
|
+
router,
|
|
377
|
+
basePath = '/',
|
|
378
|
+
mockUser = mockData.user,
|
|
379
|
+
useAuth = true,
|
|
380
|
+
validToken = 'valid-token',
|
|
381
|
+
} = {}) => {
|
|
382
|
+
const app = express();
|
|
383
|
+
app.use(express.json());
|
|
384
|
+
|
|
385
|
+
if (useAuth) {
|
|
386
|
+
app.use(createAuthMiddleware({ mockUser, validToken }));
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (router) {
|
|
390
|
+
app.use(basePath, router);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Add Boom error handler (must be after routes)
|
|
394
|
+
app.use(boomErrorHandler);
|
|
395
|
+
|
|
396
|
+
return app;
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
// ============================================================================
|
|
400
|
+
// Database Config Mock
|
|
401
|
+
// ============================================================================
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Standard database config mock object
|
|
405
|
+
* Use this to mock '../database/config' in tests
|
|
406
|
+
*/
|
|
407
|
+
const databaseConfigMock = {
|
|
408
|
+
DB_TYPE: 'mongodb',
|
|
409
|
+
getDatabaseType: jest.fn(() => 'mongodb'),
|
|
410
|
+
PRISMA_LOG_LEVEL: 'error,warn',
|
|
411
|
+
PRISMA_QUERY_LOGGING: false,
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
// ============================================================================
|
|
415
|
+
// App Definition Mock
|
|
416
|
+
// ============================================================================
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Create a mock app definition
|
|
420
|
+
* @param {Object} overrides - Override specific properties
|
|
421
|
+
* @returns {Object} Mock app definition
|
|
422
|
+
*/
|
|
423
|
+
const createMockAppDefinition = (overrides = {}) => ({
|
|
424
|
+
integrations: overrides.integrations || [
|
|
425
|
+
createMockModuleDefinition({ moduleName: 'hubspot' }),
|
|
426
|
+
createMockModuleDefinition({
|
|
427
|
+
moduleName: 'salesforce',
|
|
428
|
+
displayName: 'Salesforce',
|
|
429
|
+
description: 'Connect to Salesforce CRM',
|
|
430
|
+
capabilities: ['accounts', 'contacts', 'opportunities'],
|
|
431
|
+
}),
|
|
432
|
+
],
|
|
433
|
+
userConfig: {
|
|
434
|
+
usePassword: true,
|
|
435
|
+
primary: 'individual',
|
|
436
|
+
authModes: {
|
|
437
|
+
friggToken: true,
|
|
438
|
+
},
|
|
439
|
+
...(overrides.userConfig || {}),
|
|
440
|
+
},
|
|
441
|
+
...overrides,
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// ============================================================================
|
|
445
|
+
// Test Helpers
|
|
446
|
+
// ============================================================================
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Helper to make authenticated requests
|
|
450
|
+
* @param {Object} app - Express app
|
|
451
|
+
* @param {string} token - Auth token (default: 'valid-token')
|
|
452
|
+
* @returns {Object} Supertest agent with auth header set
|
|
453
|
+
*/
|
|
454
|
+
const authenticatedRequest = (request, token = 'valid-token') => {
|
|
455
|
+
return {
|
|
456
|
+
get: (url) => request.get(url).set('Authorization', `Bearer ${token}`),
|
|
457
|
+
post: (url) => request.post(url).set('Authorization', `Bearer ${token}`),
|
|
458
|
+
put: (url) => request.put(url).set('Authorization', `Bearer ${token}`),
|
|
459
|
+
patch: (url) => request.patch(url).set('Authorization', `Bearer ${token}`),
|
|
460
|
+
delete: (url) => request.delete(url).set('Authorization', `Bearer ${token}`),
|
|
461
|
+
};
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Setup common jest mocks for repository factories
|
|
466
|
+
* Call this at the top of your test file after imports
|
|
467
|
+
* @param {Object} factoryMocks - Map of factory name to mock repository
|
|
468
|
+
*/
|
|
469
|
+
const setupRepositoryFactoryMocks = ({
|
|
470
|
+
createUserRepository,
|
|
471
|
+
createCredentialRepository,
|
|
472
|
+
createModuleRepository,
|
|
473
|
+
createIntegrationRepository,
|
|
474
|
+
createAuthorizationSessionRepository,
|
|
475
|
+
loadAppDefinition,
|
|
476
|
+
mocks,
|
|
477
|
+
}) => {
|
|
478
|
+
if (createUserRepository && mocks.userRepository) {
|
|
479
|
+
createUserRepository.mockReturnValue(mocks.userRepository);
|
|
480
|
+
}
|
|
481
|
+
if (createCredentialRepository && mocks.credentialRepository) {
|
|
482
|
+
createCredentialRepository.mockReturnValue(mocks.credentialRepository);
|
|
483
|
+
}
|
|
484
|
+
if (createModuleRepository && mocks.moduleRepository) {
|
|
485
|
+
createModuleRepository.mockReturnValue(mocks.moduleRepository);
|
|
486
|
+
}
|
|
487
|
+
if (createIntegrationRepository && mocks.integrationRepository) {
|
|
488
|
+
createIntegrationRepository.mockReturnValue(mocks.integrationRepository);
|
|
489
|
+
}
|
|
490
|
+
if (createAuthorizationSessionRepository && mocks.authorizationSessionRepository) {
|
|
491
|
+
createAuthorizationSessionRepository.mockReturnValue(mocks.authorizationSessionRepository);
|
|
492
|
+
}
|
|
493
|
+
if (loadAppDefinition) {
|
|
494
|
+
loadAppDefinition.mockReturnValue(createMockAppDefinition());
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
// ============================================================================
|
|
499
|
+
// Exports
|
|
500
|
+
// ============================================================================
|
|
501
|
+
|
|
502
|
+
module.exports = {
|
|
503
|
+
// Mock data generators
|
|
504
|
+
mockData,
|
|
505
|
+
createMockUser,
|
|
506
|
+
createMockCredential,
|
|
507
|
+
createMockEntity,
|
|
508
|
+
createMockIntegration,
|
|
509
|
+
createMockModuleDefinition,
|
|
510
|
+
|
|
511
|
+
// Repository mocks
|
|
512
|
+
createMockUserRepository,
|
|
513
|
+
createMockCredentialRepository,
|
|
514
|
+
createMockModuleRepository,
|
|
515
|
+
createMockIntegrationRepository,
|
|
516
|
+
createMockAuthorizationSessionRepository,
|
|
517
|
+
createMockRepositories,
|
|
518
|
+
|
|
519
|
+
// API/Factory mocks
|
|
520
|
+
createMockApiRequester,
|
|
521
|
+
createMockModuleFactory,
|
|
522
|
+
|
|
523
|
+
// Express app utilities
|
|
524
|
+
createTestApp,
|
|
525
|
+
createAuthMiddleware,
|
|
526
|
+
boomErrorHandler,
|
|
527
|
+
|
|
528
|
+
// Config mocks
|
|
529
|
+
databaseConfigMock,
|
|
530
|
+
createMockAppDefinition,
|
|
531
|
+
|
|
532
|
+
// Test helpers
|
|
533
|
+
authenticatedRequest,
|
|
534
|
+
setupRepositoryFactoryMocks,
|
|
535
|
+
};
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Router Test Utilities - Unit Tests
|
|
3
|
+
* @description Tests for the shared router test utilities
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const request = require('supertest');
|
|
7
|
+
const express = require('express');
|
|
8
|
+
const Boom = require('@hapi/boom');
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
// Mock data generators
|
|
12
|
+
mockData,
|
|
13
|
+
createMockUser,
|
|
14
|
+
createMockCredential,
|
|
15
|
+
createMockEntity,
|
|
16
|
+
createMockIntegration,
|
|
17
|
+
createMockModuleDefinition,
|
|
18
|
+
|
|
19
|
+
// Repository mocks
|
|
20
|
+
createMockUserRepository,
|
|
21
|
+
createMockCredentialRepository,
|
|
22
|
+
createMockModuleRepository,
|
|
23
|
+
createMockIntegrationRepository,
|
|
24
|
+
createMockAuthorizationSessionRepository,
|
|
25
|
+
createMockRepositories,
|
|
26
|
+
|
|
27
|
+
// API/Factory mocks
|
|
28
|
+
createMockApiRequester,
|
|
29
|
+
createMockModuleFactory,
|
|
30
|
+
|
|
31
|
+
// Express app utilities
|
|
32
|
+
createTestApp,
|
|
33
|
+
createAuthMiddleware,
|
|
34
|
+
boomErrorHandler,
|
|
35
|
+
|
|
36
|
+
// Config mocks
|
|
37
|
+
databaseConfigMock,
|
|
38
|
+
createMockAppDefinition,
|
|
39
|
+
} = require('./index');
|
|
40
|
+
|
|
41
|
+
describe('Router Test Utilities', () => {
|
|
42
|
+
describe('Mock Data Generators', () => {
|
|
43
|
+
describe('createMockUser', () => {
|
|
44
|
+
it('should create user with default values', () => {
|
|
45
|
+
const user = createMockUser();
|
|
46
|
+
expect(user.id).toBe('user-123');
|
|
47
|
+
expect(user.username).toBe('testuser');
|
|
48
|
+
expect(user.email).toBe('test@example.com');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should allow overriding values', () => {
|
|
52
|
+
const user = createMockUser({ id: 'custom-id', username: 'custom' });
|
|
53
|
+
expect(user.id).toBe('custom-id');
|
|
54
|
+
expect(user.username).toBe('custom');
|
|
55
|
+
expect(user.email).toBe('test@example.com'); // Default preserved
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should include getId function', () => {
|
|
59
|
+
const user = createMockUser({ id: 'test-id' });
|
|
60
|
+
expect(user.getId()).toBe('test-id');
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('createMockCredential', () => {
|
|
65
|
+
it('should create credential with default values', () => {
|
|
66
|
+
const cred = createMockCredential();
|
|
67
|
+
expect(cred.id).toBe('cred-123');
|
|
68
|
+
expect(cred.type).toBe('hubspot');
|
|
69
|
+
expect(cred.authIsValid).toBe(true);
|
|
70
|
+
expect(cred.data.access_token).toBeDefined();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should allow overriding values', () => {
|
|
74
|
+
const cred = createMockCredential({ type: 'salesforce', authIsValid: false });
|
|
75
|
+
expect(cred.type).toBe('salesforce');
|
|
76
|
+
expect(cred.authIsValid).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('createMockEntity', () => {
|
|
81
|
+
it('should create entity with default values', () => {
|
|
82
|
+
const entity = createMockEntity();
|
|
83
|
+
expect(entity.id).toBe('entity-123');
|
|
84
|
+
expect(entity.userId).toBe('user-123');
|
|
85
|
+
expect(entity.credentialId).toBe('cred-123');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('createMockIntegration', () => {
|
|
90
|
+
it('should create integration with default values', () => {
|
|
91
|
+
const integration = createMockIntegration();
|
|
92
|
+
expect(integration.id).toBe('integration-123');
|
|
93
|
+
expect(integration.userId).toBe('user-123');
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('createMockModuleDefinition', () => {
|
|
98
|
+
it('should create module definition with default values', () => {
|
|
99
|
+
const def = createMockModuleDefinition();
|
|
100
|
+
expect(def.moduleName).toBe('hubspot');
|
|
101
|
+
expect(def.definition.getDisplayName()).toBe('HubSpot');
|
|
102
|
+
expect(def.definition.getAuthType()).toBe('oauth2');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should allow customization', () => {
|
|
106
|
+
const def = createMockModuleDefinition({
|
|
107
|
+
moduleName: 'custom',
|
|
108
|
+
displayName: 'Custom Service',
|
|
109
|
+
authType: 'api-key',
|
|
110
|
+
});
|
|
111
|
+
expect(def.moduleName).toBe('custom');
|
|
112
|
+
expect(def.definition.getDisplayName()).toBe('Custom Service');
|
|
113
|
+
expect(def.definition.getAuthType()).toBe('api-key');
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('mockData convenience object', () => {
|
|
118
|
+
it('should provide pre-created instances', () => {
|
|
119
|
+
expect(mockData.user).toBeDefined();
|
|
120
|
+
expect(mockData.credential).toBeDefined();
|
|
121
|
+
expect(mockData.entity).toBeDefined();
|
|
122
|
+
expect(mockData.integration).toBeDefined();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('Mock Repository Factories', () => {
|
|
128
|
+
describe('createMockUserRepository', () => {
|
|
129
|
+
it('should create repository with all methods', () => {
|
|
130
|
+
const repo = createMockUserRepository();
|
|
131
|
+
expect(repo.findById).toBeDefined();
|
|
132
|
+
expect(repo.findByToken).toBeDefined();
|
|
133
|
+
expect(repo.getSessionToken).toBeDefined();
|
|
134
|
+
expect(repo.findIndividualUserById).toBeDefined();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should have working mock implementations', async () => {
|
|
138
|
+
const repo = createMockUserRepository();
|
|
139
|
+
const user = await repo.findById('test');
|
|
140
|
+
expect(user.id).toBe('user-123');
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('createMockCredentialRepository', () => {
|
|
145
|
+
it('should create repository with all methods', () => {
|
|
146
|
+
const repo = createMockCredentialRepository();
|
|
147
|
+
expect(repo.findById).toBeDefined();
|
|
148
|
+
expect(repo.findCredential).toBeDefined();
|
|
149
|
+
expect(repo.deleteCredentialById).toBeDefined();
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('createMockRepositories', () => {
|
|
154
|
+
it('should create all repositories at once', () => {
|
|
155
|
+
const repos = createMockRepositories();
|
|
156
|
+
expect(repos.userRepository).toBeDefined();
|
|
157
|
+
expect(repos.credentialRepository).toBeDefined();
|
|
158
|
+
expect(repos.moduleRepository).toBeDefined();
|
|
159
|
+
expect(repos.integrationRepository).toBeDefined();
|
|
160
|
+
expect(repos.authorizationSessionRepository).toBeDefined();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('Mock API/Factory Utilities', () => {
|
|
166
|
+
describe('createMockApiRequester', () => {
|
|
167
|
+
it('should create API requester with all methods', () => {
|
|
168
|
+
const api = createMockApiRequester();
|
|
169
|
+
expect(api.request).toBeDefined();
|
|
170
|
+
expect(api._get).toBeDefined();
|
|
171
|
+
expect(api._post).toBeDefined();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should have working request mock', async () => {
|
|
175
|
+
const api = createMockApiRequester();
|
|
176
|
+
const response = await api.request({ method: 'GET', path: '/test' });
|
|
177
|
+
expect(response.status).toBe(200);
|
|
178
|
+
expect(response.data.success).toBe(true);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('createMockModuleFactory', () => {
|
|
183
|
+
it('should create factory with getModuleInstance', () => {
|
|
184
|
+
const factory = createMockModuleFactory();
|
|
185
|
+
expect(factory.getModuleInstance).toBeDefined();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should return module with api property', async () => {
|
|
189
|
+
const factory = createMockModuleFactory();
|
|
190
|
+
const instance = await factory.getModuleInstance('entity-123', 'user-123');
|
|
191
|
+
expect(instance.api).toBeDefined();
|
|
192
|
+
expect(instance.api.request).toBeDefined();
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('Express App Utilities', () => {
|
|
198
|
+
describe('boomErrorHandler', () => {
|
|
199
|
+
it('should handle Boom errors', async () => {
|
|
200
|
+
const app = express();
|
|
201
|
+
app.get('/error', (req, res, next) => {
|
|
202
|
+
next(Boom.notFound('Resource not found'));
|
|
203
|
+
});
|
|
204
|
+
app.use(boomErrorHandler);
|
|
205
|
+
|
|
206
|
+
const response = await request(app).get('/error');
|
|
207
|
+
expect(response.status).toBe(404);
|
|
208
|
+
expect(response.body.error).toBe('Resource not found');
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('should handle non-Boom errors', async () => {
|
|
212
|
+
const app = express();
|
|
213
|
+
app.get('/error', () => {
|
|
214
|
+
throw new Error('Something went wrong');
|
|
215
|
+
});
|
|
216
|
+
app.use(boomErrorHandler);
|
|
217
|
+
|
|
218
|
+
const response = await request(app).get('/error');
|
|
219
|
+
expect(response.status).toBe(500);
|
|
220
|
+
expect(response.body.error).toBe('Internal Server Error');
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe('createAuthMiddleware', () => {
|
|
225
|
+
it('should authenticate valid token', async () => {
|
|
226
|
+
const app = express();
|
|
227
|
+
app.use(createAuthMiddleware());
|
|
228
|
+
app.get('/test', (req, res) => res.json({ user: req.user }));
|
|
229
|
+
app.use(boomErrorHandler);
|
|
230
|
+
|
|
231
|
+
const response = await request(app)
|
|
232
|
+
.get('/test')
|
|
233
|
+
.set('Authorization', 'Bearer valid-token');
|
|
234
|
+
|
|
235
|
+
expect(response.status).toBe(200);
|
|
236
|
+
expect(response.body.user.id).toBe('user-123');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('should reject missing token', async () => {
|
|
240
|
+
const app = express();
|
|
241
|
+
app.use(createAuthMiddleware());
|
|
242
|
+
app.get('/test', (req, res) => res.json({ user: req.user }));
|
|
243
|
+
app.use(boomErrorHandler);
|
|
244
|
+
|
|
245
|
+
const response = await request(app).get('/test');
|
|
246
|
+
expect(response.status).toBe(401);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('should reject invalid token', async () => {
|
|
250
|
+
const app = express();
|
|
251
|
+
app.use(createAuthMiddleware());
|
|
252
|
+
app.get('/test', (req, res) => res.json({ user: req.user }));
|
|
253
|
+
app.use(boomErrorHandler);
|
|
254
|
+
|
|
255
|
+
const response = await request(app)
|
|
256
|
+
.get('/test')
|
|
257
|
+
.set('Authorization', 'Bearer invalid-token');
|
|
258
|
+
|
|
259
|
+
expect(response.status).toBe(401);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('should use custom valid token', async () => {
|
|
263
|
+
const app = express();
|
|
264
|
+
app.use(createAuthMiddleware({ validToken: 'custom-token' }));
|
|
265
|
+
app.get('/test', (req, res) => res.json({ user: req.user }));
|
|
266
|
+
app.use(boomErrorHandler);
|
|
267
|
+
|
|
268
|
+
const response = await request(app)
|
|
269
|
+
.get('/test')
|
|
270
|
+
.set('Authorization', 'Bearer custom-token');
|
|
271
|
+
|
|
272
|
+
expect(response.status).toBe(200);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
describe('createTestApp', () => {
|
|
277
|
+
it('should create app with JSON parsing', async () => {
|
|
278
|
+
const router = express.Router();
|
|
279
|
+
router.post('/test', (req, res) => res.json(req.body));
|
|
280
|
+
|
|
281
|
+
const app = createTestApp({ router, useAuth: false });
|
|
282
|
+
|
|
283
|
+
const response = await request(app)
|
|
284
|
+
.post('/test')
|
|
285
|
+
.send({ data: 'test' });
|
|
286
|
+
|
|
287
|
+
expect(response.status).toBe(200);
|
|
288
|
+
expect(response.body.data).toBe('test');
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('should include auth middleware by default', async () => {
|
|
292
|
+
const router = express.Router();
|
|
293
|
+
router.get('/test', (req, res) => res.json({ ok: true }));
|
|
294
|
+
|
|
295
|
+
const app = createTestApp({ router });
|
|
296
|
+
|
|
297
|
+
const response = await request(app).get('/test');
|
|
298
|
+
expect(response.status).toBe(401);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('should mount router at custom base path', async () => {
|
|
302
|
+
const router = express.Router();
|
|
303
|
+
router.get('/resource', (req, res) => res.json({ ok: true }));
|
|
304
|
+
|
|
305
|
+
const app = createTestApp({ router, basePath: '/api', useAuth: false });
|
|
306
|
+
|
|
307
|
+
const response = await request(app).get('/api/resource');
|
|
308
|
+
expect(response.status).toBe(200);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('should include Boom error handler', async () => {
|
|
312
|
+
const router = express.Router();
|
|
313
|
+
router.get('/error', (req, res, next) => {
|
|
314
|
+
next(Boom.badRequest('Invalid input'));
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
const app = createTestApp({ router, useAuth: false });
|
|
318
|
+
|
|
319
|
+
const response = await request(app).get('/error');
|
|
320
|
+
expect(response.status).toBe(400);
|
|
321
|
+
expect(response.body.error).toBe('Invalid input');
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
describe('Config Mocks', () => {
|
|
327
|
+
describe('databaseConfigMock', () => {
|
|
328
|
+
it('should have expected properties', () => {
|
|
329
|
+
expect(databaseConfigMock.DB_TYPE).toBe('mongodb');
|
|
330
|
+
expect(databaseConfigMock.getDatabaseType()).toBe('mongodb');
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
describe('createMockAppDefinition', () => {
|
|
335
|
+
it('should create app definition with defaults', () => {
|
|
336
|
+
const def = createMockAppDefinition();
|
|
337
|
+
expect(def.integrations).toBeDefined();
|
|
338
|
+
expect(def.integrations.length).toBeGreaterThan(0);
|
|
339
|
+
expect(def.userConfig).toBeDefined();
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('should allow customization', () => {
|
|
343
|
+
const def = createMockAppDefinition({
|
|
344
|
+
userConfig: { usePassword: false },
|
|
345
|
+
});
|
|
346
|
+
expect(def.userConfig.usePassword).toBe(false);
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
});
|