Files
gridpilot.gg/core/shared/domain/Result.test.ts
Marc Mintel 093eece3d7
Some checks failed
Contract Testing / contract-tests (pull_request) Failing after 5m47s
Contract Testing / contract-snapshot (pull_request) Has been skipped
core tests
2026-01-22 18:20:33 +01:00

371 lines
12 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { Result } from './Result';
describe('Result', () => {
describe('Result.ok()', () => {
it('should create a success result with a value', () => {
const result = Result.ok('success-value');
expect(result.isOk()).toBe(true);
expect(result.isErr()).toBe(false);
expect(result.unwrap()).toBe('success-value');
});
it('should create a success result with undefined value', () => {
const result = Result.ok(undefined);
expect(result.isOk()).toBe(true);
expect(result.isErr()).toBe(false);
expect(result.unwrap()).toBe(undefined);
});
it('should create a success result with null value', () => {
const result = Result.ok(null);
expect(result.isOk()).toBe(true);
expect(result.isErr()).toBe(false);
expect(result.unwrap()).toBe(null);
});
it('should create a success result with complex object', () => {
const complexValue = { id: 123, name: 'test', nested: { data: 'value' } };
const result = Result.ok(complexValue);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(complexValue);
});
it('should create a success result with array', () => {
const arrayValue = [1, 2, 3, 'test'];
const result = Result.ok(arrayValue);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(arrayValue);
});
});
describe('Result.err()', () => {
it('should create an error result with an error', () => {
const error = new Error('test error');
const result = Result.err(error);
expect(result.isOk()).toBe(false);
expect(result.isErr()).toBe(true);
expect(result.unwrapErr()).toBe(error);
});
it('should create an error result with string error', () => {
const result = Result.err('string error');
expect(result.isOk()).toBe(false);
expect(result.isErr()).toBe(true);
expect(result.unwrapErr()).toBe('string error');
});
it('should create an error result with object error', () => {
const error = { code: 'VALIDATION_ERROR', message: 'Invalid input' };
const result = Result.err(error);
expect(result.isOk()).toBe(false);
expect(result.isErr()).toBe(true);
expect(result.unwrapErr()).toEqual(error);
});
it('should create an error result with custom error type', () => {
interface CustomError {
code: string;
details: Record<string, unknown>;
}
const error: CustomError = {
code: 'NOT_FOUND',
details: { id: '123' }
};
const result = Result.err<unknown, CustomError>(error);
expect(result.isOk()).toBe(false);
expect(result.isErr()).toBe(true);
expect(result.unwrapErr()).toEqual(error);
});
});
describe('Result.isOk()', () => {
it('should return true for success results', () => {
const result = Result.ok('value');
expect(result.isOk()).toBe(true);
});
it('should return false for error results', () => {
const result = Result.err(new Error('error'));
expect(result.isOk()).toBe(false);
});
});
describe('Result.isErr()', () => {
it('should return false for success results', () => {
const result = Result.ok('value');
expect(result.isErr()).toBe(false);
});
it('should return true for error results', () => {
const result = Result.err(new Error('error'));
expect(result.isErr()).toBe(true);
});
});
describe('Result.unwrap()', () => {
it('should return the value for success results', () => {
const result = Result.ok('test-value');
expect(result.unwrap()).toBe('test-value');
});
it('should throw error for error results', () => {
const result = Result.err(new Error('test error'));
expect(() => result.unwrap()).toThrow('Called unwrap on an error result');
});
it('should return complex values for success results', () => {
const complexValue = { id: 123, data: { nested: 'value' } };
const result = Result.ok(complexValue);
expect(result.unwrap()).toEqual(complexValue);
});
it('should return arrays for success results', () => {
const arrayValue = [1, 2, 3];
const result = Result.ok(arrayValue);
expect(result.unwrap()).toEqual(arrayValue);
});
});
describe('Result.unwrapOr()', () => {
it('should return the value for success results', () => {
const result = Result.ok('actual-value');
expect(result.unwrapOr('default-value')).toBe('actual-value');
});
it('should return default value for error results', () => {
const result = Result.err(new Error('error'));
expect(result.unwrapOr('default-value')).toBe('default-value');
});
it('should return default value when value is undefined', () => {
const result = Result.ok(undefined);
expect(result.unwrapOr('default-value')).toBe(undefined);
});
it('should return default value when value is null', () => {
const result = Result.ok(null);
expect(result.unwrapOr('default-value')).toBe(null);
});
});
describe('Result.unwrapErr()', () => {
it('should return the error for error results', () => {
const error = new Error('test error');
const result = Result.err(error);
expect(result.unwrapErr()).toBe(error);
});
it('should throw error for success results', () => {
const result = Result.ok('value');
expect(() => result.unwrapErr()).toThrow('Called unwrapErr on a success result');
});
it('should return string errors', () => {
const result = Result.err('string error');
expect(result.unwrapErr()).toBe('string error');
});
it('should return object errors', () => {
const error = { code: 'ERROR', message: 'Something went wrong' };
const result = Result.err(error);
expect(result.unwrapErr()).toEqual(error);
});
});
describe('Result.map()', () => {
it('should transform success values', () => {
const result = Result.ok(5);
const mapped = result.map((x) => x * 2);
expect(mapped.isOk()).toBe(true);
expect(mapped.unwrap()).toBe(10);
});
it('should not transform error results', () => {
const error = new Error('test error');
const result = Result.err<number, Error>(error);
const mapped = result.map((x) => x * 2);
expect(mapped.isErr()).toBe(true);
expect(mapped.unwrapErr()).toBe(error);
});
it('should handle complex transformations', () => {
const result = Result.ok({ id: 1, name: 'test' });
const mapped = result.map((obj) => ({ ...obj, name: obj.name.toUpperCase() }));
expect(mapped.isOk()).toBe(true);
expect(mapped.unwrap()).toEqual({ id: 1, name: 'TEST' });
});
it('should handle array transformations', () => {
const result = Result.ok([1, 2, 3]);
const mapped = result.map((arr) => arr.map((x) => x * 2));
expect(mapped.isOk()).toBe(true);
expect(mapped.unwrap()).toEqual([2, 4, 6]);
});
});
describe('Result.mapErr()', () => {
it('should transform error values', () => {
const error = new Error('original error');
const result = Result.err<string, Error>(error);
const mapped = result.mapErr((e) => new Error(`wrapped: ${e.message}`));
expect(mapped.isErr()).toBe(true);
expect(mapped.unwrapErr().message).toBe('wrapped: original error');
});
it('should not transform success results', () => {
const result = Result.ok('value');
const mapped = result.mapErr((e) => new Error(`wrapped: ${e.message}`));
expect(mapped.isOk()).toBe(true);
expect(mapped.unwrap()).toBe('value');
});
it('should handle string error transformations', () => {
const result = Result.err('error message');
const mapped = result.mapErr((e) => e.toUpperCase());
expect(mapped.isErr()).toBe(true);
expect(mapped.unwrapErr()).toBe('ERROR MESSAGE');
});
it('should handle object error transformations', () => {
const error = { code: 'ERROR', message: 'Something went wrong' };
const result = Result.err(error);
const mapped = result.mapErr((e) => ({ ...e, code: `WRAPPED_${e.code}` }));
expect(mapped.isErr()).toBe(true);
expect(mapped.unwrapErr()).toEqual({ code: 'WRAPPED_ERROR', message: 'Something went wrong' });
});
});
describe('Result.andThen()', () => {
it('should chain success results', () => {
const result1 = Result.ok(5);
const result2 = result1.andThen((x) => Result.ok(x * 2));
expect(result2.isOk()).toBe(true);
expect(result2.unwrap()).toBe(10);
});
it('should propagate errors through chain', () => {
const error = new Error('first error');
const result1 = Result.err<number, Error>(error);
const result2 = result1.andThen((x) => Result.ok(x * 2));
expect(result2.isErr()).toBe(true);
expect(result2.unwrapErr()).toBe(error);
});
it('should handle error in chained function', () => {
const result1 = Result.ok(5);
const result2 = result1.andThen((x) => Result.err(new Error(`error at ${x}`)));
expect(result2.isErr()).toBe(true);
expect(result2.unwrapErr().message).toBe('error at 5');
});
it('should support multiple chaining steps', () => {
const result = Result.ok(2)
.andThen((x) => Result.ok(x * 3))
.andThen((x) => Result.ok(x + 1))
.andThen((x) => Result.ok(x * 2));
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBe(14); // ((2 * 3) + 1) * 2 = 14
});
it('should stop chaining on first error', () => {
const result = Result.ok(2)
.andThen((x) => Result.ok(x * 3))
.andThen((x) => Result.err(new Error('stopped here')))
.andThen((x) => Result.ok(x + 1)); // This should not execute
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().message).toBe('stopped here');
});
});
describe('Result.value getter', () => {
it('should return value for success results', () => {
const result = Result.ok('test-value');
expect(result.value).toBe('test-value');
});
it('should return undefined for error results', () => {
const result = Result.err(new Error('error'));
expect(result.value).toBe(undefined);
});
it('should return undefined for success results with undefined value', () => {
const result = Result.ok(undefined);
expect(result.value).toBe(undefined);
});
});
describe('Result.error getter', () => {
it('should return error for error results', () => {
const error = new Error('test error');
const result = Result.err(error);
expect(result.error).toBe(error);
});
it('should return undefined for success results', () => {
const result = Result.ok('value');
expect(result.error).toBe(undefined);
});
it('should return string errors', () => {
const result = Result.err('string error');
expect(result.error).toBe('string error');
});
});
describe('Result type safety', () => {
it('should work with custom error codes', () => {
type MyErrorCode = 'NOT_FOUND' | 'VALIDATION_ERROR' | 'PERMISSION_DENIED';
const successResult = Result.ok<string, MyErrorCode>('data');
const errorResult = Result.err<string, MyErrorCode>('NOT_FOUND');
expect(successResult.isOk()).toBe(true);
expect(errorResult.isErr()).toBe(true);
});
it('should work with ApplicationErrorCode pattern', () => {
interface ApplicationErrorCode<Code extends string, Details = unknown> {
code: Code;
details?: Details;
}
type MyErrorCodes = 'USER_NOT_FOUND' | 'INVALID_EMAIL';
const successResult = Result.ok<string, ApplicationErrorCode<MyErrorCodes>>('user');
const errorResult = Result.err<string, ApplicationErrorCode<MyErrorCodes>>({
code: 'USER_NOT_FOUND',
details: { userId: '123' }
});
expect(successResult.isOk()).toBe(true);
expect(errorResult.isErr()).toBe(true);
expect(errorResult.unwrapErr().code).toBe('USER_NOT_FOUND');
});
});
});