From e7e7e5be89673037a0f0aedf85c97f5702cfb284 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Feb 2026 07:01:48 +0000 Subject: [PATCH] fix: add factories, fix HtmlSanitiser HTML5 elements, fix TestCase - Create Database/Factories for ContentWebhookEndpoint, ContentWebhookLog, ContentItem, ContentTaxonomy, ContentBrief - Register HTML5 elements (section, article, figure, figcaption, mark) with HTMLPurifier custom definitions - Use RefreshDatabase trait in TestCase with SQLite in-memory DB - Update Pest.php to use custom Tests\TestCase Co-Authored-By: Claude Opus 4.6 --- Database/Factories/ContentBriefFactory.php | 24 +++++++++++ Database/Factories/ContentItemFactory.php | 29 +++++++++++++ Database/Factories/ContentTaxonomyFactory.php | 25 +++++++++++ .../ContentWebhookEndpointFactory.php | 42 +++++++++++++++++++ .../Factories/ContentWebhookLogFactory.php | 31 ++++++++++++++ Services/HtmlSanitiser.php | 16 ++++--- tests/Pest.php | 4 +- tests/TestCase.php | 13 ++++++ 8 files changed, 175 insertions(+), 9 deletions(-) create mode 100644 Database/Factories/ContentBriefFactory.php create mode 100644 Database/Factories/ContentItemFactory.php create mode 100644 Database/Factories/ContentTaxonomyFactory.php create mode 100644 Database/Factories/ContentWebhookEndpointFactory.php create mode 100644 Database/Factories/ContentWebhookLogFactory.php diff --git a/Database/Factories/ContentBriefFactory.php b/Database/Factories/ContentBriefFactory.php new file mode 100644 index 0000000..3459f8c --- /dev/null +++ b/Database/Factories/ContentBriefFactory.php @@ -0,0 +1,24 @@ + Workspace::factory(), + 'title' => $this->faker->sentence(), + 'description' => $this->faker->paragraph(), + 'status' => ContentBrief::STATUS_PENDING, + ]; + } +} diff --git a/Database/Factories/ContentItemFactory.php b/Database/Factories/ContentItemFactory.php new file mode 100644 index 0000000..3d629f5 --- /dev/null +++ b/Database/Factories/ContentItemFactory.php @@ -0,0 +1,29 @@ + Workspace::factory(), + 'content_type' => ContentType::NATIVE->value, + 'type' => 'post', + 'status' => 'publish', + 'slug' => $this->faker->slug(), + 'title' => $this->faker->sentence(), + 'excerpt' => $this->faker->paragraph(), + 'content_html' => '

' . $this->faker->paragraphs(3, true) . '

', + ]; + } +} diff --git a/Database/Factories/ContentTaxonomyFactory.php b/Database/Factories/ContentTaxonomyFactory.php new file mode 100644 index 0000000..d8f5d79 --- /dev/null +++ b/Database/Factories/ContentTaxonomyFactory.php @@ -0,0 +1,25 @@ + Workspace::factory(), + 'type' => $this->faker->randomElement(['category', 'tag']), + 'name' => $this->faker->word(), + 'slug' => $this->faker->slug(), + 'count' => $this->faker->numberBetween(0, 100), + ]; + } +} diff --git a/Database/Factories/ContentWebhookEndpointFactory.php b/Database/Factories/ContentWebhookEndpointFactory.php new file mode 100644 index 0000000..c3e1582 --- /dev/null +++ b/Database/Factories/ContentWebhookEndpointFactory.php @@ -0,0 +1,42 @@ + Workspace::factory(), + 'name' => $this->faker->company(), + 'secret' => $this->faker->sha256(), + 'require_signature' => true, + 'allowed_types' => [], + 'is_enabled' => true, + 'failure_count' => 0, + ]; + } + + public function circuitBroken(): static + { + return $this->state([ + 'failure_count' => ContentWebhookEndpoint::MAX_FAILURES, + 'is_enabled' => false, + ]); + } + + public function disabled(): static + { + return $this->state([ + 'is_enabled' => false, + ]); + } +} diff --git a/Database/Factories/ContentWebhookLogFactory.php b/Database/Factories/ContentWebhookLogFactory.php new file mode 100644 index 0000000..30d2dc7 --- /dev/null +++ b/Database/Factories/ContentWebhookLogFactory.php @@ -0,0 +1,31 @@ + Workspace::factory(), + 'endpoint_id' => ContentWebhookEndpoint::factory(), + 'event_type' => $this->faker->randomElement([ + 'wordpress.post_created', + 'wordpress.post_updated', + 'cms.content_created', + ]), + 'payload' => ['title' => $this->faker->sentence()], + 'status' => 'pending', + 'source_ip' => $this->faker->ipv4(), + ]; + } +} diff --git a/Services/HtmlSanitiser.php b/Services/HtmlSanitiser.php index 301e225..3c5eb31 100644 --- a/Services/HtmlSanitiser.php +++ b/Services/HtmlSanitiser.php @@ -97,12 +97,16 @@ class HtmlSanitiser $config->set('HTML.Nofollow', true); $config->set('HTML.TargetNoopener', true); - // Disable cache in development, enable via config in production - $cacheDir = config('content.purifier_cache_dir'); - if ($cacheDir && is_dir($cacheDir) && is_writable($cacheDir)) { - $config->set('Cache.SerializerPath', $cacheDir); - } else { - $config->set('Cache.DefinitionImpl', null); + // Disable cache to allow custom HTML definitions + $config->set('Cache.DefinitionImpl', null); + + // Register HTML5 elements that HTMLPurifier doesn't know about + if ($def = $config->maybeGetRawHTMLDefinition()) { + $def->addElement('section', 'Block', 'Flow', 'Common'); + $def->addElement('article', 'Block', 'Flow', 'Common'); + $def->addElement('figure', 'Block', 'Flow', 'Common'); + $def->addElement('figcaption', 'Inline', 'Flow', 'Common'); + $def->addElement('mark', 'Inline', 'Inline', 'Common'); } // Safe URI schemes only diff --git a/tests/Pest.php b/tests/Pest.php index 7f6d8cf..2545261 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -2,6 +2,4 @@ declare(strict_types=1); -use Orchestra\Testbench\TestCase; - -uses(TestCase::class)->in('Feature', 'Unit'); +uses(Tests\TestCase::class)->in('Feature', 'Unit'); diff --git a/tests/TestCase.php b/tests/TestCase.php index c6d0316..4e09424 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -4,10 +4,13 @@ declare(strict_types=1); namespace Tests; +use Illuminate\Foundation\Testing\RefreshDatabase; use Orchestra\Testbench\TestCase as BaseTestCase; abstract class TestCase extends BaseTestCase { + use RefreshDatabase; + protected function getPackageProviders($app): array { return [ @@ -15,4 +18,14 @@ abstract class TestCase extends BaseTestCase \Core\Mod\Content\Boot::class, ]; } + + protected function getEnvironmentSetUp($app): void + { + $app['config']->set('database.default', 'testing'); + $app['config']->set('database.connections.testing', [ + 'driver' => 'sqlite', + 'database' => ':memory:', + 'prefix' => '', + ]); + } }