207 lines
7.6 KiB
PHP
207 lines
7.6 KiB
PHP
<?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);
|
|
});
|
|
});
|