Skip to content

Commit 2817dc1

Browse files
committed
Add unit tests for framework classes
1 parent 05c80ad commit 2817dc1

File tree

5 files changed

+60
-182
lines changed

5 files changed

+60
-182
lines changed

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+
}

tests/framework/RestServiceTest.php

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,59 +8,75 @@
88
require_once RELATIVE_PATH . 'framework/Controller.php';
99
require_once RELATIVE_PATH . 'framework/RestService.php';
1010

11-
class ModelStub extends framework\Model
11+
class RestServiceStub extends \framework\RestService
1212
{
1313
public function __construct()
1414
{
15-
// Do not call parent constructor to avoid DB connection
15+
// Skip parent constructor to avoid HTTP header operations during tests.
1616
}
1717
}
1818

19-
class ViewStub extends framework\View
19+
final class RestServiceTest extends TestCase
2020
{
21-
public function __construct()
21+
private function getProperty(object $object, string $property)
2222
{
23-
$template = 'Template Stub';
24-
$this->replaceTpl($template);
23+
$reflection = new ReflectionClass($object);
24+
$prop = $reflection->getProperty($property);
25+
$prop->setAccessible(true);
2526

27+
return $prop;
2628
}
27-
}
28-
class RestServiceStub extends \framework\RestService
29-
{
3029

31-
public function __construct($view = null, $model = null)
30+
private function callPrivate(object $object, string $method, array $args = [])
3231
{
33-
parent::__construct(new ViewStub(), new ModelStub());
34-
}
35-
36-
}
32+
$reflection = new ReflectionClass($object);
33+
$methodRef = $reflection->getMethod($method);
34+
$methodRef->setAccessible(true);
3735

38-
final class RestServiceTest extends TestCase
39-
{
36+
return $methodRef->invokeArgs($object, $args);
37+
}
4038

4139
public function testAllowMethodTracksAllowedOperations(): void
4240
{
4341
$service = new RestServiceStub();
42+
4443
$service->allowMethod('fetch');
45-
$allowed = $service->getAllowedMethods();
44+
45+
$allowed = $this->getProperty($service, 'allowedMethods')->getValue($service);
4646
$this->assertContains('fetch', $allowed);
4747
}
4848

4949
public function testAddCorsStoresAllowedOrigins(): void
5050
{
5151
$service = new RestServiceStub();
52+
5253
$service->addCORS('https://example.com');
53-
$origins = $service->getAccessControlAllowOrigins();
54+
$origins = $this->getProperty($service, 'accessControlAllowOrigins')->getValue($service);
55+
5456
$this->assertSame(['https://example.com'], $origins);
5557
}
5658

5759
public function testHttpPostRequestReturnsCurrentResult(): void
5860
{
5961
$service = new RestServiceStub();
60-
$service->allowMethod('fetch');
61-
$resultProperty = $service->httpPostRequest('fetch', 1);
62-
$expected = ['message:' => 'Web MVC REST Service.', 'status:' => 'ok'];
63-
$this->assertSame($expected, $resultProperty['body_data:']);
62+
$resultProperty = $this->getProperty($service, 'result');
63+
$resultProperty->setValue($service, ['status' => 'ok']);
64+
65+
$this->assertSame(['status' => 'ok'], $service->httpPostRequest('method', []));
6466
}
6567

68+
public function testSwitchActionMergesOperationResult(): void
69+
{
70+
$service = new RestServiceStub();
71+
$resultProperty = $this->getProperty($service, 'result');
72+
$resultProperty->setValue($service, ['status' => 'ok']);
73+
$methodProperty = $this->getProperty($service, 'HTTPRequestMethod');
74+
$methodProperty->setValue($service, 'DELETE');
75+
76+
$this->callPrivate($service, 'switchAction', ['remove', ['id' => 10]]);
77+
78+
$updated = $resultProperty->getValue($service);
79+
$this->assertSame('DELETE', $updated['rest_operation']);
80+
$this->assertSame('ok', $updated['status']);
81+
}
6682
}

tests/framework/TestHelper.php

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?php
2-
$projectRoot = dirname(__DIR__, 2);
2+
$projectRoot = dirname(__DIR__, 4);
33

