Files

516 lines
15 KiB
PHP
Raw Permalink Normal View History

<?php
use App\Models\Idea;
use App\Models\Project;
use App\Models\Task;
use App\Models\TaskComment;
// ==================== PROJECTS API TESTS ====================
// Projects - Index
test('it lists all projects', function () {
Project::factory()->count(3)->create();
$response = $this->getJson('/api/projects');
$response->assertOk()->assertJsonCount(3, 'data');
});
test('it filters projects by status', function () {
Project::factory()->create(['status' => 'active']);
Project::factory()->create(['status' => 'completed']);
Project::factory()->create(['status' => 'archived']);
$response = $this->getJson('/api/projects?status=active');
$response->assertOk()
->assertJsonCount(1, 'data')
->assertJsonPath('data.0.status', 'active');
});
test('it filters projects by name', function () {
Project::factory()->create(['name' => 'Alpha Project']);
Project::factory()->create(['name' => 'Beta Project']);
Project::factory()->create(['name' => 'Gamma Project']);
$response = $this->getJson('/api/projects?name=Alpha');
$response->assertOk()
->assertJsonCount(1, 'data')
->assertJsonPath('data.0.name', 'Alpha Project');
});
test('it includes tasks when requested', function () {
$project = Project::factory()->create();
Task::factory()->count(2)->create(['project_id' => $project->id]);
$response = $this->getJson('/api/projects?with_tasks=1');
$response->assertOk()
->assertJsonPath('data.0.task_count', 2);
});
test('it shows a single project', function () {
$project = Project::factory()->create(['name' => 'Test Project']);
$response = $this->getJson("/api/projects/{$project->id}");
$response->assertOk()->assertJsonPath('data.name', 'Test Project');
});
test('it returns 404 for a missing project', function () {
$this->getJson('/api/projects/999')->assertNotFound();
});
// Projects - Store
test('it creates a project', function () {
$data = [
'name' => 'New Project',
'description' => 'A new test project',
'status' => 'planned',
'due_at' => now()->addDays(30)->toDateTimeString(),
];
$response = $this->postJson('/api/projects', $data);
$response->assertCreated()
->assertJsonPath('data.name', 'New Project')
->assertJsonPath('data.status', 'planned');
$this->assertDatabaseHas('projects', ['name' => 'New Project']);
});
// Projects - Update
test('it updates a project', function () {
$project = Project::factory()->create(['name' => 'Original Name']);
$data = [
'name' => 'Updated Name',
'description' => 'Updated description',
'status' => 'active',
];
$response = $this->putJson("/api/projects/{$project->id}", $data);
$response->assertOk()
->assertJsonPath('data.name', 'Updated Name')
->assertJsonPath('data.description', 'Updated description');
$this->assertDatabaseHas('projects', ['name' => 'Updated Name']);
});
// Projects - Destroy
test('it deletes a project', function () {
$project = Project::factory()->create();
$response = $this->deleteJson("/api/projects/{$project->id}");
$response->assertNoContent();
$this->assertSoftDeleted($project);
});
// Projects - Tasks Endpoint
test('it lists tasks for a project', function () {
$project = Project::factory()->create();
Task::factory()->count(3)->create(['project_id' => $project->id]);
Task::factory()->count(2)->create(); // Tasks not in this project
$response = $this->getJson("/api/projects/{$project->id}/tasks");
$response->assertOk()->assertJsonCount(3, 'data');
});
// ==================== TASKS API TESTS ====================
// Tasks - Index
test('it lists all tasks', function () {
Task::factory()->count(5)->create();
$response = $this->getJson('/api/tasks');
$response->assertOk()->assertJsonCount(5, 'data');
});
test('it filters tasks by status', function () {
Task::factory()->create(['status' => 'todo']);
Task::factory()->create(['status' => 'in_progress']);
Task::factory()->create(['status' => 'done']);
$response = $this->getJson('/api/tasks?status=in_progress');
$response->assertOk()
->assertJsonCount(1, 'data')
->assertJsonPath('data.0.status', 'in_progress');
});
test('it filters tasks by priority', function () {
Task::factory()->create(['priority' => 5]);
Task::factory()->create(['priority' => 3]);
Task::factory()->create(['priority' => 5]);
$response = $this->getJson('/api/tasks?priority=5');
$response->assertOk()->assertJsonCount(2, 'data');
});
test('it includes comments when requested', function () {
$task = Task::factory()->create();
TaskComment::factory()->count(2)->create(['task_id' => $task->id]);
$response = $this->getJson('/api/tasks?with_comments=1');
$response->assertOk()
->assertJsonPath('data.0.comment_count', 2);
});
// Tasks - Show
test('it shows a single task', function () {
$task = Task::factory()->create(['title' => 'Test Task']);
$response = $this->getJson("/api/tasks/{$task->id}");
$response->assertOk()->assertJsonPath('data.title', 'Test Task');
});
// Tasks - Store
test('it creates a task', function () {
$project = Project::factory()->create();
$data = [
'title' => 'New Task',
'description' => 'A new test task',
'status' => 'todo',
'project_id' => $project->id,
'priority' => 5,
'due_at' => now()->addDays(7)->toDateTimeString(),
'estimated_hours' => 4,
];
$response = $this->postJson('/api/tasks', $data);
$response->assertCreated()
->assertJsonPath('data.title', 'New Task')
->assertJsonPath('data.priority', 5);
$this->assertDatabaseHas('tasks', ['title' => 'New Task']);
});
// Tasks - Update
test('it updates a task', function () {
$task = Task::factory()->create(['title' => 'Original Title']);
$data = [
'title' => 'Updated Title',
'status' => 'in_progress',
'priority' => 8,
];
$response = $this->putJson("/api/tasks/{$task->id}", $data);
$response->assertOk()
->assertJsonPath('data.title', 'Updated Title')
->assertJsonPath('data.status', 'in_progress');
$this->assertDatabaseHas('tasks', ['title' => 'Updated Title']);
});
// Tasks - Destroy
test('it deletes a task', function () {
$task = Task::factory()->create();
$response = $this->deleteJson("/api/tasks/{$task->id}");
$response->assertNoContent();
$this->assertSoftDeleted($task);
});
// Tasks - Comments Endpoint
test('it lists comments for a task', function () {
$task = Task::factory()->create();
TaskComment::factory()->count(3)->create(['task_id' => $task->id]);
$response = $this->getJson("/api/tasks/{$task->id}/comments");
$response->assertOk()->assertJsonCount(3, 'data');
});
// Tasks - Change Status
test('it changes task status', function () {
$task = Task::factory()->create(['status' => 'todo']);
$response = $this->postJson("/api/tasks/{$task->id}/status", [
'status' => 'in_progress',
]);
$response->assertOk();
$task->refresh();
$this->assertEquals('in_progress', $task->status);
});
// ==================== TASK COMMENTS API TESTS ====================
// Task Comments - Index
test('it lists all task comments for a task', function () {
$task = Task::factory()->create();
TaskComment::factory()->count(4)->create(['task_id' => $task->id]);
$response = $this->getJson("/api/tasks/{$task->id}/comments");
$response->assertOk()->assertJsonCount(4, 'data');
});
test('it shows a single task comment', function () {
$comment = TaskComment::factory()->create(['body' => 'Test Comment']);
$response = $this->getJson("/api/comments/{$comment->id}");
$response->assertOk()->assertJsonPath('data.body', 'Test Comment');
});
// Task Comments - Store
test('it creates a task comment', function () {
$task = Task::factory()->create();
$data = [
'body' => 'New comment from AI',
'author_type' => 'ai',
'author_name' => 'AI Assistant',
];
$response = $this->postJson("/api/tasks/{$task->id}/comments", $data);
$response->assertCreated()
->assertJsonPath('data.body', 'New comment from AI')
->assertJsonPath('data.author_type', 'ai');
$this->assertDatabaseHas('task_comments', ['body' => 'New comment from AI']);
});
// Task Comments - Update
test('it updates a task comment', function () {
$comment = TaskComment::factory()->create(['body' => 'Original Comment']);
$data = [
'body' => 'Updated Comment',
];
$response = $this->putJson("/api/comments/{$comment->id}", $data);
$response->assertOk()
->assertJsonPath('data.body', 'Updated Comment');
$this->assertDatabaseHas('task_comments', ['body' => 'Updated Comment']);
});
// Task Comments - Destroy
test('it deletes a task comment', function () {
$comment = TaskComment::factory()->create();
$response = $this->deleteJson("/api/comments/{$comment->id}");
$response->assertNoContent();
$this->assertSoftDeleted($comment);
});
// ==================== IDEAS API TESTS ====================
// Ideas - Index
test('it lists all ideas', function () {
Idea::factory()->count(5)->create();
$response = $this->getJson('/api/ideas');
$response->assertOk()->assertJsonCount(5, 'data');
});
test('it filters ideas by status', function () {
Idea::factory()->create(['status' => 'new']);
Idea::factory()->create(['status' => 'considered']);
Idea::factory()->create(['status' => 'planned']);
$response = $this->getJson('/api/ideas?status=considered');
$response->assertOk()
->assertJsonCount(1, 'data')
->assertJsonPath('data.0.status', 'considered');
});
test('it filters ideas by category', function () {
Idea::factory()->feature()->create();
Idea::factory()->improvement()->create();
Idea::factory()->bugfix()->create();
$response = $this->getJson('/api/ideas?category=feature');
$response->assertOk()
->assertJsonCount(1, 'data')
->assertJsonPath('data.0.category', 'feature');
});
test('it filters unlinked ideas', function () {
Idea::factory()->create(['project_id' => null]);
Idea::factory()->withProject()->create();
$response = $this->getJson('/api/ideas?unlinked=1');
$response->assertOk()->assertJsonCount(1, 'data');
});
test('it filters ideas by project', function () {
$project = Project::factory()->create();
Idea::factory()->count(2)->withProject($project)->create();
Idea::factory()->count(3)->create();
$response = $this->getJson("/api/ideas?project_id={$project->id}");
$response->assertOk()->assertJsonCount(2, 'data');
});
test('it searches ideas', function () {
Idea::factory()->create(['title' => 'AI Integration']);
Idea::factory()->create(['title' => 'Database Optimization']);
$response = $this->getJson('/api/ideas?search=AI');
$response->assertOk()
->assertJsonCount(1, 'data')
->assertJsonPath('data.0.title', 'AI Integration');
});
// Ideas - Show
test('it shows a single idea', function () {
$idea = Idea::factory()->create(['title' => 'Test Idea']);
$response = $this->getJson("/api/ideas/{$idea->id}");
$response->assertOk()->assertJsonPath('data.title', 'Test Idea');
});
// Ideas - Store
test('it creates an idea', function () {
$data = [
'title' => 'New Idea',
'description' => 'A great new idea',
'status' => 'new',
'category' => 'feature',
'priority' => 5,
'tags' => 'ai,automation,innovation',
];
$response = $this->postJson('/api/ideas', $data);
$response->assertCreated()
->assertJsonPath('data.title', 'New Idea')
->assertJsonPath('data.category', 'feature');
$this->assertDatabaseHas('ideas', ['title' => 'New Idea']);
});
// Ideas - Update
test('it updates an idea', function () {
$idea = Idea::factory()->create(['title' => 'Original Title']);
$data = [
'title' => 'Updated Title',
'status' => 'considered',
'category' => 'improvement',
];
$response = $this->putJson("/api/ideas/{$idea->id}", $data);
$response->assertOk()
->assertJsonPath('data.title', 'Updated Title')
->assertJsonPath('data.status', 'considered');
$this->assertDatabaseHas('ideas', ['title' => 'Updated Title']);
});
// Ideas - Destroy
test('it deletes an idea', function () {
$idea = Idea::factory()->create();
$response = $this->deleteJson("/api/ideas/{$idea->id}");
$response->assertNoContent();
$this->assertSoftDeleted($idea);
});
// Ideas - Change Status
test('it changes idea status', function () {
$idea = Idea::factory()->create(['status' => 'new']);
$response = $this->postJson("/api/ideas/{$idea->id}/status", [
'status' => 'considered',
]);
$response->assertOk();
$idea->refresh();
$this->assertEquals('considered', $idea->status);
});
// Ideas - Link to Project
test('it links idea to project', function () {
$idea = Idea::factory()->create(['project_id' => null]);
$project = Project::factory()->create();
$response = $this->postJson("/api/ideas/{$idea->id}/link-project", [
'project_id' => $project->id,
]);
$response->assertOk();
$idea->refresh();
$this->assertEquals($project->id, $idea->project_id);
});
// Ideas - Unlink from Project
test('it unlinks idea from project', function () {
$project = Project::factory()->create();
$idea = Idea::factory()->withProject($project)->create();
$response = $this->postJson("/api/ideas/{$idea->id}/unlink-project");
$response->assertOk();
$idea->refresh();
$this->assertNull($idea->project_id);
});
// ==================== DATA FACTORIES TESTS ====================
// Test that factories work correctly
test('project factory creates valid projects', function () {
$project = Project::factory()->create();
$this->assertNotNull($project->name);
$this->assertContains($project->status, Project::STATUSES);
});
test('task factory creates valid tasks', function () {
$task = Task::factory()->create();
$this->assertNotNull($task->title);
$this->assertContains($task->status, Task::STATUSES);
$this->assertNotNull($task->project_id);
});
test('task comment factory creates valid comments', function () {
$comment = TaskComment::factory()->create();
$this->assertNotNull($comment->body);
$this->assertContains($comment->author_type, TaskComment::AUTHOR_TYPES);
$this->assertNotNull($comment->task_id);
});
test('idea factory creates valid ideas', function () {
$idea = Idea::factory()->create();
$this->assertNotNull($idea->title);
$this->assertContains($idea->status, Idea::STATUSES);
});