Files
2026-02-10 22:11:06 -05:00

258 lines
7.3 KiB
PHP

<?php
namespace {
if (!function_exists('view')) {
function view($name, array $data = [], $options = [])
{
return ['view' => $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'));
}
}
}