207 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			207 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * @author Daniel J. Summers <daniel@bitbadger.solutions>
 | |
|  * @license MIT
 | |
|  */
 | |
| 
 | |
| declare(strict_types=1);
 | |
| 
 | |
| use BitBadger\InspiredByFSharp\{Option, Result};
 | |
| 
 | |
| describe('->getOK()', function () {
 | |
|     test('returns OK value for OK result', function () {
 | |
|         expect(Result::OK('yay')->getOK())->toBe('yay');
 | |
|     });
 | |
|     test('throws an exception for Error result', function () {
 | |
|         expect(fn() => Result::Error('whoops')->getOK())->toThrow(InvalidArgumentException::class);
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->getError()', function () {
 | |
|     test('throws an exception for OK result', function () {
 | |
|         expect(fn() => Result::OK('yeah')->getError())->toThrow(InvalidArgumentException::class);
 | |
|     });
 | |
|     test('returns Error value for Error result', function () {
 | |
|         expect(Result::Error('boo')->getError())->toBe('boo');
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->isOK()', function () {
 | |
|     test('returns true for OK result', function () {
 | |
|         expect(Result::OK('ok')->isOK())->toBeTrue();
 | |
|     });
 | |
|     test('returns false for Error result', function () {
 | |
|         expect(Result::Error('error')->isOK())->toBeFalse();
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->isError()', function () {
 | |
|     test('returns false for OK result', function () {
 | |
|         expect(Result::OK('fine')->isError())->toBeFalse();
 | |
|     });
 | |
|     test('returns true for Error result', function () {
 | |
|         expect(Result::Error('not ok')->isError())->toBeTrue();
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->bind()', function () {
 | |
|     test('returns OK when binding against OK with OK', function () {
 | |
|         expect(Result::OK('one')->bind(fn($it) => Result::OK("$it two")))
 | |
|             ->isOK()->toBeTrue()->getOK()->toBe('one two');
 | |
|     });
 | |
|     test('returns Error when binding against OK with Error', function () {
 | |
|         expect(Result::OK('three')->bind(fn($it) => Result::Error('back to two')))
 | |
|             ->isError()->toBeTrue()->getError()->toBe('back to two');
 | |
|     });
 | |
|     test('returns Error when binding against Error', function () {
 | |
|         $original = Result::Error('oops');
 | |
|         $result   = $original->bind(fn($it) => Result::OK('never mind - it worked!'));
 | |
|         expect($result->isError())->toBeTrue()
 | |
|             ->and($result)->toBe($original);
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->contains()', function () {
 | |
|     test('returns true when OK is strictly equal', function () {
 | |
|         expect(Result::OK(18)->contains(18))->toBeTrue();
 | |
|     });
 | |
|     test('returns false when OK is not strictly equal', function () {
 | |
|         expect(Result::OK(18)->contains('18'))->toBeFalse();
 | |
|     });
 | |
|     test('returns true when OK is loosely equal', function () {
 | |
|         expect(Result::OK(18)->contains('18', strict: false))->toBeTrue();
 | |
|     });
 | |
|     test('returns false when OK is not loosely equal', function () {
 | |
|         expect(Result::OK(18)->contains(17, strict: false))->toBeFalse();
 | |
|     });
 | |
|     test('returns false for Error', function () {
 | |
|         expect(Result::Error('ouch')->contains('ouch'))->toBeFalse();
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->exists()', function () {
 | |
|     test('returns true for OK when condition matches', function () {
 | |
|         expect(Result::OK(14)->exists(fn($it) => $it < 100))->toBeTrue();
 | |
|     });
 | |
|     test('returns false for OK when condition does not match', function () {
 | |
|         expect(Result::OK(14)->exists(fn($it) => $it > 100))->toBeFalse();
 | |
|     });
 | |
|     test('returns false for Error', function () {
 | |
|         expect(Result::Error(true)->exists(fn($it) => true))->toBeFalse();
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->map()', function () {
 | |
|     test('maps value for OK', function () {
 | |
|         expect(Result::OK('yard')->map(fn($it) => strrev($it)))
 | |
|             ->isOK()->toBeTrue()->getOK()->toBe('dray');
 | |
|     });
 | |
|     test('throws an exception for OK when mapping result is null', function () {
 | |
|         expect(fn() => Result::OK('not null')->map(fn($it) => null))->toThrow(InvalidArgumentException::class);
 | |
|     });
 | |
|     test('does nothing for Error', function () {
 | |
|         $tattle = new class { public bool $called = false; };
 | |
|         $error  = Result::Error('nope');
 | |
|         $mapped = $error->map(function () use ($tattle)
 | |
|         {
 | |
|             $tattle->called = true;
 | |
|             return 'hello';
 | |
|         });
 | |
|         expect($mapped)
 | |
|             ->isError()->toBeTrue()
 | |
|             ->and($tattle->called)->toBeFalse()
 | |
|             ->and($mapped)->toBe($error);
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->mapError()', function () {
 | |
|     test('does nothing for OK', function () {
 | |
|         $tattle = new class { public bool $called = false; };
 | |
|         $ok     = Result::OK('sure');
 | |
|         $mapped = $ok->mapError(function () use ($tattle)
 | |
|         {
 | |
|             $tattle->called = true;
 | |
|             return 'hello';
 | |
|         });
 | |
|         expect($mapped)
 | |
|             ->isOK()->toBeTrue()
 | |
|             ->and($tattle->called)->toBeFalse()
 | |
|             ->and($mapped)->toBe($ok);
 | |
|     });
 | |
|     test('maps value for Error', function () {
 | |
|         expect(Result::Error('taco')->mapError(fn($it) => str_repeat('*', strlen($it))))
 | |
|             ->isError()->toBeTrue()->getError()->toBe('****');
 | |
|     });
 | |
|     test('throws an exception for Error when mapping result is null', function () {
 | |
|         expect(fn() => Result::Error('pizza')->mapError(fn($it) => null))->toThrow(InvalidArgumentException::class);
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->iter()', function () {
 | |
|     test('iterates for OK', function () {
 | |
|         $target = new class { public mixed $called = null; };
 | |
|         Result::OK(77)->iter(function ($it) use ($target) { $target->called = $it; });
 | |
|         expect($target->called)->toBe(77);
 | |
|     });
 | |
|     test('does nothing for Error', function () {
 | |
|         $target = new class { public mixed $called = null; };
 | |
|         Result::Error('')->iter(function () use ($target) { $target->called = 'uh oh'; });
 | |
|         expect($target->called)->toBeNull();
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->toArray()', function () {
 | |
|     test('returns a one-item array for OK', function () {
 | |
|         expect(Result::OK('yay')->toArray())->not->toBeNull()->toBe(['yay']);
 | |
|     });
 | |
|     test('returns an empty array for Error', function () {
 | |
|         expect(Result::Error('oh no')->toArray())->not->toBeNull()->toBeEmpty();
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->toOption()', function () {
 | |
|     test('returns a Some option for OK', function () {
 | |
|         expect(Result::OK(99)->toOption())->isSome()->toBeTrue()->get()->toBe(99);
 | |
|     });
 | |
|     test('returns a None option for Error', function () {
 | |
|         expect(Result::Error('file not found')->toOption())->isNone()->toBeTrue();
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->tap()', function () {
 | |
|     test('is called for OK', function () {
 | |
|         $value = '';
 | |
|         $original = Result::OK('working');
 | |
|         $tapped = $original->tap(function (Result $it) use (&$value) {
 | |
|             $value = $it->isOK() ? 'OK: ' . $it->getOK() : 'Error: ' . $it->getError();
 | |
|         });
 | |
|         expect($value)->toBe('OK: working')->and($tapped)->toBe($original);
 | |
|     });
 | |
|     test('is called for Error', function () {
 | |
|         $value    = '';
 | |
|         $original = Result::Error('failed');
 | |
|         $tapped   = $original->tap(function (Result $it) use (&$value) {
 | |
|             $value = $it->isOK() ? 'OK: ' . $it->getOK() : 'Error: ' . $it->getError();
 | |
|         });
 | |
|         expect($value)->toBe('Error: failed')->and($tapped)->toBe($original);
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('::OK()', function () {
 | |
|     test('creates an OK result for a non-null value', function () {
 | |
|         expect(Result::OK('something'))->isOK()->toBeTrue()->getOK()->toBe('something');
 | |
|     });
 | |
|     test('throws an exception for OK with a null value', function () {
 | |
|         expect(fn() => Result::OK(null))->toThrow(InvalidArgumentException::class);
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('::Error()', function () {
 | |
|     test('creates an Error result for a non-null value', function () {
 | |
|         expect(Result::Error('sad trombone'))->isError()->toBeTrue()->getError()->toBe('sad trombone');
 | |
|     });
 | |
|     test('throws an exception for Error with a null value', function () {
 | |
|         expect(fn() => Result::Error(null))->toThrow(InvalidArgumentException::class);
 | |
|     });
 | |
| });
 |