@friggframework/core 2.0.0--canary.461.bb7fcba.0 → 2.0.0--canary.461.5767fa4.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/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/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,79 +0,0 @@
|
|
|
1
|
-
const fetch = require('node-fetch');
|
|
2
|
-
const { stripIndent } = require('common-tags');
|
|
3
|
-
const { FetchError } = require('./fetch-error');
|
|
4
|
-
const FormData = require('form-data');
|
|
5
|
-
|
|
6
|
-
describe('FetchError', () => {
|
|
7
|
-
it('can be instantiated with default arguments', () => {
|
|
8
|
-
const error = new FetchError();
|
|
9
|
-
expect(error).toHaveProperty('message');
|
|
10
|
-
expect(error).not.toHaveProperty('cause');
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('can be created asynchronously with default values', async () => {
|
|
14
|
-
const error = await FetchError.create();
|
|
15
|
-
expect(error).toHaveProperty('message');
|
|
16
|
-
expect(error).not.toHaveProperty('cause');
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('can be created asynchronously', async () => {
|
|
20
|
-
const resource = 'http://example.com';
|
|
21
|
-
const init = {};
|
|
22
|
-
const response = {
|
|
23
|
-
status: 500,
|
|
24
|
-
statusText: 'Space aliens!',
|
|
25
|
-
headers: Object.entries({ 'cache-control': '123' }), // needs to be an Iterable
|
|
26
|
-
text: async () => '<!doctype html>',
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const error = await FetchError.create({
|
|
30
|
-
resource,
|
|
31
|
-
init,
|
|
32
|
-
response,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
expect(error).toHaveProperty('message');
|
|
36
|
-
expect(error.message).toContain('GET http://example.com');
|
|
37
|
-
expect(error.message).toContain('500 Space aliens!');
|
|
38
|
-
expect(error.message).toContain('"cache-control": "123"');
|
|
39
|
-
expect(error.message).toContain('<!doctype html>');
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('can be passed an object for the body', async () => {
|
|
43
|
-
const error = new FetchError({ responseBody: { test: true } });
|
|
44
|
-
expect(error).toHaveProperty('message');
|
|
45
|
-
expect(error.message).toContain('"test": true');
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('ignores response body if already streamed', async () => {
|
|
49
|
-
const response = { bodyUsed: true };
|
|
50
|
-
const error = await FetchError.create({ response });
|
|
51
|
-
|
|
52
|
-
expect(error).toHaveProperty('message');
|
|
53
|
-
expect(error.message).toContain('<response body is unavailable>');
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it.only('prints a formData body legibly', async () => {
|
|
57
|
-
const response = {
|
|
58
|
-
status: 500,
|
|
59
|
-
statusText: 'Space aliens!',
|
|
60
|
-
headers: Object.entries({ 'cache-control': '123' }), // needs to be an Iterable
|
|
61
|
-
text: async () => '<!doctype html>',
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const params = new URLSearchParams();
|
|
65
|
-
params.append('test', 'test');
|
|
66
|
-
const init = {
|
|
67
|
-
method: 'POST',
|
|
68
|
-
credentials: 'include',
|
|
69
|
-
headers: {},
|
|
70
|
-
query: {},
|
|
71
|
-
body: params,
|
|
72
|
-
returnFullRes: false,
|
|
73
|
-
};
|
|
74
|
-
const error = await FetchError.create({ response, init });
|
|
75
|
-
|
|
76
|
-
expect(error).toHaveProperty('message');
|
|
77
|
-
expect(error.message).toContain('test=test');
|
|
78
|
-
});
|
|
79
|
-
});
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
const { HaltError } = require('./halt-error');
|
|
2
|
-
|
|
3
|
-
describe('HaltError', () => {
|
|
4
|
-
it('can be instantiated', () => {
|
|
5
|
-
const rootError = new Error('Gremlinoids!!');
|
|
6
|
-
const error = new HaltError('STOP', { cause: rootError });
|
|
7
|
-
expect(error).toHaveProperty('message', 'STOP');
|
|
8
|
-
expect(error).toHaveProperty('cause', rootError);
|
|
9
|
-
expect(error).toHaveProperty('isHaltError', true);
|
|
10
|
-
});
|
|
11
|
-
});
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
const {
|
|
2
|
-
RequiredPropertyError,
|
|
3
|
-
ParameterTypeError,
|
|
4
|
-
} = require('./validation-errors');
|
|
5
|
-
|
|
6
|
-
describe('RequiredPropertyError', () => {
|
|
7
|
-
it('can be instantiated with default arguments', () => {
|
|
8
|
-
const error = new RequiredPropertyError();
|
|
9
|
-
expect(error).toHaveProperty(
|
|
10
|
-
'message',
|
|
11
|
-
'Key "" is a required parameter.'
|
|
12
|
-
);
|
|
13
|
-
expect(error).not.toHaveProperty('cause');
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('allows setting the key name of the error', () => {
|
|
17
|
-
const error = new RequiredPropertyError({ key: 'abc' });
|
|
18
|
-
expect(error).toHaveProperty(
|
|
19
|
-
'message',
|
|
20
|
-
'Key "abc" is a required parameter.'
|
|
21
|
-
);
|
|
22
|
-
expect(error).not.toHaveProperty('cause');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('allows setting the parent of the error', () => {
|
|
26
|
-
const error = new RequiredPropertyError({ parent: class Xyz {} });
|
|
27
|
-
expect(error).toHaveProperty(
|
|
28
|
-
'message',
|
|
29
|
-
'(Xyz) Key "" is a required parameter.'
|
|
30
|
-
);
|
|
31
|
-
expect(error).not.toHaveProperty('cause');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('passes cause through to the Error parent class', () => {
|
|
35
|
-
const error = new RequiredPropertyError(
|
|
36
|
-
{
|
|
37
|
-
parent: class Qrs {},
|
|
38
|
-
key: 'def',
|
|
39
|
-
},
|
|
40
|
-
{ cause: new Error('Gremlins!!') }
|
|
41
|
-
);
|
|
42
|
-
const expectedMessage = '(Qrs) Key "def" is a required parameter.';
|
|
43
|
-
expect(error).toHaveProperty('message', expectedMessage);
|
|
44
|
-
expect(error).toHaveProperty('cause');
|
|
45
|
-
expect(error.cause).toHaveProperty('message', 'Gremlins!!');
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
describe('ParameterTypeError', () => {
|
|
50
|
-
it('can be instantiated with default arguments', () => {
|
|
51
|
-
const error = new ParameterTypeError();
|
|
52
|
-
expect(error).toHaveProperty(
|
|
53
|
-
'message',
|
|
54
|
-
'Expected value "" to be of type ""'
|
|
55
|
-
);
|
|
56
|
-
expect(error).not.toHaveProperty('cause');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('allows setting the key name of the error', () => {
|
|
60
|
-
const error = new ParameterTypeError({ key: 'abc' });
|
|
61
|
-
expect(error).toHaveProperty(
|
|
62
|
-
'message',
|
|
63
|
-
'Expected key "abc" with value "" to be of type ""'
|
|
64
|
-
);
|
|
65
|
-
expect(error).not.toHaveProperty('cause');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('allows setting the parent of the error', () => {
|
|
69
|
-
const error = new ParameterTypeError({ parent: class Xyz {} });
|
|
70
|
-
expect(error).toHaveProperty(
|
|
71
|
-
'message',
|
|
72
|
-
'(Xyz) Expected value "" to be of type ""'
|
|
73
|
-
);
|
|
74
|
-
expect(error).not.toHaveProperty('cause');
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('allows setting the value of the error', () => {
|
|
78
|
-
const error = new ParameterTypeError({ value: 1 });
|
|
79
|
-
expect(error).toHaveProperty(
|
|
80
|
-
'message',
|
|
81
|
-
'Expected value "1" to be of type ""'
|
|
82
|
-
);
|
|
83
|
-
expect(error).not.toHaveProperty('cause');
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('allows setting the expected type of the error', () => {
|
|
87
|
-
const error = new ParameterTypeError({ expectedType: class Xyz {} });
|
|
88
|
-
expect(error).toHaveProperty(
|
|
89
|
-
'message',
|
|
90
|
-
'Expected value "" to be of type "Xyz"'
|
|
91
|
-
);
|
|
92
|
-
expect(error).not.toHaveProperty('cause');
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('allows setting the expected type of the error to Array', () => {
|
|
96
|
-
const error = new ParameterTypeError({ expectedType: Array });
|
|
97
|
-
expect(error).toHaveProperty(
|
|
98
|
-
'message',
|
|
99
|
-
'Expected value "" to be of type "Array"'
|
|
100
|
-
);
|
|
101
|
-
expect(error).not.toHaveProperty('cause');
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('passes cause through to the Error parent class', () => {
|
|
105
|
-
const rootError = new Error('Gremlins!!');
|
|
106
|
-
const error = new ParameterTypeError(
|
|
107
|
-
{
|
|
108
|
-
parent: class Parent {},
|
|
109
|
-
key: 'clé',
|
|
110
|
-
expectedType: class Expected {},
|
|
111
|
-
value: 'schmalue',
|
|
112
|
-
},
|
|
113
|
-
{ cause: rootError }
|
|
114
|
-
);
|
|
115
|
-
const expectedMessage =
|
|
116
|
-
'(Parent) Expected key "clé" with value "schmalue" to be of type "Expected"';
|
|
117
|
-
expect(error).toHaveProperty('message', expectedMessage);
|
|
118
|
-
expect(error).toHaveProperty('cause', rootError);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
@@ -1,147 +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 { IntegrationEventDispatcher } = require('./integration-event-dispatcher');
|
|
9
|
-
const { IntegrationBase } = require('../integrations/integration-base');
|
|
10
|
-
|
|
11
|
-
class SimulatedAsanaIntegration extends IntegrationBase {
|
|
12
|
-
static Definition = {
|
|
13
|
-
name: 'asana',
|
|
14
|
-
version: '1.0.0',
|
|
15
|
-
modules: {},
|
|
16
|
-
routes: [
|
|
17
|
-
{ path: '/auth', method: 'GET', event: 'AUTH_REQUEST' },
|
|
18
|
-
{ path: '/auth/redirect/:provider', method: 'GET', event: 'AUTH_REDIRECT' },
|
|
19
|
-
{ path: '/form', method: 'GET', event: 'LOAD_FORM' },
|
|
20
|
-
],
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
constructor(params = {}) {
|
|
24
|
-
super(params);
|
|
25
|
-
this.events = {
|
|
26
|
-
AUTH_REQUEST: { handler: this.authRequest.bind(this) },
|
|
27
|
-
AUTH_REDIRECT: { handler: this.authRedirect.bind(this) },
|
|
28
|
-
LOAD_FORM: { handler: this.loadForm.bind(this) },
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async authRequest() {
|
|
33
|
-
return {
|
|
34
|
-
success: true,
|
|
35
|
-
action: 'redirect',
|
|
36
|
-
hydrated: this.isHydrated,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async authRedirect({ req }) {
|
|
41
|
-
const { code } = req.query || {};
|
|
42
|
-
return {
|
|
43
|
-
success: true,
|
|
44
|
-
action: 'tokens_received',
|
|
45
|
-
receivedCode: code,
|
|
46
|
-
hydrated: this.isHydrated,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async loadForm() {
|
|
51
|
-
if (!this.isHydrated && SimulatedAsanaIntegration.testRecord) {
|
|
52
|
-
this.setIntegrationRecord({
|
|
53
|
-
record: SimulatedAsanaIntegration.testRecord.record,
|
|
54
|
-
modules: SimulatedAsanaIntegration.testRecord.modules,
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
this.assertHydrated('Integration not found - must authenticate first');
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
success: true,
|
|
62
|
-
form: {
|
|
63
|
-
fields: ['field1', 'field2'],
|
|
64
|
-
},
|
|
65
|
-
integrationId: this.id,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
describe('IntegrationEventDispatcher auth flow', () => {
|
|
71
|
-
const createDispatcher = () =>
|
|
72
|
-
new IntegrationEventDispatcher(new SimulatedAsanaIntegration());
|
|
73
|
-
|
|
74
|
-
beforeEach(() => {
|
|
75
|
-
SimulatedAsanaIntegration.testRecord = null;
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('handles auth request without hydration', async () => {
|
|
79
|
-
const dispatcher = createDispatcher();
|
|
80
|
-
const result = await dispatcher.dispatchHttp({
|
|
81
|
-
event: 'AUTH_REQUEST',
|
|
82
|
-
req: { params: { provider: 'asana' }, query: {} },
|
|
83
|
-
res: {},
|
|
84
|
-
next: jest.fn(),
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
expect(result).toEqual({ success: true, action: 'redirect', hydrated: false });
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('handles auth redirect without hydration', async () => {
|
|
91
|
-
const dispatcher = createDispatcher();
|
|
92
|
-
const result = await dispatcher.dispatchHttp({
|
|
93
|
-
event: 'AUTH_REDIRECT',
|
|
94
|
-
req: { params: { provider: 'asana' }, query: { code: 'abc123' } },
|
|
95
|
-
res: {},
|
|
96
|
-
next: jest.fn(),
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
expect(result).toEqual({
|
|
100
|
-
success: true,
|
|
101
|
-
action: 'tokens_received',
|
|
102
|
-
receivedCode: 'abc123',
|
|
103
|
-
hydrated: false,
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('throws for protected routes when no record is loaded', async () => {
|
|
108
|
-
const dispatcher = createDispatcher();
|
|
109
|
-
await expect(
|
|
110
|
-
dispatcher.dispatchHttp({
|
|
111
|
-
event: 'LOAD_FORM',
|
|
112
|
-
req: { query: {} },
|
|
113
|
-
res: {},
|
|
114
|
-
next: jest.fn(),
|
|
115
|
-
})
|
|
116
|
-
).rejects.toThrow('Integration not found - must authenticate first');
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('allows handlers to hydrate explicitly before continuing', async () => {
|
|
120
|
-
SimulatedAsanaIntegration.testRecord = {
|
|
121
|
-
record: {
|
|
122
|
-
id: 'integration-123',
|
|
123
|
-
userId: 'user-456',
|
|
124
|
-
config: { type: 'asana' },
|
|
125
|
-
status: 'ENABLED',
|
|
126
|
-
version: '1.0.0',
|
|
127
|
-
messages: { errors: [], warnings: [] },
|
|
128
|
-
entities: [],
|
|
129
|
-
},
|
|
130
|
-
modules: [],
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const dispatcher = createDispatcher();
|
|
134
|
-
const result = await dispatcher.dispatchHttp({
|
|
135
|
-
event: 'LOAD_FORM',
|
|
136
|
-
req: { query: {} },
|
|
137
|
-
res: {},
|
|
138
|
-
next: jest.fn(),
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
expect(result).toEqual({
|
|
142
|
-
success: true,
|
|
143
|
-
form: { fields: ['field1', 'field2'] },
|
|
144
|
-
integrationId: 'integration-123',
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
});
|
|
@@ -1,209 +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 { IntegrationEventDispatcher } = require('./integration-event-dispatcher');
|
|
9
|
-
const { IntegrationBase } = require('../integrations/integration-base');
|
|
10
|
-
|
|
11
|
-
class TestIntegration extends IntegrationBase {
|
|
12
|
-
static Definition = {
|
|
13
|
-
name: 'test-integration',
|
|
14
|
-
version: '1.0.0',
|
|
15
|
-
modules: {},
|
|
16
|
-
routes: [
|
|
17
|
-
{ path: '/auth', method: 'GET', event: 'AUTH_REQUEST' },
|
|
18
|
-
{ path: '/data', method: 'GET', event: 'LOAD_DATA' },
|
|
19
|
-
{ path: '/job', method: 'POST', event: 'TEST_EVENT' },
|
|
20
|
-
{ path: '/dynamic', method: 'GET', event: 'DYNAMIC_EVENT' },
|
|
21
|
-
],
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
constructor(params) {
|
|
25
|
-
super(params);
|
|
26
|
-
this.events = {
|
|
27
|
-
AUTH_REQUEST: { handler: this.authRequest.bind(this) },
|
|
28
|
-
LOAD_DATA: { handler: this.loadData.bind(this) },
|
|
29
|
-
TEST_EVENT: { handler: this.testHandler.bind(this) },
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async authRequest() {
|
|
34
|
-
TestIntegration.latestInstance = this;
|
|
35
|
-
return {
|
|
36
|
-
success: true,
|
|
37
|
-
hydrated: this.isHydrated,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async loadData() {
|
|
42
|
-
this.assertHydrated('loadData requires hydration');
|
|
43
|
-
return { success: true };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async testHandler({ data }) {
|
|
47
|
-
TestIntegration.latestInstance = this;
|
|
48
|
-
return { received: data };
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async initialize() {
|
|
52
|
-
this.events = {
|
|
53
|
-
...this.events,
|
|
54
|
-
DYNAMIC_EVENT: { handler: this.dynamicHandler.bind(this) },
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async dynamicHandler() {
|
|
59
|
-
TestIntegration.latestInstance = this;
|
|
60
|
-
return { dynamic: true };
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
describe('IntegrationEventDispatcher', () => {
|
|
65
|
-
const createDispatcher = () =>
|
|
66
|
-
new IntegrationEventDispatcher(new TestIntegration());
|
|
67
|
-
|
|
68
|
-
beforeEach(() => {
|
|
69
|
-
TestIntegration.latestInstance = null;
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
describe('dispatchHttp', () => {
|
|
73
|
-
it('creates a stateless integration instance for HTTP events', async () => {
|
|
74
|
-
const dispatcher = createDispatcher();
|
|
75
|
-
const result = await dispatcher.dispatchHttp({
|
|
76
|
-
event: 'AUTH_REQUEST',
|
|
77
|
-
req: {},
|
|
78
|
-
res: {},
|
|
79
|
-
next: jest.fn(),
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
expect(result).toEqual({ success: true, hydrated: false });
|
|
83
|
-
expect(TestIntegration.latestInstance).toBeInstanceOf(TestIntegration);
|
|
84
|
-
expect(TestIntegration.latestInstance.isHydrated).toBe(false);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('calls initialize to register dynamic events', async () => {
|
|
88
|
-
const dispatcher = createDispatcher();
|
|
89
|
-
await dispatcher.integrationInstance.initialize();
|
|
90
|
-
const result = await dispatcher.dispatchHttp({
|
|
91
|
-
event: 'DYNAMIC_EVENT',
|
|
92
|
-
req: {},
|
|
93
|
-
res: {},
|
|
94
|
-
next: jest.fn(),
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
expect(result).toEqual({ dynamic: true });
|
|
98
|
-
expect(TestIntegration.latestInstance).toBeInstanceOf(TestIntegration);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('throws when requesting an unknown event', async () => {
|
|
102
|
-
const dispatcher = createDispatcher();
|
|
103
|
-
await expect(
|
|
104
|
-
dispatcher.dispatchHttp({
|
|
105
|
-
event: 'UNKNOWN',
|
|
106
|
-
req: {},
|
|
107
|
-
res: {},
|
|
108
|
-
next: jest.fn(),
|
|
109
|
-
})
|
|
110
|
-
).rejects.toThrow('Event UNKNOWN not registered for test-integration');
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('does not hydrate automatically for handlers that require data', async () => {
|
|
114
|
-
const dispatcher = createDispatcher();
|
|
115
|
-
await expect(
|
|
116
|
-
dispatcher.dispatchHttp({
|
|
117
|
-
event: 'LOAD_DATA',
|
|
118
|
-
req: {},
|
|
119
|
-
res: {},
|
|
120
|
-
next: jest.fn(),
|
|
121
|
-
})
|
|
122
|
-
).rejects.toThrow('loadData requires hydration');
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
describe('dispatchJob', () => {
|
|
127
|
-
it('creates a stateless integration instance for job events', async () => {
|
|
128
|
-
const payload = { foo: 'bar' };
|
|
129
|
-
const dispatcher = createDispatcher();
|
|
130
|
-
const result = await dispatcher.dispatchJob({
|
|
131
|
-
event: 'TEST_EVENT',
|
|
132
|
-
data: payload,
|
|
133
|
-
context: {},
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
expect(result).toEqual({ received: payload });
|
|
137
|
-
expect(TestIntegration.latestInstance).toBeInstanceOf(TestIntegration);
|
|
138
|
-
expect(TestIntegration.latestInstance.isHydrated).toBe(false);
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
describe('Webhook Events', () => {
|
|
143
|
-
it('should dispatch WEBHOOK_RECEIVED without hydration', async () => {
|
|
144
|
-
const integration = new TestIntegration();
|
|
145
|
-
integration.events.WEBHOOK_RECEIVED = {
|
|
146
|
-
handler: jest.fn().mockResolvedValue({ received: true })
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const dispatcher = new IntegrationEventDispatcher(integration);
|
|
150
|
-
const req = { body: { test: 'data' }, params: {} };
|
|
151
|
-
const res = {};
|
|
152
|
-
|
|
153
|
-
await dispatcher.dispatchHttp({
|
|
154
|
-
event: 'WEBHOOK_RECEIVED',
|
|
155
|
-
req,
|
|
156
|
-
res,
|
|
157
|
-
next: jest.fn()
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
expect(integration.events.WEBHOOK_RECEIVED.handler).toHaveBeenCalledWith({
|
|
161
|
-
req,
|
|
162
|
-
res,
|
|
163
|
-
next: expect.any(Function)
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('should dispatch ON_WEBHOOK with job context', async () => {
|
|
168
|
-
const integration = new TestIntegration({ id: '123', userId: 'user1' });
|
|
169
|
-
integration.events.ON_WEBHOOK = {
|
|
170
|
-
handler: jest.fn().mockResolvedValue({ processed: true })
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
const dispatcher = new IntegrationEventDispatcher(integration);
|
|
174
|
-
const data = { integrationId: '123', body: { event: 'test' } };
|
|
175
|
-
|
|
176
|
-
await dispatcher.dispatchJob({
|
|
177
|
-
event: 'ON_WEBHOOK',
|
|
178
|
-
data,
|
|
179
|
-
context: {}
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
expect(integration.events.ON_WEBHOOK.handler).toHaveBeenCalledWith({
|
|
183
|
-
data,
|
|
184
|
-
context: {}
|
|
185
|
-
});
|
|
186
|
-
expect(integration.isHydrated).toBe(true);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it('should use default WEBHOOK_RECEIVED handler if not overridden', async () => {
|
|
190
|
-
const integration = new TestIntegration();
|
|
191
|
-
const dispatcher = new IntegrationEventDispatcher(integration);
|
|
192
|
-
|
|
193
|
-
const req = { body: { test: 'data' }, params: {}, headers: {}, query: {} };
|
|
194
|
-
const res = { status: jest.fn().mockReturnThis(), json: jest.fn() };
|
|
195
|
-
|
|
196
|
-
// Mock queueWebhook
|
|
197
|
-
integration.queueWebhook = jest.fn().mockResolvedValue('message-id');
|
|
198
|
-
|
|
199
|
-
const handler = dispatcher.findEventHandler(integration, 'WEBHOOK_RECEIVED');
|
|
200
|
-
expect(handler).toBeDefined();
|
|
201
|
-
|
|
202
|
-
await handler.call(integration, { req, res });
|
|
203
|
-
|
|
204
|
-
expect(integration.queueWebhook).toHaveBeenCalled();
|
|
205
|
-
expect(res.status).toHaveBeenCalledWith(200);
|
|
206
|
-
expect(res.json).toHaveBeenCalledWith({ received: true });
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
});
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Adapter Layer Tests - Database Migration Router
|
|
3
|
-
*
|
|
4
|
-
* CRITICAL TEST: Verify handler loads without app definition
|
|
5
|
-
*
|
|
6
|
-
* Business logic is tested in:
|
|
7
|
-
* - database/use-cases/trigger-database-migration-use-case.test.js (14 tests)
|
|
8
|
-
* - database/use-cases/get-migration-status-use-case.test.js (11 tests)
|
|
9
|
-
*
|
|
10
|
-
* Following hexagonal architecture principles:
|
|
11
|
-
* - Handlers are thin adapters (HTTP → Use Case → HTTP)
|
|
12
|
-
* - Use cases contain all business logic (fully tested)
|
|
13
|
-
* - Repositories are infrastructure adapters (tested separately)
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
process.env.ADMIN_API_KEY = 'test-admin-key';
|
|
17
|
-
process.env.DB_MIGRATION_QUEUE_URL = 'https://sqs.test/queue';
|
|
18
|
-
|
|
19
|
-
// Mock infrastructure dependencies to prevent app definition loading
|
|
20
|
-
jest.mock('../../integrations/repositories/process-repository-postgres', () => ({
|
|
21
|
-
ProcessRepositoryPostgres: jest.fn(() => ({
|
|
22
|
-
create: jest.fn(),
|
|
23
|
-
findById: jest.fn(),
|
|
24
|
-
})),
|
|
25
|
-
}));
|
|
26
|
-
|
|
27
|
-
describe('Database Migration Router - Adapter Layer', () => {
|
|
28
|
-
it('should load without requiring app definition (critical bug fix)', () => {
|
|
29
|
-
// Before fix: createProcessRepository() → getDatabaseType() → loads app definition → requires integrations → CRASH
|
|
30
|
-
// After fix: ProcessRepositoryPostgres instantiated directly → no app definition → SUCCESS
|
|
31
|
-
|
|
32
|
-
expect(() => {
|
|
33
|
-
require('./db-migration');
|
|
34
|
-
}).not.toThrow();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should export handler and router', () => {
|
|
38
|
-
const { handler, router } = require('./db-migration');
|
|
39
|
-
expect(typeof handler).toBe('function');
|
|
40
|
-
expect(typeof router).toBe('function');
|
|
41
|
-
expect(router.stack).toBeDefined();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should hardcode dbType as postgresql (migrations are PostgreSQL-only)', () => {
|
|
45
|
-
// Migration infrastructure is only created for PostgreSQL deployments
|
|
46
|
-
// So we can safely hardcode dbType instead of requiring it from request
|
|
47
|
-
const router = require('./db-migration').router;
|
|
48
|
-
expect(router).toBeDefined();
|
|
49
|
-
// Test will pass if handler doesn't crash when dbType is omitted from request
|
|
50
|
-
});
|
|
51
|
-
});
|