背景
原先有一个配置项同时承担了两个角色:
- 作为发件人(所有 mailer 的
default from) - 作为收件人(部分管理通知的
to,以及页面上展示的联系邮箱)
迁移到新 SMTPS 服务后,发件人需要用专门的”通知邮箱”(notification 邮箱只用来发、不用来收),而收件人仍应保持原有的真实邮件列表。一个配置项承担两个相反角色会冲突,需要拆分。
方案
将原配置项拆成两个语义清晰的配置:
- 原配置项(收件人):保持为真实邮件列表(管理/团队列表)
- 新增发件人配置项:值为各环境的通知邮箱,只用于
default from
配置层(各环境 YAML)
| 环境 | 收件人(原配置) | 发件人(新配置) |
|---|---|---|
| 生产 | 真实团队列表 | 生产通知邮箱 |
| 预发布 | 各自通知邮箱 | 各自通知邮箱 |
| 开发/测试 | 测试占位地址 | 测试占位地址 |
- 预发布环境选择”收发都用通知邮箱”(测试环境通知不打扰真人列表);生产环境”发件用通知邮箱、收件用真实团队列表”。
- 开发/测试环境的”占位地址”指不真实发信时用的假地址(测试惯例),不影响真实投递。
代码层
- 7 个 mailer 的
default from改用新的发件人配置项 - 所有
to:收件人保持用原配置项不变
测试层
- 更新断言
from的测试,改用新发件人配置 - 更新 config 继承测试的断言(预发布环境覆盖值变化)
排查过程中的关键点
1. YAML key 字母序约束
项目有一个测试强制要求各环境 YAML 文件的 key 必须按字母序排列。新增配置 key 时打乱了顺序,导致该测试失败。修正:把新 key 插入到正确的字母序位置。
2. 配置继承测试
预发布环境的 YAML 从公共基文件继承配置。测试断言某些值”继承自公共文件”。在环境专属文件新增覆盖后,这些断言失效,需同步更新为新值;未改动的环境保持原断言。
3. mailer 类加载时求值(重要)
default from: CFG['...'] 是类加载时求值的,不是发信时。因此只要测试触及某个 mailer 类,这行就会读取配置项——在严格 mock 的 minitest 下,未 stub 该配置会报 unexpected invocation。需要给相关测试补上新配置项的 stub。
验证
- CI 测试全部通过
- 预发布环境 console 验证:发件人正确来自新配置、收件人逻辑用原配置、发信成功
- 生产部署后验证:发件人为生产通知邮箱、通知发到真实团队列表、Send-As 通过
通俗版复述
这件事在解决什么?
原来有一个”邮箱配置”被当成两用:既是”寄件人地址”,又是”收件人地址”。现在新邮局要求寄件人必须是一个专门的”通知邮箱”(这个邮箱只负责往外发,不收信),但收件人还得是原来那些真人的邮件列表。一个配置顶两个相反的用途,肯定要打架,所以把它拆成两个:一个专管”从谁发”,一个专管”发给谁”。
改动点?
代码里所有发邮件的地方,”寄件人”改用新的通知邮箱配置;”收件人”还用老配置不变。
踩的坑?
- 项目规定配置文件里的条目必须按字母顺序排,加新条目插错位置了,测试报错,挪到正确位置就好。
- 有测试专门检查”某配置是从公共文件继承来的”,我们在分环境文件里加了覆盖,这个检查就得同步更新。
- 有个隐蔽点:代码里”默认寄件人”是在程序加载那一刻就去读配置的,不是发信时才读。所以只要测试一碰到发邮件的类,就会去读新配置——测试里得提前把这个配置”喂”给它,否则报错。
结果:拆分完成,生产环境做到了”用通知邮箱发信、通知仍送达真人列表”。