create([ 'id' => 1, 'short_code' => 'en', 'name' => 'English', 'native_name' => 'English', 'is_active' => true, ]); } public function test_status_shows_needs_onboarding_when_no_components_exist() { $response = $this->getJson('/api/v1/onboarding/status'); $response->assertStatus(200) ->assertJson([ 'success' => true, 'data' => [ 'needs_onboarding' => true, 'current_step' => 'platform', 'has_platform_account' => false, 'has_feed' => false, 'has_channel' => false, 'has_route' => false, 'onboarding_skipped' => false, ], ]); } public function test_status_shows_feed_step_when_platform_account_exists() { PlatformAccount::factory()->create(['is_active' => true]); $response = $this->getJson('/api/v1/onboarding/status'); $response->assertStatus(200) ->assertJson([ 'success' => true, 'data' => [ 'needs_onboarding' => true, 'current_step' => 'feed', 'has_platform_account' => true, 'has_feed' => false, 'has_channel' => false, 'has_route' => false, ], ]); } public function test_status_shows_channel_step_when_platform_account_and_feed_exist() { PlatformAccount::factory()->create(['is_active' => true]); Feed::factory()->create(['is_active' => true]); $response = $this->getJson('/api/v1/onboarding/status'); $response->assertStatus(200) ->assertJson([ 'success' => true, 'data' => [ 'needs_onboarding' => true, 'current_step' => 'channel', 'has_platform_account' => true, 'has_feed' => true, 'has_channel' => false, 'has_route' => false, ], ]); } public function test_status_shows_route_step_when_platform_account_feed_and_channel_exist() { PlatformAccount::factory()->create(['is_active' => true]); Feed::factory()->create(['is_active' => true]); PlatformChannel::factory()->create(['is_active' => true]); $response = $this->getJson('/api/v1/onboarding/status'); $response->assertStatus(200) ->assertJson([ 'success' => true, 'data' => [ 'needs_onboarding' => true, 'current_step' => 'route', 'has_platform_account' => true, 'has_feed' => true, 'has_channel' => true, 'has_route' => false, ], ]); } public function test_status_shows_no_onboarding_needed_when_all_components_exist() { PlatformAccount::factory()->create(['is_active' => true]); Feed::factory()->create(['is_active' => true]); PlatformChannel::factory()->create(['is_active' => true]); Route::factory()->create(['is_active' => true]); $response = $this->getJson('/api/v1/onboarding/status'); $response->assertStatus(200) ->assertJson([ 'success' => true, 'data' => [ 'needs_onboarding' => false, 'current_step' => null, 'has_platform_account' => true, 'has_feed' => true, 'has_channel' => true, 'has_route' => true, ], ]); } public function test_status_shows_no_onboarding_needed_when_skipped() { // No components exist but onboarding is skipped Setting::create([ 'key' => 'onboarding_skipped', 'value' => 'true', ]); $response = $this->getJson('/api/v1/onboarding/status'); $response->assertStatus(200) ->assertJson([ 'success' => true, 'data' => [ 'needs_onboarding' => false, 'current_step' => null, 'has_platform_account' => false, 'has_feed' => false, 'has_channel' => false, 'has_route' => false, 'onboarding_skipped' => true, ], ]); } public function test_options_returns_languages_and_platform_instances() { PlatformInstance::factory()->create([ 'platform' => 'lemmy', 'url' => 'https://lemmy.world', 'name' => 'Lemmy World', 'is_active' => true, ]); $response = $this->getJson('/api/v1/onboarding/options'); $response->assertStatus(200) ->assertJsonStructure([ 'success', 'data' => [ 'languages' => [ '*' => ['id', 'short_code', 'name', 'native_name', 'is_active'] ], 'platform_instances' => [ '*' => ['id', 'platform', 'url', 'name', 'description', 'is_active'] ] ] ]); } public function test_create_feed_validates_required_fields() { $response = $this->postJson('/api/v1/onboarding/feed', []); $response->assertStatus(422) ->assertJsonValidationErrors(['name', 'url', 'type', 'language_id']); } public function test_create_feed_creates_feed_successfully() { $feedData = [ 'name' => 'Test Feed', 'url' => 'https://example.com/rss', 'type' => 'rss', 'language_id' => 1, 'description' => 'Test description', ]; $response = $this->postJson('/api/v1/onboarding/feed', $feedData); $response->assertStatus(200) ->assertJson([ 'success' => true, 'data' => [ 'name' => 'Test Feed', 'url' => 'https://example.com/rss', 'type' => 'rss', 'is_active' => true, ] ]); $this->assertDatabaseHas('feeds', [ 'name' => 'Test Feed', 'url' => 'https://example.com/rss', 'type' => 'rss', 'language_id' => 1, 'is_active' => true, ]); } public function test_create_channel_validates_required_fields() { $response = $this->postJson('/api/v1/onboarding/channel', []); $response->assertStatus(422) ->assertJsonValidationErrors(['name', 'platform_instance_id', 'language_id']); } public function test_create_channel_creates_channel_successfully() { $platformInstance = PlatformInstance::factory()->create(); $channelData = [ 'name' => 'test_community', 'platform_instance_id' => $platformInstance->id, 'language_id' => 1, 'description' => 'Test community description', ]; $response = $this->postJson('/api/v1/onboarding/channel', $channelData); $response->assertStatus(200) ->assertJson([ 'success' => true, 'data' => [ 'name' => 'test_community', 'display_name' => 'Test_community', 'channel_id' => 'test_community', 'is_active' => true, ] ]); $this->assertDatabaseHas('platform_channels', [ 'name' => 'test_community', 'channel_id' => 'test_community', 'platform_instance_id' => $platformInstance->id, 'language_id' => 1, 'is_active' => true, ]); } public function test_create_route_validates_required_fields() { $response = $this->postJson('/api/v1/onboarding/route', []); $response->assertStatus(422) ->assertJsonValidationErrors(['feed_id', 'platform_channel_id']); } public function test_create_route_creates_route_successfully() { $feed = Feed::factory()->create(); $platformChannel = PlatformChannel::factory()->create(); $routeData = [ 'feed_id' => $feed->id, 'platform_channel_id' => $platformChannel->id, 'priority' => 75, 'filters' => ['keyword' => 'test'], ]; $response = $this->postJson('/api/v1/onboarding/route', $routeData); $response->assertStatus(200) ->assertJson([ 'success' => true, 'data' => [ 'feed_id' => $feed->id, 'platform_channel_id' => $platformChannel->id, 'priority' => 75, 'is_active' => true, ] ]); $this->assertDatabaseHas('routes', [ 'feed_id' => $feed->id, 'platform_channel_id' => $platformChannel->id, 'priority' => 75, 'is_active' => true, ]); } public function test_complete_onboarding_returns_success() { $response = $this->postJson('/api/v1/onboarding/complete'); $response->assertStatus(200) ->assertJson([ 'success' => true, 'data' => ['completed' => true] ]); } public function test_skip_onboarding_creates_setting() { $response = $this->postJson('/api/v1/onboarding/skip'); $response->assertStatus(200) ->assertJson([ 'success' => true, 'data' => ['skipped' => true] ]); $this->assertDatabaseHas('settings', [ 'key' => 'onboarding_skipped', 'value' => 'true', ]); } public function test_skip_onboarding_updates_existing_setting() { // Create existing setting with false value Setting::create([ 'key' => 'onboarding_skipped', 'value' => 'false', ]); $response = $this->postJson('/api/v1/onboarding/skip'); $response->assertStatus(200); $this->assertDatabaseHas('settings', [ 'key' => 'onboarding_skipped', 'value' => 'true', ]); // Ensure only one setting exists $this->assertEquals(1, Setting::where('key', 'onboarding_skipped')->count()); } public function test_reset_skip_removes_setting() { // Create skipped setting Setting::create([ 'key' => 'onboarding_skipped', 'value' => 'true', ]); $response = $this->postJson('/api/v1/onboarding/reset-skip'); $response->assertStatus(200) ->assertJson([ 'success' => true, 'data' => ['reset' => true] ]); $this->assertDatabaseMissing('settings', [ 'key' => 'onboarding_skipped', ]); } public function test_reset_skip_works_when_no_setting_exists() { $response = $this->postJson('/api/v1/onboarding/reset-skip'); $response->assertStatus(200) ->assertJson([ 'success' => true, 'data' => ['reset' => true] ]); } public function test_create_platform_validates_instance_url_format() { $response = $this->postJson('/api/v1/onboarding/platform', [ 'instance_url' => 'invalid.domain.with.spaces and symbols!', 'username' => 'testuser', 'password' => 'password123', 'platform' => 'lemmy', ]); $response->assertStatus(422) ->assertJsonValidationErrors(['instance_url']); } public function test_create_platform_validates_required_fields() { $response = $this->postJson('/api/v1/onboarding/platform', []); $response->assertStatus(422) ->assertJsonValidationErrors(['instance_url', 'username', 'password', 'platform']); } public function test_onboarding_flow_integration() { // 1. Initial status - needs onboarding $response = $this->getJson('/api/v1/onboarding/status'); $response->assertJson(['data' => ['needs_onboarding' => true, 'current_step' => 'platform']]); // 2. Skip onboarding $response = $this->postJson('/api/v1/onboarding/skip'); $response->assertJson(['data' => ['skipped' => true]]); // 3. Status after skip - no longer needs onboarding $response = $this->getJson('/api/v1/onboarding/status'); $response->assertJson(['data' => ['needs_onboarding' => false, 'onboarding_skipped' => true]]); // 4. Reset skip $response = $this->postJson('/api/v1/onboarding/reset-skip'); $response->assertJson(['data' => ['reset' => true]]); // 5. Status after reset - needs onboarding again $response = $this->getJson('/api/v1/onboarding/status'); $response->assertJson(['data' => ['needs_onboarding' => true, 'onboarding_skipped' => false]]); } }