actingAs(User::factory()->create(['must_change_password' => false])); } public function test_help_route_loads_for_an_authenticated_admin(): void { $this->actAsAdmin(); $this->get('/help')->assertOk(); } public function test_default_topic_is_overview(): void { $this->actAsAdmin(); Livewire::test(Index::class) ->assertSet('topic', 'overview') ->assertSee(__('help.topic_overview')); } public function test_unknown_topic_falls_back_to_overview(): void { $this->actAsAdmin(); Livewire::test(Index::class, ['topic' => 'bogus'])->assertSet('topic', 'overview'); } public function test_domain_tls_topic_has_the_reverse_proxy_guide(): void { $this->actAsAdmin(); Livewire::test(Index::class) ->set('topic', 'domain-tls') ->assertSee('X-Forwarded-Proto') ->assertSee('TRUSTED_PROXY_CIDR') ->assertDontSee('Zoraxy'); // generic — no product names } public function test_security_topic_explains_the_2fa_access_paths(): void { $this->actAsAdmin(); Livewire::test(Index::class) ->set('topic', 'security') ->assertSee('Backup-Code') ->assertSee('TOTP'); } public function test_topic_property_is_url_bound(): void { $attrs = (new \ReflectionProperty(Index::class, 'topic'))->getAttributes(Url::class); $this->assertNotEmpty($attrs, 'Help topic must be #[Url]-bound for deep links / reload'); } public function test_recovery_shows_the_short_host_command_not_the_long_compose_one(): void { $this->actAsAdmin(); Livewire::test(Index::class) ->set('topic', 'recovery') ->assertSee('clusev reset-admin') ->assertDontSee('docker-compose.prod.yml'); } public function test_commands_topic_lists_the_cli(): void { $this->actAsAdmin(); Livewire::test(Index::class) ->set('topic', 'commands') ->assertSee('clusev reset-admin') ->assertSee('sudo clusev update') ->assertDontSee('docker-compose.prod.yml'); } #[DataProvider('topicProvider')] public function test_each_topic_renders_its_own_partial(string $topic, string $marker): void { $this->actAsAdmin(); Livewire::test(Index::class)->set('topic', $topic)->assertSee($marker); } public static function topicProvider(): array { return [ ['updates', 'clusev update'], ['servers', 'SSH'], ['sessions', 'Sitzung'], ['email', 'SMTP'], ['audit', 'Audit'], ['recovery', 'clusev reset-admin'], ['commands', 'clusev ps'], ]; } }