* @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); }); });