feat(test): 框架测试基建基类TestCaseBase+app层入口+冒烟测试

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
augushong
2026-07-18 00:12:41 +08:00
parent 44621b1e9d
commit 3f7050bf65
3 changed files with 334 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace tests\Feature;
use app\common\test\TestCase;
use PHPUnit\Framework\AssertionFailedError;
use ReflectionMethod;
use think\facade\Config;
/**
* 隔离机制冒烟测试.
*
* 验证 TestCaseBase 的两个核心契约:
* 1. 事务回滚隔离真正生效(探针行跨测试不残留)
* 2. 安全校验拦截非测试库(拒绝连生产库)
*
* 继承 app 层入口 app\common\test\TestCaseBase/App 铁律:业务测试不直接继承 Base
*/
class IsolationSmokeTest extends TestCase
{
/**
* 探针自验证:先断言不存在→插入→断言可见.
*
* 完整契约依赖 tearDown 的 Db::rollback():本用例插入的探针行必须被回滚撤销,
* 从而【下次运行】本用例时第 1 步"不存在"断言才能成立。
* 单次运行验证写入路径;连续两次运行验证回滚隔离。
*/
public function test_isolation_probe_rolls_back(): void
{
// assertIsolationWorks 内部完成missing → insert → has 三段断言。
$this->assertIsolationWorks();
// 显式成功标记assertIsolationWorks 不带返回值,避免 risky test 告警)。
$this->assertTrue(true, 'isolation probe inserted and is visible within the transaction');
}
/**
* 拒绝非测试库:临时把连接库名改为不含 "test" 的值,
* 反射调用 protected assertTestDatabase 应触发 fail()。
*
* 使用 try/catch/finally 而非 expectException目的是无论是否抛出
* 都能在 finally 中恢复 config避免污染后续测试。
*/
public function test_refuses_non_test_database(): void
{
// think 的 Config::set 不支持点号多级路径(见 bootstrap 注释),
// 必须像 bootstrap 那样整段 pull 出来改了再 set 回去。
$dbConfig = Config::get('database');
$backup = is_array($dbConfig) ? $dbConfig : [];
$thrown = null;
try {
$dbConfig['connections']['main']['database'] = 'ulthon_admin_production_safe_marker';
Config::set($dbConfig, 'database');
// 反射调用 protected assertTestDatabasesetUp 里已自动调过一次并通过,
// 这里在事务内二次校验非测试库名,应当 fail
$method = new ReflectionMethod(self::class, 'assertTestDatabase');
$method->setAccessible(true);
$method->invoke($this);
} catch (AssertionFailedError $e) {
$thrown = $e;
} finally {
// 无论是否抛出,恢复 config —— 不污染同一进程后续测试。
Config::set($backup, 'database');
}
$this->assertNotNull($thrown, 'assertTestDatabase should fail when database name lacks "test"');
$this->assertStringContainsString('non-test database', $thrown->getMessage());
}
}