diff --git a/backend/app/Http/Controllers/ArticlesController.php b/backend/app/Http/Controllers/ArticlesController.php deleted file mode 100644 index 101a2a6..0000000 --- a/backend/app/Http/Controllers/ArticlesController.php +++ /dev/null @@ -1,37 +0,0 @@ -orderBy('created_at', 'desc') - ->paginate(15); - - $publishingApprovalsEnabled = Setting::isPublishingApprovalsEnabled(); - - return view('pages.articles.index', compact('articles', 'publishingApprovalsEnabled')); - } - - public function approve(Article $article): RedirectResponse - { - $article->approve('manual'); - - return redirect()->back()->with('success', 'Article approved and queued for publishing.'); - } - - public function reject(Article $article): RedirectResponse - { - $article->reject('manual'); - - return redirect()->back()->with('success', 'Article rejected.'); - } -} diff --git a/backend/app/Http/Controllers/FeedsController.php b/backend/app/Http/Controllers/FeedsController.php deleted file mode 100644 index f233e2d..0000000 --- a/backend/app/Http/Controllers/FeedsController.php +++ /dev/null @@ -1,81 +0,0 @@ -orderBy('name') - ->get(); - - return view('pages.feeds.index', compact('feeds')); - } - - public function create(): View - { - return view('pages.feeds.create'); - } - - public function store(StoreFeedRequest $request): RedirectResponse - { - $validated = $request->validated(); - $validated['is_active'] = $validated['is_active'] ?? true; - - Feed::create($validated); - - return OnboardingRedirectService::handleRedirect( - $request, - 'feeds.index', - 'Feed created successfully!' - ); - } - - public function show(Feed $feed): View - { - return view('pages.feeds.show', compact('feed')); - } - - public function edit(Feed $feed): View - { - return view('pages.feeds.edit', compact('feed')); - } - - public function update(UpdateFeedRequest $request, Feed $feed): RedirectResponse - { - $validated = $request->validated(); - $validated['is_active'] = $validated['is_active'] ?? $feed->is_active; - - $feed->update($validated); - - return redirect()->route('feeds.index') - ->with('success', 'Feed updated successfully!'); - } - - public function destroy(Feed $feed): RedirectResponse - { - $feed->delete(); - - return redirect()->route('feeds.index') - ->with('success', 'Feed deleted successfully!'); - } - - public function toggle(Feed $feed): RedirectResponse - { - $newStatus = !$feed->is_active; - $feed->update(['is_active' => $newStatus]); - - $status = $newStatus ? 'activated' : 'deactivated'; - - return redirect()->route('feeds.index') - ->with('success', "Feed {$status} successfully!"); - } -} diff --git a/backend/app/Http/Controllers/LogsController.php b/backend/app/Http/Controllers/LogsController.php deleted file mode 100644 index c38fb91..0000000 --- a/backend/app/Http/Controllers/LogsController.php +++ /dev/null @@ -1,17 +0,0 @@ -paginate(50); - - return view('pages.logs.index', compact('logs')); - } -} diff --git a/backend/app/Http/Controllers/OnboardingController.php b/backend/app/Http/Controllers/OnboardingController.php deleted file mode 100644 index 2403c25..0000000 --- a/backend/app/Http/Controllers/OnboardingController.php +++ /dev/null @@ -1,114 +0,0 @@ -needsOnboarding()) { - $systemStatus = resolve(SystemStatusService::class)->getSystemStatus(); - $statsService = resolve(DashboardStatsService::class); - - $period = $request->get('period', 'today'); - $stats = $statsService->getStats($period); - $systemStats = $statsService->getSystemStats(); - $availablePeriods = $statsService->getAvailablePeriods(); - - return view('pages.dashboard', compact( - 'systemStatus', - 'stats', - 'systemStats', - 'availablePeriods', - 'period' - )); - } - - return view('onboarding.welcome'); - } - - public function platform(): View|RedirectResponse - { - if (!$this->needsOnboarding()) { - return redirect()->route('feeds.index'); - } - - return view('onboarding.platform'); - } - - public function feed(): View|RedirectResponse - { - if (!$this->needsOnboarding()) { - return redirect()->route('feeds.index'); - } - - if (!$this->hasPlatformAccount()) { - return redirect()->route('onboarding.platform'); - } - - return view('onboarding.feed'); - } - - public function channel(): View|RedirectResponse - { - if (!$this->needsOnboarding()) { - return redirect()->route('feeds.index'); - } - - if (!$this->hasPlatformAccount()) { - return redirect()->route('onboarding.platform'); - } - - if (!$this->hasFeed()) { - return redirect()->route('onboarding.feed'); - } - - return view('onboarding.channel'); - } - - public function complete(): View|RedirectResponse - { - if (!$this->needsOnboarding()) { - return redirect()->route('feeds.index'); - } - - if (!$this->hasPlatformAccount() || !$this->hasFeed() || !$this->hasChannel()) { - return redirect()->route('onboarding.index'); - } - - $systemStatus = resolve(SystemStatusService::class)->getSystemStatus(); - - return view('onboarding.complete', compact('systemStatus')); - } - - private function needsOnboarding(): bool - { - return !$this->hasPlatformAccount() || !$this->hasFeed() || !$this->hasChannel(); - } - - private function hasPlatformAccount(): bool - { - return PlatformAccount::where('is_active', true)->exists(); - } - - private function hasFeed(): bool - { - return Feed::where('is_active', true)->exists(); - } - - private function hasChannel(): bool - { - return PlatformChannel::where('is_active', true)->exists(); - } -} \ No newline at end of file diff --git a/backend/app/Http/Controllers/PlatformAccountsController.php b/backend/app/Http/Controllers/PlatformAccountsController.php deleted file mode 100644 index e96a57a..0000000 --- a/backend/app/Http/Controllers/PlatformAccountsController.php +++ /dev/null @@ -1,106 +0,0 @@ -orderBy('created_at', 'desc')->get(); - - return view('pages.platforms.index', compact('accounts')); - } - - public function create(): View - { - return ViewFacade::make('pages.platforms.create'); - } - - public function store(Request $request): RedirectResponse - { - $validated = $request->validate([ - 'platform' => 'required|in:lemmy,mastodon,reddit', - 'instance_url' => 'required|url', - 'username' => 'required|string|max:255', - 'password' => 'required|string', - 'settings' => 'nullable|array', - ]); - - // Create or find platform instance - $platformEnum = PlatformEnum::from($validated['platform']); - $instance = PlatformInstance::firstOrCreate([ - 'platform' => $platformEnum, - 'url' => $validated['instance_url'], - ], [ - 'name' => parse_url($validated['instance_url'], PHP_URL_HOST), - 'description' => ucfirst($validated['platform']) . ' instance', - 'is_active' => true, - ]); - - $account = PlatformAccount::create($validated); - - // If this is the first account for this platform, make it active - if (! PlatformAccount::where('platform', $validated['platform'])->where('is_active', true)->exists()) { - $account->setAsActive(); - } - - // Check if there's a redirect_to parameter for onboarding flow - $redirectTo = $request->input('redirect_to'); - if ($redirectTo) { - return redirect($redirectTo) - ->with('success', 'Platform account created successfully!'); - } - - return redirect()->route('platforms.index') - ->with('success', 'Platform account created successfully!'); - } - - public function edit(PlatformAccount $platformAccount): View - { - return ViewFacade::make('pages.platforms.edit', compact('platformAccount')); - } - - public function update(Request $request, PlatformAccount $platformAccount): RedirectResponse - { - $validated = $request->validate([ - 'instance_url' => 'required|url', - 'username' => 'required|string|max:255', - 'password' => 'nullable|string', - 'settings' => 'nullable|array', - ]); - - // Don't update password if not provided - if (empty($validated['password'])) { - unset($validated['password']); - } - - $platformAccount->update($validated); - - return redirect()->route('platforms.index') - ->with('success', 'Platform account updated successfully!'); - } - - public function destroy(PlatformAccount $platformAccount): RedirectResponse - { - $platformAccount->delete(); - - return redirect()->route('platforms.index') - ->with('success', 'Platform account deleted successfully!'); - } - - public function setActive(PlatformAccount $platformAccount): RedirectResponse - { - $platformAccount->setAsActive(); - - return redirect()->route('platforms.index') - ->with('success', "Set $platformAccount->username@$platformAccount->instance_url as active for {$platformAccount->platform->value}!"); - } -} diff --git a/backend/app/Http/Controllers/PlatformChannelsController.php b/backend/app/Http/Controllers/PlatformChannelsController.php deleted file mode 100644 index 364301c..0000000 --- a/backend/app/Http/Controllers/PlatformChannelsController.php +++ /dev/null @@ -1,116 +0,0 @@ -orderBy('platform_instance_id') - ->orderBy('name') - ->get(); - - return ViewFacade::make('pages.channels.index', compact('channels')); - } - - public function create(): View - { - $instances = PlatformInstance::where('is_active', true) - ->orderBy('name') - ->get(); - - return ViewFacade::make('pages.channels.create', compact('instances')); - } - - public function store(Request $request): RedirectResponse - { - $validated = $request->validate([ - 'platform_instance_id' => 'required|exists:platform_instances,id', - 'name' => 'required|string|max:255', - 'display_name' => 'nullable|string|max:255', - 'channel_id' => 'nullable|string|max:255', - 'description' => 'nullable|string', - 'language_id' => 'required|exists:languages,id', - 'is_active' => 'boolean', - ]); - - // Default is_active to true if not provided - $validated['is_active'] = $validated['is_active'] ?? true; - - // Set display_name to name if not provided - $validated['display_name'] = $validated['display_name'] ?? $validated['name']; - - PlatformChannel::create($validated); - - // Check if there's a redirect_to parameter for onboarding flow - $redirectTo = $request->input('redirect_to'); - if ($redirectTo) { - return redirect($redirectTo) - ->with('success', 'Channel created successfully!'); - } - - return redirect()->route('channels.index') - ->with('success', 'Channel created successfully!'); - } - - public function show(PlatformChannel $channel): View - { - $channel->load(['platformInstance', 'feeds']); - - return ViewFacade::make('pages.channels.show', compact('channel')); - } - - public function edit(PlatformChannel $channel): View - { - $instances = PlatformInstance::where('is_active', true) - ->orderBy('name') - ->get(); - - return ViewFacade::make('pages.channels.edit', compact('channel', 'instances')); - } - - public function update(Request $request, PlatformChannel $channel): RedirectResponse - { - $validated = $request->validate([ - 'platform_instance_id' => 'required|exists:platform_instances,id', - 'name' => 'required|string|max:255', - 'display_name' => 'nullable|string|max:255', - 'channel_id' => 'nullable|string|max:255', - 'description' => 'nullable|string', - 'language_id' => 'required|exists:languages,id', - 'is_active' => 'boolean', - ]); - - $channel->update($validated); - - return redirect()->route('channels.index') - ->with('success', 'Channel updated successfully!'); - } - - public function destroy(PlatformChannel $channel): RedirectResponse - { - $channel->delete(); - - return redirect()->route('channels.index') - ->with('success', 'Channel deleted successfully!'); - } - - public function toggle(PlatformChannel $channel): RedirectResponse - { - $newStatus = !$channel->is_active; - $channel->update(['is_active' => $newStatus]); - - $status = $newStatus ? 'activated' : 'deactivated'; - - return redirect()->route('channels.index') - ->with('success', "Channel {$status} successfully!"); - } -} \ No newline at end of file diff --git a/backend/app/Http/Controllers/RoutingController.php b/backend/app/Http/Controllers/RoutingController.php deleted file mode 100644 index ed02425..0000000 --- a/backend/app/Http/Controllers/RoutingController.php +++ /dev/null @@ -1,173 +0,0 @@ -where('is_active', true) - ->orderBy('name') - ->get(); - - $channels = PlatformChannel::with(['platformInstance', 'feeds']) - ->where('is_active', true) - ->orderBy('name') - ->get(); - - return view('pages.routing.index', compact('feeds', 'channels')); - } - - public function create(): View - { - $feeds = Feed::where('is_active', true) - ->orderBy('name') - ->get(); - - $channels = PlatformChannel::with('platformInstance') - ->where('is_active', true) - ->orderBy('name') - ->get(); - - return view('pages.routing.create', compact('feeds', 'channels')); - } - - public function store(Request $request): RedirectResponse - { - $validated = $request->validate([ - 'feed_id' => 'required|exists:feeds,id', - 'channel_ids' => 'required|array|min:1', - 'channel_ids.*' => 'exists:platform_channels,id', - 'priority' => 'integer|min:0|max:100', - 'filters' => 'nullable|string' - ]); - - /** @var Feed $feed */ - $feed = Feed::findOrFail($validated['feed_id']); - - /** @var Collection $channels */ - $channels = PlatformChannel::findMany($validated['channel_ids']); - $priority = $validated['priority'] ?? 0; - - try { - app(RoutingValidationService::class)->validateLanguageCompatibility($feed, $channels); - } catch (RoutingMismatchException $e) { - return redirect()->back() - ->withInput() - ->withErrors(['language' => $e->getMessage()]); - } - - $filters = $this->parseJsonFilters($validated['filters'] ?? null); - - // Attach channels to feed - $syncData = []; - foreach ($validated['channel_ids'] as $channelId) { - $syncData[$channelId] = [ - 'is_active' => true, - 'priority' => $priority, - 'filters' => $filters, - 'created_at' => now(), - 'updated_at' => now() - ]; - } - - $feed->channels()->syncWithoutDetaching($syncData); - - return redirect()->route('routing.index') - ->with('success', 'Feed routing created successfully!'); - } - - public function edit(Feed $feed, PlatformChannel $channel): View - { - $routing = $feed->channels() - ->wherePivot('platform_channel_id', $channel->id) - ->first(); - - if (! $routing) { - abort(404, 'Routing not found'); - } - - return view('pages.routing.edit', compact('feed', 'channel', 'routing')); - } - - public function update(Request $request, Feed $feed, PlatformChannel $channel): RedirectResponse - { - $validated = $request->validate([ - 'is_active' => 'boolean', - 'priority' => 'integer|min:0|max:100', - 'filters' => 'nullable|string' - ]); - - $filters = $this->parseJsonFilters($validated['filters'] ?? null); - - $feed->channels()->updateExistingPivot($channel->id, [ - 'is_active' => $validated['is_active'] ?? true, - 'priority' => $validated['priority'] ?? 0, - 'filters' => $filters, - 'updated_at' => now() - ]); - - return redirect()->route('routing.index') - ->with('success', 'Routing updated successfully!'); - } - - public function destroy(Feed $feed, PlatformChannel $channel): RedirectResponse - { - $feed->channels()->detach($channel->id); - - return redirect()->route('routing.index') - ->with('success', 'Routing deleted successfully!'); - } - - public function toggle(Request $request, Feed $feed, PlatformChannel $channel): RedirectResponse - { - $routing = Route::where('feed_id', $feed->id) - ->where('platform_channel_id', $channel->id) - ->first(); - - if (! $routing) { - abort(404, 'Routing not found'); - } - - $newStatus = ! $routing->is_active; - - $feed->channels()->updateExistingPivot($channel->id, [ - 'is_active' => $newStatus, - 'updated_at' => now() - ]); - - $status = $newStatus ? 'activated' : 'deactivated'; - - return redirect()->route('routing.index') - ->with('success', "Routing {$status} successfully!"); - } - - /** - * @return array|null - */ - private function parseJsonFilters(?string $json): ?array - { - if (empty($json)) { - return null; - } - - $decoded = json_decode($json, true); - - if (json_last_error() === JSON_ERROR_NONE) { - return $decoded; - } - - return null; - } -} diff --git a/backend/app/Http/Controllers/SettingsController.php b/backend/app/Http/Controllers/SettingsController.php deleted file mode 100644 index c8aae7d..0000000 --- a/backend/app/Http/Controllers/SettingsController.php +++ /dev/null @@ -1,39 +0,0 @@ -validate([ - 'article_processing_enabled' => 'boolean', - 'enable_publishing_approvals' => 'boolean', - ]); - - Setting::setArticleProcessingEnabled($request->boolean('article_processing_enabled')); - Setting::setPublishingApprovalsEnabled($request->boolean('enable_publishing_approvals')); - - // If redirected from onboarding, go to dashboard - if ($request->get('from') === 'onboarding') { - return redirect()->route('onboarding.index') - ->with('success', 'System activated successfully! Welcome to Lemmy Poster.'); - } - - return redirect()->route('settings.index') - ->with('success', 'Settings updated successfully.'); - } -} diff --git a/backend/app/Models/Keyword.php b/backend/app/Models/Keyword.php index 91d22b0..616d184 100644 --- a/backend/app/Models/Keyword.php +++ b/backend/app/Models/Keyword.php @@ -2,6 +2,7 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Carbon; @@ -19,6 +20,8 @@ */ class Keyword extends Model { + use HasFactory; + protected $fillable = [ 'feed_id', 'platform_channel_id', diff --git a/backend/database/factories/KeywordFactory.php b/backend/database/factories/KeywordFactory.php index e5cd7b4..0203c97 100644 --- a/backend/database/factories/KeywordFactory.php +++ b/backend/database/factories/KeywordFactory.php @@ -12,24 +12,40 @@ class KeywordFactory extends Factory public function definition(): array { return [ - 'keyword' => $this->faker->word(), - 'is_blocked' => $this->faker->boolean(30), // 30% chance of being blocked + 'feed_id' => null, + 'platform_channel_id' => null, + 'keyword' => 'test keyword', + 'is_active' => $this->faker->boolean(70), // 70% chance of being active 'created_at' => $this->faker->dateTimeBetween('-1 year', 'now'), 'updated_at' => now(), ]; } - public function blocked(): static + public function forFeed(\App\Models\Feed $feed): static { return $this->state(fn (array $attributes) => [ - 'is_blocked' => true, + 'feed_id' => $feed->id, ]); } - public function allowed(): static + public function forChannel(\App\Models\PlatformChannel $channel): static { return $this->state(fn (array $attributes) => [ - 'is_blocked' => false, + 'platform_channel_id' => $channel->id, + ]); + } + + public function active(): static + { + return $this->state(fn (array $attributes) => [ + 'is_active' => true, + ]); + } + + public function inactive(): static + { + return $this->state(fn (array $attributes) => [ + 'is_active' => false, ]); } } \ No newline at end of file diff --git a/backend/routes/web.php b/backend/routes/web.php index b18ec28..935af86 100644 --- a/backend/routes/web.php +++ b/backend/routes/web.php @@ -1,45 +1,26 @@ where('path', '.*')->name('spa'); - -// Legacy routes (can be removed once fully migrated to React) /* -// Onboarding routes -Route::get('/', [OnboardingController::class, 'index'])->name('onboarding.index'); -Route::get('/onboarding/platform', [OnboardingController::class, 'platform'])->name('onboarding.platform'); -Route::get('/onboarding/feed', [OnboardingController::class, 'feed'])->name('onboarding.feed'); -Route::get('/onboarding/channel', [OnboardingController::class, 'channel'])->name('onboarding.channel'); -Route::get('/onboarding/complete', [OnboardingController::class, 'complete'])->name('onboarding.complete'); - -Route::get('/articles', ArticlesController::class)->name('articles'); -Route::post('/articles/{article}/approve', [ArticlesController::class, 'approve'])->name('articles.approve'); -Route::post('/articles/{article}/reject', [ArticlesController::class, 'reject'])->name('articles.reject'); -Route::get('/logs', LogsController::class)->name('logs'); -Route::get('/settings', [SettingsController::class, 'index'])->name('settings.index'); -Route::put('/settings', [SettingsController::class, 'update'])->name('settings.update'); - -Route::resource('platforms', App\Http\Controllers\PlatformAccountsController::class)->names('platforms'); -Route::post('/platforms/{platformAccount}/set-active', [App\Http\Controllers\PlatformAccountsController::class, 'setActive'])->name('platforms.set-active'); - -Route::resource('channels', App\Http\Controllers\PlatformChannelsController::class)->names('channels'); -Route::post('/channels/{channel}/toggle', [App\Http\Controllers\PlatformChannelsController::class, 'toggle'])->name('channels.toggle'); -Route::resource('feeds', App\Http\Controllers\FeedsController::class)->names('feeds'); -Route::post('/feeds/{feed}/toggle', [App\Http\Controllers\FeedsController::class, 'toggle'])->name('feeds.toggle'); - -Route::get('/routing', [App\Http\Controllers\RoutingController::class, 'index'])->name('routing.index'); -Route::get('/routing/create', [App\Http\Controllers\RoutingController::class, 'create'])->name('routing.create'); -Route::post('/routing', [App\Http\Controllers\RoutingController::class, 'store'])->name('routing.store'); -Route::get('/routing/{feed}/{channel}/edit', [App\Http\Controllers\RoutingController::class, 'edit'])->name('routing.edit'); -Route::put('/routing/{feed}/{channel}', [App\Http\Controllers\RoutingController::class, 'update'])->name('routing.update'); -Route::delete('/routing/{feed}/{channel}', [App\Http\Controllers\RoutingController::class, 'destroy'])->name('routing.destroy'); -Route::post('/routing/{feed}/{channel}/toggle', [App\Http\Controllers\RoutingController::class, 'toggle'])->name('routing.toggle'); +|-------------------------------------------------------------------------- +| Web Routes +|-------------------------------------------------------------------------- +| +| Note: This Laravel backend now serves as an API for a separate React frontend. +| All UI routes are handled by the React application. +| */ + +// Simple API health check route +Route::get('/health', function () { + return response()->json(['status' => 'ok', 'service' => 'FFR API Backend']); +}); + +// For any unmatched routes, return a JSON response indicating this is an API backend +Route::fallback(function () { + return response()->json([ + 'message' => 'This is the FFR API backend. Use /api/v1/* endpoints or check the React frontend.', + 'api_base' => '/api/v1' + ], 404); +}); diff --git a/backend/tests/Feature/ApiEndpointRegressionTest.php b/backend/tests/Feature/ApiEndpointRegressionTest.php deleted file mode 100644 index 2b92190..0000000 --- a/backend/tests/Feature/ApiEndpointRegressionTest.php +++ /dev/null @@ -1,348 +0,0 @@ -get('/'); - $response->assertSuccessful(); - } - - public function test_onboarding_platform_page_loads(): void - { - $response = $this->get('/onboarding/platform'); - $response->assertSuccessful(); - } - - public function test_onboarding_feed_page_loads(): void - { - $response = $this->get('/onboarding/feed'); - - // Accept both successful response and redirects for onboarding flow - $this->assertTrue( - $response->isSuccessful() || $response->isRedirect(), - "Expected successful response or redirect, got status: " . $response->getStatusCode() - ); - } - - public function test_onboarding_channel_page_loads(): void - { - $response = $this->get('/onboarding/channel'); - - // Accept both successful response and redirects for onboarding flow - $this->assertTrue( - $response->isSuccessful() || $response->isRedirect(), - "Expected successful response or redirect, got status: " . $response->getStatusCode() - ); - } - - public function test_onboarding_complete_page_loads(): void - { - $response = $this->get('/onboarding/complete'); - $response->assertSuccessful(); - } - - public function test_articles_page_loads_successfully(): void - { - $response = $this->get('/articles'); - $response->assertSuccessful(); - } - - public function test_articles_approve_endpoint_works(): void - { - Event::fake(); // Disable events to prevent side effects - - $feed = Feed::factory()->create(); - $article = Article::factory()->create([ - 'feed_id' => $feed->id, - 'approval_status' => 'pending' - ]); - - // Ensure article exists in database - $this->assertDatabaseHas('articles', [ - 'id' => $article->id, - 'approval_status' => 'pending' - ]); - - $response = $this->withoutMiddleware() - ->post("/articles/{$article->id}/approve"); - - $response->assertRedirect(); - - // Check database directly - $this->assertDatabaseHas('articles', [ - 'id' => $article->id, - 'approval_status' => 'approved' - ]); - - $article->refresh(); - $this->assertEquals('approved', $article->approval_status, 'Article status should be approved after calling approve endpoint'); - } - - public function test_articles_reject_endpoint_works(): void - { - $feed = Feed::factory()->create(); - $article = Article::factory()->create([ - 'feed_id' => $feed->id, - 'approval_status' => 'pending' - ]); - - $response = $this->withoutMiddleware() - ->post("/articles/{$article->id}/reject"); - $response->assertRedirect(); - - $article->refresh(); - $this->assertEquals('rejected', $article->approval_status); - } - - public function test_logs_page_loads_successfully(): void - { - $response = $this->get('/logs'); - $response->assertSuccessful(); - } - - public function test_settings_page_loads_successfully(): void - { - $response = $this->get('/settings'); - $response->assertSuccessful(); - } - - public function test_settings_update_endpoint_works(): void - { - Setting::factory()->create([ - 'key' => 'test_setting', - 'value' => 'old_value' - ]); - - $response = $this->withoutMiddleware() - ->put('/settings', [ - 'test_setting' => 'new_value' - ]); - - $response->assertRedirect(); - $this->assertEquals('new_value', Setting::where('key', 'test_setting')->first()->value); - } - - public function test_platforms_index_loads_successfully(): void - { - $response = $this->get('/platforms'); - $response->assertSuccessful(); - } - - public function test_platforms_create_loads_successfully(): void - { - $response = $this->get('/platforms/create'); - $response->assertSuccessful(); - } - - public function test_platforms_show_loads_successfully(): void - { - $platform = PlatformAccount::factory()->create(); - - $response = $this->get("/platforms/{$platform->id}"); - $response->assertSuccessful(); - } - - public function test_platforms_edit_loads_successfully(): void - { - $platform = PlatformAccount::factory()->create(); - - $response = $this->get("/platforms/{$platform->id}/edit"); - $response->assertSuccessful(); - } - - public function test_platforms_set_active_endpoint_works(): void - { - $platform = PlatformAccount::factory()->create(['is_active' => false]); - - $response = $this->withoutMiddleware() - ->post("/platforms/{$platform->id}/set-active"); - $response->assertRedirect(); - - $platform->refresh(); - $this->assertTrue($platform->is_active); - } - - public function test_channels_index_loads_successfully(): void - { - $response = $this->get('/channels'); - $response->assertSuccessful(); - } - - public function test_channels_create_loads_successfully(): void - { - $response = $this->get('/channels/create'); - $response->assertSuccessful(); - } - - public function test_channels_show_loads_successfully(): void - { - $channel = PlatformChannel::factory()->create(); - - $response = $this->get("/channels/{$channel->id}"); - $response->assertSuccessful(); - } - - public function test_channels_edit_loads_successfully(): void - { - $channel = PlatformChannel::factory()->create(); - - $response = $this->get("/channels/{$channel->id}/edit"); - $response->assertSuccessful(); - } - - public function test_channels_toggle_endpoint_works(): void - { - $channel = PlatformChannel::factory()->create(['is_active' => false]); - - $response = $this->withoutMiddleware() - ->post("/channels/{$channel->id}/toggle"); - $response->assertRedirect(); - - $channel->refresh(); - $this->assertTrue($channel->is_active); - } - - public function test_feeds_index_loads_successfully(): void - { - $response = $this->get('/feeds'); - $response->assertSuccessful(); - } - - public function test_feeds_create_loads_successfully(): void - { - $response = $this->get('/feeds/create'); - $response->assertSuccessful(); - } - - public function test_feeds_show_loads_successfully(): void - { - $feed = Feed::factory()->create(); - - $response = $this->get("/feeds/{$feed->id}"); - $response->assertSuccessful(); - } - - public function test_feeds_edit_loads_successfully(): void - { - $feed = Feed::factory()->create(); - - $response = $this->get("/feeds/{$feed->id}/edit"); - $response->assertSuccessful(); - } - - public function test_feeds_toggle_endpoint_works(): void - { - $feed = Feed::factory()->create(['is_active' => false]); - - $response = $this->withoutMiddleware() - ->post("/feeds/{$feed->id}/toggle"); - $response->assertRedirect(); - - $feed->refresh(); - $this->assertTrue($feed->is_active); - } - - public function test_routing_index_loads_successfully(): void - { - $response = $this->get('/routing'); - $response->assertSuccessful(); - } - - public function test_routing_create_loads_successfully(): void - { - $response = $this->get('/routing/create'); - $response->assertSuccessful(); - } - - public function test_routing_edit_loads_successfully(): void - { - $feed = Feed::factory()->create(); - $channel = PlatformChannel::factory()->create(); - Route::factory()->create([ - 'feed_id' => $feed->id, - 'platform_channel_id' => $channel->id - ]); - - $response = $this->get("/routing/{$feed->id}/{$channel->id}/edit"); - $response->assertSuccessful(); - } - - public function test_routing_toggle_endpoint_works(): void - { - $feed = Feed::factory()->create(); - $channel = PlatformChannel::factory()->create(); - $route = Route::factory()->create([ - 'feed_id' => $feed->id, - 'platform_channel_id' => $channel->id, - 'is_active' => false - ]); - - $response = $this->withoutMiddleware() - ->post("/routing/{$feed->id}/{$channel->id}/toggle"); - $response->assertRedirect(); - - $route->refresh(); - $this->assertTrue($route->is_active); - } - - public function test_all_get_routes_return_successful_status(): void - { - // Create necessary test data - $feed = Feed::factory()->create(); - $channel = PlatformChannel::factory()->create(); - $platform = PlatformAccount::factory()->create(); - Route::factory()->create([ - 'feed_id' => $feed->id, - 'platform_channel_id' => $channel->id - ]); - - $routes = [ - '/', - '/onboarding/platform', - '/onboarding/feed', - '/onboarding/channel', - '/onboarding/complete', - '/articles', - '/logs', - '/settings', - '/platforms', - '/platforms/create', - "/platforms/{$platform->id}", - "/platforms/{$platform->id}/edit", - '/channels', - '/channels/create', - "/channels/{$channel->id}", - "/channels/{$channel->id}/edit", - '/feeds', - '/feeds/create', - "/feeds/{$feed->id}", - "/feeds/{$feed->id}/edit", - '/routing', - '/routing/create', - "/routing/{$feed->id}/{$channel->id}/edit", - ]; - - foreach ($routes as $route) { - $response = $this->get($route); - $this->assertTrue( - $response->isSuccessful(), - "Route {$route} failed with status {$response->getStatusCode()}" - ); - } - } -} \ No newline at end of file diff --git a/backend/tests/Feature/DatabaseIntegrationTest.php b/backend/tests/Feature/DatabaseIntegrationTest.php index 4dd593a..9af72e9 100644 --- a/backend/tests/Feature/DatabaseIntegrationTest.php +++ b/backend/tests/Feature/DatabaseIntegrationTest.php @@ -218,14 +218,22 @@ public function test_platform_channel_post_model_creates_successfully(): void public function test_keyword_model_creates_successfully(): void { - $keyword = Keyword::factory()->create([ - 'keyword' => 'test keyword', - 'is_blocked' => false - ]); + $feed = Feed::factory()->create(); + $channel = PlatformChannel::factory()->create(); + + $keyword = Keyword::factory() + ->forFeed($feed) + ->forChannel($channel) + ->create([ + 'keyword' => 'test keyword', + 'is_active' => true + ]); $this->assertDatabaseHas('keywords', [ 'keyword' => 'test keyword', - 'is_blocked' => false + 'is_active' => true, + 'feed_id' => $feed->id, + 'platform_channel_id' => $channel->id ]); } diff --git a/backend/tests/Feature/JobsAndEventsTest.php b/backend/tests/Feature/JobsAndEventsTest.php index 25db5c1..8254fee 100644 --- a/backend/tests/Feature/JobsAndEventsTest.php +++ b/backend/tests/Feature/JobsAndEventsTest.php @@ -53,7 +53,7 @@ public function test_article_discovery_for_feed_job_processes_feed(): void $job = new ArticleDiscoveryForFeedJob($feed); // Mock the ArticleFetcher to return created articles - $mockFetcher = \Mockery::mock('alias:' . \App\Services\Article\ArticleFetcher::class); + $mockFetcher = \Mockery::mock('overload:' . \App\Services\Article\ArticleFetcher::class); $article1 = Article::factory()->create(['url' => 'https://example.com/article1', 'feed_id' => $feed->id]); $article2 = Article::factory()->create(['url' => 'https://example.com/article2', 'feed_id' => $feed->id]); $mockFetcher->shouldReceive('getArticlesFromFeed') @@ -210,7 +210,8 @@ public function test_validate_article_listener_processes_new_article(): void ]); // Mock ArticleFetcher to return valid article data - $mockFetcher = \Mockery::mock('alias:' . \App\Services\Article\ArticleFetcher::class); + $mockFetcher = \Mockery::mock('alias:ArticleFetcher2'); + $this->app->instance(\App\Services\Article\ArticleFetcher::class, $mockFetcher); $mockFetcher->shouldReceive('fetchArticleData') ->with($article) ->andReturn([