spamGate.test.ts 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  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. messageCreatedAt: new Date().toISOString(),
  15. senderEmail: 'klient@example.com',
  16. senderName: 'Klient',
  17. senderId: '1',
  18. labels: [],
  19. customAttributes: {},
  20. additionalAttributes: {},
  21. attachmentCount: 0,
  22. attachments: [],
  23. formMail: null,
  24. needsConversationFetch: false,
  25. ...overrides,
  26. };
  27. }
  28. test('a normal customer question passes', () => {
  29. assert.equal(evaluateSpam(evt()).spam, false);
  30. });
  31. test('mailer-daemon is blocked', () => {
  32. const v = evaluateSpam(evt({ senderEmail: 'mailer-daemon@example.com' }));
  33. assert.equal(v.spam, true);
  34. assert.match(v.reason, /automated_sender/);
  35. });
  36. test('no-reply senders are blocked', () => {
  37. assert.equal(evaluateSpam(evt({ senderEmail: 'no-reply@shop.example' })).spam, true);
  38. assert.equal(evaluateSpam(evt({ senderEmail: 'noreply@shop.example' })).spam, true);
  39. });
  40. test('bounce subjects are blocked', () => {
  41. const v = evaluateSpam(evt({ subject: 'Undelivered Mail Returned to Sender' }));
  42. assert.equal(v.spam, true);
  43. assert.equal(v.reason, 'bounce_subject');
  44. });
  45. test('out-of-office autoreplies are blocked', () => {
  46. assert.equal(evaluateSpam(evt({ subject: 'Automatic reply: your message' })).spam, true);
  47. assert.equal(evaluateSpam(evt({ subject: 'Automatyczna odpowiedź' })).spam, true);
  48. });
  49. test('DMARC aggregate reports are blocked', () => {
  50. assert.equal(evaluateSpam(evt({ subject: 'Report domain: easyklima.com' })).spam, true);
  51. });
  52. test('empty bodies and attachment-only mails are blocked', () => {
  53. assert.equal(evaluateSpam(evt({ content: '' })).reason, 'empty_body');
  54. assert.equal(evaluateSpam(evt({ content: '', attachmentCount: 2 })).reason, 'attachment_only');
  55. });
  56. test('auto-submitted headers are blocked', () => {
  57. const v = evaluateSpam(
  58. evt({ additionalAttributes: { headers: { 'Auto-Submitted': 'auto-replied' } } }),
  59. );
  60. assert.equal(v.spam, true);
  61. assert.equal(v.reason, 'header:auto-submitted');
  62. });
  63. test('auto-submitted: no is NOT treated as automated', () => {
  64. const v = evaluateSpam(evt({ additionalAttributes: { headers: { 'Auto-Submitted': 'no' } } }));
  65. assert.equal(v.spam, false);
  66. });
  67. test('list-unsubscribe marks a newsletter', () => {
  68. const v = evaluateSpam(
  69. evt({ additionalAttributes: { headers: { 'List-Unsubscribe': '<mailto:x@y.z>' } } }),
  70. );
  71. assert.equal(v.spam, true);
  72. });
  73. test('a body with no words is blocked', () => {
  74. assert.equal(evaluateSpam(evt({ content: '??? !!! ...' })).reason, 'no_textual_content');
  75. });
  76. test('a legitimate mail from an address merely containing "notification" passes', () => {
  77. // Only exact local-part matches are automated; substrings must not trigger.
  78. assert.equal(evaluateSpam(evt({ senderEmail: 'notifications.jan@example.com' })).spam, false);
  79. });