@friggframework/devtools 2.0.0--canary.461.8cf93ae.0 → 2.0.0--canary.474.213c7d9.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 (32) hide show
  1. package/infrastructure/ARCHITECTURE.md +487 -0
  2. package/infrastructure/domains/database/aurora-builder.js +234 -57
  3. package/infrastructure/domains/database/aurora-builder.test.js +7 -2
  4. package/infrastructure/domains/database/aurora-resolver.js +210 -0
  5. package/infrastructure/domains/database/aurora-resolver.test.js +347 -0
  6. package/infrastructure/domains/database/migration-builder.js +256 -215
  7. package/infrastructure/domains/database/migration-builder.test.js +5 -111
  8. package/infrastructure/domains/database/migration-resolver.js +163 -0
  9. package/infrastructure/domains/database/migration-resolver.test.js +337 -0
  10. package/infrastructure/domains/integration/integration-builder.js +258 -84
  11. package/infrastructure/domains/integration/integration-resolver.js +170 -0
  12. package/infrastructure/domains/integration/integration-resolver.test.js +369 -0
  13. package/infrastructure/domains/networking/vpc-builder.js +856 -135
  14. package/infrastructure/domains/networking/vpc-builder.test.js +10 -6
  15. package/infrastructure/domains/networking/vpc-resolver.js +324 -0
  16. package/infrastructure/domains/networking/vpc-resolver.test.js +501 -0
  17. package/infrastructure/domains/security/kms-builder.js +179 -22
  18. package/infrastructure/domains/security/kms-resolver.js +96 -0
  19. package/infrastructure/domains/security/kms-resolver.test.js +216 -0
  20. package/infrastructure/domains/shared/base-resolver.js +186 -0
  21. package/infrastructure/domains/shared/base-resolver.test.js +305 -0
  22. package/infrastructure/domains/shared/cloudformation-discovery-v2.js +334 -0
  23. package/infrastructure/domains/shared/cloudformation-discovery.test.js +26 -1
  24. package/infrastructure/domains/shared/types/app-definition.js +205 -0
  25. package/infrastructure/domains/shared/types/discovery-result.js +106 -0
  26. package/infrastructure/domains/shared/types/discovery-result.test.js +258 -0
  27. package/infrastructure/domains/shared/types/index.js +46 -0
  28. package/infrastructure/domains/shared/types/resource-ownership.js +108 -0
  29. package/infrastructure/domains/shared/types/resource-ownership.test.js +101 -0
  30. package/package.json +6 -6
  31. package/infrastructure/REFACTOR.md +0 -532
  32. package/infrastructure/TRANSFORMATION-VISUAL.md +0 -239
