3
0
mirror of https://github.com/snipe/snipe-it.git synced 2026-04-03 14:28:32 +00:00
Files
snipe-it/tests/Feature/Notifications/Email/AcceptanceReminderTest.php
2026-03-16 17:40:57 -07:00

265 lines
9.0 KiB
PHP

<?php
namespace Tests\Feature\Notifications\Email;
use App\Mail\CheckoutAccessoryMail;
use App\Mail\CheckoutAssetMail;
use App\Mail\CheckoutConsumableMail;
use App\Mail\CheckoutLicenseMail;
use App\Models\Accessory;
use App\Models\AccessoryCheckout;
use App\Models\Actionlog;
use App\Models\Asset;
use App\Models\CheckoutAcceptance;
use App\Models\Consumable;
use App\Models\License;
use App\Models\LicenseSeat;
use App\Models\User;
use Generator;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Mail;
use PHPUnit\Framework\Attributes\DataProvider;
use Tests\TestCase;
class AcceptanceReminderTest extends TestCase
{
private User $admin;
private User $assignee;
protected function setUp(): void
{
parent::setUp();
Mail::fake();
$this->admin = User::factory()->canViewReports()->create();
$this->assignee = User::factory()->create();
}
public function test_must_have_permission_to_send_reminder()
{
$checkoutAcceptance = CheckoutAcceptance::factory()->pending()->create();
$userWithoutPermission = User::factory()->create();
$this->actingAs($userWithoutPermission)
->post(route('reports/unaccepted_assets_sent_reminder', [
'acceptance_id' => $checkoutAcceptance->id,
]))
->assertForbidden();
Mail::assertNotSent(CheckoutAssetMail::class);
}
public function test_reminder_not_sent_if_acceptance_does_not_exist()
{
$this->actingAs($this->admin)
->post(route('reports/unaccepted_assets_sent_reminder', [
'acceptance_id' => 999999,
]));
Mail::assertNotSent(CheckoutAssetMail::class);
}
public function test_reminder_not_sent_if_acceptance_already_accepted()
{
$checkoutAcceptanceAlreadyAccepted = CheckoutAcceptance::factory()->accepted()->create();
$this->actingAs($this->admin)
->post(route('reports/unaccepted_assets_sent_reminder', [
'acceptance_id' => $checkoutAcceptanceAlreadyAccepted->id,
]));
Mail::assertNotSent(CheckoutAssetMail::class);
}
public static function checkoutAcceptancesToUsersWithoutEmailAddresses(): Generator
{
yield 'User with null email address' => [
function () {
return CheckoutAcceptance::factory()
->pending()
->forAssignedTo(['email' => null])
->create();
},
];
yield 'User with empty string email address' => [
function () {
return CheckoutAcceptance::factory()
->pending()
->forAssignedTo(['email' => ''])
->create();
},
];
}
#[DataProvider('checkoutAcceptancesToUsersWithoutEmailAddresses')]
public function test_user_without_email_address_handled_gracefully($callback)
{
$checkoutAcceptance = $callback();
$this->actingAs($this->admin)
->post(route('reports/unaccepted_assets_sent_reminder', [
'acceptance_id' => $checkoutAcceptance->id,
]))
// check we didn't crash...
->assertRedirect();
Mail::assertNotSent(CheckoutAssetMail::class);
}
public function test_reminder_is_sent_to_user_for_accessory()
{
$accessory = Accessory::factory()->requiringAcceptance()->create();
$acceptance = $this->createCheckoutAcceptance($accessory, $this->assignee);
$this->createActionLogEntry($accessory, $this->admin, $this->assignee, $acceptance);
AccessoryCheckout::factory()
->for($this->admin, 'adminuser')
->for($accessory)
->for($this->assignee, 'assignedTo')
->create();
$this->actingAs($this->admin)
->post(route('reports/unaccepted_assets_sent_reminder', [
'acceptance_id' => $acceptance->id,
]))
->assertRedirect(route('reports/unaccepted_assets'));
Mail::assertSent(CheckoutAccessoryMail::class, 1);
Mail::assertSent(CheckoutAccessoryMail::class, function (CheckoutAccessoryMail $mail) {
return $mail->hasTo($this->assignee);
});
Mail::assertSent(CheckoutAccessoryMail::class, function (CheckoutAccessoryMail $mail) {
return $mail->hasSubject(trans('mail.unaccepted_asset_reminder'));
});
Mail::assertSent(CheckoutAccessoryMail::class, function (CheckoutAccessoryMail $mail) {
return str_contains($mail->render(), trans('mail.recent_item_checked'));
});
}
public function test_reminder_is_sent_to_user_for_asset()
{
$asset = Asset::factory()->requiresAcceptance()->create();
$acceptance = $this->createCheckoutAcceptance($asset, $this->assignee);
$this->createActionLogEntry($asset, $this->admin, $this->assignee, $acceptance);
$this->actingAs($this->admin)
->post(route('reports/unaccepted_assets_sent_reminder', [
'acceptance_id' => $acceptance->id,
]))
->assertRedirect(route('reports/unaccepted_assets'));
Mail::assertSent(CheckoutAssetMail::class, 1);
Mail::assertSent(CheckoutAssetMail::class, function (CheckoutAssetMail $mail) {
return $mail->hasTo($this->assignee);
});
Mail::assertSent(CheckoutAssetMail::class, function (CheckoutAssetMail $mail) {
return $mail->hasSubject(trans('mail.unaccepted_asset_reminder'));
});
Mail::assertSent(CheckoutAssetMail::class, function (CheckoutAssetMail $mail) {
return str_contains($mail->render(), trans('mail.recent_item_checked'));
});
}
public function test_reminder_is_sent_to_user_for_consumable()
{
$consumable = Consumable::factory()->requiringAcceptance()->create();
$acceptance = $this->createCheckoutAcceptance($consumable, $this->assignee);
$this->createActionLogEntry($consumable, $this->admin, $this->assignee, $acceptance);
$this->actingAs($this->admin)
->post(route('reports/unaccepted_assets_sent_reminder', [
'acceptance_id' => $acceptance->id,
]))
->assertRedirect(route('reports/unaccepted_assets'));
Mail::assertSent(CheckoutConsumableMail::class, 1);
Mail::assertSent(CheckoutConsumableMail::class, function (CheckoutConsumableMail $mail) {
return $mail->hasTo($this->assignee);
});
Mail::assertSent(CheckoutConsumableMail::class, function (CheckoutConsumableMail $mail) {
return $mail->hasSubject(trans('mail.unaccepted_asset_reminder'));
});
Mail::assertSent(CheckoutConsumableMail::class, function (CheckoutConsumableMail $mail) {
return str_contains($mail->render(), trans('mail.recent_item_checked'));
});
}
public function test_reminder_is_sent_to_user_for_license_seat()
{
$licenseSeat = LicenseSeat::factory()->requiringAcceptance()->create();
$acceptance = $this->createCheckoutAcceptance($licenseSeat, $this->assignee);
$this->createActionLogEntry($licenseSeat, $this->admin, $this->assignee, $acceptance);
$this->actingAs($this->admin)
->post(route('reports/unaccepted_assets_sent_reminder', [
'acceptance_id' => $acceptance->id,
]))
->assertRedirect(route('reports/unaccepted_assets'));
Mail::assertSent(CheckoutLicenseMail::class, 1);
Mail::assertSent(CheckoutLicenseMail::class, function (CheckoutLicenseMail $mail) {
return $mail->hasTo($this->assignee);
});
Mail::assertSent(CheckoutLicenseMail::class, function (CheckoutLicenseMail $mail) {
return $mail->hasSubject(trans('mail.unaccepted_asset_reminder'));
});
Mail::assertSent(CheckoutLicenseMail::class, function (CheckoutLicenseMail $mail) {
return str_contains($mail->render(), trans('mail.recent_item_checked'));
});
}
private function createCheckoutAcceptance(Model $item, Model $assignee): CheckoutAcceptance
{
return CheckoutAcceptance::factory()
->for($item, 'checkoutable')
->for($assignee, 'assignedTo')
->withoutActionLog()
->pending()
->create();
}
private function createActionLogEntry(Model $item, Model $admin, Model $assignee, CheckoutAcceptance $acceptance): Actionlog
{
$itemId = $item->id;
$itemType = get_class($item);
if (get_class($item) === LicenseSeat::class) {
$itemId = $item->license->id;
$itemType = License::class;
}
return Actionlog::factory()
->for($admin, 'adminuser')
->for($assignee, 'target')
->create([
'action_type' => 'checkout',
'item_id' => $itemId,
'item_type' => $itemType,
'created_at' => $acceptance->created_at,
]);
}
}