diff --git a/app/Actions/Post/DuplicatePost.php b/app/Actions/Post/DuplicatePost.php index 1e40854e..3ce6dc1d 100644 --- a/app/Actions/Post/DuplicatePost.php +++ b/app/Actions/Post/DuplicatePost.php @@ -12,9 +12,13 @@ use Illuminate\Support\Facades\DB; /** - * Clones a Post (and its enabled platform rows + label associations) into a - * fresh Draft. The new post is owned by the actor and unscheduled — the user - * picks a new date in the editor. + * Clones a Post (and its still-connected platform rows + label associations) + * into a fresh Draft. The new post is owned by the actor and unscheduled — + * the user picks a new date in the editor. + * + * Only platform rows with a still-existing social account are copied + * (`whereHas('socialAccount')`), so orphan history rows left after disconnect + * never land on the new draft. */ class DuplicatePost { @@ -31,7 +35,11 @@ public static function execute(Post $original, User $user): Post 'published_at' => null, ]); - foreach ($original->postPlatforms as $platform) { + $platforms = $original->postPlatforms() + ->whereHas('socialAccount') + ->get(); + + foreach ($platforms as $platform) { $copy->postPlatforms()->create([ 'social_account_id' => $platform->social_account_id, 'platform' => $platform->platform, diff --git a/tests/Feature/Actions/Post/DuplicatePostTest.php b/tests/Feature/Actions/Post/DuplicatePostTest.php index 13e43daf..8254e116 100644 --- a/tests/Feature/Actions/Post/DuplicatePostTest.php +++ b/tests/Feature/Actions/Post/DuplicatePostTest.php @@ -5,8 +5,12 @@ use App\Actions\Post\DuplicatePost; use App\Enums\Post\CreatedVia; use App\Enums\Post\Status as PostStatus; +use App\Enums\PostPlatform\Status as PostPlatformStatus; +use App\Enums\SocialAccount\Platform; use App\Events\PostCreated; use App\Models\Post; +use App\Models\PostPlatform; +use App\Models\SocialAccount; use App\Models\User; use App\Models\Workspace; use Illuminate\Support\Facades\Event; @@ -49,3 +53,79 @@ fn (PostCreated $event) => $event->post->id === $copy->id, ); }); + +test('execute skips platform rows whose social account was removed', function () { + $user = User::factory()->create(); + $workspace = Workspace::factory()->create(['user_id' => $user->id]); + $liveAccount = SocialAccount::factory()->create([ + 'workspace_id' => $workspace->id, + 'platform' => Platform::X, + 'display_name' => 'Live Account', + 'username' => 'live_user', + ]); + + $original = Post::factory()->create([ + 'workspace_id' => $workspace->id, + 'user_id' => $user->id, + 'status' => PostStatus::Published, + ]); + + PostPlatform::factory()->x()->published()->create([ + 'post_id' => $original->id, + 'social_account_id' => $liveAccount->id, + 'platform_name' => 'Live Account', + 'platform_username' => 'live_user', + 'platform_avatar' => 'avatars/live.jpg', + 'enabled' => true, + ]); + + // History row left after disconnect: FK nullOnDelete + snapshot fields. + PostPlatform::factory()->tiktok()->published()->create([ + 'post_id' => $original->id, + 'social_account_id' => null, + 'platform_name' => 'Removed Account', + 'platform_username' => 'gone_user', + 'platform_avatar' => 'avatars/orphan.jpg', + 'enabled' => true, + ]); + + $copy = DuplicatePost::execute($original->load(['postPlatforms', 'labels']), $user); + + $copiedPlatforms = $copy->postPlatforms()->get(); + + expect($copiedPlatforms)->toHaveCount(1) + ->and($copiedPlatforms->first()->social_account_id)->toBe($liveAccount->id) + ->and($copiedPlatforms->first()->platform)->toBe(Platform::X) + ->and($copiedPlatforms->first()->platform_name)->toBe('Live Account') + ->and($copiedPlatforms->first()->status)->toBe(PostPlatformStatus::Pending) + ->and($copiedPlatforms->first()->enabled)->toBeTrue(); +}); + +test('execute copies only platforms that still have a social account relation', function () { + $user = User::factory()->create(); + $workspace = Workspace::factory()->create(['user_id' => $user->id]); + $account = SocialAccount::factory()->create(['workspace_id' => $workspace->id]); + + $original = Post::factory()->create([ + 'workspace_id' => $workspace->id, + 'user_id' => $user->id, + 'status' => PostStatus::Published, + ]); + + $platform = PostPlatform::factory()->published()->create([ + 'post_id' => $original->id, + 'social_account_id' => $account->id, + 'platform_name' => $account->display_name, + 'platform_username' => $account->username, + 'enabled' => true, + ]); + + $account->delete(); + $platform->refresh(); + + expect($platform->social_account_id)->toBeNull(); + + $copy = DuplicatePost::execute($original->fresh(['postPlatforms', 'labels']), $user); + + expect($copy->postPlatforms)->toHaveCount(0); +});