verified = $this->freshVerificationEnvironment(); } protected function clearConfigurationCache(): void {} }; try { $backup = $configuration->persist([ 'host' => 'shared.example.test', 'port' => 8443, 'database' => 'shared_system', 'username' => 'fixture_user', 'password' => 'fixture_password', 'prefix' => '', 'tls_required' => true, 'tls_verify_server_cert' => false, 'mirror_database' => 'caiwu_business_mirror', ]); $saved = \Dotenv\Dotenv::parse(file_get_contents($environmentPath)); $this->assertSame('shared.example.test', $saved['BUSINESS_MIRROR_DB_HOST']); $this->assertSame('shared.example.test', $saved['BUSINESS_MIRROR_DB_HOST_READ']); $this->assertSame('shared.example.test', $saved['BUSINESS_MIRROR_DB_HOST_WRITE']); $this->assertSame('8443', $saved['BUSINESS_MIRROR_DB_PORT']); $this->assertSame('', $saved['BUSINESS_MIRROR_DB_SOCKET']); $this->assertSame('caiwu_business_mirror', $saved['BUSINESS_MIRROR_DB_DATABASE']); $this->assertSame('fixture_password', $configuration->verified['BUSINESS_MIRROR_DB_PASSWORD']); foreach (['DB_HOST', 'DB_HOST_READ', 'DB_HOST_WRITE'] as $key) { $this->assertSame('shared.example.test', $saved[$key]); $this->assertSame('shared.example.test', $configuration->verified[$key]); } $this->assertSame('', $saved['DATABASE_URL']); $this->assertSame('', $saved['DB_SOCKET']); $this->assertSame('keep', $saved['UNRELATED_OPTION']); $this->assertSame($original, file_get_contents($backup)); } finally { @unlink($environmentPath); if (isset($backup)) @unlink($backup); } } public function test_failed_fresh_process_verification_restores_original_environment(): void { $environmentPath = storage_path('framework/testing-system-database.env'); $original = "DB_HOST=mysql\nDB_DATABASE=akaunting\nSYSTEM_DATABASE_MODE=bootstrap\n"; file_put_contents($environmentPath, $original); $configuration = new class($environmentPath) extends SystemDatabaseConfiguration { public array $commands = []; protected function runFreshVerification(string $command): void { $this->commands[] = $command; if ($command === 'system-database:verify') { throw new SystemDatabaseVerificationException('系统表验证失败'); } } }; try { $configuration->persist([ 'host' => 'shared-db.test', 'port' => 3306, 'database' => 'shared_system', 'username' => 'shared_writer', 'password' => 'shared-secret', 'prefix' => 'ak_', 'tls_required' => true, 'tls_verify_server_cert' => false, 'tls_ca' => '', ]); $this->fail('验证失败时应抛出异常。'); } catch (RuntimeException $exception) { $this->assertSame( '新配置未通过应用级验证(系统表验证失败),已自动恢复原系统数据库。', $exception->getMessage() ); $this->assertSame($original, file_get_contents($environmentPath)); $this->assertSame(['system-database:verify', 'app:ready'], $configuration->commands); } finally { @unlink($environmentPath); } } public function test_root_owned_or_invalid_backup_directory_uses_writable_storage_fallback(): void { $environmentPath = storage_path('framework/testing-system-database-fallback.env'); $blockedBackupPath = storage_path('framework/testing-blocked-backup-path'); file_put_contents($environmentPath, "DB_HOST=mysql\nDB_DATABASE=akaunting\n"); file_put_contents($blockedBackupPath, 'not-a-directory'); $configuration = new class($environmentPath, $blockedBackupPath) extends SystemDatabaseConfiguration { protected function runFreshVerification(string $command): void { } }; try { $backupPath = $configuration->persist([ 'host' => 'shared-db.test', 'port' => 3306, 'database' => 'shared_system', 'username' => 'shared_writer', 'password' => 'shared-secret', 'prefix' => 'ak_', 'tls_required' => true, 'tls_verify_server_cert' => false, 'tls_ca' => '', ]); $this->assertSame(storage_path('app'), dirname($backupPath)); $this->assertFileExists($backupPath); } finally { @unlink($environmentPath); @unlink($blockedBackupPath); if (isset($backupPath)) { @unlink($backupPath); } } } public function test_fresh_verification_uses_values_from_the_just_saved_environment(): void { $environmentPath = storage_path('framework/testing-fresh-verification.env'); file_put_contents($environmentPath, implode("\n", [ 'DB_HOST="new-shared-host.test"', 'DB_DATABASE="new_shared_database"', 'DB_PASSWORD="new-shared-password"', 'SYSTEM_DATABASE_MODE="shared"', 'UPSTREAM_STORED_CONFIG_ENABLED="true"', 'UPSTREAM_LOCAL_CONFIG_ENABLED="false"', '', ])); try { $configuration = new class($environmentPath) extends SystemDatabaseConfiguration { public function verificationEnvironment(): array { return $this->freshVerificationEnvironment(); } }; $environment = $configuration->verificationEnvironment(); $this->assertSame('new-shared-host.test', $environment['DB_HOST']); $this->assertSame('new_shared_database', $environment['DB_DATABASE']); $this->assertSame('new-shared-password', $environment['DB_PASSWORD']); $this->assertSame('shared', $environment['SYSTEM_DATABASE_MODE']); $this->assertSame('true', $environment['UPSTREAM_STORED_CONFIG_ENABLED']); $this->assertSame('false', $environment['UPSTREAM_LOCAL_CONFIG_ENABLED']); $this->assertFalse($environment['DB_PREFIX']); $this->assertFalse($environment['DATABASE_URL']); $this->assertFalse($environment['DB_HOST_READ']); $this->assertFalse($environment['DB_HOST_WRITE']); $this->assertFalse($environment['DB_SOCKET']); } finally { @unlink($environmentPath); } } public function test_php_cli_finder_returns_an_executable_for_fresh_verification(): void { $configuration = new class extends SystemDatabaseConfiguration { public function executable(): string { return $this->phpExecutable(); } }; $executable = $configuration->executable(); $this->assertNotSame('', $executable); $this->assertTrue(is_executable($executable)); $this->assertSame('php', pathinfo($executable, PATHINFO_FILENAME)); } public function test_failed_rollback_reports_candidate_and_rollback_stages(): void { $environmentPath = storage_path('framework/testing-system-database-rollback.env'); $original = "DB_HOST=mysql\nDB_DATABASE=akaunting\nSYSTEM_DATABASE_MODE=bootstrap\n"; file_put_contents($environmentPath, $original); $configuration = new class($environmentPath) extends SystemDatabaseConfiguration { protected function runFreshVerification(string $command): void { throw new SystemDatabaseVerificationException( $command === 'system-database:verify' ? 'Laravel 数据库连接失败' : '运行目录不可写' ); } protected function clearConfigurationCache(): void { } }; try { $configuration->persist([ 'host' => 'shared-db.test', 'port' => 3306, 'database' => 'shared_system', 'username' => 'shared_writer', 'password' => 'shared-secret', 'prefix' => 'ak_', 'tls_required' => true, 'tls_verify_server_cert' => false, 'tls_ca' => '', ]); $this->fail('新旧配置验证均失败时应抛出异常。'); } catch (RuntimeException $exception) { $this->assertSame( '新配置未通过应用级验证(Laravel 数据库连接失败),旧配置文件已经恢复,但旧配置复核失败(运行目录不可写)。', $exception->getMessage() ); $this->assertSame($original, file_get_contents($environmentPath)); } finally { @unlink($environmentPath); } } }