ThinkAdmin/plugin/think-plugs-system/tests/ApiQueueControllerTest.php
Anyon e7a8c05556 chore(repo): 统一 v8 仓库品牌名称
将 v8 重构分支中残留的 ThinkAdminDeveloper 文本统一调整为 ThinkAdmin,避免迁移到主仓库后继续暴露旧开发仓库名称。

主要内容:

- 更新 README 标题与项目描述。

- 统一 PHP 文件头注释中的项目标识。

- 同步调整测试、配置、插件与文档中的旧仓库名称文本。

- 保持旧包删除说明与架构边界测试语义不变,只清理品牌名称残留。
2026-05-08 16:15:24 +08:00

271 lines
10 KiB
PHP

<?php
declare(strict_types=1);
/**
* +----------------------------------------------------------------------
* | ThinkAdmin Plugin
* +----------------------------------------------------------------------
* | Copyright (c) 2014~2026 ThinkAdmin [ thinkadmin.top ]
* +----------------------------------------------------------------------
* | Official Website: https://thinkadmin.top
* +----------------------------------------------------------------------
* | Licensed: https://mit-license.org
* | Disclaimer: https://thinkadmin.top/disclaimer
* | Vip Rights: https://thinkadmin.top/vip-introduce
* +----------------------------------------------------------------------
* | Gitee Repository: https://gitee.com/zoujingli/ThinkAdmin
* | Github Repository: https://github.com/zoujingli/ThinkAdmin
* +----------------------------------------------------------------------
*/
namespace think\admin\tests;
use plugin\system\controller\api\Queue as QueueController;
use plugin\system\model\SystemOplog;
use plugin\system\service\SystemContext as PluginSystemContext;
use think\admin\contract\SystemContextInterface;
use think\admin\runtime\RequestContext;
use think\admin\tests\Support\SqliteIntegrationTestCase;
use think\Container;
use think\exception\HttpResponseException;
use think\Request;
final class FakeConsoleCallResult
{
public function __construct(private readonly string $message) {}
public function fetch(): string
{
return $this->message;
}
}
final class FakeConsole
{
public array $calls = [];
/**
* @param array<string,string> $responses
*/
public function __construct(private readonly array $responses = [], private readonly ?\Throwable $exception = null) {}
public function call(string $command, array $parameters = [], ?string $scene = null): FakeConsoleCallResult
{
$this->calls[] = compact('command', 'parameters', 'scene');
if ($this->exception instanceof \Throwable) {
throw $this->exception;
}
$key = trim($command . ' ' . join(' ', $parameters));
$message = $this->responses[$key] ?? $this->responses[$command] ?? '';
return new FakeConsoleCallResult($message);
}
}
/**
* @internal
* @coversNothing
*/
class ApiQueueControllerTest extends SqliteIntegrationTestCase
{
public function testStartInvokesWorkerCommandAndWritesOplogForSuperAdmin(): void
{
$console = $this->bindConsole([
'xadmin:worker start queue --daemon' => 'Queue started successfully for pid 1234',
]);
$result = $this->callActionController('start');
$oplog = SystemOplog::mk()->order('id desc')->findOrEmpty();
$this->assertSame(200, intval($result['code'] ?? 0));
$this->assertSame('任务监听服务启动成功!', $result['info'] ?? '');
$this->assertCount(1, $console->calls);
$this->assertSame('xadmin:worker', $console->calls[0]['command']);
$this->assertSame(['start', 'queue', '--daemon'], $console->calls[0]['parameters']);
$this->assertTrue($oplog->isExists());
$this->assertSame('系统运维管理', $oplog->getData('action'));
$this->assertSame('尝试启动任务监听服务', $oplog->getData('content'));
$this->assertSame('admin', $oplog->getData('username'));
}
public function testStartRejectsNonSuperAdmin(): void
{
$console = $this->bindConsole([
'xadmin:worker start queue --daemon' => 'Queue started successfully for pid 1234',
]);
$result = $this->callActionController('start', [], false);
$this->assertSame(500, intval($result['code'] ?? 1));
$this->assertSame('请使用超管账号操作!', $result['info'] ?? '');
$this->assertCount(0, $console->calls);
$this->assertSame(0, SystemOplog::mk()->count());
}
public function testStopInvokesWorkerCommandAndWritesOplogForSuperAdmin(): void
{
$console = $this->bindConsole([
'xadmin:worker stop queue' => 'stop signal sent to 1234',
]);
$result = $this->callActionController('stop');
$oplog = SystemOplog::mk()->order('id desc')->findOrEmpty();
$this->assertSame(200, intval($result['code'] ?? 0));
$this->assertSame('停止任务监听服务成功!', $result['info'] ?? '');
$this->assertCount(1, $console->calls);
$this->assertSame('xadmin:worker', $console->calls[0]['command']);
$this->assertSame(['stop', 'queue'], $console->calls[0]['parameters']);
$this->assertTrue($oplog->isExists());
$this->assertSame('系统运维管理', $oplog->getData('action'));
$this->assertSame('尝试停止任务监听服务', $oplog->getData('content'));
$this->assertSame('admin', $oplog->getData('username'));
}
public function testStatusRendersRunningBadgeForSuperAdmin(): void
{
$console = $this->bindConsole([
'xadmin:worker status queue' => 'process queue 1234 running',
]);
$output = $this->callStatusController(true);
$this->assertStringContainsString('color-green', $output);
$this->assertStringContainsString('已启动', $output);
$this->assertStringContainsString('process queue 1234 running', $output);
$this->assertCount(1, $console->calls);
$this->assertSame(['status', 'queue'], $console->calls[0]['parameters']);
}
public function testStatusRendersNoPermissionBadgeForNonSuperAdmin(): void
{
$console = $this->bindConsole([
'xadmin:worker status queue' => 'process queue 1234 running',
]);
$output = $this->callStatusController(false);
$this->assertStringContainsString('color-red', $output);
$this->assertStringContainsString('无权限', $output);
$this->assertStringContainsString('只有超级管理员才能操作!', $output);
$this->assertCount(0, $console->calls);
}
public function testProgressReturnsDecodedQueueMessage(): void
{
$queue = $this->createSystemQueueFixture([
'code' => 'QUEUE_PROGRESS_001',
'message' => json_encode([
'status' => 'working',
'progress' => 66,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
]);
$result = $this->callActionController('progress', ['code' => $queue->getData('code')]);
$this->assertSame(200, intval($result['code'] ?? 0));
$this->assertSame('获取任务进度成功!', $result['info'] ?? '');
$this->assertSame('working', $result['data']['status'] ?? null);
$this->assertSame(66, $result['data']['progress'] ?? null);
}
public function testProgressReturnsFallbackSnapshotWhenMessageMissing(): void
{
$queue = $this->createSystemQueueFixture([
'code' => 'QUEUE_PROGRESS_002',
'status' => 2,
'exec_desc' => '正在执行',
'message' => null,
]);
$result = $this->callActionController('progress', ['code' => $queue->getData('code')]);
$this->assertSame(200, intval($result['code'] ?? 0));
$this->assertSame('获取任务进度成功!', $result['info'] ?? '');
$this->assertSame('QUEUE_PROGRESS_002', $result['data']['code'] ?? null);
$this->assertSame(2, $result['data']['status'] ?? null);
$this->assertSame('>>> 正在执行 <<<', $result['data']['message'] ?? null);
$this->assertSame('0.00', $result['data']['progress'] ?? null);
$this->assertIsArray($result['data']['history'] ?? null);
$this->assertCount(1, $result['data']['history'] ?? []);
}
protected function defineSchema(): void
{
$this->createSystemQueueTable();
$this->createSystemOplogTable();
}
protected function afterSchemaCreated(): void
{
$context = new PluginSystemContext();
Container::getInstance()->instance(SystemContextInterface::class, $context);
$this->app->instance(SystemContextInterface::class, $context);
}
private function bindConsole(array $responses = [], ?\Throwable $exception = null): FakeConsole
{
$console = new FakeConsole($responses, $exception);
$this->app->instance('console', $console);
return $console;
}
private function callStatusController(bool $super): string
{
$request = (new Request())
->setMethod('GET')
->setController('queue')
->setAction('status');
$this->bindAdminUser($super);
$this->setRequestPayload($request, []);
$this->app->instance('request', $request);
ob_start();
(new QueueController($this->app))->status();
return strval(ob_get_clean());
}
private function callActionController(string $action, array $payload = [], bool $super = true): array
{
$request = (new Request())
->withGet($payload)
->withPost($payload)
->setMethod('POST')
->setController('queue')
->setAction($action);
$this->bindAdminUser($super);
$this->setRequestPayload($request, $payload);
$this->app->instance('request', $request);
try {
$controller = new QueueController($this->app);
$controller->{$action}();
self::fail("Expected QueueController::{$action} to throw HttpResponseException.");
} catch (HttpResponseException $exception) {
return json_decode($exception->getResponse()->getContent(), true) ?: [];
}
}
private function bindAdminUser(bool $super): void
{
RequestContext::instance()->setAuth([
'id' => $super ? 10000 : 9101,
'username' => $super ? 'admin' : 'tester',
'password' => $this->hashSystemPassword('changed-password'),
], '', true);
}
private function setRequestPayload(Request $request, array $data): void
{
$property = new \ReflectionProperty(Request::class, 'request');
$property->setAccessible(true);
$property->setValue($request, $data);
}
}