优化 AWS 邮件验证码过滤与有效字段输出

This commit is contained in:
2026-10-08 15:29:35 +08:00
parent d1398b9475
commit 2174b61606
7 changed files with 1169 additions and 0 deletions
+209
View File
@@ -0,0 +1,209 @@
"""Offline tests for explicit recovery of the last generated report."""
from contextlib import redirect_stdout
from email.message import EmailMessage
from io import StringIO
import json
from pathlib import Path
import sys
from tempfile import TemporaryDirectory
import unittest
from unittest.mock import patch
from zoneinfo import ZoneInfo
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
import check_mail
class FakeIMAP:
def __init__(self, message, uid=b"42"):
self.message = message
self.uid_value = uid
self.fetches = 0
def __enter__(self):
return self
def __exit__(self, *_args):
pass
def login(self, *_args):
return "OK", [b"logged in"]
def select(self, *_args, **_kwargs):
return "OK", [b"1"]
def response(self, name):
assert name == "UIDVALIDITY"
return "OK", [b"123"]
def uid(self, command, *_args):
if command == "SEARCH":
return "OK", [self.uid_value]
if command == "FETCH":
self.fetches += 1
return "OK", [
(b'1 (UID 42 INTERNALDATE "28-Sep-2026 10:00:00 +0800" BODY[] {1}', self.message),
b")",
]
raise AssertionError(command)
class ReplayTests(unittest.TestCase):
def setUp(self):
self.temp = TemporaryDirectory()
self.addCleanup(self.temp.cleanup)
self.root = Path(self.temp.name)
self.config = self.root / "config.json"
self.state = self.root / "state" / "processed.json"
self.config.write_text(json.dumps({
"imap": {
"host": "imap.example.com",
"username": "reader@example.com",
"authorization_code": "test-only",
},
"timezone": "Asia/Shanghai",
"state_file": "state/processed.json",
}), encoding="utf-8")
mail = EmailMessage()
mail["Subject"] = "AWS Account on Hold: Response Required"
mail["Message-ID"] = "<report-42@example.com>"
mail.set_content("AWS Account ID: 123456789012\nYour account has been placed on hold.")
self.fake = FakeIMAP(mail.as_bytes())
# Keep the fake INTERNALDATE within the test's day without changing production filtering.
window = check_mail.day_window(ZoneInfo("Asia/Shanghai"), "2026-09-28")
fixed_day = patch.object(check_mail, "day_window", return_value=window)
fixed_day.start()
self.addCleanup(fixed_day.stop)
def run_main(self, *args):
output = StringIO()
with redirect_stdout(output):
check_mail.main(["--config", str(self.config), *args])
return output.getvalue()
def test_dedup_and_explicit_replay_without_connection(self):
with patch.object(check_mail.imaplib, "IMAP4_SSL", return_value=self.fake):
first = self.run_main()
second = self.run_main()
self.assertIn("【AWS账号风控邮件】", first)
self.assertIn("AWS账号ID:123456789012", first)
self.assertEqual(second, check_mail.NO_NEW + "\n")
self.assertEqual(self.fake.fetches, 1)
before = self.state.read_bytes()
with patch.object(check_mail.imaplib, "IMAP4_SSL", side_effect=AssertionError("unexpected connection")):
replay = self.run_main("--replay-last")
self.assertEqual(replay, first)
self.assertEqual(self.state.read_bytes(), before)
self.assertEqual(json.loads(before)["last_report"], first.removesuffix("\n"))
def test_empty_scan_does_not_replace_previous_report(self):
with patch.object(check_mail.imaplib, "IMAP4_SSL", return_value=self.fake):
first = self.run_main()
empty = FakeIMAP(self.fake.message, uid=b"")
with patch.object(check_mail.imaplib, "IMAP4_SSL", return_value=empty):
self.assertEqual(self.run_main(), check_mail.NO_NEW + "\n")
self.assertEqual(self.run_main("--replay-last"), first)
def test_older_batch_survives_next_report_and_empty_scan(self):
with patch.object(check_mail.imaplib, "IMAP4_SSL", return_value=self.fake):
first = self.run_main()
newer_mail = EmailMessage()
newer_mail["Subject"] = "AWS Account on Hold: Second Notice"
newer_mail["Message-ID"] = "<report-43@example.com>"
newer_mail.set_content("AWS Account ID: 999999999999\nYour account has been placed on hold.")
newer = FakeIMAP(newer_mail.as_bytes(), uid=b"43")
with patch.object(check_mail.imaplib, "IMAP4_SSL", return_value=newer):
second = self.run_main()
self.assertEqual(self.run_main(), check_mail.NO_NEW + "\n")
state_before = self.state.read_bytes()
with patch.object(check_mail.imaplib, "IMAP4_SSL", side_effect=AssertionError("unexpected connection")):
ids = self.run_main("--list-report-batches").splitlines()
self.assertEqual(len(ids), 2)
self.assertEqual(self.run_main("--replay-id", ids[1]), first)
self.assertEqual(self.run_main("--replay-id", ids[0]), second)
self.assertEqual(self.run_main("--replay-last"), second)
self.assertEqual(self.state.read_bytes(), state_before)
def test_history_is_bounded(self):
state = {"mailboxes": {}}
for number in range(check_mail.REPORT_HISTORY_LIMIT + 4):
check_mail.save_report_batch(state, f"report {number}")
history = check_mail.report_history(state)
self.assertEqual(len(history), check_mail.REPORT_HISTORY_LIMIT)
self.assertEqual(history[0]["report"], "report 4")
self.assertEqual(history[-1]["report"], f"report {check_mail.REPORT_HISTORY_LIMIT + 3}")
self.assertEqual(len({item["id"] for item in history}), len(history))
def test_legacy_last_report_remains_replayable(self):
self.state.parent.mkdir(parents=True, exist_ok=True)
self.state.write_text(json.dumps({"mailboxes": {}, "last_report": "旧报告"}), encoding="utf-8")
with patch.object(check_mail.imaplib, "IMAP4_SSL", side_effect=AssertionError("unexpected connection")):
self.assertEqual(self.run_main("--list-report-batches"), "legacy\n")
self.assertEqual(self.run_main("--replay-id", "legacy"), "旧报告\n")
self.assertEqual(self.run_main("--replay-last"), "旧报告\n")
def test_missing_batch_and_invalid_history_are_clear_errors(self):
with patch.object(check_mail.imaplib, "IMAP4_SSL", side_effect=AssertionError("unexpected connection")):
with self.assertRaisesRegex(RuntimeError, "报告批次 ID 不存在"):
self.run_main("--replay-id", "missing")
self.state.parent.mkdir(parents=True, exist_ok=True)
self.state.write_text('{"mailboxes": {}, "report_history": [42]}', encoding="utf-8")
with self.assertRaisesRegex(ValueError, "报告历史格式无效"):
self.run_main("--list-report-batches")
def test_no_saved_report_has_clear_error(self):
with patch.object(check_mail.imaplib, "IMAP4_SSL", side_effect=AssertionError("unexpected connection")):
with self.assertRaisesRegex(RuntimeError, "没有可重放的上次报告"):
self.run_main("--replay-last")
self.state.parent.mkdir(parents=True, exist_ok=True)
self.state.write_text('{"mailboxes": {}}', encoding="utf-8")
with self.assertRaisesRegex(RuntimeError, "没有可重放的上次报告"):
self.run_main("--replay-last")
def test_local_eml_mode_does_not_write_state(self):
folder = self.root / "samples"
folder.mkdir()
mail = EmailMessage()
mail["Subject"] = "AWS Account on Hold: Response Required"
mail["Date"] = "Mon, 28 Sep 2026 10:00:00 +0800"
mail.set_content("AWS Account ID: 123456789012\nYour account has been placed on hold.")
(folder / "sample.eml").write_bytes(mail.as_bytes())
self.assertIn("【AWS账号风控邮件】", self.run_main("--eml-dir", str(folder), "--date", "2026-09-28"))
self.assertFalse(self.state.exists())
def test_filtered_code_is_deduplicated_without_report_history(self):
mail = EmailMessage()
mail["Subject"] = "AWS 验证您的身份"
mail["Message-ID"] = "<code-42@example.com>"
mail.set_content("AWS 验证码 123456,用于登录。如果发现异常活动,请启用 MFA。")
fake = FakeIMAP(mail.as_bytes())
with patch.object(check_mail.imaplib, "IMAP4_SSL", return_value=fake):
self.assertEqual(self.run_main(), check_mail.NO_NEW + "\n")
self.assertEqual(self.run_main(), check_mail.NO_NEW + "\n")
self.assertEqual(fake.fetches, 1)
state = json.loads(self.state.read_text(encoding="utf-8"))
entry = next(iter(state["mailboxes"].values()))
self.assertEqual(entry["uids"], ["42"])
self.assertEqual(entry["message_ids"], ["<code-42@example.com>"])
self.assertNotIn("report_history", state)
self.assertNotIn("last_report", state)
def test_filtered_code_preserves_previous_report_batch(self):
with patch.object(check_mail.imaplib, "IMAP4_SSL", return_value=self.fake):
first = self.run_main()
before = json.loads(self.state.read_text(encoding="utf-8"))
mail = EmailMessage()
mail["Subject"] = "AWS verification code"
mail["Message-ID"] = "<code-43@example.com>"
mail.set_content("Verification code: 123456. Use this code to sign in.")
with patch.object(check_mail.imaplib, "IMAP4_SSL", return_value=FakeIMAP(mail.as_bytes(), uid=b"43")):
self.assertEqual(self.run_main(), check_mail.NO_NEW + "\n")
after = json.loads(self.state.read_text(encoding="utf-8"))
self.assertEqual(after["report_history"], before["report_history"])
self.assertEqual(self.run_main("--replay-last"), first)
if __name__ == "__main__":
unittest.main()