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); });