diff --git a/tests/Functional/BucketOwnerApiTest.php b/tests/Functional/BucketOwnerApiTest.php index ea7d276..cea796e 100644 --- a/tests/Functional/BucketOwnerApiTest.php +++ b/tests/Functional/BucketOwnerApiTest.php @@ -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 {