Files
snipe-it/tests/Feature/Checkins/Ui/BulkAssetCheckinTest.php

323 lines
12 KiB
PHP

<?php
namespace Tests\Feature\Checkins\Ui;
use App\Events\CheckoutableCheckedIn;
use App\Models\Asset;
use App\Models\CheckoutAcceptance;
use App\Models\LicenseSeat;
use App\Models\Location;
use App\Models\Statuslabel;
use App\Models\User;
use Illuminate\Support\Facades\Event;
use Tests\TestCase;
class BulkAssetCheckinTest extends TestCase
{
public function test_requires_checkin_permission()
{
$asset = Asset::factory()->assignedToUser()->create();
$this->actingAs(User::factory()->create())
->post(route('hardware.bulkcheckin.store'), [
'selected_assets' => [$asset->id],
])
->assertForbidden();
}
public function test_show_page_requires_checkin_permission()
{
$this->actingAs(User::factory()->create())
->get(route('hardware.bulkcheckin.show'))
->assertForbidden();
}
public function test_show_page_renders()
{
$this->actingAs(User::factory()->checkinAssets()->create())
->get(route('hardware.bulkcheckin.show'))
->assertOk();
}
public function test_bulk_edit_routing_redirects_to_bulk_checkin_show()
{
$assets = Asset::factory()->assignedToUser()->count(2)->create();
$this->actingAs(User::factory()->checkinAssets()->viewAssets()->create())
->post(route('hardware.bulkedit.show'), [
'ids' => $assets->pluck('id')->toArray(),
'bulk_actions' => 'checkin',
'sort' => 'id',
'order' => 'asc',
])
->assertRedirectToRoute('hardware.bulkcheckin.show');
}
public function test_can_bulk_checkin_assets()
{
Event::fake([CheckoutableCheckedIn::class]);
$user = User::factory()->create();
$assets = Asset::factory()->assignedToUser($user)->count(2)->create([
'expected_checkin' => now()->addDay(),
'accepted' => 'accepted',
]);
$response = $this->actingAs(User::factory()->checkinAssets()->viewAssets()->create())
->post(route('hardware.bulkcheckin.store'), [
'selected_assets' => $assets->pluck('id')->toArray(),
])
->assertStatus(302);
$assets->each(function (Asset $asset) {
$fresh = $asset->fresh();
$this->assertNull($fresh->assigned_to);
$this->assertNull($fresh->assigned_type);
$this->assertNull($fresh->expected_checkin);
$this->assertNull($fresh->accepted);
$this->assertNotNull($fresh->last_checkin);
});
Event::assertDispatched(CheckoutableCheckedIn::class, 2);
$this->followRedirects($response)->assertSee('alert-success');
}
public function test_skips_assets_not_checked_out()
{
Event::fake([CheckoutableCheckedIn::class]);
$assignedAsset = Asset::factory()->assignedToUser()->create();
$unassignedAsset = Asset::factory()->create();
$this->actingAs(User::factory()->checkinAssets()->create())
->post(route('hardware.bulkcheckin.store'), [
'selected_assets' => [$assignedAsset->id, $unassignedAsset->id],
])
->assertStatus(302);
$this->assertNull($assignedAsset->fresh()->assigned_to);
$this->assertNotNull($unassignedAsset->fresh()->id);
Event::assertDispatched(CheckoutableCheckedIn::class, 1);
}
public function test_status_can_be_changed_upon_bulk_checkin()
{
$rtdStatus = Statuslabel::factory()->readyToDeploy()->create();
$assets = Asset::factory()->assignedToUser()->count(2)->create();
$this->actingAs(User::factory()->checkinAssets()->create())
->post(route('hardware.bulkcheckin.store'), [
'selected_assets' => $assets->pluck('id')->toArray(),
'status_id' => $rtdStatus->id,
]);
$assets->each(function (Asset $asset) use ($rtdStatus) {
$this->assertEquals($rtdStatus->id, $asset->fresh()->status_id);
});
}
public function test_location_is_set_to_rtd_location_upon_bulk_checkin()
{
$rtdLocation = Location::factory()->create();
$checkedOutLocation = Location::factory()->create();
$assets = Asset::factory()->assignedToUser()->count(2)->create([
'location_id' => $checkedOutLocation->id,
'rtd_location_id' => $rtdLocation->id,
]);
$this->actingAs(User::factory()->checkinAssets()->create())
->post(route('hardware.bulkcheckin.store'), [
'selected_assets' => $assets->pluck('id')->toArray(),
]);
$assets->each(function (Asset $asset) use ($rtdLocation) {
$this->assertEquals($rtdLocation->id, $asset->fresh()->location_id);
});
}
public function test_license_seats_are_cleared_when_checkbox_is_checked()
{
$asset = Asset::factory()->assignedToUser()->create();
LicenseSeat::factory()->assignedToUser()->for($asset)->create();
$this->assertNotNull($asset->licenseseats->first()->assigned_to);
$this->actingAs(User::factory()->checkinAssets()->create())
->post(route('hardware.bulkcheckin.store'), [
'selected_assets' => [$asset->id],
'checkin_licenses' => '1',
]);
$this->assertNull($asset->fresh()->licenseseats->first()->assigned_to);
}
public function test_license_seats_are_retained_when_checkbox_is_unchecked()
{
$asset = Asset::factory()->assignedToUser()->create();
LicenseSeat::factory()->assignedToUser()->for($asset)->create();
$originalAssignedTo = $asset->licenseseats->first()->assigned_to;
$this->assertNotNull($originalAssignedTo);
$this->actingAs(User::factory()->checkinAssets()->create())
->post(route('hardware.bulkcheckin.store'), [
'selected_assets' => [$asset->id],
'checkin_licenses' => '0',
]);
$this->assertEquals($originalAssignedTo, $asset->fresh()->licenseseats->first()->assigned_to);
}
public function test_pending_checkout_acceptances_are_cleared_upon_bulk_checkin()
{
$asset = Asset::factory()->assignedToUser()->create();
$acceptance = CheckoutAcceptance::factory()->for($asset, 'checkoutable')->pending()->create();
$this->actingAs(User::factory()->checkinAssets()->create())
->post(route('hardware.bulkcheckin.store'), [
'selected_assets' => [$asset->id],
]);
$this->assertFalse($acceptance->exists(), 'Pending acceptance was not deleted upon bulk checkin');
}
public function test_checkin_date_and_note_are_passed_to_event()
{
Event::fake([CheckoutableCheckedIn::class]);
$asset = Asset::factory()->assignedToUser()->create();
$this->actingAs(User::factory()->checkinAssets()->create())
->post(route('hardware.bulkcheckin.store'), [
'selected_assets' => [$asset->id],
'checkin_at' => '2024-06-15',
'note' => 'bulk checkin note',
]);
Event::assertDispatched(function (CheckoutableCheckedIn $event) {
return $event->action_date === '2024-06-15'
&& $event->note === 'bulk checkin note';
});
}
public function test_child_asset_locations_are_updated_when_checkbox_is_checked()
{
$originalLocation = Location::factory()->create();
$checkedOutLocation = Location::factory()->create();
$parentAsset = Asset::factory()->assignedToLocation($checkedOutLocation)->create([
'location_id' => $checkedOutLocation->id,
'rtd_location_id' => $originalLocation->id,
]);
$childAsset = Asset::factory()->create([
'assigned_to' => $parentAsset->id,
'assigned_type' => Asset::class,
'location_id' => $checkedOutLocation->id,
]);
$this->actingAs(User::factory()->checkinAssets()->create())
->post(route('hardware.bulkcheckin.store'), [
'selected_assets' => [$parentAsset->id],
'checkin_child_assets' => '1',
]);
$this->assertEquals($originalLocation->id, $parentAsset->fresh()->location_id);
$this->assertEquals($originalLocation->id, $childAsset->fresh()->location_id);
}
public function test_child_asset_locations_are_retained_when_checkbox_is_unchecked()
{
$originalLocation = Location::factory()->create();
$checkedOutLocation = Location::factory()->create();
$parentAsset = Asset::factory()->assignedToLocation($checkedOutLocation)->create([
'location_id' => $checkedOutLocation->id,
'rtd_location_id' => $originalLocation->id,
]);
$childAsset = Asset::factory()->create([
'assigned_to' => $parentAsset->id,
'assigned_type' => Asset::class,
'location_id' => $checkedOutLocation->id,
]);
$this->actingAs(User::factory()->checkinAssets()->create())
->post(route('hardware.bulkcheckin.store'), [
'selected_assets' => [$parentAsset->id],
'checkin_child_assets' => '0',
]);
$this->assertEquals($originalLocation->id, $parentAsset->fresh()->location_id);
$this->assertEquals($checkedOutLocation->id, $childAsset->fresh()->location_id);
}
public function test_origin_url_is_stored_when_routing_to_bulk_checkin()
{
$assets = Asset::factory()->assignedToUser()->count(2)->create();
$originUrl = route('hardware.index').'?status_type=Deployed';
$this->actingAs(User::factory()->checkinAssets()->viewAssets()->create())
->from($originUrl)
->post(route('hardware.bulkedit.show'), [
'ids' => $assets->pluck('id')->toArray(),
'bulk_actions' => 'checkin',
'sort' => 'id',
'order' => 'asc',
])
->assertSessionHas('url.intended', $originUrl);
}
public function test_successful_checkin_redirects_to_origin_url()
{
$asset = Asset::factory()->assignedToUser()->create();
$originUrl = route('hardware.index').'?status_type=Deployed';
$this->actingAs(User::factory()->checkinAssets()->create())
->withSession(['url.intended' => $originUrl])
->post(route('hardware.bulkcheckin.store'), [
'selected_assets' => [$asset->id],
])
->assertRedirect($originUrl);
}
public function test_external_referer_is_not_stored_as_intended_url()
{
$assets = Asset::factory()->assignedToUser()->count(2)->create();
$this->actingAs(User::factory()->checkinAssets()->viewAssets()->create())
->from('https://evil.example.com/phish')
->post(route('hardware.bulkedit.show'), [
'ids' => $assets->pluck('id')->toArray(),
'bulk_actions' => 'checkin',
'sort' => 'id',
'order' => 'asc',
])
->assertSessionMissing('url.intended');
}
public function test_successful_checkin_falls_back_to_hardware_index_without_origin_url()
{
$asset = Asset::factory()->assignedToUser()->create();
$this->actingAs(User::factory()->checkinAssets()->create())
->post(route('hardware.bulkcheckin.store'), [
'selected_assets' => [$asset->id],
])
->assertRedirect(route('hardware.index'));
}
public function test_returns_error_when_no_assets_selected()
{
$this->actingAs(User::factory()->checkinAssets()->create())
->post(route('hardware.bulkcheckin.store'), [
'selected_assets' => null,
])
->assertRedirectToRoute('hardware.bulkcheckin.show')
->assertSessionHas('error');
}
}