CluPilotCloud/tests/Feature/Readiness
nexxo 786318b6d4 Never tell anyone to delete a catalogue their contracts bill on
The refusal added in the previous commit had a state it read wrongly, and
the wrong reading was destructive.

record() sat at the END of handle() and in no try/finally, while
createProduct() and ensure() throw uncaught. A run that died after the
first object left hasStoredObjects() true and recorded() null. The same
state arises with no failure at all: CheckoutController → PlanPrices::
ensure() and BookAddon → SyncStripeAddonItems → AddonPrices::ensure()
mint missing prices and never call record().

In that state the next run — in the SAME mode — refused with the foreign
account message and its "Clear plan_families.stripe_product_id,
plan_prices.stripe_price_id and the … registers first", and
billing.catalogue_synced blocked with the same text. The objects were
from the account in force. The right move was to resume, which is what
the idempotency keys exist for; instead an operator was handed a delete
instruction for a catalogue live contracts are billed on.

Two changes:

  1. record() moves ahead of the creation loop, right behind the refusal.
     There it is already proved that either nothing is stored or what is
     stored belongs to the active account, so the moment carries the
     claim just as well — and a run that dies part-way can no longer
     leave a state that contradicts itself.
  2. "Origin never recorded" gets its own sentence and its own cure,
     separate from "established: other account".
     StripeCatalogueMode::matchesActiveMode() becomes
     belongsToAnotherMode(), which is only true where the other account
     is fact. The check still blocks — the origin cannot be proved — but
     it says "run the sync again", and it names no register to empty.

Red first:

  ⨯ it takes up a catalogue whose origin was never written down
      Failed asserting that 1 matches expected 0.
  ⨯ it leaves no half-built catalogue that contradicts itself when a run dies
      Failed asserting that null is identical to an object of class "App\Support\OperatingMode".
  ⨯ it tells an unrecorded origin apart from a foreign account

The two states are held apart by assertion, not by wording: only the
foreign-account sentence may name stripe_plan_prices, and the unrecorded
one must name stripe:sync-catalogue instead. The existing foreign-account
test keeps its teeth.

Full suite: 1817 passed, 6366 assertions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 17:30:56 +02:00
..
ActiveChecksTest.php Fix round: CGNAT, guarded write/delete, and a silent host is not a missing template 2026-07-30 14:19:44 +02:00
BillingChecksTest.php Never tell anyone to delete a catalogue their contracts bill on 2026-07-30 17:30:56 +02:00
DeliveryChecksTest.php Put every missing field on one page before a customer pays 2026-07-30 15:59:34 +02:00
HeartbeatTest.php Fix round: guard the parse, bound the future, name the real tab 2026-07-30 14:49:06 +02:00
OnboardingChecksTest.php Put every missing field on one page before a customer pays 2026-07-30 15:59:34 +02:00
ProvisioningChecksTest.php Fix round: read the mailer against NON_DELIVERING, not against 'log' alone 2026-07-30 13:27:14 +02:00