create(['name' => 'Busy Feed']); $quiet = Feed::factory()->create(['name' => 'Quiet Feed']); Article::factory()->count(3)->create([ 'feed_id' => $busy->id, 'created_at' => Carbon::parse('2026-07-10 12:00:00'), ]); Article::factory()->create([ 'feed_id' => $quiet->id, 'created_at' => Carbon::parse('2026-07-11 12:00:00'), ]); $rows = (new ArticlesPerFeed)->for($this->range())->rows; $this->assertSame(['Busy Feed' => 3, 'Quiet Feed' => 1], $this->pluck($rows)); } public function test_it_reports_zero_for_a_feed_with_no_articles_in_the_range(): void { Feed::factory()->create(['name' => 'Silent Feed']); $rows = (new ArticlesPerFeed)->for($this->range())->rows; $this->assertSame(['Silent Feed' => 0], $this->pluck($rows)); } public function test_it_excludes_articles_outside_the_range(): void { $feed = Feed::factory()->create(['name' => 'Feed']); Article::factory()->create([ 'feed_id' => $feed->id, 'created_at' => Carbon::parse('2026-06-30 23:59:59'), ]); Article::factory()->create([ 'feed_id' => $feed->id, 'created_at' => Carbon::parse('2026-08-01 00:00:00'), ]); $rows = (new ArticlesPerFeed)->for($this->range())->rows; $this->assertSame(['Feed' => 0], $this->pluck($rows)); } public function test_it_orders_feeds_by_count_descending(): void { $low = Feed::factory()->create(['name' => 'Low']); $high = Feed::factory()->create(['name' => 'High']); Article::factory()->create([ 'feed_id' => $low->id, 'created_at' => Carbon::parse('2026-07-10 12:00:00'), ]); Article::factory()->count(5)->create([ 'feed_id' => $high->id, 'created_at' => Carbon::parse('2026-07-10 12:00:00'), ]); $rows = (new ArticlesPerFeed)->for($this->range())->rows; $this->assertSame(['High', 'Low'], array_map(fn ($row) => $row->label, $rows)); } public function test_it_breaks_ties_alphabetically(): void { foreach (['Zulu', 'Alpha', 'Mike'] as $name) { $feed = Feed::factory()->create(['name' => $name]); Article::factory()->create([ 'feed_id' => $feed->id, 'created_at' => Carbon::parse('2026-07-10 12:00:00'), ]); } $rows = (new ArticlesPerFeed)->for($this->range())->rows; $this->assertSame(['Alpha', 'Mike', 'Zulu'], array_map(fn ($row) => $row->label, $rows)); } public function test_it_reports_the_range_total(): void { $feed = Feed::factory()->create(); Article::factory()->count(4)->create([ 'feed_id' => $feed->id, 'created_at' => Carbon::parse('2026-07-10 12:00:00'), ]); $this->assertSame(4, (new ArticlesPerFeed)->for($this->range())->total()); } public function test_it_reports_a_zero_total_with_no_feeds(): void { $result = (new ArticlesPerFeed)->for($this->range()); $this->assertSame(0, $result->total()); $this->assertSame([], $result->rows); } public function test_it_exposes_a_stable_key_and_label(): void { $stat = new ArticlesPerFeed; $this->assertSame('articles-per-feed', $stat->key()); $this->assertSame('Articles per Feed', $stat->label()); } /** * @param array $rows * @return array */ private function pluck(array $rows): array { $out = []; foreach ($rows as $row) { $out[$row->label] = $row->count; } return $out; } }