delete(); // isAdmin() 은 admin 타입 권한 보유 여부로 판정되므로 임의 권한을 부여한다. // 대시보드 라우트는 admin 미들웨어만 사용 (별도 permission 가드 없음). $this->adminUser = $this->createAdminUser(['sirsoft-board.boards.read']); $this->normalUser = $this->createUser(); Board::create([ 'name' => ['ko' => 'API 테스트', 'en' => 'API Test'], 'slug' => 'api-test-'.uniqid(), 'type' => 'basic', ]); } // ========== overview ========== #[Test] public function test_overview_requires_authentication(): void { $this->getJson(self::BASE.'/overview')->assertStatus(401); } #[Test] public function test_overview_rejects_non_admin_user(): void { $this->actingAs($this->normalUser)->getJson(self::BASE.'/overview')->assertStatus(403); } #[Test] public function test_overview_returns_today_counts_for_admin(): void { BoardStat::create([ 'date' => CarbonImmutable::today()->toDateString(), 'post_count' => 5, 'comment_count' => 12, ]); $this->actingAs($this->adminUser)->getJson(self::BASE.'/overview') ->assertStatus(200) ->assertJsonStructure(['success', 'data' => ['today_posts', 'today_comments']]) ->assertJsonPath('data.today_posts', 5) ->assertJsonPath('data.today_comments', 12); } // ========== post-graph ========== #[Test] public function test_post_graph_requires_authentication(): void { $this->getJson(self::BASE.'/post-graph')->assertStatus(401); } #[Test] public function test_post_graph_returns_schema_for_admin(): void { $today = CarbonImmutable::today(); for ($i = 0; $i < 7; $i++) { BoardStat::create([ 'date' => $today->subDays($i)->toDateString(), 'post_count' => 1, 'comment_count' => 2, ]); } $this->actingAs($this->adminUser)->getJson(self::BASE.'/post-graph') ->assertStatus(200) ->assertJsonStructure([ 'success', 'data' => [ 'days' => [['date', 'post_count', 'comment_count']], 'total_posts', 'total_comments', 'posts_change', 'comments_change', 'updated_at', ], ]) ->assertJsonCount(7, 'data.days') ->assertJsonPath('data.total_posts', 7) ->assertJsonPath('data.total_comments', 14); } // ========== recent-posts ========== #[Test] public function test_recent_posts_requires_authentication(): void { $this->getJson(self::BASE.'/recent-posts')->assertStatus(401); } #[Test] public function test_recent_posts_returns_collection_for_admin(): void { $this->actingAs($this->adminUser)->getJson(self::BASE.'/recent-posts') ->assertStatus(200) ->assertJsonStructure(['success', 'data']); } #[Test] public function test_recent_posts_rejects_limit_over_max(): void { $this->actingAs($this->adminUser) ->getJson(self::BASE.'/recent-posts?limit=999') ->assertStatus(422); } #[Test] public function test_recent_posts_rejects_non_integer_limit(): void { $this->actingAs($this->adminUser) ->getJson(self::BASE.'/recent-posts?limit=abc') ->assertStatus(422); } // ========== pending-reports ========== #[Test] public function test_pending_reports_requires_authentication(): void { $this->getJson(self::BASE.'/pending-reports')->assertStatus(401); } #[Test] public function test_pending_reports_returns_items_and_total_for_admin(): void { $this->actingAs($this->adminUser)->getJson(self::BASE.'/pending-reports') ->assertStatus(200) ->assertJsonStructure(['success', 'data' => ['items', 'total']]); } }