docs(openspec): add reporting API contract documentation
Add comprehensive API documentation for the reporting endpoint: - Request/response structure - View type inference (did/is/will) - Blank vs explicit zero semantics - Status values and error responses Related to enhanced-allocation change.
This commit is contained in:
@@ -257,4 +257,161 @@ class AllocationTest extends TestCase
|
||||
|
||||
$response->assertStatus(404);
|
||||
}
|
||||
|
||||
// ===== Allocation Indicator Tests =====
|
||||
// 1.1 API test: GET /api/allocations returns allocation_indicator per item
|
||||
public function test_get_allocations_returns_allocation_indicator()
|
||||
{
|
||||
$token = $this->loginAsManager();
|
||||
$role = Role::factory()->create();
|
||||
$teamMember = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]);
|
||||
$project = Project::factory()->create(['approved_estimate' => 100]);
|
||||
|
||||
Allocation::factory()->create([
|
||||
'project_id' => $project->id,
|
||||
'team_member_id' => $teamMember->id,
|
||||
'month' => '2026-02-01',
|
||||
'allocated_hours' => 100,
|
||||
]);
|
||||
|
||||
$response = $this->withHeader('Authorization', "Bearer {$token}")
|
||||
->getJson('/api/allocations?month=2026-02');
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonStructure([
|
||||
'data' => [
|
||||
'*' => ['allocation_indicator'],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
// 1.2 API test: allocation_indicator is green when >= 100%
|
||||
public function test_allocation_indicator_is_green_when_full()
|
||||
{
|
||||
$token = $this->loginAsManager();
|
||||
$role = Role::factory()->create();
|
||||
$teamMember = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]);
|
||||
$project = Project::factory()->create(['approved_estimate' => 100]);
|
||||
|
||||
Allocation::factory()->create([
|
||||
'project_id' => $project->id,
|
||||
'team_member_id' => $teamMember->id,
|
||||
'month' => '2026-02-01',
|
||||
'allocated_hours' => 100,
|
||||
]);
|
||||
|
||||
$response = $this->withHeader('Authorization', "Bearer {$token}")
|
||||
->getJson('/api/allocations?month=2026-02');
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonPath('data.0.allocation_indicator', 'green');
|
||||
}
|
||||
|
||||
// 1.3 API test: allocation_indicator is yellow when < 100%
|
||||
public function test_allocation_indicator_is_yellow_when_under()
|
||||
{
|
||||
$token = $this->loginAsManager();
|
||||
$role = Role::factory()->create();
|
||||
$teamMember = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]);
|
||||
$project = Project::factory()->create(['approved_estimate' => 100]);
|
||||
|
||||
Allocation::factory()->create([
|
||||
'project_id' => $project->id,
|
||||
'team_member_id' => $teamMember->id,
|
||||
'month' => '2026-02-01',
|
||||
'allocated_hours' => 80,
|
||||
]);
|
||||
|
||||
$response = $this->withHeader('Authorization', "Bearer {$token}")
|
||||
->getJson('/api/allocations?month=2026-02');
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonPath('data.0.allocation_indicator', 'yellow');
|
||||
}
|
||||
|
||||
// 1.4 API test: allocation_indicator is red when > 100%
|
||||
public function test_allocation_indicator_is_red_when_over()
|
||||
{
|
||||
$token = $this->loginAsManager();
|
||||
$role = Role::factory()->create();
|
||||
$teamMember = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]);
|
||||
$project = Project::factory()->create(['approved_estimate' => 100]);
|
||||
|
||||
Allocation::factory()->create([
|
||||
'project_id' => $project->id,
|
||||
'team_member_id' => $teamMember->id,
|
||||
'month' => '2026-02-01',
|
||||
'allocated_hours' => 120,
|
||||
]);
|
||||
|
||||
$response = $this->withHeader('Authorization', "Bearer {$token}")
|
||||
->getJson('/api/allocations?month=2026-02');
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonPath('data.0.allocation_indicator', 'red');
|
||||
}
|
||||
|
||||
// 1.5 API test: allocation_indicator is gray when no approved_estimate
|
||||
public function test_allocation_indicator_is_gray_when_no_estimate()
|
||||
{
|
||||
$token = $this->loginAsManager();
|
||||
$role = Role::factory()->create();
|
||||
$teamMember = TeamMember::factory()->create(['role_id' => $role->id, 'active' => true]);
|
||||
$project = Project::factory()->create(['approved_estimate' => null]);
|
||||
|
||||
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);
|
||||
$response->assertJsonPath('data.0.allocation_indicator', 'gray');
|
||||
}
|
||||
|
||||
// ===== Untracked Allocation Tests =====
|
||||
// 2.1 API test: POST /api/allocations accepts null team_member_id
|
||||
public function test_can_create_allocation_with_null_team_member()
|
||||
{
|
||||
$token = $this->loginAsManager();
|
||||
$project = Project::factory()->create();
|
||||
|
||||
$response = $this->withHeader('Authorization', "Bearer {$token}")
|
||||
->postJson('/api/allocations', [
|
||||
'project_id' => $project->id,
|
||||
'team_member_id' => null,
|
||||
'month' => '2026-02',
|
||||
'allocated_hours' => 40,
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
$response->assertJsonPath('data.team_member_id', null);
|
||||
$response->assertJsonPath('data.team_member', null);
|
||||
}
|
||||
|
||||
// 2.2 API test: GET /api/allocations returns null team_member
|
||||
public function test_get_allocations_returns_null_team_member()
|
||||
{
|
||||
$token = $this->loginAsManager();
|
||||
$project = Project::factory()->create();
|
||||
|
||||
// Create untracked allocation
|
||||
Allocation::factory()->create([
|
||||
'project_id' => $project->id,
|
||||
'team_member_id' => null,
|
||||
'month' => '2026-02-01',
|
||||
'allocated_hours' => 40,
|
||||
]);
|
||||
|
||||
$response = $this->withHeader('Authorization', "Bearer {$token}")
|
||||
->getJson('/api/allocations?month=2026-02');
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonPath('data.0.team_member_id', null);
|
||||
$response->assertJsonPath('data.0.team_member', null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,4 +47,13 @@ class AllocationPolicyTest extends TestCase
|
||||
|
||||
$this->assertFalse($this->policy->create($developer));
|
||||
}
|
||||
|
||||
// 2.3 Unit test: AllocationPolicy allows untracked allocation
|
||||
public function test_manager_can_create_untracked_allocations()
|
||||
{
|
||||
$manager = User::factory()->create(['role' => 'manager']);
|
||||
|
||||
// Policy should allow creating allocations (untracked is just null team_member_id)
|
||||
$this->assertTrue($this->policy->create($manager));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user