From 903d4d37c8b015619c37d29bb3a1b84047fdb473 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 29 Jun 2026 10:52:55 -0500 Subject: [PATCH] test: add unit tests for pure JsonLdBuilder methods (#33 partial) - JsonLdBuilderLocalBusinessTest: buildLocalBusiness() null/minimal/custom-type/ partial-address/geo-requires-both-coords cases - JsonLdScriptTagTest: toScriptTag() wraps in ld+json, escapes breakout, and produces valid JSON after unescaping Covers the pure (Factory/Uri/DB-free) helpers that run under the existing minimal bootstrap. The remaining #33 targets (ImageHelper, BatchController, CSV import, content/system plugin integration) need a Joomla test harness. --- .../Helper/JsonLdBuilderLocalBusinessTest.php | 93 +++++++++++++++++++ tests/Unit/Helper/JsonLdScriptTagTest.php | 56 +++++++++++ 2 files changed, 149 insertions(+) create mode 100644 tests/Unit/Helper/JsonLdBuilderLocalBusinessTest.php create mode 100644 tests/Unit/Helper/JsonLdScriptTagTest.php diff --git a/tests/Unit/Helper/JsonLdBuilderLocalBusinessTest.php b/tests/Unit/Helper/JsonLdBuilderLocalBusinessTest.php new file mode 100644 index 0000000..be1de69 --- /dev/null +++ b/tests/Unit/Helper/JsonLdBuilderLocalBusinessTest.php @@ -0,0 +1,93 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Mokoconsulting\MokoOG\Tests\Unit\Helper; + +use Joomla\Plugin\System\MokoOG\Helper\JsonLdBuilder; +use PHPUnit\Framework\TestCase; + +class JsonLdBuilderLocalBusinessTest extends TestCase +{ + /** + * Minimal Registry-like stand-in exposing get($key, $default). + */ + private function params(array $data): object + { + return new class ($data) { + private array $data; + + public function __construct(array $data) + { + $this->data = $data; + } + + public function get($key, $default = null) + { + return $this->data[$key] ?? $default; + } + }; + } + + public function testReturnsNullWithoutName(): void + { + $this->assertNull(JsonLdBuilder::buildLocalBusiness($this->params([]))); + $this->assertNull(JsonLdBuilder::buildLocalBusiness($this->params(['lb_name' => ' ']))); + } + + public function testMinimalSchemaHasNoOptionalKeys(): void + { + $result = JsonLdBuilder::buildLocalBusiness($this->params(['lb_name' => 'Acme Co'])); + + $this->assertSame('https://schema.org', $result['@context']); + $this->assertSame('LocalBusiness', $result['@type']); + $this->assertSame('Acme Co', $result['name']); + $this->assertArrayNotHasKey('address', $result); + $this->assertArrayNotHasKey('geo', $result); + $this->assertArrayNotHasKey('telephone', $result); + } + + public function testCustomTypeAndPartialAddress(): void + { + $result = JsonLdBuilder::buildLocalBusiness($this->params([ + 'lb_name' => 'Joe Pizza', + 'lb_type' => 'Restaurant', + 'lb_street' => '1 Main St', + 'lb_city' => 'Springfield', + 'lb_country' => 'US', + 'lb_phone' => '+1-555-0100', + ])); + + $this->assertSame('Restaurant', $result['@type']); + $this->assertSame('PostalAddress', $result['address']['@type']); + $this->assertSame('1 Main St', $result['address']['streetAddress']); + $this->assertSame('Springfield', $result['address']['addressLocality']); + $this->assertSame('US', $result['address']['addressCountry']); + $this->assertArrayNotHasKey('postalCode', $result['address']); + $this->assertSame('+1-555-0100', $result['telephone']); + } + + public function testGeoRequiresBothCoordinates(): void + { + $partial = JsonLdBuilder::buildLocalBusiness($this->params([ + 'lb_name' => 'X', + 'lb_latitude' => '1.0', + ])); + $this->assertArrayNotHasKey('geo', $partial); + + $full = JsonLdBuilder::buildLocalBusiness($this->params([ + 'lb_name' => 'X', + 'lb_latitude' => '1.0', + 'lb_longitude' => '2.0', + ])); + $this->assertSame('GeoCoordinates', $full['geo']['@type']); + $this->assertSame('1.0', $full['geo']['latitude']); + $this->assertSame('2.0', $full['geo']['longitude']); + } +} diff --git a/tests/Unit/Helper/JsonLdScriptTagTest.php b/tests/Unit/Helper/JsonLdScriptTagTest.php new file mode 100644 index 0000000..757e484 --- /dev/null +++ b/tests/Unit/Helper/JsonLdScriptTagTest.php @@ -0,0 +1,56 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +namespace Mokoconsulting\MokoOG\Tests\Unit\Helper; + +use Joomla\Plugin\System\MokoOG\Helper\JsonLdBuilder; +use PHPUnit\Framework\TestCase; + +class JsonLdScriptTagTest extends TestCase +{ + private const OPEN = ''; + + private function body(string $output): string + { + return substr($output, \strlen(self::OPEN), -\strlen(self::CLOSE)); + } + + public function testWrapsInLdJsonScriptTag(): void + { + $out = JsonLdBuilder::toScriptTag(['@type' => 'Thing']); + + $this->assertStringStartsWith(self::OPEN, $out); + $this->assertStringEndsWith(self::CLOSE, $out); + } + + public function testEscapesClosingScriptInsideData(): void + { + $out = JsonLdBuilder::toScriptTag(['name' => 'evil ']); + $body = $this->body($out); + + // No raw " block. The builder rewrites every "assertStringNotContainsString('assertStringContainsString('<\\/', $body); + } + + public function testBodyIsValidJsonAfterUnescaping(): void + { + $out = JsonLdBuilder::toScriptTag(['@context' => 'https://schema.org', '@type' => 'Article']); + $json = str_replace('<\\/', 'body($out)); + + $decoded = json_decode($json, true); + + $this->assertIsArray($decoded); + $this->assertSame('Article', $decoded['@type']); + $this->assertSame('https://schema.org', $decoded['@context']); + } +}