Path: blob/main/extensions/copilot/src/util/common/test/result.spec.ts
13401 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { describe, expect, it } from 'vitest';6import { Result } from '../result';78describe('Result', () => {910describe('Result.ok', () => {11it('creates an ok result', () => {12const r = Result.ok(42);13expect(r.isOk()).toBe(true);14expect(r.isError()).toBe(false);15expect(r.val).toBe(42);16});17});1819describe('Result.error', () => {20it('creates an error result', () => {21const r = Result.error('bad');22expect(r.isOk()).toBe(false);23expect(r.isError()).toBe(true);24expect(r.err).toBe('bad');25});26});2728describe('Result.fromString', () => {29it('creates an error result with an Error instance', () => {30const r = Result.fromString('something failed');31expect(r.isError()).toBe(true);32expect(r.err).toBeInstanceOf(Error);33expect(r.err.message).toBe('something failed');34});35});3637describe('Result.tryWith', () => {38it('returns ok when the function succeeds', () => {39const r = Result.tryWith(() => 10 + 5);40expect(r.isOk()).toBe(true);41if (r.isOk()) {42expect(r.val).toBe(15);43}44});4546it('returns error when the function throws', () => {47const r = Result.tryWith(() => { throw new Error('boom'); });48expect(r.isError()).toBe(true);49if (r.isError()) {50expect(r.err.message).toBe('boom');51}52});53});5455describe('Result.tryWithAsync', () => {56it('returns ok when the async function resolves', async () => {57const r = await Result.tryWithAsync(async () => 99);58expect(r.isOk()).toBe(true);59if (r.isOk()) {60expect(r.val).toBe(99);61}62});6364it('returns error when the async function rejects', async () => {65const r = await Result.tryWithAsync(async () => { throw new Error('async boom'); });66expect(r.isError()).toBe(true);67if (r.isError()) {68expect(r.err.message).toBe('async boom');69}70});71});7273describe('map', () => {74it('transforms the value of an ok result', () => {75const r = Result.ok(3).map(x => x * 2);76expect(r.isOk()).toBe(true);77if (r.isOk()) {78expect(r.val).toBe(6);79}80});8182it('is a no-op on an error result', () => {83const r: Result<number, string> = Result.error('fail');84const mapped = r.map(x => x * 2);85expect(mapped.isError()).toBe(true);86if (mapped.isError()) {87expect(mapped.err).toBe('fail');88}89});90});9192describe('mapError', () => {93it('is a no-op on an ok result', () => {94const r: Result<number, string> = Result.ok(5);95const mapped = r.mapError(e => new Error(e));96expect(mapped.isOk()).toBe(true);97if (mapped.isOk()) {98expect(mapped.val).toBe(5);99}100});101102it('transforms the error of an error result', () => {103const r = Result.error('oops');104const mapped = r.mapError(e => ({ reason: e }));105expect(mapped.isError()).toBe(true);106if (mapped.isError()) {107expect(mapped.err).toEqual({ reason: 'oops' });108}109});110});111112describe('flatMap', () => {113it('chains ok results', () => {114const r = Result.ok(10).flatMap(x =>115x > 0 ? Result.ok(x.toString()) : Result.error('negative' as const)116);117expect(r.isOk()).toBe(true);118if (r.isOk()) {119expect(r.val).toBe('10');120}121});122123it('chains to an error result', () => {124const r = Result.ok(-1).flatMap(x =>125x > 0 ? Result.ok(x.toString()) : Result.error('negative' as const)126);127expect(r.isError()).toBe(true);128if (r.isError()) {129expect(r.err).toBe('negative');130}131});132133it('is a no-op on an error result', () => {134const r: Result<number, string> = Result.error('already bad');135const chained = r.flatMap(x => Result.ok(x * 2));136expect(chained.isError()).toBe(true);137if (chained.isError()) {138expect(chained.err).toBe('already bad');139}140});141});142143describe('unwrap', () => {144it('returns the value for ok results', () => {145expect(Result.ok('hello').unwrap()).toBe('hello');146});147148it('throws for error results with an Error', () => {149const r: Result<string, Error> = Result.error(new Error('fail'));150expect(() => r.unwrap()).toThrow('fail');151});152153it('throws a wrapped error for non-Error error values', () => {154const r: Result<string, string> = Result.error('string error');155expect(() => r.unwrap()).toThrow('string error');156});157});158159describe('unwrapOr', () => {160it('returns the value for ok results', () => {161const r: Result<number, string> = Result.ok(42);162expect(r.unwrapOr(0)).toBe(42);163});164165it('returns the default for error results', () => {166const r: Result<number, string> = Result.error('nope');167expect(r.unwrapOr(0)).toBe(0);168});169});170171describe('type narrowing', () => {172it('narrows to ok after isOk check', () => {173const r: Result<number, string> = Result.ok(1);174if (r.isOk()) {175// TypeScript should know r.val exists here176const _v: number = r.val;177expect(_v).toBe(1);178}179});180181it('narrows to error after isError check', () => {182const r: Result<number, string> = Result.error('e');183if (r.isError()) {184// TypeScript should know r.err exists here185const _e: string = r.err;186expect(_e).toBe('e');187}188});189});190});191192193