createAdmin(); // 临时注入收集器监听器(容器内与框架注册的 NoticeSended LogEvent 并存,互不影响) $captured = []; Event::listen('NoticeSended', function ($payload) use (&$captured) { $captured[] = $payload; }); $noticeId = NoticeService::send([$adminId], '事件测试', '内容'); $this->assertCount(1, $captured, 'send 应触发一次 NoticeSended 事件'); [$notice, $targets] = $captured[0]; $this->assertSame($noticeId, (int) $notice->id, 'payload[0] 应为本次创建的 notice 模型'); $this->assertIsArray($targets, 'payload[1] 应为投递行数组'); $this->assertCount(1, $targets); $this->assertSame($adminId, (int) $targets[0]['admin_id']); $this->assertSame('inmail', $targets[0]['channel']); } // ========================================================================= // 2. 登录提醒开关开启:NoticeEvent 落 admin_login 通知 // ========================================================================= public function test_login_notice_sent_when_scene_switch_on(): void { $adminId = $this->createAdmin(['username' => 'lgn_' . uniqid()]); $admin = SystemAdmin::find($adminId); $this->assertNotNull($admin, 'SystemAdmin 模型应能取到监听器入参形态'); $this->setLoginSceneSwitch('1'); (new NoticeEvent())->handle($admin); $row = Db::name('system_notice') ->where('scene', 'admin_login') ->where('send_admin_id', 0) ->find(); $this->assertNotNull($row, '开关开启时应产生 admin_login 通知主体'); $this->assertSame('登录提醒', $row['title']); // 文案含登录时间(Y-m-d H:i),跨分钟竞态下按正则断言而非逐字相等 $pattern = '/^账号 ' . preg_quote((string) $admin->username, '/') . ' 于 \d{4}-\d{2}-\d{2} \d{2}:\d{2} 登录成功$/u'; $this->assertMatchesRegularExpression($pattern, (string) $row['content']); // inmail 行直接成功终态(无 email 地址的管理员 email 渠道被跳过) $this->assertDatabaseHas('system_notice_target', [ 'notice_id' => $row['id'], 'admin_id' => $adminId, 'channel' => 'inmail', 'status' => 2, ]); $this->assertSame( 1, Db::name('system_notice_target')->where('notice_id', $row['id'])->count(), '无 email 管理员应仅展开 inmail 一行' ); } // ========================================================================= // 3. 登录提醒开关默认关闭 // ========================================================================= public function test_login_notice_skipped_when_scene_switch_absent(): void { $adminId = $this->createAdmin(['username' => 'lgn_' . uniqid()]); $admin = SystemAdmin::find($adminId); // 无配置行 = 默认关;清理可能被前序用例缓存的非 '0' 值 $this->clearLoginSceneSwitch(); (new NoticeEvent())->handle($admin); $this->assertSame( 0, Db::name('system_notice')->where('scene', 'admin_login')->where('send_admin_id', 0)->count(), '开关缺省(无配置行)时不应产生 admin_login 通知' ); } // ========================================================================= // fixture 工厂(事务内运行,tearDown 自动回滚) // ========================================================================= /** * 造启用管理员. */ private function createAdmin(array $overrides = []): int { $data = array_merge([ 'username' => 'evt_' . uniqid(), 'password' => 'not-a-real-password', 'nickname' => '事件测试用户', 'email' => '', 'status' => 1, 'create_time' => time(), 'update_time' => time(), 'delete_time' => 0, ], $overrides); return (int) Db::name('system_admin')->insertGetId($data); } /** * 写 notify 组登录提醒开关配置行并清缓存(缓存不受事务回滚影响). */ private function setLoginSceneSwitch(string $value): void { Db::name('system_config')->insert([ 'name' => 'notify_login_scene_enable', 'group' => 'notify', 'value' => $value, 'remark' => 'NoticeEventTest fixture', 'sort' => 100, 'create_time' => time(), 'update_time' => time(), ]); Cache::tag('sysconfig')->clear(); } /** * 删除 notify 组登录提醒开关配置行并清缓存. */ private function clearLoginSceneSwitch(): void { Db::name('system_config') ->where('group', 'notify') ->where('name', 'notify_login_scene_enable') ->delete(); Cache::tag('sysconfig')->clear(); } }