recreate project

This commit is contained in:
root
2026-02-10 22:11:06 -05:00
commit 663c0cdbda
10149 changed files with 1379710 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
<IfModule authz_core_module>
Require all denied
</IfModule>
<IfModule !authz_core_module>
Deny from all
</IfModule>
+118
View File
@@ -0,0 +1,118 @@
# Running Application Tests
This is the quick-start to CodeIgniter testing. Its intent is to describe what
it takes to set up your application and get it ready to run unit tests.
It is not intended to be a full description of the test features that you can
use to test your application. Those details can be found in the documentation.
## Resources
* [CodeIgniter 4 User Guide on Testing](https://codeigniter.com/user_guide/testing/index.html)
* [PHPUnit docs](https://phpunit.de/documentation.html)
* [Any tutorials on Unit testing in CI4?](https://forum.codeigniter.com/showthread.php?tid=81830)
## Requirements
It is recommended to use the latest version of PHPUnit. At the time of this
writing, we are running version 9.x. Support for this has been built into the
**composer.json** file that ships with CodeIgniter and can easily be installed
via [Composer](https://getcomposer.org/) if you don't already have it installed globally.
```console
> composer install
```
If running under macOS or Linux, you can create a symbolic link to make running tests a touch nicer.
```console
> ln -s ./vendor/bin/phpunit ./phpunit
```
You also need to install [XDebug](https://xdebug.org/docs/install) in order
for code coverage to be calculated successfully. After installing `XDebug`, you must add `xdebug.mode=coverage` in the **php.ini** file to enable code coverage.
## Setting Up
A number of the tests use a running database.
In order to set up the database edit the details for the `tests` group in
**app/Config/Database.php** or **.env**.
Make sure that you provide a database engine that is currently running on your machine.
More details on a test database setup are in the
[Testing Your Database](https://codeigniter.com/user_guide/testing/database.html) section of the documentation.
## Running the tests
The entire test suite can be run by simply typing one command-line command from the main directory.
```console
> ./phpunit
```
If you are using Windows, use the following command.
```console
> vendor\bin\phpunit
```
You can limit tests to those within a single test directory by specifying the
directory name after phpunit.
```console
> ./phpunit app/Models
```
## Generating Code Coverage
To generate coverage information, including HTML reports you can view in your browser,
you can use the following command:
```console
> ./phpunit --colors --coverage-text=tests/coverage.txt --coverage-html=tests/coverage/ -d memory_limit=1024m
```
This runs all of the tests again collecting information about how many lines,
functions, and files are tested. It also reports the percentage of the code that is covered by tests.
It is collected in two formats: a simple text file that provides an overview as well
as a comprehensive collection of HTML files that show the status of every line of code in the project.
The text file can be found at **tests/coverage.txt**.
The HTML files can be viewed by opening **tests/coverage/index.html** in your favorite browser.
## PHPUnit XML Configuration
The repository has a ``phpunit.xml.dist`` file in the project root that's used for
PHPUnit configuration. This is used to provide a default configuration if you
do not have your own configuration file in the project root.
The normal practice would be to copy ``phpunit.xml.dist`` to ``phpunit.xml``
(which is git ignored), and to tailor it as you see fit.
For instance, you might wish to exclude database tests, or automatically generate
HTML code coverage reports.
## Test Cases
Every test needs a *test case*, or class that your tests extend. CodeIgniter 4
provides one class that you may use directly:
* `CodeIgniter\Test\CIUnitTestCase`
Most of the time you will want to write your own test cases that extend `CIUnitTestCase`
to hold functions and services common to your test suites.
## Creating Tests
All tests go in the **tests/** directory. Each test file is a class that extends a
**Test Case** (see above) and contains methods for the individual tests. These method
names must start with the word "test" and should have descriptive names for precisely what
they are testing:
`testUserCanModifyFile()` `testOutputColorMatchesInput()` `testIsLoggedInFailsWithInvalidUser()`
Writing tests is an art, and there are many resources available to help learn how.
Review the links above and always pay attention to your code coverage.
### Database Tests
Tests can include migrating, seeding, and testing against a mock or live database.
Be sure to modify the test case (or create your own) to point to your seed and migrations
and include any additional steps to be run before tests in the `setUp()` method.
See [Testing Your Database](https://codeigniter.com/user_guide/testing/database.html)
for details.
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace Tests\Support;
use CodeIgniter\Test\CIUnitTestCase;
class BaseWebTestCase extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
// Start session if not already started
if (!session()->isStarted()) {
session()->start();
}
}
/**
* Set session role and user ID
*
* @param string $role e.g., 'administrator', 'teacher', 'parent', etc.
* @param int $userId Default to 1
*/
protected function setSessionRole(string $role, int $userId = 1): void
{
session()->set([
'role' => strtolower($role),
'user_id' => $userId,
]);
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace Tests\Support;
use CodeIgniter\Test\CIUnitTestCase;
use Config\Database;
class DBReset
{
protected static array $excludeTables = ['migrations'];
public static function resetDatabase(): void
{
$db = Database::connect();
$db->disableForeignKeyChecks();
// Get all table names
$tables = $db->listTables();
foreach ($tables as $table) {
if (!in_array($table, self::$excludeTables)) {
$db->table($table)->truncate();
}
}
$db->enableForeignKeyChecks();
}
}
@@ -0,0 +1,37 @@
<?php
namespace Tests\Support\Database\Migrations;
use CodeIgniter\Database\Migration;
class ExampleMigration extends Migration
{
protected $DBGroup = 'tests';
public function up(): void
{
$this->forge->addField('id');
$this->forge->addField([
'name' => ['type' => 'varchar', 'constraint' => 31],
'uid' => ['type' => 'varchar', 'constraint' => 31],
'class' => ['type' => 'varchar', 'constraint' => 63],
'icon' => ['type' => 'varchar', 'constraint' => 31],
'summary' => ['type' => 'varchar', 'constraint' => 255],
'created_at' => ['type' => 'datetime', 'null' => true],
'updated_at' => ['type' => 'datetime', 'null' => true],
'deleted_at' => ['type' => 'datetime', 'null' => true],
]);
$this->forge->addKey('name');
$this->forge->addKey('uid');
$this->forge->addKey(['deleted_at', 'id']);
$this->forge->addKey('created_at');
$this->forge->createTable('factories');
}
public function down(): void
{
$this->forge->dropTable('factories');
}
}
@@ -0,0 +1,29 @@
<?php
namespace Tests\Support\Database\Seeds;
use CodeIgniter\Database\Seeder;
class AttendanceSeeder extends Seeder
{
public function run()
{
$data = [
[
'class_section_id' => 1,
'class_section_id' => 1,
'student_id' => 1,
'date' => '2024-08-20',
'status' => 'Absent',
'reason' => 'Sick',
'modified_by' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
],
// Add more records as necessary
];
// Insert the data into the database
$this->db->table('attendance')->insertBatch($data);
}
}
@@ -0,0 +1,28 @@
<?php
namespace Tests\Support\Database\Seeds;
use CodeIgniter\Database\Seeder;
class ClassSeeder extends Seeder
{
public function run()
{
$data = [
[
'class_name' => 'Math 101',
'section' => 'A',
'schedule' => 'MWF 9:00-10:00',
'capacity' => 25,
],
[
'class_name' => 'English 101',
'section' => 'B',
'schedule' => 'TTh 10:00-11:30',
'capacity' => 30,
],
];
$this->db->table('classes')->insertBatch($data);
}
}
@@ -0,0 +1,41 @@
<?php
namespace Tests\Support\Database\Seeds;
use CodeIgniter\Database\Seeder;
class ExampleSeeder extends Seeder
{
public function run(): void
{
$factories = [
[
'name' => 'Test Factory',
'uid' => 'test001',
'class' => 'Factories\Tests\NewFactory',
'icon' => 'fas fa-puzzle-piece',
'summary' => 'Longer sample text for testing',
],
[
'name' => 'Widget Factory',
'uid' => 'widget',
'class' => 'Factories\Tests\WidgetPlant',
'icon' => 'fas fa-puzzle-piece',
'summary' => 'Create widgets in your factory',
],
[
'name' => 'Evil Factory',
'uid' => 'evil-maker',
'class' => 'Factories\Evil\MyFactory',
'icon' => 'fas fa-book-dead',
'summary' => 'Abandon all hope, ye who enter here',
],
];
$builder = $this->db->table('factories');
foreach ($factories as $factory) {
$builder->insert($factory);
}
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
// tests/_support/bootstrap.php
// Load Composer autoloader
require __DIR__ . '/../../vendor/autoload.php';
// Load CodeIgniter testing bootstrap from Composer package
require __DIR__ . '/../../vendor/codeigniter4/framework/system/Test/bootstrap.php';
@@ -0,0 +1,304 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\AdministratorController;
use CodeIgniter\Test\CIUnitTestCase;
use Config\Services;
class AdministratorControllerTest extends CIUnitTestCase
{
protected $controller;
protected function setUp(): void
{
parent::setUp();
// ✅ Create a fake database connection object
$fakeDb = $this->createMock(\CodeIgniter\Database\BaseConnection::class);
// ✅ Stub Config\Database::connect() globally using class_alias
// so CodeIgniter's static Database::connect() returns our fake DB
if (!class_exists('Config\\FakeDatabase')) {
eval('namespace Config; class FakeDatabase extends \\Config\\Database {
public static function connect($group = null, bool $getShared = true) {
return $GLOBALS["__fakeDb__"];
}
}');
}
$GLOBALS['__fakeDb__'] = $fakeDb;
// Temporarily alias Config\Database to our FakeDatabase
class_alias('Config\\FakeDatabase', 'Config\\Database');
// ✅ Mock all required models/services
foreach ([
\App\Models\UserModel::class,
\App\Models\StudentModel::class,
\App\Models\ConfigurationModel::class,
\App\Models\LoginActivityModel::class,
\App\Models\EnrollmentModel::class,
\App\Models\RefundModel::class,
\App\Models\InvoiceModel::class,
\App\Models\ClassSectionModel::class,
\App\Models\UserRoleModel::class,
\App\Models\StudentClassModel::class,
\App\Models\StaffAttendanceModel::class,
\App\Services\FeeCalculationService::class,
] as $class) {
$this->mockModel($class);
}
// ✅ Configuration mock
$configMock = $this->mockModel(\App\Models\ConfigurationModel::class);
$configMock->method('getConfig')->willReturnMap([
['school_year', '2024-2025'],
['semester', 'Fall'],
]);
// ✅ Instantiate controller safely (no DB hit)
$this->controller = new AdministratorController();
}
protected function mockModel(string $class)
{
$mock = $this->createMock($class);
Services::injectMock($class, $mock);
return $mock;
}
/** @test */
public function testDashboardSuccess()
{
$result = $this->controller->dashboard();
$this->assertNotNull($result);
}
/* --------------------------------------------------------------------------
* absenceInfo()
* -------------------------------------------------------------------------- */
public function testAbsenceInfoWithValidUser()
{
helper('date');
$this->controller->setSession(['user_id' => 7]);
$userMock = model(UserModel::class);
$staffMock = model(StaffAttendanceModel::class);
$userMock->method('find')->willReturn(['firstname' => 'Jane', 'lastname' => 'Admin']);
$staffMock->method('where')->willReturnSelf();
$staffMock->method('orderBy')->willReturnSelf();
$staffMock->method('findAll')->willReturn([
['date' => '2025-09-14', 'status' => 'present'],
]);
$response = $this->controller->absenceInfo();
$this->assertTrue($response['status']);
$this->assertArrayHasKey('available_dates', $response['data']);
}
public function testAbsenceInfoUnauthorized()
{
$this->controller->setSession([]); // no user
$response = $this->controller->absenceInfo();
$this->assertFalse($response['status']);
$this->assertEquals(401, $response['code']);
}
/* --------------------------------------------------------------------------
* submitAbsence()
* -------------------------------------------------------------------------- */
public function testSubmitAbsenceRejectsMissingReason()
{
$this->controller->setSession(['user_id' => 2]);
$req = $this->makeRequest('POST', '/api/v1/administrator/absence', [
'dates' => ['2025-10-05'],
'reason' => '',
]);
$this->controller->setRequest($req);
$resp = $this->controller->submitAbsence();
$this->assertFalse($resp['status']);
$this->assertEquals('Reason is required', $resp['message']);
}
public function testSubmitAbsenceValidDateAndReason()
{
$this->controller->setSession(['user_id' => 5]);
$req = $this->makeRequest('POST', '/api/v1/administrator/absence', [
'dates' => ['2025-09-07'],
'reason_type' => 'Personal',
'reason' => 'Travel',
]);
$this->controller->setRequest($req);
$staffMock = model(StaffAttendanceModel::class);
$userMock = model(UserModel::class);
$userMock->method('getUserRole')->willReturn('admin');
$staffMock->method('upsertOne')->willReturn(true);
$userMock->method('find')->willReturn(['firstname' => 'Sarah', 'lastname' => 'Ali', 'email' => 's@a.com']);
$email = Services::email();
$email->clear();
$resp = $this->controller->submitAbsence();
$this->assertTrue($resp['status']);
$this->assertArrayHasKey('saved_dates', $resp['data']);
}
/* --------------------------------------------------------------------------
* search()
* -------------------------------------------------------------------------- */
public function testSearchRequiresQueryParam()
{
$this->controller->setRequest($this->makeRequest('GET', '/api/v1/administrator/search?q='));
$resp = $this->controller->search();
$this->assertFalse($resp['status']);
$this->assertEquals('Search query is required', $resp['message']);
}
public function testSearchReturnsStructuredResults()
{
$this->controller->setRequest($this->makeRequest('GET', '/api/v1/administrator/search?q=john'));
$db = $this->createMock(\CodeIgniter\Database\BaseConnection::class);
$builder = $this->createMock(\CodeIgniter\Database\BaseBuilder::class);
$builder->method('select')->willReturnSelf();
$builder->method('limit')->willReturnSelf();
$builder->method('get')->willReturnSelf();
$builder->method('getResultArray')->willReturn([
['id' => 1, 'firstname' => 'John', 'lastname' => 'Doe'],
]);
$db->method('table')->willReturn($builder);
service('database')->shouldReceive('connect')->andReturn($db);
$resp = $this->controller->search();
$this->assertTrue($resp['status']);
$this->assertArrayHasKey('users', $resp['data']);
}
/* --------------------------------------------------------------------------
* enrollmentWithdrawal()
* -------------------------------------------------------------------------- */
public function testEnrollmentWithdrawalPagination()
{
$mock = model(EnrollmentModel::class);
$mock->method('where')->willReturnSelf();
$mock->method('orderBy')->willReturnSelf();
$mock->method('findAll')->willReturn([
['student_id' => 1, 'status' => 'enrolled'],
]);
$resp = $this->controller->enrollmentWithdrawal();
$this->assertTrue($resp['status']);
$this->assertEquals('Enrollment/withdrawal data retrieved successfully', $resp['message']);
}
/* --------------------------------------------------------------------------
* enrollmentWithdrawalData()
* -------------------------------------------------------------------------- */
public function testEnrollmentWithdrawalDataBuildsStudentList()
{
$stuMock = model(StudentModel::class);
$stuMock->method('getStudentsWithClassAndEnrollment')->willReturn([
['id' => 1, 'firstname' => 'A', 'lastname' => 'Z', 'parent_id' => 10],
]);
$clsMock = model(ClassSectionModel::class);
$clsMock->method('select')->willReturnSelf();
$clsMock->method('where')->willReturnSelf();
$clsMock->method('orderBy')->willReturnSelf();
$clsMock->method('findAll')->willReturn([
['id' => 1, 'class_section_name' => 'KG1'],
]);
$enrollMock = model(EnrollmentModel::class);
$enrollMock->method('getEnrollmentStatus')->willReturn('enrolled');
$resp = $this->controller->enrollmentWithdrawalData();
$this->assertTrue($resp['status']);
$this->assertArrayHasKey('students', $resp['data']);
$this->assertArrayHasKey('classes', $resp['data']);
}
/* --------------------------------------------------------------------------
* updateEnrollmentStatuses()
* -------------------------------------------------------------------------- */
public function testUpdateEnrollmentStatusesRejectsEmptyPayload()
{
$req = $this->makeRequest('POST', '/api/v1/administrator/enrollment-withdrawal/update', []);
$this->controller->setRequest($req);
$resp = $this->controller->updateEnrollmentStatuses();
$this->assertFalse($resp['status']);
$this->assertEquals('No enrollment statuses were submitted.', $resp['message']);
}
public function testUpdateEnrollmentStatusesWithValidStatus()
{
$req = $this->makeRequest('POST', '/api/v1/administrator/enrollment-withdrawal/update', [
'enrollment_status' => [1 => 'enrolled']
]);
$this->controller->setRequest($req);
$stuMock = model(StudentModel::class);
$stuMock->method('find')->willReturn(['id' => 1, 'parent_id' => 2]);
$enrollMock = model(EnrollmentModel::class);
$enrollMock->method('where')->willReturnSelf();
$enrollMock->method('get')->willReturnSelf();
$enrollMock->method('getRowArray')->willReturn(['enrollment_status' => 'payment pending', 'parent_id' => 2]);
$invoiceMock = model(InvoiceModel::class);
$invoiceMock->method('where')->willReturnSelf();
$invoiceMock->method('orderBy')->willReturnSelf();
$invoiceMock->method('first')->willReturn(['id' => 10, 'school_year' => '2024-2025']);
$refundMock = model(RefundModel::class);
$refundMock->method('where')->willReturnSelf();
$refundMock->method('first')->willReturn(null);
$resp = $this->controller->updateEnrollmentStatuses();
$this->assertTrue($resp['status']);
$this->assertArrayHasKey('groupsByParentStatus', $resp['data']);
}
/* --------------------------------------------------------------------------
* studentProfiles()
* -------------------------------------------------------------------------- */
public function testStudentProfilesReturnsList()
{
$studClassMock = model(StudentClassModel::class);
$studClassMock->method('getClassSectionNameByStudentId')->willReturn('KG2');
$resp = $this->controller->studentProfiles();
$this->assertTrue($resp['status']);
$this->assertArrayHasKey('students', $resp['data']);
}
/* --------------------------------------------------------------------------
* parentProfiles()
* -------------------------------------------------------------------------- */
public function testParentProfilesWithParentRole()
{
$userMock = model(UserModel::class);
$userMock->method('findAll')->willReturn([
['id' => 20, 'firstname' => 'Layla', 'lastname' => 'Yusuf', 'email' => 'layla@test.com', 'cellphone' => '123', 'school_id' => 'P1'],
]);
$roleMock = model(UserRoleModel::class);
$roleMock->method('getRolesByUserId')->willReturn([
['role_name' => 'parent'],
]);
$invMock = model(InvoiceModel::class);
$invMock->method('getLatestInvoicePaidAmount')->willReturn(150.00);
$invMock->method('getLatestInvoiceBalance')->willReturn(50.00);
$resp = $this->controller->parentProfiles();
$this->assertTrue($resp['status']);
$this->assertArrayHasKey('parents', $resp['data']);
$this->assertEquals('Layla', $resp['data']['parents'][0]['firstname']);
}
}
@@ -0,0 +1,282 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\AssignmentController;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\HTTP\URI;
use CodeIgniter\HTTP\UserAgent;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\Mock\MockIncomingRequest;
use Config\App;
class FakeJsonRequest extends MockIncomingRequest
{
public function __construct(private readonly ?array $payload)
{
parent::__construct(config(App::class), new URI('https://test.alrahmaisgl.org'), 'php://input', new UserAgent());
}
public function getJSON(bool $assoc = false, int $depth = 512, int $options = 0): ?array
{
return $this->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']);
}
}
@@ -0,0 +1,465 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\AttendanceController;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\HTTP\URI;
use CodeIgniter\HTTP\UserAgent;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\Mock\MockIncomingRequest;
use Config\App;
class FakeAttendanceRequest extends MockIncomingRequest
{
public function __construct(private readonly ?array $json = null, private readonly array $gets = [])
{
parent::__construct(config(App::class), new URI('https://test.alrahmaisgl.org'), 'php://input', new UserAgent());
}
public function getJSON(bool $assoc = false, int $depth = 512, int $options = 0): ?array
{
return $this->json;
}
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 StubAttendanceModel
{
public array $whereHistory = [];
public array $findResult = [];
public array $findAllResult = [];
public array $saved = [];
public array $attendanceLookup = [];
public int $insertId = 0;
public array $pagerData = ['current' => 1, 'perPage' => 20, 'total' => 0];
public array $firstQueue = [];
public array $updatedAttendance = [];
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->insertId++;
$this->saved[$this->insertId] = $data;
$this->findResult[$this->insertId] = ['id' => $this->insertId] + $data;
return $this->insertId;
}
public function getInsertID()
{
return $this->insertId;
}
public function update($id, array $data)
{
$this->saved[$id] = $data;
$this->findResult[$id] = ['id' => $id] + $data;
}
public function delete($id)
{
unset($this->findResult[$id]);
}
public function first()
{
return array_shift($this->firstQueue) ?? null;
}
public function queueFirst($row): void
{
$this->firstQueue[] = $row;
}
public function findAll(): array
{
return $this->findAllResult;
}
public function getAttendance(int $studentId, int $classSectionId, string $date): array
{
return $this->attendanceLookup[$studentId] ?? [];
}
public function setAttendanceLookup(int $studentId, array $row): void
{
$this->attendanceLookup[$studentId] = $row;
}
public function updateAttendance(int $id, array $data): bool
{
$this->updatedAttendance[$id] = $data;
$this->findResult[$id] = ['id' => $id] + $data;
return true;
}
public function orderBy(...$args): self
{
return $this;
}
}
class StubStudentClassModel
{
public ?string $grade = null;
public function getStudentGrade(int $studentId)
{
return $this->grade;
}
}
class StubConfigModel
{
public function __construct(private readonly array $values)
{
}
public function getConfig(string $key)
{
return $this->values[$key] ?? null;
}
}
class StubDbTable
{
private array $results = [];
public function __construct(array $rows)
{
$this->results = $rows;
}
public function select(...$args)
{
return $this;
}
public function join(...$args)
{
return $this;
}
public function where(...$args)
{
return $this;
}
public function orderBy(...$args)
{
return $this;
}
public function get()
{
return $this;
}
public function getResultArray()
{
return $this->results;
}
}
class StubDbConnection
{
public function __construct(private readonly array $rows)
{
}
public function table($name)
{
return new StubDbTable($this->rows);
}
}
class TestableAttendanceController extends AttendanceController
{
public bool $validationResult = true;
public array $validationErrors = [];
public $validator;
private ?StubDbConnection $db = null;
public function __construct()
{
}
public function setModels(StubAttendanceModel $attendanceModel, StubStudentClassModel $studentClassModel, StubConfigModel $configModel): void
{
$this->attendanceModel = $attendanceModel;
$this->studentClassModel = $studentClassModel;
$this->configModel = $configModel;
$this->schoolYear = (string) $configModel->getConfig('school_year');
$this->semester = (string) $configModel->getConfig('semester');
}
public function setDatabaseConnection(StubDbConnection $db): void
{
$this->db = $db;
}
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 getDatabaseConnection()
{
return $this->db ?? parent::getDatabaseConnection();
}
protected function respond($data = null, ?int $status = null, string $message = '')
{
return ['data' => $data, 'code' => $status, 'message' => $message];
}
protected function error(string $message = 'An error occurred', int $code = 400, ?array $errors = null)
{
$payload = ['status' => false, 'message' => $message, 'code' => $code];
if (!empty($errors)) {
$payload['errors'] = $errors;
}
return $payload;
}
}
class AttendanceControllerTest extends CIUnitTestCase
{
private TestableAttendanceController $controller;
private StubAttendanceModel $attendanceModel;
private StubStudentClassModel $studentClassModel;
private StubConfigModel $configModel;
protected function setUp(): void
{
parent::setUp();
$this->controller = new TestableAttendanceController();
$this->attendanceModel = new StubAttendanceModel();
$this->studentClassModel = new StubStudentClassModel();
$this->configModel = new StubConfigModel(['school_year' => '2024-2025', 'semester' => 'Spring']);
$this->controller->setModels($this->attendanceModel, $this->studentClassModel, $this->configModel);
}
public function testIndexUsesFilters()
{
$request = new FakeAttendanceRequest(null, ['student_id' => '5', 'date' => '2024-09-01', 'page' => '2', 'per_page' => '10']);
$this->attendanceModel->findAllResult = [['id' => 1]];
$this->attendanceModel->pagerData = ['current' => 2, 'perPage' => 10, 'total' => 30];
$this->controller->setRequest($request);
$response = $this->controller->index();
$this->assertSame('Attendance records retrieved successfully', $response['message']);
$this->assertSame(2, $response['data']['pagination']['current_page']);
$this->assertSame(1, $response['data']['data'][0]['id']);
}
public function testShowNotFound()
{
$response = $this->controller->show(7);
$this->assertSame(ResponseInterface::HTTP_NOT_FOUND, $response['code']);
}
public function testShowSuccess()
{
$this->attendanceModel->findResult[8] = ['id' => 8];
$response = $this->controller->show(8);
$this->assertSame('Attendance record retrieved successfully', $response['message']);
}
public function testStoreValidationFails()
{
$this->controller->setValidationResult(false, ['status' => 'required']);
$request = new FakeAttendanceRequest(['student_id'=>1,'date'=>'','status'=>'']);
$this->controller->setRequest($request);
$response = $this->controller->store();
$this->assertSame(422, $response['code']);
$this->assertArrayHasKey('status', $response['errors']);
}
public function testStoreAlreadyRecorded()
{
$request = new FakeAttendanceRequest(['student_id'=>2,'date'=>'2024-09-01','status'=>'present']);
$this->controller->setRequest($request);
$this->attendanceModel->queueFirst(['id'=>5]);
$response = $this->controller->store();
$this->assertSame(409, $response['code']);
}
public function testStoreCreatesRecord()
{
$request = new FakeAttendanceRequest(['student_id'=>2,'date'=>'2024-09-01','status'=>'present','notes'=>'ok']);
$this->controller->setRequest($request);
$this->controller->setValidationResult(true);
$response = $this->controller->store();
$this->assertSame('Attendance record created successfully', $response['message']);
$this->assertSame(1, $this->attendanceModel->insertId);
}
public function testUpdateNotFound()
{
$response = $this->controller->update(99);
$this->assertSame(ResponseInterface::HTTP_NOT_FOUND, $response['code']);
}
public function testUpdateValidationFails()
{
$this->attendanceModel->findResult[12] = ['id' => 12];
$this->controller->setValidationResult(false, ['status' => 'invalid']);
$this->controller->setRequest(new FakeAttendanceRequest(['status'=>'bad']));
$response = $this->controller->update(12);
$this->assertSame(422, $response['code']);
}
public function testUpdateSuccess()
{
$this->attendanceModel->findResult[13] = ['id' => 13];
$this->controller->setValidationResult(true);
$this->controller->setRequest(new FakeAttendanceRequest(['status'=>'absent']));
$response = $this->controller->update(13);
$this->assertSame('Attendance record updated successfully', $response['message']);
$this->assertSame('absent', $this->attendanceModel->findResult[13]['status']);
}
public function testDeleteNotFound()
{
$response = $this->controller->delete(20);
$this->assertSame(ResponseInterface::HTTP_NOT_FOUND, $response['code']);
}
public function testDeleteSuccess()
{
$this->attendanceModel->findResult[21] = ['id' => 21];
$response = $this->controller->delete(21);
$this->assertSame('Attendance record deleted successfully', $response['message']);
}
public function testStudentEndpoint()
{
$this->attendanceModel->findAllResult = [['id' => 5]];
$response = $this->controller->student(5);
$this->assertSame('Student attendance retrieved successfully', $response['message']);
}
public function testClassRosterAggregatesAttendance()
{
$stubRows = [
['student_id' => 1, 'firstname' => 'A', 'lastname' => 'B'],
['student_id' => 2, 'firstname' => 'C', 'lastname' => 'D'],
];
$db = new StubDbConnection($stubRows);
$this->controller->setDatabaseConnection($db);
$this->controller->setRequest(new FakeAttendanceRequest(null, ['date' => '2024-10-05']));
$this->attendanceModel->setAttendanceLookup(1, ['status' => 'present', 'reason' => '']);
$this->attendanceModel->setAttendanceLookup(2, []);
$response = $this->controller->classRoster(3);
$this->assertSame('Class roster attendance retrieved', $response['message']);
$this->assertSame(2, count($response['data']['students']));
}
public function testRecordClassValidationFails()
{
$this->controller->setRequest(new FakeAttendanceRequest(['records' => []]));
$response = $this->controller->recordClass(1);
$this->assertSame(422, $response['code']);
}
public function testRecordClassSavesRows()
{
$this->studentClassModel->grade = '5';
$this->attendanceModel->setAttendanceLookup(7, ['id' => 99]);
$payload = [
'date' => '2024-10-01',
'records' => [
['student_id' => 7, 'status' => 'present'],
['student_id' => 8, 'status' => 'late'],
],
];
$this->controller->setRequest(new FakeAttendanceRequest($payload));
$response = $this->controller->recordClass(12);
$this->assertSame('2 attendance row(s) saved', $response['message']);
$this->assertSame(2, $response['data']['saved']);
}
}
@@ -0,0 +1,229 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\AttendanceTrackingController;
use CodeIgniter\HTTP\URI;
use CodeIgniter\HTTP\UserAgent;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\Mock\MockIncomingRequest;
use Config\App;
class FakeTrackingRequest extends MockIncomingRequest
{
public function __construct(private readonly ?array $json = null, private readonly array $gets = [])
{
parent::__construct(config(App::class), new URI('https://test.alrahmaisgl.org'), 'php://input', new UserAgent());
}
public function getJSON(bool $assoc = false, int $depth = 512, int $options = 0): ?array
{
return $this->json;
}
public function getGet($key = null, $filter = null, $default = null)
{
if ($key === null) {
return $this->gets;
}
return $this->gets[$key] ?? $default;
}
}
class StubTrackingModel
{
public array $findAllResult = [];
public array $findResult = [];
public array $updated = [];
public array $inserted = [];
public array $firstQueue = [];
public function where($column, $value = null)
{
return $this;
}
public function select($columns)
{
return $this;
}
public function groupBy($column)
{
return $this;
}
public function having($column, $value)
{
return $this;
}
public function findAll(): array
{
return $this->findAllResult;
}
public function orderBy(...$args)
{
return $this;
}
public function first()
{
return array_shift($this->firstQueue) ?? null;
}
public function queueFirst(array $row): void
{
$this->firstQueue[] = $row;
}
public function insert(array $data)
{
$this->inserted[] = $data;
return 1;
}
public function getInsertID()
{
return 1;
}
public function update(int $id, array $data)
{
$this->updated[$id] = $data;
}
public function find(int $id)
{
return $this->findResult[$id] ?? null;
}
}
class TestableAttendanceTrackingController extends AttendanceTrackingController
{
public bool $validationResult = true;
public array $validationErrors = [];
public $validator;
public function __construct()
{
// bypass real model instantiation
}
public function setTrackingModel(StubTrackingModel $model): void
{
$this->trackingModel = $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 AttendanceTrackingControllerTest extends CIUnitTestCase
{
private TestableAttendanceTrackingController $controller;
private StubTrackingModel $model;
protected function setUp(): void
{
parent::setUp();
$this->controller = new TestableAttendanceTrackingController();
$this->model = new StubTrackingModel();
$this->controller->setTrackingModel($this->model);
}
public function testRecordValidationFails()
{
$this->controller->setValidationResult(false, ['student_id' => 'required']);
$this->controller->setRequest(new FakeTrackingRequest(['student_id' => null]));
$response = $this->controller->record();
$this->assertSame(422, $response['code']);
$this->assertArrayHasKey('student_id', $response['errors']);
}
public function testRecordUpdatesExisting()
{
$this->model->queueFirst(['id' => 5]);
$request = new FakeTrackingRequest(['student_id' => 1, 'date' => '2024-09-01', 'status' => 'present']);
$this->controller->setRequest($request);
$response = $this->controller->record();
$this->assertSame('Attendance tracking record updated successfully', $response['message']);
$this->assertSame(['student_id' => 1, 'date' => '2024-09-01', 'status' => 'present'], $this->model->updated[5]);
}
public function testRecordCreatesNew()
{
$request = new FakeTrackingRequest(['student_id' => 2, 'date' => '2024-09-02', 'status' => 'late']);
$this->controller->setRequest($request);
$this->controller->setValidationResult(true);
$response = $this->controller->record();
$this->assertSame('Attendance tracking record created successfully', $response['message']);
$this->assertSame(1, $response['data']['id']);
}
public function testViolationsDefaultsThreshold()
{
$this->controller->setRequest(new FakeTrackingRequest(null, []));
$this->model->findAllResult = [['student_id' => 3, 'total_violations' => 5]];
$response = $this->controller->violations();
$this->assertSame('Attendance violations retrieved successfully', $response['message']);
$this->assertSame(1, count($response['data']));
}
public function testViolationsCustomThreshold()
{
$request = new FakeTrackingRequest(null, ['threshold' => '2']);
$this->controller->setRequest($request);
$this->model->findAllResult = [['student_id' => 4, 'total_violations' => 3]];
$response = $this->controller->violations();
$this->assertSame(1, count($response['data']));
}
public function testStudentRecordsOrder()
{
$this->model->findAllResult = [['id' => 7]];
$response = $this->controller->student(7);
$this->assertSame('Tracking records retrieved successfully for student', $response['message']);
$this->assertSame(7, $response['data'][0]['id']);
}
}
@@ -0,0 +1,180 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\BroadcastEmailController;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\HTTP\URI;
use CodeIgniter\HTTP\UserAgent;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\Mock\MockIncomingRequest;
use Config\App;
class FakeBroadcastRequest extends MockIncomingRequest
{
public function __construct(private readonly ?array $json = null)
{
parent::__construct(config(App::class), new URI('https://test.alrahmaisgl.org'), 'php://input', new UserAgent());
}
public function getJSON(bool $assoc = false, int $depth = 512, int $options = 0): ?array
{
return $this->json;
}
}
class StubBroadcastModel
{
public array $inserted = [];
public function insert(array $data)
{
$this->inserted[] = $data;
return 1;
}
}
class StubUserModel
{
public function __construct(private readonly array $parents = [])
{
}
public function select($columns)
{
return $this;
}
public function join(...$args)
{
return $this;
}
public function where(...$args)
{
return $this;
}
public function findAll(): array
{
return $this->parents;
}
}
class TestableBroadcastEmailController extends BroadcastEmailController
{
public bool $validationResult = true;
public array $validationErrors = [];
public $validator;
public function __construct()
{
// keep the parent constructor from running to avoid real models
}
public function setBroadcastModel(StubBroadcastModel $model): void
{
$this->broadcastModel = $model;
}
public function setUserModel(StubUserModel $model): void
{
$this->userModel = $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) {
public function __construct(private readonly array $errors)
{
}
public function getErrors(): array
{
return $this->errors;
}
};
return $this->validationResult;
}
protected function respondSuccess($data = null, string $message = 'Success', int $code = ResponseInterface::HTTP_OK)
{
return ['status' => true, 'message' => $message, 'data' => $data, 'code' => $code];
}
protected function respondValidationError(array $errors, string $message = 'Validation failed')
{
return ['status' => false, 'message' => $message, 'errors' => $errors, 'code' => ResponseInterface::HTTP_UNPROCESSABLE_ENTITY];
}
}
class BroadcastEmailControllerTest extends CIUnitTestCase
{
private TestableBroadcastEmailController $controller;
private StubBroadcastModel $broadcastModel;
protected function setUp(): void
{
parent::setUp();
$this->controller = new TestableBroadcastEmailController();
$this->broadcastModel = new StubBroadcastModel();
$this->controller->setBroadcastModel($this->broadcastModel);
}
public function testSendValidationFails(): void
{
$this->controller->setValidationResult(false, ['subject' => 'required']);
$this->controller->setRequest(new FakeBroadcastRequest(['subject' => '', 'body' => '', 'recipients' => []]));
$response = $this->controller->send();
$this->assertSame(ResponseInterface::HTTP_UNPROCESSABLE_ENTITY, $response['code']);
$this->assertSame('Validation failed', $response['message']);
$this->assertArrayHasKey('subject', $response['errors']);
}
public function testSendStoresSentAndFailedRecipients(): void
{
$requestData = [
'subject' => 'Weekly Update',
'body' => 'Hello parents',
'recipients' => ['parent1@example.com', 'bad-email'],
];
$this->controller->setRequest(new FakeBroadcastRequest($requestData));
$response = $this->controller->send();
$this->assertSame(ResponseInterface::HTTP_OK, $response['code']);
$this->assertSame('Broadcast email sent successfully', $response['message']);
$this->assertSame(['parent1@example.com'], $response['data']['sent']);
$this->assertSame(['bad-email'], $response['data']['failed']);
$this->assertCount(1, $this->broadcastModel->inserted);
$data = $this->broadcastModel->inserted[0];
$this->assertSame('Weekly Update', $data['subject']);
$this->assertSame('Hello parents', $data['body']);
$this->assertSame(1, $data['total_sent']);
$this->assertArrayHasKey('created_at', $data);
}
public function testParentsReturnsParentList(): void
{
$parents = [
['id' => 10, 'firstname' => 'Test', 'lastname' => 'Parent', 'email' => 'test@parent.com'],
];
$this->controller->setUserModel(new StubUserModel($parents));
$response = $this->controller->parents();
$this->assertSame(ResponseInterface::HTTP_OK, $response['code']);
$this->assertSame('Parent list retrieved successfully', $response['message']);
$this->assertSame($parents, $response['data']);
}
}
@@ -0,0 +1,142 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\ContactController;
use App\Controllers\View\EmailController;
use CodeIgniter\Test\CIUnitTestCase;
use Config\Services;
class FakeJsonRequest
{
public function __construct(private readonly ?array $payload)
{
}
public function getJSON(bool $assoc = false): ?array
{
return $this->payload;
}
}
class StubEmailController extends EmailController
{
public function __construct(private readonly bool $result)
{
}
public function sendEmail(
string $recipient,
string $subject,
string $htmlMessage,
?string $profile = null,
?string $replyToEmail = null,
?string $replyToName = null,
array $attachments = []
): bool {
return $this->result;
}
}
class TestableContactController extends ContactController
{
private ?EmailController $emailController = null;
public function __construct()
{
// Skip the parent bootstrap so we control dependencies manually.
}
public function setEmailController(EmailController $emailController): void
{
$this->emailController = $emailController;
}
protected function createEmailController(): EmailController
{
return $this->emailController ?? parent::createEmailController();
}
}
class ContactControllerTest extends CIUnitTestCase
{
private TestableContactController $controller;
protected function setUp(): void
{
parent::setUp();
Services::resetSingle('validation');
$this->controller = new TestableContactController();
}
public function testSubmitReturnsErrorWhenJsonMissing()
{
$this->setRequestPayload(null);
$response = $this->controller->submit();
$this->assertFalse($response['status']);
$this->assertSame(400, $response['code']);
$this->assertSame('Invalid request data', $response['message']);
}
public function testSubmitReturnsValidationErrors()
{
$this->setRequestPayload([
'name' => 'Al',
'email' => 'invalid',
'subject' => 'Hi',
'message' => 'short',
]);
$response = $this->controller->submit();
$this->assertFalse($response['status']);
$this->assertSame(422, $response['code']);
$this->assertSame('Validation failed', $response['message']);
$this->assertArrayHasKey('errors', $response);
$this->assertNotEmpty($response['errors']);
}
public function testSubmitReturnsSuccessWhenEmailSent()
{
$this->controller->setEmailController(new StubEmailController(true));
$this->setRequestPayload($this->validPayload());
$response = $this->controller->submit();
$this->assertTrue($response['status']);
$this->assertSame('Thank you for contacting us! We will get back to you soon.', $response['message']);
$this->assertNull($response['data']);
}
public function testSubmitReturnsErrorWhenEmailFails()
{
$this->controller->setEmailController(new StubEmailController(false));
$this->setRequestPayload($this->validPayload());
$response = $this->controller->submit();
$this->assertFalse($response['status']);
$this->assertSame(500, $response['code']);
$this->assertSame('There was an error sending your message. Please try again later.', $response['message']);
}
private function setRequestPayload(?array $payload): void
{
$request = new FakeJsonRequest($payload);
self::setPrivateProperty($this->controller, 'request', $request);
}
private function validPayload(): array
{
return [
'name' => 'Alia Ahmed',
'email' => 'alia@alrahmaisgl.org',
'subject' => 'Support request',
'message' => str_repeat('Please assist. ', 5),
];
}
}
@@ -0,0 +1,30 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\DocsController;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Test\CIUnitTestCase;
class TestableDocsController extends DocsController
{
protected function success($data = null, string $message = 'Success', int $code = ResponseInterface::HTTP_OK)
{
return ['data' => $data, 'message' => $message, 'code' => $code];
}
}
class DocsControllerTest extends CIUnitTestCase
{
public function testSwaggerReturnsTwoSpecs()
{
$controller = new TestableDocsController();
$response = $controller->swagger();
$this->assertSame(ResponseInterface::HTTP_OK, $response['code']);
$this->assertSame('Available OpenAPI specifications', $response['message']);
$this->assertIsArray($response['data']);
$this->assertCount(2, $response['data']);
$this->assertSame('/docs/openapi_administrator_controller.yaml', $response['data'][0]['url']);
}
}
@@ -0,0 +1,104 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\EmergencyContactController;
use App\Models\ConfigurationModel;
use App\Models\EmergencyContactModel;
use CodeIgniter\Test\CIUnitTestCase;
class TestableEmergencyContactController extends EmergencyContactController
{
public function setModels(EmergencyContactModel $model, ConfigurationModel $config): void
{
$this->emergencyContactModel = $model;
$this->configModel = $config;
}
}
class EmergencyContactControllerTest extends CIUnitTestCase
{
private $controller;
private $contactModel;
private $configModel;
protected function setUp(): void
{
parent::setUp();
$this->contactModel = $this->createMock(EmergencyContactModel::class);
$this->configModel = $this->createMock(ConfigurationModel::class);
$this->controller = new TestableEmergencyContactController();
$this->controller->setModels($this->contactModel, $this->configModel);
}
public function testIndexFiltersByParent()
{
$query = $this->makeRequest('GET', '/api/v1/emergency-contacts', ['parent_id' => 9]);
$this->controller->setRequest($query);
$this->configModel->method('getConfig')->willReturnMap([
['school_year', '2024-2025'],
['semester', 'Fall'],
]);
$this->contactModel->method('where')->willReturnSelf();
$this->contactModel->method('findAll')->willReturn([
['id' => 1, 'parent_id' => 9, 'cellphone' => '5551234'],
]);
$response = $this->controller->index();
$this->assertTrue($response['status']);
$this->assertCount(1, $response['data']);
}
public function testShowReturnsNotFound()
{
$this->contactModel->method('find')->with(5)->willReturn(null);
$response = $this->controller->show(5);
$this->assertFalse($response['status']);
$this->assertEquals(404, $response['code']);
}
public function testCreateValidContact()
{
$payload = [
'parent_id' => 5,
'emergency_contact_name' => 'Hakim',
'cellphone' => '555-1234',
'relation' => 'Uncle',
'school_year' => '2024-2025',
'semester' => 'Fall',
];
$req = $this->makeRequest('POST', '/api/v1/emergency-contacts', $payload);
$this->controller->setRequest($req);
$this->configModel->method('getConfig')->willReturnMap([
['school_year', '2024-2025'],
['semester', 'Fall'],
]);
$this->contactModel->method('insert')->willReturn(99);
$this->contactModel->method('find')->with(99)->willReturn([
'id' => 99,
'emergency_contact_name' => 'Hakim',
]);
$response = $this->controller->create();
$this->assertTrue($response['status']);
$this->assertEquals('Emergency contact created successfully', $response['message']);
$this->assertEquals(99, $response['data']['id']);
}
public function testCreateInvalidPayload()
{
$req = $this->makeRequest('POST', '/api/v1/emergency-contacts', []);
$this->controller->setRequest($req);
$response = $this->controller->create();
$this->assertFalse($response['status']);
$this->assertEquals(422, $response['code']);
}
}
@@ -0,0 +1,129 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\FamilyController;
use App\Models\FamilyGuardianModel;
use App\Models\FamilyModel;
use App\Models\FamilyStudentModel;
use CodeIgniter\Test\CIUnitTestCase;
class TestableFamilyController extends FamilyController
{
private array $overridePagination = [];
public function setModels(FamilyModel $familyModel, FamilyStudentModel $familyStudentModel, FamilyGuardianModel $familyGuardianModel): void
{
$this->familyModel = $familyModel;
$this->familyStudentModel = $familyStudentModel;
$this->familyGuardianModel = $familyGuardianModel;
}
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);
}
}
class FamilyControllerTest extends CIUnitTestCase
{
private $controller;
private $familyModel;
private $studentModel;
private $guardianModel;
protected function setUp(): void
{
parent::setUp();
$this->familyModel = $this->createMock(FamilyModel::class);
$this->studentModel = $this->createMock(FamilyStudentModel::class);
$this->guardianModel = $this->createMock(FamilyGuardianModel::class);
$this->controller = new TestableFamilyController();
$this->controller->setModels($this->familyModel, $this->studentModel, $this->guardianModel);
}
public function testGetGuardiansReturnsDataWhenFamilyExists()
{
$this->familyModel->method('find')->with(5)->willReturn(['id' => 5]);
$this->guardianModel->method('where')->willReturnSelf();
$this->guardianModel->method('findAll')->willReturn([
['id' => 11, 'cellphone' => '555-0101'],
]);
$response = $this->controller->getGuardians(5);
$this->assertTrue($response['status']);
$this->assertCount(1, $response['data']);
}
public function testGetGuardiansReturnsErrorWhenFamilyMissing()
{
$this->familyModel->method('find')->with(3)->willReturn(null);
$response = $this->controller->getGuardians(3);
$this->assertFalse($response['status']);
$this->assertEquals(404, $response['code']);
}
public function testGetByStudentWrapsStudentsAndGuardians()
{
$this->studentModel->method('where')->willReturnSelf();
$this->studentModel->method('findColumn')->willReturn([10]);
$this->familyModel->method('whereIn')->willReturnSelf();
$this->familyModel->method('findAll')->willReturn([
['id' => 10, 'family_name' => 'Main Family'],
]);
$this->studentModel->method('findAll')->willReturn([
['id' => 222, 'firstname' => 'Amina'],
]);
$this->guardianModel->method('where')->willReturnSelf();
$this->guardianModel->method('findAll')->willReturn([
['id' => 33, 'firstname' => 'Derrell'],
]);
$response = $this->controller->getByStudent(42);
$this->assertTrue($response['status']);
$this->assertEquals('Families retrieved successfully', $response['message']);
$this->assertNotEmpty($response['data']);
$this->assertArrayHasKey('students', $response['data'][0]);
}
public function testIndexUsesPaginatedResult()
{
$paginatedPayload = [
'data' => [
['id' => 1, 'family_name' => 'Alver Family'],
],
'pagination' => ['current_page' => 1, 'per_page' => 20, 'total' => 1, 'total_pages' => 1],
];
$this->controller->setPaginatedResult($paginatedPayload);
$response = $this->controller->index();
$this->assertTrue($response['status']);
$this->assertSame($paginatedPayload, $response['data']);
}
public function testShowReturnsFamily()
{
$this->familyModel->method('find')->with(17)->willReturn(['id' => 17, 'family_name' => 'Mansour']);
$this->studentModel->method('where')->willReturnSelf();
$this->studentModel->method('findAll')->willReturn([]);
$this->guardianModel->method('where')->willReturnSelf();
$this->guardianModel->method('findAll')->willReturn([]);
$response = $this->controller->show(17);
$this->assertTrue($response['status']);
$this->assertEquals('Family retrieved successfully', $response['message']);
}
}
@@ -0,0 +1,141 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\FilesController;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\URI;
use CodeIgniter\HTTP\UserAgent;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\Mock\MockIncomingRequest;
use Config\App;
use Config\Services;
class FakeFilesRequest 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 FakeStreamResponse
{
public array $headers = [];
public string $body = '';
public function setHeader(string $key, string $value)
{
$this->headers[$key] = $value;
return $this;
}
public function setBody(string $body)
{
$this->body = $body;
return $this;
}
}
class TestableFilesController extends FilesController
{
protected function respond($data = null, ?int $status = null, string $message = '')
{
$payload = $data;
if (is_array($data) && isset($data['message'])) {
$message = $data['message'] ?? $message;
$payload = $data['data'] ?? $data;
}
return [
'status' => true,
'message' => $message,
'data' => $payload,
'code' => $status,
];
}
protected function error(string $message = 'An error occurred', int $code = 400, ?array $errors = null)
{
$payload = ['status' => false, 'message' => $message, 'code' => $code];
if (!empty($errors)) {
$payload['errors'] = $errors;
}
return $payload;
}
}
class FilesControllerTest extends CIUnitTestCase
{
private TestableFilesController $controller;
protected function setUp(): void
{
parent::setUp();
Services::resetSingle('session');
$this->controller = new TestableFilesController();
$response = new FakeStreamResponse();
self::setPrivateProperty($this->controller, 'response', $response);
}
public function testReceiptRejectsInvalidFilename()
{
$response = $this->controller->receipt('../secret.txt');
$this->assertFalse($response['status']);
$this->assertSame(400, $response['code']);
}
public function testReceiptRejectsUnsupportedExtension()
{
$response = $this->controller->receipt('malware.exe');
$this->assertFalse($response['status']);
$this->assertSame(400, $response['code']);
}
public function testReceiptReturnsNotFoundWhenMissing()
{
$response = $this->controller->receipt('missing.pdf');
$this->assertFalse($response['status']);
$this->assertSame(404, $response['code']);
}
public function testIndexRejectsInvalidType()
{
$request = new FakeFilesRequest(['type' => 'avatars']);
$this->controller->setRequest($request);
$response = $this->controller->index();
$this->assertFalse($response['status']);
$this->assertSame(400, $response['code']);
}
public function testIndexReturnsFileList()
{
$request = new FakeFilesRequest(['type' => 'receipts']);
$this->controller->setRequest($request);
$response = $this->controller->index();
$this->assertTrue($response['status']);
$this->assertSame('Files retrieved successfully', $response['message']);
$this->assertSame('receipts', $response['data']['type']);
$this->assertGreaterThanOrEqual(0, $response['data']['count']);
$names = array_column($response['data']['files'], 'name');
$this->assertIsArray($names);
}
}
@@ -0,0 +1,104 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\HealthController;
use CodeIgniter\Test\CIUnitTestCase;
use Config\Services;
use CodeIgniter\HTTP\IncomingRequest;
class StubHealthController extends HealthController
{
private $dbConnection;
public function setDatabaseConnection($connection): void
{
$this->dbConnection = $connection;
}
protected function getDatabaseConnection()
{
return $this->dbConnection ?? parent::getDatabaseConnection();
}
protected function respond($data = null, ?int $status = null, string $message = '')
{
$payload = is_array($data) ? $data : [];
$payload['code'] = $status;
return $payload;
}
}
class StubDbConnection
{
public function __construct(
private array $tables = [],
private array $fields = []
) {
}
public function tableExists(string $table): bool
{
return $this->tables[$table] ?? true;
}
public function fieldExists(string $field, string $table): bool
{
return $this->fields[$table][$field] ?? true;
}
}
class HealthControllerTest extends CIUnitTestCase
{
private StubHealthController $controller;
protected function setUp(): void
{
parent::setUp();
$this->controller = new StubHealthController();
$request = $this->createMock(IncomingRequest::class);
$this->controller->setRequest($request);
$response = Services::response();
self::setPrivateProperty($this->controller, 'response', $response);
}
public function testIndexReportsOkWhenAllChecksPass()
{
$stubDb = new StubDbConnection(
['user_preferences' => true, 'settings' => true, 'migrations' => true],
[
'user_preferences' => ['timezone' => true],
'settings' => ['timezone' => true],
]
);
$this->controller->setDatabaseConnection($stubDb);
$result = $this->controller->index();
$this->assertArrayHasKey('ok', $result);
$this->assertTrue($result['ok']);
$this->assertSame(WRITEPATH, $result['write_path']);
$this->assertSame(200, $result['code']);
$this->assertSame(true, $result['database']['user_preferences_exists']);
}
public function testIndexReturns503WhenFieldMissing()
{
$stubDb = new StubDbConnection(
['user_preferences' => true, 'settings' => true, 'migrations' => true],
[
'user_preferences' => ['timezone' => false],
'settings' => ['timezone' => true],
]
);
$this->controller->setDatabaseConnection($stubDb);
$result = $this->controller->index();
$this->assertArrayHasKey('ok', $result);
$this->assertFalse($result['ok']);
$this->assertSame(503, $result['code']);
$this->assertFalse($result['database']['user_preferences_has_timezone']);
}
}
@@ -0,0 +1,109 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\InfoIconController;
use App\Models\UserModel;
use CodeIgniter\Test\CIUnitTestCase;
class StubInfoIconUserModel extends UserModel
{
public function __construct()
{
// skip parent initialization to avoid DB dependencies
}
private array $data = [];
public function setUserInfoById(int $id, ?array $info): void
{
$this->data[$id] = $info;
}
public function getUserInfoById($id): ?array
{
return $this->data[$id] ?? null;
}
}
class TestableInfoIconController extends InfoIconController
{
private ?object $user = null;
private ?StubInfoIconUserModel $userModel = null;
public function setCurrentUser(?object $user): void
{
$this->user = $user;
}
public function setUserModel(StubInfoIconUserModel $model): void
{
$this->userModel = $model;
}
protected function getCurrentUser(): ?object
{
return $this->user;
}
protected function createUserModel(): UserModel
{
return $this->userModel ?? parent::createUserModel();
}
protected function respond($data = null, ?int $status = null, string $message = '')
{
return ['status' => true, 'code' => $status, 'message' => $message, 'data' => $data];
}
protected function error(string $message = 'An error occurred', int $code = 400, ?array $errors = null)
{
return ['status' => false, 'code' => $code, 'message' => $message, 'errors' => $errors];
}
}
class InfoIconControllerTest extends CIUnitTestCase
{
private TestableInfoIconController $controller;
private StubInfoIconUserModel $userModel;
protected function setUp(): void
{
parent::setUp();
$this->controller = new TestableInfoIconController();
$this->userModel = new StubInfoIconUserModel();
$this->controller->setUserModel($this->userModel);
}
public function testRequiresAuthentication()
{
$response = $this->controller->profileIcon();
$this->assertFalse($response['status']);
$this->assertSame(401, $response['code']);
}
public function testReturnsNotFoundWhenUserMissing()
{
$this->controller->setCurrentUser((object) ['id' => 12]);
$response = $this->controller->profileIcon();
$this->assertFalse($response['status']);
$this->assertSame(404, $response['code']);
}
public function testReturnsUserProfileInfo()
{
$this->controller->setCurrentUser((object) ['id' => 7]);
$this->userModel->setUserInfoById(7, ['firstname' => 'Lana', 'lastname' => 'Kay', 'email' => 'test']);
$response = $this->controller->profileIcon();
$this->assertTrue($response['status']);
$this->assertSame('Profile icon info retrieved successfully', $response['message']);
$this->assertSame('Lana Kay', $response['data']['user_name']);
$this->assertSame('LK', $response['data']['user_initials']);
}
}
@@ -0,0 +1,257 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\MessagesController;
use App\Models\MessageModel as BaseMessageModel;
use App\Models\StudentClassModel as BaseStudentClassModel;
use App\Models\StudentModel as BaseStudentModel;
use App\Models\TeacherClassModel as BaseTeacherClassModel;
use App\Models\UserModel as BaseUserModel;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\HTTP\URI;
use CodeIgniter\HTTP\UserAgent;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\Mock\MockIncomingRequest;
use Config\App;
class FakeMessagesRequest extends MockIncomingRequest
{
public function __construct(private readonly ?array $json = null)
{
parent::__construct(config(App::class), new URI('http://example.com'), 'php://input', new UserAgent());
}
public function getJSON(bool $assoc = false, int $depth = 512, int $options = 0): ?array
{
return $this->json;
}
public function getPost($key = null, $filter = null, $default = null)
{
if ($key === null) {
return $this->json ?? [];
}
return $this->json[$key] ?? $default;
}
}
class StubMessageModel extends BaseMessageModel
{
public array $inbox = [];
public array $sent = [];
public array $drafts = [];
public array $trashed = [];
public array $inserted = [];
public array $marked = [];
public function __construct()
{
// avoid parent initialization
}
public function getInboxMessages($userId)
{
return $this->inbox;
}
public function getSentMessages($userId)
{
return $this->sent;
}
public function getDraftMessages($userId)
{
return $this->drafts;
}
public function getTrashedMessages($userId)
{
return $this->trashed;
}
public function insert($row = null, bool $returnID = true)
{
$this->inserted = is_array($row) ? $row : [];
return 123;
}
public function find($id = null)
{
return ['id' => $id ?? 0] + $this->inserted;
}
public function markAsRead($messageId)
{
$this->marked[] = $messageId;
}
}
class StubTeacherClassModel extends BaseTeacherClassModel
{
public array $data = [];
public function findAll($limit = 0, $offset = 0)
{
return $this->data;
}
}
class StubStudentClassModel extends BaseStudentClassModel
{
public array $perClass = [];
private $lastClass = null;
public function where($column, $value)
{
if ($column === 'class_section_id') {
$this->lastClass = $value;
}
return $this;
}
public function findAll($limit = 0, $offset = 0)
{
return $this->perClass[$this->lastClass] ?? [];
}
}
class StubStudentModel extends BaseStudentModel
{
public array $students = [];
public function find($id = null)
{
return $this->students[$id] ?? null;
}
}
class StubUserModel extends BaseUserModel
{
public array $users = [];
public function find($id = null)
{
return $this->users[$id] ?? null;
}
}
class TestableMessagesController extends MessagesController
{
public StubMessageModel $stubMessageModel;
public StubTeacherClassModel $stubTeacherClassModel;
public StubStudentClassModel $stubStudentClassModel;
public StubStudentModel $stubStudentModel;
public StubUserModel $stubUserModel;
public array $receivedMessages = [];
public array $read = [];
public function __construct()
{
// intentionally skip parent constructor
}
protected function success($data = null, string $message = 'Success', int $code = ResponseInterface::HTTP_OK)
{
return ['data' => $data, 'message' => $message, 'code' => $code];
}
protected function error(string $message = 'An error occurred', int $code = ResponseInterface::HTTP_BAD_REQUEST, ?array $errors = null)
{
return ['message' => $message, 'code' => $code, 'errors' => $errors];
}
protected function getCurrentUser(): ?object
{
return (object)[
'id' => 42,
'roles' => ['administrator'],
];
}
protected function getReceivedMessages(int $userId): array
{
return $this->receivedMessages;
}
protected function markAsRead(int $messageId): void
{
$this->read[] = $messageId;
}
protected function generateMessageNumber(): string
{
return 'MSG-TEST';
}
protected function getRecipientId(array $role): ?int
{
return 99;
}
public function setModels()
{
$this->messageModel = new StubMessageModel();
$this->teacherClassModel = new StubTeacherClassModel();
$this->studentClassModel = new StubStudentClassModel();
$this->studentModel = new StubStudentModel();
$this->userModel = new StubUserModel();
}
}
class MessagesControllerTest extends CIUnitTestCase
{
private TestableMessagesController $controller;
protected function setUp(): void
{
parent::setUp();
$this->controller = new TestableMessagesController();
$this->controller->setModels();
}
public function testInboxUsesMessageModel()
{
$this->controller->messageModel->inbox = [['id' => 1]];
$response = $this->controller->inbox();
$this->assertSame(ResponseInterface::HTTP_OK, $response['code']);
$this->assertSame([['id' => 1]], $response['data']);
}
public function testSendCreatesMessageWhenRecipientProvided()
{
$request = new FakeMessagesRequest([
'recipient_id' => 5,
'subject' => 'Hi',
'message' => 'Hello',
]);
$this->controller->setRequest($request);
$response = $this->controller->send();
$this->assertSame(ResponseInterface::HTTP_OK, $response['code']);
$this->assertSame('Message sent successfully', $response['message']);
$this->assertSame('MSG-TEST', $response['data']['message_number']);
$this->assertNotEmpty($this->controller->messageModel->inserted);
}
public function testGetRecipientsReturnsTeachers()
{
$this->controller->teacherClassModel->data = [
['teacher_id' => 12, 'class_section_id' => 1],
];
$this->controller->userModel->users = [
12 => ['firstname' => 'Jane', 'lastname' => 'Doe'],
];
$response = $this->controller->getRecipients('teacher');
$this->assertSame(ResponseInterface::HTTP_OK, $response['code']);
$this->assertCount(1, $response['data']);
$this->assertSame('Jane Doe', $response['data'][0]['name']);
}
}
@@ -0,0 +1,197 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\NotificationController;
use CodeIgniter\HTTP\URI;
use CodeIgniter\HTTP\UserAgent;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\Mock\MockIncomingRequest;
use Config\App;
class StubUserNotificationModel
{
public array $whereHistory = [];
private ?array $firstResult = null;
public ?array $lastUpdate = null;
public function select(string $columns): self
{
return $this;
}
public function join(string $table, string $cond): self
{
return $this;
}
public function where(string $column, $value = null): self
{
$this->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']);
}
}
@@ -0,0 +1,125 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\ParentController;
use App\Models\AllergyModel;
use App\Models\ClassSectionModel;
use App\Models\MedicalConditionModel;
use App\Models\StudentClassModel;
use App\Models\StudentModel;
use App\Models\UserModel;
use CodeIgniter\Test\CIUnitTestCase;
class TestableParentController extends ParentController
{
public function __construct()
{
// Avoid real model initialization
}
public function setModels(UserModel $userModel, StudentModel $studentModel, StudentClassModel $studentClassModel, ClassSectionModel $classSectionModel, MedicalConditionModel $medicalConditionModel, AllergyModel $allergyModel): void
{
$this->userModel = $userModel;
$this->studentModel = $studentModel;
$this->studentClassModel = $studentClassModel;
$this->classSectionModel = $classSectionModel;
$this->medicalConditionModel = $medicalConditionModel;
$this->allergyModel = $allergyModel;
}
}
class ParentControllerTest extends CIUnitTestCase
{
private TestableParentController $controller;
private $userModel;
private $studentModel;
private $studentClassModel;
private $classSectionModel;
private $medicalConditionModel;
private $allergyModel;
protected function setUp(): void
{
parent::setUp();
$this->userModel = $this->createMock(UserModel::class);
$this->studentModel = $this->createMock(StudentModel::class);
$this->studentClassModel = $this->createMock(StudentClassModel::class);
$this->classSectionModel = $this->createMock(ClassSectionModel::class);
$this->medicalConditionModel = $this->createMock(MedicalConditionModel::class);
$this->allergyModel = $this->createMock(AllergyModel::class);
$this->controller = new TestableParentController();
$this->controller->setModels(
$this->userModel,
$this->studentModel,
$this->studentClassModel,
$this->classSectionModel,
$this->medicalConditionModel,
$this->allergyModel
);
}
public function testShowReturnsParentById()
{
$this->userModel->method('select')->willReturnSelf();
$this->userModel->method('join')->willReturnSelf();
$this->userModel->method('where')->willReturnSelf();
$this->userModel->method('first')->willReturn(['id' => 7, 'firstname' => 'Sara']);
$response = $this->controller->show(7);
$this->assertTrue($response['status']);
$this->assertEquals('Parent retrieved successfully', $response['message']);
}
public function testShowMissingParentReturnsError()
{
$this->userModel->method('select')->willReturnSelf();
$this->userModel->method('join')->willReturnSelf();
$this->userModel->method('where')->willReturnSelf();
$this->userModel->method('first')->willReturn(null);
$response = $this->controller->show(99);
$this->assertFalse($response['status']);
$this->assertEquals(404, $response['code']);
}
public function testGetStudentsReturnsStudentList()
{
$this->userModel->method('find')->with(5)->willReturn(['id' => 5]);
$this->studentModel->method('where')->willReturnSelf();
$this->studentModel->method('findAll')->willReturn([
['id' => 11, 'firstname' => 'Meera', 'lastname' => 'Saleh'],
]);
$this->studentClassModel->method('select')->willReturnSelf();
$this->studentClassModel->method('join')->willReturnSelf();
$this->studentClassModel->method('where')->willReturnSelf();
$this->studentClassModel->method('orderBy')->willReturnSelf();
$this->studentClassModel->method('first')->willReturn(['id' => 2, 'class_section_id' => 3]);
$this->classSectionModel->method('getClassSectionNameBySectionId')->with(3)->willReturn('Grade 1A');
$this->medicalConditionModel->method('where')->willReturnSelf();
$this->medicalConditionModel->method('findColumn')->willReturn([]);
$this->allergyModel->method('where')->willReturnSelf();
$this->allergyModel->method('findColumn')->willReturn([]);
$response = $this->controller->getStudents(5);
$this->assertTrue($response['status']);
$this->assertNotEmpty($response['data']);
$this->assertEquals('Grade 1A', $response['data'][0]['class_section']);
$this->assertEquals(3, $response['data'][0]['current_class']['class_section_id']);
}
public function testGetStudentsMissingParent()
{
$this->userModel->method('find')->with(9)->willReturn(null);
$response = $this->controller->getStudents(9);
$this->assertFalse($response['status']);
$this->assertEquals(404, $response['code']);
}
}
@@ -0,0 +1,38 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\PolicyController;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Test\CIUnitTestCase;
class TestablePolicyController extends PolicyController
{
protected function success($data = null, string $message = 'Success', int $code = ResponseInterface::HTTP_OK)
{
return ['data' => $data, 'message' => $message, 'code' => $code];
}
}
class PolicyControllerTest extends CIUnitTestCase
{
public function testSchoolPicturePolicyProducesHtml()
{
$controller = new TestablePolicyController();
$response = $controller->schoolPicturePolicy();
$this->assertSame(ResponseInterface::HTTP_OK, $response['code']);
$this->assertStringContainsString('<div class="policy-text">', $response['data']['html']);
}
public function testSchoolPolicyReturnsMetaAndHtml()
{
$controller = new TestablePolicyController();
$response = $controller->schoolPolicy();
$this->assertSame(ResponseInterface::HTTP_OK, $response['code']);
$this->assertArrayHasKey('meta', $response['data']);
$this->assertArrayHasKey('html', $response['data']);
$this->assertStringContainsString('<!DOCTYPE html>', $response['data']['html']);
}
}
@@ -0,0 +1,250 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\PreferencesController;
use CodeIgniter\HTTP\URI;
use CodeIgniter\HTTP\UserAgent;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\Mock\MockIncomingRequest;
use Config\App;
use Config\Services;
class StubPreferencesModel
{
private ?array $firstResult = null;
private array $findResults = [];
private int $nextInsertId = 200;
public ?array $lastUpdate = null;
public ?array $lastInsertData = null;
public function where(string $column, $value): self
{
return $this;
}
public function first(): ?array
{
return $this->firstResult;
}
public function setFirstResult(?array $result): void
{
$this->firstResult = $result;
}
public function update(int $id, array $data): bool
{
$this->lastUpdate = ['id' => $id, 'data' => $data];
$this->findResults[$id] = ['id' => $id] + $data;
return true;
}
public function insert(array $data): int
{
$id = $this->nextInsertId++;
$this->lastInsertData = $data;
$this->findResults[$id] = ['id' => $id] + $data;
return $id;
}
public function find(int $id): ?array
{
return $this->findResults[$id] ?? null;
}
public function setFindResult(int $id, array $result): void
{
$this->findResults[$id] = $result;
}
}
class FakeJsonRequest extends MockIncomingRequest
{
public function __construct(private readonly ?array $payload)
{
parent::__construct(config(App::class), new URI('https://test.alrahmaisgl.org'), null, new UserAgent());
}
public function getJSON(bool $assoc = false, int $depth = 512, int $options = 0): ?array
{
return $this->payload;
}
}
class TestablePreferencesController extends PreferencesController
{
private ?object $currentUser = null;
public function __construct()
{
// Skip the real constructor to avoid bootstrapping models.
}
public function setPreferencesModel(StubPreferencesModel $model): void
{
$this->preferencesModel = $model;
}
public function setCurrentUser(?object $user): void
{
$this->currentUser = $user;
}
protected function getCurrentUser(): ?object
{
return $this->currentUser;
}
}
class PreferencesControllerTest extends CIUnitTestCase
{
private TestablePreferencesController $controller;
private StubPreferencesModel $preferencesModel;
protected function setUp(): void
{
parent::setUp();
Services::resetSingle('validation');
Services::resetSingle('session');
$this->preferencesModel = new StubPreferencesModel();
$this->controller = new TestablePreferencesController();
$this->controller->setPreferencesModel($this->preferencesModel);
}
public function testIndexRequiresAuthentication()
{
$this->controller->setCurrentUser(null);
$response = $this->controller->index();
$this->assertFalse($response['status']);
$this->assertSame(401, $response['code']);
$this->assertSame('Authentication required', $response['message']);
}
public function testIndexReturnsDefaultsIfNoPreferences()
{
$this->controller->setCurrentUser((object) ['id' => 7]);
$this->preferencesModel->setFirstResult(null);
$response = $this->controller->index();
$this->assertTrue($response['status']);
$this->assertSame('Preferences retrieved successfully', $response['message']);
$this->assertSame(1, $response['data']['notification_email']);
$this->assertSame('light', $response['data']['theme']);
$this->assertSame('blue', $response['data']['style_color']);
$this->assertSame('white', $response['data']['menu_color']);
}
public function testIndexPropagatesStoredPreferencesAndSessionStyles()
{
$this->controller->setCurrentUser((object) ['id' => 9]);
$this->preferencesModel->setFirstResult([
'id' => 11,
'user_id' => 9,
'notification_email' => 0,
'notification_sms' => 0,
'theme' => 'dark',
'language' => 'fr',
]);
session()->set('style_color', 'rose');
session()->set('menu_color', 'navy');
$response = $this->controller->index();
$this->assertTrue($response['status']);
$this->assertSame(0, $response['data']['notification_email']);
$this->assertSame('rose', $response['data']['style_color']);
$this->assertSame('navy', $response['data']['menu_color']);
}
public function testUpdateReturnsValidationErrors()
{
$this->controller->setCurrentUser((object) ['id' => 3]);
$this->setRequestPayload(['notification_email' => '2']);
$response = $this->controller->update();
$this->assertFalse($response['status']);
$this->assertSame(422, $response['code']);
$this->assertSame('Validation failed', $response['message']);
$this->assertArrayHasKey('errors', $response);
}
public function testUpdateInsertsWhenPreferencesAbsent()
{
$this->controller->setCurrentUser((object) ['id' => 12]);
$this->preferencesModel->setFirstResult(null);
$payload = [
'notification_email' => 0,
'notification_sms' => 1,
'theme' => 'dark',
'language' => 'es',
'style_color' => 'green',
'menu_color' => 'brand',
];
$this->setRequestPayload($payload);
$response = $this->controller->update();
$this->assertTrue($response['status']);
$this->assertSame('Preferences updated successfully', $response['message']);
$this->assertSame(0, $response['data']['notification_email']);
$this->assertSame('green', session()->get('style_color'));
$this->assertSame('brand', session()->get('menu_color'));
$this->assertSame($this->preferencesModel->lastInsertData, [
'user_id' => 12,
'notification_email' => 0,
'notification_sms' => 1,
'theme' => 'dark',
'language' => 'es',
]);
}
public function testUpdateModifiesExistingPreferences()
{
$this->controller->setCurrentUser((object) ['id' => 21]);
$this->preferencesModel->setFirstResult([
'id' => 18,
'user_id' => 21,
'notification_email' => 1,
'notification_sms' => 1,
'theme' => 'light',
'language' => 'en',
]);
$this->preferencesModel->setFindResult(18, [
'id' => 18,
'user_id' => 21,
'notification_email' => 1,
'notification_sms' => 1,
'theme' => 'light',
'language' => 'en',
]);
$payload = ['notification_email' => 1, 'notification_sms' => 0, 'language' => 'fr'];
$this->setRequestPayload($payload);
$response = $this->controller->update();
$this->assertTrue($response['status']);
$this->assertSame('Preferences updated successfully', $response['message']);
$this->assertSame(1, $this->preferencesModel->lastUpdate['data']['notification_email']);
$this->assertSame(0, $this->preferencesModel->lastUpdate['data']['notification_sms']);
$this->assertSame(18, $this->preferencesModel->lastUpdate['id']);
$this->assertSame('fr', $response['data']['language']);
}
private function setRequestPayload(?array $payload): void
{
$request = new FakeJsonRequest($payload);
$this->controller->setRequest($request);
}
}
@@ -0,0 +1,132 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\RolePermissionController;
use App\Models\PermissionModel;
use App\Models\RoleModel;
use App\Models\RolePermissionModel;
use App\Models\UserModel;
use CodeIgniter\Test\CIUnitTestCase;
class TestableRolePermissionController extends RolePermissionController
{
public function setModels(RoleModel $roleModel, PermissionModel $permissionModel, RolePermissionModel $rolePermissionModel, UserModel $userModel): void
{
$this->roleModel = $roleModel;
$this->permissionModel = $permissionModel;
$this->rolePermissionModel = $rolePermissionModel;
$this->userModel = $userModel;
}
private ?int $currentUserId = 1;
public function setCurrentUserId(int $id): void
{
$this->currentUserId = $id;
}
protected function getCurrentUserId(): ?int
{
return $this->currentUserId;
}
}
class RolePermissionControllerTest extends CIUnitTestCase
{
private $controller;
private $roleModel;
private $permissionModel;
private $rolePermissionModel;
private $userModel;
protected function setUp(): void
{
parent::setUp();
$this->roleModel = $this->createMock(RoleModel::class);
$this->permissionModel = $this->createMock(PermissionModel::class);
$this->rolePermissionModel = $this->createMock(RolePermissionModel::class);
$this->userModel = $this->createMock(UserModel::class);
$this->controller = new TestableRolePermissionController();
$this->controller->setModels($this->roleModel, $this->permissionModel, $this->rolePermissionModel, $this->userModel);
}
public function testRolesListReturnsData()
{
$this->roleModel->method('findAll')->willReturn([
['id' => 1, 'name' => 'admin'],
]);
$response = $this->controller->roles();
$this->assertTrue($response['status']);
$this->assertNotEmpty($response['data']);
}
public function testPermissionsListReturnsData()
{
$this->permissionModel->method('findAll')->willReturn([
['id' => 11, 'name' => 'view_reports'],
]);
$response = $this->controller->permissions();
$this->assertTrue($response['status']);
$this->assertNotEmpty($response['data']);
}
public function testRolePermissionsReturnsDataForRole()
{
$this->rolePermissionModel->method('where')->with('role_id', 2)->willReturnSelf();
$this->rolePermissionModel->method('findAll')->willReturn([
['permission_id' => 5],
]);
$response = $this->controller->rolePermissions(2);
$this->assertTrue($response['status']);
$this->assertEquals('Role permissions retrieved successfully', $response['message']);
}
public function testUsersListReturnsSummaries()
{
$this->userModel->method('select')->willReturnSelf();
$this->userModel->method('findAll')->willReturn([
['id' => 3, 'firstname' => 'Ali', 'lastname' => 'Khan'],
]);
$response = $this->controller->users();
$this->assertTrue($response['status']);
$this->assertCount(1, $response['data']);
}
public function testAssignRoleSuccess()
{
$dbMock = $this->createMock(\CodeIgniter\Database\BaseConnection::class);
$dbMock->method('transStart')->willReturn(null);
$dbMock->method('transComplete')->willReturn(null);
$dbMock->method('transStatus')->willReturn(true);
if (!class_exists('Config\\FakeDatabase')) {
eval('namespace Config; class FakeDatabase extends \\Config\\Database {
public static function connect($group = null, bool $getShared = true) {
return $GLOBALS["__fakeDb__"];
}
}');
}
$GLOBALS['__fakeDb__'] = $dbMock;
class_alias('Config\\FakeDatabase', 'Config\\Database');
$this->userRoleModel->method('where')->with('user_id', 7)->willReturnSelf();
$this->userRoleModel->method('delete')->willReturn(true);
$this->userRoleModel->method('insert')->willReturn(true);
$this->controller->setCurrentUserId(12);
$req = $this->makeRequest('POST', '/api/v1/users/7/roles', ['role_ids' => [1, 2]]);
$this->controller->setRequest($req);
$response = $this->controller->assignRole(7);
$this->assertTrue($response['status']);
$this->assertEquals('Roles assigned successfully', $response['message']);
}
}
@@ -0,0 +1,240 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\ScoreCommentController;
use CodeIgniter\HTTP\URI;
use CodeIgniter\HTTP\UserAgent;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\Mock\MockIncomingRequest;
use Config\App;
use Config\Services;
class FakeScoreRequest extends MockIncomingRequest
{
public function __construct(private readonly ?array $json, private readonly array $gets = [])
{
parent::__construct(config(App::class), new URI('https://test.alrahmaisgl.org'), 'php://input', new UserAgent());
}
public function getJSON(bool $assoc = false, int $depth = 512, int $options = 0): ?array
{
return $this->json;
}
public function getGet($key = null, $filter = null, $default = null)
{
if ($key === null) {
return $this->gets;
}
return $this->gets[$key] ?? $default;
}
}
class StubScoreCommentModel
{
private array $existingQueue = [];
public array $savedRecords = [];
public array $lastUpdate = [];
private int $lastInsert = 100;
public array $findAllResult = [];
public function where($column, $value = null): self
{
return $this;
}
public function first(): ?array
{
return array_shift($this->existingQueue) ?? null;
}
public function addExisting(array $row): void
{
$this->existingQueue[] = $row;
}
public function insert(array $data): int
{
$this->lastInsert++;
$id = $this->lastInsert;
$this->savedRecords[$id] = ['id' => $id] + $data;
return $id;
}
public function update(int $id, array $data): bool
{
$this->savedRecords[$id] = ['id' => $id] + $data;
$this->lastUpdate = ['id' => $id, 'data' => $data];
return true;
}
public function find(int $id): ?array
{
return $this->savedRecords[$id] ?? null;
}
public function setFind(int $id, array $row): void
{
$this->savedRecords[$id] = $row;
}
public function setFindAllResult(array $result): void
{
$this->findAllResult = $result;
}
public function findAll(): array
{
return $this->findAllResult;
}
}
class TestableScoreCommentController extends ScoreCommentController
{
private ?object $currentUser = null;
public function __construct()
{
// bypass real constructor logic.
}
public function setScoreCommentModel(StubScoreCommentModel $model): void
{
$this->scoreCommentModel = $model;
}
public function setCurrentUser(?object $user): void
{
$this->currentUser = $user;
}
public function setAcademicPeriod(string $schoolYear, string $semester): void
{
$this->schoolYear = $schoolYear;
$this->semester = $semester;
}
protected function getCurrentUser(): ?object
{
return $this->currentUser;
}
}
class ScoreCommentControllerTest extends CIUnitTestCase
{
private TestableScoreCommentController $controller;
private StubScoreCommentModel $model;
protected function setUp(): void
{
parent::setUp();
Services::resetSingle('validation');
$this->controller = new TestableScoreCommentController();
$this->model = new StubScoreCommentModel();
$this->controller->setScoreCommentModel($this->model);
$this->controller->setAcademicPeriod('2024-2025', 'Spring');
}
public function testSaveReturnsErrorWhenNoData()
{
$request = new FakeScoreRequest(null);
$this->controller->setRequest($request);
$this->controller->setCurrentUser((object) ['id' => 4]);
$response = $this->controller->save();
$this->assertFalse($response['status']);
$this->assertSame(400, $response['code']);
$this->assertSame('Invalid request data', $response['message']);
}
public function testSaveInsertsCommentsSkippingEmpty()
{
$request = new FakeScoreRequest([
'subject_id' => 7,
'comments' => [
101 => ['Exam' => 'Well done', 'Project' => ' '],
],
]);
$this->controller->setRequest($request);
$this->controller->setCurrentUser((object) ['id' => 22]);
$response = $this->controller->save();
$this->assertTrue($response['status']);
$this->assertSame('Comments saved successfully', $response['message']);
$this->assertCount(1, $response['data']);
$saved = $response['data'][0];
$this->assertSame('Well done', $saved['comment']);
$this->assertSame('exam', $saved['score_type']);
}
public function testSaveUpdatesExistingComment()
{
$existing = [
'id' => 55,
'student_id' => 201,
'score_type' => 'Exam',
];
$this->model->addExisting($existing);
$this->model->setFind(55, ['id' => 55, 'comment' => 'Old']);
$request = new FakeScoreRequest([
'subject_id' => 7,
'comments' => [
201 => ['Exam' => 'Better now'],
],
'semester' => 'Fall',
]);
$this->controller->setRequest($request);
$this->controller->setCurrentUser((object) ['id' => 9]);
$response = $this->controller->save();
$this->assertTrue($response['status']);
$this->assertSame(55, $this->model->lastUpdate['id']);
$this->assertSame('Better now', $this->model->lastUpdate['data']['comment']);
$this->assertSame('Fall', $this->model->lastUpdate['data']['semester']);
}
public function testGetByStudentReturnsComments()
{
$this->controller->setRequest(new FakeScoreRequest(null, ['subject_id' => 5, 'score_type' => 'exam']));
$this->model->setFindAllResult([['id' => 1, 'student_id' => 7]]);
$response = $this->controller->getByStudent(7);
$this->assertTrue($response['status']);
$this->assertCount(1, $response['data']);
$this->assertSame('Comments retrieved successfully', $response['message']);
}
public function testUpdateReturnsNotFoundWhenMissing()
{
$this->model->setFindAllResult([]);
$request = new FakeScoreRequest([]);
$this->controller->setRequest($request);
$response = $this->controller->update(99);
$this->assertFalse($response['status']);
$this->assertSame(404, $response['code']);
}
public function testUpdateModifiesComment()
{
$this->model->setFind(42, ['id' => 42, 'comment' => 'Old']);
$request = new FakeScoreRequest(['comment' => 'Fresh']);
$this->controller->setRequest($request);
$response = $this->controller->update(42);
$this->assertTrue($response['status']);
$this->assertSame('Fresh', $this->model->lastUpdate['data']['comment']);
$this->assertSame('Comment updated successfully', $response['message']);
}
}
@@ -0,0 +1,130 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\ScorePredictor;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Test\CIUnitTestCase;
class ScorePredictorStubBuilder
{
private string $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function select(...$args)
{
return $this;
}
public function join(...$args)
{
return $this;
}
public function where(...$args)
{
return $this;
}
public function groupBy(...$args)
{
return $this;
}
public function get()
{
return $this;
}
public function getResultArray(): array
{
return [];
}
public function getRow(): object
{
return (object)['mean' => 70, 'std' => 10];
}
}
class ScorePredictorStubDatabase
{
public function connect()
{
return true;
}
public function escape($value)
{
return "'" . addslashes((string)$value) . "'";
}
public function table(string $name)
{
return new ScorePredictorStubBuilder($name);
}
}
class StubConfigurationModel
{
public function getConfig($key)
{
return match ($key) {
'semester' => 'Fall',
'school_year' => '2025-2026',
'trophy_score' => 95,
'pass_score' => 60,
default => null,
};
}
}
class StubClassSectionModel
{
public function findAll(): array
{
return [];
}
}
class TestableScorePredictor extends ScorePredictor
{
public function __construct()
{
$this->db = new ScorePredictorStubDatabase();
$this->configModel = new StubConfigurationModel();
$this->classSectionModel = new StubClassSectionModel();
$this->semester = 'Fall';
$this->schoolYear = '2025-2026';
$this->targetTrophy = 95;
$this->targetLow = 60;
}
protected function success($data = null, string $message = 'Success', int $code = ResponseInterface::HTTP_OK)
{
return ['data' => $data, 'message' => $message, 'code' => $code];
}
protected function error(string $message = 'An error occurred', int $code = ResponseInterface::HTTP_BAD_REQUEST, ?array $errors = null)
{
return ['message' => $message, 'code' => $code, 'errors' => $errors];
}
}
class ScorePredictorTest extends CIUnitTestCase
{
public function testCombinedReportReturnsSuccess()
{
$controller = new TestableScorePredictor();
$response = $controller->combinedReport();
$this->assertSame(ResponseInterface::HTTP_OK, $response['code']);
$this->assertArrayHasKey('students', $response['data']);
$this->assertArrayHasKey('class_sections', $response['data']);
$this->assertSame('Score prediction generated', $response['message']);
}
}
@@ -0,0 +1,61 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\SessionTimeoutController;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Test\CIUnitTestCase;
class TestableSessionTimeoutController extends SessionTimeoutController
{
protected function success($data = null, string $message = 'Success', int $code = ResponseInterface::HTTP_OK)
{
return ['data' => $data, 'message' => $message, 'code' => $code];
}
protected function error(string $message = 'An error occurred', int $code = ResponseInterface::HTTP_BAD_REQUEST, ?array $errors = null)
{
return ['message' => $message, 'code' => $code, 'errors' => $errors];
}
}
class SessionTimeoutControllerTest extends CIUnitTestCase
{
protected function tearDown(): void
{
parent::tearDown();
session()->destroy();
}
public function testGetTimeoutConfigIncludesKeys()
{
$controller = new TestableSessionTimeoutController();
$response = $controller->getTimeoutConfig();
$this->assertSame(ResponseInterface::HTTP_OK, $response['code']);
$this->assertArrayHasKey('timeout', $response['data']);
$this->assertArrayHasKey('check_url', $response['data']);
}
public function testCheckTimeoutReturnsActiveWhenFresh()
{
session()->set('last_activity', time());
$controller = new TestableSessionTimeoutController();
$response = $controller->checkTimeout();
$this->assertSame(ResponseInterface::HTTP_OK, $response['code']);
$this->assertSame('Session active', $response['message']);
$this->assertSame('active', $response['data']['status']);
}
public function testPingActivityRefreshesSession()
{
$controller = new TestableSessionTimeoutController();
session()->set('last_activity', time());
$response = $controller->pingActivity();
$this->assertSame(ResponseInterface::HTTP_OK, $response['code']);
$this->assertSame('Session activity refreshed', $response['message']);
$this->assertSame('active', $response['data']['status']);
}
}
@@ -0,0 +1,89 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\SupplierController;
use App\Models\SupplierModel;
use CodeIgniter\Test\CIUnitTestCase;
class TestableSupplierController extends SupplierController
{
public function setSupplierModel(SupplierModel $model): void
{
$this->supplierModel = $model;
}
}
class SupplierControllerTest extends CIUnitTestCase
{
private $controller;
private $model;
protected function setUp(): void
{
parent::setUp();
$this->model = $this->createMock(SupplierModel::class);
$this->controller = new TestableSupplierController();
$this->controller->setSupplierModel($this->model);
}
public function testShowReturnsNotFound()
{
$this->model->method('find')->with(99)->willReturn(null);
$response = $this->controller->show(99);
$this->assertFalse($response['status']);
$this->assertEquals(404, $response['code']);
}
public function testCreateValidSupplier()
{
$payload = [
'name' => 'Campus Supplies',
'email' => 'supplies@example.com',
];
$req = $this->makeRequest('POST', '/api/v1/suppliers', $payload);
$this->controller->setRequest($req);
$this->model->method('insert')->with($this->arrayHasKey('name'))->willReturn(101);
$this->model->method('find')->with(101)->willReturn(['id' => 101, 'name' => 'Campus Supplies']);
$response = $this->controller->create();
$this->assertTrue($response['status']);
$this->assertEquals('Supplier created successfully', $response['message']);
$this->assertEquals(101, $response['data']['id']);
}
public function testUpdateSupplierPersistsFields()
{
$current = ['id' => 22, 'name' => 'Old Name'];
$this->model->method('find')->willReturnCallback(fn($id) => $current);
$this->model->method('update')->willReturnCallback(function($id, $data) use (&$current) {
$current = array_merge($current, $data);
return true;
});
$req = $this->makeRequest('PUT', '/api/v1/suppliers/22', [
'name' => 'New Vendor',
'phone' => '555-0199',
]);
$this->controller->setRequest($req);
$response = $this->controller->update(22);
$this->assertTrue($response['status']);
$this->assertEquals('Supplier updated successfully', $response['message']);
$this->assertEquals('New Vendor', $response['data']['name']);
}
public function testDeleteSupplier()
{
$this->model->method('find')->with(33)->willReturn(['id' => 33]);
$this->model->method('delete')->with(33)->willReturn(true);
$response = $this->controller->delete(33);
$this->assertTrue($response['status']);
$this->assertEquals('Supplier deleted successfully', $response['message']);
}
}
@@ -0,0 +1,207 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\TeacherController;
use App\Models\ConfigurationModel;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\Test\CIUnitTestCase;
class StubUserModel
{
private ?array $firstResult = null;
public function select(string $columns): self
{
return $this;
}
public function join(string $table, string $cond, ?string $type = null): self
{
return $this;
}
public function whereIn(string $column, array $values): self
{
return $this;
}
public function groupBy(string $column): self
{
return $this;
}
public function where(string $column, $value = null): self
{
return $this;
}
public function first(): ?array
{
return $this->firstResult;
}
public function setFirstResult(?array $result): void
{
$this->firstResult = $result;
}
}
class StubTeacherClassModel
{
private array $result = [];
public function select(string $columns): self
{
return $this;
}
public function join(string $table, string $cond, ?string $type = null): self
{
return $this;
}
public function where(string $column, $value = null): self
{
return $this;
}
public function findAll(): array
{
return $this->result;
}
public function setResult(array $result): void
{
$this->result = $result;
}
}
class TestableTeacherController extends TeacherController
{
private array $overridePagination = [];
public function __construct()
{
// Prevent the real controller bootstrapping from hitting the database.
}
public function setModels($userModel, $teacherClassModel, ConfigurationModel $configModel): void
{
$this->userModel = $userModel;
$this->teacherClassModel = $teacherClassModel;
$this->configModel = $configModel;
}
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);
}
}
class TeacherControllerTest extends CIUnitTestCase
{
private TestableTeacherController $controller;
private $userModel;
private $teacherClassModel;
private $configModel;
protected function setUp(): void
{
parent::setUp();
$this->userModel = new StubUserModel();
$this->teacherClassModel = new StubTeacherClassModel();
$this->configModel = $this->createMock(ConfigurationModel::class);
$this->configModel->method('getConfig')->willReturnMap([
['school_year', '2024-2025'],
['semester', 'Spring'],
]);
$this->controller = new TestableTeacherController();
$request = $this->createMock(IncomingRequest::class);
$request->method('getGet')->willReturn(null);
$this->controller->setRequest($request);
$this->controller->setModels($this->userModel, $this->teacherClassModel, $this->configModel);
}
public function testIndexSkipsSensitiveFieldsAndAddsAssignments()
{
$paginatedPayload = [
'data' => [
['id' => 5, 'email' => 'vera@example.com', 'password' => 'secret', 'token' => 'x'],
],
'pagination' => ['current_page' => 1, 'per_page' => 20, 'total' => 1, 'total_pages' => 1],
];
$assignments = [['class_section_id' => 7, 'school_year' => '2024-2025', 'semester' => 'Spring']];
$this->teacherClassModel->setResult($assignments);
$this->controller->setPaginatedResult($paginatedPayload);
$response = $this->controller->index();
$this->assertTrue($response['status']);
$this->assertSame('Teachers retrieved successfully', $response['message']);
$teachers = $response['data']['data'];
$this->assertCount(1, $teachers);
$teacher = $teachers[0];
$this->assertArrayNotHasKey('password', $teacher);
$this->assertArrayNotHasKey('token', $teacher);
$this->assertSame($assignments, $teacher['class_assignments']);
}
public function testShowReturnsTeacherWithAssignments()
{
$teacherRow = ['id' => 8, 'email' => 'nora@example.com', 'password' => 'secret', 'token' => 'abc'];
$this->userModel->setFirstResult($teacherRow);
$assignments = [['class_section_id' => 9]];
$this->teacherClassModel->setResult($assignments);
$response = $this->controller->show(8);
$this->assertTrue($response['status']);
$this->assertSame('Teacher retrieved successfully', $response['message']);
$teacher = $response['data'];
$this->assertArrayNotHasKey('password', $teacher);
$this->assertArrayNotHasKey('token', $teacher);
$this->assertSame($assignments, $teacher['class_assignments']);
}
public function testShowReturns404WhenTeacherMissing()
{
$this->userModel->setFirstResult(null);
$response = $this->controller->show(99);
$this->assertFalse($response['status']);
$this->assertSame(404, $response['code']);
$this->assertSame('Teacher not found', $response['message']);
}
public function testGetClassesReturnsAssignments()
{
$assignments = [['class_section_id' => 3]];
$this->teacherClassModel->setResult($assignments);
$response = $this->controller->getClasses(4);
$this->assertTrue($response['status']);
$this->assertSame('Teacher classes retrieved successfully', $response['message']);
$this->assertSame($assignments, $response['data']);
}
}
@@ -0,0 +1,54 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\TestDBController;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Test\CIUnitTestCase;
class StubQuery
{
public function getRow()
{
return (object)['result' => 1];
}
}
class StubDatabase
{
public function query(string $sql)
{
return new StubQuery();
}
}
class TestableTestDBController extends TestDBController
{
protected function success($data = null, string $message = 'Success', int $code = ResponseInterface::HTTP_OK)
{
return ['data' => $data, 'message' => $message, 'code' => $code];
}
protected function error(string $message = 'An error occurred', int $code = ResponseInterface::HTTP_BAD_REQUEST, ?array $errors = null)
{
return ['message' => $message, 'code' => $code, 'errors' => $errors];
}
protected function getDatabaseConnection()
{
return new StubDatabase();
}
}
class TestDBControllerTest extends CIUnitTestCase
{
public function testIndexReturnsSuccessWhenDatabaseOk()
{
$controller = new TestableTestDBController();
$response = $controller->index();
$this->assertSame(ResponseInterface::HTTP_OK, $response['code']);
$this->assertSame('Database check completed', $response['message']);
$this->assertSame('Database connection is successful.', $response['data']['message']);
}
}
@@ -0,0 +1,136 @@
<?php
namespace Tests\App\Controllers\Api;
use App\Controllers\Api\UiController;
use CodeIgniter\HTTP\IncomingRequest;
use CodeIgniter\HTTP\URI;
use CodeIgniter\HTTP\UserAgent;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\Mock\MockIncomingRequest;
use Config\App;
use Config\Services;
class FakeJsonRequest extends MockIncomingRequest
{
public function __construct(private readonly ?array $payload)
{
parent::__construct(config(App::class), new URI('https://test.alrahmaisgl.org'), 'php://input', new UserAgent());
}
public function getJSON(bool $assoc = false, int $depth = 512, int $options = 0): ?array
{
return $this->payload;
}
}
class TestableUiController extends UiController
{
private ?object $currentUser = null;
private ?IncomingRequest $requestOverride = null;
public function setCurrentUser(?object $user): void
{
$this->currentUser = $user;
}
public function setJsonRequest(FakeJsonRequest $request): void
{
$this->requestOverride = $request;
$this->setRequest($request);
}
protected function getCurrentUser(): ?object
{
return $this->currentUser;
}
}
class UiControllerTest extends CIUnitTestCase
{
private TestableUiController $controller;
protected function setUp(): void
{
parent::setUp();
Services::resetSingle('session');
$this->controller = new TestableUiController();
}
public function testGetStyleRequiresAuthentication()
{
$response = $this->controller->getStyle();
$this->assertFalse($response['status']);
$this->assertSame(401, $response['code']);
}
public function testGetStyleReturnsSessionColors()
{
$session = Services::session();
$session->set([
'style_color' => 'green',
'menu_color' => 'brand',
'menu_custom_bg' => '#123456',
'menu_custom_text' => '#ffffff',
'menu_custom_mode' => 'dark',
]);
$this->controller->setCurrentUser((object) ['id' => 5]);
$response = $this->controller->getStyle();
$this->assertTrue($response['status']);
$this->assertSame('green', $response['data']['style_color']);
$this->assertArrayHasKey('available_palettes', $response['data']);
}
public function testUpdateStyleInvalidJsonReturnsBadRequest()
{
$this->controller->setCurrentUser((object) ['id' => 10]);
$request = new FakeJsonRequest(null);
$this->controller->setJsonRequest($request);
$response = $this->controller->updateStyle();
$this->assertFalse($response['status']);
$this->assertSame(400, $response['code']);
}
public function testUpdateStyleChangesAccentAndMenu()
{
$this->controller->setCurrentUser((object) ['id' => 10]);
$request = new FakeJsonRequest([
'accent' => 'blue',
'menu' => 'sky',
]);
$this->controller->setJsonRequest($request);
$response = $this->controller->updateStyle();
$this->assertTrue($response['status']);
$this->assertSame('sky', $response['data']['menu_color']);
$this->assertSame('blue', $response['data']['style_color']);
}
public function testUpdateStyleHandlesCustomMenu()
{
$this->controller->setCurrentUser((object) ['id' => 10]);
$request = new FakeJsonRequest([
'accent' => 'purple',
'menu' => 'custom',
'menu_bg' => 'ffffff',
'menu_text' => '000000',
'menu_mode' => 'auto',
]);
$this->controller->setJsonRequest($request);
$response = $this->controller->updateStyle();
$this->assertTrue($response['status']);
$this->assertSame('custom', $response['data']['menu_color']);
$this->assertSame('#FFFFFF', session()->get('menu_custom_bg'));
$this->assertSame('#000000', session()->get('menu_custom_text'));
}
}
@@ -0,0 +1,134 @@
<?php
namespace Tests\App\Controllers;
use App\Controllers\AuthController;
use App\Models\IpAttemptModel;
use App\Models\LoginActivityModel;
use App\Models\UserModel;
use CodeIgniter\Database\BaseBuilder;
use CodeIgniter\Database\BaseConnection;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Test\CIUnitTestCase;
class TestableAuthController extends AuthController
{
public function __construct()
{
// Skip the real constructor to avoid bootstrapping live models
}
public function setRequest(RequestInterface $request): self
{
$this->request = $request;
return $this;
}
public function setResponse(ResponseInterface $response): self
{
$this->response = $response;
return $this;
}
public function setUserModel(UserModel $model): self
{
$this->userModel = $model;
return $this;
}
public function setIpAttemptModel(IpAttemptModel $model): self
{
$this->ipAttemptModel = $model;
return $this;
}
public function setLoginActivityModel(LoginActivityModel $model): self
{
$this->loginActivityModel = $model;
return $this;
}
}
class AuthControllerTest extends CIUnitTestCase
{
private TestableAuthController $controller;
private UserModel $userModel;
private IpAttemptModel $ipAttemptModel;
private LoginActivityModel $loginActivityModel;
protected function setUp(): void
{
parent::setUp();
$this->controller = new TestableAuthController();
$this->controller->setResponse(service('response'));
$this->userModel = $this->createMock(UserModel::class);
$this->ipAttemptModel = $this->createMock(IpAttemptModel::class);
$this->loginActivityModel = $this->createMock(LoginActivityModel::class);
$this->controller->setUserModel($this->userModel);
$this->controller->setIpAttemptModel($this->ipAttemptModel);
$this->controller->setLoginActivityModel($this->loginActivityModel);
$builder = $this->createMock(BaseBuilder::class);
$builder->method('select')->willReturnSelf();
$builder->method('join')->willReturnSelf();
$builder->method('where')->willReturnSelf();
$builder->method('get')->willReturnSelf();
$builder->method('getResultArray')->willReturn([
['name' => 'admin'],
]);
$fakeDb = $this->createMock(BaseConnection::class);
$fakeDb->method('table')->willReturn($builder);
if (! class_exists('Config\\FakeDatabase')) {
eval('namespace Config; class FakeDatabase extends \\Config\\Database { public static function connect($group = null, bool $getShared = true) { return $GLOBALS["__fakeDb__"]; } }');
}
$GLOBALS['__fakeDb__'] = $fakeDb;
class_alias('Config\\FakeDatabase', 'Config\\Database');
}
public function testApiLoginReturnsTokenAndRoles()
{
$payload = [
'email' => 'admin@example.com',
'password' => '12345678',
];
$user = [
'id' => 16,
'email' => $payload['email'],
'firstname' => 'Admin',
'lastname' => 'Panel',
'password' => pbkdf2_hash($payload['password']),
'is_suspended' => false,
'user_type' => 'admin',
];
$this->ipAttemptModel->method('where')->willReturnSelf();
$this->ipAttemptModel->method('first')->willReturn(null);
$this->userModel->method('where')->willReturnSelf();
$this->userModel->method('first')->willReturn($user);
$this->userModel->method('update')->willReturn(true);
$this->loginActivityModel->method('insert')->willReturn(true);
$this->controller->setRequest($this->makeRequest('POST', '/api/v1/login', $payload));
$response = $this->controller->apiLogin();
$body = json_decode((string) $response->getBody(), true);
$this->assertIsArray($body);
$this->assertTrue($body['status']);
$this->assertArrayHasKey('token', $body);
$this->assertArrayHasKey('user', $body);
$this->assertSame($user['id'], $body['user']['id']);
$this->assertArrayHasKey('roles', $body['user']);
$this->assertTrue($body['user']['roles']['admin'] ?? false);
$this->assertIsString($body['token']);
}
}
@@ -0,0 +1,257 @@
<?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'));
}
}
}
@@ -0,0 +1,82 @@
<?php
namespace Tests\App\Controllers\View {
use App\Controllers\View\RegisterController;
use CodeIgniter\HTTP\RedirectResponse;
use CodeIgniter\Test\CIUnitTestCase;
use Config\Services;
class TestableRegisterController extends RegisterController
{
public function __construct()
{
// Skip the heavy constructor that connects to the real database.
}
}
class RegisterControllerTest extends CIUnitTestCase
{
private TestableRegisterController $controller;
protected function setUp(): void
{
parent::setUp();
Services::resetSingle('session');
session()->start();
$this->controller = new TestableRegisterController();
}
protected function tearDown(): void
{
session()->remove('captcha_answer');
session()->remove('user_email');
parent::tearDown();
}
public function testIndexGeneratesCaptchaWhenMissing()
{
session()->remove('captcha_answer');
$response = $this->controller->index();
$this->assertIsString($response);
$captcha = session()->get('captcha_answer');
$this->assertNotEmpty($captcha);
$this->assertGreaterThanOrEqual(4, strlen($captcha));
$this->assertLessThanOrEqual(8, strlen($captcha));
}
public function testIndexReusesExistingCaptcha()
{
session()->set('captcha_answer', 'EXISTING');
$response = $this->controller->index();
$this->assertIsString($response);
$this->assertSame('EXISTING', session()->get('captcha_answer'));
}
public function testSuccessRedirectsWhenEmailMissing()
{
session()->remove('user_email');
$response = $this->controller->success();
$this->assertInstanceOf(RedirectResponse::class, $response);
$this->assertStringContainsString('/register', $response->getHeaderLine('Location') ?: '');
}
public function testSuccessReturnsViewWhenEmailPresent()
{
session()->set('user_email', 'parent@example.com');
$response = $this->controller->success();
$this->assertIsString($response);
$this->assertSame('parent@example.com', session()->get('user_email'));
}
}
}
@@ -0,0 +1,142 @@
<?php
namespace {
if (!function_exists('base_url')) {
function base_url($uri = '')
{
return 'https://test.alrahmaisgl.org/' . ltrim((string)$uri, '/');
}
}
if (!function_exists('site_url')) {
function site_url($uri = '')
{
return 'https://test.alrahmaisgl.org/' . ltrim((string)$uri, '/');
}
}
if (!function_exists('view')) {
function view($name, $data = [], $options = [])
{
return ['view' => $name, 'data' => $data, 'options' => $options];
}
}
}
namespace Tests\App\Controllers\View {
use App\Controllers\View\ReimbursementController;
use CodeIgniter\Database\BaseBuilder;
use CodeIgniter\Database\BaseConnection;
use CodeIgniter\Database\BaseResult;
use CodeIgniter\Test\CIUnitTestCase;
use Config\Services;
class TestableReimbursementController extends ReimbursementController
{
public function __construct()
{
// Skip the real parent constructor so we can inject mocks.
}
public function setDb(BaseConnection $db): self
{
$this->db = $db;
return $this;
}
}
class FakeRenderer
{
private array $data = [];
private array $lastRender = [];
public function setData(?array $data = null): self
{
$this->data = $data ?? [];
return $this;
}
public function render(string $view, array $data = [], $options = null)
{
$this->lastRender = [
'view' => $view,
'data' => $data ?: $this->data,
'options' => $options,
];
return 'fake-rendered:' . $view;
}
public function getLastRender(): array
{
return $this->lastRender;
}
}
class ReimbursementControllerTest extends CIUnitTestCase
{
private TestableReimbursementController $controller;
private FakeRenderer $renderer;
protected function setUp(): void
{
parent::setUp();
$this->renderer = new FakeRenderer();
Services::injectMock('renderer', $this->renderer);
$result = $this->createMock(BaseResult::class);
$result->method('getResultArray')->willReturn([
[
'id' => 5,
'expense_receipt' => 'uploads/path/expense_receipt.pdf',
'receipt_path' => null,
],
[
'id' => 6,
'expense_receipt' => null,
'receipt_path' => 'uploads/receipt_path.pdf',
],
]);
$builder = $this->createMock(BaseBuilder::class);
$builder->method('select')->willReturnSelf();
$builder->method('join')->willReturnSelf();
$builder->method('where')->willReturnSelf();
$builder->method('orderBy')->willReturnSelf();
$builder->method('get')->willReturn($result);
$db = $this->createMock(BaseConnection::class);
$db->method('table')->willReturn($builder);
$this->controller = new TestableReimbursementController();
$this->controller->setDb($db);
}
public function testUnderProcessingBuildsViewWithExpenses()
{
$result = $this->controller->underProcessing();
$this->assertSame('fake-rendered:reimbursements/under_processing', $result);
$rendered = $this->renderer->getLastRender();
$this->assertSame('reimbursements/under_processing', $rendered['view']);
$expenses = $rendered['data']['expenses'];
$this->assertCount(2, $expenses);
$first = $expenses[0];
$this->assertSame(site_url('receipts/expense_receipt.pdf'), $first['expense_receipt_url']);
$this->assertSame(
'<a href="' . base_url('reimbursements/create?expense_id=5') . '" class="btn btn-sm btn-primary">Reimburse</a>',
$first['action']
);
$second = $expenses[1];
$this->assertSame(site_url('receipts/receipt_path.pdf'), $second['expense_receipt_url']);
$this->assertSame(
'<a href="' . base_url('reimbursements/create?expense_id=6') . '" class="btn btn-sm btn-primary">Reimburse</a>',
$second['action']
);
}
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\AttendanceDataModel;
class AttendanceDataModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(AttendanceDataModel::class);
$this->assertNotNull($record->id);
$fetched = (new AttendanceDataModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(AttendanceDataModel::class);
$model = new AttendanceDataModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(AttendanceDataModel::class);
$model = new AttendanceDataModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\AttendanceRecordModel;
class AttendanceRecordModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(AttendanceRecordModel::class);
$this->assertNotNull($record->id);
$fetched = (new AttendanceRecordModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(AttendanceRecordModel::class);
$model = new AttendanceRecordModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(AttendanceRecordModel::class);
$model = new AttendanceRecordModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\AuthorizedUserModel;
class AuthorizedUserModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(AuthorizedUserModel::class);
$this->assertNotNull($record->id);
$fetched = (new AuthorizedUserModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(AuthorizedUserModel::class);
$model = new AuthorizedUserModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(AuthorizedUserModel::class);
$model = new AuthorizedUserModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\CalendarModel;
class CalendarModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(CalendarModel::class);
$this->assertNotNull($record->id);
$fetched = (new CalendarModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(CalendarModel::class);
$model = new CalendarModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(CalendarModel::class);
$model = new CalendarModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\ClassModel;
class ClassModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(ClassModel::class);
$this->assertNotNull($record->id);
$fetched = (new ClassModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(ClassModel::class);
$model = new ClassModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(ClassModel::class);
$model = new ClassModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\ClassSectionModel;
class ClassSectionModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(ClassSectionModel::class);
$this->assertNotNull($record->id);
$fetched = (new ClassSectionModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(ClassSectionModel::class);
$model = new ClassSectionModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(ClassSectionModel::class);
$model = new ClassSectionModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\ConfigurationModel;
class ConfigurationModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(ConfigurationModel::class);
$this->assertNotNull($record->id);
$fetched = (new ConfigurationModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(ConfigurationModel::class);
$model = new ConfigurationModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(ConfigurationModel::class);
$model = new ConfigurationModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\ContactUsModel;
class ContactUsModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(ContactUsModel::class);
$this->assertNotNull($record->id);
$fetched = (new ContactUsModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(ContactUsModel::class);
$model = new ContactUsModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(ContactUsModel::class);
$model = new ContactUsModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\CurrentFlagModel;
class CurrentFlagModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(CurrentFlagModel::class);
$this->assertNotNull($record->id);
$fetched = (new CurrentFlagModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(CurrentFlagModel::class);
$model = new CurrentFlagModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(CurrentFlagModel::class);
$model = new CurrentFlagModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\EmergencyContactModel;
class EmergencyContactModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(EmergencyContactModel::class);
$this->assertNotNull($record->id);
$fetched = (new EmergencyContactModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(EmergencyContactModel::class);
$model = new EmergencyContactModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(EmergencyContactModel::class);
$model = new EmergencyContactModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\EnrollmentModel;
class EnrollmentModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(EnrollmentModel::class);
$this->assertNotNull($record->id);
$fetched = (new EnrollmentModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(EnrollmentModel::class);
$model = new EnrollmentModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(EnrollmentModel::class);
$model = new EnrollmentModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\EventModel;
class EventModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(EventModel::class);
$this->assertNotNull($record->id);
$fetched = (new EventModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(EventModel::class);
$model = new EventModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(EventModel::class);
$model = new EventModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\ExamModel;
class ExamModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(ExamModel::class);
$this->assertNotNull($record->id);
$fetched = (new ExamModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(ExamModel::class);
$model = new ExamModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(ExamModel::class);
$model = new ExamModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\FinalExamModel;
class FinalExamModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(FinalExamModel::class);
$this->assertNotNull($record->id);
$fetched = (new FinalExamModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(FinalExamModel::class);
$model = new FinalExamModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(FinalExamModel::class);
$model = new FinalExamModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\FinalScoreModel;
class FinalScoreModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(FinalScoreModel::class);
$this->assertNotNull($record->id);
$fetched = (new FinalScoreModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(FinalScoreModel::class);
$model = new FinalScoreModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(FinalScoreModel::class);
$model = new FinalScoreModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\FlagModel;
class FlagModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(FlagModel::class);
$this->assertNotNull($record->id);
$fetched = (new FlagModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(FlagModel::class);
$model = new FlagModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(FlagModel::class);
$model = new FlagModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\HomeworkModel;
class HomeworkModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(HomeworkModel::class);
$this->assertNotNull($record->id);
$fetched = (new HomeworkModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(HomeworkModel::class);
$model = new HomeworkModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(HomeworkModel::class);
$model = new HomeworkModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\InvoiceModel;
class InvoiceModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(InvoiceModel::class);
$this->assertNotNull($record->id);
$fetched = (new InvoiceModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(InvoiceModel::class);
$model = new InvoiceModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(InvoiceModel::class);
$model = new InvoiceModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\InvoiceStudentListModel;
class InvoiceStudentListModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(InvoiceStudentListModel::class);
$this->assertNotNull($record->id);
$fetched = (new InvoiceStudentListModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(InvoiceStudentListModel::class);
$model = new InvoiceStudentListModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(InvoiceStudentListModel::class);
$model = new InvoiceStudentListModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\LoginActivityModel;
class LoginActivityModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(LoginActivityModel::class);
$this->assertNotNull($record->id);
$fetched = (new LoginActivityModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(LoginActivityModel::class);
$model = new LoginActivityModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(LoginActivityModel::class);
$model = new LoginActivityModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\MessageModel;
class MessageModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(MessageModel::class);
$this->assertNotNull($record->id);
$fetched = (new MessageModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(MessageModel::class);
$model = new MessageModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(MessageModel::class);
$model = new MessageModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\MidtermExamModel;
class MidtermExamModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(MidtermExamModel::class);
$this->assertNotNull($record->id);
$fetched = (new MidtermExamModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(MidtermExamModel::class);
$model = new MidtermExamModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(MidtermExamModel::class);
$model = new MidtermExamModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,50 @@
<?php
use App\Models\NotificationModel;
use CodeIgniter\Test\CIUnitTestCase;
class NotificationModelTest extends CIUnitTestCase
{
protected $model;
protected function setUp(): void
{
parent::setUp();
$this->model = new NotificationModel();
}
public function testGetActiveNotifications()
{
$result = $this->model->getActiveNotifications();
$this->assertIsArray($result);
}
public function testGetDeletedNotifications()
{
$result = $this->model->getDeletedNotifications();
$this->assertIsArray($result);
}
public function testRestoreNotification()
{
// Create + soft delete a test record
$id = $this->model->insert([
'title' => 'Test restore',
'message' => 'Test message',
'target_group' => 'parent',
'delivery_channels' => 'in_app',
'priority' => 'normal',
'status' => 'sent',
'scheduled_at' => date('Y-m-d H:i:s'),
]);
$this->model->delete($id);
$this->assertNotNull($this->model->onlyDeleted()->find($id));
// Restore
$this->model->restoreNotification($id);
$this->assertNull($this->model->onlyDeleted()->find($id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\ParentModel;
class ParentModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(ParentModel::class);
$this->assertNotNull($record->id);
$fetched = (new ParentModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(ParentModel::class);
$model = new ParentModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(ParentModel::class);
$model = new ParentModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\ParticipationModel;
class ParticipationModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(ParticipationModel::class);
$this->assertNotNull($record->id);
$fetched = (new ParticipationModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(ParticipationModel::class);
$model = new ParticipationModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(ParticipationModel::class);
$model = new ParticipationModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\PasswordResetModel;
class PasswordResetModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(PasswordResetModel::class);
$this->assertNotNull($record->id);
$fetched = (new PasswordResetModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(PasswordResetModel::class);
$model = new PasswordResetModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(PasswordResetModel::class);
$model = new PasswordResetModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\PaymentModel;
class PaymentModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(PaymentModel::class);
$this->assertNotNull($record->id);
$fetched = (new PaymentModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(PaymentModel::class);
$model = new PaymentModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(PaymentModel::class);
$model = new PaymentModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\PaymentTransactionModel;
class PaymentTransactionModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(PaymentTransactionModel::class);
$this->assertNotNull($record->id);
$fetched = (new PaymentTransactionModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(PaymentTransactionModel::class);
$model = new PaymentTransactionModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(PaymentTransactionModel::class);
$model = new PaymentTransactionModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\PaypalTransactionModel;
class PaypalTransactionModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(PaypalTransactionModel::class);
$this->assertNotNull($record->id);
$fetched = (new PaypalTransactionModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(PaypalTransactionModel::class);
$model = new PaypalTransactionModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(PaypalTransactionModel::class);
$model = new PaypalTransactionModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\PermissionModel;
class PermissionModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(PermissionModel::class);
$this->assertNotNull($record->id);
$fetched = (new PermissionModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(PermissionModel::class);
$model = new PermissionModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(PermissionModel::class);
$model = new PermissionModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\PreferencesModel;
class PreferencesModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(PreferencesModel::class);
$this->assertNotNull($record->id);
$fetched = (new PreferencesModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(PreferencesModel::class);
$model = new PreferencesModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(PreferencesModel::class);
$model = new PreferencesModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\ProjectModel;
class ProjectModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(ProjectModel::class);
$this->assertNotNull($record->id);
$fetched = (new ProjectModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(ProjectModel::class);
$model = new ProjectModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(ProjectModel::class);
$model = new ProjectModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\QuizModel;
class QuizModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(QuizModel::class);
$this->assertNotNull($record->id);
$fetched = (new QuizModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(QuizModel::class);
$model = new QuizModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(QuizModel::class);
$model = new QuizModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\RoleModel;
class RoleModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(RoleModel::class);
$this->assertNotNull($record->id);
$fetched = (new RoleModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(RoleModel::class);
$model = new RoleModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(RoleModel::class);
$model = new RoleModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\RolePermissionModel;
class RolePermissionModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(RolePermissionModel::class);
$this->assertNotNull($record->id);
$fetched = (new RolePermissionModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(RolePermissionModel::class);
$model = new RolePermissionModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(RolePermissionModel::class);
$model = new RolePermissionModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\ScoreModel;
class ScoreModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(ScoreModel::class);
$this->assertNotNull($record->id);
$fetched = (new ScoreModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(ScoreModel::class);
$model = new ScoreModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(ScoreModel::class);
$model = new ScoreModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\SectionModel;
class SectionModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(SectionModel::class);
$this->assertNotNull($record->id);
$fetched = (new SectionModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(SectionModel::class);
$model = new SectionModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(SectionModel::class);
$model = new SectionModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\SemesterScoreModel;
class SemesterScoreModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(SemesterScoreModel::class);
$this->assertNotNull($record->id);
$fetched = (new SemesterScoreModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(SemesterScoreModel::class);
$model = new SemesterScoreModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(SemesterScoreModel::class);
$model = new SemesterScoreModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\SettingsModel;
class SettingsModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(SettingsModel::class);
$this->assertNotNull($record->id);
$fetched = (new SettingsModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(SettingsModel::class);
$model = new SettingsModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(SettingsModel::class);
$model = new SettingsModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\StatsModel;
class StatsModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(StatsModel::class);
$this->assertNotNull($record->id);
$fetched = (new StatsModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(StatsModel::class);
$model = new StatsModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(StatsModel::class);
$model = new StatsModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\StudentClassModel;
class StudentClassModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(StudentClassModel::class);
$this->assertNotNull($record->id);
$fetched = (new StudentClassModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(StudentClassModel::class);
$model = new StudentClassModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(StudentClassModel::class);
$model = new StudentClassModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\StudentModel;
class StudentModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(StudentModel::class);
$this->assertNotNull($record->id);
$fetched = (new StudentModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(StudentModel::class);
$model = new StudentModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(StudentModel::class);
$model = new StudentModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\SupportRequestModel;
class SupportRequestModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(SupportRequestModel::class);
$this->assertNotNull($record->id);
$fetched = (new SupportRequestModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(SupportRequestModel::class);
$model = new SupportRequestModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(SupportRequestModel::class);
$model = new SupportRequestModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\TeacherClassModel;
class TeacherClassModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(TeacherClassModel::class);
$this->assertNotNull($record->id);
$fetched = (new TeacherClassModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(TeacherClassModel::class);
$model = new TeacherClassModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(TeacherClassModel::class);
$model = new TeacherClassModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\TeacherModel;
class TeacherModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(TeacherModel::class);
$this->assertNotNull($record->id);
$fetched = (new TeacherModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(TeacherModel::class);
$model = new TeacherModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(TeacherModel::class);
$model = new TeacherModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\UserModel;
class UserModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(UserModel::class);
$this->assertNotNull($record->id);
$fetched = (new UserModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(UserModel::class);
$model = new UserModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(UserModel::class);
$model = new UserModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\UserNotificationModel;
class UserNotificationModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(UserNotificationModel::class);
$this->assertNotNull($record->id);
$fetched = (new UserNotificationModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(UserNotificationModel::class);
$model = new UserNotificationModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(UserNotificationModel::class);
$model = new UserNotificationModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Tests\App\Models;
use Tests\Support\DBReset;
use CodeIgniter\Test\CIUnitTestCase;
use App\Models\UserRoleModel;
class UserRoleModelTest extends CIUnitTestCase
{
protected function setUp(): void
{
parent::setUp();
DBReset::resetDatabase();
}
public function testCanInsertAndRetrieve()
{
$record = fake(UserRoleModel::class);
$this->assertNotNull($record->id);
$fetched = (new UserRoleModel())->find($record->id);
$this->assertEquals($record->id, $fetched->id);
}
public function testCanUpdate()
{
$record = fake(UserRoleModel::class);
$model = new UserRoleModel();
$model->update($record->id, ['updated_at' => date('Y-m-d H:i:s')]);
$this->assertTrue(true); // simple test to verify no exception thrown
}
public function testCanDelete()
{
$record = fake(UserRoleModel::class);
$model = new UserRoleModel();
$model->delete($record->id);
$this->assertNull($model->find($record->id));
}
}
+18
View File
@@ -0,0 +1,18 @@
<?php
// test_smtp.php
$host = 'smtp.hostinger.com';
$ports = [587, 465];
foreach ($ports as $port) {
$connection = @fsockopen($host, $port, $errno, $errstr, 10);
if (is_resource($connection)) {
echo "✅ Port $port is OPEN\n";
fclose($connection);
} else {
echo "❌ Port $port is CLOSED: $errstr ($errno)\n";
}
}
// Test DNS
echo "DNS resolution: " . gethostbyname('smtp.hostinger.com') . "\n";
+46
View File
@@ -0,0 +1,46 @@
<?php
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\DatabaseTestTrait;
use Tests\Support\Database\Seeds\ExampleSeeder;
use Tests\Support\Models\ExampleModel;
/**
* @internal
*/
final class ExampleDatabaseTest extends CIUnitTestCase
{
use DatabaseTestTrait;
protected $seed = ExampleSeeder::class;
public function testModelFindAll(): void
{
$model = new ExampleModel();
// Get every row created by ExampleSeeder
$objects = $model->findAll();
// Make sure the count is as expected
$this->assertCount(3, $objects);
}
public function testSoftDeleteLeavesRow(): void
{
$model = new ExampleModel();
$this->setPrivateProperty($model, 'useSoftDeletes', true);
$this->setPrivateProperty($model, 'tempUseSoftDeletes', true);
/** @var stdClass $object */
$object = $model->first();
$model->delete($object->id);
// The model should no longer find it
$this->assertNull($model->find($object->id));
// ... but it should still be in the database
$result = $model->builder()->where('id', $object->id)->get()->getResult();
$this->assertCount(1, $result);
}
}
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
+18
View File
@@ -0,0 +1,18 @@
<?php
use CodeIgniter\Test\CIUnitTestCase;
use Config\Services;
/**
* @internal
*/
final class ExampleSessionTest extends CIUnitTestCase
{
public function testSessionSimple(): void
{
$session = Services::session();
$session->set('logged_in', 123);
$this->assertSame(123, $session->get('logged_in'));
}
}