firstWhere('key', $key); } it('reports the stripe key of the active mode as missing', function () { OperatingMode::set(OperatingMode::Test); expect(checkFor('billing.stripe_secret')->satisfied)->toBeFalse(); expect(checkFor('billing.stripe_secret')->severity)->toBe(Check::SEVERITY_BLOCKING); }); it('reports it as satisfied once the key of that mode is stored', function () { OperatingMode::set(OperatingMode::Test); app(SecretVault::class)->put('stripe.secret', 'sk_test_x', Operator::factory()->create(), OperatingMode::Test); expect(checkFor('billing.stripe_secret')->satisfied)->toBeTrue(); }); it('does not accept a live key as proof while the test mode is active', function () { // Sonst meldete die Seite Bereitschaft für einen Modus, in dem die Kasse // sperrt — die Seite widerspräche der Sperre aus Task 5. OperatingMode::set(OperatingMode::Test); app(SecretVault::class)->put('stripe.secret', 'sk_live_x', Operator::factory()->create(), OperatingMode::Live); expect(checkFor('billing.stripe_secret')->satisfied)->toBeFalse(); }); it('reports incomplete company details without repeating the list', function () { Settings::forget('company.name'); expect(checkFor('billing.company_details')->satisfied)->toBeFalse(); }); it('names what breaks, not just what is missing', function () { expect(checkFor('billing.company_details')->breaks)->not->toBe(''); }); it('says the installation is not ready while something blocking is open', function () { expect(Readiness::isReady())->toBeFalse(); expect(Readiness::blocking())->not->toBeEmpty(); }); it('does not report the invoice series ready when the required series is switched off', function () { // IssueInvoice draws its series with `where('kind', ...)->where('active', true)` // (IssueInvoice.php:373 and :444) — a deactivated 'invoice' row is as // invisible to it as a missing one. Counting distinct kinds without the // same `active` filter would report readiness for an installation where // not a single invoice can draw a number. InvoiceSeries::query()->where('kind', 'invoice')->update(['active' => false]); expect(checkFor('billing.invoice_series')->satisfied)->toBeFalse(); }); it('does not raise a permanent alarm for a draft version stripe never touches', function () { // stripe:sync-catalogue only mirrors PUBLISHED versions (its own header // comment: "Only PUBLISHED versions are synced. A draft has promised // nothing..."). A draft is an entirely ordinary state — there is a // dedicated modal to create one — so a query that does not also filter on // `published_at` would keep this check permanently unsatisfied on any // installation that has ever drafted a next version: an alarm that is // always on and therefore never read. PlanPrice::query()->update(['stripe_price_id' => 'price_already_synced']); $family = PlanFamily::query()->create(['key' => 'draftonly', 'name' => 'Draft Only', 'tier' => 9]); $draft = $family->versions()->create([ 'version' => 1, 'quota_gb' => 10, 'traffic_gb' => 100, 'seats' => 1, 'ram_mb' => 1024, 'cores' => 1, 'disk_gb' => 20, 'performance' => 'standard', 'features' => [], 'available_from' => now(), // published_at left null: a draft, exactly what stripe:sync-catalogue skips. ]); $draft->prices()->create(['term' => 'monthly', 'amount_cents' => 1000, 'currency' => 'EUR']); expect(checkFor('billing.catalogue_synced')->satisfied)->toBeTrue(); });