'mysql','host'=>'new-team.test','port'=>8444,'database'=>'shared_system', 'mirror_database'=>'caiwu_business_mirror','username'=>'team_user','password'=>'fixture-secret', 'prefix'=>'ak_','tls_required'=>true,'tls_verify_server_cert'=>false,'tls_ca'=>'']; } private function configureProfile(): void { $this->app->instance(TeamSystemDatabaseProfile::class, new TeamSystemDatabaseProfile( TeamSystemDatabaseProfile::seal($this->settings(), 'fixture-secret') )); } public function test_existing_password_repairs_old_endpoint_without_connecting_to_old_database(): void { $this->configureProfile(); $configuration=Mockery::mock(SystemDatabaseConfiguration::class); $configuration->shouldReceive('current')->once()->andReturn(array_replace($this->settings(), ['host'=>'retired.test','mode'=>'shared'])); $configuration->shouldReceive('probe')->once()->with(Mockery::on(fn($s)=>$s['host']==='new-team.test'))->andReturn(['prefix'=>'ak_']); $configuration->shouldReceive('persist')->once()->with($this->settings())->andReturn('private-backup'); $this->app->instance(SystemDatabaseConfiguration::class,$configuration); $this->artisan('system-database:connect-team')->expectsOutputToContain('已备份并更新本机')->assertSuccessful(); } public function test_first_bootstrap_without_team_password_does_not_write_or_probe(): void { $this->configureProfile(); $configuration=Mockery::mock(SystemDatabaseConfiguration::class); $configuration->shouldReceive('current')->once()->andReturn(['password'=>'bootstrap-only','mode'=>'bootstrap']); $configuration->shouldNotReceive('probe'); $configuration->shouldNotReceive('persist'); $this->app->instance(SystemDatabaseConfiguration::class,$configuration); $this->artisan('system-database:connect-team')->expectsOutputToContain('输入一次密码')->assertExitCode(13); } public function test_current_team_connection_is_not_rewritten_on_every_initialization(): void { $this->configureProfile(); $s=$this->settings(); config(['upstreams.stored_configuration.enabled'=>true,'upstreams.local_configuration.enabled'=>false, 'database.connections.business_mirror'=>['read'=>['host'=>[$s['host']]],'write'=>['host'=>[$s['host']]],'port'=>$s['port'], 'database'=>$s['mirror_database'],'username'=>$s['username'],'password'=>$s['password'],'unix_socket'=>'']]); $configuration=Mockery::mock(SystemDatabaseConfiguration::class); $configuration->shouldReceive('current')->once()->andReturn($s+['mode'=>'shared']); $configuration->shouldReceive('probe')->once()->andReturn(['prefix'=>'ak_']); $configuration->shouldNotReceive('persist'); $this->app->instance(SystemDatabaseConfiguration::class,$configuration); $this->artisan('system-database:connect-team')->expectsOutputToContain('已连接最新团队')->assertSuccessful(); } public function test_failed_new_endpoint_probe_keeps_original_configuration(): void { $this->configureProfile(); $configuration=Mockery::mock(SystemDatabaseConfiguration::class); $configuration->shouldReceive('current')->once()->andReturn($this->settings()+['mode'=>'shared']); $configuration->shouldReceive('probe')->once()->andThrow(new RuntimeException('private connection details')); $configuration->shouldNotReceive('persist'); $this->app->instance(SystemDatabaseConfiguration::class,$configuration); $this->artisan('system-database:connect-team')->expectsOutputToContain('原配置已保留')->doesntExpectOutputToContain('private connection details')->assertFailed(); } public function test_production_never_rewrites_its_internal_endpoint(): void { $this->app->detectEnvironment(fn()=>'production'); $configuration=Mockery::mock(SystemDatabaseConfiguration::class); $configuration->shouldNotReceive('current'); $this->app->instance(SystemDatabaseConfiguration::class,$configuration); $this->artisan('system-database:connect-team')->expectsOutputToContain('不修改生产服务器连接')->assertFailed(); } public function test_persistence_failure_does_not_claim_connection_success(): void { $this->configureProfile(); $configuration=Mockery::mock(SystemDatabaseConfiguration::class); $configuration->shouldReceive('current')->once()->andReturn(array_replace($this->settings(), ['host'=>'retired.test','mode'=>'shared'])); $configuration->shouldReceive('probe')->once()->andReturn(['prefix'=>'ak_']); $configuration->shouldReceive('persist')->once()->andThrow(new RuntimeException('private details')); $this->app->instance(SystemDatabaseConfiguration::class,$configuration); $this->artisan('system-database:connect-team')->expectsOutputToContain('请核对私有配置备份')->doesntExpectOutputToContain('已备份并更新')->assertFailed(); } }