getJson('/api/v1/dashboard/stats') ->assertStatus(200) ->assertJsonStructure([ 'success', 'data' => [ 'article_stats' => [ 'articles_fetched', 'articles_published', 'published_percentage', ], 'system_stats' => [ 'total_feeds', 'active_feeds', 'total_platform_channels', 'active_platform_channels', 'total_routes', 'active_routes', ], 'range' => ['from', 'to'], ], 'message', ]); } public function test_stats_no_longer_exposes_named_periods(): void { $this ->getJson('/api/v1/dashboard/stats') ->assertStatus(200) ->assertJsonMissingPath('data.available_periods') ->assertJsonMissingPath('data.current_period'); } public function test_stats_honours_an_explicit_from_and_to_range(): void { Article::factory()->count(2)->create(['created_at' => Carbon::parse('2026-07-10 12:00:00')]); Article::factory()->create(['created_at' => Carbon::parse('2026-08-05 12:00:00')]); $this ->getJson('/api/v1/dashboard/stats?from=2026-07-01&to=2026-07-31') ->assertStatus(200) ->assertJson([ 'data' => [ 'article_stats' => ['articles_fetched' => 2], 'range' => ['from' => '2026-07-01', 'to' => '2026-07-31'], ], ]); } public function test_stats_defaults_to_the_current_day_without_params(): void { Carbon::setTestNow('2026-07-15 13:45:00'); Article::factory()->create(['created_at' => Carbon::parse('2026-07-15 09:00:00')]); Article::factory()->create(['created_at' => Carbon::parse('2026-07-14 09:00:00')]); $this ->getJson('/api/v1/dashboard/stats') ->assertStatus(200) ->assertJson([ 'data' => [ 'article_stats' => ['articles_fetched' => 1], 'range' => ['from' => '2026-07-15', 'to' => '2026-07-15'], ], ]); } public function test_stats_rejects_an_inverted_range(): void { $this ->getJson('/api/v1/dashboard/stats?from=2026-07-31&to=2026-07-01') ->assertStatus(422); } public function test_stats_rejects_a_malformed_date(): void { $this ->getJson('/api/v1/dashboard/stats?from=not-a-date&to=2026-07-01') ->assertStatus(422); } public function test_stats_with_sample_data(): void { $feed = Feed::factory()->create(['is_active' => true]); $channel = PlatformChannel::factory()->create(['is_active' => true]); Route::factory()->create(['is_active' => true]); $articles = Article::factory()->count(3)->create([ 'feed_id' => $feed->id, 'created_at' => Carbon::parse('2026-07-10 12:00:00'), ]); ArticlePublication::factory()->create([ 'article_id' => $articles->first()->id, 'platform_channel_id' => $channel->id, 'published_at' => Carbon::parse('2026-07-10 13:00:00'), ]); $response = $this->getJson('/api/v1/dashboard/stats?from=2026-07-01&to=2026-07-31'); $response->assertStatus(200) ->assertJson([ 'success' => true, 'data' => [ 'article_stats' => [ 'articles_fetched' => 3, 'articles_published' => 1, ], 'system_stats' => [ 'total_feeds' => Feed::count(), 'total_platform_channels' => PlatformChannel::count(), 'total_routes' => Route::count(), ], ], ]); } public function test_stats_returns_empty_data_with_no_records(): void { $this ->getJson('/api/v1/dashboard/stats') ->assertStatus(200) ->assertJson([ 'success' => true, 'data' => [ 'article_stats' => [ 'articles_fetched' => 0, 'articles_published' => 0, 'published_percentage' => 0.0, ], 'system_stats' => [ 'total_feeds' => 0, 'active_feeds' => 0, 'total_platform_accounts' => 0, 'active_platform_accounts' => 0, 'total_platform_channels' => 0, 'active_platform_channels' => 0, 'total_routes' => 0, 'active_routes' => 0, ], ], ]); } }