Skip to content

Commit

Permalink
Fix tests and lint
Browse files Browse the repository at this point in the history
  • Loading branch information
FrederikBolding committed Oct 16, 2023
1 parent 00214e6 commit 84c43c4
Show file tree
Hide file tree
Showing 18 changed files with 209 additions and 60 deletions.
1 change: 0 additions & 1 deletion packages/rpc-methods/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ export type { PermittedRpcMethodHooks } from './permitted';
export {
handlers as permittedMethods,
createSnapsMethodMiddleware,
FileEncoding,
} from './permitted';
export * from './restricted';
export { SnapCaveatType } from '@metamask/snaps-utils';
Expand Down
59 changes: 55 additions & 4 deletions packages/rpc-methods/src/permitted/getFile.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { AuxiliaryFileEncoding, VirtualFile } from '@metamask/snaps-utils';
import type {
JsonRpcRequest,
PendingJsonRpcResponse,
JsonRpcFailure,
JsonRpcSuccess,
} from '@metamask/types';
import { stringToBytes, bytesToHex } from '@metamask/utils';
import { stringToBytes } from '@metamask/utils';
import { JsonRpcEngine } from 'json-rpc-engine';

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

const hooks = getMockHooks();

const hexadecimal = bytesToHex(
const vfile = new VirtualFile(
stringToBytes(JSON.stringify({ foo: 'bar' })),
);
const base64 = vfile.toString('base64');
(
hooks.getSnapFile as jest.MockedFunction<typeof hooks.getSnapFile>
).mockImplementation(async (_path: string) => base64);

const engine = new JsonRpcEngine();
engine.push((req, res, next, end) => {
const result = implementation(
req as JsonRpcRequest<JsonRpcRequest<unknown>>,
res as PendingJsonRpcResponse<unknown>,
next,
end,
hooks,
);

result?.catch(end);
});

const response = (await engine.handle({
jsonrpc: '2.0',
id: 1,
method: 'snap_getFile',
params: {
path: './src/foo.json',
},
})) as JsonRpcSuccess<string>;

expect(response.result).toBe(base64);
expect(hooks.getSnapFile).toHaveBeenCalledWith(
'./src/foo.json',
AuxiliaryFileEncoding.Base64,
);
});

it('supports hex in encoding parameter', async () => {
const { implementation } = getFileHandler;

const hooks = getMockHooks();

const vfile = new VirtualFile(
stringToBytes(JSON.stringify({ foo: 'bar' })),
);
const hexadecimal = vfile.toString('hex');
(
hooks.getSnapFile as jest.MockedFunction<typeof hooks.getSnapFile>
).mockImplementation(async (_path: string) => hexadecimal);
Expand All @@ -60,11 +104,15 @@ describe('snap_getFile', () => {
method: 'snap_getFile',
params: {
path: './src/foo.json',
encoding: 'hex',
},
})) as JsonRpcSuccess<string>;

expect(response.result).toBe(hexadecimal);
expect(hooks.getSnapFile).toHaveBeenCalledWith('./src/foo.json');
expect(hooks.getSnapFile).toHaveBeenCalledWith(
'./src/foo.json',
AuxiliaryFileEncoding.Hex,
);
});

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

expect(response.error.message).toBe('foo bar');
expect(hooks.getSnapFile).toHaveBeenCalledWith('./src/foo.json');
expect(hooks.getSnapFile).toHaveBeenCalledWith(
'./src/foo.json',
AuxiliaryFileEncoding.Base64,
);
});
});
});
17 changes: 9 additions & 8 deletions packages/rpc-methods/src/permitted/getFile.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { enumValue } from '@metamask/snaps-utils';
import { enumValue, AuxiliaryFileEncoding } from '@metamask/snaps-utils';
import type {
PermittedHandlerExport,
PendingJsonRpcResponse,
Expand All @@ -12,15 +12,13 @@ import { object, optional, string, union } from 'superstruct';

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

export enum FileEncoding {
Base64 = 'base64',
Hex = 'hex',
}

export const GetFileArgsStruct = object({
path: string(),
encoding: optional(
union([enumValue(FileEncoding.Base64), enumValue(FileEncoding.Hex)]),
union([
enumValue(AuxiliaryFileEncoding.Base64),
enumValue(AuxiliaryFileEncoding.Hex),
]),
),
});

Expand Down Expand Up @@ -76,7 +74,10 @@ async function implementation(
);

try {
res.result = await getSnapFile(params.path, params.encoding);
res.result = await getSnapFile(
params.path,
params.encoding ?? AuxiliaryFileEncoding.Base64,
);
} catch (error) {
return end(error);
}
Expand Down
1 change: 0 additions & 1 deletion packages/rpc-methods/src/permitted/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@ export type PermittedRpcMethodHooks = GetSnapsHooks & RequestSnapsHooks;

export * from './handlers';
export * from './middleware';
export { FileEncoding } from './getFile';
2 changes: 1 addition & 1 deletion packages/snaps-controllers/coverage.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"branches": 89.51,
"functions": 96.2,
"lines": 97.09,
"statements": 96.75
"statements": 96.76
}
40 changes: 40 additions & 0 deletions packages/snaps-controllers/src/snaps/SnapController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
ValidatedSnapId,
} from '@metamask/snaps-utils';
import {
AuxiliaryFileEncoding,
DEFAULT_ENDOWMENTS,
DEFAULT_REQUESTED_SNAP_VERSION,
getSnapChecksum,
Expand Down Expand Up @@ -5786,6 +5787,45 @@ describe('SnapController', () => {
MOCK_SNAP_ID,
'./src/foo.json',
),
).toStrictEqual(auxiliaryFile.toString('base64'));

snapController.destroy();
});

