Skip to content

Commit 84c43c4

Browse files
Fix tests and lint
1 parent 00214e6 commit 84c43c4

File tree

18 files changed

+209
-60
lines changed

18 files changed

+209
-60
lines changed

packages/rpc-methods/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ export type { PermittedRpcMethodHooks } from './permitted';
22
export {
33
handlers as permittedMethods,
44
createSnapsMethodMiddleware,
5-
FileEncoding,
65
} from './permitted';
76
export * from './restricted';
87
export { SnapCaveatType } from '@metamask/snaps-utils';

packages/rpc-methods/src/permitted/getFile.test.ts

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
import { AuxiliaryFileEncoding, VirtualFile } from '@metamask/snaps-utils';
12
import type {
23
JsonRpcRequest,
34
PendingJsonRpcResponse,
45
JsonRpcFailure,
56
JsonRpcSuccess,
67
} from '@metamask/types';
7-
import { stringToBytes, bytesToHex } from '@metamask/utils';
8+
import { stringToBytes } from '@metamask/utils';
89
import { JsonRpcEngine } from 'json-rpc-engine';
910

1011
import type { GetFileHooks } from './getFile';
@@ -34,9 +35,52 @@ describe('snap_getFile', () => {
3435

3536
const hooks = getMockHooks();
3637

37-
const hexadecimal = bytesToHex(
38+
const vfile = new VirtualFile(
3839
stringToBytes(JSON.stringify({ foo: 'bar' })),
3940
);
41+
const base64 = vfile.toString('base64');
42+
(
43+
hooks.getSnapFile as jest.MockedFunction<typeof hooks.getSnapFile>
44+
).mockImplementation(async (_path: string) => base64);
45+
46+
const engine = new JsonRpcEngine();
47+
engine.push((req, res, next, end) => {
48+
const result = implementation(
49+
req as JsonRpcRequest<JsonRpcRequest<unknown>>,
50+
res as PendingJsonRpcResponse<unknown>,
51+
next,
52+
end,
53+
hooks,
54+
);
55+
56+
result?.catch(end);
57+
});
58+
59+
const response = (await engine.handle({
60+
jsonrpc: '2.0',
61+
id: 1,
62+
method: 'snap_getFile',
63+
params: {
64+
path: './src/foo.json',
65+
},
66+
})) as JsonRpcSuccess<string>;
67+
68+
expect(response.result).toBe(base64);
69+
expect(hooks.getSnapFile).toHaveBeenCalledWith(
70+
'./src/foo.json',
71+
AuxiliaryFileEncoding.Base64,
72+
);
73+
});
74+
75+
it('supports hex in encoding parameter', async () => {
76+
const { implementation } = getFileHandler;
77+
78+
const hooks = getMockHooks();
79+
80+
const vfile = new VirtualFile(
81+
stringToBytes(JSON.stringify({ foo: 'bar' })),
82+
);
83+
const hexadecimal = vfile.toString('hex');
4084
(
4185
hooks.getSnapFile as jest.MockedFunction<typeof hooks.getSnapFile>
4286
).mockImplementation(async (_path: string) => hexadecimal);
@@ -60,11 +104,15 @@ describe('snap_getFile', () => {
60104
method: 'snap_getFile',
61105
params: {
62106
path: './src/foo.json',
107+
encoding: 'hex',
63108
},
64109
})) as JsonRpcSuccess<string>;
65110

66111
expect(response.result).toBe(hexadecimal);
67-
expect(hooks.getSnapFile).toHaveBeenCalledWith('./src/foo.json');
112+
expect(hooks.getSnapFile).toHaveBeenCalledWith(
113+
'./src/foo.json',
114+
AuxiliaryFileEncoding.Hex,
115+
);
68116
});
69117

70118
it('ends with error if hook throws', async () => {
@@ -101,7 +149,10 @@ describe('snap_getFile', () => {
101149
})) as JsonRpcFailure;
102150

103151
expect(response.error.message).toBe('foo bar');
104-
expect(hooks.getSnapFile).toHaveBeenCalledWith('./src/foo.json');
152+
expect(hooks.getSnapFile).toHaveBeenCalledWith(
153+
'./src/foo.json',
154+
AuxiliaryFileEncoding.Base64,
155+
);
105156
});
106157
});
107158
});

