runInChildCoroutineAndWait(static function () use ($action): void { OperationLogService::log( user_id: 1, action: $action, target_type: 'user', target_id: 99, description: '单元测试操作日志', detail: ['key' => 'value'], ip: '127.0.0.1', ); }); // join 等待子协程退出后 defer 已执行,记录已写入 $record = OperationLog::query()->where('action', $action)->first(); $this->assertNotNull($record); $this->assertSame(1, $record->user_id); $this->assertSame($action, $record->action); $this->assertSame('user', $record->target_type); $this->assertSame(99, $record->target_id); $this->assertSame('单元测试操作日志', $record->description); $this->assertSame(['key' => 'value'], $record->detail); $this->assertSame('127.0.0.1', $record->ip); // 清理测试数据 OperationLog::query()->where('id', $record->id)->delete(); } public function test_log_with_null_detail_and_ip(): void { $action = 'test.null_detail_' . uniqid(); $this->runInChildCoroutineAndWait(static function () use ($action): void { OperationLogService::log( user_id: 2, action: $action, target_type: 'auth', target_id: null, description: '测试空 detail 和 ip', ); }); $record = OperationLog::query()->where('action', $action)->first(); $this->assertNotNull($record); $this->assertSame(2, $record->user_id); $this->assertNull($record->target_id); $this->assertNull($record->detail); $this->assertNull($record->ip); // 清理 OperationLog::query()->where('id', $record->id)->delete(); } public function test_log_does_not_throw_on_failure(): void { // 验证 log 方法在写入失败时不抛异常(仅记录错误日志) // 使用超长 action 触发数据库约束失败 $long_action = str_repeat('a', 100); // 超过 VARCHAR(50) 限制 $this->runInChildCoroutineAndWait(static function () use ($long_action): void { OperationLogService::log( user_id: 1, action: $long_action, target_type: 'test', target_id: null, description: '不应抛异常', ); }); // 如果到达这里说明没有抛异常 $this->assertTrue(true); } }