$name, 'data' => $data, 'options' => $options]; } } if (!function_exists('local_date')) { function local_date($timestamp, $format) { if ($timestamp instanceof \DateTimeInterface) { return $timestamp->format($format); } if (is_numeric($timestamp)) { return date($format, (int) $timestamp); } return date($format); } } } namespace Tests\App\Controllers\View { use App\Controllers\View\AdministratorController; use App\Models\UserModel; use CodeIgniter\HTTP\RedirectResponse; use CodeIgniter\Test\CIUnitTestCase; use Config\Services; class DummyPostRequest { public function __construct(private array $post = []) { } public function getPost($key = null) { if ($key === null) { return $this->post; } return $this->post[$key] ?? null; } } class FakeRenderer { public array $lastData = []; public string $lastName = ''; public function setData(array $data, string $type = 'raw') { $this->lastData = $data; return $this; } public function render(string $name, array $options = [], bool $saveData = false) { $this->lastName = $name; return 'fake-render'; } } class FakeAttendanceModel { public function __construct(private array $existing = []) { } public function where(...$args) { return $this; } public function orderBy(...$args) { return $this; } public function findAll() { return $this->existing; } public function upsertOne(...$args) { return true; } } class TestableAdministratorController extends AdministratorController { public function __construct() { // Skip expensive parent constructor to avoid real DB/models. } public function setUserModel(UserModel $model): self { $this->userModel = $model; return $this; } public function setStaffAttendanceModel($model): self { $this->staffAttendanceModel = $model; return $this; } public function setSemester(string $semester): self { $this->semester = $semester; return $this; } public function setSchoolYear(string $schoolYear): self { $this->schoolYear = $schoolYear; return $this; } public function setRequest($request): self { $this->request = $request; return $this; } } class AdministratorControllerTest extends CIUnitTestCase { private TestableAdministratorController $controller; private FakeRenderer $renderer; protected function setUp(): void { parent::setUp(); Services::resetSingle('session'); helper('date'); session()->start(); $this->controller = new TestableAdministratorController(); $this->controller->setSemester('Fall')->setSchoolYear('2024-2025'); $this->renderer = new FakeRenderer(); Services::injectMock('renderer', $this->renderer); } protected function tearDown(): void { session()->destroy(); parent::tearDown(); } public function testAbsenceFormRedirectsWhenNotLoggedIn() { session()->remove('user_id'); $response = $this->controller->absenceFormAdmin(); $this->assertInstanceOf(RedirectResponse::class, $response); $this->assertStringContainsString('/login', $response->getHeaderLine('Location') ?: ''); } public function testAbsenceFormBuildsViewWithAttendance() { session()->set('user_id', 7); $userModel = $this->createMock(UserModel::class); $userModel->method('find')->with(7)->willReturn([ 'firstname' => 'Alex', 'lastname' => 'Admin', ]); $attendance = new FakeAttendanceModel([ ['date' => '2024-12-01'], ]); $this->controller->setUserModel($userModel)->setStaffAttendanceModel($attendance); $result = $this->controller->absenceFormAdmin(); $this->assertSame('fake-render', $result); $this->assertSame('administrator/absence_vacation', $this->renderer->lastName); $data = $this->renderer->lastData; $this->assertSame('Alex Admin', $data['admin_name']); $this->assertSame('Fall', $data['semester']); $this->assertSame('2024-2025', $data['schoolYear']); $this->assertSame([['date' => '2024-12-01']], $data['existing']); $this->assertIsArray($data['availableDates']); } public function testSubmitAbsenceRejectsMissingReason() { session()->set('user_id', 9); $req = new DummyPostRequest([ 'dates' => ['2024-12-01'], 'reason_type' => '', 'reason' => '', ]); $this->controller->setRequest($req); $response = $this->controller->submitAbsenceAdmin(); $this->assertInstanceOf(RedirectResponse::class, $response); $this->assertStringContainsString('/administrator/absence', $response->getHeaderLine('Location') ?: ''); $this->assertSame('Reason is required.', session()->getFlashdata('message')); } public function testSubmitAbsenceSavesAndRedirects() { session()->set('user_id', 10); $userModel = $this->createMock(UserModel::class); $userModel->method('getUserRole')->with(10)->willReturn('administrator'); $userModel->method('find')->with(10)->willReturn([ 'firstname' => 'Sam', 'lastname' => 'Admin', 'email' => 'sam@school.test', ]); $attendance = new FakeAttendanceModel(); $this->controller->setUserModel($userModel)->setStaffAttendanceModel($attendance); $method = new \ReflectionMethod($this->controller, 'allowedAbsenceDates'); $method->setAccessible(true); $allowed = $method->invoke($this->controller); if (empty($allowed)) { $this->markTestSkipped('No allowed absence dates are currently available.'); } $date = $allowed[0]; $req = new DummyPostRequest([ 'dates' => [$date], 'reason_type' => 'Personal', 'reason' => 'Conference', ]); $this->controller->setRequest($req); $emailService = $this->createMock(\App\Services\EmailService::class); $emailService->method('send')->willReturn(true); Services::injectMock('emailService', $emailService); $response = $this->controller->submitAbsenceAdmin(); $this->assertInstanceOf(RedirectResponse::class, $response); $this->assertStringContainsString('/administrator/absence', $response->getHeaderLine('Location') ?: ''); $this->assertSame('success', session()->getFlashdata('status')); $this->assertStringContainsString('saved', (string) session()->getFlashdata('message')); } } }