Skip to content

Commit 358aad0

Browse files
Add test
1 parent ac4fa3f commit 358aad0

File tree

2 files changed

+95
-7
lines changed

2 files changed

+95
-7
lines changed

packages/snaps-rpc-methods/jest.config.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ module.exports = deepmerge(baseConfig, {
1010
],
1111
coverageThreshold: {
1212
global: {
13-
branches: 95.68,
14-
functions: 98.75,
13+
branches: 95.7,
14+
functions: 98.76,
1515
lines: 98.99,
16-
statements: 98.69,
16+
statements: 98.7,
1717
},
1818
},
1919
});

packages/snaps-rpc-methods/src/permitted/setState.test.ts

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { JsonRpcEngine } from '@metamask/json-rpc-engine';
22
import { errorCodes } from '@metamask/rpc-errors';
33
import type { SetStateResult } from '@metamask/snaps-sdk';
4-
import type {
5-
Json,
6-
JsonRpcRequest,
7-
PendingJsonRpcResponse,
4+
import {
5+
createDeferredPromise,
6+
type Json,
7+
type JsonRpcRequest,
8+
type PendingJsonRpcResponse,
89
} from '@metamask/utils';
910

1011
import { setStateHandler, type SetStateParameters, set } from './setState';
@@ -196,6 +197,93 @@ describe('snap_setState', () => {
196197
});
197198
});
198199

200+
it('uses a mutex to protect state updates', async () => {
201+
const { implementation } = setStateHandler;
202+
203+
const { promise: getStateCalled, resolve: resolveGetStateCalled } =
204+
createDeferredPromise();
205+
const getSnapState = jest.fn().mockImplementation(() => {
206+
resolveGetStateCalled();
207+
return {};
208+
});
209+
210+
const { promise: updateSnapStatePromise, resolve } =
211+
createDeferredPromise();
212+
213+
const updateSnapState = jest.fn().mockReturnValue(updateSnapStatePromise);
214+
const getUnlockPromise = jest.fn().mockResolvedValue(undefined);
215+
const hasPermission = jest.fn().mockReturnValue(true);
216+
const getSnap = jest.fn().mockReturnValue({ preinstalled: false });
217+
218+
const hooks = {
219+
getSnapState,
220+
updateSnapState,
221+
getUnlockPromise,
222+
hasPermission,
223+
getSnap,
224+
};
225+
226+
const engine = new JsonRpcEngine();
227+
228+
engine.push((request, response, next, end) => {
229+
const result = implementation(
230+
request as JsonRpcRequest<SetStateParameters>,
231+
response as PendingJsonRpcResponse<SetStateResult>,
232+
next,
233+
end,
234+
hooks,
235+
);
236+
237+
result?.catch(end);
238+
});
239+
240+
const responsePromise1 = engine.handle({
241+
jsonrpc: '2.0',
242+
id: 1,
243+
method: 'snap_setState',
244+
params: {
245+
key: 'foo',
246+
value: 'baz',
247+
encrypted: false,
248+
},
249+
});
250+
251+
const responsePromise2 = engine.handle({
252+
jsonrpc: '2.0',
253+
id: 2,
254+
method: 'snap_setState',
255+
params: {
256+
key: 'foo',
257+
value: 'bar',
258+
encrypted: false,
259+
},
260+
});
261+
262+
await getStateCalled;
263+
264+
expect(getSnapState).toHaveBeenCalledTimes(1);
265+
266+
resolve();
267+
268+
const response1 = await responsePromise1;
269+
const response2 = await responsePromise2;
270+
271+
expect(getSnapState).toHaveBeenCalledTimes(2);
272+
expect(updateSnapState).toHaveBeenNthCalledWith(2, { foo: 'bar' }, false);
273+
274+
expect(response1).toStrictEqual({
275+
jsonrpc: '2.0',
276+
id: 1,
277+
result: null,
278+
});
279+
280+
expect(response2).toStrictEqual({
281+
jsonrpc: '2.0',
282+
id: 2,
283+
result: null,
284+
});
285+
});
286+
199287
it('throws if the requesting origin does not have the required permission', async () => {
200288
const { implementation } = setStateHandler;
201289

0 commit comments

Comments
 (0)