create(['status' => 'active']); $this->grantAdminRole($user); return [$user, $user->createToken('backlog-test')->plainTextToken]; } private function project(User $user): Project { return Project::create([ 'title' => 'پروژه بک‌لاگ', 'project_manager_id' => $user->id, 'created_by' => $user->id, 'start_date' => now()->toDateString(), 'end_date' => now()->addMonth()->toDateString(), 'status' => 'in_progress', ]); } public function test_backlog_description_is_preserved_when_created_and_edited(): void { [$user, $token] = $this->authenticatedAdmin(); $project = $this->project($user); $response = $this->withToken($token)->postJson('/api/backlog-items', [ 'title' => 'بک‌لاگ توضیح‌دار', 'description' => 'توضیحات اولیه', 'type' => 'feature', 'project_id' => $project->id, 'priority' => 'medium', ])->assertCreated() ->assertJsonPath('data.description', 'توضیحات اولیه') ->assertJsonPath('data.status', 'active') ->assertJsonPath('data.is_archived', false) ->assertJsonPath('data.archived_at', null); $backlogId = $response->json('data.id'); $this->withToken($token)->putJson("/api/backlog-items/{$backlogId}", [ 'title' => 'بک‌لاگ توضیح‌دار', 'description' => 'توضیحات ویرایش‌شده', 'type' => 'feature', 'project_id' => $project->id, 'priority' => 'medium', ])->assertOk()->assertJsonPath('data.description', 'توضیحات ویرایش‌شده'); $this->assertDatabaseHas('backlog_items', [ 'id' => $backlogId, 'description' => 'توضیحات ویرایش‌شده', 'status' => 'active', 'archived_at' => null, 'converted_to_task_id' => null, ]); } public function test_conversion_assigns_task_and_archives_backlog_atomically(): void { [$admin, $token] = $this->authenticatedAdmin(); $assignee = User::factory()->create(['status' => 'active']); $project = $this->project($admin); $backlog = BacklogItem::create([ 'title' => 'بک‌لاگ قابل تبدیل', 'description' => 'شرح تسک', 'type' => 'bug', 'project_id' => $project->id, 'priority' => 'high', 'status' => 'active', 'created_by' => $admin->id, ]); $response = $this->withToken($token)->postJson("/api/backlog-items/{$backlog->id}/convert-to-task", [ 'assignee_id' => $assignee->id, 'priority' => 'urgent', 'start_date' => now()->addDay()->toDateString(), 'due_date' => now()->addDays(3)->toDateString(), 'estimated_time' => 6, ])->assertCreated()->assertJsonPath('data.assignee_id', $assignee->id); $taskId = $response->json('data.id'); $this->assertDatabaseHas('tasks', [ 'id' => $taskId, 'assignee_id' => $assignee->id, 'description' => 'شرح تسک', 'priority' => 'urgent', ]); $this->assertDatabaseHas('backlog_items', [ 'id' => $backlog->id, 'status' => 'converted', 'converted_to_task_id' => $taskId, ]); $this->assertNotNull($backlog->fresh()->archived_at); $this->assertDatabaseHas('notifications', [ 'user_id' => $assignee->id, 'type' => 'task_assigned', 'notifiable_id' => $taskId, ]); $this->assertTrue(app(ResourceAccessService::class)->tasks($assignee)->whereKey($taskId)->exists()); $this->withToken($token)->getJson('/api/backlog-items?archive_state=active') ->assertOk()->assertJsonCount(0, 'data'); $this->withToken($token)->getJson('/api/backlog-items?archive_state=archived') ->assertOk()->assertJsonPath('data.0.converted_to_task_id', $taskId); $this->withToken($token)->postJson("/api/backlog-items/{$backlog->id}/convert-to-task", [ 'assignee_id' => $assignee->id, 'priority' => 'high', ])->assertUnprocessable(); $this->assertDatabaseCount('tasks', 1); } }