Files
ulthon_admin/tests/Feature/IsolationSmokeTest.php

74 lines
2.9 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\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());
}
}