@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.
Files changed (120) hide show
  1. package/database/use-cases/check-migration-status-use-case.js +81 -0
  2. package/generated/prisma-mongodb/client.d.ts +1 -0
  3. package/generated/prisma-mongodb/client.js +4 -0
  4. package/generated/prisma-mongodb/default.d.ts +1 -0
  5. package/generated/prisma-mongodb/default.js +4 -0
  6. package/generated/prisma-mongodb/edge.d.ts +1 -0
  7. package/generated/prisma-mongodb/edge.js +336 -0
  8. package/generated/prisma-mongodb/index-browser.js +318 -0
  9. package/generated/prisma-mongodb/index.d.ts +22993 -0
  10. package/generated/prisma-mongodb/index.js +361 -0
  11. package/generated/prisma-mongodb/package.json +183 -0
  12. package/generated/prisma-mongodb/query-engine-debian-openssl-3.0.x +0 -0
  13. package/generated/prisma-mongodb/query-engine-rhel-openssl-3.0.x +0 -0
  14. package/generated/prisma-mongodb/runtime/binary.d.ts +1 -0
  15. package/generated/prisma-mongodb/runtime/binary.js +289 -0
  16. package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
  17. package/generated/prisma-mongodb/runtime/edge.js +34 -0
  18. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  19. package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
  20. package/generated/prisma-mongodb/runtime/library.d.ts +3977 -0
  21. package/generated/prisma-mongodb/runtime/react-native.js +83 -0
  22. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
  23. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
  24. package/generated/prisma-mongodb/schema.prisma +364 -0
  25. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
  26. package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
  27. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  28. package/generated/prisma-mongodb/wasm.js +343 -0
  29. package/generated/prisma-postgresql/client.d.ts +1 -0
  30. package/generated/prisma-postgresql/client.js +4 -0
  31. package/generated/prisma-postgresql/default.d.ts +1 -0
  32. package/generated/prisma-postgresql/default.js +4 -0
  33. package/generated/prisma-postgresql/edge.d.ts +1 -0
  34. package/generated/prisma-postgresql/edge.js +358 -0
  35. package/generated/prisma-postgresql/index-browser.js +340 -0
  36. package/generated/prisma-postgresql/index.d.ts +25171 -0
  37. package/generated/prisma-postgresql/index.js +383 -0
  38. package/generated/prisma-postgresql/package.json +183 -0
  39. package/generated/prisma-postgresql/query-engine-debian-openssl-3.0.x +0 -0
  40. package/generated/prisma-postgresql/query-engine-rhel-openssl-3.0.x +0 -0
  41. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  42. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  43. package/generated/prisma-postgresql/runtime/binary.d.ts +1 -0
  44. package/generated/prisma-postgresql/runtime/binary.js +289 -0
  45. package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
  46. package/generated/prisma-postgresql/runtime/edge.js +34 -0
  47. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  48. package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
  49. package/generated/prisma-postgresql/runtime/library.d.ts +3977 -0
  50. package/generated/prisma-postgresql/runtime/react-native.js +83 -0
  51. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
  52. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
  53. package/generated/prisma-postgresql/schema.prisma +347 -0
  54. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
  55. package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
  56. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  57. package/generated/prisma-postgresql/wasm.js +365 -0
  58. package/handlers/routers/db-migration.js +52 -0
  59. package/package.json +5 -5
  60. package/application/commands/integration-commands.test.js +0 -123
  61. package/core/Worker.test.js +0 -159
  62. package/database/encryption/encryption-integration.test.js +0 -553
  63. package/database/encryption/encryption-schema-registry.test.js +0 -392
  64. package/database/encryption/field-encryption-service.test.js +0 -525
  65. package/database/encryption/mongo-decryption-fix-verification.test.js +0 -348
  66. package/database/encryption/postgres-decryption-fix-verification.test.js +0 -371
  67. package/database/encryption/postgres-relation-decryption.test.js +0 -245
  68. package/database/encryption/prisma-encryption-extension.test.js +0 -439
  69. package/database/repositories/migration-status-repository-s3.test.js +0 -158
  70. package/database/use-cases/check-encryption-health-use-case.test.js +0 -192
  71. package/database/use-cases/get-migration-status-use-case.test.js +0 -171
  72. package/database/use-cases/run-database-migration-use-case.test.js +0 -310
  73. package/database/use-cases/trigger-database-migration-use-case.test.js +0 -250
  74. package/database/utils/prisma-runner.test.js +0 -486
  75. package/encrypt/Cryptor.test.js +0 -144
  76. package/errors/base-error.test.js +0 -32
  77. package/errors/fetch-error.test.js +0 -79
  78. package/errors/halt-error.test.js +0 -11
  79. package/errors/validation-errors.test.js +0 -120
  80. package/handlers/auth-flow.integration.test.js +0 -147
  81. package/handlers/integration-event-dispatcher.test.js +0 -209
  82. package/handlers/routers/db-migration.test.js +0 -51
  83. package/handlers/routers/health.test.js +0 -210
  84. package/handlers/routers/integration-webhook-routers.test.js +0 -126
  85. package/handlers/use-cases/check-integrations-health-use-case.test.js +0 -125
  86. package/handlers/webhook-flow.integration.test.js +0 -356
  87. package/handlers/workers/db-migration.test.js +0 -50
  88. package/handlers/workers/integration-defined-workers.test.js +0 -184
  89. package/integrations/tests/integration-router-multi-auth.test.js +0 -369
  90. package/integrations/tests/use-cases/create-integration.test.js +0 -131
  91. package/integrations/tests/use-cases/delete-integration-for-user.test.js +0 -150
  92. package/integrations/tests/use-cases/find-integration-context-by-external-entity-id.test.js +0 -92
  93. package/integrations/tests/use-cases/get-integration-for-user.test.js +0 -150
  94. package/integrations/tests/use-cases/get-integration-instance.test.js +0 -176
  95. package/integrations/tests/use-cases/get-integrations-for-user.test.js +0 -176
  96. package/integrations/tests/use-cases/get-possible-integrations.test.js +0 -188
  97. package/integrations/tests/use-cases/update-integration-messages.test.js +0 -142
  98. package/integrations/tests/use-cases/update-integration-status.test.js +0 -103
  99. package/integrations/tests/use-cases/update-integration.test.js +0 -141
  100. package/integrations/use-cases/create-process.test.js +0 -178
  101. package/integrations/use-cases/get-process.test.js +0 -190
  102. package/integrations/use-cases/load-integration-context-full.test.js +0 -329
  103. package/integrations/use-cases/load-integration-context.test.js +0 -114
  104. package/integrations/use-cases/update-process-metrics.test.js +0 -308
  105. package/integrations/use-cases/update-process-state.test.js +0 -256
  106. package/lambda/TimeoutCatcher.test.js +0 -68
  107. package/logs/logger.test.js +0 -76
  108. package/modules/module-hydration.test.js +0 -205
  109. package/modules/requester/requester.test.js +0 -28
  110. package/queues/queuer-util.test.js +0 -132
  111. package/user/tests/use-cases/create-individual-user.test.js +0 -24
  112. package/user/tests/use-cases/create-organization-user.test.js +0 -28
  113. package/user/tests/use-cases/create-token-for-user-id.test.js +0 -19
  114. package/user/tests/use-cases/get-user-from-adopter-jwt.test.js +0 -113
  115. package/user/tests/use-cases/get-user-from-bearer-token.test.js +0 -64
  116. package/user/tests/use-cases/get-user-from-x-frigg-headers.test.js +0 -346
  117. package/user/tests/use-cases/login-user.test.js +0 -220
  118. package/user/tests/user-password-encryption-isolation.test.js +0 -237
  119. package/user/tests/user-password-hashing.test.js +0 -235
  120. 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
- });