whereHistory[] = ['column' => $column, 'value' => $value]; return $this; } public function first(): ?array { return $this->firstResult; } public function update(int $id, array $data): bool { $this->lastUpdate = ['id' => $id, 'data' => $data]; return true; } public function setFirstResult(?array $result): void { $this->firstResult = $result; } } class FakeNotificationRequest extends MockIncomingRequest { public function __construct(private readonly array $params = []) { parent::__construct(config(App::class), new URI('https://test.alrahmaisgl.org'), null, new UserAgent()); } public function getGet($key = null, $filter = null, $default = null) { if ($key === null) { return $this->params; } return $this->params[$key] ?? $default; } } class TestableNotificationController extends NotificationController { private array $overridePagination = []; private ?object $currentUser = null; public function __construct() { // Skip normal boot to keep dependencies under test control. } public function setModels(StubUserNotificationModel $model): void { $this->userNotificationModel = $model; } public function setCurrentUser(object $user): void { $this->currentUser = $user; } public function setPaginatedResult(array $result): void { $this->overridePagination = $result; } protected function paginate($source, int $page = 1, int $perPage = 20): array { if (!empty($this->overridePagination)) { return $this->overridePagination; } return parent::paginate($source, $page, $perPage); } protected function getCurrentUser(): ?object { return $this->currentUser; } } class NotificationControllerTest extends CIUnitTestCase { private TestableNotificationController $controller; private StubUserNotificationModel $model; protected function setUp(): void { parent::setUp(); $this->controller = new TestableNotificationController(); $this->model = new StubUserNotificationModel(); $this->controller->setModels($this->model); $this->controller->setCurrentUser((object) ['id' => 42]); } public function testIndexReturnsPaginatedNotifications() { $payload = ['data' => [['id' => 1]], 'pagination' => ['total' => 1]]; $this->controller->setPaginatedResult($payload); $request = new FakeNotificationRequest(); $this->controller->setRequest($request); $response = $this->controller->index(); $this->assertTrue($response['status']); $this->assertSame($payload, $response['data']); $this->assertSame('Notifications retrieved successfully', $response['message']); } public function testIndexRespectsReadFilter() { $payload = ['data' => [], 'pagination' => ['total' => 0]]; $this->controller->setPaginatedResult($payload); $request = new FakeNotificationRequest(['read' => 'false']); $this->controller->setRequest($request); $this->controller->index(); $readWhere = array_filter( $this->model->whereHistory, static fn ($entry) => $entry['column'] === 'user_notifications.is_read' ); $this->assertNotEmpty($readWhere); $this->assertSame(0, $readWhere[array_key_last($readWhere)]['value']); } public function testShowReturnsNotification() { $this->model->setFirstResult(['id' => 99, 'notification_id' => 99]); $response = $this->controller->show(99); $this->assertTrue($response['status']); $this->assertSame('Notification retrieved successfully', $response['message']); } public function testShowReturns404WhenMissing() { $this->model->setFirstResult(null); $response = $this->controller->show(9); $this->assertFalse($response['status']); $this->assertSame(404, $response['code']); } public function testMarkReadUpdatesNotification() { $this->model->setFirstResult(['id' => 50, 'notification_id' => 500]); $response = $this->controller->markRead(500); $this->assertTrue($response['status']); $this->assertSame('Notification marked as read', $response['message']); $this->assertSame(50, $this->model->lastUpdate['id']); $this->assertSame(1, $this->model->lastUpdate['data']['is_read']); $this->assertNotEmpty($this->model->lastUpdate['data']['read_at']); } public function testMarkReadReturns404WhenNotFound() { $this->model->setFirstResult(null); $response = $this->controller->markRead(77); $this->assertFalse($response['status']); $this->assertSame(404, $response['code']); } }