create(['role' => 'user']); $this->get('/moderation/traffic/online') ->assertRedirect(route('login')); $this->actingAs($user) ->get('/moderation/traffic/online') ->assertRedirect(route('index')); $this->actingAs($user) ->getJson('/moderation/traffic/online/data') ->assertForbidden(); } public function test_staff_can_open_online_page_and_json_endpoint(): void { $admin = User::factory()->create(['role' => 'admin']); $repository = Mockery::mock(OnlineVisitorRepository::class); $repository->shouldReceive('summary')->twice()->andReturn([ 'total' => 3, 'logged' => 1, 'guests' => 1, 'bots' => 1, 'search_bots' => 1, 'ai_bots' => 0, 'social_bots' => 0, 'seo_bots' => 0, 'suspicious_bots' => 0, ]); $repository->shouldReceive('all')->twice()->andReturn([ [ 'visitor_key' => 'user:1', 'type' => 'human_logged', 'user_name' => 'Gregor', 'ip_masked' => '188.230.xxx.xxx', 'browser' => 'Chrome', 'platform' => 'Windows', 'current_url' => '/wallpapers', 'route_name' => 'wallpapers.index', 'referer' => null, 'first_seen_at' => now()->subMinute()->toIso8601String(), 'last_seen_at' => now()->toIso8601String(), 'hits' => 2, ], ]); $repository->shouldReceive('activePages')->twice()->andReturn([ ['url' => '/wallpapers', 'visitors' => 3], ]); $repository->shouldReceive('track')->andReturnNull(); $this->app->instance(OnlineVisitorRepository::class, $repository); $this->actingAs($admin) ->get('/moderation/traffic/online') ->assertOk() ->assertSee('Online Visitors') ->assertSee('Live view of logged users, guests, crawlers, AI bots, and suspicious traffic.'); $this->actingAs($admin) ->getJson('/moderation/traffic/online/data') ->assertOk() ->assertJsonStructure([ 'summary' => ['total', 'logged', 'guests', 'bots', 'search_bots', 'ai_bots', 'social_bots', 'seo_bots', 'suspicious_bots'], 'visitors', 'active_pages', 'generated_at', ]) ->assertJsonPath('summary.total', 3) ->assertJsonPath('active_pages.0.url', '/wallpapers'); } public function test_redis_failure_does_not_break_public_request(): void { $repository = Mockery::mock(OnlineVisitorRepository::class); $repository->shouldReceive('track')->andThrow(new \RuntimeException('Redis unavailable')); $this->app->instance(OnlineVisitorRepository::class, $repository); $this->get('/robots.txt') ->assertOk(); } }