|
11 | 11 | use Sofascore\PurgatoryBundle\Exception\ValueNotIterableException; |
12 | 12 | use Sofascore\PurgatoryBundle\RouteProvider\PropertyAccess\PurgatoryPropertyAccessor; |
13 | 13 | use Sofascore\PurgatoryBundle\Tests\RouteProvider\PropertyAccess\Fixtures\Foo; |
| 14 | +use Symfony\Component\HttpKernel\Kernel; |
14 | 15 | use Symfony\Component\PropertyAccess\PropertyAccess; |
15 | 16 |
|
16 | 17 | #[CoversClass(PurgatoryPropertyAccessor::class)] |
@@ -130,17 +131,127 @@ public static function traversableProvider(): iterable |
130 | 131 | ]; |
131 | 132 | } |
132 | 133 |
|
133 | | - public function testNotTraversable(): void |
| 134 | + #[DataProvider('nullSafeProvider')] |
| 135 | + public function testNullSafeCollectionPath(object $object, string $propertyPath, mixed $expectedResult): void |
134 | 136 | { |
| 137 | + if (Kernel::MAJOR_VERSION <= 5) { |
| 138 | + self::markTestSkipped('Requires Symfony 6 or higher.'); |
| 139 | + } |
| 140 | + |
| 141 | + self::assertTrue($this->purgatoryPropertyAccessor->isReadable($object, $propertyPath)); |
| 142 | + self::assertSame( |
| 143 | + expected: $expectedResult, |
| 144 | + actual: $this->purgatoryPropertyAccessor->getValue($object, $propertyPath), |
| 145 | + ); |
| 146 | + } |
| 147 | + |
| 148 | + public static function nullSafeProvider(): iterable |
| 149 | + { |
| 150 | + yield 'null-safe parent short-circuits to null' => [ |
| 151 | + 'object' => new Foo( |
| 152 | + id: 100, |
| 153 | + children: new ArrayCollection([]), |
| 154 | + linked: null, |
| 155 | + ), |
| 156 | + 'propertyPath' => 'linked?.children[*].id', |
| 157 | + 'expectedResult' => null, |
| 158 | + ]; |
| 159 | + |
| 160 | + yield 'null-safe parent present with populated collection yields values' => [ |
| 161 | + 'object' => new Foo( |
| 162 | + id: 100, |
| 163 | + children: new ArrayCollection([]), |
| 164 | + linked: new Foo( |
| 165 | + id: 1, |
| 166 | + children: new ArrayCollection([ |
| 167 | + new Foo(id: 10, children: new ArrayCollection([])), |
| 168 | + new Foo(id: 11, children: new ArrayCollection([])), |
| 169 | + ]), |
| 170 | + ), |
| 171 | + ), |
| 172 | + 'propertyPath' => 'linked?.children[*].id', |
| 173 | + 'expectedResult' => [10, 11], |
| 174 | + ]; |
| 175 | + |
| 176 | + yield 'null-safe collection element resolving to null short-circuits to null' => [ |
| 177 | + 'object' => new Foo( |
| 178 | + id: 100, |
| 179 | + children: new ArrayCollection([]), |
| 180 | + nullableChildren: null, |
| 181 | + ), |
| 182 | + 'propertyPath' => 'nullableChildren?[*].id', |
| 183 | + 'expectedResult' => null, |
| 184 | + ]; |
| 185 | + |
| 186 | + yield 'nested null-safe short-circuit contributes a null entry, consistent with a scalar leaf' => [ |
| 187 | + 'object' => new Foo( |
| 188 | + id: 0, |
| 189 | + children: new ArrayCollection([ |
| 190 | + new Foo(id: 1, children: new ArrayCollection([]), linked: null), |
| 191 | + new Foo( |
| 192 | + id: 2, |
| 193 | + children: new ArrayCollection([]), |
| 194 | + linked: new Foo( |
| 195 | + id: 20, |
| 196 | + children: new ArrayCollection([ |
| 197 | + new Foo(id: 100, children: new ArrayCollection([])), |
| 198 | + new Foo(id: 101, children: new ArrayCollection([])), |
| 199 | + ]), |
| 200 | + ), |
| 201 | + ), |
| 202 | + ]), |
| 203 | + ), |
| 204 | + 'propertyPath' => 'children[*].linked?.children[*].id', |
| 205 | + 'expectedResult' => [null, 100, 101], |
| 206 | + ]; |
| 207 | + } |
| 208 | + |
| 209 | + #[DataProvider('notTraversableProvider')] |
| 210 | + public function testNotTraversableThrows(object $object, string $propertyPath, string $expectedMessage): void |
| 211 | + { |
| 212 | + self::assertFalse($this->purgatoryPropertyAccessor->isReadable($object, $propertyPath)); |
| 213 | + |
135 | 214 | $this->expectException(ValueNotIterableException::class); |
136 | | - $this->expectExceptionMessage('Expected an iterable, "int" given at property path "id[*]".'); |
| 215 | + $this->expectExceptionMessage($expectedMessage); |
| 216 | + |
| 217 | + $this->purgatoryPropertyAccessor->getValue($object, $propertyPath); |
| 218 | + } |
137 | 219 |
|
138 | | - $this->purgatoryPropertyAccessor->getValue( |
139 | | - objectOrArray: new Foo( |
| 220 | + public static function notTraversableProvider(): iterable |
| 221 | + { |
| 222 | + yield 'scalar value is not iterable' => [ |
| 223 | + 'object' => new Foo( |
140 | 224 | id: 1, |
141 | 225 | children: new ArrayCollection([]), |
142 | 226 | ), |
143 | | - propertyPath: 'id[*].id', |
144 | | - ); |
| 227 | + 'propertyPath' => 'id[*].id', |
| 228 | + 'expectedMessage' => 'Expected an iterable, "int" given at property path "id[*]".', |
| 229 | + ]; |
| 230 | + |
| 231 | + yield 'non-null-safe collection resolving to null' => [ |
| 232 | + 'object' => new Foo( |
| 233 | + id: 100, |
| 234 | + children: new ArrayCollection([]), |
| 235 | + nullableChildren: null, |
| 236 | + ), |
| 237 | + 'propertyPath' => 'nullableChildren[*].id', |
| 238 | + 'expectedMessage' => 'Expected an iterable, "null" given at property path "nullableChildren[*]".', |
| 239 | + ]; |
| 240 | + |
| 241 | + if (Kernel::MAJOR_VERSION > 5) { |
| 242 | + yield 'null-safe parent present but non-null-safe collection resolving to null' => [ |
| 243 | + 'object' => new Foo( |
| 244 | + id: 100, |
| 245 | + children: new ArrayCollection([]), |
| 246 | + linked: new Foo( |
| 247 | + id: 1, |
| 248 | + children: new ArrayCollection([]), |
| 249 | + nullableChildren: null, |
| 250 | + ), |
| 251 | + ), |
| 252 | + 'propertyPath' => 'linked?.nullableChildren[*].id', |
| 253 | + 'expectedMessage' => 'Expected an iterable, "null" given at property path "linked?.nullableChildren[*]".', |
| 254 | + ]; |
| 255 | + } |
145 | 256 | } |
146 | 257 | } |
0 commit comments