packages/rpc-methods/src/permitted/getFile.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { enumValue } from '@metamask/snaps-utils';
1+
import { enumValue, AuxiliaryFileEncoding } from '@metamask/snaps-utils';
22
import type {
33
PermittedHandlerExport,
44
PendingJsonRpcResponse,
@@ -12,15 +12,13 @@ import { object, optional, string, union } from 'superstruct';
1212

1313
import type { MethodHooksObject } from '../utils';
1414

15-
export enum FileEncoding {
16-
Base64 = 'base64',
17-
Hex = 'hex',
18-
}
19-
2015
export const GetFileArgsStruct = object({
2116
path: string(),
2217
encoding: optional(
23-
union([enumValue(FileEncoding.Base64), enumValue(FileEncoding.Hex)]),
18+
union([
19+
enumValue(AuxiliaryFileEncoding.Base64),
20+
enumValue(AuxiliaryFileEncoding.Hex),
21+
]),
2422
),
2523
});
2624

@@ -76,7 +74,10 @@ async function implementation(
7674
);
7775

7876
try {
79-
res.result = await getSnapFile(params.path, params.encoding);
77+
res.result = await getSnapFile(
78+
params.path,
79+
params.encoding ?? AuxiliaryFileEncoding.Base64,
80+
);
8081
} catch (error) {
8182
return end(error);
8283
}

packages/rpc-methods/src/permitted/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,3 @@ export type PermittedRpcMethodHooks = GetSnapsHooks & RequestSnapsHooks;
55

66
export * from './handlers';
77
export * from './middleware';
8-
export { FileEncoding } from './getFile';

packages/snaps-controllers/coverage.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"branches": 89.51,
33
"functions": 96.2,
44
"lines": 97.09,
5-
"statements": 96.75
5+
"statements": 96.76
66
}

