3
0
mirror of https://github.com/snipe/snipe-it.git synced 2026-06-16 03:54:37 +00:00
Files
snipe-it/tests/Feature/Console/CheckinAndDeleteItemsTest.php
2026-05-15 00:43:18 +01:00

348 lines
12 KiB
PHP

<?php
namespace Tests\Feature\Console;
use App\Events\CheckoutableCheckedIn;
use App\Models\Accessory;
use App\Models\Asset;
use App\Models\Company;
use App\Models\Component;
use App\Models\License;
use App\Models\LicenseSeat;
use App\Models\User;
use Illuminate\Support\Facades\Event;
use Tests\TestCase;
class CheckinAndDeleteItemsTest extends TestCase
{
// ---------------------------------------------------------------------------
// Assets
// ---------------------------------------------------------------------------
public function test_checked_out_asset_is_checked_in_and_soft_deleted()
{
$user = User::factory()->create();
$asset = Asset::factory()->assignedToUser($user)->create();
$this->assertNotNull($asset->assigned_to);
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'assets', '--force' => true])
->assertExitCode(0);
$this->assertSoftDeleted($asset);
$this->assertNull($asset->fresh()->assigned_to);
}
public function test_unassigned_asset_is_soft_deleted_without_checkin_log()
{
$asset = Asset::factory()->create();
$this->assertNull($asset->assigned_to);
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'assets', '--force' => true])
->assertExitCode(0);
$this->assertSoftDeleted($asset);
}
public function test_asset_checkin_creates_action_log()
{
$user = User::factory()->create();
$asset = Asset::factory()->assignedToUser($user)->create();
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'assets', '--force' => true, '--no-notifications' => true])
->assertExitCode(0);
$this->assertHasTheseActionLogs($asset, ['create', 'checkin from', 'delete']);
}
public function test_event_is_fired_for_checked_out_asset_when_notifications_enabled()
{
Event::fake([CheckoutableCheckedIn::class]);
User::factory()->superuser()->create();
$asset = Asset::factory()->assignedToUser()->create();
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'assets', '--force' => true])
->assertExitCode(0);
Event::assertDispatched(CheckoutableCheckedIn::class, function ($event) use ($asset) {
return $event->checkoutable->is($asset);
});
}
public function test_admin_id_option_is_used_as_checked_in_by_user()
{
Event::fake([CheckoutableCheckedIn::class]);
$admin = User::factory()->superuser()->create();
$asset = Asset::factory()->assignedToUser()->create();
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'assets', '--force' => true, '--admin-id' => $admin->id])
->assertExitCode(0);
Event::assertDispatched(CheckoutableCheckedIn::class, function ($event) use ($admin) {
return $event->checkedInBy->is($admin);
});
}
public function test_invalid_admin_id_returns_exit_code_1()
{
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'assets', '--force' => true, '--admin-id' => 999999])
->assertExitCode(1);
}
public function test_event_is_not_fired_when_no_notifications_option_set()
{
Event::fake([CheckoutableCheckedIn::class]);
$asset = Asset::factory()->assignedToUser()->create();
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'assets', '--force' => true, '--no-notifications' => true])
->assertExitCode(0);
Event::assertNotDispatched(CheckoutableCheckedIn::class);
}
public function test_company_id_option_scopes_assets()
{
[$companyA, $companyB] = Company::factory()->count(2)->create();
$assetA = Asset::factory()->for($companyA)->assignedToUser()->create();
$assetB = Asset::factory()->for($companyB)->assignedToUser()->create();
$this->artisan('snipeit:checkin-delete-all', [
'--type' => 'assets',
'--company-id' => $companyA->id,
'--force' => true,
'--no-notifications' => true,
])->assertExitCode(0);
$this->assertSoftDeleted($assetA);
$this->assertNotSoftDeleted($assetB);
}
public function test_asset_license_seats_are_freed_on_checkin()
{
$user = User::factory()->create();
$asset = Asset::factory()->assignedToUser($user)->create();
$seat = LicenseSeat::factory()->create(['asset_id' => $asset->id, 'assigned_to' => $user->id]);
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'assets', '--force' => true, '--no-notifications' => true])
->assertExitCode(0);
$this->assertNull($seat->fresh()->assigned_to);
}
// ---------------------------------------------------------------------------
// Licenses
// ---------------------------------------------------------------------------
public function test_assigned_license_seat_is_checked_in_and_license_soft_deleted()
{
$license = License::factory()->create();
$seat = LicenseSeat::factory()->for($license)->assignedToUser()->create();
$this->assertNotNull($seat->assigned_to);
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'licenses', '--force' => true, '--no-notifications' => true])
->assertExitCode(0);
$this->assertSoftDeleted($license);
$this->assertNull($seat->fresh()->assigned_to);
$this->assertNull($seat->fresh()->asset_id);
}
public function test_unassigned_license_is_soft_deleted()
{
$license = License::factory()->create();
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'licenses', '--force' => true, '--no-notifications' => true])
->assertExitCode(0);
$this->assertSoftDeleted($license);
}
public function test_company_id_option_scopes_licenses()
{
[$companyA, $companyB] = Company::factory()->count(2)->create();
$licenseA = License::factory()->for($companyA)->create();
$licenseB = License::factory()->for($companyB)->create();
$this->artisan('snipeit:checkin-delete-all', [
'--type' => 'licenses',
'--company-id' => $companyA->id,
'--force' => true,
'--no-notifications' => true,
])->assertExitCode(0);
$this->assertSoftDeleted($licenseA);
$this->assertNotSoftDeleted($licenseB);
}
// ---------------------------------------------------------------------------
// Accessories
// ---------------------------------------------------------------------------
public function test_checked_out_accessory_is_checked_in_and_soft_deleted()
{
$accessory = Accessory::factory()->checkedOutToUser()->create();
$this->assertEquals(1, $accessory->checkouts->count());
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'accessories', '--force' => true, '--no-notifications' => true])
->assertExitCode(0);
$this->assertSoftDeleted($accessory);
$this->assertEquals(0, $accessory->fresh()->checkouts->count());
}
public function test_company_id_option_scopes_accessories()
{
[$companyA, $companyB] = Company::factory()->count(2)->create();
$accessoryA = Accessory::factory()->for($companyA)->checkedOutToUser()->create();
$accessoryB = Accessory::factory()->for($companyB)->checkedOutToUser()->create();
$this->artisan('snipeit:checkin-delete-all', [
'--type' => 'accessories',
'--company-id' => $companyA->id,
'--force' => true,
'--no-notifications' => true,
])->assertExitCode(0);
$this->assertSoftDeleted($accessoryA);
$this->assertNotSoftDeleted($accessoryB);
}
// ---------------------------------------------------------------------------
// Components
// ---------------------------------------------------------------------------
public function test_checked_out_component_is_checked_in_and_soft_deleted()
{
$component = Component::factory()->checkedOutToAsset()->create();
$this->assertDatabaseHas('components_assets', ['component_id' => $component->id]);
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'components', '--force' => true, '--no-notifications' => true])
->assertExitCode(0);
$this->assertSoftDeleted($component);
$this->assertDatabaseMissing('components_assets', ['component_id' => $component->id]);
}
public function test_company_id_option_scopes_components()
{
[$companyA, $companyB] = Company::factory()->count(2)->create();
$componentA = Component::factory()->for($companyA)->checkedOutToAsset()->create();
$componentB = Component::factory()->for($companyB)->checkedOutToAsset()->create();
$this->artisan('snipeit:checkin-delete-all', [
'--type' => 'components',
'--company-id' => $companyA->id,
'--force' => true,
'--no-notifications' => true,
])->assertExitCode(0);
$this->assertSoftDeleted($componentA);
$this->assertNotSoftDeleted($componentB);
}
// ---------------------------------------------------------------------------
// Type filtering and general behaviour
// ---------------------------------------------------------------------------
public function test_type_option_limits_processing_to_specified_type()
{
$asset = Asset::factory()->assignedToUser()->create();
$accessory = Accessory::factory()->checkedOutToUser()->create();
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'assets', '--force' => true, '--no-notifications' => true])
->assertExitCode(0);
$this->assertSoftDeleted($asset);
$this->assertNotSoftDeleted($accessory);
}
public function test_invalid_type_returns_exit_code_1()
{
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'invalid', '--force' => true])
->assertExitCode(1);
}
public function test_confirmation_is_required_without_force()
{
Asset::factory()->create();
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'assets'])
->expectsConfirmation('This will check in and soft-delete all [assets] for [all companies]. Continue?', 'no')
->assertExitCode(0);
$this->assertDatabaseCount('assets', 1);
}
public function test_force_option_skips_confirmation()
{
$asset = Asset::factory()->create();
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'assets', '--force' => true, '--no-notifications' => true])
->assertExitCode(0);
$this->assertSoftDeleted($asset);
}
public function test_dry_run_does_not_delete_or_checkin_assets()
{
$user = User::factory()->create();
$asset = Asset::factory()->assignedToUser($user)->create();
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'assets', '--dry-run' => true])
->assertExitCode(0);
$this->assertNotSoftDeleted($asset);
$this->assertNotNull($asset->fresh()->assigned_to);
}
public function test_dry_run_skips_confirmation_prompt()
{
Asset::factory()->create();
// Would hang waiting for input if confirmation were shown
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'assets', '--dry-run' => true])
->assertExitCode(0);
}
public function test_dry_run_outputs_would_be_messaging()
{
$asset = Asset::factory()->assignedToUser()->create();
$this->artisan('snipeit:checkin-delete-all', ['--type' => 'assets', '--dry-run' => true])
->expectsOutputToContain('DRY RUN')
->expectsOutputToContain('Would check in asset')
->expectsOutputToContain('Would delete asset')
->expectsOutputToContain('Dry run complete')
->assertExitCode(0);
}
public function test_custom_note_is_used_in_action_log()
{
$asset = Asset::factory()->assignedToUser()->create();
$this->artisan('snipeit:checkin-delete-all', [
'--type' => 'assets',
'--force' => true,
'--no-notifications' => true,
'--note' => 'Bulk removal for decommission',
])->assertExitCode(0);
$this->assertDatabaseHas('action_logs', [
'item_id' => $asset->id,
'action_type' => 'checkin from',
'note' => 'Bulk removal for decommission',
]);
}
}