diff --git a/backend/tests/Feature/ArticlePublishingTest.php b/backend/tests/Feature/ArticlePublishingTest.php deleted file mode 100644 index 59a39bc..0000000 --- a/backend/tests/Feature/ArticlePublishingTest.php +++ /dev/null @@ -1,177 +0,0 @@ -create(); - $article = Article::factory()->create([ - 'feed_id' => $feed->id, - 'url' => 'https://example.com/article', - 'approval_status' => 'approved', - ]); - - $listener = new PublishArticle(); - $event = new ArticleReadyToPublish($article); - - $listener->handle($event); - - Queue::assertPushed(PublishToLemmyJob::class); - } - - public function test_publish_article_listener_skips_already_published_articles(): void - { - Queue::fake(); - - $feed = Feed::factory()->create(); - $article = Article::factory()->create([ - 'feed_id' => $feed->id, - 'url' => 'https://example.com/article', - 'approval_status' => 'approved', - ]); - - // Create existing publication - ArticlePublication::create([ - 'article_id' => $article->id, - 'post_id' => 'existing-post-id', - 'platform_channel_id' => 1, - 'published_at' => now(), - 'published_by' => 'test-user', - ]); - - $listener = new PublishArticle(); - $event = new ArticleReadyToPublish($article); - - $listener->handle($event); - - Queue::assertNotPushed(PublishToLemmyJob::class); - } - - public function test_publish_to_lemmy_job_calls_publishing_service(): void - { - $feed = Feed::factory()->create(); - $article = Article::factory()->create([ - 'feed_id' => $feed->id, - 'url' => 'https://example.com/article', - 'approval_status' => 'approved', - ]); - - $job = new PublishToLemmyJob($article); - - $this->assertEquals('lemmy-posts', $job->queue); - $this->assertInstanceOf(PublishToLemmyJob::class, $job); - } - - public function test_article_ready_to_publish_event_integration(): void - { - Queue::fake(); - Event::fake(); - - $feed = Feed::factory()->create(); - $article = Article::factory()->create([ - 'feed_id' => $feed->id, - 'url' => 'https://example.com/article', - 'approval_status' => 'approved', - ]); - - event(new ArticleReadyToPublish($article)); - - Event::assertDispatched(ArticleReadyToPublish::class, function (ArticleReadyToPublish $event) use ($article) { - return $event->article->id === $article->id; - }); - } - - public function test_publishing_prevents_duplicate_publications(): void - { - $feed = Feed::factory()->create(); - $article = Article::factory()->create([ - 'feed_id' => $feed->id, - 'url' => 'https://example.com/article', - 'approval_status' => 'approved', - ]); - - ArticlePublication::create([ - 'article_id' => $article->id, - 'post_id' => 'first-post-id', - 'platform_channel_id' => 1, - 'published_at' => now(), - 'published_by' => 'test-user', - ]); - - $this->mock(ArticlePublishingService::class, function ($mock) { - $mock->shouldNotReceive('publishToRoutedChannels'); - }); - - $listener = new PublishArticle(); - $event = new ArticleReadyToPublish($article); - - $listener->handle($event); - - $this->assertEquals(1, ArticlePublication::where('article_id', $article->id)->count()); - } - - public function test_publish_article_listener_has_correct_queue_configuration(): void - { - $listener = new PublishArticle(); - - $this->assertEquals('lemmy-publish', $listener->queue); - $this->assertEquals(300, $listener->delay); - $this->assertEquals(3, $listener->tries); - $this->assertEquals(300, $listener->backoff); - } - - public function test_publish_to_lemmy_job_has_correct_queue_configuration(): void - { - $feed = Feed::factory()->create(); - $article = Article::factory()->create([ - 'feed_id' => $feed->id, - 'url' => 'https://example.com/article', - ]); - - $job = new PublishToLemmyJob($article); - - $this->assertEquals('lemmy-posts', $job->queue); - } - - public function test_multiple_articles_can_be_queued_independently(): void - { - Queue::fake(); - - $feed = Feed::factory()->create(); - $article1 = Article::factory()->create([ - 'feed_id' => $feed->id, - 'url' => 'https://example.com/article1', - 'approval_status' => 'approved', - ]); - $article2 = Article::factory()->create([ - 'feed_id' => $feed->id, - 'url' => 'https://example.com/article2', - 'approval_status' => 'approved', - ]); - - $listener = new PublishArticle(); - - $listener->handle(new ArticleReadyToPublish($article1)); - $listener->handle(new ArticleReadyToPublish($article2)); - - Queue::assertPushed(PublishToLemmyJob::class, 2); - } -} \ No newline at end of file diff --git a/backend/tests/Feature/Http/Controllers/Api/V1/LogsControllerTest.php b/backend/tests/Feature/Http/Controllers/Api/V1/LogsControllerTest.php index cbdfada..de1a42b 100644 --- a/backend/tests/Feature/Http/Controllers/Api/V1/LogsControllerTest.php +++ b/backend/tests/Feature/Http/Controllers/Api/V1/LogsControllerTest.php @@ -11,6 +11,13 @@ class LogsControllerTest extends TestCase { use RefreshDatabase; + protected function setUp(): void + { + parent::setUp(); + // Clear any logs that may have been created during application startup + Log::query()->delete(); + } + public function test_index_returns_successful_response(): void { Log::factory()->count(5)->create(); diff --git a/backend/tests/Feature/Http/Controllers/Api/V1/OnboardingControllerTest.php b/backend/tests/Feature/Http/Controllers/Api/V1/OnboardingControllerTest.php index 994b509..dfca1c1 100644 --- a/backend/tests/Feature/Http/Controllers/Api/V1/OnboardingControllerTest.php +++ b/backend/tests/Feature/Http/Controllers/Api/V1/OnboardingControllerTest.php @@ -285,11 +285,18 @@ public function test_create_channel_validates_required_fields() public function test_create_channel_creates_channel_successfully() { $platformInstance = PlatformInstance::factory()->create(); + $language = Language::factory()->create(); + + // Create a platform account for this instance first + PlatformAccount::factory()->create([ + 'instance_url' => $platformInstance->url, + 'is_active' => true + ]); $channelData = [ 'name' => 'test_community', 'platform_instance_id' => $platformInstance->id, - 'language_id' => 1, + 'language_id' => $language->id, 'description' => 'Test community description', ]; @@ -310,7 +317,7 @@ public function test_create_channel_creates_channel_successfully() 'name' => 'test_community', 'channel_id' => 'test_community', 'platform_instance_id' => $platformInstance->id, - 'language_id' => 1, + 'language_id' => $language->id, 'is_active' => true, ]); } diff --git a/backend/tests/Feature/Http/Controllers/Api/V1/PlatformChannelsControllerTest.php b/backend/tests/Feature/Http/Controllers/Api/V1/PlatformChannelsControllerTest.php index 1beb154..63765ca 100644 --- a/backend/tests/Feature/Http/Controllers/Api/V1/PlatformChannelsControllerTest.php +++ b/backend/tests/Feature/Http/Controllers/Api/V1/PlatformChannelsControllerTest.php @@ -2,6 +2,7 @@ namespace Tests\Feature\Http\Controllers\Api\V1; +use App\Models\PlatformAccount; use App\Models\PlatformChannel; use App\Models\PlatformInstance; use Illuminate\Foundation\Testing\RefreshDatabase; @@ -46,6 +47,12 @@ public function test_index_returns_successful_response(): void public function test_store_creates_platform_channel_successfully(): void { $instance = PlatformInstance::factory()->create(); + + // Create a platform account for this instance first + PlatformAccount::factory()->create([ + 'instance_url' => $instance->url, + 'is_active' => true + ]); $data = [ 'platform_instance_id' => $instance->id, @@ -76,7 +83,7 @@ public function test_store_creates_platform_channel_successfully(): void ]) ->assertJson([ 'success' => true, - 'message' => 'Platform channel created successfully!' + 'message' => 'Platform channel created successfully and linked to platform account!' ]); $this->assertDatabaseHas('platform_channels', [ diff --git a/backend/tests/Feature/JobsAndEventsTest.php b/backend/tests/Feature/JobsAndEventsTest.php index bc277fd..7ebe69e 100644 --- a/backend/tests/Feature/JobsAndEventsTest.php +++ b/backend/tests/Feature/JobsAndEventsTest.php @@ -3,17 +3,17 @@ namespace Tests\Feature; use App\Events\ArticleApproved; -use App\Events\ArticleReadyToPublish; +// use App\Events\ArticleReadyToPublish; // Class no longer exists use App\Events\ExceptionLogged; use App\Events\ExceptionOccurred; use App\Events\NewArticleFetched; use App\Jobs\ArticleDiscoveryForFeedJob; use App\Jobs\ArticleDiscoveryJob; -use App\Jobs\PublishToLemmyJob; +use App\Jobs\PublishNextArticleJob; use App\Jobs\SyncChannelPostsJob; use App\Listeners\LogExceptionToDatabase; -use App\Listeners\PublishApprovedArticle; -use App\Listeners\PublishArticle; +// use App\Listeners\PublishApprovedArticle; // Class no longer exists +// use App\Listeners\PublishArticle; // Class no longer exists use App\Listeners\ValidateArticleListener; use App\Models\Article; use App\Models\Feed; @@ -83,14 +83,12 @@ public function test_sync_channel_posts_job_processes_successfully(): void } - public function test_publish_to_lemmy_job_has_correct_configuration(): void + public function test_publish_next_article_job_has_correct_configuration(): void { - $article = Article::factory()->create(); + $job = new PublishNextArticleJob(); - $job = new PublishToLemmyJob($article); - - $this->assertEquals('lemmy-posts', $job->queue); - $this->assertInstanceOf(PublishToLemmyJob::class, $job); + $this->assertEquals('publishing', $job->queue); + $this->assertInstanceOf(PublishNextArticleJob::class, $job); } public function test_new_article_fetched_event_is_dispatched(): void @@ -120,18 +118,19 @@ public function test_article_approved_event_is_dispatched(): void }); } - public function test_article_ready_to_publish_event_is_dispatched(): void - { - Event::fake(); + // Test removed - ArticleReadyToPublish class no longer exists + // public function test_article_ready_to_publish_event_is_dispatched(): void + // { + // Event::fake(); - $article = Article::factory()->create(); + // $article = Article::factory()->create(); - event(new ArticleReadyToPublish($article)); + // event(new ArticleReadyToPublish($article)); - Event::assertDispatched(ArticleReadyToPublish::class, function (ArticleReadyToPublish $event) use ($article) { - return $event->article->id === $article->id; - }); - } + // Event::assertDispatched(ArticleReadyToPublish::class, function (ArticleReadyToPublish $event) use ($article) { + // return $event->article->id === $article->id; + // }); + // } public function test_exception_occurred_event_is_dispatched(): void { @@ -192,38 +191,40 @@ public function test_validate_article_listener_processes_new_article(): void $this->assertContains($article->approval_status, ['approved', 'rejected']); } - public function test_publish_approved_article_listener_queues_job(): void - { - Event::fake(); + // Test removed - PublishApprovedArticle and ArticleReadyToPublish classes no longer exist + // public function test_publish_approved_article_listener_queues_job(): void + // { + // Event::fake(); - $article = Article::factory()->create([ - 'approval_status' => 'approved', - 'approval_status' => 'approved', - ]); + // $article = Article::factory()->create([ + // 'approval_status' => 'approved', + // 'approval_status' => 'approved', + // ]); - $listener = new PublishApprovedArticle(); - $event = new ArticleApproved($article); + // $listener = new PublishApprovedArticle(); + // $event = new ArticleApproved($article); - $listener->handle($event); + // $listener->handle($event); - Event::assertDispatched(ArticleReadyToPublish::class); - } + // Event::assertDispatched(ArticleReadyToPublish::class); + // } - public function test_publish_article_listener_queues_publish_job(): void - { - Queue::fake(); + // Test removed - PublishArticle and ArticleReadyToPublish classes no longer exist + // public function test_publish_article_listener_queues_publish_job(): void + // { + // Queue::fake(); - $article = Article::factory()->create([ - 'approval_status' => 'approved', - ]); + // $article = Article::factory()->create([ + // 'approval_status' => 'approved', + // ]); - $listener = new PublishArticle(); - $event = new ArticleReadyToPublish($article); + // $listener = new PublishArticle(); + // $event = new ArticleReadyToPublish($article); - $listener->handle($event); + // $listener->handle($event); - Queue::assertPushed(PublishToLemmyJob::class); - } + // Queue::assertPushed(PublishNextArticleJob::class); + // } public function test_log_exception_to_database_listener_creates_log(): void { @@ -255,11 +256,13 @@ public function test_event_listener_registration_works(): void $listeners = Event::getListeners(NewArticleFetched::class); $this->assertNotEmpty($listeners); - $listeners = Event::getListeners(ArticleApproved::class); - $this->assertNotEmpty($listeners); + // ArticleApproved event exists but has no listeners after publishing redesign + // $listeners = Event::getListeners(ArticleApproved::class); + // $this->assertNotEmpty($listeners); - $listeners = Event::getListeners(ArticleReadyToPublish::class); - $this->assertNotEmpty($listeners); + // ArticleReadyToPublish no longer exists - removed this check + // $listeners = Event::getListeners(ArticleReadyToPublish::class); + // $this->assertNotEmpty($listeners); $listeners = Event::getListeners(ExceptionOccurred::class); $this->assertNotEmpty($listeners); @@ -267,13 +270,11 @@ public function test_event_listener_registration_works(): void public function test_job_retry_configuration(): void { - $article = Article::factory()->create(); + $job = new PublishNextArticleJob(); - $job = new PublishToLemmyJob($article); - - // Test that job has retry configuration - $this->assertObjectHasProperty('tries', $job); - $this->assertObjectHasProperty('backoff', $job); + // Test that job has unique configuration + $this->assertObjectHasProperty('uniqueFor', $job); + $this->assertEquals(300, $job->uniqueFor); } public function test_job_queue_configuration(): void @@ -284,13 +285,13 @@ public function test_job_queue_configuration(): void $discoveryJob = new ArticleDiscoveryJob(); $feedJob = new ArticleDiscoveryForFeedJob($feed); - $publishJob = new PublishToLemmyJob($article); + $publishJob = new PublishNextArticleJob(); $syncJob = new SyncChannelPostsJob($channel); // Test queue assignments $this->assertEquals('feed-discovery', $discoveryJob->queue ?? 'default'); $this->assertEquals('feed-discovery', $feedJob->queue ?? 'discovery'); - $this->assertEquals('lemmy-posts', $publishJob->queue); + $this->assertEquals('publishing', $publishJob->queue); $this->assertEquals('sync', $syncJob->queue ?? 'sync'); } diff --git a/backend/tests/Unit/Jobs/PublishToLemmyJobTest.php b/backend/tests/Unit/Jobs/PublishToLemmyJobTest.php deleted file mode 100644 index 563a69c..0000000 --- a/backend/tests/Unit/Jobs/PublishToLemmyJobTest.php +++ /dev/null @@ -1,109 +0,0 @@ - 'Test Article']); - - // Act - $job = new PublishToLemmyJob($article); - - // Assert - $this->assertEquals('lemmy-posts', $job->queue); - $this->assertEquals(3, $job->tries); - $this->assertEquals([60, 120, 300], $job->backoff); - } - - public function test_job_implements_should_queue(): void - { - // Arrange - $article = new Article(['title' => 'Test Article']); - $job = new PublishToLemmyJob($article); - - // Assert - $this->assertInstanceOf(\Illuminate\Contracts\Queue\ShouldQueue::class, $job); - } - - public function test_job_uses_queueable_trait(): void - { - // Arrange - $article = new Article(['title' => 'Test Article']); - $job = new PublishToLemmyJob($article); - - // Assert - $this->assertTrue(method_exists($job, 'onQueue')); - $this->assertTrue(method_exists($job, 'onConnection')); - $this->assertTrue(method_exists($job, 'delay')); - $this->assertTrue(method_exists($job, 'fail')); - } - - public function test_handle_method_exists(): void - { - // Arrange - $article = new Article(['title' => 'Test Article']); - $job = new PublishToLemmyJob($article); - - // Assert - $this->assertTrue(method_exists($job, 'handle')); - } - - public function test_job_calls_article_fetcher_and_publishing_service(): void - { - // This is a structural test - we can't easily mock static methods - // But we can verify the job has the correct structure - - // Arrange - $article = new Article(['title' => 'Test Article']); - $job = new PublishToLemmyJob($article); - - // Assert - Job should have handle method that uses the required services - $this->assertTrue(method_exists($job, 'handle')); - $this->assertIsObject($job); - - // We can't easily test the actual execution due to static method calls - // but we can verify the job structure is correct - $this->assertTrue(true); - } - - public function test_job_properties_are_correct_type(): void - { - // Arrange - $article = new Article(['title' => 'Test Article']); - $job = new PublishToLemmyJob($article); - - // Assert - $this->assertIsInt($job->tries); - $this->assertIsArray($job->backoff); - $this->assertGreaterThan(0, $job->tries); - $this->assertNotEmpty($job->backoff); - } - - public function test_job_backoff_increases_progressively(): void - { - // Arrange - $article = new Article(['title' => 'Test Article']); - $job = new PublishToLemmyJob($article); - - // Assert - Backoff should increase with each attempt - $backoff = $job->backoff; - $this->assertCount(3, $backoff); // Should match tries - $this->assertLessThan($backoff[1], $backoff[0]); // Second attempt waits longer than first - $this->assertLessThan($backoff[2], $backoff[1]); // Third attempt waits longer than second - } -} \ No newline at end of file diff --git a/backend/tests/Unit/Modules/Lemmy/Services/LemmyPublisherTest.php b/backend/tests/Unit/Modules/Lemmy/Services/LemmyPublisherTest.php index d09c02c..5f17c6b 100644 --- a/backend/tests/Unit/Modules/Lemmy/Services/LemmyPublisherTest.php +++ b/backend/tests/Unit/Modules/Lemmy/Services/LemmyPublisherTest.php @@ -308,15 +308,19 @@ public function test_publish_to_channel_handles_string_channel_id(): void ->once() ->andReturn('token'); - // Mock LemmyApiService - should receive integer conversion of channel_id + // Mock LemmyApiService - should call getCommunityId for non-numeric channel_id $apiMock = Mockery::mock(LemmyApiService::class); + $apiMock->shouldReceive('getCommunityId') + ->once() + ->with('string-42', 'token') + ->andReturn(42); $apiMock->shouldReceive('createPost') ->once() ->with( 'token', 'Test Title', '', - 0, // 'string-42' converts to 0 + 42, // resolved community ID 'https://example.com/article', null, null