From 1db2871398bd86deb0a3c48ba1f1881822e016cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Falc=C3=B3n?= Date: Mon, 29 Jun 2026 12:31:29 +0200 Subject: [PATCH] feat(stats): add --no-discord flag to stats:experiment-funnel (#606) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What Adds a `--no-discord` flag to `stats:experiment-funnel` so the per-variant report can be run **on demand (e.g. in production) and only printed to the console**, without posting to the Discord channel. ```bash php artisan stats:experiment-funnel --no-discord ``` ## Why Handy for ad-hoc checks of the live experiment without spamming the ops/Stripe Discord channel. The scheduled weekly run (and any run without the flag) still posts to Discord exactly as before — default behaviour is unchanged. ## Notes - The console table already prints before the Discord step; the flag just short-circuits the `->send()` and prints `Skipped Discord (--no-discord).` - Test added: with `--no-discord`, the command succeeds, prints the table, and `Http::assertNothingSent()`. - Pint + Larastan + the command's Pest suite green. > Tip: the command queries the default DB connection, so to see **production** numbers run it inside the prod container (e.g. via the Coolify terminal). --- .../SendExperimentFunnelReportCommand.php | 8 +++++++- .../SendExperimentFunnelReportCommandTest.php | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/app/Console/Commands/SendExperimentFunnelReportCommand.php b/app/Console/Commands/SendExperimentFunnelReportCommand.php index 92e60e7a..18bfa2c2 100644 --- a/app/Console/Commands/SendExperimentFunnelReportCommand.php +++ b/app/Console/Commands/SendExperimentFunnelReportCommand.php @@ -10,7 +10,7 @@ use Illuminate\Console\Command; class SendExperimentFunnelReportCommand extends Command { - protected $signature = 'stats:experiment-funnel'; + protected $signature = 'stats:experiment-funnel {--no-discord : Print the report to the console only, without posting to Discord}'; protected $description = 'Post the trial/pricing experiment funnel (per variant) to Discord'; @@ -39,6 +39,12 @@ class SendExperimentFunnelReportCommand extends Command $this->line($line); } + if ($this->option('no-discord')) { + $this->info('Skipped Discord (--no-discord).'); + + return self::SUCCESS; + } + $webhookUrl = config('services.discord.ai_cohort_webhook_url') ?: config('services.discord.webhook_url'); diff --git a/tests/Feature/SendExperimentFunnelReportCommandTest.php b/tests/Feature/SendExperimentFunnelReportCommandTest.php index 4cc30c24..4654740a 100644 --- a/tests/Feature/SendExperimentFunnelReportCommandTest.php +++ b/tests/Feature/SendExperimentFunnelReportCommandTest.php @@ -168,3 +168,19 @@ it('does not post when the experiment has not started', function () { Http::assertNothingSent(); }); + +it('prints the report without posting to discord when --no-discord is set', function () { + config(['services.discord.ai_cohort_webhook_url' => 'https://discord.test/hook']); + Http::fake(['discord.test/*' => Http::response('', 204)]); + + experimentUser(SubscriptionExperiment::CONTROL, CarbonImmutable::parse('2026-06-05'), [ + 'status' => 'active', + 'at' => CarbonImmutable::parse('2026-06-05'), + ]); + + artisan('stats:experiment-funnel', ['--no-discord' => true]) + ->expectsOutputToContain('control') + ->assertSuccessful(); + + Http::assertNothingSent(); +});