From 922d7f766e851c0710e039dc908e76d60ff911dc Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 9 Sep 2026 20:12:48 +0930 Subject: [PATCH] Drop Instagram import job when profile is missing instead of crashing --- app/Jobs/ImportPipeline/ImportInstagram.php | 22 ++++++- .../ImportInstagramMissingProfileTest.php | 64 +++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 tests/Feature/ImportInstagramMissingProfileTest.php diff --git a/app/Jobs/ImportPipeline/ImportInstagram.php b/app/Jobs/ImportPipeline/ImportInstagram.php index 90e0b4581..853c6e936 100644 --- a/app/Jobs/ImportPipeline/ImportInstagram.php +++ b/app/Jobs/ImportPipeline/ImportInstagram.php @@ -51,9 +51,27 @@ class ImportInstagram implements ShouldQueue return; } - $job = ImportJob::findOrFail($this->import->id); - $profile = Profile::findOrFail($job->profile_id); + $job = ImportJob::find($this->import->id); + if (! $job) { + return; + } + + // The profile may have been soft-deleted (e.g. account deletion) after + // this job was queued. findOrFail would throw inside handle() and the + // job would retry until it lands in failed_jobs; drop it cleanly instead. + $profile = Profile::find($job->profile_id); + if (! $profile) { + $job->delete(); + + return; + } + $user = $profile->user; + if (! $user) { + $job->delete(); + + return; + } $json = $job->mediaJson(); $collection = array_reverse($json['photos']); $files = $job->files; diff --git a/tests/Feature/ImportInstagramMissingProfileTest.php b/tests/Feature/ImportInstagramMissingProfileTest.php new file mode 100644 index 000000000..1be8f1cd0 --- /dev/null +++ b/tests/Feature/ImportInstagramMissingProfileTest.php @@ -0,0 +1,64 @@ + true]); +}); + +it('drops the import job when the profile has been soft-deleted', function () { + $user = User::factory()->create(); + $user->refresh(); + + $job = new ImportJob; + $job->profile_id = $user->profile_id; + $job->service = 'instagram'; + $job->uuid = (string) \Illuminate\Support\Str::uuid(); + $job->stage = 0; + $job->save(); + + // Soft-delete the profile, as DeleteAccountPipeline does. + Profile::whereId($user->profile_id)->delete(); + + // Must not throw. + (new ImportInstagram($job))->handle(); + + // The orphaned job is cleaned up. + expect(ImportJob::find($job->id))->toBeNull(); +}); + +it('drops the import job when the job no longer exists', function () { + $user = User::factory()->create(); + $user->refresh(); + + $job = new ImportJob; + $job->profile_id = $user->profile_id; + $job->service = 'instagram'; + $job->uuid = (string) \Illuminate\Support\Str::uuid(); + $job->stage = 0; + $job->save(); + + $jobId = $job->id; + $job->delete(); + + // Must not throw even though the ImportJob row is gone. + (new ImportInstagram($job))->handle(); + + expect(ImportJob::find($jobId))->toBeNull(); +});