spamGate.test.ts 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import { test } from 'node:test';
  2. import assert from 'node:assert/strict';
  3. import { evaluateSpam } from '../src/domain/spamGate.js';
  4. import type { SupportMessageEvent } from '../src/domain/messageNormalizer.js';
  5. function evt(overrides: Partial<SupportMessageEvent> = {}): SupportMessageEvent {
  6. return {
  7. source: 'chatwoot',
  8. channel: 'email',
  9. conversationId: 1311,
  10. messageId: '1',
  11. inboxId: 1,
  12. content: 'Czy ten produkt pasuje do mojego auta?',
  13. subject: 'Pytanie',
  14. senderEmail: 'klient@example.com',
  15. senderName: 'Klient',
  16. senderId: '1',
  17. labels: [],
  18. customAttributes: {},
  19. additionalAttributes: {},
  20. attachmentCount: 0,
  21. needsConversationFetch: false,
  22. ...overrides,
  23. };
  24. }
  25. test('a normal customer question passes', () => {
  26. assert.equal(evaluateSpam(evt()).spam, false);
  27. });
  28. test('mailer-daemon is blocked', () => {
  29. const v = evaluateSpam(evt({ senderEmail: 'mailer-daemon@example.com' }));
  30. assert.equal(v.spam, true);
  31. assert.match(v.reason, /automated_sender/);
  32. });
  33. test('no-reply senders are blocked', () => {
  34. assert.equal(evaluateSpam(evt({ senderEmail: 'no-reply@shop.example' })).spam, true);
  35. assert.equal(evaluateSpam(evt({ senderEmail: 'noreply@shop.example' })).spam, true);
  36. });
  37. test('bounce subjects are blocked', () => {
  38. const v = evaluateSpam(evt({ subject: 'Undelivered Mail Returned to Sender' }));
  39. assert.equal(v.spam, true);
  40. assert.equal(v.reason, 'bounce_subject');
  41. });
  42. test('out-of-office autoreplies are blocked', () => {
  43. assert.equal(evaluateSpam(evt({ subject: 'Automatic reply: your message' })).spam, true);
  44. assert.equal(evaluateSpam(evt({ subject: 'Automatyczna odpowiedź' })).spam, true);
  45. });
  46. test('DMARC aggregate reports are blocked', () => {
  47. assert.equal(evaluateSpam(evt({ subject: 'Report domain: easyklima.com' })).spam, true);
  48. });
  49. test('empty bodies and attachment-only mails are blocked', () => {
  50. assert.equal(evaluateSpam(evt({ content: '' })).reason, 'empty_body');
  51. assert.equal(evaluateSpam(evt({ content: '', attachmentCount: 2 })).reason, 'attachment_only');
  52. });
  53. test('auto-submitted headers are blocked', () => {
  54. const v = evaluateSpam(
  55. evt({ additionalAttributes: { headers: { 'Auto-Submitted': 'auto-replied' } } }),
  56. );
  57. assert.equal(v.spam, true);
  58. assert.equal(v.reason, 'header:auto-submitted');
  59. });
  60. test('auto-submitted: no is NOT treated as automated', () => {
  61. const v = evaluateSpam(evt({ additionalAttributes: { headers: { 'Auto-Submitted': 'no' } } }));
  62. assert.equal(v.spam, false);
  63. });
  64. test('list-unsubscribe marks a newsletter', () => {
  65. const v = evaluateSpam(
  66. evt({ additionalAttributes: { headers: { 'List-Unsubscribe': '<mailto:x@y.z>' } } }),
  67. );
  68. assert.equal(v.spam, true);
  69. });
  70. test('a body with no words is blocked', () => {
  71. assert.equal(evaluateSpam(evt({ content: '??? !!! ...' })).reason, 'no_textual_content');
  72. });
  73. test('a legitimate mail from an address merely containing "notification" passes', () => {
  74. // Only exact local-part matches are automated; substrings must not trigger.
  75. assert.equal(evaluateSpam(evt({ senderEmail: 'notifications.jan@example.com' })).spam, false);
  76. });