@remit/backend 0.0.76 → 0.0.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/backend",
3
- "version": "0.0.76",
3
+ "version": "0.0.78",
4
4
  "description": "Remit Mail Inspector API backend",
5
5
  "license": "MIT",
6
6
  "author": "",
@@ -0,0 +1,12 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, test } from "node:test";
3
+ import { deriveAccountConfigId } from "./auth.js";
4
+
5
+ describe("derived ids are stored primary keys: a changed value orphans every row already written and needs a migration, never a new expectation here", () => {
6
+ test("deriveAccountConfigId pins the account a Cognito subject resolves to", () => {
7
+ assert.equal(
8
+ deriveAccountConfigId("golden-cognito-sub"),
9
+ "97h2fhd0c32ocenk6ju12oxto",
10
+ );
11
+ });
12
+ });
@@ -143,6 +143,22 @@ const createInMemoryOutboxRepository = (): IOutboxMessageRepository => {
143
143
  const acceptingSqsClient = (): SQSClient =>
144
144
  ({ send: async () => ({}) }) as unknown as SQSClient;
145
145
 
146
+ /** A queue that refuses every send until `accept()` is called. */
147
+ const refusingSqsClient = (): { client: SQSClient; accept: () => void } => {
148
+ let accepting = false;
149
+ return {
150
+ client: {
151
+ send: async () => {
152
+ if (!accepting) throw new Error("SQS unavailable");
153
+ return {};
154
+ },
155
+ } as unknown as SQSClient,
156
+ accept: () => {
157
+ accepting = true;
158
+ },
159
+ };
160
+ };
161
+
146
162
  const accountRepository = {
147
163
  get: async () => ({
148
164
  accountId: ACCOUNT_ID,
@@ -210,7 +226,7 @@ const createAttachmentRepository = (
210
226
  },
211
227
  }) as unknown as IOutboxAttachmentRepository;
212
228
 
213
- const installClient = (): void => {
229
+ const installClient = (sqsClient: SQSClient = acceptingSqsClient()): void => {
214
230
  const outboxMessage = createInMemoryOutboxRepository();
215
231
  const storage = createMockStorageService();
216
232
  attachmentRows = new Map<string, OutboxAttachmentItem>();
@@ -230,7 +246,7 @@ const installClient = (): void => {
230
246
  outboxAttachmentService,
231
247
  accountService: accountRepository,
232
248
  sqsSmtpQueueUrl: "http://localhost:9324/queue/outbox-test",
233
- sqsClient: acceptingSqsClient(),
249
+ sqsClient,
234
250
  }),
235
251
  } as unknown as RemitClient);
236
252
  };
@@ -259,6 +275,8 @@ const updateDraft =
259
275
  OutboxDetailOperations.OutboxDetailOperations_updateOutboxMessage as Handler;
260
276
  const deleteDraft =
261
277
  OutboxDetailOperations.OutboxDetailOperations_deleteOutboxMessage as Handler;
278
+ const getMessage =
279
+ OutboxDetailOperations.OutboxDetailOperations_getOutboxMessage as Handler;
262
280
 
263
281
  type Outcome =
264
282
  | { readonly ok: true; readonly body: Record<string, unknown> }
@@ -394,6 +412,90 @@ describe("an outbox entry that has left draft (#604)", () => {
394
412
  });
395
413
  });
396
414
 
415
+ describe("a send whose enqueue fails (#845.8)", () => {
416
+ const draftAgainstQueue = async (
417
+ queue: SQSClient,
418
+ ): Promise<{ outboxMessageId: string; response: APIGatewayProxyResult }> => {
419
+ installClient(queue);
420
+ const draft = await createDraft(
421
+ requestContext({}),
422
+ authorizedEvent({
423
+ accountId: ACCOUNT_ID,
424
+ toAddresses: ["recipient@example.com"],
425
+ subject: "the one that got stuck",
426
+ textBody: "sending",
427
+ }),
428
+ );
429
+ const outboxMessageId = String(draft.outboxMessageId);
430
+ const response = await respond(() =>
431
+ sendMessage(
432
+ requestContext({ params: { outboxMessageId } }),
433
+ authorizedEvent(),
434
+ ),
435
+ );
436
+ return { outboxMessageId, response };
437
+ };
438
+
439
+ const statusOf = async (outboxMessageId: string): Promise<unknown> => {
440
+ const response = await respond(() =>
441
+ getMessage(
442
+ requestContext({ params: { outboxMessageId } }),
443
+ authorizedEvent(),
444
+ ),
445
+ );
446
+ assert.equal(response.statusCode, 200);
447
+ return (JSON.parse(response.body) as { status?: string }).status;
448
+ };
449
+
450
+ it("answers 500, never a success the send never had", async () => {
451
+ const { response } = await draftAgainstQueue(refusingSqsClient().client);
452
+
453
+ assert.equal(response.statusCode, 500);
454
+ });
455
+
456
+ it("leaves the row a draft, not stranded at queued", async () => {
457
+ const { outboxMessageId } = await draftAgainstQueue(
458
+ refusingSqsClient().client,
459
+ );
460
+
461
+ assert.equal(await statusOf(outboxMessageId), OutboxMessageStatus.draft);
462
+ });
463
+
464
+ it("still lets the user discard what could not be queued", async () => {
465
+ const { outboxMessageId } = await draftAgainstQueue(
466
+ refusingSqsClient().client,
467
+ );
468
+
469
+ const response = await respond(() =>
470
+ deleteDraft(
471
+ requestContext({ params: { outboxMessageId } }),
472
+ authorizedEvent(),
473
+ ),
474
+ );
475
+
476
+ assert.equal(response.statusCode, 204);
477
+ });
478
+
479
+ it("sends once the queue comes back", async () => {
480
+ const queue = refusingSqsClient();
481
+ const { outboxMessageId } = await draftAgainstQueue(queue.client);
482
+ queue.accept();
483
+
484
+ const response = await respond(() =>
485
+ sendMessage(
486
+ requestContext({ params: { outboxMessageId } }),
487
+ authorizedEvent(),
488
+ ),
489
+ );
490
+
491
+ assert.equal(response.statusCode, 200);
492
+ assert.equal(
493
+ (JSON.parse(response.body) as { status?: string }).status,
494
+ OutboxMessageStatus.queued,
495
+ );
496
+ });
497
+ });
498
+
397
499
  describe("discarding a draft that carries files (#679)", () => {
398
500
  it("takes the stored bytes with it, leaving nothing behind", async () => {
399
501
  installClient();