Http::response('ok', 200), ]); $this->service = app(SmsBroadcastService::class); } public function test_resolve_recipients_for_all_excluding_ids(): void { $included = Coupon::factory()->count(2)->create(); $excluded = Coupon::factory()->create(); $recipients = $this->service->resolveRecipients( sendToAll: true, excludedIds: [$excluded->id], ); $this->assertCount(2, $recipients); $this->assertTrue($recipients->pluck('id')->contains($included->first()->id)); $this->assertFalse($recipients->pluck('id')->contains($excluded->id)); } public function test_resolve_recipients_for_selected_only(): void { $selected = Coupon::factory()->create(); Coupon::factory()->create(); $recipients = $this->service->resolveRecipients( sendToAll: false, selectedIds: [$selected->id], ); $this->assertCount(1, $recipients); $this->assertSame($selected->id, $recipients->first()->id); } public function test_start_campaign_queues_chain_and_creates_logs(): void { Bus::fake(); $admin = User::factory()->create(); $coupons = Coupon::factory()->count(2)->create(); $campaign = $this->service->startCampaign( admin: $admin, message: 'Test broadcast', sendToAll: false, selectedIds: $coupons->pluck('id')->all(), ); $this->assertSame(2, $campaign->recipient_count); $this->assertDatabaseCount(SmsSendLog::class, 2); $this->assertDatabaseHas(SmsSendLog::class, [ 'sms_campaign_id' => $campaign->id, 'status' => SmsSendStatus::Pending->value, ]); Bus::assertChained([ SendSmsToCouponJob::class, SendSmsToCouponJob::class, ]); } public function test_invalid_phone_is_skipped_without_queue_job(): void { Bus::fake(); $admin = User::factory()->create(); $invalid = Coupon::factory()->create(['phone' => '81234567']); $valid = Coupon::factory()->create(); $campaign = $this->service->startCampaign( admin: $admin, message: 'Test', sendToAll: false, selectedIds: [$invalid->id, $valid->id], ); $this->assertSame(2, $campaign->recipient_count); $this->assertSame(1, $campaign->skipped_count); $this->assertDatabaseHas(SmsSendLog::class, [ 'coupon_id' => $invalid->id, 'status' => SmsSendStatus::SkippedInvalid->value, ]); Bus::assertChained([ SendSmsToCouponJob::class, ]); } public function test_send_job_marks_success_and_completes_campaign(): void { Queue::fake(); $admin = User::factory()->create(); $coupon = Coupon::factory()->create(['phone' => '61929248']); $campaign = SmsCampaign::factory()->create([ 'message' => 'Queued hello', 'recipient_count' => 1, 'created_by' => $admin->id, ]); SmsSendLog::factory()->create([ 'sms_campaign_id' => $campaign->id, 'coupon_id' => $coupon->id, 'phone' => $coupon->phone, 'message' => $campaign->message, 'status' => SmsSendStatus::Pending, ]); $job = new SendSmsToCouponJob($campaign, $coupon); $job->handle(); Http::assertSentCount(1); $campaign->refresh(); $this->assertSame(1, $campaign->sent_count); $this->assertNotNull($campaign->completed_at); $this->assertDatabaseHas(SmsSendLog::class, [ 'sms_campaign_id' => $campaign->id, 'status' => SmsSendStatus::Sent->value, ]); } }