CaiWuXiTong/tests/Unit/Utilities/SystemDatabasePersistenceTest.php

241 lines
10 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
namespace Tests\Unit\Utilities;
use App\Exceptions\SystemDatabaseVerificationException;
use App\Utilities\SystemDatabaseConfiguration;
use RuntimeException;
use Tests\TestCase;
class SystemDatabasePersistenceTest extends TestCase
{
public function test_switch_replaces_all_legacy_connection_routes_and_duplicate_keys(): void
{
$environmentPath = storage_path('framework/testing-legacy-routes.env');
$original = "DB_HOST=mysql\nDB_HOST_READ=old-read\nDB_HOST_WRITE=old-write\n"
. "DATABASE_URL=mysql://fixture:fixture@old-url/old_database\nDB_SOCKET=/tmp/old.sock\n"
. "DB_HOST=old-duplicate\nSYSTEM_DATABASE_MODE=bootstrap\nUNRELATED_OPTION=keep\n"
. "BUSINESS_MIRROR_DB_HOST=old-mirror\nBUSINESS_MIRROR_DB_HOST_READ=old-mirror-read\nBUSINESS_MIRROR_DB_HOST_WRITE=old-mirror-write\nBUSINESS_MIRROR_DB_PORT=13306\nBUSINESS_MIRROR_DB_SOCKET=/tmp/mirror.sock\n";
file_put_contents($environmentPath, $original);
$configuration = new class($environmentPath) extends SystemDatabaseConfiguration {
public array $verified = [];
protected function runFreshVerification(string $command): void
{
$this->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);
}
}
}