test(stats): assert full command output and keep rates as float
Two funnel tests asserted several substrings that share one output line; expectsOutputToContain consumes one Mockery doWrite expectation per line, so only the first matched. Switch those to Artisan::call + expect()->toContain against the whole output. Also cast the rate numerators to float so an evenly divisible ratio (e.g. 2/2) returns 1.0 rather than int 1, matching the ?float contract.
This commit is contained in:
parent
c00069f208
commit
f7a83788ea
|
|
@ -217,13 +217,13 @@ class ExperimentFunnelCollector
|
|||
|
||||
foreach ($variants as $key => $row) {
|
||||
$variants[$key]['conversionRate'] = $row['assignedMature'] > 0
|
||||
? $row['convertedMature'] / $row['assignedMature']
|
||||
? (float) $row['convertedMature'] / $row['assignedMature']
|
||||
: null;
|
||||
$variants[$key]['netActiveRate'] = $row['assignedMature'] > 0
|
||||
? $row['activeMature'] / $row['assignedMature']
|
||||
? (float) $row['activeMature'] / $row['assignedMature']
|
||||
: null;
|
||||
$variants[$key]['activationToPaidRate'] = $row['activatedMature'] > 0
|
||||
? $row['activeMature'] / $row['activatedMature']
|
||||
? (float) $row['activeMature'] / $row['activatedMature']
|
||||
: null;
|
||||
$variants[$key]['arpuCents'] = $row['assignedMature'] > 0
|
||||
? (int) round($row['mrrCents'] / $row['assignedMature'])
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use App\Models\User;
|
|||
use App\Services\Stats\ExperimentFunnelCollector;
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
|
@ -239,14 +240,16 @@ it('reports a matured-cohort conversion capped at 100% and prints the matured de
|
|||
expect($control['assignedMature'])->toBe(2)
|
||||
->and($control['activatedMature'])->toBe(1)
|
||||
->and($control['activeMature'])->toBe(2)
|
||||
->and($control['netActiveRate'])->toBe(1.0); // Conv% = Paid ÷ MatU, always ≤ 100%
|
||||
->and($control['convertedMature'])->toBe(2)
|
||||
->and($control['conversionRate'])->toBe(1.0); // Conv% = Conv ÷ MatU, always ≤ 100%
|
||||
|
||||
artisan('stats:experiment-funnel', ['--no-discord' => true])
|
||||
->expectsOutputToContain('Conv%')
|
||||
->expectsOutputToContain('MatU')
|
||||
->expectsOutputToContain('ARPU')
|
||||
->expectsOutputToContain('100%')
|
||||
->assertSuccessful();
|
||||
Artisan::call('stats:experiment-funnel', ['--no-discord' => true]);
|
||||
$output = Artisan::output();
|
||||
|
||||
expect($output)->toContain('MatU') // matured denominator column is printed
|
||||
->toContain('Conv%')
|
||||
->toContain('100%') // capped, not the old 200% A2P%
|
||||
->not->toContain('200%');
|
||||
});
|
||||
|
||||
it('reports a Wilson confidence interval and defers the verdict while samples are small', function () {
|
||||
|
|
@ -257,11 +260,12 @@ it('reports a Wilson confidence interval and defers the verdict while samples ar
|
|||
experimentUser(SubscriptionExperiment::REDUCED_TRIAL, $signup, ['status' => 'active', 'at' => $signup->addDay()]);
|
||||
experimentUser(SubscriptionExperiment::REDUCED_TRIAL, $signup);
|
||||
|
||||
artisan('stats:experiment-funnel', ['--no-discord' => true])
|
||||
->expectsOutputToContain('Significance')
|
||||
->expectsOutputToContain('Wilson')
|
||||
->expectsOutputToContain('too small') // n = 2 per arm → verdict deferred
|
||||
->assertSuccessful();
|
||||
Artisan::call('stats:experiment-funnel', ['--no-discord' => true]);
|
||||
$output = Artisan::output();
|
||||
|
||||
expect($output)->toContain('Significance')
|
||||
->toContain('Wilson')
|
||||
->toContain('too small'); // n = 2 per arm → verdict deferred
|
||||
});
|
||||
|
||||
it('measures conversion as ever-charged, not active-now, so churn does not bias it', function () {
|
||||
|
|
|
|||
Loading…
Reference in New Issue