@othree.io/chisel-forge 1.0.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,395 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.runAndValidateAsync = exports.runAndValidate = exports.receiveMessages = exports.createQueueAndSubscribe = exports.cleanUp = exports.isAsyncSqsChiselTestConfiguration = exports.isChiselTestConfiguration = exports.when = exports.CommandExecutionError = void 0;
7
+ /** @module lowLevel */
8
+ const vitest_1 = require("vitest");
9
+ const optional_1 = require("@othree.io/optional");
10
+ const async_1 = require("async");
11
+ const lodash_ismatch_1 = __importDefault(require("lodash.ismatch"));
12
+ /**
13
+ * Custom error class for representing errors that occur during command execution.
14
+ * @class
15
+ * @extends {Error}
16
+ */
17
+ class CommandExecutionError extends Error {
18
+ static ERROR = 'CommandExecutionError';
19
+ command;
20
+ constructor(command, message) {
21
+ super((0, optional_1.Optional)(message).orElse(`Command ${command.type} failed`));
22
+ // https://github.com/Microsoft/TypeScript-wiki/blob/main/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
23
+ Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain
24
+ this.name = CommandExecutionError.ERROR;
25
+ this.command = command;
26
+ this.stack = new Error().stack;
27
+ this.message = (0, optional_1.Optional)(message).orElse(`Command ${command.type} failed`);
28
+ }
29
+ }
30
+ exports.CommandExecutionError = CommandExecutionError;
31
+ /**
32
+ * Creates a Chisel test builder based on a given run and validate function.
33
+ * @template State - The type representing the state.
34
+ * @param {RunAndValidate<State>} runAndValidate - The function to run and validate a command.
35
+ * @returns A function that accepts a command and returns a ChiselTest instance.
36
+ */
37
+ const when = (runAndValidate) =>
38
+ /**
39
+ * Builds a Chisel test for a specific command.
40
+ * @param {Command} command - The command to be tested.
41
+ * @returns {ChiselTest<State>} The ChiselTest instance configured for the given command.
42
+ */
43
+ (command) => {
44
+ const expectedEvents = [];
45
+ const getCommands = [];
46
+ const expectedStateHolder = [(0, optional_1.Empty)()];
47
+ const chiselTest = Object.freeze({
48
+ and: (getCommand) => {
49
+ getCommands.push(getCommand);
50
+ return chiselTest;
51
+ },
52
+ expectEvent: (event) => {
53
+ expectedEvents.push(event);
54
+ const narrowed = Object.freeze({
55
+ toPass: chiselTest.toPass,
56
+ expectState: chiselTest.expectState,
57
+ expectEvent: chiselTest.expectEvent,
58
+ });
59
+ return narrowed;
60
+ },
61
+ expectState: (state) => {
62
+ expectedStateHolder[0] = (0, optional_1.Optional)(state);
63
+ const narrowed = Object.freeze({
64
+ toPass: chiselTest.toPass,
65
+ expectState: chiselTest.expectState,
66
+ expectEvent: chiselTest.expectEvent,
67
+ });
68
+ return narrowed;
69
+ },
70
+ toPass: async () => {
71
+ return await runAndValidate(command, getCommands, expectedStateHolder[0], expectedEvents);
72
+ },
73
+ toFail: async (errorMsg) => {
74
+ const result = await (0, optional_1.TryAsync)(async () => {
75
+ return await runAndValidate(command, getCommands, expectedStateHolder[0], expectedEvents);
76
+ });
77
+ if (result.isPresent) {
78
+ throw new Error('Command expected to fail but succeeded');
79
+ }
80
+ if (!(result.getError() instanceof CommandExecutionError)) {
81
+ throw result.getError();
82
+ }
83
+ const errorMsgOpt = (0, optional_1.Optional)(errorMsg);
84
+ if (errorMsgOpt.isPresent) {
85
+ (0, vitest_1.expect)(result.getError().message).toEqual(errorMsgOpt.get());
86
+ }
87
+ return result.getError();
88
+ },
89
+ });
90
+ return chiselTest;
91
+ };
92
+ exports.when = when;
93
+ /**
94
+ * Determines if the configuration is an ChiselTestConfiguration
95
+ * @param configuration Test configuration
96
+ * @returns {boolean} True is configuration is of type ChiselTestConfiguration
97
+ */
98
+ const isChiselTestConfiguration = (configuration) => {
99
+ return 'commandHandlerArn' in configuration;
100
+ };
101
+ exports.isChiselTestConfiguration = isChiselTestConfiguration;
102
+ /**
103
+ * Determines if the configuration is an AsyncSqsChiselTestConfiguration
104
+ * @param configuration Test configuration
105
+ * @returns {boolean} True is configuration is of type AsyncSqsChiselTestConfiguration
106
+ */
107
+ const isAsyncSqsChiselTestConfiguration = (configuration) => {
108
+ return 'commandQueueUrl' in configuration;
109
+ };
110
+ exports.isAsyncSqsChiselTestConfiguration = isAsyncSqsChiselTestConfiguration;
111
+ /**
112
+ * Performs cleanup operations including deleting a queue, events, and data.
113
+ * @function
114
+ * @param deps - Dependencies for cleanup operations.
115
+ * @returns {CleanUp} A function that performs cleanup operations.
116
+ */
117
+ const cleanUp = (deps) => async (queue, maybeCommandResults) => {
118
+ const maybeDeleted = (await deps.deleteQueue({
119
+ queueUrl: queue.url,
120
+ })).map(_ => true)
121
+ .catch(e => {
122
+ throw new Error(`CleanUp::Failed to delete queue: ${queue.url}. Error: ${e.message}`);
123
+ });
124
+ await (0, optional_1.Optional)(queue.subscriptionArn).mapAsync(async (subscription) => {
125
+ return await deps.unsubscribe({
126
+ subscriptionArn: subscription,
127
+ });
128
+ });
129
+ const dataCleaned = (await maybeCommandResults.mapAsync(async (result) => {
130
+ const events = (await deps.getBy({
131
+ contextId: result[0].events[0].contextId,
132
+ })).catch(e => {
133
+ throw new Error(`CleanUp::Failed to get events from events table. Error: ${e.message}`);
134
+ }).get();
135
+ await Promise.all(events.map(async (event) => {
136
+ return (await deps.deleteBy({
137
+ contextId: event.contextId,
138
+ eventDate: event.eventDate,
139
+ })).get();
140
+ }));
141
+ return true;
142
+ })).orElse(true);
143
+ return maybeDeleted.get() && dataCleaned;
144
+ };
145
+ exports.cleanUp = cleanUp;
146
+ /**
147
+ * Creates and subscribes to an SQS queue.
148
+ * @function
149
+ * @param deps - Dependencies for queue creation and subscription.
150
+ * @returns {CreateQueueAndSubscribe} A function that creates and subscribes to an SQS queue.
151
+ */
152
+ const createQueueAndSubscribe = (deps) => async (configuration) => {
153
+ const queueName = `${configuration.testName}-${deps.now()}`;
154
+ const createQueueRequest = configuration.topicArn.endsWith('fifo') ? {
155
+ queueName: `${queueName}.fifo`,
156
+ fifo: {
157
+ contentBasedDeduplication: true,
158
+ throughputLimit: 'perMessageGroupId',
159
+ deduplicationScope: 'messageGroup',
160
+ },
161
+ } : {
162
+ queueName: queueName,
163
+ };
164
+ const queue = (await deps.createQueue(createQueueRequest)).catch(e => {
165
+ throw new Error(`Failed to create test queue: ${queueName}. Error: ${e.message}`);
166
+ })
167
+ .get();
168
+ const subscription = await deps.subscribeSqs({
169
+ queueUrl: queue.url,
170
+ queueArn: queue.arn,
171
+ }).then(_ => {
172
+ return _.catchAsync(async (e) => {
173
+ await deps.cleanUp({
174
+ url: queue.url,
175
+ arn: queue.arn,
176
+ }, (0, optional_1.Empty)());
177
+ throw new Error(`Failed to subscribe to the bounded context's topic. Error: ${e.message}`);
178
+ })
179
+ .then(_ => _.get());
180
+ });
181
+ await new Promise((resolve) => {
182
+ setTimeout(() => {
183
+ return resolve(true);
184
+ }, configuration.subscriptionAwaitTime);
185
+ });
186
+ return {
187
+ url: queue.url,
188
+ arn: queue.arn,
189
+ subscriptionArn: subscription.subscriptionArn,
190
+ };
191
+ };
192
+ exports.createQueueAndSubscribe = createQueueAndSubscribe;
193
+ /**
194
+ * Asynchronous function for receiving messages from an SQS queue.
195
+ * @function
196
+ * @param deps - Dependencies for receiving messages.
197
+ * @returns A function that receives messages from a specified queue URL.
198
+ */
199
+ const receiveMessages = (deps) => async (queueUrl) => {
200
+ const receiveMessagesInternal = deps.receive({
201
+ client: deps.sqsClient,
202
+ configuration: {
203
+ maxNumberOfMessages: 10,
204
+ waitTimeSeconds: 5,
205
+ QueueUrl: queueUrl,
206
+ autoAcknowledgeMessages: true,
207
+ },
208
+ transformMessage: (message) => message,
209
+ });
210
+ const poll = async (accumulated) => {
211
+ const receivedMessages = (await receiveMessagesInternal())
212
+ .catch(e => {
213
+ throw new Error(`Failed to receive messages from test queue: ${queueUrl}. Error: ${e.message}`);
214
+ })
215
+ .get();
216
+ if (receivedMessages.length === 0) {
217
+ return accumulated;
218
+ }
219
+ return poll(accumulated.concat(receivedMessages));
220
+ };
221
+ return poll([]);
222
+ };
223
+ exports.receiveMessages = receiveMessages;
224
+ /**
225
+ * Runs a command, validates the results, and performs cleanup operations.
226
+ * @function
227
+ * @param deps - Dependencies for running and validating commands.
228
+ * @returns {RunAndValidate<State>} The run and validate function.
229
+ */
230
+ const runAndValidate = (deps) => async (command, getCommands, maybeExpectedState, expectedEvents) => {
231
+ const queue = await deps.createQueueAndSubscribe(deps.configuration);
232
+ const results = await (0, optional_1.TryAsync)(async () => {
233
+ const initialCommandResult = await (await deps.sendCommand(command)).catchAsync(async (e) => {
234
+ throw new CommandExecutionError(command, e.message);
235
+ }).then(_ => _.get());
236
+ const consecuentResults = await (0, async_1.mapSeries)(getCommands, async (getCommand) => {
237
+ return await (await deps.sendCommand(getCommand((0, optional_1.Optional)(initialCommandResult.events[0].contextId))))
238
+ .catchAsync(async (e) => {
239
+ throw new CommandExecutionError(command, e.message);
240
+ }).then(_ => _.get());
241
+ });
242
+ return [initialCommandResult].concat(consecuentResults);
243
+ })
244
+ .then(_ => _.get())
245
+ .catch(e => {
246
+ deps.cleanUp(queue, (0, optional_1.Empty)());
247
+ throw e;
248
+ });
249
+ return (0, optional_1.TryAsync)(async () => {
250
+ const messages = await deps.receiveMessages(queue.url)
251
+ .catch(e => {
252
+ throw new Error(`Failed to receive messages from test queue: ${queue.url}. Error: ${e.message}`);
253
+ });
254
+ if (messages.length === 0) {
255
+ throw new Error('No events were triggered');
256
+ }
257
+ await maybeExpectedState.mapAsync(async (expectedState) => {
258
+ (0, vitest_1.expect)(results[results.length - 1].state).toStrictEqual(expectedState);
259
+ return deps.configuration.loadStateLambdaArn.mapAsync(async (_) => {
260
+ const contextId = results[results.length - 1].events[0].contextId;
261
+ const state = (await deps.loadState({
262
+ contextId: contextId,
263
+ })).catch(e => {
264
+ throw new Error(`Unable to load state for context id ${contextId}. Error: ${e.message}`);
265
+ }).get();
266
+ (0, vitest_1.expect)(state).toStrictEqual(expectedState);
267
+ return true;
268
+ }).then(_ => _.orElse(true));
269
+ }).then(_ => _.orElse(true));
270
+ const chiselEvents = messages.map(message => {
271
+ const bodyString = (0, optional_1.Optional)(message.Body).orElse('{}');
272
+ const body = JSON.parse(bodyString);
273
+ const eventState = JSON.parse(body.Message);
274
+ const chiselEvent = {
275
+ messageAttributes: {
276
+ bc: body.MessageAttributes.bc.Value,
277
+ eventType: body.MessageAttributes.eventType.Value,
278
+ },
279
+ event: eventState.event,
280
+ state: eventState.state,
281
+ };
282
+ return chiselEvent;
283
+ });
284
+ expectedEvents.forEach(expectedEvent => {
285
+ const event = (0, optional_1.Optional)(chiselEvents.find(event => event.messageAttributes.eventType === expectedEvent.type && event.messageAttributes.bc === deps.configuration.boundedContext))
286
+ .orThrow(new Error(`Expected event of type: ${expectedEvent.type} for bounded context: ${deps.configuration.boundedContext} was not triggered`));
287
+ (0, vitest_1.expect)(event.event.contextId).toEqual(expectedEvent.contextId);
288
+ (0, vitest_1.expect)(event.event.body).toStrictEqual(expectedEvent.body);
289
+ });
290
+ return results;
291
+ }).then(maybeAsserted => {
292
+ return maybeAsserted.get();
293
+ }).finally(() => {
294
+ return deps.cleanUp(queue, (0, optional_1.Optional)(results));
295
+ });
296
+ };
297
+ exports.runAndValidate = runAndValidate;
298
+ /**
299
+ * Sends a command to a queue and validates the command events and state.
300
+ * @function
301
+ * @param deps - Dependencies for running and validating async commands.
302
+ * @returns {RunAndValidate<State>} The run and validate function.
303
+ */
304
+ const runAndValidateAsync = (deps) => async (initialCommand, getCommands, maybeExpectedState, expectedEvents) => {
305
+ const queue = await deps.createQueueAndSubscribe(deps.configuration);
306
+ const commandResultHolder = [(0, optional_1.Empty)()];
307
+ const allCommands = [initialCommand];
308
+ await (deps.sendCommand(initialCommand))
309
+ .then(_ => {
310
+ return _.catchAsync(async (e) => {
311
+ await deps.cleanUp(queue, (0, optional_1.Empty)());
312
+ throw new CommandExecutionError(initialCommand, e.message);
313
+ });
314
+ })
315
+ .then(_ => {
316
+ _.get();
317
+ });
318
+ await (0, async_1.forEachSeries)(getCommands, async (getCommand) => {
319
+ const command = getCommand((0, optional_1.Empty)());
320
+ allCommands.push(command);
321
+ await (await deps.sendCommand(command)).catchAsync(async (e) => {
322
+ await deps.cleanUp(queue, (0, optional_1.Empty)());
323
+ throw new CommandExecutionError(command, e.message);
324
+ }).then(_ => _.get());
325
+ });
326
+ const errorMessages = await (await deps.receiveErrorMessages())
327
+ .catchAsync(async () => {
328
+ return [];
329
+ }).then(_ => _.get());
330
+ const maybeError = (0, optional_1.Optional)(errorMessages.find(message => {
331
+ return allCommands.find(command => (0, lodash_ismatch_1.default)(command, message.request));
332
+ }));
333
+ (await maybeError.mapAsync(async (error) => {
334
+ await deps.cleanUp(queue, (0, optional_1.Empty)());
335
+ throw new CommandExecutionError(error.request, error.error.message);
336
+ })).orElse(true);
337
+ return (0, optional_1.TryAsync)(async () => {
338
+ const messages = (await deps.receiveMessages(queue.url)
339
+ .catch(e => {
340
+ throw new Error(`Failed to receive messages from test queue: ${queue.url}. Error: ${e.message}`);
341
+ }))
342
+ .sort();
343
+ if (messages.length === 0) {
344
+ throw new Error('No events were triggered');
345
+ }
346
+ const chiselEvents = messages
347
+ .map(message => {
348
+ const bodyString = (0, optional_1.Optional)(message.Body).orElse('{}');
349
+ const body = JSON.parse(bodyString);
350
+ const eventState = JSON.parse(body.Message);
351
+ const chiselEvent = {
352
+ messageAttributes: {
353
+ bc: body.MessageAttributes.bc.Value,
354
+ eventType: body.MessageAttributes.eventType.Value,
355
+ },
356
+ event: eventState.event,
357
+ state: eventState.state,
358
+ };
359
+ return chiselEvent;
360
+ })
361
+ .sort((m1, m2) => {
362
+ return Number(m1.event.eventDate) - Number(m2.event.eventDate);
363
+ });
364
+ expectedEvents.forEach(expectedEvent => {
365
+ const event = (0, optional_1.Optional)(chiselEvents.find(event => event.messageAttributes.eventType === expectedEvent.type && event.messageAttributes.bc === deps.configuration.boundedContext))
366
+ .orThrow(new Error(`Expected event of type: ${expectedEvent.type} for bounded context: ${deps.configuration.boundedContext} was not triggered`));
367
+ (0, vitest_1.expect)(event.event.contextId).toEqual(expectedEvent.contextId);
368
+ (0, vitest_1.expect)(event.event.body).toStrictEqual(expectedEvent.body);
369
+ });
370
+ commandResultHolder[0] = (0, optional_1.Optional)([{
371
+ state: chiselEvents[chiselEvents.length - 1].state,
372
+ events: chiselEvents.map(_ => _.event),
373
+ }]);
374
+ await maybeExpectedState.mapAsync(async (expectedState) => {
375
+ (0, vitest_1.expect)(commandResultHolder[0].get()[0].state).toStrictEqual(expectedState);
376
+ return deps.configuration.loadStateLambdaArn.mapAsync(async (_) => {
377
+ const contextId = commandResultHolder[0].get()[0].events[0].contextId;
378
+ const state = (await deps.loadState({
379
+ contextId: contextId,
380
+ })).catch(e => {
381
+ throw new Error(`Unable to load state for context id ${contextId}. Error: ${e.message}`);
382
+ }).get();
383
+ (0, vitest_1.expect)(state).toStrictEqual(expectedState);
384
+ return true;
385
+ }).then(_ => _.orElse(true));
386
+ }).then(_ => _.orElse(true));
387
+ return commandResultHolder[0];
388
+ }).then(maybeAsserted => {
389
+ return maybeAsserted.get();
390
+ }).finally(() => {
391
+ return deps.cleanUp(queue, commandResultHolder[0]);
392
+ });
393
+ };
394
+ exports.runAndValidateAsync = runAndValidateAsync;
395
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["/** @module lowLevel */\nimport {expect} from 'vitest'\nimport {Command, CommandResult, InternalTriggeredEvent, ChiselEvent} from '@othree.io/chisel'\nimport {Empty, Optional, TryAsync} from '@othree.io/optional'\nimport {\n sqs, sns, dynamo\n} from '@othree.io/awsome'\nimport {\n Message, SQSClient,\n} from '@aws-sdk/client-sqs'\nimport {forEachSeries, mapSeries} from 'async'\nimport isMatch from 'lodash.ismatch'\n\n/**\n * Custom error class for representing errors that occur during command execution.\n * @class\n * @extends {Error}\n */\nexport class CommandExecutionError extends Error {\n static ERROR = 'CommandExecutionError'\n command: Command\n\n constructor(command: Command, message?: string) {\n super(Optional(message).orElse(`Command ${command.type} failed`))\n // https://github.com/Microsoft/TypeScript-wiki/blob/main/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work\n Object.setPrototypeOf(this, new.target.prototype) // restore prototype chain\n\n this.name = CommandExecutionError.ERROR\n this.command = command\n this.stack = new Error().stack\n this.message = Optional(message).orElse(`Command ${command.type} failed`)\n }\n}\n\n/**\n * Represents an error message related to a command placed in a dead-letter queue (DLQ).\n * @typedef DLQMessage\n */\nexport type DLQMessage = Readonly<{\n request: Command\n error: Readonly<{\n name: string\n message: string\n stack?: string\n }>\n}>\n\n\n/**\n * Represents a query for loading the state using a context identifier.\n * @typedef LoadStateQuery\n */\nexport type LoadStateQuery = Readonly<{\n contextId: string\n}>\n\n\n/**\n * Represents a Chisel test builder that configures expected events and states for a command.\n * @typedef ChiselTest\n * @template State - The type representing the state.\n */\nexport type ChiselTest<State> = Readonly<{\n /**\n * Specifies an expected event that should be produced by the command.\n * @param {ChiselEvent} event - The expected event.\n * @returns {ChiselTest<State>} The ChiselTest instance with the expected event.\n */\n expectEvent: (event: ChiselEvent) => ChiselTest<State>\n /**\n * Specifies an expected state that the system should be in after the command execution.\n * @param {State} state - The expected state.\n * @returns {ChiselTest<State>} The ChiselTest instance with the expected state.\n */\n expectState: (state: State) => ChiselTest<State>\n /**\n * Executes the command and validates the expected results.\n * @returns {Promise<CommandResult<State>>} A promise that resolves to the command(s) results.\n */\n toPass: () => Promise<Array<CommandResult<State>>>\n}>\n\nexport type GetCommand = (contextId: Optional<string>) => Command\n\n/**\n * A specialized extension of the `ChiselTest` type tailored for testing command execution.\n * @typedef CommandChiselTest\n * @template State - The type representing the state used in the test.\n */\nexport type CommandChiselTest<State> = Readonly<{\n and: (getCommand: GetCommand) => CommandChiselTest<State>\n toFail: (errorMsg?: string) => Promise<Error>\n}> & ChiselTest<State>\n\n/**\n * Creates a Chisel test builder based on a given run and validate function.\n * @template State - The type representing the state.\n * @param {RunAndValidate<State>} runAndValidate - The function to run and validate a command.\n * @returns A function that accepts a command and returns a ChiselTest instance.\n */\nexport const when = <State>(runAndValidate: RunAndValidate<State>) =>\n /**\n * Builds a Chisel test for a specific command.\n * @param {Command} command - The command to be tested.\n * @returns {ChiselTest<State>} The ChiselTest instance configured for the given command.\n */\n (command: Command): CommandChiselTest<State> => {\n const expectedEvents: Array<ChiselEvent> = []\n const getCommands: Array<GetCommand> = []\n const expectedStateHolder: [Optional<State>] = [Empty()]\n\n const chiselTest: CommandChiselTest<State> = Object.freeze({\n and: (getCommand: GetCommand) => {\n getCommands.push(getCommand)\n\n return chiselTest\n },\n expectEvent: (event: ChiselEvent) => {\n expectedEvents.push(event)\n\n const narrowed: ChiselTest<State> = Object.freeze({\n toPass: chiselTest.toPass,\n expectState: chiselTest.expectState,\n expectEvent: chiselTest.expectEvent,\n })\n return narrowed\n },\n expectState: (state: State) => {\n expectedStateHolder[0] = Optional(state)\n\n const narrowed: ChiselTest<State> = Object.freeze({\n toPass: chiselTest.toPass,\n expectState: chiselTest.expectState,\n expectEvent: chiselTest.expectEvent,\n })\n return narrowed\n },\n toPass: async (): Promise<Array<CommandResult<State>>> => {\n return await runAndValidate(command, getCommands, expectedStateHolder[0], expectedEvents)\n },\n toFail: async (errorMsg?: string): Promise<Error> => {\n const result = await TryAsync(async () => {\n return await runAndValidate(command, getCommands, expectedStateHolder[0], expectedEvents)\n })\n\n if (result.isPresent) {\n throw new Error('Command expected to fail but succeeded')\n }\n\n if (!(result.getError() instanceof CommandExecutionError)) {\n throw result.getError()\n }\n\n const errorMsgOpt = Optional(errorMsg)\n if (errorMsgOpt.isPresent) {\n expect(result.getError().message).toEqual(errorMsgOpt.get())\n }\n return result.getError()\n\n },\n })\n\n return chiselTest\n }\n\n/**\n * Represents the configuration for all types of tests\n * @typedef BaseTestConfiguration\n */\nexport type BaseTestConfiguration = Readonly<{\n boundedContext: string\n topicArn: string\n eventsTable: string\n subscriptionAwaitTime: number\n testName: string\n loadStateLambdaArn: Optional<string>\n}>\n\n/**\n * Configuration options for a Chisel test.\n * @typedef ChiselTestConfiguration\n */\nexport type ChiselTestConfiguration = Readonly<{\n commandHandlerArn: string\n}> & BaseTestConfiguration\n\n/**\n * Configuration options for an Async SQS Chisel test.\n * @typedef AsyncSqsChiselTestConfiguration\n */\nexport type AsyncSqsChiselTestConfiguration = Readonly<{\n commandQueueUrl: string\n extractMessageGroupId?: (command: Command) => string\n extractMessageDeduplicationId?: (command: Command) => string\n commandDLQUrl: string\n}> & BaseTestConfiguration\n\n/**\n * Configuration options for an Async SNS Chisel test.\n * @typedef AsyncSnsChiselTestConfiguration\n */\nexport type AsyncSnsChiselTestConfiguration = Readonly<{\n commandTopicArn: string\n extractMessageGroupId?: (command: Command) => string\n extractMessageDeduplicationId?: (command: Command) => string\n commandDLQUrl: string\n}> & BaseTestConfiguration\n\n/**\n * Represents the possible configurations for different types of tests.\n * @typedef {ChiselTestConfiguration | AsyncSqsChiselTestConfiguration | AsyncSnsChiselTestConfiguration} TestConfiguration\n */\nexport type TestConfiguration =\n ChiselTestConfiguration\n | AsyncSqsChiselTestConfiguration\n | AsyncSnsChiselTestConfiguration\n\n/**\n * Determines if the configuration is an ChiselTestConfiguration\n * @param configuration Test configuration\n * @returns {boolean} True is configuration is of type ChiselTestConfiguration\n */\nexport const isChiselTestConfiguration = (configuration: TestConfiguration): configuration is ChiselTestConfiguration => {\n return 'commandHandlerArn' in configuration\n}\n\n/**\n * Determines if the configuration is an AsyncSqsChiselTestConfiguration\n * @param configuration Test configuration\n * @returns {boolean} True is configuration is of type AsyncSqsChiselTestConfiguration\n */\nexport const isAsyncSqsChiselTestConfiguration = (configuration: TestConfiguration): configuration is AsyncSqsChiselTestConfiguration => {\n return 'commandQueueUrl' in configuration\n}\n\n/**\n * Represents a Chisel event message containing attributes, an event, and a state.\n * @typedef ChiselEventMessage\n * @template State - The type representing the state.\n */\nexport type ChiselEventMessage<State> = Readonly<{\n messageAttributes: Readonly<{\n bc: string\n eventType: string\n }>\n event: InternalTriggeredEvent\n state: State\n}>\n\n/**\n * Function type for cleanup operations including deleting a queue, events, and data.\n * @typedef {Function} CleanUp\n */\nexport type CleanUp = <State>(queue: SubscribedSqsQueue | sqs.SqsQueue, maybeCommandResults: Optional<Array<CommandResult<State>>>) => Promise<true>\n\n/**\n * Performs cleanup operations including deleting a queue, events, and data.\n * @function\n * @param deps - Dependencies for cleanup operations.\n * @returns {CleanUp} A function that performs cleanup operations.\n */\nexport const cleanUp = (deps: {\n getBy: (keys: dynamo.Keys) => Promise<Optional<Array<InternalTriggeredEvent>>>\n deleteBy: (keys: dynamo.Keys) => Promise<Optional<boolean>>\n deleteQueue: (request: sqs.DeleteQueueRequest) => Promise<Optional<boolean>>\n unsubscribe: (subscription: sns.Subscription) => Promise<Optional<true>>\n}): CleanUp =>\n async <State>(queue: SubscribedSqsQueue | sqs.SqsQueue, maybeCommandResults: Optional<Array<CommandResult<State>>>): Promise<true> => {\n const maybeDeleted = (await deps.deleteQueue({\n queueUrl: queue.url,\n })).map<true>(_ => true)\n .catch(e => {\n throw new Error(`CleanUp::Failed to delete queue: ${queue.url}. Error: ${e.message}`)\n })\n\n await Optional((queue as any).subscriptionArn).mapAsync(async subscription => {\n return await deps.unsubscribe({\n subscriptionArn: subscription,\n })\n })\n\n const dataCleaned = (await maybeCommandResults.mapAsync<true>(async result => {\n const events = (await deps.getBy({\n contextId: result[0].events[0].contextId,\n })).catch(e => {\n throw new Error(`CleanUp::Failed to get events from events table. Error: ${e.message}`)\n }).get()\n\n await Promise.all(events.map(async event => {\n return (await deps.deleteBy({\n contextId: event.contextId,\n eventDate: event.eventDate,\n })).get()\n }))\n\n return true\n })).orElse(true)\n\n return maybeDeleted.get() && dataCleaned\n }\n\n/**\n * Representation of a queue subscribed to a topic\n * @typedef SubscribedSqsQueue\n */\nexport type SubscribedSqsQueue = Readonly<{\n subscriptionArn: string\n}> & sqs.SqsQueue\n\n/**\n * Creates and subscribes to an SQS queue.\n * @typedef {Function} CreateQueueAndSubscribe\n */\nexport type CreateQueueAndSubscribe = (configuration: TestConfiguration) => Promise<SubscribedSqsQueue | sqs.SqsQueue>\n\n/**\n * Creates and subscribes to an SQS queue.\n * @function\n * @param deps - Dependencies for queue creation and subscription.\n * @returns {CreateQueueAndSubscribe} A function that creates and subscribes to an SQS queue.\n */\nexport const createQueueAndSubscribe = (deps: {\n now: () => number\n createQueue: (request: sqs.CreateQueueRequest) => Promise<Optional<sqs.SqsQueue>>\n cleanUp: CleanUp\n subscribeSqs: (request: sns.SubscribeQueueRequest) => Promise<Optional<sns.Subscription>>\n}): CreateQueueAndSubscribe =>\n async (configuration: TestConfiguration): Promise<SubscribedSqsQueue | sqs.SqsQueue> => {\n const queueName = `${configuration.testName}-${deps.now()}`\n\n const createQueueRequest: sqs.CreateQueueRequest = configuration.topicArn.endsWith('fifo') ? {\n queueName: `${queueName}.fifo`,\n fifo: {\n contentBasedDeduplication: true,\n throughputLimit: 'perMessageGroupId',\n deduplicationScope: 'messageGroup',\n },\n } as sqs.CreateFifoQueueRequest : {\n queueName: queueName,\n }\n const queue = (await deps.createQueue(createQueueRequest)).catch<sqs.SqsQueue>(e => {\n throw new Error(`Failed to create test queue: ${queueName}. Error: ${e.message}`)\n })\n .get()\n\n\n const subscription = await deps.subscribeSqs({\n queueUrl: queue.url,\n queueArn: queue.arn,\n }).then(_ => {\n return _.catchAsync(async e => {\n await deps.cleanUp<any>({\n url: queue.url,\n arn: queue.arn,\n }, Empty())\n throw new Error(`Failed to subscribe to the bounded context's topic. Error: ${e.message}`)\n })\n .then(_ => _.get())\n })\n\n await new Promise((resolve) => {\n setTimeout(() => {\n return resolve(true)\n }, configuration.subscriptionAwaitTime)\n })\n\n return {\n url: queue.url,\n arn: queue.arn,\n subscriptionArn: subscription.subscriptionArn,\n }\n }\n\n/**\n * Asynchronous function for receiving messages from an SQS queue.\n * @function\n * @param deps - Dependencies for receiving messages.\n * @returns A function that receives messages from a specified queue URL.\n */\nexport const receiveMessages = (deps: {\n sqsClient: SQSClient\n receive: <T>(deps: Readonly<{\n client: SQSClient\n configuration: sqs.ReceiveMessagesConfiguration\n transformMessage: (message: Message) => T\n }>) => () => Promise<Optional<Array<T>>>\n}) =>\n async (queueUrl: string): Promise<Array<Message>> => {\n const receiveMessagesInternal = deps.receive<Message>({\n client: deps.sqsClient,\n configuration: {\n maxNumberOfMessages: 10,\n waitTimeSeconds: 5,\n QueueUrl: queueUrl,\n autoAcknowledgeMessages: true,\n },\n transformMessage: (message) => message,\n })\n\n const poll = async (accumulated: Array<Message>): Promise<Array<Message>> => {\n const receivedMessages = (await receiveMessagesInternal())\n .catch<Array<Message>>(e => {\n throw new Error(`Failed to receive messages from test queue: ${queueUrl}. Error: ${e.message}`)\n })\n .get()\n\n if (receivedMessages.length === 0) {\n return accumulated\n }\n\n return poll(accumulated.concat(receivedMessages))\n }\n\n return poll([])\n }\n\n/**\n * A function that runs a command, validates the results, and performs cleanup operations.\n * @typedef {Function} RunAndValidate\n */\nexport type RunAndValidate<State> = (command: Command, getCommands: Array<GetCommand>, maybeExpectedState: Optional<State>, expectedEvents: Array<ChiselEvent>) => Promise<Array<CommandResult<State>>>\n\n/**\n * Runs a command, validates the results, and performs cleanup operations.\n * @function\n * @param deps - Dependencies for running and validating commands.\n * @returns {RunAndValidate<State>} The run and validate function.\n */\nexport const runAndValidate = <State>(deps: {\n sendCommand: (command: Command) => Promise<Optional<CommandResult<State>>>\n createQueueAndSubscribe: CreateQueueAndSubscribe\n receiveMessages: (queueUrl: string) => Promise<Array<Message>>\n cleanUp: CleanUp\n configuration: ChiselTestConfiguration\n loadState: (query: LoadStateQuery) => Promise<Optional<State>>\n}): RunAndValidate<State> =>\n async (command: Command, getCommands: Array<GetCommand>, maybeExpectedState: Optional<State>, expectedEvents: Array<ChiselEvent>): Promise<Array<CommandResult<State>>> => {\n\n const queue = await deps.createQueueAndSubscribe(deps.configuration)\n\n const results = await TryAsync(async () => {\n const initialCommandResult = await (await deps.sendCommand(command)).catchAsync<CommandResult<State>>(async e => {\n throw new CommandExecutionError(command, e.message)\n }).then(_ => _.get())\n\n const consecuentResults = await mapSeries(getCommands, async (getCommand: GetCommand) => {\n return await (await deps.sendCommand(getCommand(Optional(initialCommandResult.events[0].contextId))))\n .catchAsync<CommandResult<State>>(async e => {\n throw new CommandExecutionError(command, e.message)\n }).then(_ => _.get())\n })\n\n return [initialCommandResult].concat(consecuentResults)\n })\n .then(_ => _.get())\n .catch(e => {\n deps.cleanUp<State>(queue, Empty())\n throw e\n })\n\n\n return TryAsync(async () => {\n const messages = await deps.receiveMessages(queue.url)\n .catch(e => {\n throw new Error(`Failed to receive messages from test queue: ${queue.url}. Error: ${e.message}`)\n })\n\n if (messages.length === 0) {\n throw new Error('No events were triggered')\n }\n\n await maybeExpectedState.mapAsync<boolean>(async expectedState => {\n expect(results[results.length - 1].state).toStrictEqual(expectedState)\n\n return deps.configuration.loadStateLambdaArn.mapAsync<boolean>(async _ => {\n const contextId = results[results.length - 1].events[0].contextId\n const state = (await deps.loadState({\n contextId: contextId,\n })).catch(e => {\n throw new Error(`Unable to load state for context id ${contextId}. Error: ${e.message}`)\n }).get()\n\n expect(state).toStrictEqual(expectedState)\n\n return true\n }).then(_ => _.orElse(true))\n }).then(_ => _.orElse(true))\n\n const chiselEvents = messages.map(message => {\n const bodyString = Optional(message.Body).orElse('{}')\n const body = JSON.parse(bodyString)\n const eventState = JSON.parse(body.Message)\n const chiselEvent: ChiselEventMessage<State> = {\n messageAttributes: {\n bc: body.MessageAttributes.bc.Value,\n eventType: body.MessageAttributes.eventType.Value,\n },\n event: eventState.event,\n state: eventState.state,\n }\n\n return chiselEvent\n })\n\n expectedEvents.forEach(expectedEvent => {\n const event = Optional(chiselEvents.find(event => event.messageAttributes.eventType === expectedEvent.type && event.messageAttributes.bc === deps.configuration.boundedContext))\n .orThrow(new Error(`Expected event of type: ${expectedEvent.type} for bounded context: ${deps.configuration.boundedContext} was not triggered`))\n\n expect(event.event.contextId).toEqual(expectedEvent.contextId)\n expect(event.event.body).toStrictEqual(expectedEvent.body)\n })\n\n return results\n }).then(maybeAsserted => {\n return maybeAsserted.get()\n }).finally(() => {\n return deps.cleanUp<State>(queue, Optional(results))\n })\n\n }\n\n\n/**\n * Sends a command to a queue and validates the command events and state.\n * @function\n * @param deps - Dependencies for running and validating async commands.\n * @returns {RunAndValidate<State>} The run and validate function.\n */\nexport const runAndValidateAsync = <State>(deps: {\n sendCommand: (command: Command) => Promise<Optional<boolean>>\n createQueueAndSubscribe: CreateQueueAndSubscribe\n receiveMessages: (queueUrl: string) => Promise<Array<Message>>\n cleanUp: CleanUp\n receiveErrorMessages: () => Promise<Optional<Array<DLQMessage>>>\n configuration: AsyncSqsChiselTestConfiguration | AsyncSnsChiselTestConfiguration\n loadState: (query: LoadStateQuery) => Promise<Optional<State>>\n}): RunAndValidate<State> =>\n async (initialCommand: Command, getCommands: Array<GetCommand>, maybeExpectedState: Optional<State>, expectedEvents: Array<ChiselEvent>): Promise<Array<CommandResult<State>>> => {\n\n const queue = await deps.createQueueAndSubscribe(deps.configuration)\n const commandResultHolder: [Optional<Array<CommandResult<State>>>] = [Empty()]\n const allCommands = [initialCommand]\n\n await (deps.sendCommand(initialCommand))\n .then(_ => {\n return _.catchAsync<boolean>(async e => {\n await deps.cleanUp<State>(queue, Empty())\n throw new CommandExecutionError(initialCommand, e.message)\n })\n })\n .then(_ => {\n _.get()\n })\n\n await forEachSeries(getCommands, async (getCommand: GetCommand) => {\n const command = getCommand(Empty<string>())\n allCommands.push(command)\n await (await deps.sendCommand(command)).catchAsync<boolean>(async e => {\n await deps.cleanUp<State>(queue, Empty())\n throw new CommandExecutionError(command, e.message)\n }).then(_ => _.get())\n })\n\n const errorMessages = await (await deps.receiveErrorMessages())\n .catchAsync<Array<DLQMessage>>(async () => {\n return []\n }).then(_ => _.get())\n\n const maybeError = Optional(errorMessages.find(message => {\n return allCommands.find(command => isMatch(command, message.request))\n }))\n\n ;(await maybeError.mapAsync<boolean>(async error => {\n await deps.cleanUp<State>(queue, Empty())\n throw new CommandExecutionError(error.request, error.error.message)\n })).orElse(true)\n\n return TryAsync(async () => {\n const messages = (await deps.receiveMessages(queue.url)\n .catch(e => {\n throw new Error(`Failed to receive messages from test queue: ${queue.url}. Error: ${e.message}`)\n }))\n .sort()\n\n if (messages.length === 0) {\n throw new Error('No events were triggered')\n }\n\n const chiselEvents = messages\n .map(message => {\n const bodyString = Optional(message.Body).orElse('{}')\n const body = JSON.parse(bodyString)\n const eventState = JSON.parse(body.Message)\n const chiselEvent: ChiselEventMessage<State> = {\n messageAttributes: {\n bc: body.MessageAttributes.bc.Value,\n eventType: body.MessageAttributes.eventType.Value,\n },\n event: eventState.event,\n state: eventState.state,\n }\n\n return chiselEvent\n })\n .sort((m1, m2) => {\n return Number(m1.event.eventDate) - Number(m2.event.eventDate)\n })\n\n expectedEvents.forEach(expectedEvent => {\n const event = Optional(chiselEvents.find(event => event.messageAttributes.eventType === expectedEvent.type && event.messageAttributes.bc === deps.configuration.boundedContext))\n .orThrow(new Error(`Expected event of type: ${expectedEvent.type} for bounded context: ${deps.configuration.boundedContext} was not triggered`))\n\n expect(event.event.contextId).toEqual(expectedEvent.contextId)\n expect(event.event.body).toStrictEqual(expectedEvent.body)\n })\n\n commandResultHolder[0] = Optional([{\n state: chiselEvents[chiselEvents.length - 1].state,\n events: chiselEvents.map(_ => _.event),\n }])\n\n await maybeExpectedState.mapAsync<boolean>(async expectedState => {\n expect(commandResultHolder[0].get()[0].state).toStrictEqual(expectedState)\n\n return deps.configuration.loadStateLambdaArn.mapAsync<boolean>(async _ => {\n const contextId = commandResultHolder[0].get()[0].events[0].contextId\n const state = (await deps.loadState({\n contextId: contextId,\n })).catch(e => {\n throw new Error(`Unable to load state for context id ${contextId}. Error: ${e.message}`)\n }).get()\n\n expect(state).toStrictEqual(expectedState)\n\n return true\n }).then(_ => _.orElse(true))\n }).then(_ => _.orElse(true))\n\n return commandResultHolder[0]\n }).then(maybeAsserted => {\n return maybeAsserted.get()\n }).finally(() => {\n return deps.cleanUp<State>(queue, commandResultHolder[0])\n })\n\n }\n"]}
@@ -0,0 +1,130 @@
1
+ import { cleanUp, createQueueAndSubscribe, isAsyncSqsChiselTestConfiguration, isChiselTestConfiguration, receiveMessages, runAndValidate, runAndValidateAsync, when as lowLevelWhen } from './utils';
2
+ import { LambdaClient } from '@aws-sdk/client-lambda';
3
+ import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
4
+ import { lambda, sns, sqs, dynamo } from '@othree.io/awsome';
5
+ import { SQSClient } from '@aws-sdk/client-sqs';
6
+ import { SNSClient } from '@aws-sdk/client-sns';
7
+ import { Empty, Optional } from '@othree.io/optional';
8
+ export * as lowLevel from './utils';
9
+ /**
10
+ * Initializes a testing environment for Chisel framework.
11
+ * @function
12
+ * @param {ChiselTestConfiguration} configuration - The configuration for the Chisel test.
13
+ * @param {TestConfiguration} [credentials] - The optional AWS credentials.
14
+ * @returns A function that initializes a testing environment and returns a low-level when function.
15
+ */
16
+ export const when = (configuration, credentials) => {
17
+ const sqsClient = new SQSClient({
18
+ credentials: credentials
19
+ });
20
+ const createQueueFn = sqs.createQueue({ client: sqsClient });
21
+ const deleteQueueFn = sqs.deleteQueue({ client: sqsClient });
22
+ const snsClient = new SNSClient({
23
+ credentials: credentials
24
+ });
25
+ const snsConfiguration = {
26
+ BatchSize: 10,
27
+ Parallelism: 10,
28
+ TopicArn: configuration.topicArn
29
+ };
30
+ const subscribeSqsFn = sns.subscribeSqs({ snsClient, sqsClient, configuration: snsConfiguration });
31
+ const unsubscribeFn = sns.unsubscribe({ snsClient });
32
+ const dynamoClient = new DynamoDBClient({
33
+ credentials: credentials
34
+ });
35
+ const dynamoConfiguration = {
36
+ TableName: configuration.eventsTable
37
+ };
38
+ const deleteByFn = dynamo.deleteBy({ dynamoDb: dynamoClient, configuration: dynamoConfiguration });
39
+ const queryFn = dynamo.query({ dynamoDb: dynamoClient, configuration: dynamoConfiguration });
40
+ const getByFn = dynamo.getBy({ query: queryFn });
41
+ const cleanUpFn = cleanUp({
42
+ getBy: getByFn,
43
+ deleteBy: deleteByFn,
44
+ deleteQueue: deleteQueueFn,
45
+ unsubscribe: unsubscribeFn,
46
+ });
47
+ const loadState = configuration.loadStateLambdaArn.map(lambdaArn => {
48
+ const lambdaClient = new LambdaClient({
49
+ credentials: credentials
50
+ });
51
+ return lambda.query({ lambda: lambdaClient, functionName: lambdaArn });
52
+ }).orElse(async (payload) => Empty());
53
+ const createQueueAndSubscribeFn = createQueueAndSubscribe({
54
+ now: () => new Date().getTime(),
55
+ createQueue: createQueueFn,
56
+ cleanUp: cleanUpFn,
57
+ subscribeSqs: subscribeSqsFn,
58
+ });
59
+ const receiveMessagesFn = receiveMessages({
60
+ sqsClient: sqsClient,
61
+ receive: sqs.receiveMessages,
62
+ });
63
+ if (isChiselTestConfiguration(configuration)) {
64
+ const lambdaClient = new LambdaClient({
65
+ credentials: credentials
66
+ });
67
+ const commandHandlerClient = lambda.command({ lambda: lambdaClient, functionName: configuration.commandHandlerArn });
68
+ return lowLevelWhen(runAndValidate({
69
+ sendCommand: commandHandlerClient,
70
+ createQueueAndSubscribe: createQueueAndSubscribeFn,
71
+ receiveMessages: receiveMessagesFn,
72
+ cleanUp: cleanUpFn,
73
+ configuration,
74
+ loadState,
75
+ }));
76
+ }
77
+ else {
78
+ const receiveDLQMessages = sqs.receiveMessages({
79
+ client: sqsClient,
80
+ configuration: {
81
+ QueueUrl: configuration.commandDLQUrl,
82
+ maxNumberOfMessages: 10,
83
+ waitTimeSeconds: 5,
84
+ autoAcknowledgeMessages: true,
85
+ },
86
+ transformMessage: (message) => {
87
+ return JSON.parse(Optional(message.Body).orElse('{}'));
88
+ },
89
+ });
90
+ if (isAsyncSqsChiselTestConfiguration(configuration)) {
91
+ const sendCommand = sqs.send({
92
+ client: sqsClient,
93
+ configuration: { QueueUrl: configuration.commandQueueUrl },
94
+ withMessageGroupId: configuration.extractMessageGroupId,
95
+ withDeduplicationId: configuration.extractMessageDeduplicationId,
96
+ });
97
+ return lowLevelWhen(runAndValidateAsync({
98
+ sendCommand,
99
+ createQueueAndSubscribe: createQueueAndSubscribeFn,
100
+ receiveMessages: receiveMessagesFn,
101
+ cleanUp: cleanUpFn,
102
+ receiveErrorMessages: receiveDLQMessages,
103
+ configuration,
104
+ loadState,
105
+ }));
106
+ }
107
+ else {
108
+ const sendCommand = sns.send({
109
+ client: snsClient,
110
+ configuration: {
111
+ TopicArn: configuration.commandTopicArn,
112
+ Parallelism: 10,
113
+ BatchSize: 10,
114
+ },
115
+ withMessageGroupId: configuration.extractMessageGroupId,
116
+ withMessageDeduplicationId: configuration.extractMessageDeduplicationId,
117
+ });
118
+ return lowLevelWhen(runAndValidateAsync({
119
+ sendCommand,
120
+ createQueueAndSubscribe: createQueueAndSubscribeFn,
121
+ receiveMessages: receiveMessagesFn,
122
+ cleanUp: cleanUpFn,
123
+ receiveErrorMessages: receiveDLQMessages,
124
+ configuration,
125
+ loadState,
126
+ }));
127
+ }
128
+ }
129
+ };
130
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,OAAO,EACP,uBAAuB,EAEvB,iCAAiC,EACjC,yBAAyB,EAEzB,eAAe,EACf,cAAc,EACd,mBAAmB,EAEnB,IAAI,IAAI,YAAY,EACvB,MAAM,SAAS,CAAA;AAChB,OAAO,EAAC,YAAY,EAAC,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAC,cAAc,EAAC,MAAM,0BAA0B,CAAA;AACvD,OAAO,EAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAC,MAAM,mBAAmB,CAAA;AAE1D,OAAO,EAAU,SAAS,EAAC,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAA;AAE7C,OAAO,EAAC,KAAK,EAAE,QAAQ,EAAC,MAAM,qBAAqB,CAAA;AAEnD,OAAO,KAAK,QAAQ,MAAM,SAAS,CAAA;AAEnC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,CAAQ,aAAgC,EAAE,WAAqE,EAAE,EAAE;IACnI,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC;QAC5B,WAAW,EAAE,WAAW;KAC3B,CAAC,CAAA;IAEF,MAAM,aAAa,GAAG,GAAG,CAAC,WAAW,CAAC,EAAC,MAAM,EAAE,SAAS,EAAC,CAAC,CAAA;IAC1D,MAAM,aAAa,GAAG,GAAG,CAAC,WAAW,CAAC,EAAC,MAAM,EAAE,SAAS,EAAC,CAAC,CAAA;IAE1D,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC;QAC5B,WAAW,EAAE,WAAW;KAC3B,CAAC,CAAA;IAEF,MAAM,gBAAgB,GAAyB;QAC3C,SAAS,EAAE,EAAE;QACb,WAAW,EAAE,EAAE;QACf,QAAQ,EAAE,aAAa,CAAC,QAAQ;KACnC,CAAA;IAED,MAAM,cAAc,GAAG,GAAG,CAAC,YAAY,CAAC,EAAC,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,gBAAgB,EAAC,CAAC,CAAA;IAEhG,MAAM,aAAa,GAAG,GAAG,CAAC,WAAW,CAAC,EAAC,SAAS,EAAC,CAAC,CAAA;IAElD,MAAM,YAAY,GAAG,IAAI,cAAc,CAAC;QACpC,WAAW,EAAE,WAAW;KAC3B,CAAC,CAAA;IAEF,MAAM,mBAAmB,GAA+B;QACpD,SAAS,EAAE,aAAa,CAAC,WAAW;KACvC,CAAA;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAC,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAC,CAAC,CAAA;IAEhG,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAyB,EAAC,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAC,CAAC,CAAA;IAClH,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAyB,EAAC,KAAK,EAAE,OAAO,EAAC,CAAC,CAAA;IAEtE,MAAM,SAAS,GAAG,OAAO,CAAC;QACtB,KAAK,EAAE,OAAO;QACd,QAAQ,EAAE,UAAU;QACpB,WAAW,EAAE,aAAa;QAC1B,WAAW,EAAE,aAAa;KAC7B,CAAC,CAAA;IAEF,MAAM,SAAS,GAAG,aAAa,CAAC,kBAAkB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;QAC/D,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC;YAClC,WAAW,EAAE,WAAW;SAC3B,CAAC,CAAA;QACF,OAAO,MAAM,CAAC,KAAK,CAAwB,EAAC,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAC,CAAC,CAAA;IAC/F,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,OAAuB,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,CAAA;IAErD,MAAM,yBAAyB,GAAG,uBAAuB,CAAC;QACtD,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE;QAC/B,WAAW,EAAE,aAAa;QAC1B,OAAO,EAAE,SAAS;QAClB,YAAY,EAAE,cAAc;KAC/B,CAAC,CAAA;IACF,MAAM,iBAAiB,GAAG,eAAe,CAAC;QACtC,SAAS,EAAE,SAAS;QACpB,OAAO,EAAE,GAAG,CAAC,eAAe;KAC/B,CAAC,CAAA;IAEF,IAAI,yBAAyB,CAAC,aAAa,CAAC,EAAE,CAAC;QAC3C,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC;YAClC,WAAW,EAAE,WAAW;SAC3B,CAAC,CAAA;QACF,MAAM,oBAAoB,GAAG,MAAM,CAAC,OAAO,CAAgC,EAAC,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,CAAC,iBAAiB,EAAC,CAAC,CAAA;QAEjJ,OAAO,YAAY,CAAC,cAAc,CAAQ;YACtC,WAAW,EAAE,oBAAoB;YACjC,uBAAuB,EAAE,yBAAyB;YAClD,eAAe,EAAE,iBAAiB;YAClC,OAAO,EAAE,SAAS;YAClB,aAAa;YACb,SAAS;SACZ,CAAC,CAAC,CAAA;IACP,CAAC;SAAM,CAAC;QACJ,MAAM,kBAAkB,GAAG,GAAG,CAAC,eAAe,CAAa;YACvD,MAAM,EAAE,SAAS;YACjB,aAAa,EAAE;gBACX,QAAQ,EAAE,aAAa,CAAC,aAAa;gBACrC,mBAAmB,EAAE,EAAE;gBACvB,eAAe,EAAE,CAAC;gBAClB,uBAAuB,EAAE,IAAI;aAChC;YACD,gBAAgB,EAAE,CAAC,OAAgB,EAAE,EAAE;gBACnC,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAe,CAAA;YACxE,CAAC;SACJ,CAAC,CAAA;QAEF,IAAI,iCAAiC,CAAC,aAAa,CAAC,EAAE,CAAC;YACnD,MAAM,WAAW,GAAG,GAAG,CAAC,IAAI,CAAU;gBAClC,MAAM,EAAE,SAAS;gBACjB,aAAa,EAAE,EAAC,QAAQ,EAAE,aAAa,CAAC,eAAe,EAAC;gBACxD,kBAAkB,EAAE,aAAa,CAAC,qBAAqB;gBACvD,mBAAmB,EAAE,aAAa,CAAC,6BAA6B;aACnE,CAAC,CAAA;YAEF,OAAO,YAAY,CAAC,mBAAmB,CAAQ;gBAC3C,WAAW;gBACX,uBAAuB,EAAE,yBAAyB;gBAClD,eAAe,EAAE,iBAAiB;gBAClC,OAAO,EAAE,SAAS;gBAClB,oBAAoB,EAAE,kBAAkB;gBACxC,aAAa;gBACb,SAAS;aACZ,CAAC,CAAC,CAAA;QACP,CAAC;aAAM,CAAC;YACJ,MAAM,WAAW,GAAG,GAAG,CAAC,IAAI,CAAU;gBAClC,MAAM,EAAE,SAAS;gBACjB,aAAa,EAAE;oBACX,QAAQ,EAAE,aAAa,CAAC,eAAe;oBACvC,WAAW,EAAE,EAAE;oBACf,SAAS,EAAE,EAAE;iBAChB;gBACD,kBAAkB,EAAE,aAAa,CAAC,qBAAqB;gBACvD,0BAA0B,EAAE,aAAa,CAAC,6BAA6B;aAC1E,CAAC,CAAA;YAEF,OAAO,YAAY,CAAC,mBAAmB,CAAQ;gBAC3C,WAAW;gBACX,uBAAuB,EAAE,yBAAyB;gBAClD,eAAe,EAAE,iBAAiB;gBAClC,OAAO,EAAE,SAAS;gBAClB,oBAAoB,EAAE,kBAAkB;gBACxC,aAAa;gBACb,SAAS;aACZ,CAAC,CAAC,CAAA;QACP,CAAC;IACL,CAAC;AAEL,CAAC,CAAA","sourcesContent":["import {\n ChiselTestConfiguration,\n cleanUp,\n createQueueAndSubscribe,\n DLQMessage,\n isAsyncSqsChiselTestConfiguration,\n isChiselTestConfiguration,\n LoadStateQuery,\n receiveMessages,\n runAndValidate,\n runAndValidateAsync,\n TestConfiguration,\n when as lowLevelWhen\n} from './utils'\nimport {LambdaClient} from '@aws-sdk/client-lambda'\nimport {DynamoDBClient} from '@aws-sdk/client-dynamodb'\nimport {lambda, sns, sqs, dynamo} from '@othree.io/awsome'\nimport {Command, CommandResult, InternalTriggeredEvent} from '@othree.io/chisel'\nimport {Message, SQSClient} from '@aws-sdk/client-sqs'\nimport {SNSClient} from '@aws-sdk/client-sns'\nimport {AwsCredentialIdentity, Provider} from '@aws-sdk/types'\nimport {Empty, Optional} from '@othree.io/optional'\n\nexport * as lowLevel from './utils'\n\n/**\n * Initializes a testing environment for Chisel framework.\n * @function\n * @param {ChiselTestConfiguration} configuration - The configuration for the Chisel test.\n * @param {TestConfiguration} [credentials] - The optional AWS credentials.\n * @returns A function that initializes a testing environment and returns a low-level when function.\n */\nexport const when = <State>(configuration: TestConfiguration, credentials?: AwsCredentialIdentity | Provider<AwsCredentialIdentity>) => {\n const sqsClient = new SQSClient({\n credentials: credentials\n })\n\n const createQueueFn = sqs.createQueue({client: sqsClient})\n const deleteQueueFn = sqs.deleteQueue({client: sqsClient})\n\n const snsClient = new SNSClient({\n credentials: credentials\n })\n\n const snsConfiguration: sns.SNSConfiguration = {\n BatchSize: 10,\n Parallelism: 10,\n TopicArn: configuration.topicArn\n }\n\n const subscribeSqsFn = sns.subscribeSqs({snsClient, sqsClient, configuration: snsConfiguration})\n\n const unsubscribeFn = sns.unsubscribe({snsClient})\n\n const dynamoClient = new DynamoDBClient({\n credentials: credentials\n })\n\n const dynamoConfiguration: dynamo.DynamoConfiguration = {\n TableName: configuration.eventsTable\n }\n\n const deleteByFn = dynamo.deleteBy({dynamoDb: dynamoClient, configuration: dynamoConfiguration})\n\n const queryFn = dynamo.query<InternalTriggeredEvent>({dynamoDb: dynamoClient, configuration: dynamoConfiguration})\n const getByFn = dynamo.getBy<InternalTriggeredEvent>({query: queryFn})\n\n const cleanUpFn = cleanUp({\n getBy: getByFn,\n deleteBy: deleteByFn,\n deleteQueue: deleteQueueFn,\n unsubscribe: unsubscribeFn,\n })\n\n const loadState = configuration.loadStateLambdaArn.map(lambdaArn => {\n const lambdaClient = new LambdaClient({\n credentials: credentials\n })\n return lambda.query<LoadStateQuery, State>({lambda: lambdaClient, functionName: lambdaArn})\n }).orElse(async (payload: LoadStateQuery) => Empty())\n\n const createQueueAndSubscribeFn = createQueueAndSubscribe({\n now: () => new Date().getTime(),\n createQueue: createQueueFn,\n cleanUp: cleanUpFn,\n subscribeSqs: subscribeSqsFn,\n })\n const receiveMessagesFn = receiveMessages({\n sqsClient: sqsClient,\n receive: sqs.receiveMessages,\n })\n\n if (isChiselTestConfiguration(configuration)) {\n const lambdaClient = new LambdaClient({\n credentials: credentials\n })\n const commandHandlerClient = lambda.command<Command, CommandResult<State>>({lambda: lambdaClient, functionName: configuration.commandHandlerArn})\n\n return lowLevelWhen(runAndValidate<State>({\n sendCommand: commandHandlerClient,\n createQueueAndSubscribe: createQueueAndSubscribeFn,\n receiveMessages: receiveMessagesFn,\n cleanUp: cleanUpFn,\n configuration,\n loadState,\n }))\n } else {\n const receiveDLQMessages = sqs.receiveMessages<DLQMessage>({\n client: sqsClient,\n configuration: {\n QueueUrl: configuration.commandDLQUrl,\n maxNumberOfMessages: 10,\n waitTimeSeconds: 5,\n autoAcknowledgeMessages: true,\n },\n transformMessage: (message: Message) => {\n return JSON.parse(Optional(message.Body).orElse('{}')) as DLQMessage\n },\n })\n\n if (isAsyncSqsChiselTestConfiguration(configuration)) {\n const sendCommand = sqs.send<Command>({\n client: sqsClient,\n configuration: {QueueUrl: configuration.commandQueueUrl},\n withMessageGroupId: configuration.extractMessageGroupId,\n withDeduplicationId: configuration.extractMessageDeduplicationId,\n })\n\n return lowLevelWhen(runAndValidateAsync<State>({\n sendCommand,\n createQueueAndSubscribe: createQueueAndSubscribeFn,\n receiveMessages: receiveMessagesFn,\n cleanUp: cleanUpFn,\n receiveErrorMessages: receiveDLQMessages,\n configuration,\n loadState,\n }))\n } else {\n const sendCommand = sns.send<Command>({\n client: snsClient,\n configuration: {\n TopicArn: configuration.commandTopicArn,\n Parallelism: 10,\n BatchSize: 10,\n },\n withMessageGroupId: configuration.extractMessageGroupId,\n withMessageDeduplicationId: configuration.extractMessageDeduplicationId,\n })\n\n return lowLevelWhen(runAndValidateAsync<State>({\n sendCommand,\n createQueueAndSubscribe: createQueueAndSubscribeFn,\n receiveMessages: receiveMessagesFn,\n cleanUp: cleanUpFn,\n receiveErrorMessages: receiveDLQMessages,\n configuration,\n loadState,\n }))\n }\n }\n\n}\n"]}