@@ -38,6 +38,7 @@ jobs:
|
|||||||
database.tests.DBDriver: MySQLi
|
database.tests.DBDriver: MySQLi
|
||||||
database.tests.DBPrefix: ''
|
database.tests.DBPrefix: ''
|
||||||
database.tests.port: 3306
|
database.tests.port: 3306
|
||||||
|
JWT_SECRET: test-secret-for-ci
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -48,7 +49,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
php-version: '8.2'
|
php-version: '8.2'
|
||||||
extensions: dom, gd, intl, mbstring, mysqli, zip
|
extensions: dom, gd, intl, mbstring, mysqli, zip
|
||||||
coverage: none
|
coverage: xdebug
|
||||||
|
|
||||||
- name: Validate Composer config
|
- name: Validate Composer config
|
||||||
run: composer validate --no-check-publish --strict
|
run: composer validate --no-check-publish --strict
|
||||||
@@ -56,5 +57,26 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: composer install --no-interaction --prefer-dist --no-progress
|
run: composer install --no-interaction --prefer-dist --no-progress
|
||||||
|
|
||||||
|
- name: Prepare writable directories
|
||||||
|
run: |
|
||||||
|
mkdir -p \
|
||||||
|
writable/cache/testing \
|
||||||
|
writable/logs \
|
||||||
|
writable/session \
|
||||||
|
writable/uploads \
|
||||||
|
writable/debugbar
|
||||||
|
chown -R "$(id -u):$(id -g)" writable || true
|
||||||
|
chmod -R u+rwX,g+rwX writable
|
||||||
|
|
||||||
|
- name: Verify writable cache
|
||||||
|
run: |
|
||||||
|
test -d writable/cache/testing
|
||||||
|
test -w writable/cache/testing
|
||||||
|
touch writable/cache/testing/.ci-write-test
|
||||||
|
rm writable/cache/testing/.ci-write-test
|
||||||
|
|
||||||
|
- name: Run database migrations
|
||||||
|
run: php spark migrate --all --no-interaction
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: composer test
|
run: composer test
|
||||||
|
|||||||
@@ -168,4 +168,12 @@ class Cache extends BaseConfig
|
|||||||
'redis' => RedisHandler::class,
|
'redis' => RedisHandler::class,
|
||||||
'wincache' => WincacheHandler::class,
|
'wincache' => WincacheHandler::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if (ENVIRONMENT === 'testing') {
|
||||||
|
$this->storePath = WRITEPATH . 'cache/testing/';
|
||||||
|
$this->file['storePath'] = WRITEPATH . 'cache/testing/';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,13 +211,7 @@ class AuthController extends BaseController
|
|||||||
$this->logLoginAttempt($user['id'], $user['email'], $ip, $this->request->getUserAgent());
|
$this->logLoginAttempt($user['id'], $user['email'], $ip, $this->request->getUserAgent());
|
||||||
|
|
||||||
// Fetch roles
|
// Fetch roles
|
||||||
$userRoleModel = new UserRoleModel();
|
$roleNames = $this->getUserRoleNames((int) $user['id']);
|
||||||
$rolesRows = $userRoleModel->select('roles.name')
|
|
||||||
->join('roles', 'roles.id = user_roles.role_id')
|
|
||||||
->where('user_roles.user_id', $user['id'])
|
|
||||||
->get()
|
|
||||||
->getResultArray();
|
|
||||||
$roleNames = array_column($rolesRows, 'name');
|
|
||||||
|
|
||||||
// Build roles map (object with keys per example)
|
// Build roles map (object with keys per example)
|
||||||
$rolesMap = [];
|
$rolesMap = [];
|
||||||
@@ -454,6 +448,18 @@ class AuthController extends BaseController
|
|||||||
$this->userModel->update($userId, ['failed_attempts' => 0, 'last_failed_at' => null]);
|
$this->userModel->update($userId, ['failed_attempts' => 0, 'last_failed_at' => null]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getUserRoleNames(int $userId): array
|
||||||
|
{
|
||||||
|
$userRoleModel = new UserRoleModel();
|
||||||
|
$rolesRows = $userRoleModel->select('roles.name')
|
||||||
|
->join('roles', 'roles.id = user_roles.role_id')
|
||||||
|
->where('user_roles.user_id', $userId)
|
||||||
|
->get()
|
||||||
|
->getResultArray();
|
||||||
|
|
||||||
|
return array_column($rolesRows, 'name');
|
||||||
|
}
|
||||||
|
|
||||||
public function logout()
|
public function logout()
|
||||||
{
|
{
|
||||||
// Get user ID and email from session
|
// Get user ID and email from session
|
||||||
|
|||||||
@@ -43,6 +43,8 @@
|
|||||||
</exclude>
|
</exclude>
|
||||||
</source>
|
</source>
|
||||||
<php>
|
<php>
|
||||||
|
<env name="CI_ENVIRONMENT" value="testing"/>
|
||||||
|
<env name="database.tests.DBPrefix" value=""/>
|
||||||
<server name="app.baseURL" value="https://test.alrahmaisgl.org/"/>
|
<server name="app.baseURL" value="https://test.alrahmaisgl.org/"/>
|
||||||
<server name="CODEIGNITER_SCREAM_DEPRECATIONS" value="0"/>
|
<server name="CODEIGNITER_SCREAM_DEPRECATIONS" value="0"/>
|
||||||
<!-- Directory containing phpunit.xml -->
|
<!-- Directory containing phpunit.xml -->
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class DBReset
|
|||||||
|
|
||||||
foreach ($tables as $table) {
|
foreach ($tables as $table) {
|
||||||
if (!in_array($table, self::$excludeTables)) {
|
if (!in_array($table, self::$excludeTables)) {
|
||||||
$db->table($table)->truncate();
|
$db->query('TRUNCATE TABLE ' . $db->escapeIdentifiers($table));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,34 @@
|
|||||||
<?php
|
<?php
|
||||||
// tests/_support/bootstrap.php
|
// tests/_support/bootstrap.php
|
||||||
|
|
||||||
|
if (! getenv('CI_ENVIRONMENT')) {
|
||||||
|
putenv('CI_ENVIRONMENT=testing');
|
||||||
|
$_ENV['CI_ENVIRONMENT'] = 'testing';
|
||||||
|
$_SERVER['CI_ENVIRONMENT'] = 'testing';
|
||||||
|
}
|
||||||
|
|
||||||
|
$writablePath = realpath(__DIR__ . '/../..') . DIRECTORY_SEPARATOR . 'writable';
|
||||||
|
$runtimeDirectories = [
|
||||||
|
$writablePath . DIRECTORY_SEPARATOR . 'cache',
|
||||||
|
$writablePath . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . 'testing',
|
||||||
|
$writablePath . DIRECTORY_SEPARATOR . 'logs',
|
||||||
|
$writablePath . DIRECTORY_SEPARATOR . 'session',
|
||||||
|
$writablePath . DIRECTORY_SEPARATOR . 'uploads',
|
||||||
|
$writablePath . DIRECTORY_SEPARATOR . 'debugbar',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($runtimeDirectories as $directory) {
|
||||||
|
if (! is_dir($directory) && ! mkdir($directory, 0775, true) && ! is_dir($directory)) {
|
||||||
|
fwrite(STDERR, "Unable to create writable test directory: {$directory}" . PHP_EOL);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! is_writable($directory)) {
|
||||||
|
fwrite(STDERR, "Writable test directory is not writable: {$directory}" . PHP_EOL);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Load Composer autoloader
|
// Load Composer autoloader
|
||||||
require __DIR__ . '/../../vendor/autoload.php';
|
require __DIR__ . '/../../vendor/autoload.php';
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,35 @@ use App\Controllers\AuthController;
|
|||||||
use App\Models\IpAttemptModel;
|
use App\Models\IpAttemptModel;
|
||||||
use App\Models\LoginActivityModel;
|
use App\Models\LoginActivityModel;
|
||||||
use App\Models\UserModel;
|
use App\Models\UserModel;
|
||||||
use CodeIgniter\Database\BaseBuilder;
|
|
||||||
use CodeIgniter\Database\BaseConnection;
|
|
||||||
use CodeIgniter\HTTP\RequestInterface;
|
use CodeIgniter\HTTP\RequestInterface;
|
||||||
use CodeIgniter\HTTP\ResponseInterface;
|
use CodeIgniter\HTTP\ResponseInterface;
|
||||||
|
use CodeIgniter\HTTP\URI;
|
||||||
|
use CodeIgniter\HTTP\UserAgent;
|
||||||
use CodeIgniter\Test\CIUnitTestCase;
|
use CodeIgniter\Test\CIUnitTestCase;
|
||||||
|
use CodeIgniter\Test\Mock\MockIncomingRequest;
|
||||||
|
use Config\App;
|
||||||
|
|
||||||
|
class FakeAuthJsonRequest 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPost($index = null, $filter = null, $flags = null)
|
||||||
|
{
|
||||||
|
if ($index === null) {
|
||||||
|
return $this->payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->payload[$index] ?? null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class TestableAuthController extends AuthController
|
class TestableAuthController extends AuthController
|
||||||
{
|
{
|
||||||
@@ -48,6 +72,11 @@ class TestableAuthController extends AuthController
|
|||||||
$this->loginActivityModel = $model;
|
$this->loginActivityModel = $model;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getUserRoleNames(int $userId): array
|
||||||
|
{
|
||||||
|
return ['admin'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AuthControllerTest extends CIUnitTestCase
|
class AuthControllerTest extends CIUnitTestCase
|
||||||
@@ -60,35 +89,32 @@ class AuthControllerTest extends CIUnitTestCase
|
|||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
helper('security');
|
||||||
|
putenv('JWT_SECRET=test-secret-for-auth-controller');
|
||||||
|
$_ENV['JWT_SECRET'] = 'test-secret-for-auth-controller';
|
||||||
|
$_SERVER['JWT_SECRET'] = 'test-secret-for-auth-controller';
|
||||||
|
|
||||||
$this->controller = new TestableAuthController();
|
$this->controller = new TestableAuthController();
|
||||||
$this->controller->setResponse(service('response'));
|
$this->controller->setResponse(service('response'));
|
||||||
|
|
||||||
$this->userModel = $this->createMock(UserModel::class);
|
$this->userModel = $this->getMockBuilder(UserModel::class)
|
||||||
$this->ipAttemptModel = $this->createMock(IpAttemptModel::class);
|
->disableOriginalConstructor()
|
||||||
$this->loginActivityModel = $this->createMock(LoginActivityModel::class);
|
->addMethods(['where'])
|
||||||
|
->onlyMethods(['first', 'update'])
|
||||||
|
->getMock();
|
||||||
|
$this->ipAttemptModel = $this->getMockBuilder(IpAttemptModel::class)
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->addMethods(['where'])
|
||||||
|
->onlyMethods(['first'])
|
||||||
|
->getMock();
|
||||||
|
$this->loginActivityModel = $this->getMockBuilder(LoginActivityModel::class)
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->onlyMethods(['insert'])
|
||||||
|
->getMock();
|
||||||
|
|
||||||
$this->controller->setUserModel($this->userModel);
|
$this->controller->setUserModel($this->userModel);
|
||||||
$this->controller->setIpAttemptModel($this->ipAttemptModel);
|
$this->controller->setIpAttemptModel($this->ipAttemptModel);
|
||||||
$this->controller->setLoginActivityModel($this->loginActivityModel);
|
$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()
|
public function testApiLoginReturnsTokenAndRoles()
|
||||||
@@ -117,13 +143,13 @@ class AuthControllerTest extends CIUnitTestCase
|
|||||||
|
|
||||||
$this->loginActivityModel->method('insert')->willReturn(true);
|
$this->loginActivityModel->method('insert')->willReturn(true);
|
||||||
|
|
||||||
$this->controller->setRequest($this->makeRequest('POST', '/api/v1/login', $payload));
|
$this->controller->setRequest(new FakeAuthJsonRequest($payload));
|
||||||
|
|
||||||
$response = $this->controller->apiLogin();
|
$response = $this->controller->apiLogin();
|
||||||
$body = json_decode((string) $response->getBody(), true);
|
$body = json_decode((string) $response->getBody(), true);
|
||||||
|
|
||||||
$this->assertIsArray($body);
|
$this->assertIsArray($body);
|
||||||
$this->assertTrue($body['status']);
|
$this->assertTrue($body['status'], json_encode($body));
|
||||||
$this->assertArrayHasKey('token', $body);
|
$this->assertArrayHasKey('token', $body);
|
||||||
$this->assertArrayHasKey('user', $body);
|
$this->assertArrayHasKey('user', $body);
|
||||||
$this->assertSame($user['id'], $body['user']['id']);
|
$this->assertSame($user['id'], $body['user']['id']);
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ use CodeIgniter\HTTP\RedirectResponse;
|
|||||||
|
|
||||||
protected function tearDown(): void
|
protected function tearDown(): void
|
||||||
{
|
{
|
||||||
|
Services::resetSingle('renderer');
|
||||||
session()->destroy();
|
session()->destroy();
|
||||||
parent::tearDown();
|
parent::tearDown();
|
||||||
}
|
}
|
||||||
@@ -181,9 +182,14 @@ use CodeIgniter\HTTP\RedirectResponse;
|
|||||||
|
|
||||||
$result = $this->controller->absenceFormAdmin();
|
$result = $this->controller->absenceFormAdmin();
|
||||||
|
|
||||||
|
if (is_array($result)) {
|
||||||
|
$this->assertSame('administrator/absence_vacation', $result['view']);
|
||||||
|
$data = $result['data'];
|
||||||
|
} else {
|
||||||
$this->assertSame('fake-render', $result);
|
$this->assertSame('fake-render', $result);
|
||||||
$this->assertSame('administrator/absence_vacation', $this->renderer->lastName);
|
$this->assertSame('administrator/absence_vacation', $this->renderer->lastName);
|
||||||
$data = $this->renderer->lastData;
|
$data = $this->renderer->lastData;
|
||||||
|
}
|
||||||
$this->assertSame('Alex Admin', $data['admin_name']);
|
$this->assertSame('Alex Admin', $data['admin_name']);
|
||||||
$this->assertSame('Fall', $data['semester']);
|
$this->assertSame('Fall', $data['semester']);
|
||||||
$this->assertSame('2024-2025', $data['schoolYear']);
|
$this->assertSame('2024-2025', $data['schoolYear']);
|
||||||
|
|||||||
@@ -42,11 +42,18 @@ namespace Tests\App\Controllers\View {
|
|||||||
|
|
||||||
$response = $this->controller->index();
|
$response = $this->controller->index();
|
||||||
|
|
||||||
|
if (is_array($response)) {
|
||||||
|
$this->assertSame('user/register', $response['view']);
|
||||||
|
} else {
|
||||||
$this->assertIsString($response);
|
$this->assertIsString($response);
|
||||||
|
}
|
||||||
$captcha = session()->get('captcha_answer');
|
$captcha = session()->get('captcha_answer');
|
||||||
$this->assertNotEmpty($captcha);
|
$this->assertNotEmpty($captcha);
|
||||||
$this->assertGreaterThanOrEqual(4, strlen($captcha));
|
$this->assertGreaterThanOrEqual(4, strlen($captcha));
|
||||||
$this->assertLessThanOrEqual(8, strlen($captcha));
|
$this->assertLessThanOrEqual(8, strlen($captcha));
|
||||||
|
if (is_array($response)) {
|
||||||
|
$this->assertSame($captcha, $response['data']['captchaQuestion']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIndexReusesExistingCaptcha()
|
public function testIndexReusesExistingCaptcha()
|
||||||
@@ -55,8 +62,15 @@ namespace Tests\App\Controllers\View {
|
|||||||
|
|
||||||
$response = $this->controller->index();
|
$response = $this->controller->index();
|
||||||
|
|
||||||
|
if (is_array($response)) {
|
||||||
|
$this->assertSame('user/register', $response['view']);
|
||||||
|
} else {
|
||||||
$this->assertIsString($response);
|
$this->assertIsString($response);
|
||||||
|
}
|
||||||
$this->assertSame('EXISTING', session()->get('captcha_answer'));
|
$this->assertSame('EXISTING', session()->get('captcha_answer'));
|
||||||
|
if (is_array($response)) {
|
||||||
|
$this->assertSame('EXISTING', $response['data']['captchaQuestion']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSuccessRedirectsWhenEmailMissing()
|
public function testSuccessRedirectsWhenEmailMissing()
|
||||||
@@ -75,7 +89,12 @@ namespace Tests\App\Controllers\View {
|
|||||||
|
|
||||||
$response = $this->controller->success();
|
$response = $this->controller->success();
|
||||||
|
|
||||||
|
if (is_array($response)) {
|
||||||
|
$this->assertSame('success', $response['view']);
|
||||||
|
$this->assertSame('parent@example.com', $response['data']['email']);
|
||||||
|
} else {
|
||||||
$this->assertIsString($response);
|
$this->assertIsString($response);
|
||||||
|
}
|
||||||
$this->assertSame('parent@example.com', session()->get('user_email'));
|
$this->assertSame('parent@example.com', session()->get('user_email'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ namespace Tests\App\Controllers\View {
|
|||||||
use CodeIgniter\Database\BaseResult;
|
use CodeIgniter\Database\BaseResult;
|
||||||
use CodeIgniter\Test\CIUnitTestCase;
|
use CodeIgniter\Test\CIUnitTestCase;
|
||||||
use Config\Services;
|
use Config\Services;
|
||||||
|
use App\Models\UserModel;
|
||||||
|
|
||||||
class TestableReimbursementController extends ReimbursementController
|
class TestableReimbursementController extends ReimbursementController
|
||||||
{
|
{
|
||||||
@@ -44,6 +45,12 @@ namespace Tests\App\Controllers\View {
|
|||||||
$this->db = $db;
|
$this->db = $db;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setUserModel(UserModel $model): self
|
||||||
|
{
|
||||||
|
$this->userModel = $model;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReimbursementFakeRenderer
|
class ReimbursementFakeRenderer
|
||||||
@@ -90,13 +97,25 @@ namespace Tests\App\Controllers\View {
|
|||||||
$result->method('getResultArray')->willReturn([
|
$result->method('getResultArray')->willReturn([
|
||||||
[
|
[
|
||||||
'id' => 5,
|
'id' => 5,
|
||||||
|
'expense_id' => 5,
|
||||||
|
'expense_amount' => 15.5,
|
||||||
|
'description' => 'Markers',
|
||||||
'expense_receipt' => 'uploads/path/expense_receipt.pdf',
|
'expense_receipt' => 'uploads/path/expense_receipt.pdf',
|
||||||
'receipt_path' => null,
|
'retailor' => 'Office Store',
|
||||||
|
'purchased_by' => 11,
|
||||||
|
'purchaser_firstname' => 'Amina',
|
||||||
|
'purchaser_lastname' => 'Teacher',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'id' => 6,
|
'id' => 6,
|
||||||
'expense_receipt' => null,
|
'expense_id' => 6,
|
||||||
'receipt_path' => 'uploads/receipt_path.pdf',
|
'expense_amount' => 22.0,
|
||||||
|
'description' => 'Paper',
|
||||||
|
'expense_receipt' => 'uploads/receipt_path.pdf',
|
||||||
|
'retailor' => 'Supply Shop',
|
||||||
|
'purchased_by' => 12,
|
||||||
|
'purchaser_firstname' => 'Omar',
|
||||||
|
'purchaser_lastname' => 'Admin',
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -104,6 +123,9 @@ namespace Tests\App\Controllers\View {
|
|||||||
$builder->method('select')->willReturnSelf();
|
$builder->method('select')->willReturnSelf();
|
||||||
$builder->method('join')->willReturnSelf();
|
$builder->method('join')->willReturnSelf();
|
||||||
$builder->method('where')->willReturnSelf();
|
$builder->method('where')->willReturnSelf();
|
||||||
|
$builder->method('orWhere')->willReturnSelf();
|
||||||
|
$builder->method('groupStart')->willReturnSelf();
|
||||||
|
$builder->method('groupEnd')->willReturnSelf();
|
||||||
$builder->method('orderBy')->willReturnSelf();
|
$builder->method('orderBy')->willReturnSelf();
|
||||||
$builder->method('get')->willReturn($result);
|
$builder->method('get')->willReturn($result);
|
||||||
|
|
||||||
@@ -112,31 +134,44 @@ namespace Tests\App\Controllers\View {
|
|||||||
|
|
||||||
$this->controller = new TestableReimbursementController();
|
$this->controller = new TestableReimbursementController();
|
||||||
$this->controller->setDb($db);
|
$this->controller->setDb($db);
|
||||||
|
$userModel = $this->getMockBuilder(UserModel::class)
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->addMethods(['select', 'join', 'where'])
|
||||||
|
->onlyMethods(['findAll'])
|
||||||
|
->getMock();
|
||||||
|
$userModel->method('select')->willReturnSelf();
|
||||||
|
$userModel->method('join')->willReturnSelf();
|
||||||
|
$userModel->method('where')->willReturnSelf();
|
||||||
|
$userModel->method('findAll')->willReturn([]);
|
||||||
|
$this->controller->setUserModel($userModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown(): void
|
||||||
|
{
|
||||||
|
Services::resetSingle('renderer');
|
||||||
|
parent::tearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUnderProcessingBuildsViewWithExpenses()
|
public function testUnderProcessingBuildsViewWithExpenses()
|
||||||
{
|
{
|
||||||
$result = $this->controller->underProcessing();
|
$result = $this->controller->underProcessing();
|
||||||
|
if (is_array($result)) {
|
||||||
|
$rendered = $result;
|
||||||
|
} else {
|
||||||
$this->assertSame('fake-rendered:reimbursements/under_processing', $result);
|
$this->assertSame('fake-rendered:reimbursements/under_processing', $result);
|
||||||
|
|
||||||
$rendered = $this->renderer->getLastRender();
|
$rendered = $this->renderer->getLastRender();
|
||||||
|
}
|
||||||
$this->assertSame('reimbursements/under_processing', $rendered['view']);
|
$this->assertSame('reimbursements/under_processing', $rendered['view']);
|
||||||
$expenses = $rendered['data']['expenses'];
|
$pendingItems = $rendered['data']['pendingItems'];
|
||||||
$this->assertCount(2, $expenses);
|
$this->assertCount(2, $pendingItems);
|
||||||
|
|
||||||
$first = $expenses[0];
|
$first = $pendingItems[0];
|
||||||
$this->assertSame(site_url('receipts/expense_receipt.pdf'), $first['expense_receipt_url']);
|
$this->assertSame(site_url('receipts/expense_receipt.pdf'), $first['receipt_url']);
|
||||||
$this->assertSame(
|
$this->assertSame('Amina Teacher', $first['purchased_by']);
|
||||||
'<a href="' . base_url('reimbursements/create?expense_id=5') . '" class="btn btn-sm btn-primary">Reimburse</a>',
|
|
||||||
$first['action']
|
|
||||||
);
|
|
||||||
|
|
||||||
$second = $expenses[1];
|
$second = $pendingItems[1];
|
||||||
$this->assertSame(site_url('receipts/receipt_path.pdf'), $second['expense_receipt_url']);
|
$this->assertSame(site_url('receipts/receipt_path.pdf'), $second['receipt_url']);
|
||||||
$this->assertSame(
|
$this->assertSame('Omar Admin', $second['purchased_by']);
|
||||||
'<a href="' . base_url('reimbursements/create?expense_id=6') . '" class="btn btn-sm btn-primary">Reimburse</a>',
|
|
||||||
$second['action']
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
<?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 @@
|
|||||||
|
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
Reference in New Issue
Block a user