create([ 'name' => 'Ready for QA', ]); $this->assertNotNull($ticketStatus->project); $this->assertNull($ticketStatus->boardColumn); $this->assertSame('ready-for-qa', $ticketStatus->slug); $this->assertSame(1, $ticketStatus->position); } /** * Ensure project metadata initialization creates a default backlog status. */ public function test_project_metadata_initialization_creates_backlog_status(): void { $project = Project::factory()->create(); app(InitializeProjectTicketMetadata::class)->handle($project); $this->assertDatabaseHas('ticket_statuses', [ 'project_id' => $project->id, 'board_column_id' => null, 'name' => 'Backlog', 'slug' => 'backlog', 'position' => 1, ]); } /** * Ensure new statuses do not allow moves until transitions are configured. */ public function test_ticket_statuses_do_not_create_allowed_transition_relations_by_default(): void { $project = Project::factory()->create(); $backlogStatus = TicketStatus::factory()->for($project)->create([ 'name' => 'Backlog', 'position' => 1, ]); $readyStatus = TicketStatus::factory()->for($project)->create([ 'name' => 'Ready', 'position' => 2, ]); $this->assertFalse($backlogStatus->allowedTargetStatuses()->whereKey($readyStatus->id)->exists()); $this->assertFalse($readyStatus->allowedTargetStatuses()->whereKey($backlogStatus->id)->exists()); $this->assertCount(0, $backlogStatus->allowedTargetStatuses); $this->assertCount(0, $readyStatus->allowedTargetStatuses); } /** * Ensure allowed status moves are controlled by the transition relation. */ public function test_ticket_statuses_sync_allowed_transition_relations(): void { $project = Project::factory()->create(); $backlogStatus = TicketStatus::factory()->for($project)->create([ 'name' => 'Backlog', 'position' => 1, ]); $readyStatus = TicketStatus::factory()->for($project)->create([ 'name' => 'Ready', 'position' => 2, ]); $backlogStatus->allowedTargetStatuses()->sync([$readyStatus->id]); $this->assertSame( [$readyStatus->id], $backlogStatus->allowedTargetStatuses()->get()->pluck('id')->all(), ); $this->assertFalse($readyStatus->allowedTargetStatuses()->whereKey($backlogStatus->id)->exists()); $backlogStatus->allowedTargetStatuses()->sync([]); $this->assertCount(0, $backlogStatus->allowedTargetStatuses()->get()); } /** * Ensure board columns expose their grouped ticket statuses in position order. */ public function test_board_column_ticket_statuses_are_returned_in_position_order(): void { $project = Project::factory()->create(); $boardColumn = BoardColumn::factory()->for($project)->create(); TicketStatus::factory()->for($project)->for($boardColumn)->create([ 'name' => 'Second', 'position' => 2, ]); TicketStatus::factory()->for($project)->for($boardColumn)->create([ 'name' => 'First', 'position' => 1, ]); $this->assertSame( ['First', 'Second'], $boardColumn->fresh()->ticketStatuses->pluck('name')->all(), ); } /** * Ensure the ticket status seeder creates extra statuses for seeded projects. */ public function test_ticket_status_seeder_creates_statuses_for_seeded_projects(): void { $this->seed([ ProjectSeeder::class, BoardColumnSeeder::class, ProjectTicketMetadataSeeder::class, TicketStatusSeeder::class, ]); $coreProject = Project::query()->where('key', 'CORE')->firstOrFail(); $this->assertDatabaseHas('ticket_statuses', [ 'project_id' => $coreProject->id, 'board_column_id' => null, 'name' => 'Ready', 'slug' => 'ready', ]); } }