feat: KAN-30
CI/CD / preconditions (pull_request) Successful in 1m0s
CI/CD / test_and_check_app (pull_request) Canceled after 0s
CI/CD / Publish immutable image (pull_request) Canceled after 0s

This commit is contained in:
2026-09-12 13:33:53 +02:00
committed by Twyco
parent 509d7abf82
commit d646f8b537
9 changed files with 1058 additions and 603 deletions
@@ -1511,6 +1511,74 @@ class ProjectTicketControllerTest extends TestCase
]);
}
public function test_read_only_users_cannot_open_or_submit_the_ticket_create_form(): void
{
$viewer = User::factory()->create();
$project = Project::factory()->create();
app(InitializeProjectTicketMetadata::class)->handle($project);
$this->syncProjectPermissions($project, $viewer, ['view']);
$this->actingAs($viewer)
->get(route('projects.tickets.create', $project))
->assertForbidden();
$this->post(route('projects.tickets.store', $project), [
'title' => 'Unauthorized ticket',
'ticket_type_id' => $this->nonParentType($project)->id,
'ticket_priority_id' => $project->ticketPriorities()->firstOrFail()->id,
'ticket_status_id' => $project->ticketStatuses()->firstOrFail()->id,
])->assertForbidden();
$this->assertDatabaseCount('tickets', 0);
}
public function test_ticket_forms_reject_metadata_from_another_project(): void
{
$editor = User::factory()->create();
$project = Project::factory()->create();
$otherProject = Project::factory()->create();
app(InitializeProjectTicketMetadata::class)->handle($project);
app(InitializeProjectTicketMetadata::class)->handle($otherProject);
$this->syncProjectPermissions($project, $editor, ['write', 'view']);
$parent = Ticket::factory()->create([
'project_id' => $otherProject->id,
'ticket_key' => 'OTHER-1',
'ticket_type_id' => $this->parentType($otherProject)->id,
'ticket_priority_id' => $otherProject->ticketPriorities()->firstOrFail()->id,
]);
$invalidMetadata = [
'parent_ticket_id' => $parent->id,
'ticket_type_id' => $this->nonParentType($otherProject)->id,
'ticket_priority_id' => $otherProject->ticketPriorities()->firstOrFail()->id,
];
$this->actingAs($editor)
->post(route('projects.tickets.store', $project), [
...$invalidMetadata,
'title' => 'Rejected creation',
'ticket_status_id' => $otherProject->ticketStatuses()->firstOrFail()->id,
])
->assertSessionHasErrors([...array_keys($invalidMetadata), 'ticket_status_id']);
$this->assertSame(0, $project->tickets()->count());
$ticket = Ticket::factory()->create([
'project_id' => $project->id,
'ticket_key' => 'LOCAL-1',
'ticket_type_id' => $this->nonParentType($project)->id,
'ticket_priority_id' => $project->ticketPriorities()->firstOrFail()->id,
'title' => 'Original title',
]);
$this->patch(route('projects.tickets.update', [$project, $ticket]), $this->updatePayload($ticket, [
...$invalidMetadata,
'title' => 'Rejected update',
]))->assertSessionHasErrors(array_keys($invalidMetadata));
$this->assertSame('Original title', $ticket->fresh()->title);
$this->assertNull($ticket->fresh()->parent_ticket_id);
}
private function nonParentType(Project $project): TicketType
{
return $project->ticketTypes()