257 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			257 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * @author Daniel J. Summers <daniel@bitbadger.solutions>
 | |
|  * @license MIT
 | |
|  */
 | |
| 
 | |
| declare(strict_types=1);
 | |
| 
 | |
| use BitBadger\InspiredByFSharp\Option;
 | |
| use PhpOption\{None, Some};
 | |
| 
 | |
| describe('->get()', function () {
 | |
|     test('retrieves the value for Some', function () {
 | |
|         expect(Option::Some(9)->get())->toBe(9);
 | |
|     });
 | |
|     test('throws an exception for None', function () {
 | |
|         expect(fn() => Option::None()->get())->toThrow(InvalidArgumentException::class);
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->isNone()', function () {
 | |
|     test('returns true for None', function () {
 | |
|         expect(Option::None()->isNone())->toBeTrue();
 | |
|     });
 | |
|     test('returns false for Some', function () {
 | |
|         expect(Option::Some(8)->isNone())->toBeFalse();
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->isSome()', function () {
 | |
|     test('returns false for None', function () {
 | |
|         expect(Option::None()->isSome())->toBeFalse();
 | |
|     });
 | |
|     test('returns true for Some', function () {
 | |
|         expect(Option::Some('boo')->isSome())->toBeTrue();
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->getOrDefault()', function () {
 | |
|     test('returns default value for None', function () {
 | |
|         expect(Option::None()->getOrDefault('yes'))->toBe('yes');
 | |
|     });
 | |
|     test('returns option value for Some', function () {
 | |
|         expect(Option::Some('no')->getOrDefault('yes'))->toBe('no');
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->getOrCall()', function () {
 | |
|     test('returns value from callable for None', function () {
 | |
|         expect(Option::None()->getOrCall(fn() => 'called'))->toBe('called');
 | |
|     });
 | |
|     test('returns option value for Some', function () {
 | |
|         expect(Option::Some('passed')->getOrCall(fn() => 'called'))->toBe('passed');
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->getOrThrow()', function () {
 | |
|     test('throws an exception for None', function () {
 | |
|         expect(fn() => Option::None()->getOrThrow(fn() => throw new BadMethodCallException()))
 | |
|             ->toThrow(BadMethodCallException::class);
 | |
|     });
 | |
|     test('returns option value for Some', function () {
 | |
|         expect(Option::Some('no throw')->getOrThrow(fn() => throw new BadMethodCallException()))->toBe('no throw');
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->bind()', function () {
 | |
|     test('returns None when binding against None', function () {
 | |
|         $original = Option::None();
 | |
|         $bound = $original->bind(fn($it) => Option::Some('value'));
 | |
|         expect($bound)->isNone()->toBeTrue()->and($bound)->toBe($original);
 | |
|     });
 | |
|     test('returns Some when binding against Some with Some', function () {
 | |
|         expect(Option::Some('hello')->bind(fn($it) => Option::Some('goodbye')))
 | |
|             ->isSome()->toBeTrue()->get()->toBe('goodbye');
 | |
|     });
 | |
|     test('returns None when binding against Some with None', function () {
 | |
|         expect(Option::Some('greetings')->bind(fn($it) => Option::None()))->isNone()->toBeTrue();
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->contains()', function () {
 | |
|     test('returns false for None', function () {
 | |
|         expect(Option::None()->contains(null))->toBeFalse();
 | |
|     });
 | |
|     test('returns true for Some when strict equality is matched', function () {
 | |
|         expect(Option::Some(3)->contains(3))->toBeTrue();
 | |
|     });
 | |
|     test('returns false for Some when strict equality is not matched', function () {
 | |
|         expect(Option::Some('3')->contains(3))->toBeFalse();
 | |
|     });
 | |
|     test('returns true for Some when loose equality is matched', function () {
 | |
|         expect(Option::Some('3')->contains(3, strict: false))->toBeTrue();
 | |
|     });
 | |
|     test('returns false for Some when loose equality is not matched', function () {
 | |
|         expect(Option::Some('3')->contains(4, strict: false))->toBeFalse();
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->exists()', function () {
 | |
|     test('returns false for None', function () {
 | |
|         expect(Option::None()->exists(fn($it) => true))->toBeFalse();
 | |
|     });
 | |
|     test('returns true for Some and matching condition', function () {
 | |
|         expect(Option::Some(14)->exists(fn($it) => $it < 100))->toBeTrue();
 | |
|     });
 | |
|     test('returns false for Some and non-matching condition', function () {
 | |
|         expect(Option::Some(14)->exists(fn($it) => $it > 100))->toBeFalse();
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->map()', function () {
 | |
|     test('does nothing for None', function () {
 | |
|         $tattle = new class { public bool $called = false; };
 | |
|         $none   = Option::None();
 | |
|         $mapped = $none->map(function () use ($tattle)
 | |
|         {
 | |
|             $tattle->called = true;
 | |
|             return 'hello';
 | |
|         });
 | |
|         expect($mapped)
 | |
|             ->isNone()->toBeTrue()
 | |
|             ->and($tattle->called)->toBeFalse()
 | |
|             ->and($mapped)->toBe($none);
 | |
|     });
 | |
|     test('maps value for Some', function () {
 | |
|         expect(Option::Some('abc ')->map(fn($it) => str_repeat($it, 2)))
 | |
|             ->isSome()->toBeTrue()->get()->toBe('abc abc ');
 | |
|     });
 | |
|     test('throws an exception if mapping returns null', function () {
 | |
|         expect(fn() => Option::Some('oof')->map(fn($it) => null))->toThrow(InvalidArgumentException::class);
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->iter()', function () {
 | |
|     test('does nothing for None', function () {
 | |
|         $target = new class { public mixed $called = null; };
 | |
|         Option::None()->iter(function () use ($target) { $target->called = 'uh oh'; });
 | |
|         expect($target->called)->toBeNull();
 | |
|     });
 | |
|     test('iterates for Some', function () {
 | |
|         $target = new class { public mixed $called = null; };
 | |
|         Option::Some(33)->iter(function ($it) use ($target) { $target->called = $it; });
 | |
|         expect($target->called)->toBe(33);
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->filter()', function () {
 | |
|     test('does nothing for None', function () {
 | |
|         $tattle   = new class { public bool $called = false; };
 | |
|         $none     = Option::None();
 | |
|         $filtered = $none->filter(function () use ($tattle)
 | |
|         {
 | |
|             $tattle->called = true;
 | |
|             return true;
 | |
|         });
 | |
|         expect($filtered)
 | |
|             ->isNone()->toBeTrue()
 | |
|             ->and($tattle->called)->toBeFalse()
 | |
|             ->and($filtered)->toBe($none);
 | |
|     });
 | |
|     test('returns Some when filter is matched', function () {
 | |
|         $some     = Option::Some(12);
 | |
|         $filtered = $some->filter(fn($it) => $it % 2 === 0);
 | |
|         expect($filtered)
 | |
|             ->isSome()->toBeTrue()
 | |
|             ->get()->toBe(12)
 | |
|             ->and($filtered)->toBe($some);
 | |
|     });
 | |
|     test('returns None when filter is not matched', function () {
 | |
|         expect(Option::Some(23)->filter(fn($it) => $it % 2 === 0)->isNone())->toBeTrue();
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->unwrap()', function () {
 | |
|     test('returns null for None', function () {
 | |
|         expect(Option::None()->unwrap())->toBeNull();
 | |
|     });
 | |
|     test('returns option value for Some', function () {
 | |
|         expect(Option::Some('boy howdy')->unwrap())->toBe('boy howdy');
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->tap()', function () {
 | |
|     test('is called for None', function () {
 | |
|         $value    = '';
 | |
|         $original = Option::None();
 | |
|         $tapped   = $original->tap(
 | |
|             function (Option $it) use (&$value) { $value = $it->isSome() ? $it->get() : 'none'; });
 | |
|         expect($value)->toBe('none')->and($original)->toBe($tapped);
 | |
|     });
 | |
|     test('is called for Some', function () {
 | |
|         $value    = '';
 | |
|         $original = Option::Some('testing');
 | |
|         $tapped   = $original->tap(
 | |
|             function (Option $it) use (&$value) { $value = $it->isSome() ? $it->get() : 'none'; });
 | |
|         expect($value)->toBe('testing')->and($original)->toBe($tapped);
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->toArray()', function () {
 | |
|     test('returns empty array for None', function () {
 | |
|         expect(Option::None()->toArray())->not->toBeNull()->toBeEmpty();
 | |
|     });
 | |
|     test('returns one-item array for Some', function () {
 | |
|         expect(Option::Some('15')->toArray())->not->toBeNull()->toBe(['15']);
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('->toPhpOption()', function () {
 | |
|     test('converts None', function () {
 | |
|         expect(Option::None()->toPhpOption())
 | |
|             ->toBeInstanceOf('PhpOption\None')
 | |
|             ->not->toBeNull()
 | |
|             ->isDefined()->toBeFalse();
 | |
|     });
 | |
|     test('converts Some', function () {
 | |
|         expect(Option::Some('php')->toPhpOption())
 | |
|             ->toBeInstanceOf('PhpOption\Some')
 | |
|             ->not->toBeNull()
 | |
|             ->isDefined()->toBeTrue()
 | |
|             ->get()->toBe('php');
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('::Some()', function () {
 | |
|     test('creates a Some option when given a value', function () {
 | |
|         expect(Option::Some('hello'))->not->toBeNull()->isSome()->toBeTrue();
 | |
|     });
 | |
|     test('throws an exception when given null', function () {
 | |
|         expect(fn() => Option::Some(null))->toThrow(InvalidArgumentException::class);
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('::None()', function () {
 | |
|     test('creates a None option', function () {
 | |
|         expect(Option::None())->not->toBeNull()->isNone()->toBeTrue();
 | |
|     });
 | |
| });
 | |
| 
 | |
| describe('::of()', function () {
 | |
|     test('creates a None option when given null', function () {
 | |
|         expect(Option::of(null))->not->toBeNull()->isNone()->toBeTrue();
 | |
|     });
 | |
|     test('creates a Some option when given a value', function () {
 | |
|         expect(Option::of('test'))->not->toBeNull()
 | |
|             ->isSome()->toBeTrue()
 | |
|             ->get()->toBe('test');
 | |
|     });
 | |
|     test('creates a None option when given PhpOption\None', function () {
 | |
|         expect(Option::of(None::create()))->isNone()->toBeTrue();
 | |
|     });
 | |
|     test('creates a Some option when given PhpOption\Some', function () {
 | |
|         expect(Option::of(Some::create('something')))->isSome()->toBeTrue()->get()->toBe('something');
 | |
|     });
 | |
| });
 |