packages/snaps-controllers/src/snaps/SnapController.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type {
1111
ValidatedSnapId,
1212
} from '@metamask/snaps-utils';
1313
import {
14+
AuxiliaryFileEncoding,
1415
DEFAULT_ENDOWMENTS,
1516
DEFAULT_REQUESTED_SNAP_VERSION,
1617
getSnapChecksum,
@@ -5786,6 +5787,45 @@ describe('SnapController', () => {
57865787
MOCK_SNAP_ID,
57875788
'./src/foo.json',
57885789
),
5790+
).toStrictEqual(auxiliaryFile.toString('base64'));
5791+
5792+
snapController.destroy();
5793+
});
5794+
5795+
it('supports hex encoding', async () => {
5796+
const auxiliaryFile = new VirtualFile({
5797+
path: 'src/foo.json',
5798+
value: stringToBytes('{ "foo" : "bar" }'),
5799+
});
5800+
const { manifest, sourceCode, svgIcon, auxiliaryFiles } = getSnapFiles({
5801+
manifest: getSnapManifest({ files: ['./src/foo.json'] }),
5802+
auxiliaryFiles: [auxiliaryFile],
5803+
});
5804+
5805+
const messenger = getSnapControllerMessenger();
5806+
5807+
const snapController = getSnapController(
5808+
getSnapControllerOptions({
5809+
messenger,
5810+
detectSnapLocation: loopbackDetect({
5811+
manifest,
5812+
files: [sourceCode, svgIcon as VirtualFile, ...auxiliaryFiles],
5813+
}),
5814+
}),
5815+
);
5816+
5817+
// By installing we also indirectly test that the unpacking of the file works.
5818+
await snapController.installSnaps(MOCK_ORIGIN, {
5819+
[MOCK_SNAP_ID]: {},
5820+
});
5821+
5822+
expect(
5823+
await messenger.call(
5824+
'SnapController:getFile',
5825+
MOCK_SNAP_ID,
5826+
'./src/foo.json',
5827+
AuxiliaryFileEncoding.Hex,
5828+
),
57895829
).toStrictEqual(auxiliaryFile.toString('hex'));
57905830

57915831
snapController.destroy();

packages/snaps-controllers/src/snaps/SnapController.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,7 @@ import type {
2424
ValidPermission,
2525
} from '@metamask/permission-controller';
2626
import { SubjectType } from '@metamask/permission-controller';
27-
import {
28-
WALLET_SNAP_PERMISSION_KEY,
29-
FileEncoding,
30-
} from '@metamask/rpc-methods';
27+
import { WALLET_SNAP_PERMISSION_KEY } from '@metamask/rpc-methods';
3128
import type { BlockReason } from '@metamask/snaps-registry';
3229
import type {
3330
FetchedSnapFiles,
@@ -49,8 +46,10 @@ import type {
4946
import {
5047
assertIsSnapManifest,
5148
assertIsValidSnapId,
49+
AuxiliaryFileEncoding,
5250
DEFAULT_ENDOWMENTS,
5351
DEFAULT_REQUESTED_SNAP_VERSION,
52+
encodeAuxiliaryFile,
5453
getErrorMessage,
5554
HandlerType,
5655
isOriginAllowed,
@@ -92,12 +91,7 @@ import type {
9291
TerminateAllSnapsAction,
9392
TerminateSnapAction,
9493
} from '../services';
95-
import {
96-
encodeAuxiliaryFile,
97-
hasTimedOut,
98-
setDiff,
99-
withTimeout,
100-
} from '../utils';
94+
import { hasTimedOut, setDiff, withTimeout } from '../utils';
10195
import { handlerEndowments, SnapEndowments } from './endowments';
10296
import { getKeyringCaveatOrigins } from './endowments/keyring';
10397
import { getRpcCaveatOrigins } from './endowments/rpc';
@@ -1405,7 +1399,7 @@ export class SnapController extends BaseController<
14051399
getSnapFile(
14061400
snapId: ValidatedSnapId,
14071401
path: string,
1408-
encoding: FileEncoding = FileEncoding.Base64,
1402+
encoding: AuxiliaryFileEncoding = AuxiliaryFileEncoding.Base64,
14091403
): string | null {
14101404
const snap = this.getExpect(snapId);
14111405
const normalizedPath = normalizeRelative(path);

packages/snaps-controllers/src/utils.ts

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
import { FileEncoding } from '@metamask/rpc-methods';
2-
import { bytesToHex } from '@metamask/utils';
3-
import { base64 } from '@scure/base';
4-
51
import { Timer } from './snaps/Timer';
62

73
/**
@@ -202,21 +198,3 @@ export type Mutable<
202198
} & {
203199
[Key in keyof Omit<Type, TargetKey>]: Type[Key];
204200
};
205-
206-
/**
207-
* Re-encodes an auxiliary file if needed depending on the requested file encoding.
208-
*
209-
* @param value - The base64 value stored for the auxiliary file.
210-
* @param encoding - The chosen encoding.
211-
* @returns The file encoded in the requested encoding.
212-
*/
213-
export function encodeAuxiliaryFile(value: string, encoding: FileEncoding) {
214-
// Input is assumed to be the stored file in base64.
215-
if (encoding === FileEncoding.Base64) {
216-
return value;
217-
}
218-
219-
// For now, the requested encoding here will always be hex.
220-
const decoded = base64.decode(value);
221-
return bytesToHex(decoded);
222-
}

packages/snaps-simulator/src/features/simulation/hooks.test.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { DialogType } from '@metamask/rpc-methods';
22
import { text } from '@metamask/snaps-ui';
3-
import { VirtualFile, normalizeRelative } from '@metamask/snaps-utils';
3+
import {
4+
AuxiliaryFileEncoding,
5+
VirtualFile,
6+
normalizeRelative,
7+
} from '@metamask/snaps-utils';
48
import { stringToBytes } from '@metamask/utils';
59
import { expectSaga } from 'redux-saga-test-plan';
610

@@ -154,9 +158,27 @@ describe('getSnapState', () => {
154158
});
155159

156160
describe('getSnapFile', () => {
157-
it('returns the requested file in hexadecimal', async () => {
161+
it('returns the requested file in base64 by default', async () => {
158162
const path = './src/foo.json';
159163
await expectSaga(getSnapFile, path)
164+
.withState({
165+
simulation: {
166+
auxiliaryFiles: [
167+
new VirtualFile({
168+
path: normalizeRelative(path),
169+
value: stringToBytes(JSON.stringify({ foo: 'bar' })),
170+
}),
171+
],
172+
},
173+
})
174+
.select(getAuxiliaryFiles)
175+
.returns('eyJmb28iOiJiYXIifQ==')
176+
.silentRun();
177+
});
178+
179+
it('returns the requested file in hex when requested', async () => {
180+
const path = './src/foo.json';
181+
await expectSaga(getSnapFile, path, AuxiliaryFileEncoding.Hex)
160182
.withState({
161183
simulation: {
162184
auxiliaryFiles: [

packages/snaps-simulator/src/features/simulation/hooks.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import type { DialogType, NotificationArgs } from '@metamask/rpc-methods';
22
import type { Component } from '@metamask/snaps-ui';
33
import type { VirtualFile } from '@metamask/snaps-utils';
4-
import { normalizeRelative } from '@metamask/snaps-utils';
4+
import {
5+
AuxiliaryFileEncoding,
6+
encodeAuxiliaryFile,
7+
normalizeRelative,
8+
} from '@metamask/snaps-utils';
59
import { nanoid } from '@reduxjs/toolkit';
610
import type { SagaIterator } from 'redux-saga';
711
import { call, put, select, take } from 'redux-saga/effects';
@@ -164,13 +168,21 @@ export function* getSnapState(_snapId: string): SagaIterator {
164168
* Usually these would be stored in the SnapController.
165169
*
166170
* @param path - The file path.
171+
* @param encoding - The requested file encoding.
167172
* @returns The file in hexadecimal if found, otherwise null.
168173
* @yields Selects the state from the simulation slice.
169174
*/
170-
export function* getSnapFile(path: string): SagaIterator {
175+
export function* getSnapFile(
176+
path: string,
177+
encoding: AuxiliaryFileEncoding = AuxiliaryFileEncoding.Base64,
178+
): SagaIterator {
171179
const files: VirtualFile[] = yield select(getAuxiliaryFiles);
172180
const normalizedPath = normalizeRelative(path);
173-
return (
174-
files.find((file) => file.path === normalizedPath)?.toString('hex') ?? null
175-
);
181+
const base64 = files
182+
.find((file) => file.path === normalizedPath)
183+
?.toString('base64');
184+
if (!base64) {
185+
return null;
186+
}
187+
return encodeAuxiliaryFile(base64, encoding);
176188
}

packages/snaps-utils/coverage.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"branches": 95.38,
2+
"branches": 95.5,
33
"functions": 100,
4-
"lines": 98.7,
5-
"statements": 95.75
4+
"lines": 98.72,
5+
"statements": 95.81
66
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { bytesToHex, stringToBytes } from '@metamask/utils';
2+
import { base64 } from '@scure/base';
3+
4+
import { AuxiliaryFileEncoding, encodeAuxiliaryFile } from './auxiliaryFiles';
5+
6+
describe('encodeAuxiliaryFile', () => {
7+
it('returns value without modifying it for base64', () => {
8+
const value = base64.encode(stringToBytes('foo'));
9+
expect(
10+
encodeAuxiliaryFile(value, AuxiliaryFileEncoding.Base64),
11+
).toStrictEqual(value);
12+
});
13+
14+
it('re-encodes to hex when requested', () => {
15+
const bytes = stringToBytes('foo');
16+
const value = base64.encode(bytes);
17+
expect(encodeAuxiliaryFile(value, AuxiliaryFileEncoding.Hex)).toStrictEqual(
18+
bytesToHex(bytes),
19+
);
20+
});
21+
});

0 commit comments

Comments
 (0)