fix: add factories, fix HtmlSanitiser HTML5 elements, fix TestCase
Some checks are pending
CI / PHP 8.3 (push) Waiting to run
CI / PHP 8.4 (push) Waiting to run

- 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 <noreply@anthropic.com>
This commit is contained in:
Claude 2026-02-23 07:01:48 +00:00
parent 4d66f0b101
commit e7e7e5be89
No known key found for this signature in database
GPG key ID: AF404715446AEB41
8 changed files with 175 additions and 9 deletions

View file

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Content\Database\Factories;
use Core\Mod\Content\Models\ContentBrief;
use Core\Tenant\Models\Workspace;
use Illuminate\Database\Eloquent\Factories\Factory;
class ContentBriefFactory extends Factory
{
protected $model = ContentBrief::class;
public function definition(): array
{
return [
'workspace_id' => Workspace::factory(),
'title' => $this->faker->sentence(),
'description' => $this->faker->paragraph(),
'status' => ContentBrief::STATUS_PENDING,
];
}
}

View file

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Content\Database\Factories;
use Core\Mod\Content\Enums\ContentType;
use Core\Mod\Content\Models\ContentItem;
use Core\Tenant\Models\Workspace;
use Illuminate\Database\Eloquent\Factories\Factory;
class ContentItemFactory extends Factory
{
protected $model = ContentItem::class;
public function definition(): array
{
return [
'workspace_id' => 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' => '<p>' . $this->faker->paragraphs(3, true) . '</p>',
];
}
}

View file

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Content\Database\Factories;
use Core\Mod\Content\Models\ContentTaxonomy;
use Core\Tenant\Models\Workspace;
use Illuminate\Database\Eloquent\Factories\Factory;
class ContentTaxonomyFactory extends Factory
{
protected $model = ContentTaxonomy::class;
public function definition(): array
{
return [
'workspace_id' => Workspace::factory(),
'type' => $this->faker->randomElement(['category', 'tag']),
'name' => $this->faker->word(),
'slug' => $this->faker->slug(),
'count' => $this->faker->numberBetween(0, 100),
];
}
}

View file

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Content\Database\Factories;
use Core\Mod\Content\Models\ContentWebhookEndpoint;
use Core\Tenant\Models\Workspace;
use Illuminate\Database\Eloquent\Factories\Factory;
class ContentWebhookEndpointFactory extends Factory
{
protected $model = ContentWebhookEndpoint::class;
public function definition(): array
{
return [
'workspace_id' => 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,
]);
}
}

View file

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Content\Database\Factories;
use Core\Mod\Content\Models\ContentWebhookEndpoint;
use Core\Mod\Content\Models\ContentWebhookLog;
use Core\Tenant\Models\Workspace;
use Illuminate\Database\Eloquent\Factories\Factory;
class ContentWebhookLogFactory extends Factory
{
protected $model = ContentWebhookLog::class;
public function definition(): array
{
return [
'workspace_id' => 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(),
];
}
}

View file

@ -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

View file

@ -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');

View file

@ -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' => '',
]);
}
}