it('supports hex encoding', async () => {
const auxiliaryFile = new VirtualFile({
path: 'src/foo.json',
value: stringToBytes('{ "foo" : "bar" }'),
});
const { manifest, sourceCode, svgIcon, auxiliaryFiles } = getSnapFiles({
manifest: getSnapManifest({ files: ['./src/foo.json'] }),
auxiliaryFiles: [auxiliaryFile],
});

const messenger = getSnapControllerMessenger();

const snapController = getSnapController(
getSnapControllerOptions({
messenger,
detectSnapLocation: loopbackDetect({
manifest,
files: [sourceCode, svgIcon as VirtualFile, ...auxiliaryFiles],
}),
}),
);

// By installing we also indirectly test that the unpacking of the file works.
await snapController.installSnaps(MOCK_ORIGIN, {
[MOCK_SNAP_ID]: {},
});

expect(
await messenger.call(
'SnapController:getFile',
MOCK_SNAP_ID,
'./src/foo.json',
AuxiliaryFileEncoding.Hex,
),
).toStrictEqual(auxiliaryFile.toString('hex'));

snapController.destroy();
Expand Down
16 changes: 5 additions & 11 deletions packages/snaps-controllers/src/snaps/SnapController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@ import type {
ValidPermission,
} from '@metamask/permission-controller';
import { SubjectType } from '@metamask/permission-controller';
import {
WALLET_SNAP_PERMISSION_KEY,
FileEncoding,
} from '@metamask/rpc-methods';
import { WALLET_SNAP_PERMISSION_KEY } from '@metamask/rpc-methods';
import type { BlockReason } from '@metamask/snaps-registry';
import type {
FetchedSnapFiles,
Expand All @@ -49,8 +46,10 @@ import type {
import {
assertIsSnapManifest,
assertIsValidSnapId,
AuxiliaryFileEncoding,
DEFAULT_ENDOWMENTS,
DEFAULT_REQUESTED_SNAP_VERSION,
encodeAuxiliaryFile,
getErrorMessage,
HandlerType,
isOriginAllowed,
Expand Down Expand Up @@ -92,12 +91,7 @@ import type {
TerminateAllSnapsAction,
TerminateSnapAction,
} from '../services';
import {
encodeAuxiliaryFile,
hasTimedOut,
setDiff,
withTimeout,
} from '../utils';
import { hasTimedOut, setDiff, withTimeout } from '../utils';
import { handlerEndowments, SnapEndowments } from './endowments';
import { getKeyringCaveatOrigins } from './endowments/keyring';
import { getRpcCaveatOrigins } from './endowments/rpc';
Expand Down Expand Up @@ -1405,7 +1399,7 @@ export class SnapController extends BaseController<
getSnapFile(
snapId: ValidatedSnapId,
path: string,
encoding: FileEncoding = FileEncoding.Base64,
encoding: AuxiliaryFileEncoding = AuxiliaryFileEncoding.Base64,
): string | null {
const snap = this.getExpect(snapId);
const normalizedPath = normalizeRelative(path);
Expand Down
22 changes: 0 additions & 22 deletions packages/snaps-controllers/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
import { FileEncoding } from '@metamask/rpc-methods';
import { bytesToHex } from '@metamask/utils';
import { base64 } from '@scure/base';

import { Timer } from './snaps/Timer';

/**
Expand Down Expand Up @@ -202,21 +198,3 @@ export type Mutable<
} & {
[Key in keyof Omit<Type, TargetKey>]: Type[Key];
};

/**
* Re-encodes an auxiliary file if needed depending on the requested file encoding.
*
* @param value - The base64 value stored for the auxiliary file.
* @param encoding - The chosen encoding.
* @returns The file encoded in the requested encoding.
*/
export function encodeAuxiliaryFile(value: string, encoding: FileEncoding) {
// Input is assumed to be the stored file in base64.
if (encoding === FileEncoding.Base64) {
return value;
}

// For now, the requested encoding here will always be hex.
const decoded = base64.decode(value);
return bytesToHex(decoded);
}
26 changes: 24 additions & 2 deletions packages/snaps-simulator/src/features/simulation/hooks.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { DialogType } from '@metamask/rpc-methods';
import { text } from '@metamask/snaps-ui';
import { VirtualFile, normalizeRelative } from '@metamask/snaps-utils';
import {
AuxiliaryFileEncoding,
VirtualFile,
normalizeRelative,
} from '@metamask/snaps-utils';
import { stringToBytes } from '@metamask/utils';
import { expectSaga } from 'redux-saga-test-plan';

Expand Down Expand Up @@ -154,9 +158,27 @@ describe('getSnapState', () => {
});

describe('getSnapFile', () => {
it('returns the requested file in hexadecimal', async () => {
it('returns the requested file in base64 by default', async () => {
const path = './src/foo.json';
await expectSaga(getSnapFile, path)
.withState({
simulation: {
auxiliaryFiles: [
new VirtualFile({
path: normalizeRelative(path),
value: stringToBytes(JSON.stringify({ foo: 'bar' })),
}),
],
},
})
.select(getAuxiliaryFiles)
.returns('eyJmb28iOiJiYXIifQ==')
.silentRun();
});

it('returns the requested file in hex when requested', async () => {
const path = './src/foo.json';
await expectSaga(getSnapFile, path, AuxiliaryFileEncoding.Hex)
.withState({
simulation: {
auxiliaryFiles: [
Expand Down
22 changes: 17 additions & 5 deletions packages/snaps-simulator/src/features/simulation/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import type { DialogType, NotificationArgs } from '@metamask/rpc-methods';
import type { Component } from '@metamask/snaps-ui';
import type { VirtualFile } from '@metamask/snaps-utils';
import { normalizeRelative } from '@metamask/snaps-utils';
import {
AuxiliaryFileEncoding,
encodeAuxiliaryFile,
normalizeRelative,
} from '@metamask/snaps-utils';
import { nanoid } from '@reduxjs/toolkit';
import type { SagaIterator } from 'redux-saga';
import { call, put, select, take } from 'redux-saga/effects';
Expand Down Expand Up @@ -164,13 +168,21 @@ export function* getSnapState(_snapId: string): SagaIterator {
* Usually these would be stored in the SnapController.
*
* @param path - The file path.
* @param encoding - The requested file encoding.
* @returns The file in hexadecimal if found, otherwise null.
* @yields Selects the state from the simulation slice.
*/
export function* getSnapFile(path: string): SagaIterator {
export function* getSnapFile(
path: string,
encoding: AuxiliaryFileEncoding = AuxiliaryFileEncoding.Base64,
): SagaIterator {
const files: VirtualFile[] = yield select(getAuxiliaryFiles);
const normalizedPath = normalizeRelative(path);
return (
files.find((file) => file.path === normalizedPath)?.toString('hex') ?? null
);
const base64 = files
.find((file) => file.path === normalizedPath)
?.toString('base64');
if (!base64) {
return null;
}
return encodeAuxiliaryFile(base64, encoding);
}
6 changes: 3 additions & 3 deletions packages/snaps-utils/coverage.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"branches": 95.38,
"branches": 95.5,
"functions": 100,
"lines": 98.7,
"statements": 95.75
"lines": 98.72,
"statements": 95.81
}
21 changes: 21 additions & 0 deletions packages/snaps-utils/src/auxiliaryFiles.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { bytesToHex, stringToBytes } from '@metamask/utils';
import { base64 } from '@scure/base';

import { AuxiliaryFileEncoding, encodeAuxiliaryFile } from './auxiliaryFiles';

describe('encodeAuxiliaryFile', () => {
it('returns value without modifying it for base64', () => {
const value = base64.encode(stringToBytes('foo'));
expect(
encodeAuxiliaryFile(value, AuxiliaryFileEncoding.Base64),
).toStrictEqual(value);
});

it('re-encodes to hex when requested', () => {
const bytes = stringToBytes('foo');
const value = base64.encode(bytes);
expect(encodeAuxiliaryFile(value, AuxiliaryFileEncoding.Hex)).toStrictEqual(
bytesToHex(bytes),
);
});
});
Loading

0 comments on commit 84c43c4

Please sign in to comment.