UNPKG

@daisugi/anzen

Version:

Anzen helps write safe code without exceptions, taking roots from Rust's Result and Haskell's Either.

513 lines (485 loc) 14.4 kB
import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { type AnzenResultFailure, type AnzenResultSuccess, Result, } from '../anzen.js'; import type { Equal, Expect } from './utils/types.js'; function getRandomRes() { return Math.random() > 0.5 ? Result.success(1) : Result.failure('a'); } describe('Result', () => { describe('success', () => { it('should return expected value', () => { const res = Result.success(1); assert.deepEqual(res, Result.success(12)); assert.equal(res.isSuccess, true); assert.equal(res.isFailure, false); type check = Expect< Equal<typeof res, AnzenResultSuccess<number>> >; }); }); describe('failure', () => { it('should return expected value', () => { const res = Result.failure(1); assert.deepEqual(res, Result.failure(12)); assert.notDeepEqual(res, Result.success(1)); assert.equal(res.isSuccess, false); assert.equal(res.isFailure, true); type check = Expect< Equal<typeof res, AnzenResultFailure<number>> >; }); }); describe('getValue', () => { it('should return expected value', () => { const successRes = Result.success(1); assert.equal(successRes.getValue(), 1); const failureRes = Result.failure(1); assert.throws(() => failureRes.getValue(), { message: 'Cannot get the value of a failure.', }); }); }); describe('getError', () => { it('should return expected value', () => { const successRes = Result.success(1); assert.throws(() => successRes.getError(), { message: 'Cannot get the error of a success.', }); const failureRes = Result.failure(1); assert.equal(failureRes.getError(), 1); }); }); describe('chain', () => { it('should return expected value', () => { assert.equal( Result.success(1) .chain((x) => { type check = Expect<Equal<typeof x, number>>; return Result.success(x + 1); }) .getValue(), 2, ); assert.equal( Result.failure(1) .chain((x) => { type check = Expect<Equal<typeof x, number>>; return Result.success(x + 1); }) .getError(), 1, ); }); }); describe('elseChain', () => { it('should return expected value', () => { assert.equal( Result.success(1) .elseChain((x) => { type check = Expect<Equal<typeof x, number>>; return Result.success(x + 1); }) .getValue(), 1, ); const failureRes = Result.failure(1); assert.equal( failureRes .elseChain((x) => { type check = Expect<Equal<typeof x, number>>; return Result.success(x + 1); }) .getValue(), 2, ); }); }); describe('chainElseChain', () => { it('should return expected value', () => { assert.equal( Result.success(1) .chain(() => Result.success('a')) .elseChain((x) => { type check = Expect<Equal<typeof x, string>>; return Result.success(2); }) .getValue(), 'a', ); assert.equal( Result.failure(1) .chain(() => Result.success('a')) .elseChain((x) => { type check = Expect<Equal<typeof x, number>>; return Result.success(2); }) .getValue(), 2, ); const randomRes = getRandomRes() .chain((x) => { type check = Expect< Equal<typeof x, number | string> >; return Result.success('a'); }) .elseChain((x) => { type check = Expect<Equal<typeof x, string>>; return Result.failure(2); }); type check = Expect< Equal< typeof randomRes, | AnzenResultFailure<number> | AnzenResultSuccess<string> > >; }); }); describe('map', () => { it('should return expected value', () => { assert.equal( Result.success(1) .map((x) => { type check = Expect<Equal<typeof x, number>>; return x + 1; }) .getValue(), 2, ); assert.equal( Result.failure(1) .map((x) => { type check = Expect<Equal<typeof x, number>>; return x + 1; }) .getError(), 1, ); }); }); describe('elseMap', () => { it('should return expected value', () => { assert.equal( Result.success(1) .elseMap((x) => { type check = Expect<Equal<typeof x, number>>; return x + 1; }) .getValue(), 1, ); assert.equal( Result.failure(1) .elseMap((x) => { type check = Expect<Equal<typeof x, number>>; return x + 1; }) .getValue(), 2, ); }); }); describe('mapElseMap', () => { it('should return expected value', () => { assert.equal( Result.success(1) .map(() => 'b') .elseMap((x) => { type check = Expect<Equal<typeof x, string>>; return 2; }) .getValue(), 'b', ); assert.equal( Result.failure(1) .map(() => 'a') .elseMap((x) => { type check = Expect<Equal<typeof x, number>>; return 2; }) .getValue(), 2, ); const randomRes = getRandomRes() .map((x) => { type check = Expect< Equal<typeof x, number | string> >; return 'a'; }) .elseMap((x) => { type check = Expect<Equal<typeof x, string>>; return 2; }); type check = Expect< Equal< typeof randomRes, | AnzenResultSuccess<number> | AnzenResultSuccess<string> > >; }); }); describe('getOrElse', () => { it('should return expected value', () => { const successRes = Result.success(1); assert.equal(successRes.getOrElse(2), 1); const failureRes = Result.failure(1); assert.equal(failureRes.getOrElse(2), 2); }); }); describe('unwrap', () => { it('should return expected value', () => { const success = Result.success(1).unwrap(); assert.equal(success[0].getValue(), 1); assert.equal(success[1], 1); type check = Expect< Equal< typeof success, [AnzenResultSuccess<number>, number] > >; const failure = Result.failure(1).unwrap(); assert.equal(failure[0].getError(), 1); assert.equal(failure[1], undefined); type check2 = Expect< Equal< typeof failure, [AnzenResultFailure<number>, undefined] > >; const failureWithDefault = Result.failure(1).unwrap(2); assert.equal(failureWithDefault[0].getError(), 1); assert.equal(failureWithDefault[1], 2); type check3 = Expect< Equal< typeof failureWithDefault, [AnzenResultFailure<number>, number] > >; }); }); describe('unsafeUnwrap', () => { it('should return expected value', () => { const successRes = Result.success(1); assert.equal(successRes.unsafeUnwrap(), 1); const failureRes = Result.failure(1); assert.equal(failureRes.unsafeUnwrap(), 1); }); }); describe('toJSON', () => { it('should return expected value', () => { const successRes = Result.success(1); assert.deepEqual( successRes.toJSON(), JSON.stringify({ value: 1, isSuccess: true }), ); const failureRes = Result.failure(1); assert.deepEqual( failureRes.toJSON(), JSON.stringify({ error: 1, isSuccess: false }), ); }); }); describe('Result.fromJSON', () => { it('should return expected value', () => { const successRes = Result.fromJSON( JSON.stringify({ value: 1, isSuccess: true }), ); assert.equal(successRes.isSuccess, true); assert.equal(successRes.isFailure, false); assert.equal(successRes.getValue(), 1); const failureRes = Result.fromJSON( JSON.stringify({ error: 1, isSuccess: false, }), ); assert.equal(failureRes.isSuccess, false); assert.equal(failureRes.isFailure, true); assert.equal(failureRes.getError(), 1); }); }); describe('Result.promiseAll', () => { it('when all promises are resolved with success, should return expected value', async () => { const promise1 = Promise.resolve(Result.success(1)); const promise2 = async () => Result.success(2); const promise3 = () => Result.success('A'); const res = await Result.promiseAll([ promise1, promise2(), promise3(), ]); assert.equal(res.isSuccess, true); assert.equal(res.isFailure, false); assert.deepEqual(res.getValue(), [1, 2, 'A']); type check = Expect< Equal< typeof res, AnzenResultSuccess<[number, number, string]> > >; const res2 = await Result.promiseAll([ promise1, promise2(), promise3(), getRandomRes(), ]); type check2 = Expect< Equal< typeof res2, | AnzenResultFailure<string> | AnzenResultSuccess< [number, number, string, number] > > >; }); it('when promises are resolved with failure, should return expected value', async () => { const promise1 = Promise.resolve(Result.failure(2)); const res = await Result.promiseAll([promise1]); assert.equal(res.isSuccess, false); assert.equal(res.isFailure, true); assert.equal(res.getError(), 2); type check = Expect< Equal<typeof res, AnzenResultFailure<number>> >; }); }); describe('Result.unwrapPromiseAll', () => { it('when all promises are resolved with success, should return expected value', async () => { const promise1 = Promise.resolve(Result.success(1)); const promise2 = Promise.resolve(Result.success(2)); const promise3 = Promise.resolve(Result.success('A')); const [res, ...results] = await Result.unwrapPromiseAll([ [], promise1, promise2, promise3, ]); if (res.isSuccess) { results; } assert.equal(res.isSuccess, true); assert.equal(res.isFailure, false); assert.deepEqual(results, [1, 2, 'A']); type checks = [ Expect< Equal< typeof res, AnzenResultSuccess<[number, number, string]> > >, Expect< Equal<typeof results, [number, number, string]> >, ]; }); it('when promises are resolved with failure, should return expected value', async () => { const promise1 = Promise.resolve(Result.failure(2)); const [res, ...results] = await Result.unwrapPromiseAll([[], promise1]); assert.equal(res.isSuccess, false); assert.equal(res.isFailure, true); assert.deepEqual(results, []); type checks = [ Expect< Equal<typeof res, AnzenResultFailure<number>> >, ]; }); }); describe('Result.unwrap', () => { it('should return expected value', async () => { const successRes = Result.success(1); const failureRes = Result.failure(1); const fn = async () => successRes; const res = await fn().then(Result.unwrap()); assert.deepEqual(res, [successRes, 1]); type chack = Expect< Equal< typeof res, [AnzenResultSuccess<number>, number] > >; const fn2 = async () => failureRes; const result2 = await fn2().then(Result.unwrap()); assert.deepEqual(result2, [failureRes, undefined]); type check2 = Expect< Equal< typeof result2, [AnzenResultFailure<number>, undefined] > >; const result3 = await fn2().then(Result.unwrap(1)); assert.deepEqual(result3, [failureRes, 1]); type check3 = Expect< Equal< typeof result3, [AnzenResultFailure<number>, number] > >; }); }); describe('Result.fromThrowable', () => { it('when throwable is thrown, should return expected value', () => { const res = Result.fromSyncThrowable<Error, Error>( () => { throw new Error('err'); }, ); assert.equal(res.isSuccess, false); assert.equal(res.isFailure, true); assert.equal(res.getError().message, 'err'); }); it('when throwable is not thrown, should return expected value', () => { const res = Result.fromSyncThrowable(() => 1); assert.equal(res.isSuccess, true); assert.equal(res.isFailure, false); assert.equal(res.getValue(), 1); }); describe('parseError is provided', () => { it('when throwable is thrown, should return expected value', () => { const res = Result.fromSyncThrowable( () => { throw new Error('err'); }, (err) => err instanceof Error ? err.message : err, ); assert.equal(res.isSuccess, false); assert.equal(res.isFailure, true); assert.equal(res.getError(), 'err'); }); }); describe('async', () => { it('when throwable is thrown, should return expected value', async () => { const res = await Result.fromThrowable< Error, Error >(async () => { throw new Error('err'); }); assert.equal(res.isSuccess, false); assert.equal(res.isFailure, true); assert.equal(res.getError().message, 'err'); }); }); it('when throwable is not thrown, should return expected value', async () => { const res = await Result.fromThrowable(async () => 1); assert.equal(res.isSuccess, true); assert.equal(res.isFailure, false); assert.equal(res.getValue(), 1); }); }); });