57 - Tighten foreign-scenario docblocks to the locked 400 decision

This commit is contained in:
myrmidex 2026-06-27 23:34:20 +02:00
parent cb4af693b1
commit 1e9abf884b

View file

@ -155,19 +155,16 @@ final class BucketOwnerApiTest extends WebTestCase
}
/**
* PINS CURRENT (PRE-#50-STORY-3) BEHAVIOUR — flagged for review, not a locked contract.
* Pins the LOCKED 400-not-404 decision for a bucket POSTed under a scenario IRI
* the caller does not own (ratified project-wide see PLATFORM.md "Per-user ownership").
*
* The ticket asks for 404 when POSTing a bucket under a scenario IRI the caller
* does not own. Empirically, today this is 400: API Platform's IRI denormalizer
* (`AbstractItemNormalizer::getResourceFromIri`) raises "Item not found" as soon as
* the `scenario` relation can't be resolved — Scenario's own ownership extension
* (#50 Story 2) already makes a foreign scenario IRI unresolvable to a non-owner,
* so this 400 already happens BEFORE any Bucket-specific extension code runs.
*
* This test asserts the OBSERVED 400, not the ticket's desired 404. If a 404 is
* required, it needs a normalizing guard (e.g. catching the IRI-resolution failure
* and re-throwing as NotFoundHttpException) that's an implementation decision,
* flagged to the user rather than assumed.
* API Platform's IRI denormalizer (`AbstractItemNormalizer::getResourceFromIri`)
* raises "Item not found" as soon as the `scenario` relation can't be resolved
* Scenario's own ownership extension (#50 Story 2) already makes a foreign scenario
* IRI unresolvable to a non-owner, so this 400 happens BEFORE any Bucket-specific
* extension code runs. Forcing 404 would require a normalizing guard that risks
* masking legitimate 400s; the security properties are already met (no write, no
* leak, no oracle), so 400 stays.
*/
public function testPostUnderAForeignScenarioCurrentlyReturnsBadRequestNotFound(): void
{
@ -351,15 +348,14 @@ final class BucketOwnerApiTest extends WebTestCase
}
/**
* Mirrors the class-level docblock on {@see testPostUnderAForeignScenarioCurrentlyReturnsBadRequestNotFound()}:
* this pins OBSERVED behaviour for an attempted cross-tenant re-parent via PATCH, not an assumed contract.
* Mirrors {@see testPostUnderAForeignScenarioCurrentlyReturnsBadRequestNotFound()}:
* the LOCKED 400-not-404 decision applies to an attempted cross-tenant re-parent via PATCH too.
*
* The caller PATCHes their OWN bucket but supplies a `scenario` IRI pointing at a scenario owned by
* `otherUser`. `ScenarioOwnerExtension` (#50 Story 2) already makes that IRI unresolvable to a non-owner,
* so API Platform's IRI denormalizer fails to resolve `scenario` while building the merge-patched object
* the same `AbstractItemNormalizer::getResourceFromIri` path that produces 400 on the analogous foreign-scenario
* POST. Empirically this also resolves to 400 here. Pinning it as a coherent rejection: the bucket is
* confirmed NOT re-parented below.
* POST. 400 is the ratified outcome (no write, no leak, no oracle); the bucket is confirmed NOT re-parented below.
*/
public function testPatchReparentingIntoForeignScenarioIsRejected(): void
{