Convert tests to Pest
This commit is contained in:
256
tests/Unit/OptionTest.php
Normal file
256
tests/Unit/OptionTest.php
Normal file
@@ -0,0 +1,256 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Daniel J. Summers <daniel@bitbadger.solutions>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use BitBadger\InspiredByFSharp\Option;
|
||||
use PhpOption\{None, Some};
|
||||
|
||||
describe('->value', function () {
|
||||
test('retrieves the value for Some', function () {
|
||||
expect(Option::Some(9)->value)->toBe(9);
|
||||
});
|
||||
test('throws an exception for None', function () {
|
||||
expect(fn() => Option::None()->value)->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()->value->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()->value->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()
|
||||
->value->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->value : '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->value : '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()
|
||||
->value->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()->value->toBe('something');
|
||||
});
|
||||
});
|
||||
206
tests/Unit/ResultTest.php
Normal file
206
tests/Unit/ResultTest.php
Normal file
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Daniel J. Summers <daniel@bitbadger.solutions>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use BitBadger\InspiredByFSharp\{Option, Result};
|
||||
|
||||
describe('->ok', function () {
|
||||
test('returns OK value for OK result', function () {
|
||||
expect(Result::OK('yay')->ok)->toBe('yay');
|
||||
});
|
||||
test('throws an exception for Error result', function () {
|
||||
expect(fn() => Result::Error('whoops')->ok)->toThrow(InvalidArgumentException::class);
|
||||
});
|
||||
});
|
||||
|
||||
describe('->error', function () {
|
||||
test('throws an exception for OK result', function () {
|
||||
expect(fn() => Result::OK('yeah')->error)->toThrow(InvalidArgumentException::class);
|
||||
});
|
||||
test('returns Error value for Error result', function () {
|
||||
expect(Result::Error('boo')->error)->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()->ok->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()->error->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()->ok->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()->error->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()->value->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->ok : 'Error: ' . $it->error;
|
||||
});
|
||||
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->ok : 'Error: ' . $it->error;
|
||||
});
|
||||
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()->ok->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()->error->toBe('sad trombone');
|
||||
});
|
||||
test('throws an exception for Error with a null value', function () {
|
||||
expect(fn() => Result::Error(null))->toThrow(InvalidArgumentException::class);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user