recreate project
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
<IfModule authz_core_module>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
<IfModule !authz_core_module>
|
||||
Deny from all
|
||||
</IfModule>
|
||||
+118
@@ -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.
|
||||
@@ -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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>403 Forbidden</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>Directory access is forbidden.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user