Skip to content

Commit fea282b

Browse files
[Coverage Improvement] Add comprehensive unit tests for resultTypes helper (#4768)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent af6d9c5 commit fea282b

File tree

1 file changed

+255
-0
lines changed

1 file changed

+255
-0
lines changed
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
import {
2+
getResultValue,
3+
isErrorResult,
4+
isOkResult,
5+
type ExtractResultError,
6+
type ExtractResultValue,
7+
} from './resultTypes';
8+
import { describe, expect, it } from '@jest/globals';
9+
10+
describe('resultTypes utilities', () => {
11+
describe('isOkResult', () => {
12+
it('should return true for successful result', () => {
13+
const result = { ok: true, value: 'test data' };
14+
expect(isOkResult(result)).toBe(true);
15+
});
16+
17+
it('should return false for failed result', () => {
18+
const result = { ok: false, errors: ['error message'] };
19+
expect(isOkResult(result)).toBe(false);
20+
});
21+
22+
it('should narrow type correctly for successful result', () => {
23+
const result = { ok: true, value: 42 } as
24+
| { ok: true; value: number }
25+
| { ok: false; errors: string[] };
26+
27+
if (isOkResult(result)) {
28+
// Type should be narrowed to { ok: true; value: number }
29+
expect(result.value).toBe(42);
30+
// TypeScript should recognize result.value exists
31+
const value: number = result.value;
32+
expect(value).toBe(42);
33+
}
34+
});
35+
36+
it('should handle result with undefined value', () => {
37+
const result = { ok: true, value: undefined };
38+
expect(isOkResult(result)).toBe(true);
39+
});
40+
41+
it('should handle result with null value', () => {
42+
const result = { ok: true, value: null };
43+
expect(isOkResult(result)).toBe(true);
44+
});
45+
});
46+
47+
describe('isErrorResult', () => {
48+
it('should return true for failed result', () => {
49+
const result = { ok: false, errors: ['error1', 'error2'] };
50+
expect(isErrorResult(result)).toBe(true);
51+
});
52+
53+
it('should return false for successful result', () => {
54+
const result = { ok: true, value: 'success' };
55+
expect(isErrorResult(result)).toBe(false);
56+
});
57+
58+
it('should narrow type correctly for failed result', () => {
59+
const result = { ok: false, errors: ['network error'] } as
60+
| { ok: true; value: string }
61+
| { ok: false; errors: string[] };
62+
63+
if (isErrorResult(result)) {
64+
// Type should be narrowed to { ok: false; errors: string[] }
65+
expect(result.errors).toEqual(['network error']);
66+
// TypeScript should recognize result.errors exists
67+
const errors: string[] = result.errors;
68+
expect(errors.length).toBe(1);
69+
}
70+
});
71+
72+
it('should handle different error types', () => {
73+
interface CustomError {
74+
message: string;
75+
code: number;
76+
}
77+
const result = {
78+
ok: false,
79+
errors: { message: 'custom error', code: 500 },
80+
} as { ok: true; value: any } | { ok: false; errors: CustomError };
81+
82+
if (isErrorResult<CustomError>(result)) {
83+
expect(result.errors.message).toBe('custom error');
84+
expect(result.errors.code).toBe(500);
85+
}
86+
});
87+
88+
it('should handle result with undefined errors', () => {
89+
const result = { ok: false, errors: undefined };
90+
expect(isErrorResult(result)).toBe(true);
91+
});
92+
});
93+
94+
describe('getResultValue', () => {
95+
it('should return value for successful result', () => {
96+
const result = { ok: true, value: 'test value' };
97+
expect(getResultValue(result)).toBe('test value');
98+
});
99+
100+
it('should return null for failed result', () => {
101+
const result = { ok: false, errors: ['error'] };
102+
expect(getResultValue(result)).toBeNull();
103+
});
104+
105+
it('should return custom default value for failed result', () => {
106+
const result = { ok: false, errors: ['error'] };
107+
const defaultValue = 'fallback value';
108+
expect(getResultValue(result, defaultValue)).toBe(defaultValue);
109+
});
110+
111+
it('should return null when value is undefined in successful result', () => {
112+
const result = { ok: true, value: undefined };
113+
expect(getResultValue(result)).toBeNull();
114+
});
115+
116+
it('should return custom default when value is undefined', () => {
117+
const result = { ok: true, value: undefined };
118+
expect(getResultValue(result, 'default')).toBe('default');
119+
});
120+
121+
it('should handle numeric zero as valid value', () => {
122+
const result = { ok: true, value: 0 };
123+
expect(getResultValue(result)).toBe(0);
124+
});
125+
126+
it('should handle empty string as valid value', () => {
127+
const result = { ok: true, value: '' };
128+
expect(getResultValue(result)).toBe('');
129+
});
130+
131+
it('should handle boolean false as valid value', () => {
132+
const result = { ok: true, value: false };
133+
expect(getResultValue(result)).toBe(false);
134+
});
135+
136+
it('should handle null as valid value when explicitly set', () => {
137+
const result = { ok: true, value: null };
138+
// null value is considered undefined per the logic
139+
expect(getResultValue(result)).toBeNull();
140+
});
141+
142+
it('should handle complex objects', () => {
143+
const complexObject = { id: 1, name: 'test', nested: { value: 42 } };
144+
const result = { ok: true, value: complexObject };
145+
expect(getResultValue(result)).toEqual(complexObject);
146+
});
147+
148+
it('should handle arrays', () => {
149+
const array = [1, 2, 3, 4, 5];
150+
const result = { ok: true, value: array };
151+
expect(getResultValue(result)).toEqual(array);
152+
});
153+
154+
it('should return default for non-ok result with no default specified', () => {
155+
const result = { ok: false };
156+
expect(getResultValue(result)).toBeNull();
157+
});
158+
});
159+
160+
describe('Type extraction utilities', () => {
161+
it('should correctly extract success value type', () => {
162+
type TestResult =
163+
| { ok: true; value: string }
164+
| { ok: false; errors: string[] };
165+
type ValueType = ExtractResultValue<TestResult>;
166+
167+
// This is a type-level test - if it compiles, the type is correct
168+
const value: ValueType = 'test';
169+
expect(typeof value).toBe('string');
170+
});
171+
172+
it('should correctly extract error type', () => {
173+
type TestResult =
174+
| { ok: true; value: number }
175+
| { ok: false; errors: Error[] };
176+
type ErrorType = ExtractResultError<TestResult>;
177+
178+
// This is a type-level test - if it compiles, the type is correct
179+
const errors: ErrorType = [new Error('test')];
180+
expect(Array.isArray(errors)).toBe(true);
181+
});
182+
});
183+
184+
describe('Real-world GraphQL Result pattern', () => {
185+
it('should handle typical GraphQL @catch result', () => {
186+
type User = { id: string; name: string };
187+
type UserResult =
188+
| { ok: true; value: User }
189+
| { ok: false; errors: readonly { message: string }[] };
190+
191+
const successResult: UserResult = {
192+
ok: true,
193+
value: { id: '123', name: 'John' },
194+
};
195+
196+
const errorResult: UserResult = {
197+
ok: false,
198+
errors: [{ message: 'User not found' }],
199+
};
200+
201+
// Success case
202+
if (isOkResult(successResult)) {
203+
expect(successResult.value.id).toBe('123');
204+
expect(successResult.value.name).toBe('John');
205+
}
206+
207+
// Error case
208+
if (isErrorResult(errorResult)) {
209+
expect(errorResult.errors[0].message).toBe('User not found');
210+
}
211+
212+
// Value extraction
213+
const user = getResultValue(successResult);
214+
expect(user?.name).toBe('John');
215+
216+
const noUser = getResultValue(errorResult);
217+
expect(noUser).toBeNull();
218+
});
219+
220+
it('should handle nullable result values', () => {
221+
type NullableResult =
222+
| { ok: true; value: string | null }
223+
| { ok: false; errors: string[] };
224+
225+
const nullValueResult: NullableResult = {
226+
ok: true,
227+
value: null,
228+
};
229+
230+
expect(isOkResult(nullValueResult)).toBe(true);
231+
// Since value is explicitly null, getResultValue returns the default
232+
expect(getResultValue(nullValueResult)).toBeNull();
233+
});
234+
235+
it('should handle multiple error types', () => {
236+
type MultiErrorResult =
237+
| { ok: true; value: number }
238+
| { ok: false; errors: Array<{ message: string; code: number }> };
239+
240+
const errorResult: MultiErrorResult = {
241+
ok: false,
242+
errors: [
243+
{ message: 'Validation error', code: 400 },
244+
{ message: 'Auth error', code: 401 },
245+
],
246+
};
247+
248+
if (isErrorResult(errorResult)) {
249+
expect(errorResult.errors).toHaveLength(2);
250+
expect(errorResult.errors[0].code).toBe(400);
251+
expect(errorResult.errors[1].code).toBe(401);
252+
}
253+
});
254+
});
255+
});

0 commit comments

Comments
 (0)