import { describe, it, expect, vi } from 'vitest'; import { Result, ResultInterface, Ok, Err, ok, err } from '../'; describe('Result namespace - Runtime Tests', () => { describe('Result.from', () => { it('should return Ok when function succeeds', () => { // Arrange const fn = () => 42; // Act const result = Result.from(fn); // Assert expect(result.isOk()).toBe(true); if (result.isOk()) { expect(result.value).toBe(42); } }); it('should return Err when function throws', () => { // Arrange const error = new Error('test error'); const fn = () => { throw error; }; // Act const result = Result.from(fn); // Assert expect(result.isErr()).toBe(true); if (result.isErr()) { expect(result.err).toBe(error); } }); it('should apply errorMap when function throws and errorMap is provided', () => { // Arrange const fn = () => { throw new Error('original'); }; const errorMap = (e: unknown) => `Mapped: ${(e as Error).message}`; // Act const result = Result.from(fn, errorMap); // Assert expect(result.isErr()).toBe(true); if (result.isErr()) { expect(result.err).toBe('Mapped: original'); } }); it('should not call errorMap when function succeeds', () => { // Arrange const fn = () => 42; const errorMapSpy = vi.fn((e: unknown) => `Mapped: ${e}`); // Act const result = Result.from(fn, errorMapSpy); // Assert expect(result.isOk()).toBe(true); expect(errorMapSpy).not.toHaveBeenCalled(); }); it('should handle JSON.parse correctly', () => { // Arrange & Act const validResult = Result.from(() => JSON.parse('{"valid": true}')); const invalidResult = Result.from(() => JSON.parse('invalid')); // Assert expect(validResult.isOk()).toBe(true); if (validResult.isOk()) { expect(validResult.value).toEqual({ valid: true }); } expect(invalidResult.isErr()).toBe(true); }); it('should handle functions returning falsy values', () => { // Arrange & Act const zeroResult = Result.from(() => 0); const falseResult = Result.from(() => false); const emptyResult = Result.from(() => ''); const nullResult = Result.from(() => null); const undefinedResult = Result.from(() => undefined); // Assert expect(zeroResult.isOk()).toBe(true); expect(falseResult.isOk()).toBe(true); expect(emptyResult.isOk()).toBe(true); expect(nullResult.isOk()).toBe(true); expect(undefinedResult.isOk()).toBe(true); if (zeroResult.isOk()) expect(zeroResult.value).toBe(0); if (falseResult.isOk()) expect(falseResult.value).toBe(false); if (emptyResult.isOk()) expect(emptyResult.value).toBe(''); if (nullResult.isOk()) expect(nullResult.value).toBeNull(); if (undefinedResult.isOk()) expect(undefinedResult.value).toBeUndefined(); }); }); describe('Result.isResult', () => { it('should return true for Ok instances', () => { // Arrange & Act & Assert expect(Result.isResult(ok(42))).toBe(true); expect(Result.isResult(ok('hello'))).toBe(true); expect(Result.isResult(ok(null))).toBe(true); expect(Result.isResult(new Ok(42))).toBe(true); }); it('should return true for Err instances', () => { // Arrange & Act & Assert expect(Result.isResult(err('error'))).toBe(true); expect(Result.isResult(err(404))).toBe(true); expect(Result.isResult(err(null))).toBe(true); expect(Result.isResult(new Err('error'))).toBe(true); }); it('should return false for non-Result values', () => { // Arrange & Act & Assert expect(Result.isResult(42)).toBe(false); expect(Result.isResult('hello')).toBe(false); expect(Result.isResult(null)).toBe(false); expect(Result.isResult(undefined)).toBe(false); expect(Result.isResult(true)).toBe(false); expect(Result.isResult({ value: 42 })).toBe(false); expect(Result.isResult([1, 2, 3])).toBe(false); expect(Result.isResult({ isOk: () => true })).toBe(false); }); }); describe('Result.all', () => { it('should return Ok with all values when all results are Ok', () => { // Arrange const results: Result[] = [ ok(1) as Result, ok(2) as Result, ok(3) as Result, ]; // Act const result = Result.all(results); // Assert expect(result.isOk()).toBe(true); if (result.isOk()) { expect(result.value).toEqual([1, 2, 3]); } }); it('should return the first Err when any result is Err', () => { // Arrange const results: Result[] = [ ok(1) as Result, err('first error') as Result, ok(3) as Result, err('second error') as Result, ]; // Act const result = Result.all(results); // Assert expect(result.isErr()).toBe(true); if (result.isErr()) { expect(result.err).toBe('first error'); } }); it('should return Ok with empty array for empty input', () => { // Arrange const results: Result[] = []; // Act const result = Result.all(results); // Assert expect(result.isOk()).toBe(true); if (result.isOk()) { expect(result.value).toEqual([]); } }); it('should handle single element arrays', () => { // Arrange const okResults: Result[] = [ok(42) as Result]; const errResults: Result[] = [err('error') as Result]; // Act const okResult = Result.all(okResults); const errResult = Result.all(errResults); // Assert expect(okResult.isOk()).toBe(true); if (okResult.isOk()) expect(okResult.value).toEqual([42]); expect(errResult.isErr()).toBe(true); if (errResult.isErr()) expect(errResult.err).toBe('error'); }); it('should handle complex value types', () => { // Arrange type User = { id: number; name: string }; const results: Result[] = [ ok({ id: 1, name: 'Alice' }) as Result, ok({ id: 2, name: 'Bob' }) as Result, ]; // Act const result = Result.all(results); // Assert expect(result.isOk()).toBe(true); if (result.isOk()) { expect(result.value).toEqual([ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, ]); } }); }); describe('Result.partition', () => { it('should partition results into values and errors', () => { // Arrange const results: Result[] = [ ok(1) as Result, err('error1') as Result, ok(3) as Result, err('error2') as Result, ]; // Act const partitioned = Result.partition(results); // Assert expect(partitioned.values).toEqual([1, 3]); expect(partitioned.errors).toEqual(['error1', 'error2']); }); it('should handle all Ok results', () => { // Arrange const results: Result[] = [ ok(1) as Result, ok(2) as Result, ]; // Act const partitioned = Result.partition(results); // Assert expect(partitioned.values).toEqual([1, 2]); expect(partitioned.errors).toEqual([]); }); it('should handle all Err results', () => { // Arrange const results: Result[] = [ err('error1') as Result, err('error2') as Result, ]; // Act const partitioned = Result.partition(results); // Assert expect(partitioned.values).toEqual([]); expect(partitioned.errors).toEqual(['error1', 'error2']); }); it('should handle empty array', () => { // Arrange const results: Result[] = []; // Act const partitioned = Result.partition(results); // Assert expect(partitioned.values).toEqual([]); expect(partitioned.errors).toEqual([]); }); }); describe('Result.compact', () => { it('should extract values from Ok instances', () => { // Arrange const results: Result[] = [ ok(1) as Result, err('error') as Result, ok(3) as Result, ]; // Act const values = Result.compact(results); // Assert expect(values).toEqual([1, 3]); }); it('should return empty array when all are Err', () => { // Arrange const results: Result[] = [ err('error1') as Result, err('error2') as Result, ]; // Act const values = Result.compact(results); // Assert expect(values).toEqual([]); }); it('should return all values when all are Ok', () => { // Arrange const results: Result[] = [ ok(1) as Result, ok(2) as Result, ok(3) as Result, ]; // Act const values = Result.compact(results); // Assert expect(values).toEqual([1, 2, 3]); }); it('should handle empty array', () => { // Arrange const results: Result[] = []; // Act const values = Result.compact(results); // Assert expect(values).toEqual([]); }); it('should handle falsy Ok values', () => { // Arrange const results: Result[] = [ ok(0) as Result, ok(false) as Result, ok('') as Result, err('error') as Result, ]; // Act const values = Result.compact(results); // Assert expect(values).toEqual([0, false, '']); }); }); describe('Result.some', () => { it('should return true when at least one result is Ok', () => { // Arrange const results: Result[] = [ ok(1) as Result, err('error') as Result, ok(3) as Result, ]; // Act const result = Result.some(results); // Assert expect(result).toBe(true); }); it('should return false when all results are Err', () => { // Arrange const results: Result[] = [ err('error1') as Result, err('error2') as Result, ]; // Act const result = Result.some(results); // Assert expect(result).toBe(false); }); it('should return false for empty array', () => { // Arrange const results: Result[] = []; // Act const result = Result.some(results); // Assert expect(result).toBe(false); }); it('should return true when all results are Ok', () => { // Arrange const results: Result[] = [ ok(1) as Result, ok(2) as Result, ]; // Act const result = Result.some(results); // Assert expect(result).toBe(true); }); }); describe('Result.every', () => { it('should return true when all results are Ok', () => { // Arrange const results: Result[] = [ ok(1) as Result, ok(2) as Result, ]; // Act const result = Result.every(results); // Assert expect(result).toBe(true); }); it('should return false when any result is Err', () => { // Arrange const results: Result[] = [ ok(1) as Result, err('error') as Result, ]; // Act const result = Result.every(results); // Assert expect(result).toBe(false); }); it('should return true for empty array', () => { // Arrange const results: Result[] = []; // Act const result = Result.every(results); // Assert expect(result).toBe(true); }); it('should return false when all results are Err', () => { // Arrange const results: Result[] = [ err('error1') as Result, err('error2') as Result, ]; // Act const result = Result.every(results); // Assert expect(result).toBe(false); }); }); describe('Integration tests', () => { it('should work with Result.from and Result.all together', () => { // Arrange const parse = (s: string) => Result.from(() => JSON.parse(s), () => `Invalid JSON: ${s}`); // Act const allValid = Result.all([ parse('{"a": 1}'), parse('{"b": 2}'), ]); const someInvalid = Result.all([ parse('{"a": 1}'), parse('invalid'), ]); // Assert expect(allValid.isOk()).toBe(true); if (allValid.isOk()) { expect(allValid.value).toEqual([{ a: 1 }, { b: 2 }]); } expect(someInvalid.isErr()).toBe(true); if (someInvalid.isErr()) { expect(someInvalid.err).toBe('Invalid JSON: invalid'); } }); it('should work with Result.from and Result.partition together', () => { // Arrange const inputs = ['1', 'invalid', '3', 'also invalid']; const results = inputs.map(s => Result.from(() => parseInt(s), () => `Bad: ${s}`) ).map((r): Result => (r as ResultInterface).flatMap((n): Result => isNaN(n) ? err(`NaN from parse`) : ok(n)) ); // Act const partitioned = Result.partition(results); // Assert expect(partitioned.values).toEqual([1, 3]); expect(partitioned.errors).toEqual(['NaN from parse', 'NaN from parse']); }); }); });