Files
ulthon_admin/tests/bootstrap.php
2026-07-18 00:12:30 +08:00

107 lines
5.5 KiB
PHP
Raw Permalink 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
/**
* 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/HOSTPORTputenv/$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.phpApp::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);
}