Files
huangjingfen/pro_v3.5.1/tests/hjf/SmokeTest.php
apple 78de918c37 Initial commit: queue workspace
Made-with: Cursor
2026-03-21 02:55:24 +08:00

327 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace tests\hjf;
use PHPUnit\Framework\TestCase;
/**
* P4 联调冒烟测试SmokeTest
*
* 使用说明:
* 1. 确保 Phase 2 数据库迁移已执行eb_queue_pool / eb_points_release_log 等表已创建)
* 2. 确保后端服务已启动ThinkPHP 8 + Swoole 或 PHP-FPM
* 3. 设置环境变量:
* HJF_API_BASE=http://127.0.0.1:8080/api
* HJF_ADMIN_BASE=http://127.0.0.1:8080/adminapi
* HJF_TOKEN=<有效用户Token>
* HJF_ADMIN_TOKEN=<有效管理员Token>
* 4. 运行:./vendor/bin/phpunit tests/hjf/SmokeTest.php
*
* 覆盖场景:
* P4-02 UniApp 冒烟:登录后访问公排/资产/积分/会员接口
* P4-03 Admin 冒烟:公排订单列表 / 配置保存 / 会员等级 / 积分日志
* P4-05 定时任务验证:手动调用积分释放 command验证 release_log 有新记录
*
* Class SmokeTest
* @package tests\hjf
*/
class SmokeTest extends TestCase
{
private string $apiBase;
private string $adminBase;
private string $token;
private string $adminToken;
protected function setUp(): void
{
$this->apiBase = rtrim(getenv('HJF_API_BASE') ?: 'http://127.0.0.1:8080/api', '/');
$this->adminBase = rtrim(getenv('HJF_ADMIN_BASE') ?: 'http://127.0.0.1:8080/adminapi', '/');
$this->token = getenv('HJF_TOKEN') ?: '';
$this->adminToken = getenv('HJF_ADMIN_TOKEN') ?: '';
}
// -----------------------------------------------------------------------
// 辅助方法
// -----------------------------------------------------------------------
/**
* 发送 GET 请求,返回解析后的响应体数组
*/
private function get(string $url, string $token = ''): array
{
$headers = ['Content-Type: application/json'];
if ($token) {
$headers[] = "Authorization: Bearer {$token}";
}
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => 10,
]);
$body = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode((string)$body, true) ?: [];
$data['__http_code'] = $http;
return $data;
}
/**
* 发送 POST 请求,返回解析后的响应体数组
*/
private function post(string $url, array $payload, string $token = ''): array
{
$headers = ['Content-Type: application/json'];
if ($token) {
$headers[] = "Authorization: Bearer {$token}";
}
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => 10,
]);
$body = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode((string)$body, true) ?: [];
$data['__http_code'] = $http;
return $data;
}
// -----------------------------------------------------------------------
// P4-02 UniApp 冒烟测试
// -----------------------------------------------------------------------
/**
* @test
* P4-02-A: 公排状态接口可访问,返回 200 且包含 total_orders/my_orders/progress 字段
*/
public function testUniAppQueueStatus(): void
{
if (!$this->token) {
$this->markTestSkipped('HJF_TOKEN 未设置,跳过');
}
$res = $this->get("{$this->apiBase}/hjf/queue/status", $this->token);
$this->assertEquals(200, $res['__http_code'], '公排状态接口 HTTP 200');
$this->assertArrayHasKey('data', $res, '响应包含 data 字段');
$data = $res['data'];
$this->assertArrayHasKey('total_orders', $data, 'data 包含 total_orders');
$this->assertArrayHasKey('my_orders', $data, 'data 包含 my_orders');
$this->assertArrayHasKey('progress', $data, 'data 包含 progress');
}
/**
* @test
* P4-02-B: 公排历史接口可访问,返回分页格式
*/
public function testUniAppQueueHistory(): void
{
if (!$this->token) {
$this->markTestSkipped('HJF_TOKEN 未设置,跳过');
}
$res = $this->get("{$this->apiBase}/hjf/queue/history?page=1&limit=10", $this->token);
$this->assertEquals(200, $res['__http_code'], '公排历史接口 HTTP 200');
$data = $res['data'] ?? [];
$this->assertArrayHasKey('list', $data, 'data 包含 list');
$this->assertArrayHasKey('count', $data, 'data 包含 count');
}
/**
* @test
* P4-02-C: 资产总览接口,返回余额+积分字段
*/
public function testUniAppAssetsOverview(): void
{
if (!$this->token) {
$this->markTestSkipped('HJF_TOKEN 未设置,跳过');
}
$res = $this->get("{$this->apiBase}/hjf/assets/overview", $this->token);
$this->assertEquals(200, $res['__http_code'], '资产总览接口 HTTP 200');
$data = $res['data'] ?? [];
$this->assertArrayHasKey('now_money', $data, 'data 包含 now_money');
$this->assertArrayHasKey('frozen_points', $data, 'data 包含 frozen_points');
$this->assertArrayHasKey('available_points', $data, 'data 包含 available_points');
}
/**
* @test
* P4-02-D: 积分明细接口,返回分页列表
*/
public function testUniAppPointsDetail(): void
{
if (!$this->token) {
$this->markTestSkipped('HJF_TOKEN 未设置,跳过');
}
$res = $this->get("{$this->apiBase}/hjf/points/detail?page=1&limit=10", $this->token);
$this->assertEquals(200, $res['__http_code'], '积分明细接口 HTTP 200');
$data = $res['data'] ?? [];
$this->assertArrayHasKey('list', $data, 'data 包含 list');
$this->assertArrayHasKey('count', $data, 'data 包含 count');
}
/**
* @test
* P4-02-E: 会员信息接口,返回 member_level 字段
*/
public function testUniAppMemberInfo(): void
{
if (!$this->token) {
$this->markTestSkipped('HJF_TOKEN 未设置,跳过');
}
$res = $this->get("{$this->apiBase}/hjf/member/info", $this->token);
$this->assertEquals(200, $res['__http_code'], '会员信息接口 HTTP 200');
$data = $res['data'] ?? [];
$this->assertArrayHasKey('member_level', $data, 'data 包含 member_level');
$this->assertContains(
(int)($data['member_level'] ?? -1),
[0, 1, 2, 3, 4],
'member_level 取值 0~4'
);
}
// -----------------------------------------------------------------------
// P4-03 Admin 冒烟测试
// -----------------------------------------------------------------------
/**
* @test
* P4-03-A: Admin 公排订单列表接口,返回分页数据
*/
public function testAdminQueueOrderList(): void
{
if (!$this->adminToken) {
$this->markTestSkipped('HJF_ADMIN_TOKEN 未设置,跳过');
}
$res = $this->get("{$this->adminBase}/hjf/queue/order?page=1&limit=10", $this->adminToken);
$this->assertEquals(200, $res['__http_code'], 'Admin 公排订单列表 HTTP 200');
$data = $res['data'] ?? [];
$this->assertArrayHasKey('list', $data, 'data 包含 list');
$this->assertArrayHasKey('count', $data, 'data 包含 count');
}
/**
* @test
* P4-03-B: Admin 公排配置读取,返回 hjf_trigger_multiple 等关键字段
*/
public function testAdminQueueConfigGet(): void
{
if (!$this->adminToken) {
$this->markTestSkipped('HJF_ADMIN_TOKEN 未设置,跳过');
}
$res = $this->get("{$this->adminBase}/hjf/queue/config", $this->adminToken);
$this->assertEquals(200, $res['__http_code'], 'Admin 公排配置 HTTP 200');
$data = $res['data'] ?? [];
$this->assertArrayHasKey('hjf_trigger_multiple', $data, 'data 包含 hjf_trigger_multiple');
}
/**
* @test
* P4-03-C: Admin 公排配置保存(写回原值,不改变业务数据)
*/
public function testAdminQueueConfigSave(): void
{
if (!$this->adminToken) {
$this->markTestSkipped('HJF_ADMIN_TOKEN 未设置,跳过');
}
// 先读取当前配置
$getRes = $this->get("{$this->adminBase}/hjf/queue/config", $this->adminToken);
$current = $getRes['data'] ?? ['hjf_trigger_multiple' => 4, 'hjf_release_rate' => 4];
// 写回原值
$saveRes = $this->post("{$this->adminBase}/hjf/queue/config", $current, $this->adminToken);
$this->assertEquals(200, $saveRes['__http_code'], 'Admin 公排配置保存 HTTP 200');
}
/**
* @test
* P4-03-D: Admin 会员列表接口,返回分页数据含 member_level 列
*/
public function testAdminMemberList(): void
{
if (!$this->adminToken) {
$this->markTestSkipped('HJF_ADMIN_TOKEN 未设置,跳过');
}
$res = $this->get("{$this->adminBase}/hjf/member/list?page=1&limit=10", $this->adminToken);
$this->assertEquals(200, $res['__http_code'], 'Admin 会员列表 HTTP 200');
$data = $res['data'] ?? [];
$this->assertArrayHasKey('list', $data, 'data 包含 list');
$this->assertArrayHasKey('count', $data, 'data 包含 count');
// 若有记录,验证字段完整性
if (!empty($data['list'])) {
$first = $data['list'][0];
$this->assertArrayHasKey('member_level', $first, 'list[0] 包含 member_level');
$this->assertArrayHasKey('direct_order_count', $first, 'list[0] 包含 direct_order_count');
$this->assertArrayHasKey('umbrella_order_count', $first, 'list[0] 包含 umbrella_order_count');
}
}
/**
* @test
* P4-03-E: Admin 积分释放日志接口,返回分页数据
*/
public function testAdminPointsReleaseLog(): void
{
if (!$this->adminToken) {
$this->markTestSkipped('HJF_ADMIN_TOKEN 未设置,跳过');
}
$res = $this->get("{$this->adminBase}/hjf/points/release-log?page=1&limit=10", $this->adminToken);
$this->assertEquals(200, $res['__http_code'], 'Admin 积分释放日志 HTTP 200');
$data = $res['data'] ?? [];
$this->assertArrayHasKey('list', $data, 'data 包含 list');
$this->assertArrayHasKey('count', $data, 'data 包含 count');
}
// -----------------------------------------------------------------------
// P4-05 定时任务验证(积分每日释放)
// -----------------------------------------------------------------------
/**
* @test
* P4-05: 手动触发 Artisan/Think 积分释放命令后release_log 有新记录
*
* 验证方式:
* 1. 记录触发前的日志总数
* 2. 调用 Admin 接口触发(或本地 CLIphp think hjf:release-points
* 3. 等待队列消费最多10秒
* 4. 再次查询日志总数,新增量 >= 1
*/
public function testDailyPointsReleaseCreatesLogs(): void
{
if (!$this->adminToken) {
$this->markTestSkipped('HJF_ADMIN_TOKEN 未设置,跳过');
}
// 触发前的日志总数
$before = $this->get("{$this->adminBase}/hjf/points/release-log?page=1&limit=1", $this->adminToken);
$countBefore = (int)($before['data']['count'] ?? 0);
// 触发定时任务(通过 CLI实际测试时需在后端服务器执行
// 此处记录预期行为:执行后 count 应增加
// 若在 CI 环境中可替换为shell_exec('php think hjf:release-points');
// 由于无法在 PHPUnit 内部确保队列消费完成,此测试标记为 skipped 并记录说明
$this->markTestIncomplete(
"P4-05 定时任务测试需在后端服务器手动执行:\n" .
" php think hjf:release-points\n" .
"执行后再运行本测试检验 release_log count 从 {$countBefore} 增加。\n" .
"若 frozen_points 为0的用户较多释放量可能为0属正常行为"
);
}
}