2026-01-26 23:59:46 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
2026-01-27 16:24:53 +00:00
|
|
|
namespace Core\Mod\Content\Tests\Unit;
|
2026-01-26 23:59:46 +00:00
|
|
|
|
2026-01-27 17:34:49 +00:00
|
|
|
use Core\Tenant\Models\Workspace;
|
2026-01-26 23:59:46 +00:00
|
|
|
use Illuminate\Support\Facades\Queue;
|
2026-01-27 16:24:53 +00:00
|
|
|
use Core\Mod\Content\Enums\ContentType;
|
|
|
|
|
use Core\Mod\Content\Jobs\ProcessContentWebhook;
|
|
|
|
|
use Core\Mod\Content\Models\ContentItem;
|
|
|
|
|
use Core\Mod\Content\Models\ContentWebhookEndpoint;
|
|
|
|
|
use Core\Mod\Content\Models\ContentWebhookLog;
|
2026-01-26 23:59:46 +00:00
|
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
|
|
|
use Tests\TestCase;
|
|
|
|
|
|
|
|
|
|
class ProcessContentWebhookTest extends TestCase
|
|
|
|
|
{
|
|
|
|
|
protected Workspace $workspace;
|
|
|
|
|
|
|
|
|
|
protected ContentWebhookEndpoint $endpoint;
|
|
|
|
|
|
|
|
|
|
protected function setUp(): void
|
|
|
|
|
{
|
|
|
|
|
parent::setUp();
|
|
|
|
|
|
|
|
|
|
$this->workspace = Workspace::factory()->create();
|
|
|
|
|
$this->endpoint = ContentWebhookEndpoint::factory()->create([
|
|
|
|
|
'workspace_id' => $this->workspace->id,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[Test]
|
|
|
|
|
public function it_processes_wordpress_post_created(): void
|
|
|
|
|
{
|
|
|
|
|
$log = ContentWebhookLog::factory()->create([
|
|
|
|
|
'workspace_id' => $this->workspace->id,
|
|
|
|
|
'endpoint_id' => $this->endpoint->id,
|
|
|
|
|
'event_type' => 'wordpress.post_created',
|
|
|
|
|
'status' => 'pending',
|
|
|
|
|
'payload' => [
|
|
|
|
|
'ID' => 123,
|
|
|
|
|
'post_title' => 'Test Post',
|
|
|
|
|
'post_name' => 'test-post',
|
|
|
|
|
'post_status' => 'publish',
|
|
|
|
|
'post_type' => 'post',
|
|
|
|
|
'post_content' => '<p>Test content</p>',
|
|
|
|
|
'post_excerpt' => 'Test excerpt',
|
|
|
|
|
],
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$job = new ProcessContentWebhook($log);
|
|
|
|
|
$job->handle();
|
|
|
|
|
|
|
|
|
|
$log->refresh();
|
|
|
|
|
$this->assertEquals('completed', $log->status);
|
|
|
|
|
|
|
|
|
|
// Verify content item was created
|
|
|
|
|
$contentItem = ContentItem::where('workspace_id', $this->workspace->id)
|
|
|
|
|
->where('wp_id', 123)
|
|
|
|
|
->first();
|
|
|
|
|
|
|
|
|
|
$this->assertNotNull($contentItem);
|
|
|
|
|
$this->assertEquals('Test Post', $contentItem->title);
|
|
|
|
|
$this->assertEquals('test-post', $contentItem->slug);
|
|
|
|
|
$this->assertEquals('publish', $contentItem->status);
|
|
|
|
|
$this->assertEquals(ContentType::WORDPRESS, $contentItem->content_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[Test]
|
|
|
|
|
public function it_updates_existing_post(): void
|
|
|
|
|
{
|
|
|
|
|
// Create existing content item
|
|
|
|
|
$existingItem = ContentItem::factory()->create([
|
|
|
|
|
'workspace_id' => $this->workspace->id,
|
|
|
|
|
'wp_id' => 456,
|
|
|
|
|
'title' => 'Original Title',
|
|
|
|
|
'slug' => 'original-slug',
|
|
|
|
|
'content_type' => ContentType::WORDPRESS,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$log = ContentWebhookLog::factory()->create([
|
|
|
|
|
'workspace_id' => $this->workspace->id,
|
|
|
|
|
'endpoint_id' => $this->endpoint->id,
|
|
|
|
|
'event_type' => 'wordpress.post_updated',
|
|
|
|
|
'status' => 'pending',
|
|
|
|
|
'payload' => [
|
|
|
|
|
'ID' => 456,
|
|
|
|
|
'post_title' => 'Updated Title',
|
|
|
|
|
'post_name' => 'updated-slug',
|
|
|
|
|
'post_status' => 'publish',
|
|
|
|
|
],
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$job = new ProcessContentWebhook($log);
|
|
|
|
|
$job->handle();
|
|
|
|
|
|
|
|
|
|
$existingItem->refresh();
|
|
|
|
|
$this->assertEquals('Updated Title', $existingItem->title);
|
|
|
|
|
$this->assertEquals('updated-slug', $existingItem->slug);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[Test]
|
|
|
|
|
public function it_handles_wordpress_post_deleted(): void
|
|
|
|
|
{
|
|
|
|
|
// Create content item to delete
|
|
|
|
|
$item = ContentItem::factory()->create([
|
|
|
|
|
'workspace_id' => $this->workspace->id,
|
|
|
|
|
'wp_id' => 789,
|
|
|
|
|
'content_type' => ContentType::WORDPRESS,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$log = ContentWebhookLog::factory()->create([
|
|
|
|
|
'workspace_id' => $this->workspace->id,
|
|
|
|
|
'endpoint_id' => $this->endpoint->id,
|
|
|
|
|
'event_type' => 'wordpress.post_deleted',
|
|
|
|
|
'status' => 'pending',
|
|
|
|
|
'payload' => [
|
|
|
|
|
'ID' => 789,
|
|
|
|
|
],
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$job = new ProcessContentWebhook($log);
|
|
|
|
|
$job->handle();
|
|
|
|
|
|
|
|
|
|
$log->refresh();
|
|
|
|
|
$this->assertEquals('completed', $log->status);
|
|
|
|
|
|
|
|
|
|
// Item should be soft deleted
|
|
|
|
|
$this->assertSoftDeleted('content_items', ['id' => $item->id]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[Test]
|
|
|
|
|
public function it_handles_wordpress_post_trashed(): void
|
|
|
|
|
{
|
|
|
|
|
$item = ContentItem::factory()->create([
|
|
|
|
|
'workspace_id' => $this->workspace->id,
|
|
|
|
|
'wp_id' => 101,
|
|
|
|
|
'status' => 'publish',
|
|
|
|
|
'content_type' => ContentType::WORDPRESS,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$log = ContentWebhookLog::factory()->create([
|
|
|
|
|
'workspace_id' => $this->workspace->id,
|
|
|
|
|
'endpoint_id' => $this->endpoint->id,
|
|
|
|
|
'event_type' => 'wordpress.post_trashed',
|
|
|
|
|
'status' => 'pending',
|
|
|
|
|
'payload' => [
|
|
|
|
|
'ID' => 101,
|
|
|
|
|
],
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$job = new ProcessContentWebhook($log);
|
|
|
|
|
$job->handle();
|
|
|
|
|
|
|
|
|
|
$item->refresh();
|
|
|
|
|
$this->assertEquals('trash', $item->status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[Test]
|
|
|
|
|
public function it_processes_cms_content_created(): void
|
|
|
|
|
{
|
|
|
|
|
$log = ContentWebhookLog::factory()->create([
|
|
|
|
|
'workspace_id' => $this->workspace->id,
|
|
|
|
|
'endpoint_id' => $this->endpoint->id,
|
|
|
|
|
'event_type' => 'cms.content_created',
|
|
|
|
|
'status' => 'pending',
|
|
|
|
|
'payload' => [
|
|
|
|
|
'content' => [
|
|
|
|
|
'id' => 'ext-123',
|
|
|
|
|
'title' => 'CMS Content',
|
|
|
|
|
'slug' => 'cms-content',
|
|
|
|
|
'status' => 'draft',
|
|
|
|
|
'body' => '<p>CMS content body</p>',
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$job = new ProcessContentWebhook($log);
|
|
|
|
|
$job->handle();
|
|
|
|
|
|
|
|
|
|
$log->refresh();
|
|
|
|
|
$this->assertEquals('completed', $log->status);
|
|
|
|
|
|
|
|
|
|
$contentItem = ContentItem::where('workspace_id', $this->workspace->id)
|
|
|
|
|
->where('wp_id', 'ext-123')
|
|
|
|
|
->first();
|
|
|
|
|
|
|
|
|
|
$this->assertNotNull($contentItem);
|
|
|
|
|
$this->assertEquals('CMS Content', $contentItem->title);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[Test]
|
|
|
|
|
public function it_handles_generic_payload(): void
|
|
|
|
|
{
|
|
|
|
|
$log = ContentWebhookLog::factory()->create([
|
|
|
|
|
'workspace_id' => $this->workspace->id,
|
|
|
|
|
'endpoint_id' => $this->endpoint->id,
|
|
|
|
|
'event_type' => 'generic.payload',
|
|
|
|
|
'status' => 'pending',
|
|
|
|
|
'payload' => [
|
|
|
|
|
'custom_key' => 'custom_value',
|
|
|
|
|
],
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$job = new ProcessContentWebhook($log);
|
|
|
|
|
$job->handle();
|
|
|
|
|
|
|
|
|
|
$log->refresh();
|
|
|
|
|
$this->assertEquals('completed', $log->status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[Test]
|
|
|
|
|
public function it_marks_log_as_failed_on_error(): void
|
|
|
|
|
{
|
|
|
|
|
$log = ContentWebhookLog::factory()->create([
|
|
|
|
|
'workspace_id' => $this->workspace->id,
|
|
|
|
|
'endpoint_id' => $this->endpoint->id,
|
|
|
|
|
'event_type' => 'wordpress.post_created',
|
|
|
|
|
'status' => 'pending',
|
|
|
|
|
'payload' => [], // Empty payload will fail
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$job = new ProcessContentWebhook($log);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$job->handle();
|
|
|
|
|
} catch (\Exception) {
|
|
|
|
|
// Expected
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$log->refresh();
|
|
|
|
|
// Note: The job marks it as completed even for skipped items
|
|
|
|
|
// The 'skipped' action is valid
|
|
|
|
|
$this->assertContains($log->status, ['completed', 'failed']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[Test]
|
|
|
|
|
public function it_resets_endpoint_failure_count_on_success(): void
|
|
|
|
|
{
|
|
|
|
|
$this->endpoint->update(['failure_count' => 3]);
|
|
|
|
|
|
|
|
|
|
$log = ContentWebhookLog::factory()->create([
|
|
|
|
|
'workspace_id' => $this->workspace->id,
|
|
|
|
|
'endpoint_id' => $this->endpoint->id,
|
|
|
|
|
'event_type' => 'generic.payload',
|
|
|
|
|
'status' => 'pending',
|
|
|
|
|
'payload' => ['test' => 'data'],
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$job = new ProcessContentWebhook($log);
|
|
|
|
|
$job->handle();
|
|
|
|
|
|
|
|
|
|
$this->endpoint->refresh();
|
|
|
|
|
$this->assertEquals(0, $this->endpoint->failure_count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[Test]
|
|
|
|
|
public function it_queues_on_correct_queue(): void
|
|
|
|
|
{
|
|
|
|
|
Queue::fake();
|
|
|
|
|
|
|
|
|
|
$log = ContentWebhookLog::factory()->create([
|
|
|
|
|
'workspace_id' => $this->workspace->id,
|
|
|
|
|
'status' => 'pending',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
ProcessContentWebhook::dispatch($log);
|
|
|
|
|
|
|
|
|
|
Queue::assertPushedOn('content-webhooks', ProcessContentWebhook::class);
|
|
|
|
|
}
|
|
|
|
|
}
|