payload; } } class FakeGetRequest extends MockIncomingRequest { public function __construct(private readonly array $gets = []) { parent::__construct(config(App::class), new URI('https://test.alrahmaisgl.org'), 'php://input', new UserAgent()); } public function getGet($key = null, $filter = null, $default = null) { if ($key === null) { return $this->gets; } return $this->gets[$key] ?? $default; } } class StubPager { public function __construct(private readonly int $current, private readonly int $perPage, private readonly int $total) { } public function getCurrentPage() { return $this->current; } public function getPerPage() { return $this->perPage; } public function getTotal() { return $this->total; } public function getPageCount() { return (int) ceil($this->total / max(1, $this->perPage)); } } class StubAssignmentModel { public array $whereHistory = []; public array $saved = []; public int $nextId = 10; public array $findAllResult = []; public array $findResult = []; public array $pagerData = ['current' => 1, 'perPage' => 20, 'total' => 0]; public function where(string $column, $value = null): self { $this->whereHistory[] = [$column => $value]; return $this; } public function paginate(int $perPage, string $group, int $page) { $this->pager = new StubPager($this->pagerData['current'], $this->pagerData['perPage'], $this->pagerData['total']); return $this->findAllResult; } public function find($id) { return $this->findResult[$id] ?? null; } public function insert(array $data) { $this->nextId++; $this->saved[$this->nextId] = $data; $this->findResult[$this->nextId] = ['id' => $this->nextId] + $data; return $this->nextId; } public function getInsertID() { return $this->nextId; } public function update($id, array $data) { $this->saved[$id] = $data; $this->findResult[$id] = ['id' => $id] + $data; return true; } public function delete($id) { unset($this->findResult[$id]); return true; } public function findAll() { return $this->findAllResult; } } class TestableAssignmentController extends AssignmentController { public bool $validationResult = true; public array $validationErrors = []; public $validator; public function __construct() { // skip real initialization } public function setAssignmentModel(StubAssignmentModel $model): void { $this->assignmentModel = $model; } public function setValidationResult(bool $result, array $errors = []): void { $this->validationResult = $result; $this->validationErrors = $errors; } protected function validate($rules, array $messages = []): bool { $this->validator = new class($this->validationErrors) { private array $errors; public function __construct(array $errors) { $this->errors = $errors; } public function getErrors() { return $this->errors; } }; return $this->validationResult; } protected function respond($data = null, ?int $status = null, string $message = '') { return ['data' => $data, 'code' => $status, 'message' => $message]; } } class AssignmentControllerTest extends CIUnitTestCase { private TestableAssignmentController $controller; private StubAssignmentModel $model; protected function setUp(): void { parent::setUp(); $this->controller = new TestableAssignmentController(); $this->model = new StubAssignmentModel(); $this->controller->setAssignmentModel($this->model); } public function testIndexAppliesFilters() { $request = new FakeGetRequest(['page' => '2', 'per_page' => '5', 'class_id' => '12', 'teacher_id' => '3', 'student_id' => '7']); $this->controller->setRequest($request); $this->model->findAllResult = [['id' => 1]]; $this->model->pagerData = ['current' => 2, 'perPage' => 5, 'total' => 11]; $response = $this->controller->index(); $this->assertSame('Assignments retrieved successfully', $response['message']); $this->assertSame(['id' => 1], $response['data']['data'][0]); $this->assertSame(2, $response['data']['pagination']['current_page']); $this->assertCount(3, $this->model->whereHistory); } public function testShowNotFound() { $response = $this->controller->show(99); $this->assertSame(ResponseInterface::HTTP_NOT_FOUND, $response['code']); } public function testShowSuccess() { $this->model->findResult[5] = ['id' => 5]; $response = $this->controller->show(5); $this->assertSame('Assignment retrieved successfully', $response['message']); $this->assertSame(['id' => 5], $response['data']); } public function testStoreValidationFails() { $this->controller->setValidationResult(false, ['title' => 'required']); $request = new FakeJsonRequest(['title' => '']); $this->controller->setRequest($request); $response = $this->controller->store(); $this->assertSame(422, $response['code']); $this->assertArrayHasKey('title', $response['errors']); } public function testStoreCreatesAssignment() { $request = new FakeJsonRequest([ 'title' => 'Test', 'description' => '', 'due_date' => '2025-01-01', 'class_id' => 1, 'teacher_id' => 2, ]); $this->controller->setRequest($request); $this->controller->setValidationResult(true); $response = $this->controller->store(); $this->assertSame('Assignment created successfully', $response['message']); $this->assertSame('Test', $this->model->saved[$this->model->getInsertID()]['title']); } public function testUpdateNotFound() { $response = $this->controller->update(11); $this->assertSame(ResponseInterface::HTTP_NOT_FOUND, $response['code']); } public function testUpdateModifiesAssignment() { $this->model->findResult[15] = ['id' => 15, 'title' => 'Old']; $request = new FakeJsonRequest(['title' => 'New']); $this->controller->setRequest($request); $this->controller->setValidationResult(true); $response = $this->controller->update(15); $this->assertSame('Assignment updated successfully', $response['message']); $this->assertSame('New', $this->model->findResult[15]['title']); } public function testDeleteFlow() { $this->model->findResult[20] = ['id' => 20]; $response = $this->controller->delete(20); $this->assertSame('Assignment deleted successfully', $response['message']); $this->assertArrayNotHasKey(20, $this->model->findResult); } public function testStudentReturnsList() { $this->model->findAllResult = [['id' => 1]]; $response = $this->controller->student(4); $this->assertSame('Assignments retrieved successfully for student', $response['message']); $this->assertNotEmpty($response['data']); } }