create([ 'email' => 'manager@test.com', 'password' => bcrypt('password123'), 'role' => 'manager', 'active' => true, ]); $response = $this->postJson('/api/auth/login', [ 'email' => 'manager@test.com', 'password' => 'password123', ]); return $response->json('access_token'); } public function test_bulk_update_creates_new_plan_records(): void { $token = $this->loginAsManager(); $project = Project::factory()->create(); $response = $this->withHeader('Authorization', "Bearer {$token}") ->putJson('/api/project-month-plans/bulk', [ 'year' => 2026, 'items' => [ [ 'project_id' => $project->id, 'month' => '2026-01', 'planned_hours' => 100, ], ], ]); $response->assertStatus(200); } public function test_validation_rejects_invalid_data(): void { $token = $this->loginAsManager(); $response = $this->withHeader('Authorization', "Bearer {$token}") ->putJson('/api/project-month-plans/bulk', [ 'year' => 2026, 'items' => [ [ 'project_id' => 'invalid-uuid', 'month' => '2026-01', 'planned_hours' => 100, ], ], ]); $response->assertStatus(422); } public function test_index_returns_existing_plan_for_month(): void { $token = $this->loginAsManager(); $project = Project::factory()->create(); ProjectMonthPlan::create([ 'project_id' => $project->id, 'month' => '2026-02-01', 'planned_hours' => 50, ]); $projectData = $this->fetchProjectPlan($token, $project); $this->assertNotNull($projectData['months']['2026-02-01']); } public function test_index_returns_null_for_missing_month(): void { $token = $this->loginAsManager(); $project = Project::factory()->create(); $projectData = $this->fetchProjectPlan($token, $project); $this->assertNull($projectData['months']['2026-03-01']); } public function test_bulk_update_roundtrip_populates_month(): void { $token = $this->loginAsManager(); $project = Project::factory()->create(); $this->withHeader('Authorization', "Bearer {$token}") ->putJson('/api/project-month-plans/bulk', [ 'year' => 2026, 'items' => [ [ 'project_id' => $project->id, 'month' => '2026-02', 'planned_hours' => 72, ], ], ]) ->assertStatus(200); $projectData = $this->fetchProjectPlan($token, $project); $this->assertNotNull($projectData['months']['2026-02-01']); $this->assertEquals(72, $projectData['plan_sum']); } private function fetchProjectPlan(string $token, Project $project): array { $response = $this->withHeader('Authorization', "Bearer {$token}") ->getJson('/api/project-month-plans?year=2026'); $response->assertStatus(200); $projectData = collect($response->json('data'))->firstWhere('project_id', $project->id); $this->assertNotNull($projectData); return $projectData; } }