Skip to content

Commit 08ee8bf

Browse files
committed
Add unit tests for framework classes
1 parent 205e077 commit 08ee8bf

File tree

13 files changed

+754
-62
lines changed

13 files changed

+754
-62
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
use PHPUnit\Framework\TestCase;
4+
5+
require_once __DIR__ . '/TestHelper.php';
6+
require_once RELATIVE_PATH . 'framework/Model.php';
7+
require_once RELATIVE_PATH . 'framework/Bean.php';
8+
require_once RELATIVE_PATH . 'framework/BeanAdapter.php';
9+
10+
class FakeBeanImplementation
11+
{
12+
public $calls = [];
13+
14+
public function select(...$args)
15+
{
16+
$this->calls[] = ['select', $args];
17+
return 'select:' . implode('-', $args);
18+
}
19+
20+
public function insert()
21+
{
22+
$this->calls[] = ['insert', []];
23+
return 'inserted';
24+
}
25+
26+
public function delete(...$args)
27+
{
28+
$this->calls[] = ['delete', $args];
29+
return 'delete:' . implode('-', $args);
30+
}
31+
32+
public function update(...$args)
33+
{
34+
$this->calls[] = ['update', $args];
35+
return 'update:' . implode('-', $args);
36+
}
37+
38+
public function updateCurrent()
39+
{
40+
$this->calls[] = ['updateCurrent', []];
41+
return 'update-current';
42+
}
43+
44+
public function isSqlError()
45+
{
46+
$this->calls[] = ['isSqlError', []];
47+
return false;
48+
}
49+
50+
public function lastSqlError()
51+
{
52+
$this->calls[] = ['lastSqlError', []];
53+
return '';
54+
}
55+
}
56+
57+
final class BeanAdapterTest extends TestCase
58+
{
59+
private function createAdapter(FakeBeanImplementation $bean): \framework\BeanAdapter
60+
{
61+
$reflection = new ReflectionClass(\framework\BeanAdapter::class);
62+
/** @var \framework\BeanAdapter $adapter */
63+
$adapter = $reflection->newInstanceWithoutConstructor();
64+
$property = $reflection->getProperty('bean');
65+
$property->setAccessible(true);
66+
$property->setValue($adapter, $bean);
67+
68+
return $adapter;
69+
}
70+
71+
public function testAdapterDelegatesCallsToUnderlyingBean(): void
72+
{
73+
$bean = new FakeBeanImplementation();
74+
$adapter = $this->createAdapter($bean);
75+
76+
$this->assertSame('select:10', $adapter->select(10));
77+
$this->assertSame('select:10-20', $adapter->select([10, 20]));
78+
$this->assertSame('inserted', $adapter->insert());
79+
$this->assertSame('delete:5', $adapter->delete(5));
80+
$this->assertSame('delete:7-8', $adapter->delete([7, 8]));
81+
$this->assertSame('update:3', $adapter->update(3));
82+
$this->assertSame('update:4-5', $adapter->update([4, 5]));
83+
$this->assertSame('update-current', $adapter->updateCurrent());
84+
$this->assertFalse($adapter->isSqlError());
85+
$this->assertSame('', $adapter->lastSqlError());
86+
87+
$recorded = array_column($bean->calls, 0);
88+
$this->assertContains('select', $recorded);
89+
$this->assertContains('insert', $recorded);
90+
$this->assertContains('delete', $recorded);
91+
$this->assertContains('update', $recorded);
92+
$this->assertContains('updateCurrent', $recorded);
93+
$this->assertContains('isSqlError', $recorded);
94+
$this->assertContains('lastSqlError', $recorded);
95+
}
96+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
use PHPUnit\Framework\TestCase;
4+
5+
require_once __DIR__ . '/TestHelper.php';
6+
require_once RELATIVE_PATH . 'framework/Bean.php';
7+
8+
final class BeanTest extends TestCase
9+
{
10+
public function testBeanInterfaceDefinesExpectedMethods(): void
11+
{
12+
$reflection = new ReflectionClass(\framework\Bean::class);
13+
14+
$this->assertTrue($reflection->isInterface());
15+
$expectedMethods = ['select', 'insert', 'delete', 'update', 'updateCurrent', 'isSqlError', 'lastSqlError'];
16+
17+
foreach ($expectedMethods as $method) {
18+
$this->assertTrue($reflection->hasMethod($method));
19+
}
20+
}
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
use PHPUnit\Framework\TestCase;
4+
5+
require_once __DIR__ . '/TestHelper.php';
6+
require_once RELATIVE_PATH . 'framework/BeanUser.php';
7+
8+
final class BeanUserTest extends TestCase
9+
{
10+
public function testBeanUserInterfaceDefinesAccessors(): void
11+
{
12+
$reflection = new ReflectionClass(\framework\BeanUser::class);
13+
14+
$this->assertTrue($reflection->isInterface());
15+
$expectedMethods = ['getId', 'getEmail', 'getPassword', 'getRole', 'getToken', 'getTokenTimeStamp'];
16+
17+
foreach ($expectedMethods as $method) {
18+
$this->assertTrue($reflection->hasMethod($method));
19+
}
20+
}
21+
}

tests/framework/tests/framework/ControllerTest.php

Lines changed: 6 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -5,54 +5,11 @@
55
use framework\Model;
66
use framework\View;
77

8-
// Define minimal configuration constants required by the framework.
9-
$rootPath = realpath(__DIR__ . '/..');
10-
$projectRoot = dirname($rootPath);
11-
12-
if (!defined('RELATIVE_PATH')) {
13-
define('RELATIVE_PATH', $projectRoot . DIRECTORY_SEPARATOR);
14-
}
15-
if (!defined('SECURING_OUTSIDE_HTTP_FOLDER')) {
16-
define('SECURING_OUTSIDE_HTTP_FOLDER', '');
17-
}
18-
if (!defined('APP_CONTROLLERS_PATH')) {
19-
define('APP_CONTROLLERS_PATH', $projectRoot . DIRECTORY_SEPARATOR . 'controllers');
20-
}
21-
if (!defined('APP_LOCALE_PATH')) {
22-
define('APP_LOCALE_PATH', $projectRoot . DIRECTORY_SEPARATOR . 'locales');
23-
}
24-
if (!defined('JSFRAMEWORK')) {
25-
define('JSFRAMEWORK', 'framework/js');
26-
}
27-
if (!defined('SITEURL')) {
28-
define('SITEURL', 'http://localhost');
29-
}
30-
if (!defined('SERVER_OS_ENCODING')) {
31-
define('SERVER_OS_ENCODING', 'Linux');
32-
}
33-
if (!defined('DEFAULT_LOGIN_PAGE')) {
34-
define('DEFAULT_LOGIN_PAGE', 'common/login');
35-
}
36-
if (!defined('LoginRBACWarningMessage')) {
37-
define('LoginRBACWarningMessage', 'login-rbac-warning');
38-
}
39-
if (!defined('LoginAuthWarningMessage')) {
40-
define('LoginAuthWarningMessage', 'login-auth-warning');
41-
}
42-
if (!defined('COMPRESS_OUTPUT')) {
43-
define('COMPRESS_OUTPUT', false);
44-
}
45-
if (!defined('CHARSET')) {
46-
define('CHARSET', 'UTF-8');
47-
}
48-
if (!defined('SUBSYSTEMS')) {
49-
define('SUBSYSTEMS', serialize(['sub']));
50-
}
51-
52-
require_once $projectRoot . '/framework/Loader.php';
53-
require_once $projectRoot . '/framework/Model.php';
54-
require_once $projectRoot . '/framework/View.php';
55-
require_once $projectRoot . '/framework/Controller.php';
8+
require_once __DIR__ . '/TestHelper.php';
9+
require_once RELATIVE_PATH . 'framework/Loader.php';
10+
require_once RELATIVE_PATH . 'framework/Model.php';
11+
require_once RELATIVE_PATH . 'framework/View.php';
12+
require_once RELATIVE_PATH . 'framework/Controller.php';
5613

5714
class FakeModel extends Model
5815
{
@@ -197,17 +154,4 @@ public function testGetSubSystemDetectsCurrentSubsystem(): void
197154

198155
$this->assertSame('sub', $controller->getSubSystem());
199156
}
200-
201-
public function testSetAsChildControllerSwitchesRootFlag(): void
202-
{
203-
$controller = new TestableController(new FakeView('<div>test</div>'), new FakeModel());
204-
205-
$this->assertTrue($controller->isRootController());
206-
$this->assertFalse($controller->isChildController());
207-
208-
$controller->setAsChildController();
209-
210-
$this->assertFalse($controller->isRootController());
211-
$this->assertTrue($controller->isChildController());
212-
}
213-
}
157+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
use PHPUnit\Framework\TestCase;
4+
5+
require_once __DIR__ . '/TestHelper.php';
6+
require_once RELATIVE_PATH . 'framework/Loader.php';
7+
require_once RELATIVE_PATH . 'framework/Model.php';
8+
require_once RELATIVE_PATH . 'framework/View.php';
9+
require_once RELATIVE_PATH . 'framework/Controller.php';
10+
require_once RELATIVE_PATH . 'framework/Dispatcher.php';
11+
12+
final class DispatcherTest extends TestCase
13+
{
14+
protected function setUp(): void
15+
{
16+
parent::setUp();
17+
$_GET = [];
18+
$_SESSION = [];
19+
}
20+
21+
private function getPrivateProperty(object $object, string $property)
22+
{
23+
$reflection = new ReflectionClass($object);
24+
$prop = $reflection->getProperty($property);
25+
$prop->setAccessible(true);
26+
27+
return $prop->getValue($object);
28+
}
29+
30+
private function invokePrivateMethod(object $object, string $method, array $args = [])
31+
{
32+
$reflection = new ReflectionClass($object);
33+
$methodRef = $reflection->getMethod($method);
34+
$methodRef->setAccessible(true);
35+
36+
return $methodRef->invokeArgs($object, $args);
37+
}
38+
39+
public function testDispatcherParsesUrlSegmentsIntoProperties(): void
40+
{
41+
$_GET['url'] = 'example/show/42';
42+
43+
$dispatcher = new \framework\Dispatcher();
44+
45+
$this->assertSame('controllers\\example', $this->getPrivateProperty($dispatcher, 'controllerClass'));
46+
$this->assertSame('show', $this->getPrivateProperty($dispatcher, 'method'));
47+
$this->assertSame(['42'], $this->getPrivateProperty($dispatcher, 'methodParameters'));
48+
$this->assertSame('example', $this->getPrivateProperty($dispatcher, 'controllerSEOClassName'));
49+
$this->assertSame('', $this->getPrivateProperty($dispatcher, 'currentSubSystem'));
50+
$this->assertArrayHasKey('example', $_SESSION);
51+
}
52+
53+
public function testDispatcherSupportsSubsystems(): void
54+
{
55+
$_GET['url'] = 'sub/report/list';
56+
57+
$dispatcher = new \framework\Dispatcher();
58+
59+
$this->assertSame('controllers\\sub\\report', $this->getPrivateProperty($dispatcher, 'controllerClass'));
60+
$this->assertSame('list', $this->getPrivateProperty($dispatcher, 'method'));
61+
$this->assertSame([], $this->getPrivateProperty($dispatcher, 'methodParameters'));
62+
$this->assertSame('report', $this->getPrivateProperty($dispatcher, 'controllerSEOClassName'));
63+
$this->assertArrayHasKey('report', $_SESSION);
64+
}
65+
66+
public function testUnderscoreToCamelCaseConvertsSeoNames(): void
67+
{
68+
unset($_GET['url']);
69+
$dispatcher = new \framework\Dispatcher();
70+
71+
$result = $this->invokePrivateMethod($dispatcher, 'underscoreToCamelCase', ['user_profile']);
72+
73+
$this->assertSame('userProfile', $result);
74+
}
75+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
use PHPUnit\Framework\TestCase;
4+
5+
require_once __DIR__ . '/TestHelper.php';
6+
require_once RELATIVE_PATH . 'framework/Loader.php';
7+
8+
final class LoaderTest extends TestCase
9+
{
10+
protected function setUp(): void
11+
{
12+
parent::setUp();
13+
$_SESSION = [];
14+
}
15+
16+
public function testGetDirectoriesMergesSubsystemPaths(): void
17+
{
18+
$directories = \framework\Loader::getDirectories();
19+
20+
$this->assertContains(APP_CONTROLLERS_PATH . DIRECTORY_SEPARATOR . 'sub', $directories);
21+
$this->assertContains(APP_VIEWS_PATH . DIRECTORY_SEPARATOR . 'sub', $directories);
22+
$this->assertContains(APP_MODELS_PATH . DIRECTORY_SEPARATOR . 'sub', $directories);
23+
$this->assertContains('framework', $directories);
24+
}
25+
26+
public function testGetCurrentSubSystemDetectsLongestMatch(): void
27+
{
28+
$_SESSION = [];
29+
$sub = \framework\Loader::getCurrentSubSystem('sub/example');
30+
31+
$this->assertSame('sub', $sub);
32+
$this->assertSame('sub', $_SESSION['current_subsystem']);
33+
}
34+
35+
public function testListFoldersReturnsNestedDirectories(): void
36+
{
37+
$folders = \framework\Loader::listFolders(APP_CONTROLLERS_PATH);
38+
39+
$this->assertContains('common', $folders);
40+
$this->assertContains('builders', $folders);
41+
}
42+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
use PHPUnit\Framework\TestCase;
4+
5+
require_once __DIR__ . '/TestHelper.php';
6+
require_once RELATIVE_PATH . 'framework/Model.php';
7+
8+
final class ModelTest extends TestCase
9+
{
10+
private function createModel(): \framework\Model
11+
{
12+
$reflection = new ReflectionClass(\framework\Model::class);
13+
/** @var \framework\Model $model */
14+
$model = $reflection->newInstanceWithoutConstructor();
15+
16+
return $model;
17+
}
18+
19+
public function testSetAndGetResultSet(): void
20+
{
21+
$model = $this->createModel();
22+
$result = new stdClass();
23+
24+
$model->setResultSet($result);
25+
26+
$this->assertSame($result, $model->getResultSet());
27+
}
28+
29+
public function testEnvelopeSqlWrapsCurrentQuery(): void
30+
{
31+
$model = $this->createModel();
32+
$reflection = new ReflectionClass($model);
33+
$property = $reflection->getProperty('sql');
34+
$property->setAccessible(true);
35+
$property->setValue($model, 'SELECT * FROM users');
36+
37+
$model->envelopeSql();
38+
39+
$this->assertSame('SELECT mvc_sql_evelop.* FROM (SELECT * FROM users) mvc_sql_evelop', $property->getValue($model));
40+
}
41+
}

0 commit comments

Comments
 (0)