set('clusev.release_controls', true); config()->set('clusev.version', '0.9.58'); $this->actingAs(User::factory()->create(['must_change_password' => false])); $this->dir = storage_path('app/restart-signal'); @mkdir($this->dir, 0775, true); @unlink($this->dir.'/release-request.json'); } protected function tearDown(): void { @unlink($this->dir.'/release-request.json'); foreach (glob($this->dir.'/release-result-*.json') ?: [] as $f) { @unlink($f); } parent::tearDown(); } public function test_deploy_staging_for_an_allowed_target_writes_a_request_and_audits(): void { Livewire::test(Index::class)->call('deployStaging', '0.10.0'); $payload = json_decode((string) file_get_contents($this->dir.'/release-request.json'), true); $this->assertSame('stage', $payload['action']); $this->assertSame('0.10.0', $payload['target']); $this->assertTrue(AuditEvent::where('action', 'deploy.staging_release')->exists()); } public function test_deploy_staging_rejects_a_target_not_in_the_allowed_set(): void { Livewire::test(Index::class)->call('deployStaging', '5.5.5'); $this->assertFileDoesNotExist($this->dir.'/release-request.json'); } public function test_deploy_staging_is_throttled_per_user(): void { $key = 'release-stage:'.auth()->id(); for ($i = 0; $i < 3; $i++) { RateLimiter::hit($key, 600); } Livewire::test(Index::class)->call('deployStaging', '0.10.0'); $this->assertFileDoesNotExist($this->dir.'/release-request.json'); } public function test_poll_result_surfaces_a_success(): void { $c = Livewire::test(Index::class)->call('deployStaging', '0.10.0'); $id = $c->get('pendingId'); file_put_contents( $this->dir."/release-result-{$id}.json", json_encode(['id' => $id, 'ok' => true, 'tag' => 'v0.10.0-beta1', 'message' => 'ok']) ); $c->call('pollResult')->assertSet('pendingId', null)->assertSee('v0.10.0-beta1'); } public function test_poll_result_audits_a_host_failure_as_an_error(): void { $c = Livewire::test(Index::class)->call('deployStaging', '0.10.0'); $id = $c->get('pendingId'); file_put_contents( $this->dir."/release-result-{$id}.json", json_encode(['id' => $id, 'ok' => false, 'tag' => null, 'message' => 'dirty-tree']) ); $c->call('pollResult')->assertSet('pendingId', null); $this->assertTrue(AuditEvent::where('action', 'deploy.staging_release_failed')->exists()); } public function test_mount_aborts_when_the_flag_is_off(): void { config()->set('clusev.release_controls', false); Livewire::test(Index::class)->assertStatus(404); } public function test_render_includes_the_live_pipeline_for_a_beta(): void { config()->set('clusev.version', '0.9.61-beta1'); config()->set('clusev.staging_slug', ''); // degrade: no GitHub calls config()->set('clusev.github_token', ''); Livewire::test(Index::class) ->assertViewHas('pipeline', fn ($p) => is_array($p) && $p['tag'] === 'v0.9.61-beta1'); } public function test_render_pipeline_is_null_for_a_stable_version(): void { config()->set('clusev.version', '0.9.60'); Livewire::test(Index::class)->assertViewHas('pipeline', null); } public function test_save_test_server_persists_a_valid_url(): void { Livewire::test(Index::class)->call('saveTestServer', 'http://staging.local'); $this->assertSame('http://staging.local', Setting::get('pipeline_test_server')); } public function test_save_test_server_rejects_a_bad_url(): void { Livewire::test(Index::class)->call('saveTestServer', 'not-a-url'); $this->assertNull(Setting::get('pipeline_test_server')); } public function test_save_test_server_empty_clears_it(): void { Setting::put('pipeline_test_server', 'http://staging.local'); Livewire::test(Index::class)->call('saveTestServer', ''); $this->assertSame('', (string) Setting::get('pipeline_test_server', '')); } }