@@ -0,0 +1,369 @@
1
+ /**
2
+ * Tests for IntegrationResourceResolver
3
+ */
4
+
5
+ const IntegrationResourceResolver = require('./integration-resolver');
6
+ const { ResourceOwnership } = require('../shared/types/resource-ownership');
7
+
8
+ describe('IntegrationResourceResolver', () => {
9
+ let resolver;
10
+
11
+ beforeEach(() => {
12
+ resolver = new IntegrationResourceResolver();
13
+ });
14
+
15
+ describe('resolveAll', () => {
16
+ it('should resolve InternalErrorQueue and per-integration queues', () => {
17
+ const appDef = {
18
+ integrations: [
19
+ { Definition: { name: 'slack' } },
20
+ { Definition: { name: 'hubspot' } },
21
+ ],
22
+ };
23
+
24
+ const discovery = {
25
+ fromCloudFormationStack: false,
26
+ stackName: null,
27
+ existingLogicalIds: [],
28
+ stackManaged: [],
29
+ stackResources: {},
30
+ external: [],
31
+ };
32
+
33
+ const decisions = resolver.resolveAll(appDef, discovery);
34
+
35
+ expect(decisions.internalErrorQueue).toBeDefined();
36
+ expect(decisions.internalErrorQueue.ownership).toBe(ResourceOwnership.STACK);
37
+ expect(decisions.integrations.slack).toBeDefined();
38
+ expect(decisions.integrations.slack.queue.ownership).toBe(ResourceOwnership.STACK);
39
+ expect(decisions.integrations.hubspot).toBeDefined();
40
+ expect(decisions.integrations.hubspot.queue.ownership).toBe(ResourceOwnership.STACK);
41
+ });
42
+
43
+ it('should skip integrations without Definition.name', () => {
44
+ const appDef = {
45
+ integrations: [
46
+ { Definition: { name: 'slack' } },
47
+ { Definition: {} }, // Missing name
48
+ { something: 'else' }, // Missing Definition
49
+ ],
50
+ };
51
+
52
+ const discovery = {
53
+ fromCloudFormationStack: false,
54
+ stackName: null,
55
+ existingLogicalIds: [],
56
+ stackManaged: [],
57
+ stackResources: {},
58
+ external: [],
59
+ };
60
+
61
+ const decisions = resolver.resolveAll(appDef, discovery);
62
+
63
+ expect(decisions.integrations.slack).toBeDefined();
64
+ expect(Object.keys(decisions.integrations)).toHaveLength(1);
65
+ });
66
+ });
67
+
68
+ describe('resolveInternalErrorQueue', () => {
69
+ it('should create in stack when nothing exists (AUTO)', () => {
70
+ const appDef = { integrations: [] };
71
+ const discovery = {
72
+ fromCloudFormationStack: false,
73
+ stackName: null,
74
+ existingLogicalIds: [],
75
+ stackManaged: [],
76
+ stackResources: {},
77
+ external: [],
78
+ };
79
+
80
+ const decision = resolver.resolveInternalErrorQueue(appDef, discovery);
81
+
82
+ expect(decision.ownership).toBe(ResourceOwnership.STACK);
83
+ expect(decision.physicalId).toBeNull();
84
+ expect(decision.reason).toContain('will create in stack');
85
+ });
86
+
87
+ it('should use stack resource when found (AUTO)', () => {
88
+ const appDef = { integrations: [] };
89
+ const discovery = {
90
+ fromCloudFormationStack: true,
91
+ stackName: 'test-stack',
92
+ existingLogicalIds: ['InternalErrorQueue'],
93
+ stackManaged: [
94
+ {
95
+ logicalId: 'InternalErrorQueue',
96
+ physicalId: 'https://sqs.us-east-1.amazonaws.com/123456789/internal-error-queue',
97
+ type: 'AWS::SQS::Queue',
98
+ },
99
+ ],
100
+ stackResources: {
101
+ InternalErrorQueue: {
102
+ logicalId: 'InternalErrorQueue',
103
+ physicalId: 'https://sqs.us-east-1.amazonaws.com/123456789/internal-error-queue',
104
+ type: 'AWS::SQS::Queue',
105
+ },
106
+ },
107
+ external: [],
108
+ };
109
+
110
+ const decision = resolver.resolveInternalErrorQueue(appDef, discovery);
111
+
112
+ expect(decision.ownership).toBe(ResourceOwnership.STACK);
113
+ expect(decision.physicalId).toBe('https://sqs.us-east-1.amazonaws.com/123456789/internal-error-queue');
114
+ expect(decision.reason).toContain('Found InternalErrorQueue in CloudFormation stack');
115
+ });
116
+
117
+ it('should respect explicit STACK ownership', () => {
118
+ const appDef = {
119
+ integrations: {
120
+ ownership: {
121
+ internalErrorQueue: ResourceOwnership.STACK,
122
+ },
123
+ },
124
+ };
125
+ const discovery = {
126
+ fromCloudFormationStack: false,
127
+ stackName: null,
128
+ existingLogicalIds: [],
129
+ stackManaged: [],
130
+ stackResources: {},
131
+ external: [],
132
+ };
133
+
134
+ const decision = resolver.resolveInternalErrorQueue(appDef, discovery);
135
+
136
+ expect(decision.ownership).toBe(ResourceOwnership.STACK);
137
+ expect(decision.physicalId).toBeNull();
138
+ });
139
+
140
+ it('should use external ARN when ownership=EXTERNAL', () => {
141
+ const appDef = {
142
+ integrations: {
143
+ ownership: {
144
+ internalErrorQueue: ResourceOwnership.EXTERNAL,
145
+ },
146
+ internalErrorQueue: {
147
+ arn: 'arn:aws:sqs:us-east-1:123456789:my-error-queue',
148
+ },
149
+ },
150
+ };
151
+ const discovery = {
152
+ fromCloudFormationStack: false,
153
+ stackName: null,
154
+ existingLogicalIds: [],
155
+ stackManaged: [],
156
+ stackResources: {},
157
+ external: [],
158
+ };
159
+
160
+ const decision = resolver.resolveInternalErrorQueue(appDef, discovery);
161
+
162
+ expect(decision.ownership).toBe(ResourceOwnership.EXTERNAL);
163
+ expect(decision.physicalId).toBe('arn:aws:sqs:us-east-1:123456789:my-error-queue');
164
+ expect(decision.reason).toContain('Using external InternalErrorQueue ARN');
165
+ });
166
+
167
+ it('should throw when EXTERNAL ownership but no ARN provided', () => {
168
+ const appDef = {
169
+ integrations: {
170
+ ownership: {
171
+ internalErrorQueue: ResourceOwnership.EXTERNAL,
172
+ },
173
+ },
174
+ };
175
+ const discovery = {
176
+ fromCloudFormationStack: false,
177
+ stackName: null,
178
+ existingLogicalIds: [],
179
+ stackManaged: [],
180
+ stackResources: {},
181
+ external: [],
182
+ };
183
+
184
+ expect(() => {
185
+ resolver.resolveInternalErrorQueue(appDef, discovery);
186
+ }).toThrow('InternalErrorQueue configured with ownership=external');
187
+ });
188
+ });
189
+
190
+ describe('resolveQueue', () => {
191
+ it('should create in stack when nothing exists (AUTO)', () => {
192
+ const appDef = {
193
+ integrations: [{ Definition: { name: 'slack' } }],
194
+ };
195
+ const discovery = {
196
+ fromCloudFormationStack: false,
197
+ stackName: null,
198
+ existingLogicalIds: [],
199
+ stackManaged: [],
200
+ stackResources: {},
201
+ external: [],
202
+ };
203
+
204
+ const decision = resolver.resolveQueue('slack', appDef, discovery);
205
+
206
+ expect(decision.ownership).toBe(ResourceOwnership.STACK);
207
+ expect(decision.physicalId).toBeNull();
208
+ expect(decision.reason).toContain('will create in stack');
209
+ });
210
+
211
+ it('should use stack resource when found (AUTO)', () => {
212
+ const appDef = {
213
+ integrations: [{ Definition: { name: 'slack' } }],
214
+ };
215
+ const discovery = {
216
+ fromCloudFormationStack: true,
217
+ stackName: 'test-stack',
218
+ existingLogicalIds: ['SlackQueue'],
219
+ stackManaged: [
220
+ {
221
+ logicalId: 'SlackQueue',
222
+ physicalId: 'https://sqs.us-east-1.amazonaws.com/123456789/slack-queue',
223
+ type: 'AWS::SQS::Queue',
224
+ },
225
+ ],
226
+ stackResources: {
227
+ SlackQueue: {
228
+ logicalId: 'SlackQueue',
229
+ physicalId: 'https://sqs.us-east-1.amazonaws.com/123456789/slack-queue',
230
+ type: 'AWS::SQS::Queue',
231
+ },
232
+ },
233
+ external: [],
234
+ };
235
+
236
+ const decision = resolver.resolveQueue('slack', appDef, discovery);
237
+
238
+ expect(decision.ownership).toBe(ResourceOwnership.STACK);
239
+ expect(decision.physicalId).toBe('https://sqs.us-east-1.amazonaws.com/123456789/slack-queue');
240
+ expect(decision.reason).toContain('Found SlackQueue in CloudFormation stack');
241
+ });
242
+
243
+ it('should respect per-integration STACK ownership', () => {
244
+ const appDef = {
245
+ integrations: [
246
+ {
247
+ Definition: { name: 'slack' },
248
+ ownership: { queue: ResourceOwnership.STACK },
249
+ },
250
+ ],
251
+ };
252
+ const discovery = {
253
+ fromCloudFormationStack: false,
254
+ stackName: null,
255
+ existingLogicalIds: [],
256
+ stackManaged: [],
257
+ stackResources: {},
258
+ external: [],
259
+ };
260
+
261
+ const decision = resolver.resolveQueue('slack', appDef, discovery);
262
+
263
+ expect(decision.ownership).toBe(ResourceOwnership.STACK);
264
+ expect(decision.physicalId).toBeNull();
265
+ });
266
+
267
+ it('should use external URL when ownership=EXTERNAL', () => {
268
+ const appDef = {
269
+ integrations: [
270
+ {
271
+ Definition: { name: 'slack' },
272
+ ownership: { queue: ResourceOwnership.EXTERNAL },
273
+ queue: { url: 'https://sqs.us-east-1.amazonaws.com/123456789/my-slack-queue' },
274
+ },
275
+ ],
276
+ };
277
+ const discovery = {
278
+ fromCloudFormationStack: false,
279
+ stackName: null,
280
+ existingLogicalIds: [],
281
+ stackManaged: [],
282
+ stackResources: {},
283
+ external: [],
284
+ };
285
+
286
+ const decision = resolver.resolveQueue('slack', appDef, discovery);
287
+
288
+ expect(decision.ownership).toBe(ResourceOwnership.EXTERNAL);
289
+ expect(decision.physicalId).toBe('https://sqs.us-east-1.amazonaws.com/123456789/my-slack-queue');
290
+ expect(decision.reason).toContain('Using external queue URL');
291
+ });
292
+
293
+ it('should throw when EXTERNAL ownership but no URL provided', () => {
294
+ const appDef = {
295
+ integrations: [
296
+ {
297
+ Definition: { name: 'slack' },
298
+ ownership: { queue: ResourceOwnership.EXTERNAL },
299
+ },
300
+ ],
301
+ };
302
+ const discovery = {
303
+ fromCloudFormationStack: false,
304
+ stackName: null,
305
+ existingLogicalIds: [],
306
+ stackManaged: [],
307
+ stackResources: {},
308
+ external: [],
309
+ };
310
+
311
+ expect(() => {
312
+ resolver.resolveQueue('slack', appDef, discovery);
313
+ }).toThrow("Integration 'slack' configured with ownership=external but queue.url not provided");
314
+ });
315
+
316
+ it('should handle multiple integrations with different queues', () => {
317
+ const appDef = {
318
+ integrations: [
319
+ { Definition: { name: 'slack' } },
320
+ { Definition: { name: 'hubspot' } },
321
+ ],
322
+ };
323
+ const discovery = {
324
+ fromCloudFormationStack: true,
325
+ stackName: 'test-stack',
326
+ existingLogicalIds: ['SlackQueue'],
327
+ stackManaged: [
328
+ {
329
+ logicalId: 'SlackQueue',
330
+ physicalId: 'https://sqs.us-east-1.amazonaws.com/123456789/slack-queue',
331
+ type: 'AWS::SQS::Queue',
332
+ },
333
+ ],
334
+ stackResources: {
335
+ SlackQueue: {
336
+ logicalId: 'SlackQueue',
337
+ physicalId: 'https://sqs.us-east-1.amazonaws.com/123456789/slack-queue',
338
+ type: 'AWS::SQS::Queue',
339
+ },
340
+ },
341
+ external: [],
342
+ };
343
+
344
+ const slackDecision = resolver.resolveQueue('slack', appDef, discovery);
345
+ const hubspotDecision = resolver.resolveQueue('hubspot', appDef, discovery);
346
+
347
+ expect(slackDecision.ownership).toBe(ResourceOwnership.STACK);
348
+ expect(slackDecision.physicalId).toBe('https://sqs.us-east-1.amazonaws.com/123456789/slack-queue');
349
+ expect(hubspotDecision.ownership).toBe(ResourceOwnership.STACK);
350
+ expect(hubspotDecision.physicalId).toBeNull(); // HubspotQueue not in stack
351
+ });
352
+ });
353
+
354
+ describe('capitalizeFirst', () => {
355
+ it('should capitalize first letter', () => {
356
+ expect(resolver.capitalizeFirst('slack')).toBe('Slack');
357
+ expect(resolver.capitalizeFirst('hubspot')).toBe('Hubspot');
358
+ expect(resolver.capitalizeFirst('s')).toBe('S');
359
+ });
360
+
361
+ it('should handle already capitalized strings', () => {
362
+ expect(resolver.capitalizeFirst('Slack')).toBe('Slack');
363
+ });
364
+
365
+ it('should handle empty string', () => {
366
+ expect(resolver.capitalizeFirst('')).toBe('');
367
+ });
368
+ });
369
+ });