@friggframework/core 2.0.0--canary.461.61382d8.0 → 2.0.0--canary.461.3d6d8ad.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/database/use-cases/check-migration-status-use-case.js +81 -0
- package/generated/prisma-mongodb/client.d.ts +1 -0
- package/generated/prisma-mongodb/client.js +4 -0
- package/generated/prisma-mongodb/default.d.ts +1 -0
- package/generated/prisma-mongodb/default.js +4 -0
- package/generated/prisma-mongodb/edge.d.ts +1 -0
- package/generated/prisma-mongodb/edge.js +336 -0
- package/generated/prisma-mongodb/index-browser.js +318 -0
- package/generated/prisma-mongodb/index.d.ts +22993 -0
- package/generated/prisma-mongodb/index.js +361 -0
- package/generated/prisma-mongodb/package.json +183 -0
- package/generated/prisma-mongodb/query-engine-debian-openssl-3.0.x +0 -0
- package/generated/prisma-mongodb/query-engine-rhel-openssl-3.0.x +0 -0
- package/generated/prisma-mongodb/runtime/binary.d.ts +1 -0
- package/generated/prisma-mongodb/runtime/binary.js +289 -0
- package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
- package/generated/prisma-mongodb/runtime/edge.js +34 -0
- package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
- package/generated/prisma-mongodb/runtime/library.d.ts +3977 -0
- package/generated/prisma-mongodb/runtime/react-native.js +83 -0
- package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
- package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
- package/generated/prisma-mongodb/schema.prisma +364 -0
- package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
- package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
- package/generated/prisma-mongodb/wasm.d.ts +1 -0
- package/generated/prisma-mongodb/wasm.js +343 -0
- package/generated/prisma-postgresql/client.d.ts +1 -0
- package/generated/prisma-postgresql/client.js +4 -0
- package/generated/prisma-postgresql/default.d.ts +1 -0
- package/generated/prisma-postgresql/default.js +4 -0
- package/generated/prisma-postgresql/edge.d.ts +1 -0
- package/generated/prisma-postgresql/edge.js +358 -0
- package/generated/prisma-postgresql/index-browser.js +340 -0
- package/generated/prisma-postgresql/index.d.ts +25171 -0
- package/generated/prisma-postgresql/index.js +383 -0
- package/generated/prisma-postgresql/package.json +183 -0
- package/generated/prisma-postgresql/query-engine-debian-openssl-3.0.x +0 -0
- package/generated/prisma-postgresql/query-engine-rhel-openssl-3.0.x +0 -0
- package/generated/prisma-postgresql/query_engine_bg.js +2 -0
- package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
- package/generated/prisma-postgresql/runtime/binary.d.ts +1 -0
- package/generated/prisma-postgresql/runtime/binary.js +289 -0
- package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
- package/generated/prisma-postgresql/runtime/edge.js +34 -0
- package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
- package/generated/prisma-postgresql/runtime/library.d.ts +3977 -0
- package/generated/prisma-postgresql/runtime/react-native.js +83 -0
- package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
- package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
- package/generated/prisma-postgresql/schema.prisma +347 -0
- package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
- package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
- package/generated/prisma-postgresql/wasm.d.ts +1 -0
- package/generated/prisma-postgresql/wasm.js +365 -0
- package/handlers/routers/db-migration.js +52 -0
- package/package.json +5 -5
- package/application/commands/integration-commands.test.js +0 -123
- package/core/Worker.test.js +0 -159
- package/database/encryption/encryption-integration.test.js +0 -553
- package/database/encryption/encryption-schema-registry.test.js +0 -392
- package/database/encryption/field-encryption-service.test.js +0 -525
- package/database/encryption/mongo-decryption-fix-verification.test.js +0 -348
- package/database/encryption/postgres-decryption-fix-verification.test.js +0 -371
- package/database/encryption/postgres-relation-decryption.test.js +0 -245
- package/database/encryption/prisma-encryption-extension.test.js +0 -439
- package/database/repositories/migration-status-repository-s3.test.js +0 -158
- package/database/use-cases/check-encryption-health-use-case.test.js +0 -192
- package/database/use-cases/get-migration-status-use-case.test.js +0 -171
- package/database/use-cases/run-database-migration-use-case.test.js +0 -310
- package/database/use-cases/trigger-database-migration-use-case.test.js +0 -250
- package/database/utils/prisma-runner.test.js +0 -486
- package/encrypt/Cryptor.test.js +0 -144
- package/errors/base-error.test.js +0 -32
- package/errors/fetch-error.test.js +0 -79
- package/errors/halt-error.test.js +0 -11
- package/errors/validation-errors.test.js +0 -120
- package/handlers/auth-flow.integration.test.js +0 -147
- package/handlers/integration-event-dispatcher.test.js +0 -209
- package/handlers/routers/db-migration.test.js +0 -51
- package/handlers/routers/health.test.js +0 -210
- package/handlers/routers/integration-webhook-routers.test.js +0 -126
- package/handlers/use-cases/check-integrations-health-use-case.test.js +0 -125
- package/handlers/webhook-flow.integration.test.js +0 -356
- package/handlers/workers/db-migration.test.js +0 -50
- package/handlers/workers/integration-defined-workers.test.js +0 -184
- package/integrations/tests/integration-router-multi-auth.test.js +0 -369
- package/integrations/tests/use-cases/create-integration.test.js +0 -131
- package/integrations/tests/use-cases/delete-integration-for-user.test.js +0 -150
- package/integrations/tests/use-cases/find-integration-context-by-external-entity-id.test.js +0 -92
- package/integrations/tests/use-cases/get-integration-for-user.test.js +0 -150
- package/integrations/tests/use-cases/get-integration-instance.test.js +0 -176
- package/integrations/tests/use-cases/get-integrations-for-user.test.js +0 -176
- package/integrations/tests/use-cases/get-possible-integrations.test.js +0 -188
- package/integrations/tests/use-cases/update-integration-messages.test.js +0 -142
- package/integrations/tests/use-cases/update-integration-status.test.js +0 -103
- package/integrations/tests/use-cases/update-integration.test.js +0 -141
- package/integrations/use-cases/create-process.test.js +0 -178
- package/integrations/use-cases/get-process.test.js +0 -190
- package/integrations/use-cases/load-integration-context-full.test.js +0 -329
- package/integrations/use-cases/load-integration-context.test.js +0 -114
- package/integrations/use-cases/update-process-metrics.test.js +0 -308
- package/integrations/use-cases/update-process-state.test.js +0 -256
- package/lambda/TimeoutCatcher.test.js +0 -68
- package/logs/logger.test.js +0 -76
- package/modules/module-hydration.test.js +0 -205
- package/modules/requester/requester.test.js +0 -28
- package/queues/queuer-util.test.js +0 -132
- package/user/tests/use-cases/create-individual-user.test.js +0 -24
- package/user/tests/use-cases/create-organization-user.test.js +0 -28
- package/user/tests/use-cases/create-token-for-user-id.test.js +0 -19
- package/user/tests/use-cases/get-user-from-adopter-jwt.test.js +0 -113
- package/user/tests/use-cases/get-user-from-bearer-token.test.js +0 -64
- package/user/tests/use-cases/get-user-from-x-frigg-headers.test.js +0 -346
- package/user/tests/use-cases/login-user.test.js +0 -220
- package/user/tests/user-password-encryption-isolation.test.js +0 -237
- package/user/tests/user-password-hashing.test.js +0 -235
- package/websocket/repositories/websocket-connection-repository.test.js +0 -227
|
@@ -1,369 +0,0 @@
|
|
|
1
|
-
const { AuthenticateUser } = require('../../user/use-cases/authenticate-user');
|
|
2
|
-
const { GetUserFromBearerToken } = require('../../user/use-cases/get-user-from-bearer-token');
|
|
3
|
-
const { GetUserFromXFriggHeaders } = require('../../user/use-cases/get-user-from-x-frigg-headers');
|
|
4
|
-
const { GetUserFromAdopterJwt } = require('../../user/use-cases/get-user-from-adopter-jwt');
|
|
5
|
-
const { User } = require('../../user/user');
|
|
6
|
-
const Boom = require('@hapi/boom');
|
|
7
|
-
|
|
8
|
-
describe('AuthenticateUser - Multi-Mode Authentication', () => {
|
|
9
|
-
let authenticateUser;
|
|
10
|
-
let mockGetUserFromBearerToken;
|
|
11
|
-
let mockGetUserFromXFriggHeaders;
|
|
12
|
-
let mockGetUserFromAdopterJwt;
|
|
13
|
-
let mockUserConfig;
|
|
14
|
-
let mockUser;
|
|
15
|
-
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
mockUser = new User(
|
|
18
|
-
{ id: 'user-123', username: 'testuser' },
|
|
19
|
-
null,
|
|
20
|
-
false,
|
|
21
|
-
'individual',
|
|
22
|
-
true,
|
|
23
|
-
false
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
mockGetUserFromBearerToken = {
|
|
27
|
-
execute: jest.fn().mockResolvedValue(mockUser),
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
mockGetUserFromXFriggHeaders = {
|
|
31
|
-
execute: jest.fn().mockResolvedValue(mockUser),
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
mockGetUserFromAdopterJwt = {
|
|
35
|
-
execute: jest.fn().mockResolvedValue(mockUser),
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
mockUserConfig = {
|
|
39
|
-
authModes: {
|
|
40
|
-
friggToken: true,
|
|
41
|
-
xFriggHeaders: true,
|
|
42
|
-
adopterJwt: false,
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
authenticateUser = new AuthenticateUser({
|
|
47
|
-
getUserFromBearerToken: mockGetUserFromBearerToken,
|
|
48
|
-
getUserFromXFriggHeaders: mockGetUserFromXFriggHeaders,
|
|
49
|
-
getUserFromAdopterJwt: mockGetUserFromAdopterJwt,
|
|
50
|
-
userConfig: mockUserConfig,
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
describe('Priority 1: X-Frigg Headers (Backend-to-Backend)', () => {
|
|
55
|
-
it('should authenticate with x-frigg-appUserId header', async () => {
|
|
56
|
-
const mockReq = {
|
|
57
|
-
headers: {
|
|
58
|
-
'x-frigg-appuserid': 'app-user-123',
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const result = await authenticateUser.execute(mockReq);
|
|
63
|
-
|
|
64
|
-
expect(result).toBe(mockUser);
|
|
65
|
-
expect(mockGetUserFromXFriggHeaders.execute).toHaveBeenCalledWith(
|
|
66
|
-
'app-user-123',
|
|
67
|
-
undefined
|
|
68
|
-
);
|
|
69
|
-
expect(mockGetUserFromBearerToken.execute).not.toHaveBeenCalled();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('should authenticate with x-frigg-appOrgId header', async () => {
|
|
73
|
-
const mockReq = {
|
|
74
|
-
headers: {
|
|
75
|
-
'x-frigg-apporgid': 'app-org-456',
|
|
76
|
-
},
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const result = await authenticateUser.execute(mockReq);
|
|
80
|
-
|
|
81
|
-
expect(result).toBe(mockUser);
|
|
82
|
-
expect(mockGetUserFromXFriggHeaders.execute).toHaveBeenCalledWith(
|
|
83
|
-
undefined,
|
|
84
|
-
'app-org-456'
|
|
85
|
-
);
|
|
86
|
-
expect(mockGetUserFromBearerToken.execute).not.toHaveBeenCalled();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('should authenticate with both x-frigg headers when they match', async () => {
|
|
90
|
-
const mockReq = {
|
|
91
|
-
headers: {
|
|
92
|
-
'x-frigg-appuserid': 'app-user-123',
|
|
93
|
-
'x-frigg-apporgid': 'app-org-456',
|
|
94
|
-
},
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const result = await authenticateUser.execute(mockReq);
|
|
98
|
-
|
|
99
|
-
expect(result).toBe(mockUser);
|
|
100
|
-
expect(mockGetUserFromXFriggHeaders.execute).toHaveBeenCalledWith(
|
|
101
|
-
'app-user-123',
|
|
102
|
-
'app-org-456'
|
|
103
|
-
);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('should reject conflicting x-frigg headers (delegated to use case)', async () => {
|
|
107
|
-
const mockReq = {
|
|
108
|
-
headers: {
|
|
109
|
-
'x-frigg-appuserid': 'app-user-123',
|
|
110
|
-
'x-frigg-apporgid': 'app-org-999',
|
|
111
|
-
},
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const conflictError = Boom.badRequest('User ID mismatch');
|
|
115
|
-
mockGetUserFromXFriggHeaders.execute.mockRejectedValue(conflictError);
|
|
116
|
-
|
|
117
|
-
await expect(authenticateUser.execute(mockReq)).rejects.toThrow(
|
|
118
|
-
conflictError
|
|
119
|
-
);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('should skip x-frigg headers when authModes.xFriggHeaders is false', async () => {
|
|
123
|
-
mockUserConfig.authModes.xFriggHeaders = false;
|
|
124
|
-
|
|
125
|
-
const mockReq = {
|
|
126
|
-
headers: {
|
|
127
|
-
'x-frigg-appuserid': 'app-user-123',
|
|
128
|
-
authorization: 'Bearer frigg-token-xyz',
|
|
129
|
-
},
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
await authenticateUser.execute(mockReq);
|
|
133
|
-
|
|
134
|
-
expect(mockGetUserFromXFriggHeaders.execute).not.toHaveBeenCalled();
|
|
135
|
-
expect(mockGetUserFromBearerToken.execute).toHaveBeenCalledWith(
|
|
136
|
-
'Bearer frigg-token-xyz'
|
|
137
|
-
);
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
describe('Priority 2: Adopter JWT', () => {
|
|
142
|
-
beforeEach(() => {
|
|
143
|
-
mockUserConfig.authModes.adopterJwt = true;
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it('should try JWT when enabled and Bearer token is 3-part format', async () => {
|
|
147
|
-
const mockReq = {
|
|
148
|
-
headers: {
|
|
149
|
-
authorization: 'Bearer eyJhbGci.eyJzdWIi.signature',
|
|
150
|
-
},
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
await authenticateUser.execute(mockReq);
|
|
154
|
-
|
|
155
|
-
expect(mockGetUserFromAdopterJwt.execute).toHaveBeenCalledWith(
|
|
156
|
-
'eyJhbGci.eyJzdWIi.signature'
|
|
157
|
-
);
|
|
158
|
-
expect(mockGetUserFromBearerToken.execute).not.toHaveBeenCalled();
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it('should fall back to Frigg token when Bearer token is not JWT format', async () => {
|
|
162
|
-
const mockReq = {
|
|
163
|
-
headers: {
|
|
164
|
-
authorization: 'Bearer simple-token',
|
|
165
|
-
},
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
await authenticateUser.execute(mockReq);
|
|
169
|
-
|
|
170
|
-
expect(mockGetUserFromAdopterJwt.execute).not.toHaveBeenCalled();
|
|
171
|
-
expect(mockGetUserFromBearerToken.execute).toHaveBeenCalledWith(
|
|
172
|
-
'Bearer simple-token'
|
|
173
|
-
);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it('should not try JWT when authModes.adopterJwt is false', async () => {
|
|
177
|
-
mockUserConfig.authModes.adopterJwt = false;
|
|
178
|
-
|
|
179
|
-
const mockReq = {
|
|
180
|
-
headers: {
|
|
181
|
-
authorization: 'Bearer eyJhbGci.eyJzdWIi.signature',
|
|
182
|
-
},
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
await authenticateUser.execute(mockReq);
|
|
186
|
-
|
|
187
|
-
expect(mockGetUserFromAdopterJwt.execute).not.toHaveBeenCalled();
|
|
188
|
-
expect(mockGetUserFromBearerToken.execute).toHaveBeenCalledWith(
|
|
189
|
-
'Bearer eyJhbGci.eyJzdWIi.signature'
|
|
190
|
-
);
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
describe('Priority 3: Frigg Native Token (Fallback)', () => {
|
|
195
|
-
it('should fall back to Frigg token when no x-frigg headers', async () => {
|
|
196
|
-
const mockReq = {
|
|
197
|
-
headers: {
|
|
198
|
-
authorization: 'Bearer frigg-token-123',
|
|
199
|
-
},
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
const result = await authenticateUser.execute(mockReq);
|
|
203
|
-
|
|
204
|
-
expect(result).toBe(mockUser);
|
|
205
|
-
expect(mockGetUserFromBearerToken.execute).toHaveBeenCalledWith(
|
|
206
|
-
'Bearer frigg-token-123'
|
|
207
|
-
);
|
|
208
|
-
expect(mockGetUserFromXFriggHeaders.execute).not.toHaveBeenCalled();
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it('should skip Frigg token when authModes.friggToken is false', async () => {
|
|
212
|
-
mockUserConfig.authModes.friggToken = false;
|
|
213
|
-
|
|
214
|
-
const mockReq = {
|
|
215
|
-
headers: {
|
|
216
|
-
authorization: 'Bearer frigg-token-123',
|
|
217
|
-
},
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
await expect(authenticateUser.execute(mockReq)).rejects.toThrow(
|
|
221
|
-
Boom.unauthorized().message
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
expect(mockGetUserFromBearerToken.execute).not.toHaveBeenCalled();
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
describe('Priority Ordering', () => {
|
|
229
|
-
it('should prioritize x-frigg headers over bearer token', async () => {
|
|
230
|
-
const mockReq = {
|
|
231
|
-
headers: {
|
|
232
|
-
'x-frigg-appuserid': 'app-user-123',
|
|
233
|
-
authorization: 'Bearer frigg-token-xyz',
|
|
234
|
-
},
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
await authenticateUser.execute(mockReq);
|
|
238
|
-
|
|
239
|
-
expect(mockGetUserFromXFriggHeaders.execute).toHaveBeenCalledWith(
|
|
240
|
-
'app-user-123',
|
|
241
|
-
undefined
|
|
242
|
-
);
|
|
243
|
-
expect(mockGetUserFromBearerToken.execute).not.toHaveBeenCalled();
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it('should try JWT before Frigg token when JWT enabled', async () => {
|
|
247
|
-
mockUserConfig.authModes.adopterJwt = true;
|
|
248
|
-
|
|
249
|
-
const mockReq = {
|
|
250
|
-
headers: {
|
|
251
|
-
authorization: 'Bearer part1.part2.part3',
|
|
252
|
-
},
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
await authenticateUser.execute(mockReq);
|
|
256
|
-
|
|
257
|
-
expect(mockGetUserFromAdopterJwt.execute).toHaveBeenCalledWith(
|
|
258
|
-
'part1.part2.part3'
|
|
259
|
-
);
|
|
260
|
-
expect(mockGetUserFromBearerToken.execute).not.toHaveBeenCalled();
|
|
261
|
-
});
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
describe('Auth Mode Configuration', () => {
|
|
265
|
-
it('should use default friggToken mode when authModes not configured', () => {
|
|
266
|
-
const authWithDefaults = new AuthenticateUser({
|
|
267
|
-
getUserFromBearerToken: mockGetUserFromBearerToken,
|
|
268
|
-
getUserFromXFriggHeaders: mockGetUserFromXFriggHeaders,
|
|
269
|
-
getUserFromAdopterJwt: mockGetUserFromAdopterJwt,
|
|
270
|
-
userConfig: {}, // No authModes
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
const mockReq = {
|
|
274
|
-
headers: {
|
|
275
|
-
authorization: 'Bearer token',
|
|
276
|
-
},
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
authWithDefaults.execute(mockReq);
|
|
280
|
-
|
|
281
|
-
expect(mockGetUserFromBearerToken.execute).toHaveBeenCalled();
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
it('should throw unauthorized when no valid authentication provided', async () => {
|
|
285
|
-
const mockReq = {
|
|
286
|
-
headers: {},
|
|
287
|
-
};
|
|
288
|
-
|
|
289
|
-
await expect(authenticateUser.execute(mockReq)).rejects.toThrow(
|
|
290
|
-
Boom.unauthorized().message
|
|
291
|
-
);
|
|
292
|
-
|
|
293
|
-
await expect(authenticateUser.execute(mockReq)).rejects.toThrow(
|
|
294
|
-
'No valid authentication provided'
|
|
295
|
-
);
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
it('should throw unauthorized when all auth modes disabled', async () => {
|
|
299
|
-
mockUserConfig.authModes = {
|
|
300
|
-
friggToken: false,
|
|
301
|
-
xFriggHeaders: false,
|
|
302
|
-
adopterJwt: false,
|
|
303
|
-
};
|
|
304
|
-
|
|
305
|
-
const mockReq = {
|
|
306
|
-
headers: {
|
|
307
|
-
authorization: 'Bearer token',
|
|
308
|
-
},
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
await expect(authenticateUser.execute(mockReq)).rejects.toThrow(
|
|
312
|
-
'No valid authentication provided'
|
|
313
|
-
);
|
|
314
|
-
});
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
describe('Error Handling', () => {
|
|
318
|
-
it('should propagate authentication errors from x-frigg headers', async () => {
|
|
319
|
-
const mockReq = {
|
|
320
|
-
headers: {
|
|
321
|
-
'x-frigg-appuserid': 'invalid-user',
|
|
322
|
-
},
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
const customError = Boom.badRequest('Invalid user ID');
|
|
326
|
-
mockGetUserFromXFriggHeaders.execute.mockRejectedValue(customError);
|
|
327
|
-
|
|
328
|
-
await expect(authenticateUser.execute(mockReq)).rejects.toThrow(
|
|
329
|
-
customError
|
|
330
|
-
);
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
it('should propagate authentication errors from bearer token', async () => {
|
|
334
|
-
const mockReq = {
|
|
335
|
-
headers: {
|
|
336
|
-
authorization: 'Bearer invalid-token',
|
|
337
|
-
},
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
const customError = Boom.unauthorized('Invalid token');
|
|
341
|
-
mockGetUserFromBearerToken.execute.mockRejectedValue(customError);
|
|
342
|
-
|
|
343
|
-
await expect(authenticateUser.execute(mockReq)).rejects.toThrow(
|
|
344
|
-
customError
|
|
345
|
-
);
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
it('should propagate not implemented error from JWT', async () => {
|
|
349
|
-
mockUserConfig.authModes.adopterJwt = true;
|
|
350
|
-
|
|
351
|
-
const mockReq = {
|
|
352
|
-
headers: {
|
|
353
|
-
authorization: 'Bearer part1.part2.part3',
|
|
354
|
-
},
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
const notImplementedError = Boom.notImplemented('JWT not implemented');
|
|
358
|
-
mockGetUserFromAdopterJwt.execute.mockRejectedValue(
|
|
359
|
-
notImplementedError
|
|
360
|
-
);
|
|
361
|
-
|
|
362
|
-
await expect(authenticateUser.execute(mockReq)).rejects.toThrow(
|
|
363
|
-
notImplementedError
|
|
364
|
-
);
|
|
365
|
-
});
|
|
366
|
-
});
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
jest.mock('../../../database/config', () => ({
|
|
2
|
-
DB_TYPE: 'mongodb',
|
|
3
|
-
getDatabaseType: jest.fn(() => 'mongodb'),
|
|
4
|
-
PRISMA_LOG_LEVEL: 'error,warn',
|
|
5
|
-
PRISMA_QUERY_LOGGING: false,
|
|
6
|
-
}));
|
|
7
|
-
|
|
8
|
-
const { CreateIntegration } = require('../../use-cases/create-integration');
|
|
9
|
-
const { TestIntegrationRepository } = require('../doubles/test-integration-repository');
|
|
10
|
-
const { TestModuleFactory } = require('../../../modules/tests/doubles/test-module-factory');
|
|
11
|
-
const { DummyIntegration } = require('../doubles/dummy-integration-class');
|
|
12
|
-
|
|
13
|
-
describe('CreateIntegration Use-Case', () => {
|
|
14
|
-
let integrationRepository;
|
|
15
|
-
let moduleFactory;
|
|
16
|
-
let useCase;
|
|
17
|
-
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
integrationRepository = new TestIntegrationRepository();
|
|
20
|
-
moduleFactory = new TestModuleFactory();
|
|
21
|
-
useCase = new CreateIntegration({
|
|
22
|
-
integrationRepository,
|
|
23
|
-
integrationClasses: [DummyIntegration],
|
|
24
|
-
moduleFactory,
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
describe('happy path', () => {
|
|
29
|
-
it('creates an integration and returns DTO', async () => {
|
|
30
|
-
const entities = ['entity-1'];
|
|
31
|
-
const userId = 'user-1';
|
|
32
|
-
const config = { type: 'dummy', foo: 'bar' };
|
|
33
|
-
|
|
34
|
-
const dto = await useCase.execute(entities, userId, config);
|
|
35
|
-
|
|
36
|
-
expect(dto.id).toBeDefined();
|
|
37
|
-
expect(dto.config).toEqual(config);
|
|
38
|
-
expect(dto.userId).toBe(userId);
|
|
39
|
-
expect(dto.entities).toEqual(entities);
|
|
40
|
-
expect(dto.status).toBe('NEW');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('triggers ON_CREATE event with correct payload', async () => {
|
|
44
|
-
const entities = ['entity-1'];
|
|
45
|
-
const userId = 'user-1';
|
|
46
|
-
const config = { type: 'dummy', foo: 'bar' };
|
|
47
|
-
|
|
48
|
-
const dto = await useCase.execute(entities, userId, config);
|
|
49
|
-
|
|
50
|
-
const record = await integrationRepository.findIntegrationById(dto.id);
|
|
51
|
-
expect(record).toBeTruthy();
|
|
52
|
-
|
|
53
|
-
const history = integrationRepository.getOperationHistory();
|
|
54
|
-
const createOperation = history.find(op => op.operation === 'create');
|
|
55
|
-
expect(createOperation).toEqual({
|
|
56
|
-
operation: 'create',
|
|
57
|
-
id: dto.id,
|
|
58
|
-
userId,
|
|
59
|
-
config
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('loads modules for each entity', async () => {
|
|
64
|
-
const entities = ['entity-1', 'entity-2'];
|
|
65
|
-
const userId = 'user-1';
|
|
66
|
-
const config = { type: 'dummy' };
|
|
67
|
-
|
|
68
|
-
const dto = await useCase.execute(entities, userId, config);
|
|
69
|
-
|
|
70
|
-
expect(dto.entities).toEqual(entities);
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
describe('error cases', () => {
|
|
75
|
-
it('throws error when integration class is not found', async () => {
|
|
76
|
-
const entities = ['entity-1'];
|
|
77
|
-
const userId = 'user-1';
|
|
78
|
-
const config = { type: 'unknown-type' };
|
|
79
|
-
|
|
80
|
-
await expect(useCase.execute(entities, userId, config))
|
|
81
|
-
.rejects
|
|
82
|
-
.toThrow('No integration class found for type: unknown-type');
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('throws error when no integration classes provided', async () => {
|
|
86
|
-
const useCaseWithoutClasses = new CreateIntegration({
|
|
87
|
-
integrationRepository,
|
|
88
|
-
integrationClasses: [],
|
|
89
|
-
moduleFactory,
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const entities = ['entity-1'];
|
|
93
|
-
const userId = 'user-1';
|
|
94
|
-
const config = { type: 'dummy' };
|
|
95
|
-
|
|
96
|
-
await expect(useCaseWithoutClasses.execute(entities, userId, config))
|
|
97
|
-
.rejects
|
|
98
|
-
.toThrow('No integration class found for type: dummy');
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
describe('edge cases', () => {
|
|
103
|
-
it('handles empty entities array', async () => {
|
|
104
|
-
const entities = [];
|
|
105
|
-
const userId = 'user-1';
|
|
106
|
-
const config = { type: 'dummy' };
|
|
107
|
-
|
|
108
|
-
const dto = await useCase.execute(entities, userId, config);
|
|
109
|
-
|
|
110
|
-
expect(dto.entities).toEqual([]);
|
|
111
|
-
expect(dto.id).toBeDefined();
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('handles complex config objects', async () => {
|
|
115
|
-
const entities = ['entity-1'];
|
|
116
|
-
const userId = 'user-1';
|
|
117
|
-
const config = {
|
|
118
|
-
type: 'dummy',
|
|
119
|
-
nested: {
|
|
120
|
-
value: 123,
|
|
121
|
-
array: [1, 2, 3],
|
|
122
|
-
bool: true
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const dto = await useCase.execute(entities, userId, config);
|
|
127
|
-
|
|
128
|
-
expect(dto.config).toEqual(config);
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
});
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
jest.mock('../../../database/config', () => ({
|
|
2
|
-
DB_TYPE: 'mongodb',
|
|
3
|
-
getDatabaseType: jest.fn(() => 'mongodb'),
|
|
4
|
-
PRISMA_LOG_LEVEL: 'error,warn',
|
|
5
|
-
PRISMA_QUERY_LOGGING: false,
|
|
6
|
-
}));
|
|
7
|
-
|
|
8
|
-
const { DeleteIntegrationForUser } = require('../../use-cases/delete-integration-for-user');
|
|
9
|
-
const { TestIntegrationRepository } = require('../doubles/test-integration-repository');
|
|
10
|
-
const { DummyIntegration } = require('../doubles/dummy-integration-class');
|
|
11
|
-
|
|
12
|
-
describe('DeleteIntegrationForUser Use-Case', () => {
|
|
13
|
-
let integrationRepository;
|
|
14
|
-
let useCase;
|
|
15
|
-
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
integrationRepository = new TestIntegrationRepository();
|
|
18
|
-
useCase = new DeleteIntegrationForUser({
|
|
19
|
-
integrationRepository,
|
|
20
|
-
integrationClasses: [DummyIntegration],
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
describe('happy path', () => {
|
|
25
|
-
it('deletes integration successfully', async () => {
|
|
26
|
-
const record = await integrationRepository.createIntegration(['e1'], 'user-1', { type: 'dummy' });
|
|
27
|
-
|
|
28
|
-
await useCase.execute(record.id, 'user-1');
|
|
29
|
-
|
|
30
|
-
const found = await integrationRepository.findIntegrationById(record.id);
|
|
31
|
-
expect(found).toBeNull();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('tracks delete operation', async () => {
|
|
35
|
-
const record = await integrationRepository.createIntegration(['e1'], 'user-1', { type: 'dummy' });
|
|
36
|
-
integrationRepository.clearHistory();
|
|
37
|
-
|
|
38
|
-
await useCase.execute(record.id, 'user-1');
|
|
39
|
-
|
|
40
|
-
const history = integrationRepository.getOperationHistory();
|
|
41
|
-
const deleteOperation = history.find(op => op.operation === 'delete');
|
|
42
|
-
expect(deleteOperation).toEqual({
|
|
43
|
-
operation: 'delete',
|
|
44
|
-
id: record.id,
|
|
45
|
-
existed: true,
|
|
46
|
-
success: true
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('deletes integration with multiple entities', async () => {
|
|
51
|
-
const record = await integrationRepository.createIntegration(['e1', 'e2', 'e3'], 'user-1', { type: 'dummy' });
|
|
52
|
-
|
|
53
|
-
await useCase.execute(record.id, 'user-1');
|
|
54
|
-
|
|
55
|
-
const found = await integrationRepository.findIntegrationById(record.id);
|
|
56
|
-
expect(found).toBeNull();
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
describe('error cases', () => {
|
|
61
|
-
it('throws error when integration not found', async () => {
|
|
62
|
-
const nonExistentId = 'non-existent-id';
|
|
63
|
-
|
|
64
|
-
await expect(useCase.execute(nonExistentId, 'user-1'))
|
|
65
|
-
.rejects
|
|
66
|
-
.toThrow(`Integration with id of ${nonExistentId} does not exist`);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('throws error when user does not own integration', async () => {
|
|
70
|
-
const record = await integrationRepository.createIntegration(['e1'], 'user-1', { type: 'dummy' });
|
|
71
|
-
|
|
72
|
-
await expect(useCase.execute(record.id, 'different-user'))
|
|
73
|
-
.rejects
|
|
74
|
-
.toThrow(`Integration ${record.id} does not belong to User different-user`);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('throws error when integration class not found', async () => {
|
|
78
|
-
const useCaseWithoutClasses = new DeleteIntegrationForUser({
|
|
79
|
-
integrationRepository,
|
|
80
|
-
integrationClasses: [],
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
const record = await integrationRepository.createIntegration(['e1'], 'user-1', { type: 'dummy' });
|
|
84
|
-
|
|
85
|
-
await expect(useCaseWithoutClasses.execute(record.id, 'user-1'))
|
|
86
|
-
.rejects
|
|
87
|
-
.toThrow();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('tracks failed delete operation for non-existent integration', async () => {
|
|
91
|
-
const nonExistentId = 'non-existent-id';
|
|
92
|
-
integrationRepository.clearHistory();
|
|
93
|
-
|
|
94
|
-
try {
|
|
95
|
-
await useCase.execute(nonExistentId, 'user-1');
|
|
96
|
-
} catch (error) {
|
|
97
|
-
const history = integrationRepository.getOperationHistory();
|
|
98
|
-
const findOperation = history.find(op => op.operation === 'findById');
|
|
99
|
-
expect(findOperation).toEqual({
|
|
100
|
-
operation: 'findById',
|
|
101
|
-
id: nonExistentId,
|
|
102
|
-
found: false
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
describe('edge cases', () => {
|
|
109
|
-
it('handles deletion of already deleted integration', async () => {
|
|
110
|
-
const record = await integrationRepository.createIntegration(['e1'], 'user-1', { type: 'dummy' });
|
|
111
|
-
|
|
112
|
-
await useCase.execute(record.id, 'user-1');
|
|
113
|
-
|
|
114
|
-
await expect(useCase.execute(record.id, 'user-1'))
|
|
115
|
-
.rejects
|
|
116
|
-
.toThrow(`Integration with id of ${record.id} does not exist`);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('handles integration with complex config during deletion', async () => {
|
|
120
|
-
const complexConfig = {
|
|
121
|
-
type: 'dummy',
|
|
122
|
-
settings: { nested: { deep: 'value' } },
|
|
123
|
-
credentials: { encrypted: true }
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const record = await integrationRepository.createIntegration(['e1'], 'user-1', complexConfig);
|
|
127
|
-
|
|
128
|
-
await useCase.execute(record.id, 'user-1');
|
|
129
|
-
|
|
130
|
-
const found = await integrationRepository.findIntegrationById(record.id);
|
|
131
|
-
expect(found).toBeNull();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('handles null userId gracefully', async () => {
|
|
135
|
-
const record = await integrationRepository.createIntegration(['e1'], 'user-1', { type: 'dummy' });
|
|
136
|
-
|
|
137
|
-
await expect(useCase.execute(record.id, null))
|
|
138
|
-
.rejects
|
|
139
|
-
.toThrow(`Integration ${record.id} does not belong to User null`);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('handles undefined userId gracefully', async () => {
|
|
143
|
-
const record = await integrationRepository.createIntegration(['e1'], 'user-1', { type: 'dummy' });
|
|
144
|
-
|
|
145
|
-
await expect(useCase.execute(record.id, undefined))
|
|
146
|
-
.rejects
|
|
147
|
-
.toThrow(`Integration ${record.id} does not belong to User undefined`);
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
});
|