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"'); } }