v1.0.0
This commit is contained in:
147
src/tests/Feature/RolesFieldValuesAndMessageAuthorTest.php
Normal file
147
src/tests/Feature/RolesFieldValuesAndMessageAuthorTest.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
use App\Livewire\Admin\Panel;
|
||||
use App\Livewire\Client\TicketShow as ClientTicketShow;
|
||||
use App\Livewire\Operator\TicketShow as OperatorTicketShow;
|
||||
use App\Models\Role;
|
||||
use App\Models\TicketMessageAuthor;
|
||||
use App\Models\User;
|
||||
use App\Models\UserField;
|
||||
use App\Models\UserFieldValue;
|
||||
use App\Services\TicketService;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Livewire\Livewire;
|
||||
|
||||
test('the roles table is seeded with client, operator and admin', function () {
|
||||
expect(Role::query()->pluck('key')->all())->toEqualCanonicalizing(['client', 'operator', 'admin']);
|
||||
});
|
||||
|
||||
test('a users roles are stored in the role_user pivot, not a JSON column', function () {
|
||||
$user = User::query()->create(['name' => 'Multi Role', 'email' => 'multirole@example.com', 'roles' => ['operator', 'admin']]);
|
||||
|
||||
expect(Schema::hasColumn('users', 'roles'))->toBeFalse()
|
||||
->and(DB::table('role_user')->where('user_id', $user->id)->count())->toBe(2)
|
||||
->and($user->fresh()->roles)->toEqualCanonicalizing(['operator', 'admin'])
|
||||
->and($user->isAdmin())->toBeTrue()
|
||||
->and($user->isOperator())->toBeTrue()
|
||||
->and($user->isClient())->toBeFalse();
|
||||
});
|
||||
|
||||
test('admin toggling a users role in the panel updates role_user, not a JSON column', function () {
|
||||
$admin = adminUser();
|
||||
$target = User::query()->create(['name' => 'Toggle Me', 'email' => 'toggle@example.com', 'roles' => ['client']]);
|
||||
|
||||
Livewire::actingAs($admin)->test(Panel::class)
|
||||
->call('toggleUserRole', $target->id, 'operator')
|
||||
->assertOk();
|
||||
|
||||
expect($target->fresh()->roles)->toEqualCanonicalizing(['client', 'operator']);
|
||||
});
|
||||
|
||||
test('a users custom field values are stored in user_field_values, not a JSON column', function () {
|
||||
$field = UserField::query()->create(['label' => 'Dział', 'type' => 'text', 'sort_order' => 1]);
|
||||
$user = User::query()->create([
|
||||
'name' => 'Field Test', 'email' => 'fieldtest@example.com', 'roles' => ['client'],
|
||||
'custom_field_values' => [$field->id => 'IT'],
|
||||
]);
|
||||
|
||||
expect(Schema::hasColumn('users', 'custom_field_values'))->toBeFalse()
|
||||
->and(UserFieldValue::query()->where('user_id', $user->id)->where('user_field_id', $field->id)->value('value'))->toBe('IT')
|
||||
->and($user->fresh()->custom_field_values)->toBe([$field->id => 'IT']);
|
||||
});
|
||||
|
||||
test('replacing a users field values drops the ones no longer present, matching the old JSON full-replace semantics', function () {
|
||||
$field = UserField::query()->create(['label' => 'Dział', 'type' => 'text', 'sort_order' => 1]);
|
||||
$user = User::query()->create([
|
||||
'name' => 'Field Test 2', 'email' => 'fieldtest2@example.com', 'roles' => ['client'],
|
||||
'custom_field_values' => [$field->id => 'IT'],
|
||||
]);
|
||||
|
||||
$user->update(['custom_field_values' => []]);
|
||||
|
||||
expect(UserFieldValue::query()->where('user_id', $user->id)->exists())->toBeFalse();
|
||||
});
|
||||
|
||||
test('a client reply links the message to its author and the client role via ticket_message_authors', function () {
|
||||
seedStatusesAndPriorities();
|
||||
$client = User::query()->create(['name' => 'Klient', 'email' => 'klient-msg@example.com', 'roles' => ['client']]);
|
||||
$ticket = makeTicket(['customer_id' => $client->id, 'email' => $client->email, 'name' => $client->name]);
|
||||
|
||||
app(TicketService::class)->clientReply($ticket, $client, 'Odpowiedź klienta');
|
||||
|
||||
$message = $ticket->messages()->latest('id')->first();
|
||||
|
||||
expect(Schema::hasColumn('ticket_messages', 'author_id'))->toBeFalse()
|
||||
->and(Schema::hasColumn('ticket_messages', 'role'))->toBeFalse()
|
||||
->and($message->role)->toBe('client')
|
||||
->and($message->author_id)->toBe($client->id)
|
||||
->and($message->author->id)->toBe($client->id);
|
||||
});
|
||||
|
||||
test('an operator reply and note link to the operator role', function () {
|
||||
seedStatusesAndPriorities();
|
||||
$operator = operatorUser('op-msg@example.com');
|
||||
$ticket = makeTicket();
|
||||
|
||||
app(TicketService::class)->operatorReply($ticket, $operator, 'Odpowiedź operatora');
|
||||
app(TicketService::class)->operatorNote($ticket, $operator, 'Notatka wewnętrzna');
|
||||
|
||||
$reply = $ticket->messages()->where('internal', false)->latest('id')->first();
|
||||
$note = $ticket->messages()->where('internal', true)->latest('id')->first();
|
||||
|
||||
expect($reply->role)->toBe('operator')->and($reply->author_id)->toBe($operator->id)
|
||||
->and($note->role)->toBe('operator')->and($note->author_id)->toBe($operator->id);
|
||||
});
|
||||
|
||||
test('a system-generated message (merge announcement) has no author and role defaults to "system"', function () {
|
||||
seedStatusesAndPriorities();
|
||||
$primary = makeTicket(['number' => '2001']);
|
||||
$other = makeTicket(['number' => '2002']);
|
||||
|
||||
app(TicketService::class)->merge([$primary->id, $other->id]);
|
||||
|
||||
$announcement = $primary->messages()->where('body', 'like', '%Scalono zgłoszenia%')->firstOrFail();
|
||||
|
||||
expect($announcement->role)->toBe('system')
|
||||
->and($announcement->author_id)->toBeNull()
|
||||
->and(TicketMessageAuthor::query()->where('ticket_message_id', $announcement->id)->exists())->toBeFalse();
|
||||
});
|
||||
|
||||
test('a client can edit their own message but not an operators message on the same ticket', function () {
|
||||
seedStatusesAndPriorities();
|
||||
$client = User::query()->create(['name' => 'Owner', 'email' => 'owner@example.com', 'roles' => ['client']]);
|
||||
$operator = operatorUser('op-on-client-ticket@example.com');
|
||||
$ticket = makeTicket(['customer_id' => $client->id, 'email' => $client->email, 'name' => $client->name]);
|
||||
|
||||
app(TicketService::class)->clientReply($ticket, $client, 'Treść klienta');
|
||||
$ownMessage = $ticket->messages()->latest('id')->first();
|
||||
|
||||
app(TicketService::class)->operatorReply($ticket, $operator, 'Odpowiedź operatora');
|
||||
$operatorMessage = $ticket->messages()->latest('id')->first();
|
||||
|
||||
Livewire::actingAs($client)->test(ClientTicketShow::class, ['ticket' => $ticket])
|
||||
->call('startEdit', $ownMessage->id, 'zmieniona treść')
|
||||
->assertSet('editingMessageId', $ownMessage->id);
|
||||
|
||||
// abort_unless() inside an action call doesn't translate into a clean
|
||||
// HTTP response under Livewire's test harness (it disables the app's
|
||||
// exception handling for ->call()), so assert on the instance directly.
|
||||
$component = Livewire::actingAs($client)->test(ClientTicketShow::class, ['ticket' => $ticket])->instance();
|
||||
|
||||
expect(fn () => $component->startEdit($operatorMessage->id, 'zmieniona treść'))
|
||||
->toThrow(Symfony\Component\HttpKernel\Exception\HttpException::class);
|
||||
});
|
||||
|
||||
test('an operator can edit an operator-authored message', function () {
|
||||
seedStatusesAndPriorities();
|
||||
$operator = operatorUser('op-edit@example.com');
|
||||
$ticket = makeTicket();
|
||||
|
||||
app(TicketService::class)->operatorReply($ticket, $operator, 'Treść operatora');
|
||||
$message = $ticket->messages()->latest('id')->first();
|
||||
|
||||
Livewire::actingAs($operator)->test(OperatorTicketShow::class, ['ticket' => $ticket])
|
||||
->call('startEditMessage', $message->id, 'zmieniona treść')
|
||||
->assertSet('editingMessageId', $message->id);
|
||||
});
|
||||
Reference in New Issue
Block a user