|
7 | 7 | use Utopia\CLI\CLI; |
8 | 8 | use Utopia\DI\Container; |
9 | 9 | use Utopia\Validator\ArrayList; |
| 10 | +use Utopia\Validator\Boolean; |
| 11 | +use Utopia\Validator\Nullable; |
10 | 12 | use Utopia\Validator\Text; |
11 | 13 |
|
12 | 14 | class CLITest extends TestCase |
@@ -289,6 +291,121 @@ public function testMatch() |
289 | 291 | $this->assertEquals(null, $cli->match()); |
290 | 292 | } |
291 | 293 |
|
| 294 | + /** |
| 295 | + * @return iterable<string, array{0: string, 1: bool}> |
| 296 | + */ |
| 297 | + public static function looseBooleanValuesProvider(): iterable |
| 298 | + { |
| 299 | + yield '"false" string' => ['false', false]; |
| 300 | + yield '"true" string' => ['true', true]; |
| 301 | + yield '"0" string' => ['0', false]; |
| 302 | + yield '"1" string' => ['1', true]; |
| 303 | + } |
| 304 | + |
| 305 | + /** |
| 306 | + * Regression: --flag=false used to arrive as the literal string "false", |
| 307 | + * which PHP's implicit string-to-bool cast turned into `true` at the |
| 308 | + * `bool $flag` parameter boundary. The CLI dispatcher now coerces string |
| 309 | + * inputs whose validator is `Boolean` to a real PHP bool. |
| 310 | + * |
| 311 | + * @dataProvider looseBooleanValuesProvider |
| 312 | + */ |
| 313 | + public function testBooleanParamCoercesStringInput(string $input, bool $expected): void |
| 314 | + { |
| 315 | + $captured = null; |
| 316 | + |
| 317 | + $cli = new CLI(new Generic(), ['test.php', 'build', '--commit='.$input]); |
| 318 | + |
| 319 | + $cli |
| 320 | + ->task('build') |
| 321 | + ->param('commit', false, new Boolean(true), 'Commit changes', true) |
| 322 | + ->action(function (bool $commit) use (&$captured) { |
| 323 | + $captured = $commit; |
| 324 | + }); |
| 325 | + |
| 326 | + $cli->run(); |
| 327 | + |
| 328 | + $this->assertSame($expected, $captured); |
| 329 | + } |
| 330 | + |
| 331 | + public function testBooleanParamUsesDefaultWhenOmitted(): void |
| 332 | + { |
| 333 | + $captured = null; |
| 334 | + |
| 335 | + $cli = new CLI(new Generic(), ['test.php', 'build']); |
| 336 | + |
| 337 | + $cli |
| 338 | + ->task('build') |
| 339 | + ->param('commit', false, new Boolean(true), 'Commit changes', true) |
| 340 | + ->action(function (bool $commit) use (&$captured) { |
| 341 | + $captured = $commit; |
| 342 | + }); |
| 343 | + |
| 344 | + $cli->run(); |
| 345 | + |
| 346 | + $this->assertFalse($captured); |
| 347 | + } |
| 348 | + |
| 349 | + public function testBooleanParamCoercionUnwrapsNullable(): void |
| 350 | + { |
| 351 | + $captured = 'untouched'; |
| 352 | + |
| 353 | + $cli = new CLI(new Generic(), ['test.php', 'build', '--commit=false']); |
| 354 | + |
| 355 | + $cli |
| 356 | + ->task('build') |
| 357 | + ->param('commit', null, new Nullable(new Boolean(true)), 'Commit changes', true) |
| 358 | + ->action(function (bool $commit) use (&$captured) { |
| 359 | + $captured = $commit; |
| 360 | + }); |
| 361 | + |
| 362 | + $cli->run(); |
| 363 | + |
| 364 | + $this->assertFalse($captured); |
| 365 | + } |
| 366 | + |
| 367 | + /** |
| 368 | + * Empty-string params bypass `validate()` when optional, so they reach |
| 369 | + * `coerce()` un-validated. We must NOT silently turn them into `false` |
| 370 | + * (callers like Cloud's `Patch.php` use `''` as a "not set" sentinel and |
| 371 | + * later resolve it to `null`/three-state). |
| 372 | + */ |
| 373 | + public function testBooleanParamPreservesEmptyStringSentinel(): void |
| 374 | + { |
| 375 | + $captured = 'untouched'; |
| 376 | + |
| 377 | + $cli = new CLI(new Generic(), ['test.php', 'build']); |
| 378 | + |
| 379 | + $cli |
| 380 | + ->task('build') |
| 381 | + ->param('commit', '', new Boolean(true), 'Commit changes', true) |
| 382 | + ->action(function ($commit) use (&$captured) { |
| 383 | + $captured = $commit; |
| 384 | + }); |
| 385 | + |
| 386 | + $cli->run(); |
| 387 | + |
| 388 | + $this->assertSame('', $captured); |
| 389 | + } |
| 390 | + |
| 391 | + public function testNonBooleanValidatorPassesValueThroughUnchanged(): void |
| 392 | + { |
| 393 | + $captured = null; |
| 394 | + |
| 395 | + $cli = new CLI(new Generic(), ['test.php', 'build', '--name=false']); |
| 396 | + |
| 397 | + $cli |
| 398 | + ->task('build') |
| 399 | + ->param('name', '', new Text(64), 'A name') |
| 400 | + ->action(function (string $name) use (&$captured) { |
| 401 | + $captured = $name; |
| 402 | + }); |
| 403 | + |
| 404 | + $cli->run(); |
| 405 | + |
| 406 | + $this->assertSame('false', $captured); |
| 407 | + } |
| 408 | + |
292 | 409 | public function testEscaping() |
293 | 410 | { |
294 | 411 | ob_start(); |
|
0 commit comments