inbound-mail.test.js 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. import assert from 'node:assert/strict';
  2. import { test } from 'node:test';
  3. import { decodeMimeHeader, parseInboundMessage } from '../src/inbound-mail.js';
  4. test('parseInboundMessage extracts common headers and text bodies from MIME', async () => {
  5. const rawMessage = [
  6. 'From: Alice <alice@example.net>',
  7. 'To: Support <support@inbound.example>',
  8. 'Subject: =?UTF-8?B?5pS25L+h5rWL6K+V?=',
  9. 'Message-ID: <mime-test@example.net>',
  10. 'MIME-Version: 1.0',
  11. 'Content-Type: multipart/alternative; boundary="alt"',
  12. '',
  13. '--alt',
  14. 'Content-Type: text/plain; charset=UTF-8',
  15. 'Content-Transfer-Encoding: base64',
  16. '',
  17. Buffer.from('Hello plain body.', 'utf8').toString('base64'),
  18. '--alt',
  19. 'Content-Type: text/html; charset=UTF-8',
  20. 'Content-Transfer-Encoding: quoted-printable',
  21. '',
  22. '<p>Hello <strong>HTML</strong> body.</p>',
  23. '--alt--',
  24. ''
  25. ].join('\r\n');
  26. const parsed = await parseInboundMessage(rawMessage, ['support@inbound.example']);
  27. assert.equal(parsed.sender, 'alice@example.net');
  28. assert.deepEqual(parsed.recipients, ['support@inbound.example']);
  29. assert.equal(parsed.subject, '收信测试');
  30. assert.equal(parsed.messageId, '<mime-test@example.net>');
  31. assert.equal(parsed.textBody, 'Hello plain body.');
  32. assert.match(parsed.htmlBody, /<strong>HTML<\/strong>/);
  33. assert.equal(parsed.preview, 'Hello plain body.');
  34. });
  35. test('decodeMimeHeader joins folded UTF-8 encoded words without introducing spaces', () => {
  36. const encoded = [
  37. '=?UTF-8?Q?=E6=A0=B8=E4=BA=91?=',
  38. ' =?UTF-8?Q?=E8=AE=A1=E7=AE=97=E5=B9=B3=E5=8F=B0?=',
  39. ' =?UTF-8?Q?_MailHub_=E6=B5=8B=E8=AF=95?='
  40. ].join('\r\n');
  41. assert.equal(decodeMimeHeader(encoded), '核云计算平台 MailHub 测试');
  42. });
  43. test('decodeMimeHeader preserves plain text and malformed encoded words', () => {
  44. assert.equal(decodeMimeHeader('literal?= =?UTF-8?Q?B?='), 'literal?= B');
  45. assert.equal(decodeMimeHeader('=?UTF-8?B?!!!!?='), '=?UTF-8?B?!!!!?=');
  46. assert.equal(decodeMimeHeader('=?UTF-8?B?/w==?='), '=?UTF-8?B?/w==?=');
  47. });
  48. test('parseInboundMessage does not expose attachment-only multipart wire data as text', async () => {
  49. const rawMessage = [
  50. 'From: Alice <alice@example.net>',
  51. 'To: Support <support@inbound.example>',
  52. 'Subject: Attachment only',
  53. 'MIME-Version: 1.0',
  54. 'Content-Type: multipart/mixed; boundary="mixed"',
  55. '',
  56. '--mixed',
  57. 'Content-Type: application/pdf; name="report.pdf"',
  58. 'Content-Disposition: attachment; filename="report.pdf"',
  59. 'Content-Transfer-Encoding: base64',
  60. '',
  61. 'JVBERi0xLjQ=',
  62. '--mixed--',
  63. ''
  64. ].join('\r\n');
  65. const parsed = await parseInboundMessage(rawMessage, ['support@inbound.example']);
  66. assert.equal(parsed.textBody, '');
  67. assert.equal(parsed.htmlBody, '');
  68. assert.equal(parsed.preview, '');
  69. });