feat: phase 5 — complaints with ownership-guarded resolution and activity logging
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Complaints;
|
||||
|
||||
use App\Filament\Pages\Transactions\Complaints as ComplaintsPage;
|
||||
use App\Models\User;
|
||||
use App\Services\ComplaintService;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Livewire\Livewire;
|
||||
use Tests\Support\InsForgeFake;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ComplaintTest extends TestCase
|
||||
{
|
||||
private const CUSTOMER_ID = 'cccccccc-0000-0000-0000-000000000001';
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
InsForgeFake::reset();
|
||||
InsForgeFake::fakeRest();
|
||||
|
||||
InsForgeFake::seed('customers', [
|
||||
'id' => self::CUSTOMER_ID,
|
||||
'customer_code' => 'CUST-0001',
|
||||
'name' => 'Andi',
|
||||
]);
|
||||
}
|
||||
|
||||
private function actor(string $role, string $suffix = '0001'): User
|
||||
{
|
||||
$user = new User;
|
||||
|
||||
$user->forceFill([
|
||||
'id' => "eeee7777-0000-0000-0000-{$role}{$suffix}",
|
||||
'name' => ucfirst($role),
|
||||
'email' => "{$role}@beraspro.test",
|
||||
'role' => $role,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
private function seedComplaint(array $overrides = []): array
|
||||
{
|
||||
$row = array_merge([
|
||||
'id' => 'bbbb8888-0000-0000-0000-000000000001',
|
||||
'complaint_number' => 'CMP-20260825-0001',
|
||||
'customer_id' => self::CUSTOMER_ID,
|
||||
'complaint_date' => '2026-08-25',
|
||||
'category' => 'product',
|
||||
'description' => 'Beras hitam',
|
||||
'status' => ComplaintService::STATUS_OPEN,
|
||||
'priority' => 'high',
|
||||
'created_by' => $this->actor('operator')->id,
|
||||
'customers' => ['code' => 'CUST-0001', 'name' => 'Andi'],
|
||||
], $overrides);
|
||||
|
||||
InsForgeFake::seed('complaints', $row);
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
public function test_create_generates_number_and_log(): void
|
||||
{
|
||||
$result = app(ComplaintService::class)->createComplaint([
|
||||
'customer_id' => self::CUSTOMER_ID,
|
||||
'description' => 'Kemasan rusak',
|
||||
'category' => 'product',
|
||||
'priority' => 'low',
|
||||
], $this->actor('operator'));
|
||||
|
||||
$this->assertSame('CMP-'.now()->format('Ymd').'-0001', $result['complaint_number']);
|
||||
|
||||
$stored = array_values(InsForgeFake::$store['complaints'])[0];
|
||||
$this->assertSame(ComplaintService::STATUS_OPEN, $stored['status']);
|
||||
|
||||
$log = end(InsForgeFake::$store['activity_logs']);
|
||||
$this->assertSame('complaint_created', $log['action']);
|
||||
}
|
||||
|
||||
public function test_creator_can_resolve_own_complaint(): void
|
||||
{
|
||||
$creator = $this->actor('operator');
|
||||
$complaint = $this->seedComplaint(['created_by' => $creator->id]);
|
||||
|
||||
app(ComplaintService::class)->resolve($complaint['id'], 'ganti produk baru', $creator);
|
||||
|
||||
$stored = InsForgeFake::$store['complaints'][$complaint['id']];
|
||||
$this->assertSame(ComplaintService::STATUS_RESOLVED, $stored['status']);
|
||||
$this->assertSame('ganti produk baru', $stored['resolution']);
|
||||
$this->assertNotNull($stored['resolved_at']);
|
||||
}
|
||||
|
||||
public function test_other_operator_cannot_resolve(): void
|
||||
{
|
||||
$complaint = $this->seedComplaint();
|
||||
|
||||
$this->expectException(AuthorizationException::class);
|
||||
app(ComplaintService::class)->resolve($complaint['id'], 'coba tutup', $this->actor('operator', '9999'));
|
||||
}
|
||||
|
||||
public function test_admin_can_resolve_any_complaint(): void
|
||||
{
|
||||
$complaint = $this->seedComplaint();
|
||||
|
||||
app(ComplaintService::class)->resolve($complaint['id'], 'direfund', $this->actor('admin'));
|
||||
|
||||
$this->assertSame(ComplaintService::STATUS_RESOLVED, InsForgeFake::$store['complaints'][$complaint['id']]['status']);
|
||||
}
|
||||
|
||||
public function test_already_resolved_cannot_be_resolved_again(): void
|
||||
{
|
||||
$complaint = $this->seedComplaint(['status' => ComplaintService::STATUS_RESOLVED, 'resolution' => 'ok']);
|
||||
|
||||
$this->expectException(ValidationException::class);
|
||||
app(ComplaintService::class)->resolve($complaint['id'], 'lagi', $this->actor('admin'));
|
||||
}
|
||||
|
||||
public function test_page_renders_and_shows_complaint(): void
|
||||
{
|
||||
$this->actingAs($this->actor('operator'));
|
||||
|
||||
$this->seedComplaint();
|
||||
|
||||
Livewire::test(ComplaintsPage::class)
|
||||
->assertSuccessful()
|
||||
->assertSee('CMP-20260825-0001')
|
||||
->assertSee('Beras hitam');
|
||||
}
|
||||
|
||||
public function test_close_action_hidden_for_non_owner_operator(): void
|
||||
{
|
||||
$user = $this->actor('operator', '9999');
|
||||
$this->actingAs($user);
|
||||
|
||||
$complaint = $this->seedComplaint();
|
||||
|
||||
Livewire::test(ComplaintsPage::class)
|
||||
->assertSuccessful()
|
||||
->assertDontSeeHtml('wire:key="writable-actions-tutup"');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user