diff options
| author | Philipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de> | 2024-09-24 10:53:31 +0200 |
|---|---|---|
| committer | Philipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de> | 2024-09-24 10:53:31 +0200 |
| commit | 4459dd7917f4d1c34f40bb68f0e991e9c3d53e4c (patch) | |
| tree | 5c07151ae61276d334e88f6309c30d439a85c12e /tests/unit | |
| parent | da0022e5c1abbf9825ae76debaabdff7e8623bb4 (diff) | |
| parent | 97a188592c679890a25c37ab78463add76a52ff7 (diff) | |
Merge branch 'main' into issue-3911issue-3911
Diffstat (limited to 'tests/unit')
27 files changed, 1158 insertions, 182 deletions
diff --git a/tests/unit/_bootstrap.php b/tests/unit/_bootstrap.php index 2216111..0cf0f7e 100644 --- a/tests/unit/_bootstrap.php +++ b/tests/unit/_bootstrap.php @@ -21,10 +21,7 @@ // SOFTWARE. // set error reporting -error_reporting(E_ALL & ~E_NOTICE); -if (version_compare(phpversion(), '5.4', '>=')) { - error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT); -} +error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED); // set include path $inc_path = ini_get('include_path'); @@ -32,47 +29,32 @@ $inc_path .= PATH_SEPARATOR . __DIR__ . '/../..'; $inc_path .= PATH_SEPARATOR . __DIR__ . '/../../config'; ini_set('include_path', $inc_path); +global $ABSOLUTE_URI_STUDIP, + $ASSETS_URL, + $CACHING_ENABLE, + $CACHING_FILECACHE_PATH, + $CANONICAL_RELATIVE_PATH_STUDIP, + $DYNAMIC_CONTENT_PATH, + $DYNAMIC_CONTENT_URL, + $STUDIP_BASE_PATH, + $SYMBOL_SHORT, + $TMP_PATH, + $UPLOAD_PATH; + // load varstream for easier filesystem testing require_once 'varstream.php'; -define("TEST_FIXTURES_PATH", dirname(__DIR__) . "/_data/"); +define('TEST_FIXTURES_PATH', dirname(__DIR__) . '/_data/'); require __DIR__ . '/../../composer/autoload.php'; global $STUDIP_BASE_PATH; $STUDIP_BASE_PATH = realpath(dirname(__DIR__) . '/..'); -require 'lib/classes/StudipAutoloader.php'; +require 'lib/helpers.php'; require 'lib/functions.php'; require 'lib/visual.inc.php'; -StudipAutoloader::setBasePath(realpath(__DIR__ . '/../..')); -StudipAutoloader::register(); - -StudipAutoloader::addAutoloadPath('lib/activities', 'Studip\\Activity'); -StudipAutoloader::addAutoloadPath('lib/models'); -StudipAutoloader::addAutoloadPath('lib/classes'); -StudipAutoloader::addAutoloadPath('lib/classes', 'Studip'); -StudipAutoloader::addAutoloadPath('lib/exceptions'); -StudipAutoloader::addAutoloadPath('lib/classes/sidebar'); -StudipAutoloader::addAutoloadPath('lib/classes/helpbar'); -StudipAutoloader::addAutoloadPath('lib/plugins/engine'); -StudipAutoloader::addAutoloadPath('lib/plugins/core'); -StudipAutoloader::addAutoloadPath('lib/plugins/db'); - -StudipAutoloader::addClassLookup('StudipController', 'app/controllers/studip_controller.php'); -$trails_classes = [ - 'Trails_Dispatcher', 'Trails_Response', 'Trails_Controller', - 'Trails_Inflector', 'Trails_Flash', - 'Trails_Exception', 'Trails_DoubleRenderError', 'Trails_MissingFile', - 'Trails_RoutingError', 'Trails_UnknownAction', 'Trails_UnknownController', - 'Trails_SessionRequiredException', -]; -StudipAutoloader::addClassLookup( - $trails_classes, - 'vendor/trails/trails.php' -); - // load config-variables $added_configs = []; StudipFileloader::load( @@ -86,16 +68,16 @@ foreach ($added_configs as $key => $value) { $GLOBALS[$key] = $value; } -$config = Symfony\Component\Yaml\Yaml::parseFile(__DIR__ .'/../unit.suite.yml'); +$config = Symfony\Component\Yaml\Yaml::parseFile(__DIR__ . '/../unit.suite.yml'); // connect to database if configured if (isset($config['modules']['config']['Db'])) { - DBManager::getInstance()->setConnection('studip', + DBManager::getInstance()->setConnection( + 'studip', $config['modules']['config']['Db']['dsn'], $config['modules']['config']['Db']['user'], - $config['modules']['config']['Db']['password']); -} else { - //DBManager::getInstance()->setConnection('studip', 'sqlite://'. $GLOBALS ,'', ''); + $config['modules']['config']['Db']['password'] + ); } // Disable caching to fallback to memory cache @@ -108,7 +90,7 @@ if (!class_exists('StudipTestHelper')) { static function set_up_tables($tables) { // first step, set fake cache - $cache = StudipCacheFactory::getCache(false); + $cache = \Studip\Cache\Factory::getCache(false); // second step, expire table scheme SimpleORMap::expireTableScheme(); @@ -116,17 +98,17 @@ if (!class_exists('StudipTestHelper')) { $schemes = []; foreach ($tables as $db_table) { - include TEST_FIXTURES_PATH."simpleormap/$db_table.php"; + include TEST_FIXTURES_PATH . "simpleormap/$db_table.php"; $db_fields = $pk = []; foreach ($result as $rs) { $db_fields[mb_strtolower($rs['name'])] = [ - 'name' => $rs['name'], - 'null' => $rs['null'], + 'name' => $rs['name'], + 'null' => $rs['null'], 'default' => $rs['default'], - 'type' => $rs['type'], - 'extra' => $rs['extra'] + 'type' => $rs['type'], + 'extra' => $rs['extra'], ]; - if ($rs['key'] == 'PRI'){ + if ($rs['key'] == 'PRI') { $pk[] = mb_strtolower($rs['name']); } } diff --git a/tests/unit/lib/CalendarcolumnClassTest.php b/tests/unit/lib/CalendarcolumnClassTest.php index 063f97e..d2ac9e0 100644 --- a/tests/unit/lib/CalendarcolumnClassTest.php +++ b/tests/unit/lib/CalendarcolumnClassTest.php @@ -9,7 +9,7 @@ * the License, or (at your option) any later version. */ -require_once 'lib/calendar/CalendarColumn.class.php'; +require_once 'lib/calendar/CalendarColumn.php'; class CalendarColumnCase extends \Codeception\Test\Unit { diff --git a/tests/unit/lib/CalendarviewClassTest.php b/tests/unit/lib/CalendarviewClassTest.php index 962cd06..d810575 100644 --- a/tests/unit/lib/CalendarviewClassTest.php +++ b/tests/unit/lib/CalendarviewClassTest.php @@ -9,7 +9,7 @@ * the License, or (at your option) any later version. */ -require_once 'lib/calendar/CalendarView.class.php'; +require_once 'lib/calendar/CalendarView.php'; class CalendarViewCase extends \Codeception\Test\Unit { diff --git a/tests/unit/lib/FunctionsTest.php b/tests/unit/lib/FunctionsTest.php index 9a92f1a..c254fda 100644 --- a/tests/unit/lib/FunctionsTest.php +++ b/tests/unit/lib/FunctionsTest.php @@ -75,16 +75,35 @@ class FunctionsTest extends \Codeception\Test\Unit } /** - * @covers Trails_Controller::extract_action_and_args() + * @covers Trails\Controller::extract_action_and_args() */ public function testTrailsControllerExtractActionAndArgs() { - $controller = new Trails_Controller(null); - list($action, $args, $format) = $controller->extract_action_and_args('foo/bar//42.html'); + $dispatcher = new Trails\Dispatcher('', '', ''); + $controller = new Trails\Controller($dispatcher); + [$action, $args, $format] = $controller->extract_action_and_args('foo/bar//42.html'); $this->assertEquals('foo', $action); $this->assertEquals(['bar', '', '42'], $args); $this->assertEquals('html', $format); } + + /** + * @covers ::studip_interpolate + */ + public function testStudipInterpolate() + { + $this->assertEquals( + '12bar34', + studip_interpolate('12%{foo}34', ['foo' => 'bar']) + ); + $this->assertEquals( + 'foo', + studip_interpolate('%{ bar }', ['bar' => 'foo']) + ); + + $this->expectException(Exception::class); + studip_interpolate('%{foo}', []); + } } diff --git a/tests/unit/lib/VisualTest.php b/tests/unit/lib/VisualTest.php index dfa2945..2ea8334 100644 --- a/tests/unit/lib/VisualTest.php +++ b/tests/unit/lib/VisualTest.php @@ -9,10 +9,9 @@ * the License, or (at your option) any later version. */ -require_once 'lib/models/SimpleORMap.class.php'; -require_once 'lib/models/OpenGraphURL.class.php'; +require_once 'lib/classes/SimpleORMap.php'; require_once 'lib/visual.inc.php'; -require_once 'lib/classes/Config.class.php'; +require_once 'lib/classes/Config.php'; class VisualFunctionsTest extends \Codeception\Test\Unit { @@ -21,6 +20,7 @@ class VisualFunctionsTest extends \Codeception\Test\Unit static $config = [ 'LOAD_EXTERNAL_MEDIA' => 'allow', 'OPENGRAPH_ENABLE' => false, + 'CONVERT_IDNA_URL' => true, ]; Config::set(new Config($config)); @@ -224,6 +224,16 @@ class VisualFunctionsTest extends \Codeception\Test\Unit $this->assertEquals($expected, formatReady($input)); } + public function testIdnaLink() + { + $input = htmlentities('https://www.täst-dömäne-mit-ümläuten.de'); + + $this->assertEquals( + 'https://www.xn--tst-dmne-mit-mluten-gwbfj61b7e.de', + idna_link($input) + ); + } + private function wrap($string) { return sprintf(FORMATTED_CONTENT_WRAPPER, $string); diff --git a/tests/unit/lib/classes/AvatarClassTest.php b/tests/unit/lib/classes/AvatarClassTest.php index 8172e20..e09a3c2 100644 --- a/tests/unit/lib/classes/AvatarClassTest.php +++ b/tests/unit/lib/classes/AvatarClassTest.php @@ -9,7 +9,7 @@ * the License, or (at your option) any later version. */ -require_once 'lib/phplib/Seminar_Perm.class.php'; +require_once 'lib/phplib/Seminar_Perm.php'; abstract class AvatarTest extends \Codeception\Test\Unit { diff --git a/tests/unit/lib/classes/CronjobScheduleTest.php b/tests/unit/lib/classes/CronjobScheduleTest.php index a97872e..b6fd573 100644 --- a/tests/unit/lib/classes/CronjobScheduleTest.php +++ b/tests/unit/lib/classes/CronjobScheduleTest.php @@ -26,69 +26,15 @@ class CronjobScheduleTest extends \Codeception\Test\Unit StudipTestHelper::tear_down_tables(); } - function testOnceSchedule() - { - $schedule = new CronjobSchedule(); - $schedule->type = 'once'; - - $this->assertEquals('once', $schedule->type); - - return $schedule; - } - - /** - * @depends testOnceSchedule - */ - function testNextExecutionOncePast($schedule) - { - $now = strtotime('10.11.2013 01:02:00'); - $then = strtotime('-2 weeks', $now); - - $schedule->next_execution = $then; - $schedule->calculateNextExecution(); - - $this->assertEquals($then, $schedule->next_execution); - } - - /** - * @depends testOnceSchedule - */ - function testNextExecutionOncePresent($schedule) - { - $now = strtotime('10.11.2013 01:02:00'); - - $schedule->next_execution = $now; - $schedule->calculateNextExecution(); - - $this->assertEquals($now, $schedule->next_execution); - } - - /** - * @depends testOnceSchedule - */ - function testNextExecutionOnceFuture(CronjobSchedule $schedule) - { - $now = strtotime('10.11.2013 01:02:00'); - $then = strtotime('+2 weeks', $now); - - $schedule->next_execution = $then; - $schedule->calculateNextExecution($now); - - $this->assertEquals($then, $schedule->next_execution); - } - function testPeriodicSchedule() { $schedule = new CronjobSchedule(); - $schedule->type = 'periodic'; $schedule->minute = null; $schedule->hour = null; $schedule->day = null; $schedule->month = null; $schedule->day_of_week = null; - $this->assertEquals('periodic', $schedule->type); - return $schedule; } diff --git a/tests/unit/lib/classes/IconClassTest.php b/tests/unit/lib/classes/IconClassTest.php index eb743b9..0e79df9 100644 --- a/tests/unit/lib/classes/IconClassTest.php +++ b/tests/unit/lib/classes/IconClassTest.php @@ -27,7 +27,7 @@ class IconClassTest extends \Codeception\Test\Unit { $this->assertEquals( '<img width="16" height="16" src="images/icons/blue/vote.svg" alt="" class="icon-role-clickable icon-shape-vote">', - Icon::create('vote', 'clickable')->asImg() + Icon::create('vote')->asImg() ); } @@ -35,7 +35,7 @@ class IconClassTest extends \Codeception\Test\Unit { $this->assertEquals( '<img width="16" height="16" src="images/icons/blue/vote.svg" alt="" class="icon-role-clickable icon-shape-vote">', - Icon::create('vote', 'clickable')->asImg() + Icon::create('vote')->asImg() ); } @@ -43,7 +43,7 @@ class IconClassTest extends \Codeception\Test\Unit { $this->assertEquals( '<img width="20" height="20" src="images/icons/blue/vote.svg" alt="" class="icon-role-clickable icon-shape-vote">', - Icon::create('vote', 'clickable')->asImg(20) + Icon::create('vote')->asImg(20) ); } @@ -51,7 +51,7 @@ class IconClassTest extends \Codeception\Test\Unit { $this->assertEquals( '<img title="Mit Anhang" width="20" height="20" src="images/icons/blue/vote.svg" class="icon-role-clickable icon-shape-vote">', - Icon::create('vote', 'clickable', ['title' => _("Mit Anhang")])->asImg(20) + Icon::create('vote')->asImg(20, ['title' => _('Mit Anhang')]) ); } @@ -59,7 +59,7 @@ class IconClassTest extends \Codeception\Test\Unit { $this->assertEquals( '<img hspace="3" width="16" height="16" src="images/icons/blue/arr_2left.svg" alt="" class="icon-role-clickable icon-shape-arr_2left">', - Icon::create('arr_2left', 'clickable')->asImg(['hspace' => 3]) + Icon::create('arr_2left')->asImg(['hspace' => 3]) ); } @@ -67,7 +67,7 @@ class IconClassTest extends \Codeception\Test\Unit { $this->assertEquals( '<img class="text-bottom icon-role-info icon-shape-staple" width="20" height="20" src="images/icons/black/staple.svg" alt="">', - Icon::create('staple', 'info')->asImg(20, ['class' => 'text-bottom']) + Icon::create('staple', Icon::ROLE_INFO)->asImg(20, ['class' => 'text-bottom']) ); } @@ -75,7 +75,7 @@ class IconClassTest extends \Codeception\Test\Unit { $this->assertEquals( '<img title="Datei hochladen" class="text-bottom icon-role-new icon-shape-upload" width="20" height="20" src="images/icons/red/upload.svg">', - Icon::create('upload', 'new', ['title' => _("Datei hochladen")]) + Icon::create('upload', Icon::ROLE_NEW, ['title' => _("Datei hochladen")]) ->asImg(20, ['class' => 'text-bottom']) ); } @@ -84,22 +84,22 @@ class IconClassTest extends \Codeception\Test\Unit { $this->assertEquals( '<input type="image" class="text-bottom icon-role-clickable icon-shape-upload" width="20" height="20" src="images/icons/blue/upload.svg" alt="">', - Icon::create('upload', 'clickable')->asInput(20, ['class' => 'text-bottom']) + Icon::create('upload')->asInput(20, ['class' => 'text-bottom']) ); } function testIconIsImmutable() { - $icon = Icon::create('upload', 'clickable', ['title' => _('a title')]); - $copy = $icon->copyWithRole('clickable'); + $icon = Icon::create('upload', Icon::ROLE_CLICKABLE, ['title' => _('a title')]); + $copy = $icon->copyWithRole(Icon::ROLE_CLICKABLE); $this->assertNotSame($icon, $copy); } function testIconCopyWithRole() { - $icon = Icon::create('upload', 'clickable', ['title' => _('a title')]); - $copy = $icon->copyWithRole('info'); + $icon = Icon::create('upload', Icon::ROLE_CLICKABLE, ['title' => _('a title')]); + $copy = $icon->copyWithRole(Icon::ROLE_INFO); $this->assertEquals($icon->getShape(), $copy->getShape()); $this->assertNotEquals($icon->getRole(), $copy->getRole()); @@ -108,7 +108,7 @@ class IconClassTest extends \Codeception\Test\Unit function testIconCopyWithShape() { - $icon = Icon::create('upload', 'clickable', ['title' => _('a title')]); + $icon = Icon::create('upload', Icon::ROLE_CLICKABLE, ['title' => _('a title')]); $copy = $icon->copyWithShape('staple'); $this->assertNotEquals($icon->getShape(), $copy->getShape()); @@ -118,7 +118,7 @@ class IconClassTest extends \Codeception\Test\Unit function testIconCopyWithAttributes() { - $icon = Icon::create('upload', 'clickable', ['title' => _('a title')]); + $icon = Icon::create('upload', Icon::ROLE_CLICKABLE, ['title' => _('a title')]); $copy = $icon->copyWithAttributes(['title' => _('another title')]); $this->assertEquals($icon->getShape(), $copy->getShape()); @@ -136,7 +136,7 @@ class IconClassTest extends \Codeception\Test\Unit { $this->assertEquals( 'background-image:url(images/icons/blue/vote.svg);background-size:17px 17px;', - Icon::create('vote', 'clickable')->asCSS(17) + Icon::create('vote')->asCSS(17) ); } @@ -144,7 +144,7 @@ class IconClassTest extends \Codeception\Test\Unit { $this->assertEquals( 'images/icons/blue/vote.svg', - Icon::create('vote', 'clickable')->asImagePath() + Icon::create('vote')->asImagePath() ); } @@ -152,7 +152,7 @@ class IconClassTest extends \Codeception\Test\Unit { $this->assertEquals( '<img src="images/icons/blue/vote.svg" alt="" class="icon-role-clickable icon-shape-vote">', - Icon::create('vote', 'clickable')->asImg(false) + Icon::create('vote')->asImg(false) ); } @@ -160,7 +160,19 @@ class IconClassTest extends \Codeception\Test\Unit { $this->assertEquals( '<input type="image" src="images/icons/blue/upload.svg" alt="" class="icon-role-clickable icon-shape-upload">', - Icon::create('upload', 'clickable')->asInput(false) + Icon::create('upload')->asInput(false) + ); + } + + function testIconCreateRemovedExtras() + { + $this->assertEquals( + '<img src="images/icons/blue/vote.svg" alt="" class="icon-role-clickable icon-shape-vote">', + Icon::create('add/vote')->asImg(false) + ); + $this->assertEquals( + '<img src="images/icons/blue/vote.svg" alt="" class="icon-role-clickable icon-shape-vote">', + Icon::create('vote+add')->asImg(false) ); } } diff --git a/tests/unit/lib/classes/MarkupClassTest.php b/tests/unit/lib/classes/MarkupClassTest.php index d1c35b1..347e453 100644 --- a/tests/unit/lib/classes/MarkupClassTest.php +++ b/tests/unit/lib/classes/MarkupClassTest.php @@ -21,15 +21,15 @@ require_once 'tests/unit/fakeserver.php'; # needed by visual.inc.php -require_once 'lib/classes/DbView.class.php'; -require_once 'lib/classes/TreeAbstract.class.php'; +require_once 'lib/classes/DbView.php'; +require_once 'lib/classes/TreeAbstract.php'; -# needed by Markup.class.php +# needed by Markup.php require_once 'lib/visual.inc.php'; -require_once 'lib/classes/Config.class.php'; +require_once 'lib/classes/Config.php'; # class and functions that are tested by this script -require_once 'lib/classes/Markup.class.php'; +require_once 'lib/classes/Markup.php'; # Seminar_Session cannot be mocked since it uses static functions. # Also, including phplib_local.inc.php, where Seminar_Session is diff --git a/tests/unit/lib/classes/MigrationTest.php b/tests/unit/lib/classes/MigrationTest.php index 444ebf2..12df120 100644 --- a/tests/unit/lib/classes/MigrationTest.php +++ b/tests/unit/lib/classes/MigrationTest.php @@ -16,11 +16,6 @@ class MigrationTest extends \Codeception\Test\Unit $this->before = $GLOBALS['CACHING_ENABLE'] ?? null; $GLOBALS['CACHING_ENABLE'] = false; - require_once 'lib/classes/StudipCache.class.php'; - require_once 'lib/classes/StudipMemoryCache.class.php'; - require_once 'lib/classes/StudipCacheFactory.class.php'; - require_once 'lib/models/SimpleORMap.class.php'; - require_once 'lib/migrations/Migration.php'; require_once 'lib/migrations/Migrator.php'; require_once 'lib/migrations/SchemaVersion.php'; diff --git a/tests/unit/lib/classes/OAuth1Test.php b/tests/unit/lib/classes/OAuth1Test.php new file mode 100644 index 0000000..84d4fb2 --- /dev/null +++ b/tests/unit/lib/classes/OAuth1Test.php @@ -0,0 +1,118 @@ +<?php + +use Psr\Http\Message\ServerRequestInterface; +use Studip\OAuth1; + +/** + * All values are from the OAuth 1.0 Authentication Sandbox (using the example + * used in the OAuth Specification). + * + * @see http://lti.tools/oauth/ + */ +final class OAuth1Test extends \Codeception\Test\Unit +{ + /** + * @covers OAuth1::getSignatureBaseString + */ + public function testCreationOfBaseString(): void + { + $this->assertEquals( + 'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal', + OAuth1::getSignatureBaseString($this->getDefaultTestRequest()) + ); + } + + /** + * @covers OAuth1::signRequest + */ + public function testSigningARequest(): void + { + $this->assertEquals( + 'tR3+Ty81lMeYAr/Fid0kMTYa/WM=', + OAuth1::signRequest( + $this->getDefaultTestRequest(), + 'kd94hf93k423kf44', + 'pfkkdhi9sl3r4s00', + 'HMAC-SHA1' + ) + ); + } + + /** + * @covers OAuth1::verifyRequest + */ + public function testVerifyingARequest(): void + { + $this->assertTrue( + OAuth1::verifyRequest( + $this->getDefaultTestRequest(['oauth_signature' => 'tR3+Ty81lMeYAr/Fid0kMTYa/WM=']), + 'kd94hf93k423kf44', + 'pfkkdhi9sl3r4s00' + ) + ); + } + + /** + * @covers OAuth1::verifyRequest + * @covers OAuth1::extractParameters + */ + public function testVerifyingARequestFromAuthorizationHeader(): void + { + $parameters = [ + ...$this->getDefaultParameters(), + 'oauth_signature' => 'tR3+Ty81lMeYAr/Fid0kMTYa/WM=' + ]; + + + $request = $this->getTestRequest()->withHeader( + 'Authorization', + 'OAuth ' . implode(',', array_map( + fn($key, $value) => sprintf('%s="%s"', $key, $value), + array_keys($parameters), + array_values($parameters) + )) + ); + + $this->assertTrue( + OAuth1::verifyRequest( + $request, + 'kd94hf93k423kf44', + 'pfkkdhi9sl3r4s00' + ) + ); + } + + private function getTestRequest(): ServerRequestInterface + { + $factory = new Slim\Psr7\Factory\ServerRequestFactory(); + return $factory->createServerRequest( + 'GET', + 'http://photos.example.net/photos' + )->withQueryParams([ + 'size' => 'original', + 'file' => 'vacation.jpg', + ]); + } + + private function getDefaultTestRequest(array $parameters = []): ServerRequestInterface + { + $request = $this->getTestRequest(); + return $request->withQueryParams([ + ...$request->getQueryParams(), + ...$this->getDefaultParameters(), + ...$parameters, + ]); + } + + private function getDefaultParameters(): array + { + return [ + 'oauth_consumer_key' => 'dpf43f3p2l4k3l03', + 'oauth_token' => 'nnch734d00sl2jdk', + 'oauth_nonce' => 'kllo9940pd9333jh', + 'oauth_timestamp' => '1191242096', + 'oauth_signature_method' => 'HMAC-SHA1', + 'oauth_version' => '1.0', + ]; + } +} diff --git a/tests/unit/lib/classes/OpenGraphTest.php b/tests/unit/lib/classes/OpenGraphTest.php new file mode 100644 index 0000000..416660b --- /dev/null +++ b/tests/unit/lib/classes/OpenGraphTest.php @@ -0,0 +1,34 @@ +<?php +/** + * @author Jan-Hendrik Willms <tleilax+studip@gmail.com> + * @license GPL2 or any later version + */ +class OpenGraphTest extends \Codeception\Test\Unit +{ + public function setUp(): void + { + static $config = [ + 'OPENGRAPH_ENABLE' => true, + ]; + + Config::set(new Config($config)); + } + + public function testURLExtraction() + { + $text = 'this is a link: https://example.org?foo=bar&bar=foo - believe it or not'; + $urls = OpenGraph::extractUrlsFromText($text); + + $this->assertCount(1, $urls); + $this->assertEquals('https://example.org?foo=bar&bar=foo', $urls[0]); + } + + public function testURLExtractionFromHTML() + { + $html = Studip\Markup::HTML_MARKER . '<a href="https://example.org?foo=bar&bar=foo">this is a link</a> - believe it or not'; + $urls = OpenGraph::extractUrlsFromHtml($html); + + $this->assertCount(1, $urls); + $this->assertEquals('https://example.org?foo=bar&bar=foo', $urls[0]); + } +} diff --git a/tests/unit/lib/classes/PluginRepositoryTest.php b/tests/unit/lib/classes/PluginRepositoryTest.php index 6676b7c..d045d3f 100644 --- a/tests/unit/lib/classes/PluginRepositoryTest.php +++ b/tests/unit/lib/classes/PluginRepositoryTest.php @@ -10,7 +10,7 @@ * the License, or (at your option) any later version. */ -require_once 'lib/plugins/engine/PluginRepository.class.php'; +require_once 'lib/plugins/engine/PluginRepository.php'; class PluginRepositoryTest extends \Codeception\Test\Unit { diff --git a/tests/unit/lib/classes/RequestParametersTest.php b/tests/unit/lib/classes/RequestParametersTest.php index 85194c4..85ef33c 100644 --- a/tests/unit/lib/classes/RequestParametersTest.php +++ b/tests/unit/lib/classes/RequestParametersTest.php @@ -33,12 +33,20 @@ class RequestParametersTest extends Codeception\Test\Unit $_GET['v3'] = ['root@studip', 'hotte.testfreund', 42, '!"$%&/()']; $_POST['v4'] = ['0', '1', '', 'foo']; + $_GET['date'] = '2025-03-25'; + $_GET['time'] = '17:39:04'; + $_GET['datetime'] = '2025-03-25 17:39:04'; + $_GET['invalid_date'] = 'foobar'; + $testconfig = new Config([ 'USERNAME_REGULAR_EXPRESSION' => '/^([a-zA-Z0-9_@.-]{4,})$/', ]); Config::set($testconfig); } + /** + * @covers Request::offsetGet + */ public function testArrayAccess () { $request = Request::getInstance(); @@ -49,6 +57,9 @@ class RequestParametersTest extends Codeception\Test\Unit $this->assertSame($request['c'], '-23'); } + /** + * @covers Request::set + */ public function testSetParam () { Request::set('yyy', 'xyzzy'); @@ -58,6 +69,10 @@ class RequestParametersTest extends Codeception\Test\Unit $this->assertSame(Request::getArray('zzz'), [1, 2]); } + /** + * @covers Request::get + * @covers Request::quoted + */ public function testStringParam () { $this->assertNull(Request::get('null')); @@ -74,6 +89,9 @@ class RequestParametersTest extends Codeception\Test\Unit $this->assertNull(Request::quoted('v2')); } + /** + * @covers Request::option + */ public function testOptionParam () { $this->assertNull(Request::option('null')); @@ -82,6 +100,9 @@ class RequestParametersTest extends Codeception\Test\Unit $this->assertNull(Request::option('v1')); } + /** + * @covers Request::int + */ public function testIntParam () { $this->assertNull(Request::int('null')); @@ -92,6 +113,9 @@ class RequestParametersTest extends Codeception\Test\Unit $this->assertNull(Request::int('v1')); } + /** + * @covers Request::float + */ public function testFloatParam () { $this->assertNull(Request::float('null')); @@ -102,6 +126,9 @@ class RequestParametersTest extends Codeception\Test\Unit $this->assertNull(Request::float('v1')); } + /** + * @covers Request::bool + */ public function testBoolParam () { $this->assertNull(Request::bool('null')); @@ -115,6 +142,9 @@ class RequestParametersTest extends Codeception\Test\Unit $this->assertNull(Request::bool('v1')); } + /** + * @covers Request::username + */ public function testUsernameParam () { $this->assertNull(Request::username('null')); @@ -124,6 +154,10 @@ class RequestParametersTest extends Codeception\Test\Unit $this->assertNull(Request::username('v1')); } + /** + * @covers Request::getArray + * @covers Request::quotedArray + */ public function testStringArrayParam () { $this->assertSame(Request::getArray('null'), []); @@ -137,6 +171,9 @@ class RequestParametersTest extends Codeception\Test\Unit $this->assertSame(Request::quotedArray('v2'), ['on\\\'e', 'two', 'thr33']); } + /** + * @covers Request::optionArray + */ public function testOptionArrayParam () { $this->assertSame(Request::optionArray('null'), []); @@ -145,6 +182,9 @@ class RequestParametersTest extends Codeception\Test\Unit $this->assertSame(Request::optionArray('v2'), [1 => 'two', 2 => 'thr33']); } + /** + * @covers Request::intArray + */ public function testIntArrayParam () { $this->assertSame(Request::intArray('null'), []); @@ -153,6 +193,9 @@ class RequestParametersTest extends Codeception\Test\Unit $this->assertSame(Request::intArray('v2'), [0, 0, 0]); } + /** + * @covers Request::floatArray + */ public function testFloatArrayParam () { $this->assertSame(Request::floatArray('null'), []); @@ -161,6 +204,9 @@ class RequestParametersTest extends Codeception\Test\Unit $this->assertSame(Request::floatArray('v2'), [0.0, 0.0, 0.0]); } + /** + * @covers Request::boolArray + */ public function testBoolArrayParam () { $this->assertSame(Request::boolArray('null'), []); @@ -168,6 +214,9 @@ class RequestParametersTest extends Codeception\Test\Unit $this->assertSame(Request::boolArray('v4'), [false, true, false, true]); } + /** + * @covers Request::usernameArray + */ public function testUsernameArrayParam () { $this->assertSame(Request::usernameArray('null'), []); @@ -177,6 +226,9 @@ class RequestParametersTest extends Codeception\Test\Unit $this->assertSame(Request::usernameArray('v3'), ['root@studip', 'hotte.testfreund']); } + /** + * @covers Request::submitted + */ public function testSubmitted () { $this->assertFalse(Request::submitted('null')); @@ -184,12 +236,54 @@ class RequestParametersTest extends Codeception\Test\Unit $this->assertTrue(Request::submitted('v1')); } + /** + * @covers Request::submittedSome + */ public function testSubmittedSome () { $this->assertFalse(Request::submittedSome('null', 'null')); $this->assertTrue(Request::submittedSome('null', 's', 'v')); } + /** + * @covers Request::getDateTime + */ + public function testGetDatetimeWithDate() + { + $date = Request::getDateTime(); + $this->assertNotFalse($date); + $this->assertEquals('2025-03-25 00:00:00', $date->format('Y-m-d H:i:s')); + } + + /** + * @covers Request::getDateTime + */ + public function testGetDatetimeWithDateAndTime() + { + $datetime = Request::getDateTime('datetime', 'Y-m-d H:i:s'); + $this->assertNotFalse($datetime); + $this->assertEquals('2025-03-25 17:39:04', $datetime->format('Y-m-d H:i:s')); + } + + /** + * @covers Request::getDateTime + */ + public function testGetDatetimeWithDateAndTimeInTwoParameters() + { + $date = Request::getDateTime('date', 'Y-m-d', 'time', 'H:i:s'); + $this->assertNotFalse($date); + $this->assertEquals('2025-03-25 17:39:04', $date->format('Y-m-d H:i:s')); + } + + /** + * @covers Request::getDateTime + */ + public function testGetDatetimeWithInvalidDate() + { + $invalid_date = Request::getDateTime('invalid_date'); + $this->assertFalse($invalid_date); + } + public function tearDown(): void { Config::set(null); diff --git a/tests/unit/lib/classes/SimpleOrMapNodbTest.php b/tests/unit/lib/classes/SimpleOrMapNodbTest.php index 4c35d27..3ede1aa 100644 --- a/tests/unit/lib/classes/SimpleOrMapNodbTest.php +++ b/tests/unit/lib/classes/SimpleOrMapNodbTest.php @@ -46,10 +46,17 @@ class auth_user_md5 extends SimpleORMap } } +/** + * @backupGlobals enabled + */ class SimpleOrMapNodbTest extends \Codeception\Test\Unit { protected static function setupFixture(): void { + if (count($GLOBALS['CONTENT_LANGUAGES']) < 2) { + $GLOBALS['CONTENT_LANGUAGES']['en_GB'] = ['picture' => 'lang_en.gif', 'name' => 'English']; + } + StudipTestHelper::set_up_tables(['auth_user_md5']); } diff --git a/tests/unit/lib/classes/StudipCachedArrayTest.php b/tests/unit/lib/classes/StudipCachedArrayTest.php index c98c1bd..5fd39f2 100644 --- a/tests/unit/lib/classes/StudipCachedArrayTest.php +++ b/tests/unit/lib/classes/StudipCachedArrayTest.php @@ -1,4 +1,7 @@ <?php + +use Studip\Cache\MemoryCache; + /** * StudipCachedArrayTest.php - unit tests for the StudipCachedArray class * @@ -6,7 +9,7 @@ * @license GPL2 or any later version * * @covers StudipCachedArray - * @uses StudipMemoryCache + * @uses MemoryCache */ class StudipCachedArrayTest extends \Codeception\Test\Unit diff --git a/tests/unit/lib/classes/StudipControllerTest.php b/tests/unit/lib/classes/StudipControllerTest.php index f7e4ef4..51359ab 100644 --- a/tests/unit/lib/classes/StudipControllerTest.php +++ b/tests/unit/lib/classes/StudipControllerTest.php @@ -27,13 +27,13 @@ final class StudipControllerTest extends Codeception\Test\Unit parent::tearDown(); } - private function getDispatcher(): Trails_Dispatcher + private function getDispatcher(): Trails\Dispatcher { $trails_root = $GLOBALS['STUDIP_BASE_PATH'] . DIRECTORY_SEPARATOR . 'app'; $trails_uri = rtrim($GLOBALS['ABSOLUTE_URI_STUDIP'], '/') . '/dispatch.php'; $default_controller = 'default'; - return new Trails_Dispatcher($trails_root, $trails_uri, $default_controller); + return new Trails\Dispatcher($trails_root, $trails_uri, $default_controller); } private function getController(): StudipController @@ -247,7 +247,7 @@ final class StudipControllerTest extends Codeception\Test\Unit { $args = [$should_fail]; - $this->expectException(Trails_Exception::class); + $this->expectException(Trails\Exception::class); $this->getController()->validate_args($args); } diff --git a/tests/unit/lib/classes/StudipFileloaderTest.php b/tests/unit/lib/classes/StudipFileloaderTest.php index 1ffe6b5..102aaac 100644 --- a/tests/unit/lib/classes/StudipFileloaderTest.php +++ b/tests/unit/lib/classes/StudipFileloaderTest.php @@ -20,7 +20,7 @@ class StudipFileloaderTestCase extends \Codeception\Test\Unit ]); if (!stream_wrapper_register('var', 'ArrayFileStream')) { - new Exception('Failed to register protocol'); + throw new Exception('Failed to register protocol'); } } @@ -56,7 +56,7 @@ class StudipFileloaderTestCase extends \Codeception\Test\Unit public function test_should_balk_upon_file_not_found() { - $this->expectException(\PHPUnit\Framework\Exception::class); + $this->expectException(Exception::class); StudipFileloader::load('var://pathto/not-there.php', $container); } } diff --git a/tests/unit/lib/classes/TrailsTest.php b/tests/unit/lib/classes/TrailsTest.php new file mode 100644 index 0000000..d51b795 --- /dev/null +++ b/tests/unit/lib/classes/TrailsTest.php @@ -0,0 +1,25 @@ +<?php +class TrailsTest extends \Codeception\Test\Unit +{ + /** + * @covers Trails\Inflector::camelize + */ + public function testInflectorCamelize(): void + { + $this->assertEquals( + 'Path_SubPath_UnderscoreController', + Trails\Inflector::camelize('path/sub_path/underscore_controller') + ); + } + + /** + * @covers Trails\Inflector::underscore + */ + public function testInflectorUnderscore(): void + { + $this->assertEquals( + 'path/sub_path/underscore_controller', + Trails\Inflector::underscore('Path_SubPath_UnderscoreController') + ); + } +} diff --git a/tests/unit/lib/classes/extTPLTemplateTest.php b/tests/unit/lib/classes/extTPLTemplateTest.php new file mode 100644 index 0000000..43a9629 --- /dev/null +++ b/tests/unit/lib/classes/extTPLTemplateTest.php @@ -0,0 +1,213 @@ +<?php +/* + * template_test.php - expression template unit tests + * + * Copyright (c) 2013 Elmar Ludwig + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +use exTpl\Template; + +class extTplTemplateTest extends \Codeception\Test\Unit +{ + public function testSimpleString() + { + $bindings = []; + $template = 'The quick brown fox jumps over the layz dog.'; + $expected = $template; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testConstantExpression() + { + $bindings = array(); + $template = '17 + 4 = {"foo" != "bar" ? 17 + 4 : 42.0}'; + $expected = '17 + 4 = 21'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testConditionExpression() + { + $bindings = array('a' => 0, 'b' => 42); + $template = 'answer is {"" ?: a ?: b}'; + $expected = 'answer is 42'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testStringEscapes() + { + $bindings = array(); + $template = '"{"\\tfoo\'\\"\\n"}{\'{"bar"}\'}"'; + $expected = "\"\tfoo'\"\n{\"bar\"}\""; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testOperatorPrecedence() + { + $bindings = array('val' => array(array(42))); + $template = '{-val[0][0] / (17+4) + 8 > 6 && "foo" == "f"~"o"~"o" ? 1 : 2}'; + $expected = '2'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testSimpleBindings() + { + $bindings = array('foo' => 'bar', 'val' => array(17, 4), 'pi' => 3.14159); + $template = 'foo = "{foo}", sum = {val[0] + val[1]}, pi^2 = {pi * pi}, x = {x}'; + $expected = 'foo = "bar", sum = 21, pi^2 = 9.8695877281, x = '; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testConditional() + { + $bindings = array('foo' => 'bar', 'pi' => 3.14159); + $template = '{if foo == "foo"}NO{elseif foo == "bar"}pi = {pi}{else}NO{endif}'; + $expected = 'pi = 3.14159'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testConditionalIteration() + { + $bindings = array('foo' => 'bar', 'pi' => 3.14159); + $template = '{foreach foo}{if foo}{foo}{endif}{endforeach}'; + $expected = 'bar'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testIteration() + { + $bindings = array('persons' => array( + 1 => array('user' => 'jane', 'phone' => '555-81281'), + 2 => array('user' => 'mike', 'phone' => '230-28382'), + 3 => array('user' => 'john', 'phone' => '911-19212') + )); + $template = '<ul>{foreach persons as person}<li>{index~":"~person.user~":"~phone}</li>{endforeach}</ul>'; + $expected = '<ul>' . + '<li>1:jane:555-81281</li>' . + '<li>2:mike:230-28382</li>' . + '<li>3:john:911-19212</li>' . + '</ul>'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testEmptyIteration() + { + $bindings = array('foo' => array(), 'bar' => false); + $template = '{foreach foo}foo{endforeach}:{foreach bar}bar{endforeach}'; + $expected = ':'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testVariableScope() + { + $bindings = array('value' => 42, 'test' => array( + array(), + array('value' => 17), + array('test' => array( + array(), + array('value' => 4) + )) + )); + $template = '{foreach test}{value}:{foreach test}{value}~{endforeach}{endforeach}'; + $expected = '42:42~17~42~17:17~17~17~42:42~4~'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testNestedStatements() + { + foreach (range(0, 9) as $i) { + $bindings['loop'][$i]['i'] = "$i"; + } + $template = '{foreach loop}' . + '{if i+1>4 && i<(1+10/2)}{i==4*1 ? \'foo\'~i : "bar"}' . + '{elseif !(i<=+4)}+{elseif i==""}..{else}{"-"}{endif}' . + '{endforeach}'; + $expected = '----foo4bar++++'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testFunctionCall() + { + $bindings = array('val' => array(0, 1, 2, 3)); + $template = '{strlen("foobar") + count(val)}'; + $expected = '10'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testFilters() + { + $bindings = array( + 'pi' => 3.14159, + 'format' => function($a, $b) { return number_format($a, $b); }, + 'upper' => function($a) { return strtoupper($a); } + ); + $template = '{pi|format(3) ~ ":" ~ "foobar"|upper}'; + $expected = '3.142:FOOBAR'; + $tmpl_obj = new Template($template); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testRawFilter() + { + $bindings = array('foo' => '<img>', 'upper' => function($a) { return strtoupper($a); }); + $template = '{foo}:{foo|upper|raw}'; + $expected = '<img>:<IMG>'; + $tmpl_obj = new Template($template); + $tmpl_obj->autoescape('html'); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testHtmlAutoEscape() + { + $bindings = array('foo' => '<img>', 'pi' => 3.14159); + $template = '{foo}:{pi}'; + $expected = '<img>:3.14159'; + $tmpl_obj = new Template($template); + $tmpl_obj->autoescape('html'); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } + + public function testJsonAutoEscape() + { + $bindings = array('foo' => '<img>', 'pi' => 3.14159); + $template = '{foo}:{pi}'; + $expected = '"<img>":3.14159'; + $tmpl_obj = new Template($template); + $tmpl_obj->autoescape('json'); + + $this->assertEquals($expected, $tmpl_obj->render($bindings)); + } +} diff --git a/tests/unit/lib/flexi/FactoryTest.php b/tests/unit/lib/flexi/FactoryTest.php new file mode 100644 index 0000000..f8b9b87 --- /dev/null +++ b/tests/unit/lib/flexi/FactoryTest.php @@ -0,0 +1,119 @@ +<?php + +use Flexi\Factory; +use Flexi\TemplateNotFoundException; +use Flexi\PhpTemplate; + +final class FactoryTestCase extends \Codeception\Test\Unit +{ + private Factory $factory; + + public function setUp(): void + { + $this->setUpFS(); + + $this->factory = new Factory('var://templates'); + } + + public function tearDown(): void + { + unset($this->factory); + stream_wrapper_unregister('var'); + } + + public function setUpFS(): void + { + ArrayFileStream::set_filesystem([ + 'templates' => [ + 'foo.php' => 'some content', + 'baz.unknown' => 'some content', + 'multiplebasenames' => [ + 'foo.txt' => 'there is no matching template class', + 'foo.php' => 'some content', + 'bar.txt' => 'there is no matching template class', + ], + 'baz.known-ext' => 'some content', + ], + ]); + if (!stream_wrapper_register('var', ArrayFileStream::class)) { + die('Failed to register protocol'); + } + } + + public function testShouldCreateFactory() + { + $factory = new Factory('.'); + $this->assertNotNull($factory); + } + + public function testShouldCreateFactoryUsingPath() + { + $path = 'var://'; + $factory = new Factory($path); + $this->assertNotNull($factory); + } + + public function testShouldOpenTemplateUsingRelativePath() + { + $foo = $this->factory->open('foo'); + $this->assertNotNull($foo); + } + + public function testShouldOpenTemplateUsingAbsolutePath() + { + $foo = $this->factory->open('var://templates/foo'); + $this->assertNotNull($foo); + } + + public function testShouldThrowAnExceptionOpeningAMissingTemplateWithoutFileExtension() + { + $this->expectException(TemplateNotFoundException::class); + $this->factory->open('bar'); + } + + public function testShouldThrowAnExceptionOpeningAMissingTemplateWithFileExtension() + { + $this->expectException(TemplateNotFoundException::class); + $this->factory->open('bar.php'); + } + + public function testShouldOpenTemplateUsingExtension() + { + $this->assertInstanceOf( + PhpTemplate::class, + $this->factory->open('foo.php') + ); + } + + public function testShouldThrowAnExceptionWhenOpeningATemplateWithUnknownExtension() + { + $this->expectException(TemplateNotFoundException::class); + $this->factory->open('baz'); + } + + public function testShouldThrowAnExceptionOpeningATemplateInANonExistingDirectory() + { + $this->expectException(TemplateNotFoundException::class); + $this->factory->open('doesnotexist/foo'); + } + + public function testShouldSearchForASupportedTemplate() + { + $this->assertInstanceOf( + PhpTemplate::class, + $this->factory->open('multiplebasenames/foo') + ); + } + + public function testShouldRespondToAddedHandlers() + { + $handler = new class('', $this->factory) extends Flexi\Template { + protected function _render(): string + { + return ''; + } + }; + $this->factory->add_handler('known-ext', $handler::class); + $this->factory->open('baz.known-ext'); + } +} diff --git a/tests/unit/lib/flexi/PHPTemplatePartialBugTest.php b/tests/unit/lib/flexi/PHPTemplatePartialBugTest.php new file mode 100644 index 0000000..ef265cf --- /dev/null +++ b/tests/unit/lib/flexi/PHPTemplatePartialBugTest.php @@ -0,0 +1,45 @@ +<?php + +use Flexi\Factory; + +final class PhpTemplatePartialBugTestCase extends Codeception\Test\Unit +{ + public function setUp(): void + { + $this->setUpFS(); + $this->factory = new Factory('var://templates/'); + } + + public function tearDown(): void + { + unset($this->factory); + + stream_wrapper_unregister("var"); + } + + public function setUpFS(): void + { + ArrayFileStream::set_filesystem([ + 'templates' => [ + 'layout.php' => + '<? $do_not_echo_this = $this->render_partial_collection("partial", range(1, 5));' . + 'echo $content_for_layout;', + 'partial.php' => + 'partial', + 'template.php' => + 'template', + ] + ]); + if (!stream_wrapper_register('var', ArrayFileStream::class)) { + die('Failed to register protocol'); + } + } + + public function testPartialBug() + { + $template = $this->factory->open('template'); + $template->set_layout('layout'); + $result = $template->render(); + $this->assertEquals($result, "template"); + } +} diff --git a/tests/unit/lib/flexi/PHPTemplateTest.php b/tests/unit/lib/flexi/PHPTemplateTest.php new file mode 100644 index 0000000..5dfd247 --- /dev/null +++ b/tests/unit/lib/flexi/PHPTemplateTest.php @@ -0,0 +1,136 @@ +<?php + +use Flexi\Factory; +use Flexi\TemplateNotFoundException; + +final class PhpTemplateTestCase extends Codeception\Test\Unit +{ + private Factory $factory; + + public function setUp(): void + { + $this->setUpFS(); + $this->factory = new Factory('var://templates/'); + } + + + public function tearDown(): void + { + unset($this->factory); + + stream_wrapper_unregister('var'); + } + + public function setUpFS() + { + ArrayFileStream::set_filesystem([ + 'templates' => [ + 'foo_using_partial.php' => + 'Hello, <?= $this->render_partial("foos_partial") ?>!', + + 'foos_partial.php' => + '<h1><?= $whom ?> at <?= $when ?></h1>', + + 'foo_with_partial_collection.php' => + '[<?= $this->render_partial_collection("item", $items, "spacer") ?>]', + + 'item.php' => + '"<?= $item ?>"', + + 'spacer.php' => + ', ', + + 'attributes.php' => + '<? foreach (get_defined_vars() as $name => $value) : ?>' . + '<?= $name ?><?= $value ?>' . + '<? endforeach ?>', + + 'foo.php' => + 'Hello, <?= $whom ?>!', + + 'layout.php' => + '[<?= $content_for_layout ?>]', + ] + ]); + if (!stream_wrapper_register('var', ArrayFileStream::class)) { + die('Failed to register protocol'); + } + } + + public function testRenderPartial() + { + $template = $this->factory->open('foo_using_partial'); + $template->set_attribute('whom', 'bar'); + $this->assertEquals( + 'Hello, <h1>bar at now</h1>!', + $template->render(['when' => 'now']) + ); + } + + public function testRenderPartialCollection() + { + $template = $this->factory->open('foo_with_partial_collection'); + $result = $template->render_partial_collection( + 'item', + range(1, 3), + 'spacer' + ); + $this->assertEquals('"1", "2", "3"', $result); + } + + public function testShouldOverrideAttributesWithThosePassedToRender() + { + $template = $this->factory->open('attributes'); + $template->set_attribute('foo', 'baz'); + + $template->render(['foo' => 'bar']); + $this->assertEquals('bar', $template->get_attribute('foo')); + + $template->render(); + $this->assertEquals('bar', $template->get_attribute('foo')); + } + + public function testRenderWithoutLayout() + { + $foo = $this->factory->open('foo'); + $foo->set_attribute('whom', 'bar'); + $this->assertEquals('Hello, bar!', $foo->render()); + } + + public function testRenderWithLayout() + { + $foo = $this->factory->open('foo'); + $foo->set_attribute('whom', 'bar'); + $foo->set_layout('layout'); + $out = $foo->render(); + $this->assertEquals('[Hello, bar!]', $out); + } + + public function testRenderWithLayoutInline() + { + $this->assertEquals( + '[Hello, bar!]', + $this->factory->render('foo', ['whom' => 'bar'], 'layout') + ); + } + + public function testRenderWithMissingLayout() + { + $foo = $this->factory->open('foo'); + $this->expectException(TemplateNotFoundException::class); + $foo->set_layout('nosuchlayout'); + } + + public function testRenderWithAttributes() + { + $foo = $this->factory->open('foo'); + $foo->set_attribute('whom', 'bar'); + $foo->set_layout('layout'); + $foo_out = $foo->render(); + + $bar = $this->factory->open('foo'); + $bar_out = $bar->render(['whom' => 'bar'], 'layout'); + + $this->assertEquals($foo_out, $bar_out); + } +} diff --git a/tests/unit/lib/flexi/TemplateEmptyTest.php b/tests/unit/lib/flexi/TemplateEmptyTest.php new file mode 100644 index 0000000..c5ebd79 --- /dev/null +++ b/tests/unit/lib/flexi/TemplateEmptyTest.php @@ -0,0 +1,44 @@ +<?php + +use Flexi\Factory; +use Flexi\Template; + +final class TemplateEmptyTestCase extends \Codeception\Test\Unit +{ + private Factory $factory; + + public function setUp(): void + { + $this->factory = $this->make(Factory::class, [ + 'open' => $this->make(Template::class), + ]); + } + + public function tearDown(): void + { + unset($this->factory); + } + + public function testShouldHaveNoAttributes() + { + $template = $this->factory->open(''); + $this->assertCount(0, $template->get_attributes()); + } + + public function testShouldNotBeEmptyAfterSettingAnAttribute() + { + $template = $this->factory->open(''); + $template->set_attribute('foo', 'bar'); + $this->assertNotEmpty($template->get_attributes()); + } + + public function testShouldBeEmptyAfterClear() + { + $template = $this->factory->open('foo'); + + $this->assertEmpty($template->get_attributes()); + + $template->clear_attributes(); + $this->assertEmpty($template->get_attributes()); + } +} diff --git a/tests/unit/lib/flexi/TemplateMagicMethodsTest.php b/tests/unit/lib/flexi/TemplateMagicMethodsTest.php new file mode 100644 index 0000000..ad2690a --- /dev/null +++ b/tests/unit/lib/flexi/TemplateMagicMethodsTest.php @@ -0,0 +1,78 @@ +<?php + +use Flexi\Factory; +use Flexi\Template; + +final class TemplateMagicMethodsTestCase extends \Codeception\Test\Unit +{ + private Factory $factory; + + public function setUp(): void + { + $this->factory = $this->make(Factory::class, [ + 'open' => $this->make(Template::class), + ]); + $this->template = $this->factory->open(''); + } + + public function tearDown(): void + { + unset($this->factory); + unset($this->template); + } + + public function testShouldSetAnAttributeUsingTheMagicMethods() + { + $this->template->foo = 'bar'; + $this->assertEquals('bar', $this->template->get_attribute('foo')); + } + + public function testShouldNotSetAProtectedMemberFieldAsAnAttribute() + { + $this->template->layout = 'bar'; + $this->assertEquals('bar', $this->template->layout); + $this->assertNotEquals('bar', $this->template->get_layout()); + } + + public function testShouldOverwriteAnAttribute() + { + $this->template->set_attribute('foo', 'bar'); + $this->template->foo = 'baz'; + $this->assertEquals('baz', $this->template->get_attribute('foo')); + } + + public function testShouldReturnAnExistingAttributeUsingTheMagicMethods() + { + $this->template->set_attribute('foo', 'bar'); + $this->assertEquals('bar', $this->template->foo); + } + + public function testShouldReturnNullForANonExistingAttributeUsingTheMagicMethods() + { + $this->assertNull($this->template->foo); + } + + public function testShouldUnsetAnAttributeUsingTheMagicMethods() + { + $this->template->foo = 'bar'; + unset($this->template->foo); + $this->assertNull($this->template->foo); + } + + public function testShouldReturnNullOnUnsettingANonAttribute() + { + unset($this->template->foo); + $this->assertNull($this->template->foo); + } + + public function testShouldReturnTrueOnIssetForAnAttribute() + { + $this->template->foo = 'bar'; + $this->assertTrue(isset($this->template->foo)); + } + + public function testShouldReturnFalseOnIssetForANonExistingAttribute() + { + $this->assertFalse(isset($this->template->foo)); + } +} diff --git a/tests/unit/lib/flexi/TemplateTest.php b/tests/unit/lib/flexi/TemplateTest.php new file mode 100644 index 0000000..95e9145 --- /dev/null +++ b/tests/unit/lib/flexi/TemplateTest.php @@ -0,0 +1,68 @@ +<?php + +use Flexi\Factory; +use Flexi\Template; + +final class TemplateTestCase extends \Codeception\Test\Unit +{ + private Factory $factory; + + public function setUp(): void + { + $this->factory = $this->make(Factory::class, [ + 'open' => $this->make(Template::class), + ]); + } + + public function tearDown(): void + { + unset($this->factory); + } + + public function testShouldReturnAPreviouslySetAttribute() + { + $template = $this->factory->open('foo'); + $template->set_attribute('whom', 'bar'); + $this->assertEquals('bar', $template->get_attribute('whom')); + } + + public function testShouldReturnPreviouslySetAttributes() + { + $template = $this->factory->open('foo'); + $template->set_attributes(['whom' => 'bar', 'foo' => 'baz']); + + $attributes = $template->get_attributes(); + $this->assertIsArray($attributes); + $this->assertCount(2, $attributes); + $this->assertEquals('bar', $attributes['whom']); + $this->assertEquals('baz', $attributes['foo']); + } + + public function testShouldMergeAttributesWithSetAttributes() + { + $template = $this->factory->open('foo'); + $template->set_attributes(['a' => 1, 'b' => 2]); + + $this->assertCount(2, $template->get_attributes()); + $this->assertEquals(1, $template->get_attribute('a')); + $this->assertEquals(2, $template->get_attribute('b')); + + $template->set_attributes(['b' => 8, 'c' => 9]); + + $this->assertCount(3, $template->get_attributes()); + $this->assertEquals(1, $template->get_attribute('a')); + $this->assertEquals(8, $template->get_attribute('b')); + $this->assertEquals(9, $template->get_attribute('c')); + } + + public function testShouldBeEmptyAfterClear() + { + $template = $this->factory->open('foo'); + + $template->set_attributes(['a' => 1, 'b' => 2]); + $this->assertNotEmpty($template->get_attributes()); + + $template->clear_attributes(); + $this->assertCount(0, $template->get_attributes()); + } +} diff --git a/tests/unit/varstream.php b/tests/unit/varstream.php index 9a3acb5..d2047c0 100644 --- a/tests/unit/varstream.php +++ b/tests/unit/varstream.php @@ -7,15 +7,17 @@ class ArrayFileStream private static $fs; - static function set_filesystem(array $fs) { + static function set_filesystem(array $fs) + { ArrayFileStream::$fs = $fs; } - private static function &get_element($path) { + private static function &get_element($path) + { $result =& ArrayFileStream::$fs; foreach (preg_split('/\//', $path, -1, PREG_SPLIT_NO_EMPTY) as $element) { if (!isset($result[$element])) { - $null = NULL; + $null = null; return $null; } $result =& $result[$element]; @@ -23,9 +25,10 @@ class ArrayFileStream return $result; } - private static function &get_file($path) { + private static function &get_file($path) + { $url = parse_url($path); - $file =& self::get_element($url['host'] . $url['path']); + $file =& self::get_element($url['host'] . ($url['path'] ?? '')); if (is_null($file)) { throw new Exception("file not found."); @@ -33,15 +36,18 @@ class ArrayFileStream return $file; } - public function stream_close() { + public function stream_close() + { # nothing to do } - public function stream_flush() { + public function stream_flush() + { # nothing to do } - public function stream_open($path, $mode, $options, $opened_path) { + public function stream_open($path, $mode, $options, $opened_path) + { try { $this->open_file =& self::get_file($path); $this->position = 0; @@ -51,37 +57,41 @@ class ArrayFileStream } } - public function stream_read($count) { + public function stream_read($count) + { $ret = mb_substr($this->open_file, $this->position, $count); $this->position += mb_strlen($ret); return $ret; } - public function stream_write($data) { - $left = mb_substr($this->open_file, 0, $this->position); + public function stream_write($data) + { + $left = mb_substr($this->open_file, 0, $this->position); $right = mb_substr($this->open_file, $this->position + mb_strlen($data)); $this->open_file = $left . $data . $right; $this->position += mb_strlen($data); return mb_strlen($data); } - public function stream_tell() { + public function stream_tell() + { return $this->position; } - public function stream_eof() { + public function stream_eof() + { return $this->position >= mb_strlen($this->open_file); } - public function stream_seek($offset, $whence) { + public function stream_seek($offset, $whence) + { switch ($whence) { case SEEK_SET: if ($offset < mb_strlen($this->open_file) && $offset >= 0) { $this->position = $offset; return true; - } - else { + } else { return false; } break; @@ -90,8 +100,7 @@ class ArrayFileStream if ($offset >= 0) { $this->position += $offset; return true; - } - else { + } else { return false; } break; @@ -100,8 +109,7 @@ class ArrayFileStream if (mb_strlen($this->open_file) + $offset >= 0) { $this->position = mb_strlen($this->open_file) + $offset; return true; - } - else { + } else { return false; } break; @@ -115,44 +123,61 @@ class ArrayFileStream { } - public function stream_stat() { - return array('size' => is_array($this->open_file) - ? sizeof($this->open_file) - : mb_strlen($this->open_file)); + public function stream_stat() + { + return [ + 'size' => is_array($this->open_file) + ? sizeof($this->open_file) + : mb_strlen($this->open_file), + ]; } - public function unlink($path) { + public function unlink($path) + { $parent =& self::get_file(dirname($path)); if (is_array($parent) && isset($parent[basename($path)])) { unset($parent[basename($path)]); - return TRUE; + return true; } - return FALSE; + return false; } - public function rename($path_from, $path_to) { + public function rename($path_from, $path_to) + { throw new Exception('not implemented yet'); } - public function mkdir($path, $mode, $options) { + public function mkdir($path, $mode, $options) + { throw new Exception('not implemented yet'); } - public function rmdir($path, $options) { + public function rmdir($path, $options) + { throw new Exception('not implemented yet'); } - public function dir_opendir($path, $options) { + public function dir_opendir($path, $options) + { throw new Exception('not implemented yet'); } - public function url_stat($path, $flags) { + public function url_stat($path, $flags) + { + try { + if (!self::get_file($path)) { + return false; + } + } catch (Exception $e) { + return false; + } + $time = time(); - $keys = array( + $keys = [ 'dev' => 0, 'ino' => 0, 'mode' => 33216, // chmod 700 @@ -161,25 +186,28 @@ class ArrayFileStream 'gid' => function_exists('posix_getgid') ? posix_getgid() : 0, 'rdev' => 0, 'size' => $flags & STREAM_URL_STAT_QUIET - ? @mb_strlen($this->open_file) : mb_strlen($this->open_file), + ? @mb_strlen($this->open_file) : mb_strlen($this->open_file), 'atime' => $time, 'mtime' => $time, 'ctime' => $time, 'blksize' => 0, - 'blocks' => 0 - ); + 'blocks' => 0, + ]; return array_merge(array_values($keys), $keys); } - public function dir_readdir() { + public function dir_readdir() + { throw new Exception('not implemented yet'); } - public function dir_rewinddir() { + public function dir_rewinddir() + { throw new Exception('not implemented yet'); } - public function dir_closedir() { + public function dir_closedir() + { throw new Exception('not implemented yet'); } } |