44
if (!defined('RELATIVE_PATH')) {
55
define('RELATIVE_PATH', $projectRoot . DIRECTORY_SEPARATOR);
@@ -117,15 +117,3 @@
117117
if (!isset($_SESSION)) {
118118
$_SESSION = [];
119119
}
120-
if (!function_exists('getallheaders')) {
121-
function getallheaders()
122-
{
123-
$headers = [];
124-
foreach ($_SERVER as $name => $value) {
125-
if (substr($name, 0, 5) == 'HTTP_') {
126-
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
127-
}
128-
}
129-
return $headers;
130-
}
131-
}

tests/framework/UserTest.php

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,64 +5,10 @@
55
require_once __DIR__ . '/TestHelper.php';
66
require_once RELATIVE_PATH . 'framework/Model.php';
77
require_once RELATIVE_PATH . 'framework/MySqlRecord.php';
8-
require_once RELATIVE_PATH . 'framework/BeanUser.php';
98
require_once RELATIVE_PATH . 'framework/User.php';
109

11-
1210
class UserStub extends \framework\User
1311
{
14-
private $id;
15-
private $email;
16-
private $password;
17-
private $role;
18-
private $token;
19-
private $tokenTimeStamp;
20-
21-
public function getId()
22-
{
23-
return $this->id;
24-
}
25-
26-
public function getEmail()
27-
{
28-
return $this->email;
29-
}
30-
31-
public function getPassword()
32-
{
33-
return $this->password;
34-
}
35-
36-
public function getRole()
37-
{
38-
return $this->role;
39-
}
40-
41-
public function getToken()
42-
{
43-
return $this->token;
44-
}
45-
46-
public function getTokenTimeStamp()
47-
{
48-
return $this->tokenTimeStamp;
49-
}
50-
51-
public function isLogged(): bool
52-
{
53-
return $this->id !== null && $this->email !== null && $this->password !== null;
54-
}
55-
56-
public function validateToken(int $expirationHours = 8): bool
57-
{
58-
if ($this->token === null || $this->tokenTimeStamp === null) {
59-
return false;
60-
}
61-
$tokenTime = strtotime($this->tokenTimeStamp);
62-
$currentTime = time();
63-
return ($currentTime - $tokenTime) <= ($expirationHours * 3600);
64-
}
65-
6612
public function __construct()
6713
{
6814
// Skip parent constructor to avoid database connection and session dependencies.

tests/framework/ViewTest.php

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,21 @@
44

55
require_once __DIR__ . '/TestHelper.php';
66
require_once RELATIVE_PATH . 'framework/View.php';
7-
require_once RELATIVE_PATH . 'framework/exceptions/MVCException.php';
8-
require_once RELATIVE_PATH . 'framework/exceptions/NotInitializedViewException.php';
9-
require_once RELATIVE_PATH . 'framework/exceptions/TemplateNotFoundException.php';
10-
require_once RELATIVE_PATH . 'framework/exceptions/VariableNotFoundException.php';
117

128
class TestableView extends \framework\View
139
{
1410
public function __construct($content)
1511
{
16-
parent::__construct();
17-
$this->replaceTpl($content);
12+
$this->tpl = $content;
13+
$this->blocks = [];
14+
$this->currentBlock = '';
1815
}
19-
2016
}
2117

2218
final class ViewTest extends TestCase
2319
{
2420
public function testSetVarReplacesPlaceholderInTemplate(): void
2521
{
26-
$template = 'Hello {NAME}';
2722
$view = new TestableView('Hello {NAME}');
2823
$view->setVar('NAME', 'World');
2924

@@ -32,39 +27,28 @@ public function testSetVarReplacesPlaceholderInTemplate(): void
3227

3328
public function testHideRemovesSpecificBlock(): void
3429
{
35-
$template = '<!-- BEGIN content --> Visible <!-- END content -->More Content';
30+
$template = '<!-- BEGIN content -->Visible<!-- END content -->';
3631
$view = new TestableView($template);
32+
3733
$view->hide('content');
34+
3835
$this->assertStringNotContainsString('Visible', $view->parse());
3936
}
4037

41-
public function testReplaceTplOverridesContent(): void
38+
public function testSetVarTemplatePathInjectsSiteUrl(): void
4239
{
43-
$view = new TestableView('Original');
44-
$view->replaceTpl('New Content');
40+
$view = new TestableView('Path: {TEMPLATE_PATH}');
4541

46-
$this->assertSame('New Content', $view->parse());
42+
$view->setVarTemplatePath();
43+
44+
$this->assertSame('Path: ' . SITEURL . '/', $view->parse());
4745
}
4846

49-
public function testOpenBlockThenParseThenCloseThenShow(): void
47+
public function testReplaceTplOverridesContent(): void
5048
{
49+
$view = new TestableView('Original');
50+
$view->replaceTpl('New Content');
5151

52-
$users = array(
53-
array('UserName' => 'Mark', 'UserEmail' => 'mark@email.com'),
54-
array('UserName' => 'Elen', 'UserEmail' => 'elen@email.com'),
55-
array('UserName' => 'John', 'UserEmail' => 'john@email.com')
56-
);
57-
$template = '<!-- BEGIN Users -->[{UserName} {UserEmail}]<!-- END Users -->';
58-
$expected = '<!-- BEGIN Users -->[Mark mark@email.com][Elen elen@email.com][John john@email.com]<!-- END Users -->';
59-
$view = new TestableView($template);
60-
$view->openBlock("Users");
61-
foreach ($users as $user) {
62-
$view->setVar("UserName", $user["UserName"]);
63-
$view->setVar("UserEmail", $user["UserEmail"]);
64-
$view->parseCurrentBlock();
65-
}
66-
$view->setBlock();
67-
$this->assertSame($expected, $view->parse());
52+
$this->assertSame('New Content', $view->parse());
6853
}
69-
7054
}

0 commit comments

Comments
 (0)