Skip to content

Commit 4f793ae

Browse files
committed
chore: add handle tests
1 parent cd5b925 commit 4f793ae

1 file changed

Lines changed: 178 additions & 0 deletions

File tree

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import { expect } from '@jest/globals';
2+
import { SnapError } from '@metamask/snaps-sdk';
3+
4+
import { handle } from './errors';
5+
import logger from './logger';
6+
7+
// Mock the logger to avoid actual console output during tests
8+
jest.mock('./logger', () => ({
9+
error: jest.fn(),
10+
}));
11+
12+
describe('errors', () => {
13+
const mockLogger = logger as jest.Mocked<typeof logger>;
14+
15+
beforeEach(() => {
16+
jest.clearAllMocks();
17+
});
18+
19+
describe('handle', () => {
20+
it('returns the result when the function succeeds', async () => {
21+
const mockFn = jest.fn().mockResolvedValue('success');
22+
const scope = 'test-scope';
23+
24+
const result = await handle(scope, mockFn);
25+
26+
expect(result).toBe('success');
27+
expect(mockFn).toHaveBeenCalledTimes(1);
28+
expect(mockLogger.error).not.toHaveBeenCalled();
29+
});
30+
31+
it('handles and re-throws errors as SnapError', async () => {
32+
const originalError = new Error('Test error');
33+
const mockFn = jest.fn().mockRejectedValue(originalError);
34+
const scope = 'test-scope';
35+
36+
await expect(handle(scope, mockFn)).rejects.toThrow(SnapError);
37+
38+
expect(mockFn).toHaveBeenCalledTimes(1);
39+
expect(mockLogger.error).toHaveBeenCalledTimes(1);
40+
});
41+
42+
it('logs errors with the correct scope and error details', async () => {
43+
const originalError = new Error('Test error');
44+
const mockFn = jest.fn().mockRejectedValue(originalError);
45+
const scope = 'test-scope';
46+
47+
try {
48+
await handle(scope, mockFn);
49+
} catch (error) {
50+
// Expected to throw
51+
}
52+
53+
expect(mockLogger.error).toHaveBeenCalledWith(
54+
{ error: expect.any(SnapError) },
55+
expect.stringContaining(`[${scope}] Error occurred:`),
56+
);
57+
58+
expect(mockLogger.error).toHaveBeenCalledTimes(1);
59+
const logCall = mockLogger.error.mock.calls[0];
60+
const loggedError = logCall?.[0]?.error;
61+
expect(loggedError).toBeInstanceOf(SnapError);
62+
});
63+
64+
it('handles non-Error objects and converts them to SnapError', async () => {
65+
const nonErrorValue = 'string error';
66+
const mockFn = jest.fn().mockRejectedValue(nonErrorValue);
67+
const scope = 'test-scope';
68+
69+
await expect(handle(scope, mockFn)).rejects.toThrow(SnapError);
70+
71+
expect(mockLogger.error).toHaveBeenCalledTimes(1);
72+
const logCall = mockLogger.error.mock.calls[0];
73+
const loggedError = logCall?.[0]?.error;
74+
expect(loggedError).toBeInstanceOf(SnapError);
75+
});
76+
77+
it('handles null and undefined errors', async () => {
78+
const mockFn = jest.fn().mockRejectedValue(null);
79+
const scope = 'test-scope';
80+
81+
await expect(handle(scope, mockFn)).rejects.toThrow(SnapError);
82+
83+
expect(mockLogger.error).toHaveBeenCalledTimes(1);
84+
});
85+
86+
it('preserves the original error message in the SnapError', async () => {
87+
const originalError = new Error('Custom error message');
88+
const mockFn = jest.fn().mockRejectedValue(originalError);
89+
const scope = 'test-scope';
90+
91+
let caughtError: unknown;
92+
try {
93+
await handle(scope, mockFn);
94+
} catch (error) {
95+
caughtError = error;
96+
}
97+
98+
expect(caughtError).toBeInstanceOf(SnapError);
99+
const snapError = caughtError as SnapError;
100+
expect(snapError.message).toBe('Custom error message');
101+
});
102+
103+
it('handles async functions that return different types', async () => {
104+
const testCases = [
105+
{ value: 42, type: 'number' },
106+
{ value: { key: 'value' }, type: 'object' },
107+
{ value: [1, 2, 3], type: 'array' },
108+
{ value: true, type: 'boolean' },
109+
{ value: null, type: 'null' },
110+
];
111+
112+
for (const testCase of testCases) {
113+
const mockFn = jest.fn().mockResolvedValue(testCase.value);
114+
const scope = 'test-scope';
115+
116+
const result = await handle(scope, mockFn);
117+
118+
expect(result).toBe(testCase.value);
119+
expect(mockLogger.error).not.toHaveBeenCalled();
120+
}
121+
});
122+
123+
it('handles functions that throw different error types', async () => {
124+
const errorTypes = [
125+
new TypeError('Type error'),
126+
new ReferenceError('Reference error'),
127+
new RangeError('Range error'),
128+
new SyntaxError('Syntax error'),
129+
];
130+
131+
for (const errorType of errorTypes) {
132+
const mockFn = jest.fn().mockRejectedValue(errorType);
133+
const scope = 'test-scope';
134+
135+
await expect(handle(scope, mockFn)).rejects.toThrow(SnapError);
136+
}
137+
138+
expect(mockLogger.error).toHaveBeenCalledTimes(errorTypes.length);
139+
const logCalls = mockLogger.error.mock.calls;
140+
expect(logCalls).toHaveLength(errorTypes.length);
141+
142+
for (let i = 0; i < errorTypes.length; i++) {
143+
const logCall = logCalls[i];
144+
const loggedError = logCall?.[0]?.error;
145+
expect(loggedError).toBeInstanceOf(SnapError);
146+
expect(loggedError?.message).toBe(errorTypes[i]?.message);
147+
}
148+
});
149+
150+
it('includes error stack trace in the logged error', async () => {
151+
const originalError = new Error('Test error');
152+
originalError.stack = 'Error: Test error\n at test.js:1:1';
153+
const mockFn = jest.fn().mockRejectedValue(originalError);
154+
const scope = 'test-scope';
155+
156+
try {
157+
await handle(scope, mockFn);
158+
} catch (error) {
159+
// Expected to throw
160+
}
161+
162+
expect(mockLogger.error).toHaveBeenCalledWith(
163+
{ error: expect.any(SnapError) },
164+
expect.stringContaining('Error occurred:'),
165+
);
166+
});
167+
168+
it('handles functions that throw promises', async () => {
169+
const rejectedPromise = Promise.reject(new Error('Promise error'));
170+
const mockFn = jest.fn().mockImplementation(async () => rejectedPromise);
171+
const scope = 'test-scope';
172+
173+
await expect(handle(scope, mockFn)).rejects.toThrow(SnapError);
174+
175+
expect(mockLogger.error).toHaveBeenCalledTimes(1);
176+
});
177+
});
178+
});

0 commit comments

Comments
 (0)