fix: #3621652 Cover the incident, attachment and delegation rows a tenant deletion refuses on

TenantUsage counts nine scoped entity types and is the single reader behind both surfaces that act on the answer: the guard on Tenant::preDelete(), which refuses the deletion, and the list TenantDeleteForm renders to say what is in the way. Three of the nine reached neither test.

An incident is declared by orchestra itself, so the fixtures in TenantFormTest and TenantDeletionTest could carry one and now do. An attachment and a delegation arrive with a submodule, so counting them needs those installed, which the new TenantUsageTest does.

The list is a hardcoded constant, deliberately, since the types it covers name their tenant in two different places: a base field and an exported config property. What that costs is a type going missing from the constant without anything saying so, and a realm then deletable with those rows still scoped to it while the guard and the confirmation agree with each other and are both wrong. Dropping any of the three from the constant now fails.

The complementary branch, that a type whose module is absent is skipped rather than queried, is asserted in TenantFormTest, where neither submodule is installed and a storage that does not exist would throw rather than answer zero.

RefusedDeletionTrait replaces four copies of the refusal assertion. The workflow and tenant deletion classes each carried a private one, identical but for the class they loaded and a noun hardcoded into the failure message, and the status class spelled it out inline twice more. The noun comes off the entity type now, so a fourth guarded type needs no fifth copy. The default tenant's refusal stays hand-rolled: it is reached on a syncing entity, which is what that test is about.

Tests only. phpcs, phpstan, cspell, the translations check and the affected kernel and unit classes pass locally.

Merge request reports

Loading
Loading