aboutsummaryrefslogtreecommitdiff
path: root/deno/mail-relay/mail.test.ts
blob: 09cf8ebed439473492ecc24f796819e2094d1836 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import { describe, it } from "@std/testing/bdd";
import { expect, fn } from "@std/expect";

import { Logger } from "@crupest/base/log";

import { Mail, MailDeliverContext, MailDeliverer } from "./mail.ts";

const mockDate = "Fri, 02 May 2025 08:33:02 +0000";
const mockMessageId = "mock-message-id@from.mock";
const mockMessageId2 = "mock-message-id-2@from.mock";
const mockFromAddress = "mock@from.mock";
const mockCcAddress = "mock@cc.mock";
const mockBodyStr = `This is body content.
Line 2 ${mockMessageId2}

Line 4`;
const mockHeaders = [
  ["Content-Disposition", "inline"],
  ["Content-Transfer-Encoding", "quoted-printable"],
  ["MIME-Version", "1.0"],
  ["X-Mailer", "MIME-tools 5.509 (Entity 5.509)"],
  ["Content-Type", "text/plain; charset=utf-8"],
  ["From", `"Mock From" <${mockFromAddress}>`],
  [
    "To",
    `"John \\"Big\\" Doe" <john@example.com>, "Alice (Work)" <alice+work@example.com>,
 undisclosed-recipients:;, "Group: Team" <team@company.com>,
 "Escaped, Name" <escape@test.com>, just@email.com,
 "Comment (This is valid)" <comment@domain.net>,
 "Odd @Chars" <weird!#$%'*+-/=?^_\`{|}~@char-test.com>,
 "Non-ASCII 用户" <user@例子.中国>,
 admin@[192.168.1.1]`,
  ],
  ["CC", `Mock CC <${mockCcAddress}>`],
  ["Subject", "A very long mock\n subject"],
  ["Message-ID", `<${mockMessageId}>`],
  ["Date", mockDate],
];
const mockHeaderStr = mockHeaders.map((h) => h[0] + ": " + h[1]).join("\n");
const mockMailStr = mockHeaderStr + "\n\n" + mockBodyStr;
const mockCrlfMailStr = mockMailStr.replaceAll("\n", "\r\n");
const mockToAddresses = [
  "john@example.com",
  "alice+work@example.com",
  "team@company.com",
  "escape@test.com",
  "just@email.com",
  "comment@domain.net",
  "weird!#$%'*+-/=?^_`{|}~@char-test.com",
  "user@例子.中国",
  "admin@[192.168.1.1]",
];

describe("Mail", () => {
  it("simple parse", () => {
    const parsed = new Mail(mockMailStr).startSimpleParse().sections();
    expect(parsed.header).toEqual(mockHeaderStr);
    expect(parsed.body).toEqual(mockBodyStr);
    expect(parsed.sep).toBe("\n");
    expect(parsed.eol).toBe("\n");
  });

  it("simple parse crlf", () => {
    const parsed = new Mail(mockCrlfMailStr).startSimpleParse().sections();
    expect(parsed.sep).toBe("\r\n");
    expect(parsed.eol).toBe("\r\n");
  });

  it("simple parse date", () => {
    expect(
      new Mail(mockMailStr).startSimpleParse().sections().headers().date(),
    ).toEqual(new Date(mockDate));
  });

  it("simple parse headers", () => {
    expect(
      new Mail(mockMailStr).startSimpleParse().sections().headers().fields,
    ).toEqual(mockHeaders.map((h) => [h[0], " " + h[1].replaceAll("\n", "")]));
  });

  it("parse recipients", () => {
    const mail = new Mail(mockMailStr);
    expect([
      ...mail.startSimpleParse().sections().headers().recipients(),
    ]).toEqual([...mockToAddresses, mockCcAddress]);
    expect([
      ...mail.startSimpleParse().sections().headers().recipients({
        domain: "example.com",
      }),
    ]).toEqual(
      [...mockToAddresses, mockCcAddress].filter((a) =>
        a.endsWith("example.com"),
      ),
    );
  });

  it("find all addresses", () => {
    const mail = new Mail(mockMailStr);
    expect(mail.simpleFindAllAddresses()).toEqual([
      "mock@from.mock",
      "john@example.com",
      "alice+work@example.com",
      "team@company.com",
      "escape@test.com",
      "just@email.com",
      "comment@domain.net",
      "mock@cc.mock",
      "mock-message-id@from.mock",
      "mock-message-id-2@from.mock",
    ]);
  });
});

describe("MailDeliverer", () => {
  class MockMailDeliverer extends MailDeliverer {
    name = "mock";
    override doDeliver = fn((_: Mail, ctx: MailDeliverContext) => {
      ctx.result.recipients.set("*", { kind: "done", message: "success" });
      return Promise.resolve();
    }) as MailDeliverer["doDeliver"];
  }
  const mockDeliverer = new MockMailDeliverer(new Logger());

  it("deliver success", async () => {
    await mockDeliverer.deliverRaw(mockMailStr);
    expect(mockDeliverer.doDeliver).toHaveBeenCalledTimes(1);
  });
});