修正新人临时引导库与共享数据库接入状态的误判 #86
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Utilities\SystemDatabaseConfiguration;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Throwable;
|
||||
@ -18,7 +19,7 @@ class ApplicationReady extends Command
|
||||
|
||||
protected $description = '检查应用安装、运行目录和数据库是否已经可用';
|
||||
|
||||
public function handle(): int
|
||||
public function handle(SystemDatabaseConfiguration $configuration): int
|
||||
{
|
||||
if (! config('app.installed')) {
|
||||
return $this->failed('APP_INSTALLED 未被 Laravel 加载。', self::EXIT_NOT_INSTALLED);
|
||||
@ -42,7 +43,11 @@ class ApplicationReady extends Command
|
||||
return $this->failed('Laravel 数据库连接失败。', self::EXIT_DATABASE_CONNECTION_FAILED);
|
||||
}
|
||||
|
||||
$this->components->info('应用已就绪。');
|
||||
if ($configuration->current()['mode'] === 'bootstrap') {
|
||||
$this->components->info('本地网页和临时引导库已就绪;团队共享数据尚未接入。');
|
||||
} else {
|
||||
$this->components->info('应用运行正常,当前数据库可查询。团队数据接入状态请运行 system-database:verify 检查。');
|
||||
}
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
@ -15,6 +15,8 @@ class VerifySystemDatabase extends Command
|
||||
|
||||
public const EXIT_SYSTEM_TABLE_VALIDATION_FAILED = 12;
|
||||
|
||||
public const EXIT_SHARED_NOT_CONFIGURED = 13;
|
||||
|
||||
protected $signature = 'system-database:verify';
|
||||
|
||||
protected $description = '在新的 Laravel 进程中验证当前共享系统数据库';
|
||||
@ -30,12 +32,16 @@ class VerifySystemDatabase extends Command
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
$settings['mode'] !== 'shared'
|
||||
|| ! config('upstreams.stored_configuration.enabled', false)
|
||||
) {
|
||||
if ($settings['mode'] === 'bootstrap') {
|
||||
$this->components->warn('当前仍使用本机临时引导库,尚未接入团队共享系统数据库;本次没有测试远程连接。');
|
||||
$this->line('下一步:在“系统设置 → 数据库管理 → 系统数据库”中输入一次负责人提供的系统数据库密码。');
|
||||
|
||||
return self::EXIT_SHARED_NOT_CONFIGURED;
|
||||
}
|
||||
|
||||
if ($settings['mode'] !== 'shared' || ! config('upstreams.stored_configuration.enabled', false)) {
|
||||
return $this->failed(
|
||||
'新配置未被 Laravel 加载。',
|
||||
'共享配置未完整启用,请通过系统数据库连接页面重新保存;不要仅手工修改运行模式。',
|
||||
self::EXIT_CONFIG_NOT_LOADED
|
||||
);
|
||||
}
|
||||
|
||||
@ -462,6 +462,7 @@ class SystemDatabaseConfiguration
|
||||
VerifySystemDatabase::EXIT_CONFIG_NOT_LOADED => '新配置未被加载',
|
||||
VerifySystemDatabase::EXIT_DATABASE_CONNECTION_FAILED => 'Laravel 数据库连接失败',
|
||||
VerifySystemDatabase::EXIT_SYSTEM_TABLE_VALIDATION_FAILED => '系统表验证失败',
|
||||
VerifySystemDatabase::EXIT_SHARED_NOT_CONFIGURED => '新配置仍指向本机临时引导库',
|
||||
default => '应用级验证失败',
|
||||
};
|
||||
}
|
||||
|
||||
@ -397,16 +397,30 @@ fi
|
||||
|
||||
echo ""
|
||||
echo "====================================================="
|
||||
echo "本地测试环境已就绪:http://127.0.0.1:18088"
|
||||
echo "本地网页可访问:http://127.0.0.1:18088"
|
||||
|
||||
db_host="$(read_env_value DB_HOST)"
|
||||
if [ "$db_host" = "mysql" ]; then
|
||||
echo "临时登录凭据保存在本机私有文件:$credential_file"
|
||||
echo "请自行打开该文件,不要把内容提交到 Git、聊天或日志。"
|
||||
echo ""
|
||||
echo "下一步:登录后打开“系统设置 → 数据库管理 → 系统数据库”,"
|
||||
echo "点击“连接”,只填写负责人提供的系统数据库密码。"
|
||||
# Docker healthy 只表示当前连接可用;共享接入必须由新 Laravel 进程实测,
|
||||
# 不能根据 DB_HOST 不是 mysql 就宣称团队数据已连接。
|
||||
if docker compose -f "$compose_file" exec -T -u www-data app \
|
||||
php artisan system-database:verify --no-ansi; then
|
||||
shared_status=0
|
||||
else
|
||||
echo "已连接团队共享系统数据库,请使用其中属于自己的正式账号登录。"
|
||||
shared_status=$?
|
||||
fi
|
||||
|
||||
case "$shared_status" in
|
||||
0)
|
||||
echo "团队共享系统数据库验证通过,请使用其中属于自己的正式账号登录。"
|
||||
;;
|
||||
13)
|
||||
echo "团队数据接入尚未完成;后台 worker 启动不代表数据已经同步。"
|
||||
echo "临时登录凭据保存在本机私有文件:$credential_file"
|
||||
echo "请登录后输入一次系统数据库密码,连接成功后改用团队正式账号登录。"
|
||||
;;
|
||||
*)
|
||||
echo "团队共享系统数据库未通过验证;不要将 Docker healthy 当作接入成功。" >&2
|
||||
echo "请让 Codex 根据上方失败阶段检查本机配置,保留现有配置和未提交代码。" >&2
|
||||
exit "$shared_status"
|
||||
;;
|
||||
esac
|
||||
echo "====================================================="
|
||||
|
||||
@ -18,4 +18,20 @@ class ApplicationReadyTest extends TestCase
|
||||
$this->artisan('app:ready')
|
||||
->assertExitCode(\App\Console\Commands\ApplicationReady::EXIT_NOT_INSTALLED);
|
||||
}
|
||||
|
||||
public function test_bootstrap_stays_healthy_without_claiming_shared_data_is_ready(): void
|
||||
{
|
||||
config(['app.installed' => true, 'system_database.mode' => 'bootstrap']);
|
||||
$this->artisan('app:ready')
|
||||
->expectsOutputToContain('团队共享数据尚未接入')
|
||||
->assertSuccessful();
|
||||
}
|
||||
|
||||
public function test_shared_application_health_does_not_claim_upstream_sync_success(): void
|
||||
{
|
||||
config(['app.installed' => true, 'system_database.mode' => 'shared']);
|
||||
$this->artisan('app:ready')
|
||||
->expectsOutputToContain('团队数据接入状态请运行 system-database:verify')
|
||||
->assertSuccessful();
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ use Tests\TestCase;
|
||||
|
||||
class VerifySystemDatabaseTest extends TestCase
|
||||
{
|
||||
public function test_verify_command_reports_configuration_not_loaded(): void
|
||||
public function test_bootstrap_is_not_reported_as_a_failed_remote_connection(): void
|
||||
{
|
||||
$configuration = Mockery::mock(SystemDatabaseConfiguration::class);
|
||||
$configuration->shouldReceive('current')->once()->andReturn([
|
||||
@ -19,9 +19,42 @@ class VerifySystemDatabaseTest extends TestCase
|
||||
$this->app->instance(SystemDatabaseConfiguration::class, $configuration);
|
||||
|
||||
$this->artisan('system-database:verify')
|
||||
->expectsOutputToContain('尚未接入团队共享系统数据库;本次没有测试远程连接')
|
||||
->assertExitCode(VerifySystemDatabase::EXIT_SHARED_NOT_CONFIGURED);
|
||||
}
|
||||
|
||||
public function test_shared_mode_with_disabled_stored_configuration_is_not_ready(): void
|
||||
{
|
||||
config(['upstreams.stored_configuration.enabled' => false]);
|
||||
$configuration = Mockery::mock(SystemDatabaseConfiguration::class);
|
||||
$configuration->shouldReceive('current')->once()->andReturn(['mode' => 'shared']);
|
||||
$this->app->instance(SystemDatabaseConfiguration::class, $configuration);
|
||||
$this->artisan('system-database:verify')
|
||||
->expectsOutputToContain('共享配置未完整启用')
|
||||
->assertExitCode(VerifySystemDatabase::EXIT_CONFIG_NOT_LOADED);
|
||||
}
|
||||
|
||||
public function test_current_configuration_failure_remains_a_configuration_error(): void
|
||||
{
|
||||
$configuration = Mockery::mock(SystemDatabaseConfiguration::class);
|
||||
$configuration->shouldReceive('current')->once()->andThrow(new RuntimeException('private details'));
|
||||
$this->app->instance(SystemDatabaseConfiguration::class, $configuration);
|
||||
$this->artisan('system-database:verify')
|
||||
->expectsOutputToContain('新配置未被 Laravel 加载')
|
||||
->assertExitCode(VerifySystemDatabase::EXIT_CONFIG_NOT_LOADED);
|
||||
}
|
||||
|
||||
public function test_shared_success_requires_connection_and_system_table_verification(): void
|
||||
{
|
||||
$this->enableSharedMode();
|
||||
$settings = ['mode' => 'shared', 'connection' => config('database.default')];
|
||||
$configuration = Mockery::mock(SystemDatabaseConfiguration::class);
|
||||
$configuration->shouldReceive('current')->once()->andReturn($settings);
|
||||
$configuration->shouldReceive('probe')->once()->with($settings)->andReturn([]);
|
||||
$this->app->instance(SystemDatabaseConfiguration::class, $configuration);
|
||||
$this->artisan('system-database:verify')->expectsOutputToContain('共享系统数据库已通过应用级验证')->assertSuccessful();
|
||||
}
|
||||
|
||||
public function test_verify_command_reports_database_connection_failure(): void
|
||||
{
|
||||
$this->enableSharedMode();
|
||||
|
||||
39
tests/Unit/Deployment/LocalSharedDatabaseStatusTest.php
Normal file
39
tests/Unit/Deployment/LocalSharedDatabaseStatusTest.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Deployment;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class LocalSharedDatabaseStatusTest extends TestCase
|
||||
{
|
||||
/** @dataProvider verificationStates */
|
||||
public function test_initializer_reports_the_verified_state(int $verificationExit, int $expectedExit, string $message): void
|
||||
{
|
||||
$script = file_get_contents(dirname(__DIR__, 3) . '/deploy/local-init.sh');
|
||||
$start = strpos($script, 'echo "本地网页可访问:');
|
||||
$this->assertNotFalse($start);
|
||||
$process = new Process(['bash', '-c',
|
||||
'set -eu; compose_file=fixture; credential_file=fixture; docker() { return ' . $verificationExit . '; }; '
|
||||
. substr($script, $start),
|
||||
]);
|
||||
$process->setTimeout(5);
|
||||
$process->run();
|
||||
$this->assertSame($expectedExit, $process->getExitCode());
|
||||
$this->assertStringContainsString($message, $process->getOutput() . $process->getErrorOutput());
|
||||
if ($verificationExit !== 0) {
|
||||
$this->assertStringNotContainsString('团队共享系统数据库验证通过', $process->getOutput());
|
||||
}
|
||||
}
|
||||
|
||||
public static function verificationStates(): array
|
||||
{
|
||||
return [
|
||||
'bootstrap' => [13, 0, '团队数据接入尚未完成'],
|
||||
'shared success' => [0, 0, '团队共享系统数据库验证通过'],
|
||||
'config failure' => [10, 10, '未通过验证'],
|
||||
'network failure' => [11, 11, '未通过验证'],
|
||||
'schema failure' => [12, 12, '未通过验证'],
|
||||
];
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user