diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/unit/Lib/ActivityTest.php | 48 | ||||
-rw-r--r-- | tests/unit/Lib/JcsEddsa2022Test.php | 8 | ||||
-rw-r--r-- | tests/unit/Lib/KeyutilsTest.php | 49 | ||||
-rw-r--r-- | tests/unit/Lib/MessageFilterTest.php | 207 | ||||
-rw-r--r-- | tests/unit/Module/HelpTest.php | 4 | ||||
-rw-r--r-- | tests/unit/Module/MagicTest.php | 8 | ||||
-rw-r--r-- | tests/unit/Module/TestCase.php | 2 | ||||
-rw-r--r-- | tests/unit/Widget/MessagesWidgetTest.php | 83 | ||||
-rw-r--r-- | tests/unit/includes/AccountTest.php | 21 | ||||
-rw-r--r-- | tests/unit/includes/BBCodeTest.php | 31 | ||||
-rw-r--r-- | tests/unit/includes/MarkdownTest.php | 4 | ||||
-rw-r--r-- | tests/unit/includes/PhotodriverTest.php | 58 |
12 files changed, 483 insertions, 40 deletions
diff --git a/tests/unit/Lib/ActivityTest.php b/tests/unit/Lib/ActivityTest.php index 543bf60bc..456a7535e 100644 --- a/tests/unit/Lib/ActivityTest.php +++ b/tests/unit/Lib/ActivityTest.php @@ -46,7 +46,7 @@ class ActivityTest extends UnitTestCase { * * @dataProvider get_mid_and_uuid_provider */ - public function test_get_mid_and_uuid(string $payload, $mid, $uuid): void { + public function test_get_mid_and_uuid(string $payload, string $mid, string $uuid): void { // @@ -274,4 +274,50 @@ class ActivityTest extends UnitTestCase { ]; } + public function testBuildPacketWithEmptyChannel(): void { + $data = [ 'aKey' => 'aValue' ]; + $packet = json_decode(Activity::build_packet($data, []), true); + + $this->assertArrayHasKey('aKey', $packet); + $this->assertEquals('aValue', $packet['aKey']); + } + + /** + * Test get protocols from an activitystreams actor object + * + * @dataProvider get_actor_protocols_provider + */ + public function test_get_actor_protocols(array $actor, array $expected): void { + $this->assertEquals($expected, Activity::get_actor_protocols($actor)); + } + + /** + * Dataprovider for test_get_actor_protocols. + */ + public static function get_actor_protocols_provider(): array { + return [ + 'none' => [ + ['tag' => [['type' => 'Note', 'name' => 'Website', 'content' => 'https://example.com']]], + [] + ], + 'legacy' => [ + ['tag' => [ + ['type' => 'PropertyValue', 'name' => 'Protocol', 'value' => 'zot6'], + ['type' => 'PropertyValue', 'name' => 'Protocol', 'value' => 'activitypub'], + ['type' => 'PropertyValue', 'name' => 'Protocol', 'value' => 'diaspora'] + ]], + ['zot6', 'activitypub', 'diaspora'] + ], + 'fep-fb2a' => [ + ['tag' => [['type' => 'Note', 'name' => 'Protocols', 'content' => 'zot6,activitypub,diaspora']]], + ['zot6', 'activitypub', 'diaspora'] + ], + 'fep-fb2a with spaces' => [ + ['tag' => [['type' => 'Note', 'name' => 'Protocols', 'content' => 'zot6, activitypub, diaspora']]], + ['zot6', 'activitypub', 'diaspora'] + ], + ]; + + + } } diff --git a/tests/unit/Lib/JcsEddsa2022Test.php b/tests/unit/Lib/JcsEddsa2022Test.php index d18ad01ce..7cdc655f8 100644 --- a/tests/unit/Lib/JcsEddsa2022Test.php +++ b/tests/unit/Lib/JcsEddsa2022Test.php @@ -3,6 +3,7 @@ namespace Zotlabs\Tests\Unit\Lib; use Zotlabs\Lib\JcsEddsa2022; +use Zotlabs\Lib\JcsEddsa2022SignException; use Zotlabs\Tests\Unit\UnitTestCase; class JcsEddsa2022Test extends UnitTestCase { @@ -171,4 +172,11 @@ class JcsEddsa2022Test extends UnitTestCase { $this->assertTrue($verified, 'Verify encode and decode eddsa-jcs-2022'); } + + public function testSignWithInvalidChannelShouldBeRejected(): void { + $this->expectException(JcsEddsa2022SignException::class); + + $alg = new JcsEddsa2022(); + $res = $alg->sign([], []); + } } diff --git a/tests/unit/Lib/KeyutilsTest.php b/tests/unit/Lib/KeyutilsTest.php index d1b0b5ab8..1f3fa2295 100644 --- a/tests/unit/Lib/KeyutilsTest.php +++ b/tests/unit/Lib/KeyutilsTest.php @@ -23,8 +23,9 @@ namespace Zotlabs\Tests\Unit\Lib; -use phpseclib\Crypt\RSA; -use phpseclib\Math\BigInteger; +use phpseclib3\Crypt\PublicKeyLoader; +use phpseclib3\Math\BigInteger; + use Zotlabs\Tests\Unit\UnitTestCase; use Zotlabs\Lib\Keyutils; @@ -38,56 +39,54 @@ class KeyutilsTest extends UnitTestCase { protected function getPubPKCS1() { $key = '-----BEGIN RSA PUBLIC KEY----- -MIIBCgKCAQEArXcEXQSkk25bwDxq5Ym85/OwernfOz0hgve46Jm1KXCF0+yeje8J -BDbQTsMgkF+G8eP1er3oz3E0qlIFpYrza5o6kaaLETSroTyZR5QW5S21r/QJHE+4 -F08bw1zp9hrlvoOCE/g/W0mr3asO/x7LrQRKOETlZ/U6HGexTdYLyKlXJtB+VKjI -XKAHxfVLRW2AvnFj+deowS1OhTN8ECpz88xG9wnh5agoq7Uol0WZNNm0p4oR6+cd -zTPx/mBwcOoSqHLlO7ZACbx/VyD5G7mQKWfGP4b96D8FcUO74531my+aKIpLF4Io -1JN4R4a4P8tZ8BkCnMvpuq9TF1s6vEthYQIDAQAB +MIIBCgKCAQEAsSBBV5khOWvMAOIV2AhxQukBgWc1pfYqUM/9OIFfL+KaRDOFAT0y +jMbtTCbHEjxZbasS3CNnHtAgJ+4BWjtFnZl7zY4pXb2RDe4IWNnK8BjqsWDVuPks +sc+yRBTepR/50FG/xB4HfI4GRc/8EDmynyAdLDD/r6oPxmz1gMICqSGDX8yXYG4o +DliNT3bWDH+uf1+6pWsN6IFQYmUoRLly1xsfc8AV4H1GmSFrbAvQpHA95GOlxnPY +IwVth1m8O+D9SH2+0e8ourdXH3/9ccJnh6FVvFvkyeC1e2vY+J+uC0uwu+6IvHFV +pSnS8lTmwG/BBh2dA5eqV3s+REdLdS/tsQIDAQAB -----END RSA PUBLIC KEY-----'; return str_replace(["\r", "\n"], "\r\n", $key); } protected function getPubPKCS8() { $key = '-----BEGIN PUBLIC KEY----- -MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUKfOIkFX/Zcv6bmaTIYO6OO2g -XQOne+iPfXo6YDdrtvvQNZwW5P/fptrgBzmUBkpuc/sEEKpMV2bGhBLsWSlPBYHe -2ewwLwyzbnuHvGhc1PzwMNQ7R60ubVDQT6sBVigYGZIDBgUPjAXeqmg5qgWWh04H -8Zf/YxyoGEovWDMxGQIDAQAB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsSBBV5khOWvMAOIV2Ahx +QukBgWc1pfYqUM/9OIFfL+KaRDOFAT0yjMbtTCbHEjxZbasS3CNnHtAgJ+4BWjtF +nZl7zY4pXb2RDe4IWNnK8BjqsWDVuPkssc+yRBTepR/50FG/xB4HfI4GRc/8EDmy +nyAdLDD/r6oPxmz1gMICqSGDX8yXYG4oDliNT3bWDH+uf1+6pWsN6IFQYmUoRLly +1xsfc8AV4H1GmSFrbAvQpHA95GOlxnPYIwVth1m8O+D9SH2+0e8ourdXH3/9ccJn +h6FVvFvkyeC1e2vY+J+uC0uwu+6IvHFVpSnS8lTmwG/BBh2dA5eqV3s+REdLdS/t +sQIDAQAB -----END PUBLIC KEY-----'; return str_replace(["\r", "\n"], "\r\n", $key); } public function testMeToPem() { - Keyutils::pemToMe($this->getPubPKCS8(), $m, $e); + [$m, $e] = Keyutils::pemToMe($this->getPubPKCS8()); $gen_key = Keyutils::meToPem($m, $e); self::assertEquals($this->getPubPKCS8(), $gen_key); } public function testRsaToPem() { - $rsa = new RSA(); - $rsa->setPublicKey($this->getPubPKCS8()); - $key = $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1); - $gen_key = Keyutils::rsaToPem($key); + $gen_key = Keyutils::rsaToPem($this->getPubPKCS1()); self::assertEquals($gen_key, $this->getPubPKCS8()); } public function testPemToRsa() { - $rsa = new RSA(); - $rsa->setPublicKey($this->getPubPKCS1()); - $key = $rsa->getPublicKey(RSA::PUBLIC_FORMAT_PKCS8); - $gen_key = Keyutils::pemToRsa($key); + $gen_key = Keyutils::pemToRsa($this->getPubPKCS8()); self::assertEquals($gen_key, $this->getPubPKCS1()); } public function testPemToMe() { - Keyutils::pemToMe($this->getPubPKCS8(), $m, $e); - $gen_key = new RSA(); - $gen_key->loadKey([ + [$m, $e] = Keyutils::pemToMe($this->getPubPKCS8()); + + $parsedKey = PublicKeyLoader::load([ 'e' => new BigInteger($e, 256), 'n' => new BigInteger($m, 256) ]); - self::assertEquals($gen_key->getPublicKey(), $this->getPubPKCS8()); + + self::assertEquals($parsedKey->toString('PKCS8'), $this->getPubPKCS8()); } } diff --git a/tests/unit/Lib/MessageFilterTest.php b/tests/unit/Lib/MessageFilterTest.php new file mode 100644 index 000000000..0a2aea0c6 --- /dev/null +++ b/tests/unit/Lib/MessageFilterTest.php @@ -0,0 +1,207 @@ +<?php +namespace Zotlabs\Tests\Unit\Lib; + +use Zotlabs\Tests\Unit\UnitTestCase; +use Zotlabs\Lib\MessageFilter; + +class MessageFilterTest extends UnitTestCase { + + /** + * Test the `evaluate` function. + * + * @dataProvider evaluate_provider + */ + public function test_evaluate(string $incl, string $excl, bool $result) : void { + // This is for simpler handling of the timestamps + date_default_timezone_set('UTC'); + + $item = [ + 'title' => '', + 'body' => "A grasshopper spent the summer hopping about in the sun and singing to his heart's content. One day, an ant went hurrying by, looking very hot and weary.\r\n#story #grasshopper #ant", + 'term' => [ + ['ttype' => TERM_HASHTAG, 'term' => 'story'], + ['ttype' => TERM_HASHTAG, 'term' => 'grasshopper'], + ['ttype' => TERM_HASHTAG, 'term' => 'ant'] + ], + 'verb' => 'Create', + 'obj_type' => 'Note', + 'obj' => [ + 'type' => 'Note', + 'attributedTo' => 'https://example.com/users/test', + 'summary' => null, + 'content' => "A grasshopper spent the summer hopping about in the sun and singing to his heart's content. One day, an ant went hurrying by, looking very hot and weary.\r\n#story #grasshopper #ant", + 'sensitive' => false + ], + 'item_private' => 0, + 'item_thread_top' => 1, + 'created' => '2025-04-18 20:50:00' + ]; + + $this->assertEquals($result, MessageFilter::evaluate($item, $incl, $excl)); + } + + public static function evaluate_provider() : array { + return [ + 'body contains incl' => [ + 'summer', + '', + true + ], + 'body contains excl' => [ + '', + 'summer', + false + ], + 'body contains word hopper (starting with a space) in excl using regex' => [ + '', + '/ hopper/', + true + ], + 'lang=en in incl' => [ + 'lang=en', + '', + true + ], + 'lang=en in excl' => [ + '', + 'lang=en', + false + ], + 'lang=de in incl' => [ + 'lang=de', + '', + false + ], + 'lang=de in excl' => [ + '', + 'lang=de', + true + ], + 'until=2025-04-18 20:49:00 in excl' => [ + '', + 'until=2025-04-18 20:49:00', + true + ], + 'until=2025-04-18 20:51:00 in excl' => [ + '', + 'until=2025-04-18 20:51:00', + false + ], + 'until=2025-04-18 20:49:00 in incl' => [ + 'until=2025-04-18 20:49:00', + '', + false + ], + 'until=2025-04-18 20:51:00 in incl' => [ + 'until=2025-04-18 20:51:00', + '', + true + ], + 'hashtag in incl' => [ + '#grasshopper', + '', + true + ], + 'hashtag in excl' => [ + '', + '#grasshopper', + false + ], + 'any hashtag in excl' => [ + '', + '#*', + false + ], + 'item.body contains substring hopper in excl' => [ + '', + '?body ~= hopper', + false + ], + 'item.verb == Announce in excl' => [ + '', + '?verb == Announce', + true + ], + 'item.verb != Announce in incl' => [ + '?verb != Announce', + '', + true + ], + 'combined body contains word and item.verb == Announce in excl' => [ + '', + "summer\r\n?verb == Announce", + false + ], + 'item.item_thread_top == 1 in excl' => [ + '', + "?item_thread_top == 1", + false + ], + 'combined item_private == 0 and item.item_thread_top == 1 in excl' => [ + '', + "?item_private == 0\r\n?item_thread_top == 1", + false + ], + 'item.item_private < 1 in excl' => [ + '', + "?item_private < 1", + false + ], + 'item.item_thread_top = 1 and item.item_private > 0 in excl' => [ + '', + "?item_thread_top == 1 && ?item_private > 0 ", + true + ], + 'item.item_thread_top = 1 and item.item_private < 1 in excl' => [ + '', + "?item_thread_top == 1 && ?item_private < 1 ", + false + ], + 'item.item_thread_top = 1 or item.item_private = 0 in excl' => [ + '', + "?item_thread_top == 1 && ?item_private == 0", + false + ], + 'item.item_private < 1 and item.item_thread_top = 1 in excl' => [ + '', + "?item_private < 1 && ?item_thread_top == 1", + false + ], + 'item.item_private < 1 and item.item_thread_top = 0 in excl' => [ + '', + "?item_private < 1 && ?item_thread_top == 0", + true + ], + 'combined item.verb = Create, item.item_private < 1 and item.item_thread_top = 0 in excl' => [ + '', + "?verb == Create\r\n?item_private < 1 && ?item_thread_top == 1", + false + ], + 'item.obj contains value Note in incl' => [ + '?obj {} Note', + '', + true + ], + 'item.obj contains key type in incl' => [ + '?obj {*} type', + '', + true + ], + 'obj.type = Note in incl' => [ + '?+type == Note', + '', + true + ], + 'obj.sensitive = true in incl' => [ + '?+sensitive', + '', + false + ], + 'obj.sensitive != false in incl' => [ + '?+!sensitive', + '', + true + ], + ]; + } +} diff --git a/tests/unit/Module/HelpTest.php b/tests/unit/Module/HelpTest.php index 1feef26a8..e1aea1a06 100644 --- a/tests/unit/Module/HelpTest.php +++ b/tests/unit/Module/HelpTest.php @@ -76,13 +76,13 @@ class HelpTest extends \Zotlabs\Tests\Unit\Module\TestCase { public function test_get_request_without_args_redirects_to_about_page(): void { $this->stub_goaway(); $this->expectException(\Zotlabs\Tests\Unit\Module\RedirectException::class); - $this->expectExceptionMessage('about/about'); + $this->expectExceptionMessage('about'); $this->get('help'); } public function test_getting_locale_with_no_topic_should_redirect_to_about_page(): void { - $this->expectRedirectTo('help/about/about'); + $this->expectRedirectTo('help/about'); $this->get('help/de'); } diff --git a/tests/unit/Module/MagicTest.php b/tests/unit/Module/MagicTest.php index 4a03d9d57..2c426bf76 100644 --- a/tests/unit/Module/MagicTest.php +++ b/tests/unit/Module/MagicTest.php @@ -46,9 +46,9 @@ class MagicTest extends TestCase { App::set_baseurl($baseurl); - App::$observer = [ + App::set_observer([ 'xchan_hash' => 'the hash', - ]; + ]); // We pass a local URL, and have a valid observer, but as the // delegate param is not passed, nothing will be done except @@ -72,9 +72,9 @@ class MagicTest extends TestCase { App::$timezone = 'UTC'; // Simulate a foreign (to this hub) observer, - App::$observer = [ + App::set_observer([ 'xchan_hash' => 'foreign hash', - ]; + ]); // Create the channel the foreign observer wants to access $result = create_identity([ diff --git a/tests/unit/Module/TestCase.php b/tests/unit/Module/TestCase.php index 1a4cf52fc..dd88a5a3b 100644 --- a/tests/unit/Module/TestCase.php +++ b/tests/unit/Module/TestCase.php @@ -43,6 +43,8 @@ class TestCase extends UnitTestCase { $_SERVER['REQUEST_METHOD'] = $method; $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1'; $_SERVER['QUERY_STRING'] = "q={$uri}"; + $_SERVER['REQUEST_URI'] = $uri; + // phpcs:disable Generic.PHP.DisallowRequestSuperglobal.Found $_REQUEST = array_merge($_GET, $_POST); // phpcs::enable diff --git a/tests/unit/Widget/MessagesWidgetTest.php b/tests/unit/Widget/MessagesWidgetTest.php new file mode 100644 index 000000000..ca025ff43 --- /dev/null +++ b/tests/unit/Widget/MessagesWidgetTest.php @@ -0,0 +1,83 @@ +<?php +/* + * SPDX-FileCopyrightText: 2025 The Hubzilla Community + * SPDX-FileContributor: Harald Eilertsen <haraldei@anduin.net> + * + * SPDX-License-Identifier: MIT + */ + +namespace Zotlabs\Tests\Unit\Widget; + +use App; +use Zotlabs\Widget\Messages; +use Zotlabs\Tests\Unit\Module\TestCase; + +class MessagesWidgetTest extends TestCase +{ + use \phpmock\phpunit\PHPMock; + + /** + * List of file tags should be empty if there are no file tags. + */ + public function testNoFileTags(): void + { + $local_channe_stub = $this->getFunctionMock('Zotlabs\Widget', 'local_channel') + ->expects($this->any()) + ->willReturn(42); + + $feature_enabled_stub = $this->getFunctionMock('Zotlabs\Widget', 'feature_enabled') + ->expects($this->any()) + ->willReturn(true); + + $this->renderWidget(); + $this->assertOutputMatches('|<datalist\s+id="data_filetags">\s+</datalist>|'); + } + + /** + * The widget lists file tags that are defined for the channel. + */ + public function testFileTagsAreListed(): void + { + $local_channe_stub = $this->getFunctionMock('Zotlabs\Widget', 'local_channel') + ->expects($this->any()) + ->willReturn(42); + + $feature_enabled_stub = $this->getFunctionMock('Zotlabs\Widget', 'feature_enabled') + ->expects($this->any()) + ->willReturn(true); + + /* + * Add a few tags. + */ + store_item_tag(42, 1, TERM_OBJ_POST, TERM_FILE, 'test_file_tag', ''); + store_item_tag(42, 1, TERM_OBJ_POST, TERM_FILE, 'test_file_tag2', ''); + + $this->renderWidget(); + $this->assertOutputMatches('|<option\s+value="test_file_tag">|'); + $this->assertOutputMatches('|<option\s+value="test_file_tag2">|'); + } + + /** + * Initializes the app and calls the widget code. + */ + private function renderWidget(): void { + $_GET['q'] = 'hq'; + $_SERVER['REQUEST_METHOD'] = 'GET'; + + App::init(); + + $widget = new Messages(); + $this->output = $widget->widget([]); + } + + /** + * Asserts that the output matches a given regex pattern. + * + * If the pattern does not match, the test will be marked as failed. + * + * @param string $pattern The regex that should be matched. + */ + private function assertOutputMatches(string $pattern): void { + $this->assertMatchesRegularExpression($pattern, $this->output); + } +} diff --git a/tests/unit/includes/AccountTest.php b/tests/unit/includes/AccountTest.php index 3978f9d04..66c761ef5 100644 --- a/tests/unit/includes/AccountTest.php +++ b/tests/unit/includes/AccountTest.php @@ -1,9 +1,28 @@ <?php + +use Zotlabs\Tests\Unit\UnitTestCase; + /** * Tests for account handling helper functions. */ +class AccountTest extends UnitTestCase { + + /** + * Test the `get_account_id()` function. + */ + public function test_get_account_id() { + App::set_account(null); + unset($_SESSION['account_id']); + + $this->assertEquals(false, get_account_id(), 'get_account_id() should return false if not authenticated'); + + App::set_account(['account_id' => 36]); + $this->assertEquals(36, get_account_id(), 'get_account_id() should return account from global App object'); + + $_SESSION['account_id'] = 42; + $this->assertEquals(42, get_account_id(), 'get_account_id() should return the account from the session'); + } -class AccountTest extends Zotlabs\Tests\Unit\UnitTestCase { public function test_get_account_by_id_returns_existing_account() { $account = get_account_by_id(42); $this->assertNotFalse($account); diff --git a/tests/unit/includes/BBCodeTest.php b/tests/unit/includes/BBCodeTest.php index 136fc6e0e..982ef4eb9 100644 --- a/tests/unit/includes/BBCodeTest.php +++ b/tests/unit/includes/BBCodeTest.php @@ -23,6 +23,7 @@ namespace Zotlabs\Tests\Unit\includes; +use App; use Zotlabs\Tests\Unit\UnitTestCase; class BBCodeTest extends UnitTestCase { @@ -42,7 +43,7 @@ class BBCodeTest extends UnitTestCase { */ public function test_bbcode_observer(string $src, bool $logged_in, string $lang, string $expected): void { if ($logged_in) { - \App::$observer = [ + App::set_observer([ 'xchan_addr' => '', 'xchan_name' => '', 'xchan_connurl' => '', @@ -50,9 +51,9 @@ class BBCodeTest extends UnitTestCase { // port required in xchan url due to bug in get_rpost_path 'xchan_url' => 'https://example.com:666', - ]; + ]); } else { - \App::$observer = null; + App::set_observer(null); } \App::$language = $lang; @@ -141,20 +142,36 @@ class BBCodeTest extends UnitTestCase { ], 'naked url is converted to link' => [ 'example url: https://example.com', - 'example url: <a href="https://example.com" target="_blank" rel="nofollow noopener">https://example.com</a>' + 'example url: <a href="https://example.com" target="_blank" rel="nofollow noopener">https://example.com</a>' ], 'naked url followed by newline' => [ "https://www.example.com\nhave a great day.", - '<a href="https://www.example.com" target="_blank" rel="nofollow noopener">https://www.example.com</a><br />have a great day.', + '<a href="https://www.example.com" target="_blank" rel="nofollow noopener">https://www.example.com</a><br />have a great day.', ], 'inline naked url' => [ "This is a link https://example.com/some/path more info.", - 'This is a link <a href="https://example.com/some/path" target="_blank" rel="nofollow noopener">https://example.com/some/path</a> more info.', + 'This is a link <a href="https://example.com/some/path" target="_blank" rel="nofollow noopener">https://example.com/some/path</a> more info.', ], 'naked url within code block is not converted to link' => [ "[code]\nhttp://example.com\n[/code]", "<pre><code>http://example.com</code></pre>" ], + 'geo uri is converted to link' => [ + 'example url: [url]geo:37.786971,-122.399677;u=35[/url]', + 'example url: <a href="geo:37.786971,-122.399677;u=35" target="_blank" rel="nofollow noopener">geo:37.786971,-122.399677;u=35</a>' + ], + 'geo uri with label is converted to link' => [ + 'example url: [url=geo:37.786971,-122.399677;u=35(Wikimedia+Foundation)]Wikimedia Foundation[/url]', + 'example url: <a href="geo:37.786971,-122.399677;u=35(Wikimedia+Foundation)" target="_blank" rel="nofollow noopener">Wikimedia Foundation</a>' + ], + 'naked geo uri is converted to link' => [ + 'example url: geo:37.786971,-122.399677;u=35', + 'example url: <a href="geo:37.786971,-122.399677;u=35" target="_blank" rel="nofollow noopener">geo:37.786971,-122.399677;u=35</a>' + ], + 'naked geo uri with label is converted to link' => [ + 'example url: geo:37.78918,-122.40335(Wikimedia+Foundation)', + 'example url: <a href="geo:37.78918,-122.40335(Wikimedia+Foundation)" target="_blank" rel="nofollow noopener">📍Wikimedia Foundation</a>' + ], ]; } @@ -205,7 +222,7 @@ class BBCodeTest extends UnitTestCase { '[rpost=a title]This is the body[/rpost]', true, 'en', - '<a href="https://example.com:666/rpost?f=&title=a+title&body=This+is+the+body" target="_blank" rel="nofollow noopener">https://example.com:666/rpost?f=&title=a+title&body=This+is+the+body</a>', + '<a href="https://example.com:666/rpost?f=&title=a+title&body=This+is+the+body" target="_blank" rel="nofollow noopener">https://example.com:666/rpost?f=&title=a+title&body=This+is+the+body</a>', ], 'unauthenticated observer rpost' => [ '[rpost=a title]This is the body[/rpost]', diff --git a/tests/unit/includes/MarkdownTest.php b/tests/unit/includes/MarkdownTest.php index 55dbb4445..eec809174 100644 --- a/tests/unit/includes/MarkdownTest.php +++ b/tests/unit/includes/MarkdownTest.php @@ -114,6 +114,10 @@ class MarkdownTest extends UnitTestCase { 'This is a link https://example.com/some/path more info.', 'This is a link https://example.com/some/path more info.', ], + 'mention with underscores is untouched' => [ + '@{_test_@somesite.example} @{test_2_@othersite.example}', + '@{_test_@somesite.example} @{test_2_@othersite.example}', + ], ]; } diff --git a/tests/unit/includes/PhotodriverTest.php b/tests/unit/includes/PhotodriverTest.php index 34dc058b7..db9883589 100644 --- a/tests/unit/includes/PhotodriverTest.php +++ b/tests/unit/includes/PhotodriverTest.php @@ -20,4 +20,62 @@ class PhotodriverTest extends UnitTestCase { $photo = \photo_factory(file_get_contents('images/hz-16.png'), 'image/png'); $this->assertInstanceOf('Zotlabs\Photo\PhotoGd', $photo); } + + // Helper to create a temporary image file + private function createTempImage($type = 'jpeg'): string + { + $tmp = tempnam(sys_get_temp_dir(), 'img'); + switch ($type) { + case 'png': + $im = imagecreatetruecolor(10, 10); + imagepng($im, $tmp); + imagedestroy($im); + break; + case 'jpeg': + default: + $im = imagecreatetruecolor(10, 10); + imagejpeg($im, $tmp); + imagedestroy($im); + break; + } + return $tmp; + } + + public function testGuessImageTypeFromRawData() + { + $filename = 'irrelevant'; + $data = [ + 'body' => file_get_contents($this->createTempImage('jpeg')) + ]; + $result = guess_image_type($filename, $data); + $this->assertEquals('image/jpeg', $result); + } + + public function testGuessImageTypeFromLocalFile() + { + $file = $this->createTempImage('png'); + $result = guess_image_type($file); + $this->assertEquals('image/png', $result); + unlink($file); + } + + public function testGuessImageTypeFromHeaders() + { + $filename = 'irrelevant'; + $data = [ + 'header' => "Content-Type: image/jpeg\nOther: value" + ]; + $result = guess_image_type($filename, $data); + $this->assertEquals('image/jpeg', $result); + } + + public function testGuessImageTypeUnknownTypeReturnsNull() + { + $filename = 'not_an_image.txt'; + $data = [ + 'body' => 'not an image' + ]; + $result = guess_image_type($filename, $data); + $this->assertNull($result); + } } |