@@ -1729,6 +1729,50 @@ public function testOperatorUnboundedPowerOnUndefinedBase(): void
17291729 $ database ->deleteCollection ($ collectionId );
17301730 }
17311731
1732+ /**
1733+ * A bounded power (with a max) must still compute the result whenever it fits within the max —
1734+ * it only leaves the value unchanged when the result would exceed the max, or when the input is
1735+ * mathematically undefined. A base of 1 or less is NOT a reason to skip: 0.5^2 = 0.25 and
1736+ * (-4)^2 = 16 are perfectly valid and within their bounds. Verified via a fresh read.
1737+ */
1738+ public function testOperatorBoundedPowerComputesWithinMax (): void
1739+ {
1740+ $ database = static ::getDatabase ();
1741+
1742+ if (!$ database ->getAdapter ()->getSupportForOperators ()) {
1743+ $ this ->expectNotToPerformAssertions ();
1744+ return ;
1745+ }
1746+
1747+ $ collectionId = 'operator_bounded_power ' ;
1748+ $ database ->createCollection ($ collectionId );
1749+ $ database ->createAttribute ($ collectionId , 'value ' , Database::VAR_FLOAT , 0 , false , 0.0 );
1750+
1751+ // [id, starting value, operator, expected stored value].
1752+ $ cases = [
1753+ ['fraction ' , 0.5 , Operator::power (2 , 1 ), 0.25 ], // 0.5^2 = 0.25, within max 1 → applied
1754+ ['negeven ' , -4.0 , Operator::power (2 , 20 ), 16.0 ], // (-4)^2 = 16, within max 20 → applied
1755+ ['within ' , 2.0 , Operator::power (3 , 100 ), 8.0 ], // 2^3 = 8, within max 100 → applied
1756+ ['exceeds ' , 5.0 , Operator::power (3 , 100 ), 5.0 ], // 5^3 = 125 > 100 → left unchanged
1757+ ['negfrac ' , -4.0 , Operator::power (0.5 , 100 ), -4.0 ], // sqrt(-4) undefined → left unchanged
1758+ ['zeroneg ' , 0.0 , Operator::power (-1 , 100 ), 0.0 ], // 0^-1 undefined → left unchanged
1759+ ];
1760+
1761+ foreach ($ cases as [$ id , $ start , $ operator , $ expected ]) {
1762+ $ database ->createDocument ($ collectionId , new Document ([
1763+ '$id ' => $ id ,
1764+ '$permissions ' => [Permission::read (Role::any ()), Permission::update (Role::any ())],
1765+ 'value ' => $ start ,
1766+ ]));
1767+ $ database ->updateDocument ($ collectionId , $ id , new Document (['value ' => $ operator ]));
1768+
1769+ $ stored = $ database ->getDocument ($ collectionId , $ id )->getAttribute ('value ' );
1770+ $ this ->assertEqualsWithDelta ($ expected , $ stored , 0.000001 , "bounded power case ' {$ id }' stored the wrong value " );
1771+ }
1772+
1773+ $ database ->deleteCollection ($ collectionId );
1774+ }
1775+
17321776 /**
17331777 * Passing more values than the allowed maximum (10000) to an array operator must be rejected
17341778 * the same way on every adapter. Covers each operator that takes a caller-supplied value list.
@@ -1759,6 +1803,11 @@ public function testOperatorArraySizeLimit(): void
17591803 'arrayPrepend ' => Operator::arrayPrepend ($ tooMany ),
17601804 'arrayIntersect ' => Operator::arrayIntersect ($ tooMany ),
17611805 'arrayDiff ' => Operator::arrayDiff ($ tooMany ),
1806+ // arrayRemove wraps its argument, so the oversized list lands in values[0].
1807+ 'arrayRemove ' => Operator::arrayRemove ($ tooMany ),
1808+ // A wrapped payload (the list nested in values[0]) must be capped too, not just the
1809+ // spread form — otherwise count($values) would see 1 and slip past the limit.
1810+ 'arrayAppend (wrapped) ' => Operator::arrayAppend ([$ tooMany ]),
17621811 ];
17631812
17641813 foreach ($ operators as $ name => $ operator ) {
0 commit comments