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 "" may survive inside the body — it would let content break out
+ // of the