update
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace HyperfTest\Cases\Unit\Service;
|
||||
|
||||
use App\Model\OperationLog;
|
||||
use App\Service\OperationLogService;
|
||||
use HyperfTest\TestCase;
|
||||
use HyperfTest\Traits\AuthenticatedTestTrait;
|
||||
|
||||
/**
|
||||
* OperationLogService 单元测试
|
||||
*
|
||||
* 验证操作日志的异步写入和辅助方法
|
||||
*
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class OperationLogServiceTest extends TestCase
|
||||
{
|
||||
use AuthenticatedTestTrait;
|
||||
|
||||
/**
|
||||
* 在子协程中执行回调,等待其退出(含 defer 执行)
|
||||
*
|
||||
* co-phpunit 下所有测试共享同一顶层协程,
|
||||
* Coroutine::defer 的回调要到协程退出才执行。
|
||||
* 此方法创建子协程并 join 等待,确保 defer 回调完成后再继续。
|
||||
*/
|
||||
protected function runInChildCoroutineAndWait(callable $callback): void
|
||||
{
|
||||
$cid = \Swoole\Coroutine::create($callback);
|
||||
if ($cid !== false) {
|
||||
\Swoole\Coroutine::join([$cid]);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_log_creates_record_in_coroutine(): void
|
||||
{
|
||||
$action = 'test.unit_' . uniqid();
|
||||
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user