- Fix CI registry login (unauthorized): use dedicated REGISTRY_TOKEN secret instead of GITHUB_TOKEN, fail fast with a clear error if it's unset. - Pause ticket work-timer while a ticket is closed (won't auto-start on open or manual resume; stops on close via status change, quick action, API, or merge). - Reject empty/blank login submissions client- and server-side instead of passing them straight to the auth provider. - Closing a ticket now sends only the "ticket closed" notification instead of also sending a duplicate "status changed" one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -90,14 +90,14 @@ test('changing the subcategory fires category_changed, but re-saving details wit
|
||||
Notification::assertSentOnDemandTimes(TicketNotification::class, 1);
|
||||
});
|
||||
|
||||
test('closing a ticket fires both status_changed and ticket_closed', function () {
|
||||
test('closing a ticket fires only ticket_closed, not status_changed, so it does not double-notify', function () {
|
||||
Notification::fake();
|
||||
seedStatusesAndPriorities();
|
||||
NotificationSetting::query()->where('trigger_key', 'ticket_closed')->update(['enabled' => true]);
|
||||
|
||||
// status_changed ships enabled by default, but in a bare migrated (unseeded)
|
||||
// database it has no template assigned yet — give it one so both triggers
|
||||
// actually have something to send, isolating this test from seeding order.
|
||||
// database it has no template assigned yet — give it one so it would have
|
||||
// something to send if it (wrongly) fired, isolating this test from seeding order.
|
||||
$statusTemplate = EmailTemplate::query()->create([
|
||||
'key' => 'tpl-status-test', 'name' => 'Status', 'trigger_label' => 'x', 'subject' => 'S', 'body' => 'B',
|
||||
]);
|
||||
@@ -107,7 +107,23 @@ test('closing a ticket fires both status_changed and ticket_closed', function ()
|
||||
|
||||
app(TicketService::class)->setStatus($ticket, 'closed');
|
||||
|
||||
Notification::assertSentOnDemandTimes(TicketNotification::class, 2);
|
||||
Notification::assertSentOnDemandTimes(TicketNotification::class, 1);
|
||||
});
|
||||
|
||||
test('a non-closing status change still fires status_changed as usual', function () {
|
||||
Notification::fake();
|
||||
seedStatusesAndPriorities();
|
||||
|
||||
$statusTemplate = EmailTemplate::query()->create([
|
||||
'key' => 'tpl-status-test-2', 'name' => 'Status', 'trigger_label' => 'x', 'subject' => 'S', 'body' => 'B',
|
||||
]);
|
||||
NotificationSetting::query()->where('trigger_key', 'status_changed')->update(['email_template_id' => $statusTemplate->id]);
|
||||
|
||||
$ticket = makeTicket();
|
||||
|
||||
app(TicketService::class)->setStatus($ticket, 'open');
|
||||
|
||||
Notification::assertSentOnDemandTimes(TicketNotification::class, 1);
|
||||
});
|
||||
|
||||
test('an operator reply fires operator_replied once enabled, independent of any status change', function () {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<?php
|
||||
|
||||
use App\Ldap\LldapUser;
|
||||
use App\Livewire\Auth\Login;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Str;
|
||||
use LdapRecord\Laravel\Testing\DirectoryEmulator;
|
||||
use Livewire\Livewire;
|
||||
|
||||
afterEach(function () {
|
||||
DirectoryEmulator::tearDown();
|
||||
@@ -63,3 +65,22 @@ test('an unknown username does not authenticate', function () {
|
||||
|
||||
expect(Auth::attempt(['uid' => 'someone.else', 'password' => 'whatever']))->toBeFalse();
|
||||
});
|
||||
|
||||
test('submitting the login form with a blank username or password shows an error and never attempts to authenticate', function () {
|
||||
Livewire::test(Login::class)
|
||||
->set('username', '')
|
||||
->set('password', '')
|
||||
->call('submit')
|
||||
->assertSet('error', 'Podaj nazwę użytkownika i hasło.');
|
||||
|
||||
expect(Auth::check())->toBeFalse();
|
||||
|
||||
// Whitespace-only counts as blank for the username too.
|
||||
Livewire::test(Login::class)
|
||||
->set('username', ' ')
|
||||
->set('password', 'somepassword')
|
||||
->call('submit')
|
||||
->assertSet('error', 'Podaj nazwę użytkownika i hasło.');
|
||||
|
||||
expect(Auth::check())->toBeFalse();
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
use App\Livewire\Operator\TicketShow as OperatorTicketShow;
|
||||
use App\Services\TicketService;
|
||||
use Livewire\Livewire;
|
||||
|
||||
test('opening a ticket for the first time auto-starts the timer', function () {
|
||||
@@ -186,6 +187,47 @@ test('the stop-timer beacon endpoint checkpoints and stops a running timer', fun
|
||||
->and($ticket->time_spent_seconds)->toBe(50);
|
||||
});
|
||||
|
||||
test('opening a closed ticket does not auto-start the timer', function () {
|
||||
seedStatusesAndPriorities();
|
||||
$operator = operatorUser('timer-closed-open@example.com');
|
||||
$ticket = makeTicket(['status_key' => 'closed', 'time_spent_seconds' => 30, 'timer_started_at' => null]);
|
||||
|
||||
Livewire::actingAs($operator)->test(OperatorTicketShow::class, ['ticket' => $ticket]);
|
||||
|
||||
$ticket->refresh();
|
||||
expect($ticket->timer_started_at)->toBeNull()
|
||||
->and($ticket->time_spent_seconds)->toBe(30);
|
||||
});
|
||||
|
||||
test('manually resuming a closed ticket does not start the timer', function () {
|
||||
seedStatusesAndPriorities();
|
||||
$operator = operatorUser('timer-closed-resume@example.com');
|
||||
$ticket = makeTicket(['status_key' => 'closed', 'timer_started_at' => null]);
|
||||
|
||||
Livewire::actingAs($operator)->test(OperatorTicketShow::class, ['ticket' => $ticket])
|
||||
->call('resumeTimer');
|
||||
|
||||
expect($ticket->fresh()->timer_started_at)->toBeNull();
|
||||
});
|
||||
|
||||
test('closing a ticket via TicketService::setStatus checkpoints and stops a running timer', function () {
|
||||
seedStatusesAndPriorities();
|
||||
$this->travelTo(now());
|
||||
$operator = operatorUser('timer-close-via-status@example.com');
|
||||
$ticket = makeTicket();
|
||||
|
||||
Livewire::actingAs($operator)->test(OperatorTicketShow::class, ['ticket' => $ticket]);
|
||||
$ticket->refresh();
|
||||
|
||||
$this->travel(70)->seconds();
|
||||
|
||||
app(TicketService::class)->setStatus($ticket, 'closed');
|
||||
|
||||
$ticket->refresh();
|
||||
expect($ticket->timer_started_at)->toBeNull()
|
||||
->and($ticket->time_spent_seconds)->toBe(70);
|
||||
});
|
||||
|
||||
test('cancelling the timer edit leaves the tracked time untouched', function () {
|
||||
seedStatusesAndPriorities();
|
||||
$operator = operatorUser('timer-edit-cancel@example.com');
|
||||
|
||||
Reference in New Issue
Block a user