mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-08-31 05:05:33 +08:00
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
107 lines
5.5 KiB
PHP
107 lines
5.5 KiB
PHP
<?php
|
||
/**
|
||
* PHPUnit 引导脚本.
|
||
*
|
||
* 职责:
|
||
* 1. require composer autoload
|
||
* 2. 引导 ThinkPHP 容器(App::initialize)
|
||
* 3. 【config 级覆盖】把默认连接 connections.main 指向测试库
|
||
* 4. 早期安全护栏:解析出的库名必须含 "test",否则拒绝运行
|
||
*
|
||
* 与 wkbox 衍生项目同构,但区别:
|
||
* - 连接参数从 .env 读取(hostname/hostport/username/password/charset/prefix),
|
||
* 复用开发库同实例的连接(典型单机 mysql,测试库仅库名不同)
|
||
* - 测试库名 = .env 中 DATABASE 值 + "_test" 后缀(如 ulthon_admin → ulthon_admin_test)
|
||
*
|
||
* 为什么用 config 级覆盖而不是环境变量:
|
||
* ThinkPHP 的 Env 组件加载 .env,且 .env 值【优先于】OS 环境变量(getenv 只是 fallback)。
|
||
* .env 里已存在 HOSTNAME/DATABASE/USERNAME/PASSWORD/HOSTPORT,putenv/$env:VAR/bash 前缀变量
|
||
* 都无法覆盖它们。唯一可靠覆盖点 = config 级:App::initialize() 之后直接改写
|
||
* config/database.php 解析出的连接数组。Db facade 运行时读 config,覆盖后所有 DB
|
||
* 操作都连测试库。
|
||
*
|
||
* 关键坑点(think 8):
|
||
* think\Config::set(array $config, ?string $name) 的 $name 只支持【单段一级配置名】,
|
||
* 不像 Config::get 那样解析点号多级路径。直接传 'database.connections.main' 会被当成
|
||
* 带点的字面顶层 key,根本没写进嵌套位置(静默失败)。
|
||
* 正确做法:把整段 'database' 配置 pull 出来 → 原地改 connections.main → set 回 'database'。
|
||
*
|
||
* 不在此做的事:
|
||
* - 不 require 全局 common.php(App::initialize() + autoload 已够引导 facade,
|
||
* helper.php 全局函数测试基建用不到)
|
||
* - 不开事务(事务隔离由 TestCaseBase.setUp 负责)
|
||
* - 不跑 migrate/scheme/seed(由独立的 setup 脚本负责)
|
||
* - 不销毁容器(容器跨测试复用,tearDown 只回滚事务)
|
||
*/
|
||
|
||
declare(strict_types=1);
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// 1. autoload + ThinkPHP 容器引导
|
||
// ----------------------------------------------------------------------------
|
||
require __DIR__ . '/../vendor/autoload.php';
|
||
|
||
$app = new \think\App();
|
||
$app->initialize();
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// 2. 从 .env 读取连接参数,组装测试库连接配置
|
||
// 复用开发库同实例的连接参数(hostname/hostport/username/password/charset/prefix),
|
||
// 仅将 database 名替换为测试库名({原库名}_test)。
|
||
// ----------------------------------------------------------------------------
|
||
$envHostname = \think\facade\Env::get('database.hostname');
|
||
$envHostport = \think\facade\Env::get('database.hostport');
|
||
$envUsername = \think\facade\Env::get('database.username');
|
||
$envPassword = \think\facade\Env::get('database.password');
|
||
$envCharset = \think\facade\Env::get('database.charset');
|
||
$envPrefix = \think\facade\Env::get('database.prefix');
|
||
$envDatabase = \think\facade\Env::get('database.database');
|
||
|
||
// 测试库名策略:原库名 + _test 后缀(如 ulthon_admin → ulthon_admin_test)
|
||
$testDatabase = is_string($envDatabase) && $envDatabase !== ''
|
||
? $envDatabase . '_test'
|
||
: 'ulthon_admin_test';
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// 3. config 级覆盖 connections.main 指向测试库
|
||
// 覆盖目标 = connections.main(本项目 config/database.php 默认连接是 main)。
|
||
// 保留框架自定义 query 类与其它运行时选项,只改"指向哪个库"的部分。
|
||
// ----------------------------------------------------------------------------
|
||
$dbConfig = \think\facade\Config::get('database');
|
||
if (!is_array($dbConfig)) {
|
||
$dbConfig = [];
|
||
}
|
||
$dbConfig['default'] = 'main';
|
||
if (!isset($dbConfig['connections']) || !is_array($dbConfig['connections'])) {
|
||
$dbConfig['connections'] = [];
|
||
}
|
||
if (!isset($dbConfig['connections']['main']) || !is_array($dbConfig['connections']['main'])) {
|
||
$dbConfig['connections']['main'] = [];
|
||
}
|
||
$dbConfig['connections']['main'] = array_merge(
|
||
$dbConfig['connections']['main'],
|
||
[
|
||
'type' => 'mysql',
|
||
'hostname' => is_string($envHostname) && $envHostname !== '' ? $envHostname : '127.0.0.1',
|
||
'hostport' => is_string($envHostport) && $envHostport !== '' ? $envHostport : '3306',
|
||
'database' => $testDatabase,
|
||
'username' => is_string($envUsername) && $envUsername !== '' ? $envUsername : 'root',
|
||
'password' => is_string($envPassword) ? $envPassword : '',
|
||
'charset' => is_string($envCharset) && $envCharset !== '' ? $envCharset : 'utf8mb4',
|
||
'prefix' => is_string($envPrefix) && $envPrefix !== '' ? $envPrefix : 'ul_',
|
||
'fields_cache' => false,
|
||
],
|
||
// 必须保留框架自定义 query 类,否则 ORM 行为异常。
|
||
['query' => \app\common\provider\db\Query::class],
|
||
);
|
||
\think\facade\Config::set($dbConfig, 'database');
|
||
|
||
// ----------------------------------------------------------------------------
|
||
// 4. 早期安全护栏:在任何测试运行前,确认解析出的库名含 "test"
|
||
// (TestCaseBase.setUp 会再校验一次;这里更早触发,防患于未然)
|
||
// ----------------------------------------------------------------------------
|
||
$resolvedDb = \think\facade\Config::get('database.connections.main.database');
|
||
if (!is_string($resolvedDb) || stripos($resolvedDb, 'test') === false) {
|
||
throw new \RuntimeException('Test bootstrap refusing non-test database: ' . $resolvedDb);
|
||
}
|