Initial commit
This commit is contained in:
328
tests/Unit/IssueTest.php
Normal file
328
tests/Unit/IssueTest.php
Normal file
@@ -0,0 +1,328 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Models\Issue;
|
||||
use App\Models\IssueComment;
|
||||
use App\Models\IssueAttachment;
|
||||
use App\Models\IssueLabel;
|
||||
use App\Models\IssueTimeLog;
|
||||
use App\Models\User;
|
||||
use App\Models\Member;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class IssueTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->artisan('db:seed', ['--class' => 'RoleSeeder']);
|
||||
}
|
||||
|
||||
public function test_issue_number_auto_generation(): void
|
||||
{
|
||||
$issue1 = Issue::factory()->create();
|
||||
$issue2 = Issue::factory()->create();
|
||||
|
||||
$this->assertMatchesRegularExpression('/^ISS-\d{4}-\d{3}$/', $issue1->issue_number);
|
||||
$this->assertMatchesRegularExpression('/^ISS-\d{4}-\d{3}$/', $issue2->issue_number);
|
||||
$this->assertNotEquals($issue1->issue_number, $issue2->issue_number);
|
||||
}
|
||||
|
||||
public function test_issue_belongs_to_creator(): void
|
||||
{
|
||||
$creator = User::factory()->create();
|
||||
$issue = Issue::factory()->create(['created_by_user_id' => $creator->id]);
|
||||
|
||||
$this->assertInstanceOf(User::class, $issue->creator);
|
||||
$this->assertEquals($creator->id, $issue->creator->id);
|
||||
}
|
||||
|
||||
public function test_issue_belongs_to_assignee(): void
|
||||
{
|
||||
$assignee = User::factory()->create();
|
||||
$issue = Issue::factory()->create(['assigned_to_user_id' => $assignee->id]);
|
||||
|
||||
$this->assertInstanceOf(User::class, $issue->assignee);
|
||||
$this->assertEquals($assignee->id, $issue->assignee->id);
|
||||
}
|
||||
|
||||
public function test_issue_belongs_to_reviewer(): void
|
||||
{
|
||||
$reviewer = User::factory()->create();
|
||||
$issue = Issue::factory()->create(['reviewer_id' => $reviewer->id]);
|
||||
|
||||
$this->assertInstanceOf(User::class, $issue->reviewer);
|
||||
$this->assertEquals($reviewer->id, $issue->reviewer->id);
|
||||
}
|
||||
|
||||
public function test_issue_has_many_comments(): void
|
||||
{
|
||||
$issue = Issue::factory()->create();
|
||||
$comment1 = IssueComment::factory()->create(['issue_id' => $issue->id]);
|
||||
$comment2 = IssueComment::factory()->create(['issue_id' => $issue->id]);
|
||||
|
||||
$this->assertCount(2, $issue->comments);
|
||||
$this->assertTrue($issue->comments->contains($comment1));
|
||||
}
|
||||
|
||||
public function test_issue_has_many_attachments(): void
|
||||
{
|
||||
$issue = Issue::factory()->create();
|
||||
$attachment1 = IssueAttachment::factory()->create(['issue_id' => $issue->id]);
|
||||
$attachment2 = IssueAttachment::factory()->create(['issue_id' => $issue->id]);
|
||||
|
||||
$this->assertCount(2, $issue->attachments);
|
||||
}
|
||||
|
||||
public function test_issue_has_many_time_logs(): void
|
||||
{
|
||||
$issue = Issue::factory()->create();
|
||||
$log1 = IssueTimeLog::factory()->create(['issue_id' => $issue->id, 'hours' => 2.5]);
|
||||
$log2 = IssueTimeLog::factory()->create(['issue_id' => $issue->id, 'hours' => 3.5]);
|
||||
|
||||
$this->assertCount(2, $issue->timeLogs);
|
||||
}
|
||||
|
||||
public function test_issue_has_many_labels(): void
|
||||
{
|
||||
$issue = Issue::factory()->create();
|
||||
$label1 = IssueLabel::factory()->create();
|
||||
$label2 = IssueLabel::factory()->create();
|
||||
|
||||
$issue->labels()->attach([$label1->id, $label2->id]);
|
||||
|
||||
$this->assertCount(2, $issue->labels);
|
||||
$this->assertTrue($issue->labels->contains($label1));
|
||||
}
|
||||
|
||||
public function test_issue_has_many_watchers(): void
|
||||
{
|
||||
$issue = Issue::factory()->create();
|
||||
$watcher1 = User::factory()->create();
|
||||
$watcher2 = User::factory()->create();
|
||||
|
||||
$issue->watchers()->attach([$watcher1->id, $watcher2->id]);
|
||||
|
||||
$this->assertCount(2, $issue->watchers);
|
||||
}
|
||||
|
||||
public function test_status_check_methods_work(): void
|
||||
{
|
||||
$issue = Issue::factory()->create(['status' => Issue::STATUS_NEW]);
|
||||
$this->assertTrue($issue->isNew());
|
||||
$this->assertFalse($issue->isAssigned());
|
||||
|
||||
$issue->status = Issue::STATUS_ASSIGNED;
|
||||
$this->assertTrue($issue->isAssigned());
|
||||
$this->assertFalse($issue->isNew());
|
||||
|
||||
$issue->status = Issue::STATUS_IN_PROGRESS;
|
||||
$this->assertTrue($issue->isInProgress());
|
||||
|
||||
$issue->status = Issue::STATUS_REVIEW;
|
||||
$this->assertTrue($issue->inReview());
|
||||
|
||||
$issue->status = Issue::STATUS_CLOSED;
|
||||
$this->assertTrue($issue->isClosed());
|
||||
$this->assertFalse($issue->isOpen());
|
||||
}
|
||||
|
||||
public function test_can_be_assigned_validates_correctly(): void
|
||||
{
|
||||
$newIssue = Issue::factory()->create(['status' => Issue::STATUS_NEW]);
|
||||
$this->assertTrue($newIssue->canBeAssigned());
|
||||
|
||||
$closedIssue = Issue::factory()->create(['status' => Issue::STATUS_CLOSED]);
|
||||
$this->assertFalse($closedIssue->canBeAssigned());
|
||||
}
|
||||
|
||||
public function test_can_move_to_in_progress_validates_correctly(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$assignedIssue = Issue::factory()->create([
|
||||
'status' => Issue::STATUS_ASSIGNED,
|
||||
'assigned_to_user_id' => $user->id,
|
||||
]);
|
||||
$this->assertTrue($assignedIssue->canMoveToInProgress());
|
||||
|
||||
$newIssue = Issue::factory()->create(['status' => Issue::STATUS_NEW]);
|
||||
$this->assertFalse($newIssue->canMoveToInProgress());
|
||||
|
||||
$assignedWithoutUser = Issue::factory()->create([
|
||||
'status' => Issue::STATUS_ASSIGNED,
|
||||
'assigned_to_user_id' => null,
|
||||
]);
|
||||
$this->assertFalse($assignedWithoutUser->canMoveToInProgress());
|
||||
}
|
||||
|
||||
public function test_can_move_to_review_validates_correctly(): void
|
||||
{
|
||||
$inProgressIssue = Issue::factory()->create(['status' => Issue::STATUS_IN_PROGRESS]);
|
||||
$this->assertTrue($inProgressIssue->canMoveToReview());
|
||||
|
||||
$newIssue = Issue::factory()->create(['status' => Issue::STATUS_NEW]);
|
||||
$this->assertFalse($newIssue->canMoveToReview());
|
||||
}
|
||||
|
||||
public function test_can_be_closed_validates_correctly(): void
|
||||
{
|
||||
$reviewIssue = Issue::factory()->create(['status' => Issue::STATUS_REVIEW]);
|
||||
$this->assertTrue($reviewIssue->canBeClosed());
|
||||
|
||||
$inProgressIssue = Issue::factory()->create(['status' => Issue::STATUS_IN_PROGRESS]);
|
||||
$this->assertTrue($inProgressIssue->canBeClosed());
|
||||
|
||||
$newIssue = Issue::factory()->create(['status' => Issue::STATUS_NEW]);
|
||||
$this->assertFalse($newIssue->canBeClosed());
|
||||
}
|
||||
|
||||
public function test_can_be_reopened_validates_correctly(): void
|
||||
{
|
||||
$closedIssue = Issue::factory()->create(['status' => Issue::STATUS_CLOSED]);
|
||||
$this->assertTrue($closedIssue->canBeReopened());
|
||||
|
||||
$openIssue = Issue::factory()->create(['status' => Issue::STATUS_NEW]);
|
||||
$this->assertFalse($openIssue->canBeReopened());
|
||||
}
|
||||
|
||||
public function test_progress_percentage_calculation(): void
|
||||
{
|
||||
$newIssue = Issue::factory()->create(['status' => Issue::STATUS_NEW]);
|
||||
$this->assertEquals(0, $newIssue->progress_percentage);
|
||||
|
||||
$assignedIssue = Issue::factory()->create(['status' => Issue::STATUS_ASSIGNED]);
|
||||
$this->assertEquals(25, $assignedIssue->progress_percentage);
|
||||
|
||||
$inProgressIssue = Issue::factory()->create(['status' => Issue::STATUS_IN_PROGRESS]);
|
||||
$this->assertEquals(50, $inProgressIssue->progress_percentage);
|
||||
|
||||
$reviewIssue = Issue::factory()->create(['status' => Issue::STATUS_REVIEW]);
|
||||
$this->assertEquals(75, $reviewIssue->progress_percentage);
|
||||
|
||||
$closedIssue = Issue::factory()->create(['status' => Issue::STATUS_CLOSED]);
|
||||
$this->assertEquals(100, $closedIssue->progress_percentage);
|
||||
}
|
||||
|
||||
public function test_overdue_detection_works(): void
|
||||
{
|
||||
$overdueIssue = Issue::factory()->create([
|
||||
'due_date' => now()->subDays(5),
|
||||
'status' => Issue::STATUS_IN_PROGRESS,
|
||||
]);
|
||||
$this->assertTrue($overdueIssue->is_overdue);
|
||||
|
||||
$upcomingIssue = Issue::factory()->create([
|
||||
'due_date' => now()->addDays(5),
|
||||
'status' => Issue::STATUS_IN_PROGRESS,
|
||||
]);
|
||||
$this->assertFalse($upcomingIssue->is_overdue);
|
||||
|
||||
$closedOverdueIssue = Issue::factory()->create([
|
||||
'due_date' => now()->subDays(5),
|
||||
'status' => Issue::STATUS_CLOSED,
|
||||
]);
|
||||
$this->assertFalse($closedOverdueIssue->is_overdue);
|
||||
}
|
||||
|
||||
public function test_days_until_due_calculation(): void
|
||||
{
|
||||
$issue = Issue::factory()->create([
|
||||
'due_date' => now()->addDays(5),
|
||||
]);
|
||||
$this->assertEquals(5, $issue->days_until_due);
|
||||
|
||||
$overdueIssue = Issue::factory()->create([
|
||||
'due_date' => now()->subDays(3),
|
||||
]);
|
||||
$this->assertEquals(-3, $overdueIssue->days_until_due);
|
||||
}
|
||||
|
||||
public function test_total_time_logged_calculation(): void
|
||||
{
|
||||
$issue = Issue::factory()->create();
|
||||
IssueTimeLog::factory()->create(['issue_id' => $issue->id, 'hours' => 2.5]);
|
||||
IssueTimeLog::factory()->create(['issue_id' => $issue->id, 'hours' => 3.5]);
|
||||
IssueTimeLog::factory()->create(['issue_id' => $issue->id, 'hours' => 1.0]);
|
||||
|
||||
$this->assertEquals(7.0, $issue->total_time_logged);
|
||||
}
|
||||
|
||||
public function test_status_label_returns_correct_text(): void
|
||||
{
|
||||
$issue = Issue::factory()->create(['status' => Issue::STATUS_NEW]);
|
||||
$this->assertEquals('New', $issue->status_label);
|
||||
|
||||
$issue->status = Issue::STATUS_CLOSED;
|
||||
$this->assertEquals('Closed', $issue->status_label);
|
||||
}
|
||||
|
||||
public function test_priority_label_returns_correct_text(): void
|
||||
{
|
||||
$issue = Issue::factory()->create(['priority' => Issue::PRIORITY_LOW]);
|
||||
$this->assertEquals('Low', $issue->priority_label);
|
||||
|
||||
$issue->priority = Issue::PRIORITY_URGENT;
|
||||
$this->assertEquals('Urgent', $issue->priority_label);
|
||||
}
|
||||
|
||||
public function test_badge_color_methods_work(): void
|
||||
{
|
||||
$urgentIssue = Issue::factory()->create(['priority' => Issue::PRIORITY_URGENT]);
|
||||
$this->assertStringContainsString('red', $urgentIssue->priority_badge_color);
|
||||
|
||||
$closedIssue = Issue::factory()->create(['status' => Issue::STATUS_CLOSED]);
|
||||
$this->assertStringContainsString('gray', $closedIssue->status_badge_color);
|
||||
}
|
||||
|
||||
public function test_scopes_work(): void
|
||||
{
|
||||
Issue::factory()->create(['status' => Issue::STATUS_NEW]);
|
||||
Issue::factory()->create(['status' => Issue::STATUS_IN_PROGRESS]);
|
||||
Issue::factory()->create(['status' => Issue::STATUS_CLOSED]);
|
||||
|
||||
$openIssues = Issue::open()->get();
|
||||
$this->assertCount(2, $openIssues);
|
||||
|
||||
$closedIssues = Issue::closed()->get();
|
||||
$this->assertCount(1, $closedIssues);
|
||||
}
|
||||
|
||||
public function test_overdue_scope_works(): void
|
||||
{
|
||||
Issue::factory()->create([
|
||||
'due_date' => now()->subDays(5),
|
||||
'status' => Issue::STATUS_IN_PROGRESS,
|
||||
]);
|
||||
Issue::factory()->create([
|
||||
'due_date' => now()->addDays(5),
|
||||
'status' => Issue::STATUS_IN_PROGRESS,
|
||||
]);
|
||||
|
||||
$overdueIssues = Issue::overdue()->get();
|
||||
$this->assertCount(1, $overdueIssues);
|
||||
}
|
||||
|
||||
public function test_parent_child_relationships_work(): void
|
||||
{
|
||||
$parentIssue = Issue::factory()->create();
|
||||
$childIssue1 = Issue::factory()->create(['parent_issue_id' => $parentIssue->id]);
|
||||
$childIssue2 = Issue::factory()->create(['parent_issue_id' => $parentIssue->id]);
|
||||
|
||||
$this->assertCount(2, $parentIssue->subTasks);
|
||||
$this->assertInstanceOf(Issue::class, $childIssue1->parentIssue);
|
||||
$this->assertEquals($parentIssue->id, $childIssue1->parentIssue->id);
|
||||
}
|
||||
|
||||
public function test_issue_type_label_returns_correct_text(): void
|
||||
{
|
||||
$issue = Issue::factory()->create(['issue_type' => Issue::TYPE_WORK_ITEM]);
|
||||
$this->assertEquals('Work Item', $issue->issue_type_label);
|
||||
|
||||
$issue->issue_type = Issue::TYPE_MEMBER_REQUEST;
|
||||
$this->assertEquals('Member Request', $issue->issue_type_label);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user