Added tests

Signed-off-by: snipe <snipe@snipe.net>
This commit is contained in:
snipe
2025-09-09 13:51:56 +01:00
parent 57af507170
commit 2eb727bd0c
8 changed files with 286 additions and 28 deletions

View File

@@ -37,10 +37,14 @@ class LocationsController extends Controller
'address',
'address2',
'assets_count',
'assets_count',
'assigned_assets_count',
'rtd_assets_count',
'accessories_count',
'assigned_accessories_count',
'assigned_assets_count',
'assigned_assets_count',
'components_count',
'consumables_count',
'users_count',
'children_count',
'city',
'country',
'created_at',
@@ -54,7 +58,6 @@ class LocationsController extends Controller
'rtd_assets_count',
'state',
'updated_at',
'users_count',
'zip',
'notes',
];
@@ -231,8 +234,13 @@ class LocationsController extends Controller
])
->withCount('assignedAssets as assigned_assets_count')
->withCount('assets as assets_count')
->withCount('assignedAccessories as assigned_accessories_count')
->withCount('accessories as accessories_count')
->withCount('rtd_assets as rtd_assets_count')
->withCount('children as children_count')
->withCount('users as users_count')
->withCount('consumables as consumables_count')
->withCount('components as components_count')
->findOrFail($id);
return (new LocationsTransformer)->transformLocation($location);
@@ -327,11 +335,15 @@ class LocationsController extends Controller
{
$this->authorize('delete', Location::class);
$location = Location::withCount('assignedAssets as assigned_assets_count')
->withCount('assignedAssets as assigned_assets_count')
->withCount('assets as assets_count')
->withCount('assignedAccessories as assigned_accessories_count')
->withCount('accessories as accessories_count')
->withCount('rtd_assets as rtd_assets_count')
->withCount('children as children_count')
->withCount('users as users_count')
->withCount('accessories as accessories_count')
->withCount('consumables as consumables_count')
->withCount('components as components_count')
->findOrFail($id);
if (! $location->isDeletable()) {

View File

@@ -189,7 +189,18 @@ class LocationsController extends Controller
{
$this->authorize('delete', Location::class);
if (is_null($location = Location::find($locationId))) {
$location = Location::withCount('assignedAssets as assigned_assets_count')
->withCount('assets as assets_count')
->withCount('assignedAccessories as assigned_accessories_count')
->withCount('accessories as accessories_count')
->withCount('rtd_assets as rtd_assets_count')
->withCount('children as children_count')
->withCount('users as users_count')
->withCount('consumables as consumables_count')
->withCount('components as components_count')
->find($locationId);
if (!$location) {
return redirect()->to(route('locations.index'))->with('error', trans('admin/locations/message.does_not_exist'));
}

View File

@@ -53,6 +53,8 @@ class LocationsTransformer
'assets_count' => (int) $location->assets_count,
'rtd_assets_count' => (int) $location->rtd_assets_count,
'users_count' => (int) $location->users_count,
'consumables_count' => (int) $location->consumables_count,
'components_count' => (int) $location->components_count,
'currency' => ($location->currency) ? e($location->currency) : null,
'ldap_ou' => ($location->ldap_ou) ? e($location->ldap_ou) : null,
'notes' => Helper::parseEscapedMarkedownInline($location->notes),

View File

@@ -44,7 +44,6 @@ class Location extends SnipeModel
];
/**
* Whether the model should inject its identifier to the unique
* validation rules before attempting validation. If this property
@@ -115,16 +114,16 @@ class Location extends SnipeModel
{
return Gate::allows('delete', $this)
&& ($this->deleted_at == '')
&& ($this->assigned_assets_count == 0)
&& ($this->assets_count == 0)
&& ($this->assigned_accessories_count == 0)
&& ($this->accessories_count == 0)
&& ($this->rtd_assets_count == 0)
&& ($this->children_count == 0)
&& ($this->consumables_count == 0)
&& ($this->componments_count == 0)
&& ($this->users_count == 0);
&& ($this->deleted_at == '')
&& (($this->assets_count ?? $this->assets()->count()) === 0)
&& (($this->assigned_assets_count ?? $this->assignedAssets()->count()) === 0)
&& (($this->accessories_count ?? $this->accessories()->count()) === 0)
&& (($this->assigned_accessories_count ?? $this->assignedAccessories()->count()) === 0)
&& (($this->children_count ?? $this->children()->count()) === 0)
&& (($this->components_count ?? $this->components()->count()) === 0)
&& (($this->consumables_count ?? $this->consumables()->count()) === 0)
&& (($this->rtd_assets_count ?? $this->rtd_assets()->count()) === 0)
&& (($this->users_count ?? $this->users()->count()) === 0);
}

View File

@@ -61,6 +61,15 @@ class LocationPresenter extends Presenter
'title' => trans('admin/locations/table.parent'),
'visible' => true,
'formatter' => 'locationsLinkObjFormatter',
], [
'field' => 'users_count',
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('general.people'),
'titleTooltip' => trans('general.people'),
'visible' => true,
'class' => 'css-house-user',
], [
'field' => 'assets_count',
'searchable' => false,
@@ -98,7 +107,7 @@ class LocationPresenter extends Presenter
'titleTooltip' => trans('general.accessories'),
'visible' => true,
'class' => 'css-accessory',
], [
],[
'field' => 'assigned_accessories_count',
'searchable' => false,
'sortable' => true,
@@ -108,15 +117,25 @@ class LocationPresenter extends Presenter
'visible' => true,
'class' => 'css-accessory-alt',
], [
'field' => 'users_count',
'field' => 'components_count',
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('general.people'),
'titleTooltip' => trans('general.people'),
'title' => trans('general.components'),
'titleTooltip' => trans('general.components'),
'visible' => true,
'class' => 'css-house-user',
], [
'class' => 'css-component',
],
[
'field' => 'consumables_count',
'searchable' => false,
'sortable' => true,
'switchable' => true,
'title' => trans('general.consumables'),
'titleTooltip' => trans('general.consumables'),
'visible' => true,
'class' => 'css-consumable',
], [
'field' => 'currency',
'searchable' => true,
'sortable' => true,

View File

@@ -498,10 +498,12 @@
{{ trans('general.delete') }}
</button>
@else
<a href="#" class="btn btn-block btn-sm btn-danger btn-social hidden-print disabled" data-tooltip="true" data-placement="top" data-title="{{ trans('general.cannot_be_deleted') }}">
<span data-placement="top" data-tooltip="true" data-title="{{ trans('admin/locations/message.assoc_users') }}">
<a href="#" class="btn btn-block btn-sm btn-danger btn-social hidden-print disabled" data-tooltip="true">
<x-icon type="delete" />
{{ trans('general.delete') }}
</a>
</span>
@endif
@else

View File

@@ -2,7 +2,10 @@
namespace Tests\Feature\Locations\Api;
use App\Models\Accessory;
use App\Models\Asset;
use App\Models\Component;
use App\Models\Consumable;
use App\Models\Location;
use App\Models\User;
use Tests\Concerns\TestsPermissionsRequirement;
@@ -24,11 +27,12 @@ class DeleteLocationsTest extends TestCase implements TestsPermissionsRequiremen
public function testErrorReturnedViaApiIfLocationDoesNotExist()
{
$this->actingAsForApi(User::factory()->superuser()->create())
->deleteJson(route('api.users.destroy', 'invalid-id'))
->deleteJson(route('api.locations.destroy', 'invalid-id'))
->assertOk()
->assertStatus(200)
->assertStatusMessageIs('error')
->json();
}
public function testErrorReturnedViaApiIfLocationIsAlreadyDeleted()
@@ -55,9 +59,10 @@ class DeleteLocationsTest extends TestCase implements TestsPermissionsRequiremen
->assertStatus(200)
->assertStatusMessageIs('error')
->json();
$this->assertNotSoftDeleted($location);
}
public function testDisallowUserDeletionViaApiIfStillHasChildLocations()
public function testDisallowLocationDeletionViaApiIfStillHasChildLocations()
{
$parent = Location::factory()->create();
Location::factory()->count(5)->create(['parent_id' => $parent->id]);
@@ -69,9 +74,10 @@ class DeleteLocationsTest extends TestCase implements TestsPermissionsRequiremen
->assertStatus(200)
->assertStatusMessageIs('error')
->json();
$this->assertNotSoftDeleted($parent);
}
public function testDisallowUserDeletionViaApiIfStillHasAssetsAssigned()
public function testDisallowLocationDeletionViaApiIfStillHasAssetsAssigned()
{
$location = Location::factory()->create();
Asset::factory()->count(5)->assignedToLocation($location)->create();
@@ -84,9 +90,10 @@ class DeleteLocationsTest extends TestCase implements TestsPermissionsRequiremen
->assertStatus(200)
->assertStatusMessageIs('error')
->json();
$this->assertNotSoftDeleted($location);
}
public function testDisallowUserDeletionViaApiIfStillHasAssetsAsLocation()
public function testDisallowLocationDeletionViaApiIfStillHasAssetsAsLocation()
{
$location = Location::factory()->create();
Asset::factory()->count(5)->create(['location_id' => $location->id]);
@@ -99,6 +106,73 @@ class DeleteLocationsTest extends TestCase implements TestsPermissionsRequiremen
->assertStatus(200)
->assertStatusMessageIs('error')
->json();
$this->assertNotSoftDeleted($location);
}
public function testDisallowLocationDeletionViaApiIfStillHasConsumablesAsLocation()
{
$location = Location::factory()->create();
Consumable::factory()->count(5)->create(['location_id' => $location->id]);
$this->assertFalse($location->isDeletable());
$this->actingAsForApi(User::factory()->superuser()->create())
->deleteJson(route('api.locations.destroy', $location->id))
->assertOk()
->assertStatus(200)
->assertStatusMessageIs('error')
->json();
$this->assertNotSoftDeleted($location);
}
public function testDisallowLocationDeletionViaApiIfStillHasComponentsAsLocation()
{
$location = Location::factory()->create();
Component::factory()->count(5)->create(['location_id' => $location->id]);
$this->assertFalse($location->isDeletable());
$this->actingAsForApi(User::factory()->superuser()->create())
->deleteJson(route('api.locations.destroy', $location->id))
->assertOk()
->assertStatus(200)
->assertStatusMessageIs('error')
->json();
$this->assertNotSoftDeleted($location);
}
public function testDisallowLocationDeletionViaApiIfStillHasAccessoriesAssigned()
{
$location = Location::factory()->create();
Accessory::factory()->count(5)->checkedOutToLocation($location)->create();
$this->assertFalse($location->isDeletable());
$this->actingAsForApi(User::factory()->superuser()->create())
->deleteJson(route('api.locations.destroy', $location->id))
->assertOk()
->assertStatus(200)
->assertStatusMessageIs('error')
->json();
$this->assertNotSoftDeleted($location);
}
public function testDisallowLocationDeletionViaApiIfStillHasAccessoriesAsLocation()
{
$location = Location::factory()->create();
Accessory::factory()->count(5)->create(['location_id' => $location->id]);
$this->assertFalse($location->isDeletable());
$this->actingAsForApi(User::factory()->superuser()->create())
->deleteJson(route('api.locations.destroy', $location->id))
->assertOk()
->assertStatus(200)
->assertStatusMessageIs('error')
->json();
$this->assertNotSoftDeleted($location);
}
public function testCanDeleteLocation()

View File

@@ -0,0 +1,139 @@
<?php
namespace Tests\Feature\Users\Ui;
use App\Models\Consumable;
use Tests\TestCase;
use App\Models\Location;
use App\Models\Accessory;
use App\Models\User;
use App\Models\Asset;
class DeleteLocationsTest extends TestCase
{
public function testRequiresPermission()
{
$this->actingAs(User::factory()->create())
->delete(route('locations.destroy', Location::factory()->create()))
->assertForbidden();
}
public function testCanDeleteLocation()
{
$location = Location::factory()->create();
$this->actingAs(User::factory()->deleteLocations()->create())
->delete(route('locations.destroy', $location))
->assertRedirectToRoute('locations.index')
->assertSessionHas('success')
->assertStatus(302)
->assertRedirect(route('locations.index'));
$this->assertSoftDeleted($location);
}
public function testCannotDeleteLocationWithAssetsAsLocation()
{
$location = Location::factory()->create();
Asset::factory()->count(5)->create(['location_id' => $location->id]);
$this->actingAs(User::factory()->deleteLocations()->create())
->delete(route('locations.destroy', $location))
->assertStatus(302)
->assertRedirectToRoute('locations.index')
->assertSessionHas('error');
$this->assertNotSoftDeleted($location);
}
public function testCannotDeleteLocationWithAssetsAssigned()
{
$location = Location::factory()->create();
Asset::factory()->count(5)->assignedToLocation($location)->create();
$this->actingAs(User::factory()->deleteLocations()->create())
->delete(route('locations.destroy', $location))
->assertStatus(302)
->assertRedirectToRoute('locations.index')
->assertSessionHas('error');
$this->assertNotSoftDeleted($location);
}
public function testCannotDeleteLocationWithChildren()
{
$parent = Location::factory()->create();
Location::factory()->count(5)->create(['parent_id' => $parent->id]);
$this->actingAs(User::factory()->deleteLocations()->create())
->delete(route('locations.destroy', $parent))
->assertStatus(302)
->assertRedirectToRoute('locations.index')
->assertSessionHas('error');
$this->assertNotSoftDeleted($parent);
}
public function testCannotDeleteLocationWithConsumableAsLocation()
{
$location = Location::factory()->create();
Consumable::factory()->count(5)->create(['location_id' => $location->id]);
$this->actingAs(User::factory()->deleteLocations()->create())
->delete(route('locations.destroy', $location))
->assertStatus(302)
->assertRedirectToRoute('locations.index')
->assertSessionHas('error');
$this->assertNotSoftDeleted($location);
}
public function testCannotDeleteLocationWithAccessoriesAssigned()
{
$location = Location::factory()->create();
Accessory::factory()->count(5)->checkedOutToLocation($location)->create();
$this->actingAs(User::factory()->deleteLocations()->create())
->delete(route('locations.destroy', $location))
->assertStatus(302)
->assertRedirectToRoute('locations.index')
->assertSessionHas('error');
$this->assertNotSoftDeleted($location);
}
public function testCannotDeleteLocationWithAccessoriesAsLocation()
{
$location = Location::factory()->create();
Accessory::factory()->count(5)->create(['location_id' => $location->id]);
$this->actingAs(User::factory()->deleteLocations()->create())
->delete(route('locations.destroy', $location))
->assertStatus(302)
->assertRedirectToRoute('locations.index')
->assertSessionHas('error');
$this->assertNotSoftDeleted($location);
}
public function testCannotDeleteLocationWithPeople()
{
$location = Location::factory()->create();
User::factory()->count(5)->create(['location_id' => $location->id]);
$this->actingAs(User::factory()->deleteLocations()->create())
->delete(route('locations.destroy', $location))
->assertStatus(302)
->assertRedirectToRoute('locations.index')
->assertSessionHas('error');
$this->assertNotSoftDeleted($location);
}
}