Skip to content

Commit 1ed232f

Browse files
authored
Add tests for ECA-3 Custodian type (#38)
* chore: add ECA3Client tests * chore: add eca-3 tests
1 parent 413fd7f commit 1ed232f

20 files changed

+762
-32
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
/* eslint-disable jest/no-conditional-expect */
2+
/* eslint-disable @typescript-eslint/naming-convention */
3+
import fetchMock from 'jest-fetch-mock';
4+
5+
import { ECA3Client } from './ECA3Client';
6+
import { SimpleCache } from '../../simple-cache';
7+
import { TOKEN_EXPIRED_EVENT } from '../constants';
8+
import { mockECA3CreateTransactionPayload } from './mocks/mockECA3CreateTransactionPayload';
9+
import { mockECA3GetSignedMessageByIdPayload } from './mocks/mockECA3GetSignedMessageByIdPayload';
10+
import { mockECA3GetSignedMessageLinkPayload } from './mocks/mockECA3GetSignedMessageLinkPayload';
11+
import { mockECA3GetTransactionByIdPayload } from './mocks/mockECA3GetTransactionByIdPayload';
12+
import { mockECA3GetTransactionLinkPayload } from './mocks/mockECA3GetTransactionLinkPayload';
13+
import { mockECA3SignPayload } from './mocks/mockECA3SignPayload';
14+
import { mockECA3SignTypedDataPayload } from './mocks/mockECA3SignTypedDataPayload';
15+
16+
// So that we can access the method returned by the call factory
17+
const jsonRpcCall = jest
18+
.fn()
19+
.mockImplementation(
20+
async (_method: string, _params: any, _accessToken: string) => {
21+
return Promise.resolve({
22+
data: { result: 'test' },
23+
});
24+
},
25+
);
26+
27+
// Mock the json-rpc-call factory
28+
jest.mock('../../../util/json-rpc-call', () => ({
29+
__esModule: true,
30+
default: (_url: string) => jsonRpcCall,
31+
}));
32+
33+
jest.mock('../../simple-cache/SimpleCache');
34+
35+
fetchMock.enableMocks();
36+
37+
describe('ECA3Client', () => {
38+
let client: ECA3Client;
39+
40+
const mockedSimpleCache = jest.mocked(SimpleCache);
41+
let mockedSimpleCacheInstance: SimpleCache;
42+
const hashedToken =
43+
'd704d4eab860b9793d8b1c03c0a0d4657908d48a5bd4b7fe0da82430b9e23e23';
44+
45+
beforeEach(() => {
46+
jest.resetAllMocks();
47+
mockedSimpleCache.mockClear();
48+
fetchMock.resetMocks();
49+
50+
client = new ECA3Client(
51+
'http://test/json-rpc',
52+
'refresh_token',
53+
'http://refresh-token-url',
54+
);
55+
56+
mockedSimpleCacheInstance = mockedSimpleCache.mock
57+
.instances[0] as SimpleCache;
58+
59+
fetchMock.mockResponse(
60+
JSON.stringify({
61+
access_token: 'accesstoken',
62+
expires_in: 10,
63+
refresh_token: 'refresh_token',
64+
}),
65+
);
66+
});
67+
68+
describe('getAccessToken', () => {
69+
it('should call the refresh token URL and return the access token', async () => {
70+
const result = await client.getAccessToken();
71+
72+
expect(fetchMock).toHaveBeenCalledWith('http://refresh-token-url', {
73+
body: JSON.stringify({
74+
grant_type: 'refresh_token',
75+
refresh_token: 'refresh_token',
76+
}),
77+
method: 'POST',
78+
credentials: 'same-origin',
79+
headers: { 'Content-Type': 'application/json' },
80+
});
81+
82+
expect(result).toBe('accesstoken');
83+
});
84+
85+
it('should return the cached version if there is a cached version', async () => {
86+
// Run once to set the expires_in
87+
await client.getAccessToken();
88+
89+
jest
90+
.spyOn(mockedSimpleCacheInstance, 'cacheExists')
91+
.mockImplementation()
92+
.mockReturnValue(true);
93+
jest
94+
.spyOn(mockedSimpleCacheInstance, 'cacheValid')
95+
.mockImplementation()
96+
.mockReturnValue(false);
97+
jest
98+
.spyOn(mockedSimpleCacheInstance, 'getCache')
99+
.mockImplementation()
100+
.mockReturnValue('cached');
101+
102+
const result = await client.getAccessToken();
103+
104+
expect(result).toBe('accesstoken');
105+
});
106+
107+
it('should not return the cached version if there is a cached version but it is invalid', async () => {
108+
// Run once to set the expires_in
109+
await client.getAccessToken();
110+
111+
jest
112+
.spyOn(mockedSimpleCacheInstance, 'cacheExists')
113+
.mockImplementation()
114+
.mockReturnValue(true);
115+
jest
116+
.spyOn(mockedSimpleCacheInstance, 'cacheValid')
117+
.mockImplementation()
118+
.mockReturnValue(true);
119+
jest
120+
.spyOn(mockedSimpleCacheInstance, 'getCache')
121+
.mockImplementation()
122+
.mockReturnValue('cached');
123+
124+
const result = await client.getAccessToken();
125+
126+
expect(result).toBe('cached');
127+
});
128+
129+
it('throws an error if there is a HTTP error', async () => {
130+
fetchMock.mockRejectedValue(new Error('HTTP error'));
131+
132+
await expect(client.getAccessToken()).rejects.toThrow('HTTP error');
133+
});
134+
135+
it('emit an ITR event if there is a HTTP 401 error status', async () => {
136+
fetchMock.mockResponseOnce(
137+
JSON.stringify({
138+
error: {
139+
message: 'Test error',
140+
},
141+
}),
142+
);
143+
144+
const messageHandler = jest.fn();
145+
146+
client.on(TOKEN_EXPIRED_EVENT, messageHandler);
147+
148+
try {
149+
await client.getAccessToken();
150+
} catch (error) {
151+
await new Promise((resolve, _reject) => {
152+
setTimeout(() => {
153+
expect(messageHandler).toHaveBeenCalledWith({
154+
url: 'test',
155+
oldRefreshToken: hashedToken,
156+
});
157+
resolve(null);
158+
}, 100);
159+
});
160+
161+
await expect(client.getAccessToken()).rejects.toThrow(
162+
'Refresh token provided is no longer valid.',
163+
);
164+
}
165+
});
166+
});
167+
168+
describe('listAccounts', () => {
169+
it('should call the custodian_listAccounts method on the json rpc caller', async () => {
170+
await client.listAccounts();
171+
expect(jsonRpcCall).toHaveBeenCalledWith(
172+
'custodian_listAccounts',
173+
{},
174+
'accesstoken',
175+
);
176+
});
177+
});
178+
179+
describe('listAccountsSigned', () => {
180+
it('should call the custodian_listAccountsSigned method on the json rpc caller', async () => {
181+
await client.listAccountsSigned();
182+
expect(jsonRpcCall).toHaveBeenCalledWith(
183+
'custodian_listAccountsSigned',
184+
{},
185+
'accesstoken',
186+
);
187+
});
188+
});
189+
190+
describe('getCustomerProof', () => {
191+
it('should call the custodian_getCustomerProof method on the json rpc caller', async () => {
192+
await client.getCustomerProof();
193+
expect(jsonRpcCall).toHaveBeenCalledWith(
194+
'custodian_getCustomerProof',
195+
{},
196+
'accesstoken',
197+
);
198+
});
199+
});
200+
201+
describe('createTransaction', () => {
202+
it('should call the custodian_createTransaction method on the json rpc caller', async () => {
203+
await client.createTransaction(mockECA3CreateTransactionPayload);
204+
expect(jsonRpcCall).toHaveBeenCalledWith(
205+
'custodian_createTransaction',
206+
mockECA3CreateTransactionPayload,
207+
'accesstoken',
208+
);
209+
});
210+
});
211+
212+
describe('listAccountChainIds', () => {
213+
it('should call the custodian_listAccountChainIds method on the json rpc caller', async () => {
214+
await client.getAccountChainIds(['0xtest']);
215+
expect(jsonRpcCall).toHaveBeenCalledWith(
216+
'custodian_listAccountChainIds',
217+
['0xtest'],
218+
'accesstoken',
219+
);
220+
});
221+
});
222+
223+
describe('signPersonalMessage', () => {
224+
it('should call the custodian_sign method on the json rpc caller', async () => {
225+
await client.signPersonalMessage(mockECA3SignPayload);
226+
expect(jsonRpcCall).toHaveBeenCalledWith(
227+
'custodian_sign',
228+
mockECA3SignPayload,
229+
'accesstoken',
230+
);
231+
});
232+
});
233+
234+
describe('signTypedData', () => {
235+
it('should call the custodian_signTypedData method on the json rpc caller', async () => {
236+
await client.signTypedData(mockECA3SignTypedDataPayload);
237+
expect(jsonRpcCall).toHaveBeenCalledWith(
238+
'custodian_signTypedData',
239+
mockECA3SignTypedDataPayload,
240+
'accesstoken',
241+
);
242+
});
243+
});
244+
245+
describe('getSignedMessage', () => {
246+
it('should call the custodian_getSignedMessageById method on the json rpc caller', async () => {
247+
await client.getSignedMessage(mockECA3GetSignedMessageByIdPayload);
248+
expect(jsonRpcCall).toHaveBeenCalledWith(
249+
'custodian_getSignedMessageById',
250+
mockECA3GetSignedMessageByIdPayload,
251+
'accesstoken',
252+
);
253+
});
254+
});
255+
256+
describe('getTransaction', () => {
257+
it('should call the custodian_getTransactionById method on the json rpc caller', async () => {
258+
await client.getTransaction(mockECA3GetTransactionByIdPayload);
259+
expect(jsonRpcCall).toHaveBeenCalledWith(
260+
'custodian_getTransactionById',
261+
mockECA3GetTransactionByIdPayload,
262+
'accesstoken',
263+
);
264+
});
265+
});
266+
267+
describe('getTransactionLink', () => {
268+
it('should call the custodian_getTransactionLink method on the json rpc caller', async () => {
269+
await client.getTransactionLink(mockECA3GetTransactionLinkPayload);
270+
expect(jsonRpcCall).toHaveBeenCalledWith(
271+
'custodian_getTransactionLink',
272+
mockECA3GetTransactionLinkPayload,
273+
'accesstoken',
274+
);
275+
});
276+
});
277+
278+
describe('getSignedMessageLink', () => {
279+
it('should call the custodian_getSignedMessageLink method on the json rpc caller', async () => {
280+
await client.getSignedMessageLink(mockECA3GetSignedMessageLinkPayload);
281+
expect(jsonRpcCall).toHaveBeenCalledWith(
282+
'custodian_getSignedMessageLink',
283+
mockECA3GetSignedMessageLinkPayload,
284+
'accesstoken',
285+
);
286+
});
287+
});
288+
});

packages/snap/src/lib/custodian-types/eca3/ECA3Client.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ export class ECA3Client extends EventEmitter {
249249

250250
async getTransaction(
251251
getTransactionPayload: ECA3GetTransactionByIdPayload,
252-
): Promise<JsonRpcResult<ECA3GetTransactionByIdResponse>> {
252+
): Promise<JsonRpcResult<ECA3GetTransactionByIdResponse | null>> {
253253
const accessToken = await this.getAccessToken();
254254

255255
return this.#call(

0 commit comments

Comments
 (0)