637 lines
26 KiB
PHP
637 lines
26 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\SystemSettings;
|
|
|
|
use App\Models\Auth\Permission;
|
|
use App\Models\Auth\Role;
|
|
use App\Models\Settings\DatabaseConnection;
|
|
use App\Models\Settings\UpstreamSchemaColumn;
|
|
use App\Utilities\SystemDatabaseConfiguration;
|
|
use App\Utilities\TeamSystemDatabaseProfile;
|
|
use App\Utilities\UpstreamCredentialCipher;
|
|
use Illuminate\Encryption\Encrypter;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Mockery;
|
|
use Modules\EcommerceFinance\Services\UpstreamSourceRegistry;
|
|
use Modules\EcommerceFinance\Services\UpstreamStoredConfiguration;
|
|
use Modules\EcommerceFinance\Services\UpstreamDatabaseProbe;
|
|
use Tests\Feature\FeatureTestCase;
|
|
|
|
class UpstreamDatabasesTest extends FeatureTestCase
|
|
{
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->user->attachPermission(
|
|
Permission::where('name', 'read-admin-panel')->firstOrFail()
|
|
);
|
|
|
|
$connection = (string) config('database.default');
|
|
config([
|
|
"database.connections.$connection.password" => 'shared-system-database-secret',
|
|
'system_database.mode' => 'shared',
|
|
'upstreams.stored_configuration.enabled' => true,
|
|
]);
|
|
}
|
|
|
|
public function test_super_admin_can_add_multiple_encrypted_database_configurations(): void
|
|
{
|
|
$this->loginAs()
|
|
->post(route('system-settings.databases.store'), $this->payload([
|
|
'name' => 'JianSun 测试库',
|
|
'source_key' => 'jiansun',
|
|
'password' => 'first-secret',
|
|
]))
|
|
->assertRedirect(route('system-settings.databases.index'));
|
|
|
|
$this->post(route('system-settings.databases.store'), $this->payload([
|
|
'name' => '云数据库二号',
|
|
'source_key' => '',
|
|
'host' => '127.0.0.2',
|
|
'password' => 'second-secret',
|
|
]))->assertRedirect(route('system-settings.databases.index'));
|
|
|
|
$this->assertSame(2, DatabaseConnection::where('company_id', 0)->count());
|
|
$connection = DatabaseConnection::where('source_key', 'jiansun')->firstOrFail();
|
|
$this->assertSame('first-secret', $connection->password);
|
|
$this->assertNotSame(
|
|
'first-secret',
|
|
DB::table('system_database_connections')->where('id', $connection->id)->value('password')
|
|
);
|
|
$sharedPassword = DB::table('system_database_connections')
|
|
->where('id', $connection->id)
|
|
->value('shared_password');
|
|
$this->assertIsString($sharedPassword);
|
|
$this->assertNotSame('first-secret', $sharedPassword);
|
|
$this->assertSame(
|
|
'first-secret',
|
|
app(UpstreamCredentialCipher::class)->decrypt($sharedPassword)
|
|
);
|
|
|
|
$sources = app(UpstreamSourceRegistry::class);
|
|
$this->assertSame('127.0.0.1', $sources->value('jiansun', 'connection.host'));
|
|
$this->assertSame('first-secret', $sources->value('jiansun', 'connection.password'));
|
|
|
|
$this->get(route('system-settings.databases.index'))
|
|
->assertOk()
|
|
->assertSeeText('JianSun 测试库')
|
|
->assertSeeText('云数据库二号')
|
|
->assertDontSee('first-secret');
|
|
}
|
|
|
|
public function test_editing_without_password_preserves_existing_password(): void
|
|
{
|
|
$connection = $this->connection('original-secret');
|
|
|
|
$this->loginAs()
|
|
->patch(route('system-settings.databases.update', $connection->id), $this->payload([
|
|
'name' => $connection->name,
|
|
'password' => '',
|
|
'host' => '127.0.0.2',
|
|
]))
|
|
->assertRedirect(route('system-settings.databases.index'));
|
|
|
|
$connection->refresh();
|
|
$this->assertSame('original-secret', $connection->password);
|
|
$this->assertSame('127.0.0.2', $connection->host);
|
|
|
|
$this->get(route('system-settings.databases.edit', $connection->id))
|
|
->assertOk()
|
|
->assertDontSee('original-secret');
|
|
}
|
|
|
|
public function test_local_password_is_used_when_shared_record_uses_another_app_key(): void
|
|
{
|
|
$connection = $this->connection('shared-secret');
|
|
$connection->update(['source_key' => 'jiansun']);
|
|
$originalEncrypter = $this->app->make('encrypter');
|
|
|
|
try {
|
|
$this->app->instance(
|
|
'encrypter',
|
|
new Encrypter(str_repeat('x', 32), (string) config('app.cipher'))
|
|
);
|
|
config([
|
|
'upstreams.local_configuration.enabled' => true,
|
|
'jiansun.connection.host' => 'local-db.test',
|
|
'jiansun.connection.database' => 'local_database',
|
|
'jiansun.connection.username' => 'local_user',
|
|
'jiansun.connection.password' => 'local-secret',
|
|
'jiansun.connection.port' => 11433,
|
|
]);
|
|
$sources = new UpstreamSourceRegistry(new UpstreamStoredConfiguration());
|
|
|
|
$this->assertSame(
|
|
'local-secret',
|
|
$sources->value('jiansun', 'connection.password')
|
|
);
|
|
$this->assertSame('local-db.test', $sources->value('jiansun', 'connection.host'));
|
|
$this->assertSame('local_database', $sources->value('jiansun', 'connection.database'));
|
|
$this->assertSame('local_user', $sources->value('jiansun', 'connection.username'));
|
|
$this->assertSame(11433, $sources->value('jiansun', 'connection.port'));
|
|
} finally {
|
|
$this->app->instance('encrypter', $originalEncrypter);
|
|
}
|
|
}
|
|
|
|
public function test_private_override_is_honored_when_explicitly_enabled_in_production(): void
|
|
{
|
|
$connection = $this->connection('shared-secret');
|
|
$connection->update(['source_key' => 'jiansun']);
|
|
$this->app->detectEnvironment(fn () => 'production');
|
|
|
|
try {
|
|
config([
|
|
'upstreams.local_configuration.enabled' => true,
|
|
'jiansun.connection.host' => 'private-tunnel.test',
|
|
'jiansun.connection.password' => 'private-secret',
|
|
]);
|
|
$sources = new UpstreamSourceRegistry(new UpstreamStoredConfiguration());
|
|
|
|
$this->assertSame(
|
|
'private-tunnel.test',
|
|
$sources->value('jiansun', 'connection.host')
|
|
);
|
|
$this->assertSame(
|
|
'private-secret',
|
|
$sources->value('jiansun', 'connection.password')
|
|
);
|
|
} finally {
|
|
$this->app->detectEnvironment(fn () => 'testing');
|
|
}
|
|
}
|
|
|
|
public function test_shared_password_is_available_with_a_different_app_key(): void
|
|
{
|
|
$connection = $this->connection('shared-secret');
|
|
$connection->update(['source_key' => 'jiansun']);
|
|
$originalEncrypter = $this->app->make('encrypter');
|
|
|
|
try {
|
|
$this->app->instance(
|
|
'encrypter',
|
|
new Encrypter(str_repeat('x', 32), (string) config('app.cipher'))
|
|
);
|
|
config(['upstreams.local_configuration.enabled' => false]);
|
|
$sources = new UpstreamSourceRegistry(new UpstreamStoredConfiguration());
|
|
|
|
// 兼容尚未重建的旧本地镜像:旧模块仍直接读取 password 属性。
|
|
$this->assertSame('shared-secret', $connection->fresh()->password);
|
|
$this->assertSame(
|
|
'shared-secret',
|
|
$sources->value('jiansun', 'connection.password')
|
|
);
|
|
} finally {
|
|
$this->app->instance('encrypter', $originalEncrypter);
|
|
}
|
|
}
|
|
|
|
public function test_complete_connection_uses_shared_fields_and_local_runtime_options(): void
|
|
{
|
|
$connection = $this->connection('shared-secret');
|
|
$connection->update([
|
|
'source_key' => 'jiansun',
|
|
'host' => 'shared-db.test',
|
|
'port' => 14330,
|
|
'database_name' => 'shared_database',
|
|
'username' => 'shared_reader',
|
|
'odbc_driver' => 'Stored Driver',
|
|
]);
|
|
config([
|
|
'upstreams.local_configuration.enabled' => false,
|
|
'jiansun.connection' => [
|
|
'host' => null,
|
|
'port' => 1433,
|
|
'database' => null,
|
|
'username' => null,
|
|
'password' => null,
|
|
'odbc_driver' => 'FreeTDS',
|
|
'tds_version' => '7.4',
|
|
'encryption' => 'off',
|
|
'connect_timeout' => 5,
|
|
'query_timeout' => 15,
|
|
'application_intent' => 'ReadOnly',
|
|
],
|
|
]);
|
|
|
|
$sources = new UpstreamSourceRegistry(new UpstreamStoredConfiguration());
|
|
$resolved = $sources->value('jiansun', 'connection', []);
|
|
|
|
$this->assertSame('shared-db.test', $resolved['host']);
|
|
$this->assertSame(14330, $resolved['port']);
|
|
$this->assertSame('shared_database', $resolved['database']);
|
|
$this->assertSame('shared_reader', $resolved['username']);
|
|
$this->assertSame('shared-secret', $resolved['password']);
|
|
$this->assertSame('FreeTDS', $resolved['odbc_driver']);
|
|
$this->assertSame('off', $resolved['encryption']);
|
|
$this->assertSame('ReadOnly', $resolved['application_intent']);
|
|
}
|
|
|
|
public function test_production_tunnel_replaces_only_the_shared_connection_endpoint(): void
|
|
{
|
|
$connection = $this->connection('shared-secret');
|
|
$connection->update([
|
|
'source_key' => 'operations_replica',
|
|
'host' => 'shared-db.test',
|
|
'port' => 14330,
|
|
'database_name' => 'shared_database',
|
|
'username' => 'shared_reader',
|
|
]);
|
|
$this->app->detectEnvironment(fn () => 'production');
|
|
|
|
try {
|
|
config([
|
|
'upstreams.local_configuration.enabled' => false,
|
|
'upstreams.stored_configuration.enabled' => true,
|
|
'upstreams.tunnel_endpoints.operations_replica' => [
|
|
'host' => 'private-tunnel.test',
|
|
'port' => 15433,
|
|
],
|
|
]);
|
|
$sources = new UpstreamSourceRegistry(new UpstreamStoredConfiguration());
|
|
$resolved = $sources->value('operations_replica', 'connection', []);
|
|
|
|
$this->assertSame('private-tunnel.test', $resolved['host']);
|
|
$this->assertSame(15433, $resolved['port']);
|
|
$this->assertSame('shared_database', $resolved['database']);
|
|
$this->assertSame('shared_reader', $resolved['username']);
|
|
$this->assertSame('shared-secret', $resolved['password']);
|
|
} finally {
|
|
$this->app->detectEnvironment(fn () => 'testing');
|
|
}
|
|
}
|
|
|
|
public function test_js_data_middle_platform_declares_a_private_production_tunnel_endpoint(): void
|
|
{
|
|
$contents = file_get_contents(config_path('upstreams.php'));
|
|
|
|
$this->assertIsString($contents);
|
|
$this->assertStringContainsString("'js_data_middle_platform' => [", $contents);
|
|
$this->assertStringContainsString("env('UPSTREAM_TUNNEL_JS_DATA_MIDDLE_PLATFORM_HOST')", $contents);
|
|
$this->assertStringContainsString("env('UPSTREAM_TUNNEL_JS_DATA_MIDDLE_PLATFORM_PORT')", $contents);
|
|
}
|
|
|
|
public function test_global_wildcard_scope_is_rejected(): void
|
|
{
|
|
$this->withExceptionHandling();
|
|
|
|
$this->loginAs()
|
|
->post(route('system-settings.databases.store'), $this->payload([
|
|
'allowed_scopes' => '*',
|
|
]))
|
|
->assertSessionHasErrors('allowed_scopes');
|
|
|
|
$this->assertDatabaseMissing('system_database_connections', ['name' => '测试云数据库']);
|
|
}
|
|
|
|
public function test_regular_admin_can_open_database_settings(): void
|
|
{
|
|
$this->user->roles()->sync([
|
|
Role::where('name', 'admin')->firstOrFail()->id,
|
|
]);
|
|
|
|
$this->loginAs()
|
|
->get(route('system-settings.databases.index'))
|
|
->assertOk()
|
|
->assertSeeText('财务系统配置数据库')
|
|
->assertSeeText('共享 · 已连接')
|
|
->assertSeeText('云数据库');
|
|
|
|
$this->get(route('system-settings.databases.system.edit'))
|
|
->assertOk()
|
|
->assertSeeText('连接系统数据库')
|
|
->assertSee('name="password"', false)
|
|
->assertDontSee('name="host"', false)
|
|
->assertDontSeeText('要求 TLS 加密连接')
|
|
->assertDontSeeText('CA 证书路径');
|
|
}
|
|
|
|
public function test_database_page_separates_system_and_cloud_database_configuration(): void
|
|
{
|
|
$connectionName = (string) config('database.default');
|
|
config(["database.connections.$connectionName.password" => 'system-database-test-secret']);
|
|
|
|
$this->loginAs()
|
|
->get(route('system-settings.databases.index'))
|
|
->assertOk()
|
|
->assertSee('data-system-database="fixed"', false)
|
|
->assertSeeTextInOrder([
|
|
'系统数据库',
|
|
'财务系统配置数据库',
|
|
'共享 · 已连接',
|
|
'云数据库',
|
|
'添加云数据库',
|
|
]);
|
|
|
|
$this->get(route('system-settings.databases.system.edit'))
|
|
->assertOk()
|
|
->assertSeeText('连接系统数据库')
|
|
->assertDontSee('name="host"', false)
|
|
->assertDontSee('name="tls_required"', false)
|
|
->assertDontSee('system-database-test-secret');
|
|
}
|
|
|
|
public function test_shared_data_dictionary_lists_objects_and_columns_without_upstream_credentials(): void
|
|
{
|
|
UpstreamSchemaColumn::query()->create([
|
|
'source_key' => 'operations_replica',
|
|
'schema_name' => 'rpt',
|
|
'object_name' => 'finance_summary',
|
|
'object_type' => 'view',
|
|
'column_name' => 'company_code',
|
|
'ordinal_position' => 1,
|
|
'data_type' => 'nvarchar',
|
|
'max_length' => 50,
|
|
'numeric_precision' => null,
|
|
'numeric_scale' => null,
|
|
'is_nullable' => false,
|
|
'synchronized_at' => now(),
|
|
]);
|
|
|
|
$this->loginAs()
|
|
->get(route('system-settings.databases.dictionary'))
|
|
->assertOk()
|
|
->assertSeeText('上游数据字典')
|
|
->assertSeeText('finance_summary')
|
|
->assertSeeText('1 个字段')
|
|
->assertDontSeeText('数据库密码');
|
|
|
|
$this->get(route('system-settings.databases.dictionary', [
|
|
'selected_source' => 'operations_replica',
|
|
'selected_schema' => 'rpt',
|
|
'selected_object' => 'finance_summary',
|
|
]))
|
|
->assertOk()
|
|
->assertSeeText('company_code')
|
|
->assertSeeText('nvarchar');
|
|
}
|
|
|
|
public function test_cloud_database_create_page_opens_inside_system_settings(): void
|
|
{
|
|
$this->loginAs()
|
|
->get(route('system-settings.databases.create'))
|
|
->assertOk()
|
|
->assertSeeText('添加数据库')
|
|
->assertDontSeeText('Not able to create a new user.');
|
|
}
|
|
|
|
public function test_production_stored_configuration_requires_explicit_private_flag(): void
|
|
{
|
|
$connection = $this->connection('production-secret');
|
|
$connection->update(['source_key' => 'jiansun']);
|
|
$originalEnvironment = $this->app->environment();
|
|
|
|
try {
|
|
$this->app['env'] = 'production';
|
|
config(['upstreams.stored_configuration.enabled' => false]);
|
|
|
|
$disabled = app(UpstreamStoredConfiguration::class);
|
|
$this->assertFalse($disabled->editable());
|
|
$this->assertNull($disabled->find('jiansun'));
|
|
|
|
config(['upstreams.stored_configuration.enabled' => true]);
|
|
$enabled = new UpstreamStoredConfiguration();
|
|
|
|
$this->assertTrue($enabled->editable());
|
|
$this->assertSame($connection->id, $enabled->find('jiansun')?->id);
|
|
} finally {
|
|
$this->app['env'] = $originalEnvironment;
|
|
}
|
|
}
|
|
|
|
public function test_bootstrap_page_is_not_presented_as_shared_database(): void
|
|
{
|
|
config([
|
|
'system_database.mode' => 'bootstrap',
|
|
'upstreams.stored_configuration.enabled' => false,
|
|
]);
|
|
|
|
$this->loginAs()
|
|
->get(route('system-settings.databases.index'))
|
|
->assertOk()
|
|
->assertSeeText('本地临时引导库')
|
|
->assertSeeText('临时 · 待切换')
|
|
->assertDontSeeText('添加云数据库');
|
|
}
|
|
|
|
public function test_first_switch_only_requires_system_database_password(): void
|
|
{
|
|
config(['system_database.mode' => 'bootstrap']);
|
|
$this->withExceptionHandling();
|
|
|
|
$this->loginAs()
|
|
->patch(route('system-settings.databases.system.update'), [
|
|
'password' => '',
|
|
])
|
|
->assertSessionHasErrors('password');
|
|
}
|
|
|
|
public function test_wrong_system_database_password_stays_on_the_form(): void
|
|
{
|
|
config([
|
|
'system_database.mode' => 'bootstrap',
|
|
'system_database.team_profile' => null,
|
|
]);
|
|
$this->withExceptionHandling();
|
|
|
|
$this->loginAs()
|
|
->from(route('system-settings.databases.system.edit'))
|
|
->patch(route('system-settings.databases.system.update'), [
|
|
'password' => 'definitely-not-the-team-password',
|
|
])
|
|
->assertRedirect(route('system-settings.databases.system.edit'))
|
|
->assertSessionHasErrors([
|
|
'password' => '系统数据库密码不正确,请重新输入。',
|
|
]);
|
|
}
|
|
|
|
public function test_successful_system_database_switch_invalidates_bootstrap_session(): void
|
|
{
|
|
$configuration = Mockery::mock(SystemDatabaseConfiguration::class);
|
|
$configuration->shouldReceive('probe')->once()->andReturn([
|
|
'cipher' => 'TLS_AES_256_GCM_SHA384',
|
|
'user_count' => 7,
|
|
'admin_count' => 2,
|
|
'source_count' => 2,
|
|
'prefix' => 'ak_',
|
|
]);
|
|
$configuration->shouldReceive('persist')->once();
|
|
$this->app->instance(SystemDatabaseConfiguration::class, $configuration);
|
|
$teamProfile = Mockery::mock(TeamSystemDatabaseProfile::class);
|
|
$teamProfile->shouldReceive('settings')->once()->with('new-shared-secret')->andReturn([
|
|
'driver' => 'mysql',
|
|
'host' => 'shared-db.test',
|
|
'port' => 3306,
|
|
'database' => 'shared_system',
|
|
'username' => 'shared_writer',
|
|
'password' => 'new-shared-secret',
|
|
'tls_required' => true,
|
|
'tls_verify_server_cert' => false,
|
|
'tls_ca' => '',
|
|
]);
|
|
$this->app->instance(TeamSystemDatabaseProfile::class, $teamProfile);
|
|
|
|
$this->loginAs()
|
|
->patch(route('system-settings.databases.system.update'), [
|
|
'password' => 'new-shared-secret',
|
|
])
|
|
->assertRedirect(route('login'));
|
|
|
|
$this->assertGuest();
|
|
}
|
|
|
|
public function test_unexpected_system_database_error_returns_to_form_instead_of_500(): void
|
|
{
|
|
$this->withExceptionHandling();
|
|
$configuration = Mockery::mock(SystemDatabaseConfiguration::class);
|
|
$configuration->shouldReceive('probe')->once()->andThrow(new \LogicException('internal detail'));
|
|
$this->app->instance(SystemDatabaseConfiguration::class, $configuration);
|
|
$teamProfile = Mockery::mock(TeamSystemDatabaseProfile::class);
|
|
$teamProfile->shouldReceive('settings')->once()->andReturn([
|
|
'driver' => 'mysql',
|
|
'host' => 'shared-db.test',
|
|
'port' => 3306,
|
|
'database' => 'shared_system',
|
|
'username' => 'shared_writer',
|
|
'password' => 'new-shared-secret',
|
|
'tls_required' => true,
|
|
'tls_verify_server_cert' => false,
|
|
'tls_ca' => '',
|
|
]);
|
|
$this->app->instance(TeamSystemDatabaseProfile::class, $teamProfile);
|
|
|
|
$this->loginAs()
|
|
->from(route('system-settings.databases.system.edit'))
|
|
->patch(route('system-settings.databases.system.update'), [
|
|
'password' => 'new-shared-secret',
|
|
])
|
|
->assertRedirect(route('system-settings.databases.system.edit'))
|
|
->assertSessionHasErrors('password');
|
|
}
|
|
|
|
public function test_postgres_can_be_saved_without_sql_server_settings(): void
|
|
{
|
|
$payload = $this->payload(['driver' => 'pgsql', 'port' => 25432, 'password' => 'pg-secret']);
|
|
unset($payload['odbc_driver'], $payload['tds_version']);
|
|
$this->loginAs()->post(route('system-settings.databases.store'), $payload)
|
|
->assertSessionHasNoErrors()->assertRedirect(route('system-settings.databases.index'));
|
|
$connection = DatabaseConnection::where('driver', 'pgsql')->firstOrFail();
|
|
$this->assertNull($connection->odbc_driver);
|
|
$this->assertNull($connection->source_key);
|
|
$this->assertSame('pg-secret', $connection->password);
|
|
$this->get(route('system-settings.databases.edit', $connection->id))
|
|
->assertOk()->assertSee('sslmode=require')->assertSee('PostgreSQL')->assertDontSee('pg-secret');
|
|
}
|
|
|
|
public function test_changing_wrong_driver_preserves_password(): void
|
|
{
|
|
$connection = $this->connection('keep-secret');
|
|
$this->loginAs()->patch(route('system-settings.databases.update', $connection->id), $this->payload([
|
|
'driver' => 'pgsql', 'port' => 25432, 'password' => '',
|
|
]))->assertSessionHasNoErrors();
|
|
$connection->refresh();
|
|
$this->assertSame('pgsql', $connection->driver);
|
|
$this->assertSame(25432, $connection->port);
|
|
$this->assertSame('keep-secret', $connection->password);
|
|
}
|
|
|
|
public function test_postgres_cannot_replace_existing_sql_server_business_source(): void
|
|
{
|
|
$this->withExceptionHandling();
|
|
$this->loginAs()->post(route('system-settings.databases.store'), $this->payload([
|
|
'driver' => 'pgsql', 'source_key' => 'operations_replica',
|
|
]))->assertSessionHasErrors('source_key');
|
|
$this->assertSame(0, DatabaseConnection::where('company_id', 0)->count());
|
|
}
|
|
|
|
public function test_host_must_not_include_port(): void
|
|
{
|
|
$this->withExceptionHandling();
|
|
$this->loginAs()->post(route('system-settings.databases.store'), $this->payload([
|
|
'host' => '127.0.0.1:25432',
|
|
]))->assertSessionHasErrors('host');
|
|
}
|
|
|
|
public function test_test_button_uses_saved_postgres_driver_and_password(): void
|
|
{
|
|
$connection = $this->connection('saved-pg-secret');
|
|
$connection->update(['driver' => 'pgsql', 'port' => 25432]);
|
|
$probe = Mockery::mock(UpstreamDatabaseProbe::class);
|
|
$probe->shouldReceive('probe')->once()->withArgs(fn ($driver, $config, $label) =>
|
|
$driver === 'pgsql' && $config['password'] === 'saved-pg-secret' && $config['port'] === 25432
|
|
);
|
|
$this->app->instance(UpstreamDatabaseProbe::class, $probe);
|
|
$this->loginAs()->post(route('system-settings.databases.test', $connection->id))
|
|
->assertSessionHasNoErrors()->assertRedirect(route('system-settings.databases.index'));
|
|
$this->assertFlashLevel('success');
|
|
$this->assertStringContainsString('PostgreSQL SSL 连接成功', session('flash_notification')->first()->message);
|
|
$this->get(route('system-settings.databases.index'))->assertSeeText('PostgreSQL')->assertDontSee('saved-pg-secret');
|
|
}
|
|
|
|
private function payload(array $overrides = []): array
|
|
{
|
|
return array_merge([
|
|
'name' => '测试云数据库',
|
|
'source_key' => '',
|
|
'host' => '127.0.0.1',
|
|
'port' => 1433,
|
|
'database_name' => 'test_finance',
|
|
'username' => 'readonly_user',
|
|
'password' => 'secret',
|
|
'odbc_driver' => 'FreeTDS',
|
|
'tds_version' => '7.4',
|
|
'connect_timeout' => 3,
|
|
'query_timeout' => 8,
|
|
'allowed_scopes' => 'supplier.options',
|
|
'enabled' => '1',
|
|
], $overrides);
|
|
}
|
|
|
|
public function test_mysql_connection_appears_in_dictionary_sources_without_a_binding(): void
|
|
{
|
|
$payload = $this->payload(['driver' => 'mysql', 'port' => 3306]);
|
|
unset($payload['source_key'], $payload['allowed_scopes'], $payload['odbc_driver'], $payload['tds_version']);
|
|
$this->loginAs()->post(route('system-settings.databases.store'), $payload)->assertSessionHasNoErrors();
|
|
$row = DatabaseConnection::where('driver', 'mysql')->firstOrFail();
|
|
$this->assertArrayHasKey($row->sourceIdentifier(), app(UpstreamSourceRegistry::class)->all());
|
|
$this->get(route('system-settings.databases.create'))->assertOk()
|
|
->assertSee('MySQL / MariaDB')->assertDontSee('name="source_key"', false)->assertDontSee('name="allowed_scopes"', false);
|
|
}
|
|
|
|
public function test_editing_simplified_form_preserves_legacy_binding_and_scopes(): void
|
|
{
|
|
$row = $this->connection('keep-secret');
|
|
$row->update(['source_key' => 'jiansun']);
|
|
$payload = $this->payload(['name' => $row->name, 'password' => '']);
|
|
unset($payload['source_key'], $payload['allowed_scopes']);
|
|
$this->loginAs()->patch(route('system-settings.databases.update', $row->id), $payload)->assertSessionHasNoErrors();
|
|
$row->refresh();
|
|
$this->assertSame('jiansun', $row->source_key);
|
|
$this->assertSame(['supplier.options'], $row->allowed_scopes);
|
|
$this->assertSame('keep-secret', $row->password);
|
|
}
|
|
|
|
private function connection(string $password): DatabaseConnection
|
|
{
|
|
return DatabaseConnection::create([
|
|
'company_id' => 0,
|
|
'source_key' => null,
|
|
'name' => '已有云数据库',
|
|
'driver' => 'sqlsrv',
|
|
'host' => '127.0.0.1',
|
|
'port' => 1433,
|
|
'database_name' => 'test_finance',
|
|
'username' => 'readonly_user',
|
|
'password' => $password,
|
|
'odbc_driver' => 'FreeTDS',
|
|
'tds_version' => '7.4',
|
|
'connect_timeout' => 3,
|
|
'query_timeout' => 8,
|
|
'allowed_scopes' => ['supplier.options'],
|
|
'charset' => 'UTF-8',
|
|
'enabled' => true,
|
|
]);
|
|
}
|
|
}
|