From 571acd0013812d525d9b67d6e94b79b9b01dbe2d Mon Sep 17 00:00:00 2001 From: nexxo Date: Thu, 30 Jul 2026 18:48:49 +0200 Subject: [PATCH] Check Stripe is configured on every path the sweep actually uses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dry-run guard was copied from stripe:sync-catalogue, where it is right: that command's --dry-run touches nothing, because everything it compares already lives in our own tables. This command has no such local-only mode — the report IS a live activePricesFor() call, dry-run or not — so `! $dryRun &&` made the guard fire only on the one path that would have worked anyway. A --dry-run against an unconfigured account fell straight into HttpStripeClient's own exception instead of the clean error and self::FAILURE. Co-Authored-By: Claude Opus 5 --- app/Console/Commands/SweepOrphanStripePrices.php | 8 +++++++- tests/Feature/Billing/SweepOrphanPricesTest.php | 13 +++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/app/Console/Commands/SweepOrphanStripePrices.php b/app/Console/Commands/SweepOrphanStripePrices.php index edc355f..cecd28f 100644 --- a/app/Console/Commands/SweepOrphanStripePrices.php +++ b/app/Console/Commands/SweepOrphanStripePrices.php @@ -36,7 +36,13 @@ class SweepOrphanStripePrices extends Command $dryRun = (bool) $this->option('dry-run'); $archive = (bool) $this->option('archive'); - if (! $dryRun && ! $stripe->isConfigured()) { + // Checked on EVERY path, unlike stripe:sync-catalogue's --dry-run, + // which touches nothing because everything it compares is already in + // our own tables. This command has no local-only mode: the report — + // the whole point of it, dry-run or not — is activePricesFor(), a + // live call. Skipping this check under --dry-run would not skip the + // call, only the graceful error in front of it. + if (! $stripe->isConfigured()) { $this->error('Stripe is not configured (STRIPE_SECRET is empty). Nothing was read.'); return self::FAILURE; diff --git a/tests/Feature/Billing/SweepOrphanPricesTest.php b/tests/Feature/Billing/SweepOrphanPricesTest.php index c33f453..1ab3771 100644 --- a/tests/Feature/Billing/SweepOrphanPricesTest.php +++ b/tests/Feature/Billing/SweepOrphanPricesTest.php @@ -64,3 +64,16 @@ it('touches nothing on a dry run, even when asked to archive', function () { expect($this->stripe->archived)->toBe([]); }); + +it('fails cleanly on a dry run when Stripe is not configured, rather than throwing', function () { + // Unlike stripe:sync-catalogue, this command has no local-only path: the + // report itself is a live activePricesFor() call, dry-run or not. A guard + // that only fired outside --dry-run would let this fall straight into + // HttpStripeClient's own "not configured" exception instead of the clean + // error below. + $this->stripe->configured = false; + + $this->artisan('stripe:sweep-orphan-prices', ['--dry-run' => true]) + ->expectsOutputToContain('Stripe is not configured (STRIPE_SECRET is empty). Nothing was read.') + ->assertFailed(); +});