3
0
mirror of https://github.com/snipe/snipe-it.git synced 2026-03-12 11:32:04 +00:00
Files
snipe-it/tests/Feature/Notifications/Email/AcceptanceReminderTest.php
2026-02-11 16:28:17 -08:00

264 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 testMustHavePermissionToSendReminder()
{
$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 testReminderNotSentIfAcceptanceDoesNotExist()
{
$this->actingAs($this->admin)
->post(route('reports/unaccepted_assets_sent_reminder', [
'acceptance_id' => 999999,
]));
Mail::assertNotSent(CheckoutAssetMail::class);
}
public function testReminderNotSentIfAcceptanceAlreadyAccepted()
{
$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 testUserWithoutEmailAddressHandledGracefully($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 testReminderIsSentToUserForAccessory()
{
$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 testReminderIsSentToUserForAsset()
{
$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 testReminderIsSentToUserForConsumable()
{
$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 testReminderIsSentToUserForLicenseSeat()
{
$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,
]);
}
}