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_post_allocations_creates_allocation(): void { $token = $this->loginAsManager(); $project = Project::factory()->create(); $role = Role::factory()->create(); $teamMember = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]); $response = $this->withHeader('Authorization', "Bearer {$token}") ->postJson('/api/allocations', [ 'project_id' => $project->id, 'team_member_id' => $teamMember->id, 'month' => '2026-02', 'allocated_hours' => 40, ]); $response->assertStatus(201); $this->assertDatabaseHas('allocations', [ 'project_id' => $project->id, 'allocated_hours' => 40, ]); } public function test_validate_hours_must_be_greater_than_zero(): void { $token = $this->loginAsManager(); $project = Project::factory()->create(); $role = Role::factory()->create(); $teamMember = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]); $response = $this->withHeader('Authorization', "Bearer {$token}") ->postJson('/api/allocations', [ 'project_id' => $project->id, 'team_member_id' => $teamMember->id, 'month' => '2026-02', 'allocated_hours' => -10, ]); $response->assertStatus(422); } public function test_get_allocations_returns_matrix(): void { $token = $this->loginAsManager(); $project = Project::factory()->create(); $role = Role::factory()->create(); $teamMember = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]); Allocation::factory()->create([ 'project_id' => $project->id, 'team_member_id' => $teamMember->id, 'month' => '2026-02-01', 'allocated_hours' => 40, ]); $response = $this->withHeader('Authorization', "Bearer {$token}") ->getJson('/api/allocations?month=2026-02'); $response->assertStatus(200); } public function test_put_allocations_updates(): void { $token = $this->loginAsManager(); $allocation = Allocation::factory()->create(); $response = $this->withHeader('Authorization', "Bearer {$token}") ->putJson("/api/allocations/{$allocation->id}", [ 'allocated_hours' => 50, ]); $response->assertStatus(200); $this->assertDatabaseHas('allocations', [ 'id' => $allocation->id, 'allocated_hours' => 50, ]); } public function test_delete_allocation_removes(): void { $token = $this->loginAsManager(); $allocation = Allocation::factory()->create(); $response = $this->withHeader('Authorization', "Bearer {$token}") ->deleteJson("/api/allocations/{$allocation->id}"); $response->assertStatus(200); $this->assertDatabaseMissing('allocations', [ 'id' => $allocation->id, ]); } public function test_post_allocations_bulk_creates_multiple(): void { $token = $this->loginAsManager(); $project = Project::factory()->create(); $role = Role::factory()->create(); $teamMember = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]); $response = $this->withHeader('Authorization', "Bearer {$token}") ->postJson('/api/allocations/bulk', [ 'allocations' => [ [ 'project_id' => $project->id, 'team_member_id' => $teamMember->id, 'month' => '2026-02', 'allocated_hours' => 40, ], ], ]); $response->assertStatus(201); $this->assertDatabaseCount('allocations', 1); } public function test_allocate_zero_hours_is_allowed(): void { $token = $this->loginAsManager(); $project = Project::factory()->create(); $role = Role::factory()->create(); $teamMember = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]); $response = $this->withHeader('Authorization', "Bearer {$token}") ->postJson('/api/allocations', [ 'project_id' => $project->id, 'team_member_id' => $teamMember->id, 'month' => '2026-02', 'allocated_hours' => 0, ]); $response->assertStatus(201); } public function test_cannot_update_nonexistent_allocation(): void { $token = $this->loginAsManager(); $fakeId = '550e8400-e29b-41d4-a716-446655440000'; $response = $this->withHeader('Authorization', "Bearer {$token}") ->putJson("/api/allocations/{$fakeId}", [ 'allocated_hours' => 50, ]); $response->assertStatus(404); } public function test_cannot_delete_nonexistent_allocation(): void { $token = $this->loginAsManager(); $fakeId = '550e8400-e29b-41d4-a716-446655440000'; $response = $this->withHeader('Authorization', "Bearer {$token}") ->deleteJson("/api/allocations/{$fakeId}"); $response->assertStatus(404); } }