From 7eaa3b3b8e2f136bec74595e44aeb3033744adc2 Mon Sep 17 00:00:00 2001 From: Josh Tsai <128559392+bounce12340@users.noreply.github.com> Date: Sat, 25 Jul 2026 20:21:46 +0800 Subject: [PATCH] test: |Worker| add junk_mail_policy regression tests for issue #1084 (#1089) Cover the junk-mail policy behavior fixed in #1085: - none/neutral results for SPF/DKIM/DMARC are treated as the method being absent and do not trigger JUNK_MAIL_CHECK_LIST rejection - explicit fail results are still rejected - JUNK_MAIL_FORCE_PASS_LIST only accepts an explicit pass Run with: node --test-isolation=none --test worker/src/email/junk_mail_policy.test.mjs Co-authored-by: Claude Fable 5 --- CHANGELOG.md | 4 ++ CHANGELOG_EN.md | 4 ++ worker/src/email/junk_mail_policy.test.mjs | 81 ++++++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 worker/src/email/junk_mail_policy.test.mjs diff --git a/CHANGELOG.md b/CHANGELOG.md index 78303a70..59d4ec71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,10 @@ - fix: |AI 提取| HTML-only 邮件在发送给 Workers AI 前会先压缩为可读文本,避免样式模板过长导致验证码位于 4000 字截断之后而无法识别 - fix: |Frontend| 移动端 Header 增加页头内边距,避免标题、菜单按钮与屏幕边缘过近 +### Testing + +- test: |Worker| 新增 junk_mail_policy 回归测试(issue #1084):覆盖 SPF/DKIM/DMARC 的 `none`/`neutral` 按认证方法不存在处理、明确 `fail` 仍被拒收,以及 `JUNK_MAIL_FORCE_PASS_LIST` 仅接受明确 `pass` + ### Improvements - docs: |README| 新增完整日文 README,并在中文和英文 README 中添加日文导航链接 diff --git a/CHANGELOG_EN.md b/CHANGELOG_EN.md index 5d6f0f6c..3c325b75 100644 --- a/CHANGELOG_EN.md +++ b/CHANGELOG_EN.md @@ -21,6 +21,10 @@ - fix: |AI Extract| Convert HTML-only mail bodies into compact readable text before sending them to Workers AI, preventing long templates from pushing verification codes past the 4000-character truncation window - fix: |Frontend| Add mobile Header page padding so the title and menu button no longer sit too close to the screen edge +### Testing + +- test: |Worker| Add junk_mail_policy regression tests (issue #1084): `none`/`neutral` results for SPF/DKIM/DMARC are treated as the method being absent, explicit `fail` results are still rejected, and `JUNK_MAIL_FORCE_PASS_LIST` only accepts an explicit `pass` + ### Improvements - docs: |README| Add a complete Japanese README and Japanese navigation links to the Chinese and English READMEs diff --git a/worker/src/email/junk_mail_policy.test.mjs b/worker/src/email/junk_mail_policy.test.mjs new file mode 100644 index 00000000..5633df84 --- /dev/null +++ b/worker/src/email/junk_mail_policy.test.mjs @@ -0,0 +1,81 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { isJunkMailByHeaders } from "./junk_mail_policy.ts"; + +const authenticationResultsHeader = (results) => [{ + key: "Authentication-Results", + value: `mx.example; ${results}`, +}]; + +test("issue #1084: dmarc=none does not reject an otherwise passing message", () => { + const headers = authenticationResultsHeader( + "spf=pass smtp.mailfrom=sender.example; " + + "dkim=pass header.d=sender.example; " + + "dmarc=none; arc=pass" + ); + + assert.equal( + isJunkMailByHeaders( + headers, + ["spf", "dkim", "dmarc"], + ["spf"], + ), + false, + ); +}); + +for (const [method, result] of [ + ["spf", "none"], + ["spf", "neutral"], + ["dkim", "none"], + ["dkim", "neutral"], + ["dmarc", "none"], + ["dmarc", "neutral"], +]) { + test(`${method}=${result} is absent for JUNK_MAIL_CHECK_LIST`, () => { + assert.equal( + isJunkMailByHeaders( + authenticationResultsHeader(`${method}=${result}`), + [method], + [], + ), + false, + ); + }); +} + +for (const method of ["spf", "dkim", "dmarc"]) { + test(`${method}=fail is junk when the method is checked`, () => { + assert.equal( + isJunkMailByHeaders( + authenticationResultsHeader(`${method}=fail`), + [method], + [], + ), + true, + ); + }); +} + +test("JUNK_MAIL_FORCE_PASS_LIST requires an explicit pass result", () => { + for (const result of ["none", "neutral", "fail"]) { + assert.equal( + isJunkMailByHeaders( + authenticationResultsHeader(`spf=${result}`), + [], + ["spf"], + ), + true, + ); + } + + assert.equal( + isJunkMailByHeaders( + authenticationResultsHeader("spf=pass"), + [], + ["spf"], + ), + false, + ); +});