feat(notice): PHPMailer 邮箱驱动与短信公众号骨架

This commit is contained in:
augushong
2026-10-12 12:09:00 +00:00
parent e95e5942ea
commit 02f77f4eca
6 changed files with 382 additions and 5 deletions

View File

@@ -0,0 +1,123 @@
<?php
declare(strict_types=1);
namespace tests\Feature;
use app\common\test\TestCase;
use base\common\notice\channel\EmailChannel;
use base\common\notice\channel\SmsChannel;
use base\common\notice\channel\WechatChannel;
use app\common\service\NoticeService;
use think\facade\Cache;
use think\facade\Db;
/**
* 渠道驱动集成测试(任务 4 增补).
*
* 覆盖:
* 1. EmailChannel 在 notify_email_enable=0 时抛异常(渠道总闸关闭=投递失败信号)
* 2. SmsChannel send() 返回 true(骨架恒成功)
* 3. WechatChannel send() 返回 true(骨架恒成功)
* 4. 驱动 name() 标识与 channels() 映射一致
*
* fixture 均为事务内自造数据,tearDown 自动回滚;
* sysconfig 缓存不受事务回滚影响,配置行写入后显式清 tag(见 setNotifyConfig/tearDown)。
*/
class NoticeChannelTest extends TestCase
{
/** 本测试类是否写过 notify 配置行(决定 tearDown 是否清 sysconfig 缓存) */
private bool $touchedSysconfig = false;
// =========================================================================
// 1. EmailChannel 总闸关闭抛异常
// =========================================================================
public function test_email_channel_throws_when_disabled(): void
{
// 插入关闭配置行(value='0')并清 sysconfig 缓存(缓存不受事务回滚影响)
$this->setNotifyConfig('notify_email_enable', '0');
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('渠道未启用');
(new EmailChannel())->send(
['channel' => 'email', 'target_addr' => 'disabled@test.com', 'status' => 0],
['id' => 1, 'scene' => 'common', 'title' => '标题', 'content' => '内容']
);
}
// =========================================================================
// 2. SmsChannel 骨架恒成功
// =========================================================================
public function test_sms_channel_send_returns_true(): void
{
$this->assertTrue((new SmsChannel())->send(
['channel' => 'sms', 'target_addr' => '13800138000', 'status' => 0],
['id' => 1, 'scene' => 'common', 'title' => '标题', 'content' => '内容']
));
}
// =========================================================================
// 3. WechatChannel 骨架恒成功
// =========================================================================
public function test_wechat_channel_send_returns_true(): void
{
$this->assertTrue((new WechatChannel())->send(
['channel' => 'wechat', 'target_addr' => 'openid_demo', 'status' => 0],
['id' => 1, 'scene' => 'common', 'title' => '标题', 'content' => '内容']
));
}
// =========================================================================
// 4. name() 契约与 channels() 映射一致
// =========================================================================
public function test_channel_names_match_service_mapping(): void
{
$this->assertSame('email', EmailChannel::name());
$this->assertSame('sms', SmsChannel::name());
$this->assertSame('wechat', WechatChannel::name());
$channels = NoticeService::channels();
$this->assertSame(EmailChannel::class, $channels['email']);
$this->assertSame(SmsChannel::class, $channels['sms']);
$this->assertSame(WechatChannel::class, $channels['wechat']);
}
// =========================================================================
// fixture 工厂(事务内运行,tearDown 自动回滚;缓存显式清理)
// =========================================================================
/**
* 写 notify 组配置行并清 sysconfig 缓存.
*
* sysconfig 缓存不受事务回滚影响:必须显式清 tag 才能让后续读取
* 看到事务内的新值(value='0' 因 empty('0') 恒不写缓存,但清理是
* 防御后续用例被前序非 '0' 缓存值污染的统一口径)。
*/
private function setNotifyConfig(string $name, string $value): void
{
Db::name('system_config')->insert([
'name' => $name,
'group' => 'notify',
'value' => $value,
'remark' => 'NoticeChannelTest fixture',
'sort' => 100,
'create_time' => time(),
'update_time' => time(),
]);
$this->touchedSysconfig = true;
Cache::tag('sysconfig')->clear();
}
protected function tearDown(): void
{
if ($this->touchedSysconfig) {
Cache::tag('sysconfig')->clear();
}
parent::tearDown();
}
}