Files
headroom/backend/tests/Feature/Allocation/AllocationTest.php
Santhosh Janardhanan 9b0f42fdf5 feat(backend): enhance allocation and project management
Update controllers and services for allocation fidelity:
- AllocationController: variance data in responses, bulk operations
- ProjectController: include plan data in responses
- ProjectMonthPlanController: planning grid API
- AllocationMatrixService: support untracked allocations
- ProjectResource/TeamMemberResource: include reconciliation data

Improved test coverage for allocation flows.
2026-03-08 18:22:53 -04:00

190 lines
6.1 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\Allocation;
use App\Models\Project;
use App\Models\Role;
use App\Models\TeamMember;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class AllocationTest extends TestCase
{
use RefreshDatabase;
protected function loginAsManager()
{
$user = User::factory()->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);
}
}