614 lines
24 KiB
PHP
614 lines
24 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Projects;
|
|
|
|
use App\Actions\Projects\DuplicateProjectConfiguration;
|
|
use App\Actions\ProjectTicketMetadata\InitializeProjectTicketMetadata;
|
|
use App\Models\Project;
|
|
use App\Models\ProjectTemplate;
|
|
use App\Models\Ticket;
|
|
use App\Models\TicketFieldDefinition;
|
|
use App\Models\TicketLinkType;
|
|
use App\Models\TicketPriority;
|
|
use App\Models\TicketType;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Inertia\Testing\AssertableInertia as Assert;
|
|
use Tests\TestCase;
|
|
|
|
class ProjectTicketMetadataSettingsControllerTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
/**
|
|
* Ensure users with edit permission can view ticket metadata settings.
|
|
*/
|
|
public function test_users_with_edit_permission_can_view_ticket_metadata_settings(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$project = Project::factory()->create();
|
|
|
|
app(InitializeProjectTicketMetadata::class)->handle($project);
|
|
|
|
TicketType::factory()->for($project)->create([
|
|
'name' => 'Bug',
|
|
'position' => 3,
|
|
]);
|
|
TicketLinkType::factory()->for($project)->create([
|
|
'label' => 'Relates to',
|
|
'counterpart_label' => 'Related from',
|
|
'position' => 1,
|
|
]);
|
|
$fieldDefinition = TicketFieldDefinition::factory()->for($project)->create([
|
|
'field_type' => 'number',
|
|
'label' => 'Complexity',
|
|
'position' => 1,
|
|
]);
|
|
$sourceTicket = Ticket::factory()->for($project)->create();
|
|
$targetTicket = Ticket::factory()->for($project)->create([
|
|
'sequence_number' => 2,
|
|
'ticket_key' => 'TEST-2',
|
|
]);
|
|
$fieldDefinition->ticketFieldValues()->create([
|
|
'ticket_id' => $sourceTicket->id,
|
|
'value_number' => 8,
|
|
]);
|
|
$project->ticketLinkTypes()->firstOrFail()->ticketLinks()->create([
|
|
'source_ticket_id' => $sourceTicket->id,
|
|
'target_ticket_id' => $targetTicket->id,
|
|
]);
|
|
|
|
$template = ProjectTemplate::factory()->create([
|
|
'configuration_snapshot' => app(DuplicateProjectConfiguration::class)->export($project, [
|
|
DuplicateProjectConfiguration::COPY_PROJECT_SETTINGS,
|
|
DuplicateProjectConfiguration::COPY_TICKET_TYPES,
|
|
DuplicateProjectConfiguration::COPY_TICKET_LINK_TYPES,
|
|
]),
|
|
'copy_areas' => [
|
|
DuplicateProjectConfiguration::COPY_PROJECT_SETTINGS,
|
|
DuplicateProjectConfiguration::COPY_TICKET_TYPES,
|
|
DuplicateProjectConfiguration::COPY_TICKET_LINK_TYPES,
|
|
],
|
|
'name' => 'Issue Tracker Template',
|
|
]);
|
|
$project->update([
|
|
'project_template_id' => $template->id,
|
|
'show_story_points' => false,
|
|
'template_overrides' => [DuplicateProjectConfiguration::COPY_PROJECT_SETTINGS],
|
|
]);
|
|
|
|
$this->syncProjectPermissions($project, $user, ['edit']);
|
|
|
|
$this->actingAs($user)
|
|
->get(route('projects.settings.metadata', $project))
|
|
->assertOk()
|
|
->assertInertia(fn (Assert $page) => $page
|
|
->component('projects/settings/Metadata')
|
|
->where('project.show_author', true)
|
|
->where('project.show_story_points', false)
|
|
->where('ticketTypes.0.name', 'Task')
|
|
->where('ticketTypes.1.name', 'Release')
|
|
->where('ticketTypes.1.is_parent', true)
|
|
->where('ticketTypes.2.name', 'Bug')
|
|
->where('fieldTypeOptions.0.value', 'user')
|
|
->where('fieldTypeOptions.1.value', 'number')
|
|
->where('ticketFieldDefinitions.0.label', 'Complexity')
|
|
->where('ticketFieldDefinitions.0.field_type', 'number')
|
|
->where('ticketFieldDefinitions.0.ticket_field_values_count', 1)
|
|
->where('ticketLinkTypes.0.label', 'Relates to')
|
|
->where('ticketLinkTypes.0.counterpart_label', 'Related from')
|
|
->where('ticketLinkTypes.0.ticket_links_count', 1)
|
|
->where('templateState.templateName', 'Issue Tracker Template')
|
|
->where('templateState.overriddenAreas.0.value', DuplicateProjectConfiguration::COPY_PROJECT_SETTINGS),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Ensure users can create custom ticket types.
|
|
*/
|
|
public function test_users_with_edit_permission_can_create_custom_ticket_types(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$project = Project::factory()->create();
|
|
|
|
$this->syncProjectPermissions($project, $user, ['edit']);
|
|
|
|
$this->actingAs($user)
|
|
->post(route('projects.settings.metadata.types.store', $project), [
|
|
'is_parent' => '1',
|
|
'name' => 'Bug',
|
|
])
|
|
->assertRedirect(route('projects.settings.metadata', $project))
|
|
->assertSessionHas('status', 'Bug type was added.');
|
|
|
|
$this->assertDatabaseHas('ticket_types', [
|
|
'project_id' => $project->id,
|
|
'name' => 'Bug',
|
|
'is_parent' => true,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Ensure type parent flags cannot be changed after creation.
|
|
*/
|
|
public function test_ticket_type_parent_flags_cannot_be_changed_after_creation(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$project = Project::factory()->create();
|
|
|
|
app(InitializeProjectTicketMetadata::class)->handle($project);
|
|
|
|
$releaseType = $project->ticketTypes()
|
|
->where('is_parent', true)
|
|
->orderBy('position')
|
|
->firstOrFail();
|
|
|
|
$this->syncProjectPermissions($project, $user, ['edit']);
|
|
|
|
$this->actingAs($user)
|
|
->patch(route('projects.settings.metadata.types.update', [$project, $releaseType]), [
|
|
'is_parent' => '0',
|
|
'name' => 'Standard Release',
|
|
'position' => 2,
|
|
])
|
|
->assertRedirect(route('projects.settings.metadata', $project))
|
|
->assertSessionHas('status', 'Standard Release type was updated.');
|
|
|
|
$this->assertDatabaseHas('ticket_types', [
|
|
'id' => $releaseType->id,
|
|
'name' => 'Standard Release',
|
|
'is_parent' => true,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Ensure users with edit permission can update project ticket visibility settings.
|
|
*/
|
|
public function test_users_with_edit_permission_can_update_ticket_visibility_settings(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$project = Project::factory()->create([
|
|
'show_author' => true,
|
|
'show_story_points' => true,
|
|
]);
|
|
|
|
$this->syncProjectPermissions($project, $user, ['edit']);
|
|
|
|
$this->actingAs($user)
|
|
->patch(route('projects.settings.metadata.update', $project), [
|
|
'show_author' => false,
|
|
'show_story_points' => false,
|
|
])
|
|
->assertRedirect(route('projects.settings.metadata', $project))
|
|
->assertSessionHas('status', 'Settings have been updated.');
|
|
|
|
$this->assertDatabaseHas('projects', [
|
|
'id' => $project->id,
|
|
'show_author' => false,
|
|
'show_story_points' => false,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Ensure users without project admin permission cannot update ticket metadata settings.
|
|
*/
|
|
public function test_non_admin_users_cannot_update_ticket_metadata_settings(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$project = Project::factory()->create();
|
|
|
|
$this->syncProjectPermissions($project, $user, ['view']);
|
|
|
|
$this->actingAs($user)
|
|
->patch(route('projects.settings.metadata.update', $project), [
|
|
'show_author' => false,
|
|
'show_story_points' => false,
|
|
])
|
|
->assertForbidden();
|
|
}
|
|
|
|
/**
|
|
* Ensure the last remaining ticket type cannot be deleted.
|
|
*/
|
|
public function test_the_last_remaining_ticket_type_cannot_be_deleted(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$project = Project::factory()->create();
|
|
$ticketType = TicketType::factory()->for($project)->create([
|
|
'name' => 'Solo',
|
|
'position' => 1,
|
|
]);
|
|
|
|
$this->syncProjectPermissions($project, $user, ['edit']);
|
|
|
|
$this->actingAs($user)
|
|
->delete(route('projects.settings.metadata.types.destroy', [$project, $ticketType]))
|
|
->assertRedirect(route('projects.settings.metadata', $project))
|
|
->assertSessionHas('status', 'The last ticket type cannot be deleted.');
|
|
|
|
$this->assertDatabaseHas('ticket_types', [
|
|
'id' => $ticketType->id,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Ensure deleting a used ticket type requires a replacement and reassigns affected tickets.
|
|
*/
|
|
public function test_used_ticket_types_require_replacement_before_deletion_and_reassign_tickets(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$author = User::factory()->create();
|
|
$project = Project::factory()->create([
|
|
'key' => 'CORE',
|
|
]);
|
|
|
|
app(InitializeProjectTicketMetadata::class)->handle($project);
|
|
|
|
$priority = $project->ticketPriorities()->firstOrFail();
|
|
$taskType = $project->ticketTypes()
|
|
->where('is_parent', false)
|
|
->orderBy('position')
|
|
->firstOrFail();
|
|
$releaseType = $project->ticketTypes()
|
|
->where('is_parent', true)
|
|
->orderBy('position')
|
|
->firstOrFail();
|
|
$ticket = Ticket::factory()->create([
|
|
'author_user_id' => $author->id,
|
|
'project_id' => $project->id,
|
|
'sequence_number' => 1,
|
|
'ticket_key' => 'CORE-1',
|
|
'ticket_priority_id' => $priority->id,
|
|
'ticket_type_id' => $taskType->id,
|
|
'title' => 'Replace my type',
|
|
]);
|
|
|
|
$this->syncProjectPermissions($project, $user, ['edit']);
|
|
|
|
$this->actingAs($user)
|
|
->from(route('projects.settings.metadata', $project))
|
|
->delete(route('projects.settings.metadata.types.destroy', [$project, $taskType]))
|
|
->assertRedirect(route('projects.settings.metadata', $project))
|
|
->assertSessionHasErrors('replacement_ticket_type_id');
|
|
|
|
$this->actingAs($user)
|
|
->delete(route('projects.settings.metadata.types.destroy', [$project, $taskType]), [
|
|
'replacement_ticket_type_id' => $releaseType->id,
|
|
])
|
|
->assertRedirect(route('projects.settings.metadata', $project))
|
|
->assertSessionHas('status', 'Task type was removed.');
|
|
|
|
$this->assertDatabaseMissing('ticket_types', [
|
|
'id' => $taskType->id,
|
|
]);
|
|
$this->assertDatabaseHas('tickets', [
|
|
'id' => $ticket->id,
|
|
'ticket_type_id' => $releaseType->id,
|
|
]);
|
|
$this->assertDatabaseHas('ticket_worklog_entries', [
|
|
'ticket_id' => $ticket->id,
|
|
'actor_id' => $user->id,
|
|
'entry_type' => 'updated',
|
|
'summary' => 'Updated type because Task type was deleted.',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Ensure parent tickets cannot be reassigned to a non-parent replacement type during delete.
|
|
*/
|
|
public function test_parent_tickets_require_a_parent_enabled_replacement_type_when_deleting_types(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$author = User::factory()->create();
|
|
$project = Project::factory()->create([
|
|
'key' => 'CORE',
|
|
]);
|
|
|
|
app(InitializeProjectTicketMetadata::class)->handle($project);
|
|
|
|
$priority = $project->ticketPriorities()->firstOrFail();
|
|
$taskType = $project->ticketTypes()
|
|
->where('is_parent', false)
|
|
->orderBy('position')
|
|
->firstOrFail();
|
|
$releaseType = $project->ticketTypes()
|
|
->where('is_parent', true)
|
|
->orderBy('position')
|
|
->firstOrFail();
|
|
$epicType = TicketType::factory()->for($project)->create([
|
|
'is_parent' => true,
|
|
'name' => 'Epic',
|
|
'position' => 3,
|
|
]);
|
|
$parentTicket = Ticket::factory()->create([
|
|
'author_user_id' => $author->id,
|
|
'project_id' => $project->id,
|
|
'sequence_number' => 1,
|
|
'ticket_key' => 'CORE-1',
|
|
'ticket_priority_id' => $priority->id,
|
|
'ticket_type_id' => $releaseType->id,
|
|
'title' => 'Parent ticket',
|
|
]);
|
|
Ticket::factory()->create([
|
|
'author_user_id' => $author->id,
|
|
'parent_ticket_id' => $parentTicket->id,
|
|
'project_id' => $project->id,
|
|
'sequence_number' => 2,
|
|
'ticket_key' => 'CORE-2',
|
|
'ticket_priority_id' => $priority->id,
|
|
'ticket_type_id' => $taskType->id,
|
|
'title' => 'Child ticket',
|
|
]);
|
|
|
|
$this->syncProjectPermissions($project, $user, ['edit']);
|
|
|
|
$this->actingAs($user)
|
|
->from(route('projects.settings.metadata', $project))
|
|
->delete(route('projects.settings.metadata.types.destroy', [$project, $releaseType]), [
|
|
'replacement_ticket_type_id' => $taskType->id,
|
|
])
|
|
->assertRedirect(route('projects.settings.metadata', $project))
|
|
->assertSessionHasErrors('replacement_ticket_type_id');
|
|
|
|
$this->actingAs($user)
|
|
->delete(route('projects.settings.metadata.types.destroy', [$project, $releaseType]), [
|
|
'replacement_ticket_type_id' => $epicType->id,
|
|
])
|
|
->assertRedirect(route('projects.settings.metadata', $project))
|
|
->assertSessionHas('status', 'Release type was removed.');
|
|
|
|
$this->assertDatabaseHas('tickets', [
|
|
'id' => $parentTicket->id,
|
|
'ticket_type_id' => $epicType->id,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Ensure priorities can be created, updated, and deleted.
|
|
*/
|
|
public function test_users_with_edit_permission_can_manage_ticket_priorities(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$project = Project::factory()->create();
|
|
$priority = TicketPriority::factory()->for($project)->create([
|
|
'name' => 'High',
|
|
'position' => 1,
|
|
]);
|
|
|
|
$this->syncProjectPermissions($project, $user, ['edit']);
|
|
|
|
$this->actingAs($user)
|
|
->post(route('projects.settings.metadata.priorities.store', $project), [
|
|
'name' => 'Low',
|
|
])
|
|
->assertRedirect(route('projects.settings.metadata', $project));
|
|
|
|
$this->actingAs($user)
|
|
->patch(route('projects.settings.metadata.priorities.update', [$project, $priority]), [
|
|
'name' => 'Urgent',
|
|
'position' => 2,
|
|
])
|
|
->assertRedirect(route('projects.settings.metadata', $project))
|
|
->assertSessionHas('status', 'Urgent priority was updated.');
|
|
|
|
$this->actingAs($user)
|
|
->delete(route('projects.settings.metadata.priorities.destroy', [$project, $priority]))
|
|
->assertRedirect(route('projects.settings.metadata', $project))
|
|
->assertSessionHas('status', 'Urgent priority was removed.');
|
|
|
|
$this->assertDatabaseMissing('ticket_priorities', [
|
|
'id' => $priority->id,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Ensure custom field definitions can be created and updated with supported types.
|
|
*/
|
|
public function test_users_with_edit_permission_can_manage_custom_field_definitions(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$project = Project::factory()->create();
|
|
$fieldDefinition = TicketFieldDefinition::factory()->for($project)->create([
|
|
'label' => 'Complexity',
|
|
'field_type' => 'number',
|
|
'position' => 1,
|
|
]);
|
|
$ticket = Ticket::factory()->for($project)->create();
|
|
$fieldValue = $fieldDefinition->ticketFieldValues()->create([
|
|
'ticket_id' => $ticket->id,
|
|
'value_number' => 5,
|
|
]);
|
|
|
|
$this->syncProjectPermissions($project, $user, ['edit']);
|
|
|
|
$this->actingAs($user)
|
|
->post(route('projects.settings.metadata.fields.store', $project), [
|
|
'label' => 'Reviewer',
|
|
'field_type' => 'user',
|
|
])
|
|
->assertRedirect(route('projects.settings.metadata', $project))
|
|
->assertSessionHas('status', 'Reviewer field was added.');
|
|
|
|
$this->actingAs($user)
|
|
->patch(route('projects.settings.metadata.fields.update', [$project, $fieldDefinition]), [
|
|
'label' => 'Risk score',
|
|
'field_type' => 'number',
|
|
'position' => 2,
|
|
])
|
|
->assertRedirect(route('projects.settings.metadata', $project))
|
|
->assertSessionHas('status', 'Risk score field was updated.');
|
|
|
|
$this->assertDatabaseHas('ticket_field_definitions', [
|
|
'id' => $fieldDefinition->id,
|
|
'label' => 'Risk score',
|
|
'field_type' => 'number',
|
|
]);
|
|
$this->assertDatabaseHas('ticket_field_values', [
|
|
'id' => $fieldValue->id,
|
|
'ticket_field_definition_id' => $fieldDefinition->id,
|
|
'value_number' => 5,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Ensure unsupported field types are rejected.
|
|
*/
|
|
public function test_unsupported_field_types_are_rejected(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$project = Project::factory()->create();
|
|
|
|
$this->syncProjectPermissions($project, $user, ['edit']);
|
|
|
|
$this->actingAs($user)
|
|
->from(route('projects.settings.metadata', $project))
|
|
->post(route('projects.settings.metadata.fields.store', $project), [
|
|
'label' => 'Reviewer',
|
|
'field_type' => 'text',
|
|
])
|
|
->assertSessionHasErrors('field_type')
|
|
->assertRedirect(route('projects.settings.metadata', $project));
|
|
}
|
|
|
|
/**
|
|
* Ensure users with edit permission can manage ticket link types.
|
|
*/
|
|
public function test_users_with_edit_permission_can_manage_ticket_link_types(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$project = Project::factory()->create();
|
|
$ticketLinkType = TicketLinkType::factory()->for($project)->create([
|
|
'label' => 'Relates to',
|
|
'counterpart_label' => 'Relates to',
|
|
'position' => 1,
|
|
]);
|
|
$sourceTicket = Ticket::factory()->for($project)->create();
|
|
$targetTicket = Ticket::factory()->for($project)->create([
|
|
'sequence_number' => 2,
|
|
'ticket_key' => 'TEST-2',
|
|
]);
|
|
$ticketLink = $ticketLinkType->ticketLinks()->create([
|
|
'source_ticket_id' => $sourceTicket->id,
|
|
'target_ticket_id' => $targetTicket->id,
|
|
]);
|
|
|
|
$this->syncProjectPermissions($project, $user, ['edit']);
|
|
|
|
$this->actingAs($user)
|
|
->post(route('projects.settings.metadata.link-types.store', $project), [
|
|
'name' => 'Blocks',
|
|
'counterpart_name' => 'Blocked by',
|
|
])
|
|
->assertRedirect(route('projects.settings.metadata', $project))
|
|
->assertSessionHas('status', 'Blocks link type was added.');
|
|
|
|
$this->assertDatabaseHas('ticket_link_types', [
|
|
'project_id' => $project->id,
|
|
'label' => 'Blocks',
|
|
'counterpart_label' => 'Blocked by',
|
|
]);
|
|
|
|
$this->actingAs($user)
|
|
->patch(route('projects.settings.metadata.link-types.update', [$project, $ticketLinkType]), [
|
|
'name' => 'Depends on',
|
|
'counterpart_name' => 'Required by',
|
|
'position' => 2,
|
|
])
|
|
->assertRedirect(route('projects.settings.metadata', $project))
|
|
->assertSessionHas('status', 'Depends on link type was updated.');
|
|
|
|
$this->assertDatabaseHas('ticket_link_types', [
|
|
'id' => $ticketLinkType->id,
|
|
'label' => 'Depends on',
|
|
'counterpart_label' => 'Required by',
|
|
]);
|
|
$this->assertDatabaseHas('ticket_links', [
|
|
'id' => $ticketLink->id,
|
|
'ticket_link_type_id' => $ticketLinkType->id,
|
|
]);
|
|
|
|
$this->actingAs($user)
|
|
->delete(route('projects.settings.metadata.link-types.destroy', [$project, $ticketLinkType]))
|
|
->assertRedirect(route('projects.settings.metadata', $project))
|
|
->assertSessionHas('status', 'Depends on link type was removed.');
|
|
|
|
$this->assertDatabaseMissing('ticket_link_types', [
|
|
'id' => $ticketLinkType->id,
|
|
]);
|
|
$this->assertDatabaseMissing('ticket_links', [
|
|
'id' => $ticketLink->id,
|
|
]);
|
|
}
|
|
|
|
public function test_last_priority_is_protected_from_deletion(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$project = Project::factory()->create();
|
|
$priority = TicketPriority::factory()->for($project)->create();
|
|
$this->syncProjectPermissions($project, $user, ['edit']);
|
|
|
|
$this->actingAs($user)
|
|
->delete(route('projects.settings.metadata.priorities.destroy', [$project, $priority]))
|
|
->assertRedirect(route('projects.settings.metadata', $project))
|
|
->assertSessionHas('error', true)
|
|
->assertSessionHas('status', 'The last ticket priority cannot be deleted.');
|
|
|
|
$this->assertModelExists($priority);
|
|
}
|
|
|
|
public function test_type_order_and_usage_counts_are_returned_after_reordering(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$project = Project::factory()->create();
|
|
app(InitializeProjectTicketMetadata::class)->handle($project);
|
|
$types = $project->ticketTypes()->get();
|
|
$first = $types->first();
|
|
$last = $types->last();
|
|
Ticket::factory()->for($project)->create([
|
|
'ticket_type_id' => $last->id,
|
|
'ticket_priority_id' => $project->ticketPriorities()->firstOrFail()->id,
|
|
]);
|
|
$this->syncProjectPermissions($project, $user, ['edit']);
|
|
|
|
$this->actingAs($user)
|
|
->patch(route('projects.settings.metadata.types.update', [$project, $last]), [
|
|
'name' => 'Customer request', 'position' => 1,
|
|
])
|
|
->assertSessionHasNoErrors();
|
|
|
|
$this->get(route('projects.settings.metadata', $project))
|
|
->assertInertia(fn (Assert $page) => $page
|
|
->where('ticketTypes.0.id', $last->id)
|
|
->where('ticketTypes.0.name', 'Customer request')
|
|
->where('ticketTypes.0.position', 1)
|
|
->where('ticketTypes.0.tickets_count', 1)
|
|
->where('ticketTypes.1.id', $first->id)
|
|
->where('ticketTypes.1.position', 2)
|
|
->where('ticketTypes.1.tickets_count', 0));
|
|
}
|
|
|
|
public function test_foreign_replacement_type_is_rejected_without_changing_tickets(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$project = Project::factory()->create();
|
|
app(InitializeProjectTicketMetadata::class)->handle($project);
|
|
$type = $project->ticketTypes()->firstOrFail();
|
|
$foreignType = TicketType::factory()->create();
|
|
$ticket = Ticket::factory()->for($project)->create([
|
|
'ticket_type_id' => $type->id,
|
|
'ticket_priority_id' => $project->ticketPriorities()->firstOrFail()->id,
|
|
]);
|
|
$this->syncProjectPermissions($project, $user, ['edit']);
|
|
|
|
$this->actingAs($user)
|
|
->from(route('projects.settings.metadata', $project))
|
|
->delete(route('projects.settings.metadata.types.destroy', [$project, $type]), [
|
|
'replacement_ticket_type_id' => $foreignType->id,
|
|
])
|
|
->assertSessionHasErrors('replacement_ticket_type_id');
|
|
|
|
$this->assertModelExists($type);
|
|
$this->assertSame($type->id, $ticket->fresh()->ticket_type_id);
|
|
}
|
|
}
|