Files
L-Ami-Fiduciaire/tests/Feature/Notifications/NudgeControllerTest.php

220 lines
7.5 KiB
PHP
Raw Normal View History

<?php
use App\Models\Declaration;
use App\Models\User;
use App\Models\Workspace;
use App\Notifications\NudgeNotification;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Notification;
use Spatie\Activitylog\Models\Activity;
function setupNudgeTest(string $senderRole = 'owner'): array
{
$sender = User::factory()->create();
$worker = User::factory()->create();
$workspace = Workspace::factory()->create();
$workspace->users()->attach($sender->id, ['role' => $senderRole, 'permissions' => []]);
$workspace->users()->attach($worker->id, ['role' => 'worker', 'permissions' => []]);
$declaration = Declaration::factory()->forWorkspace($workspace)->create([
'assigned_to' => $worker->id,
]);
session(['current_workspace_id' => $workspace->id]);
return [$sender, $worker, $workspace, $declaration];
}
// ── AC2, AC5, AC7: Owner can send nudge ──────────────────
test('owner can send nudge — notification dispatched, activity logged, success flash', function () {
Notification::fake();
[$sender, $worker, $workspace, $declaration] = setupNudgeTest('owner');
$response = $this->actingAs($sender)->post(route('declarations.nudge', $declaration));
$response->assertRedirect();
$response->assertSessionHas('flash', [
'type' => 'success',
'message' => 'Relance envoyée à '.$worker->name,
]);
Notification::assertSentTo($worker, NudgeNotification::class);
});
// ── AC2, AC5: Manager can send nudge ─────────────────────
test('manager can send nudge — same behavior as owner', function () {
Notification::fake();
[$sender, $worker, $workspace, $declaration] = setupNudgeTest('manager');
$response = $this->actingAs($sender)->post(route('declarations.nudge', $declaration));
$response->assertRedirect();
$response->assertSessionHas('flash', [
'type' => 'success',
'message' => 'Relance envoyée à '.$worker->name,
]);
Notification::assertSentTo($worker, NudgeNotification::class);
});
// ── AC5: Worker cannot send nudge ────────────────────────
test('worker cannot send nudge — abort 404', function () {
Notification::fake();
[$sender, $worker, $workspace, $declaration] = setupNudgeTest('owner');
$response = $this->actingAs($worker)->post(route('declarations.nudge', $declaration));
$response->assertNotFound();
Notification::assertNothingSent();
});
// ── AC5: Nudge on declaration in wrong workspace ─────────
test('nudge on declaration in wrong workspace — abort 404', function () {
Notification::fake();
[$sender, $worker, $workspace, $declaration] = setupNudgeTest('owner');
// Create a different workspace and set it as current
$otherWorkspace = Workspace::factory()->create();
$otherWorkspace->users()->attach($sender->id, ['role' => 'owner', 'permissions' => []]);
session(['current_workspace_id' => $otherWorkspace->id]);
$response = $this->actingAs($sender)->post(route('declarations.nudge', $declaration));
$response->assertNotFound();
Notification::assertNothingSent();
});
// ── Nudge on declaration with no assignee ────────────────
test('nudge on declaration with no assignee — returns error', function () {
Notification::fake();
[$sender, $worker, $workspace, $declaration] = setupNudgeTest('owner');
$declaration->update(['assigned_to' => null]);
$response = $this->actingAs($sender)->post(route('declarations.nudge', $declaration));
$response->assertRedirect();
$response->assertSessionHas('flash', function (array $flash) {
return $flash['type'] === 'warning';
});
Notification::assertNothingSent();
});
// ── AC8: Duplicate nudge within 1 hour — debounce ────────
test('duplicate nudge within 1 hour — debounce prevents, returns warning flash', function () {
Notification::fake();
[$sender, $worker, $workspace, $declaration] = setupNudgeTest('owner');
// Create a recent nudge notification manually in the database
$worker->notifications()->create([
'id' => \Illuminate\Support\Str::uuid(),
'type' => NudgeNotification::class,
'data' => [
'workspace_id' => $workspace->id,
'declaration_id' => $declaration->id,
'sender_id' => $sender->id,
],
'created_at' => now()->subMinutes(30),
]);
$response = $this->actingAs($sender)->post(route('declarations.nudge', $declaration));
$response->assertRedirect();
$response->assertSessionHas('flash', [
'type' => 'warning',
'message' => 'Relance déjà envoyée récemment',
]);
Notification::assertNothingSent();
});
// ── AC8: Nudge after 1 hour — allowed ────────────────────
test('nudge after 1 hour — allowed (debounce expired)', function () {
Notification::fake();
[$sender, $worker, $workspace, $declaration] = setupNudgeTest('owner');
// Create an old nudge notification (more than 1 hour ago)
$worker->notifications()->create([
'id' => \Illuminate\Support\Str::uuid(),
'type' => NudgeNotification::class,
'data' => [
'workspace_id' => $workspace->id,
'declaration_id' => $declaration->id,
'sender_id' => $sender->id,
],
'created_at' => now()->subMinutes(61),
]);
$response = $this->actingAs($sender)->post(route('declarations.nudge', $declaration));
$response->assertRedirect();
$response->assertSessionHas('flash', [
'type' => 'success',
'message' => 'Relance envoyée à '.$worker->name,
]);
Notification::assertSentTo($worker, NudgeNotification::class);
});
// ── AC7: Activity log entry created ──────────────────────
test('activity log entry created with correct actor/target/action', function () {
Notification::fake();
[$sender, $worker, $workspace, $declaration] = setupNudgeTest('owner');
$this->actingAs($sender)->post(route('declarations.nudge', $declaration));
$activity = Activity::query()
->where('subject_type', Declaration::class)
->where('subject_id', $declaration->id)
->where('causer_id', $sender->id)
->where('description', 'nudged')
->first();
expect($activity)->not->toBeNull();
expect($activity->causer_id)->toBe($sender->id);
expect($activity->subject_id)->toBe($declaration->id);
});
// ── Unauthenticated user cannot nudge ────────────────────
test('unauthenticated user cannot nudge — redirected to login', function () {
$declaration = Declaration::factory()->create();
$response = $this->post(route('declarations.nudge', $declaration));
$response->assertRedirect(route('login'));
});
// ── Cache cleared for assignee after nudge ───────────────
test('notification cache cleared for assignee after nudge', function () {
Notification::fake();
[$sender, $worker, $workspace, $declaration] = setupNudgeTest('owner');
// Pre-populate cache
Cache::put("user:{$worker->id}:workspace:{$workspace->id}:unread_notifications", 5);
$this->actingAs($sender)->post(route('declarations.nudge', $declaration));
expect(Cache::has("user:{$worker->id}:workspace:{$workspace->id}:unread_notifications"))->toBeFalse();
});