Skip to content

Commit

Permalink
Add tests for device API methods
Browse files Browse the repository at this point in the history
  • Loading branch information
Mrtenz committed Nov 11, 2024
1 parent 82d286c commit f6875d6
Show file tree
Hide file tree
Showing 13 changed files with 863 additions and 637 deletions.
227 changes: 227 additions & 0 deletions packages/snaps-rpc-methods/src/endowments/devices.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import type { PermissionConstraint } from '@metamask/permission-controller';
import { PermissionType, SubjectType } from '@metamask/permission-controller';
import { SnapCaveatType } from '@metamask/snaps-utils';

import {
getPermittedDeviceIds,
devicesEndowmentBuilder,
validateDeviceIdsCaveat,
deviceIdsCaveatSpecifications,
} from './devices';
import { SnapEndowments } from './enum';

describe('endowment:devices', () => {
const specification = devicesEndowmentBuilder.specificationBuilder({});

it('builds the expected permission specification', () => {
expect(specification).toStrictEqual({
permissionType: PermissionType.Endowment,
targetName: SnapEndowments.Devices,
endowmentGetter: expect.any(Function),
allowedCaveats: [SnapCaveatType.DeviceIds],
subjectTypes: [SubjectType.Snap],
validator: expect.any(Function),
});

expect(specification.endowmentGetter()).toBeNull();
});

describe('validator', () => {
it('allows no caveats', () => {
expect(() =>
// @ts-expect-error Missing required permission types.
specification.validator({}),
).not.toThrow();
});

it('throws if the caveats are not one or both of "chainIds" and "lookupMatchers".', () => {
expect(() =>
// @ts-expect-error Missing other required permission types.
specification.validator({
caveats: [{ type: 'foo', value: 'bar' }],
}),
).toThrow('Expected the following caveats: "deviceIds", received "foo".');

expect(() =>
// @ts-expect-error Missing other required permission types.
specification.validator({
caveats: [
{ type: 'chainIds', value: ['foo'] },
{ type: 'chainIds', value: ['bar'] },
],
}),
).toThrow('Duplicate caveats are not allowed.');
});
});
});

describe('getPermittedDeviceIds', () => {
it('returns the value from the `endowment:devices` permission', () => {
const permission: PermissionConstraint = {
date: 0,
parentCapability: 'foo',
invoker: 'bar',
id: 'baz',
caveats: [
{
type: SnapCaveatType.DeviceIds,
value: {
devices: [
{
deviceId: 'hid:123:456',
},
],
},
},
],
};

expect(getPermittedDeviceIds(permission)).toStrictEqual([
{
deviceId: 'hid:123:456',
},
]);
});

it('returns `null` if the input is `undefined`', () => {
expect(getPermittedDeviceIds(undefined)).toBeNull();
});

it('returns `null` if the permission does not have caveats', () => {
const permission: PermissionConstraint = {
date: 0,
parentCapability: 'foo',
invoker: 'bar',
id: 'baz',
caveats: null,
};

expect(getPermittedDeviceIds(permission)).toBeNull();
});

it(`returns \`null\` if the caveat doesn't have devices`, () => {
const permission: PermissionConstraint = {
date: 0,
parentCapability: 'foo',
invoker: 'bar',
id: 'baz',
caveats: [
{
type: SnapCaveatType.DeviceIds,
value: {},
},
],
};

expect(getPermittedDeviceIds(permission)).toBeNull();
});

it('throws if the caveat is not a `deviceIds` caveat', () => {
const permission: PermissionConstraint = {
date: 0,
parentCapability: 'foo',
invoker: 'bar',
id: 'baz',
caveats: [
{
type: SnapCaveatType.ChainIds,
value: 'foo',
},
],
};

expect(() => getPermittedDeviceIds(permission)).toThrow(
'Assertion failed.',
);
});
});

describe('validateDeviceIdsCaveat', () => {
it('throws if the value is not a plain object', () => {
expect(() =>
// @ts-expect-error Missing required permission types.
validateDeviceIdsCaveat({}),
).toThrow('Expected a plain object.');
});

it('throws if the value does not have a `devices` property', () => {
expect(() =>
// @ts-expect-error Missing required permission types.
validateDeviceIdsCaveat({ value: {} }),
).toThrow('Expected a valid device specification array.');
});

it('throws if the `devices` property is not a valid device specification array', () => {
expect(() =>
// @ts-expect-error Missing required permission types.
validateDeviceIdsCaveat({ value: { devices: 'foo' } }),
).toThrow('Expected a valid device specification array.');
});
});

describe('deviceIdsCaveatSpecifications', () => {
describe('validator', () => {
it('validates the device IDs caveat', () => {
const caveat = {
type: SnapCaveatType.DeviceIds,
value: {
devices: [
{
deviceId: 'hid:123:456',
},
],
},
};

expect(() =>
deviceIdsCaveatSpecifications[SnapCaveatType.DeviceIds]?.validator?.(
caveat,
),
).not.toThrow();
});
});

describe('merger', () => {
it('merges the device IDs from two caveats', () => {
const leftValue = {
devices: [
{
deviceId: 'hid:123:456',
},
],
};
const rightValue = {
devices: [
{
deviceId: 'hid:789:012',
},
],
};

expect(
deviceIdsCaveatSpecifications[SnapCaveatType.DeviceIds]?.merger?.(
leftValue,
rightValue,
),
).toStrictEqual([
{
devices: [
{
deviceId: 'hid:123:456',
},
{
deviceId: 'hid:789:012',
},
],
},
{
devices: [
{
deviceId: 'hid:789:012',
},
],
},
]);
});
});
});
17 changes: 10 additions & 7 deletions packages/snaps-rpc-methods/src/endowments/devices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
EndowmentGetterParams,
PermissionConstraint,
PermissionSpecificationBuilder,
PermissionValidatorConstraint,
ValidPermissionSpecification,
} from '@metamask/permission-controller';
import { PermissionType, SubjectType } from '@metamask/permission-controller';
Expand All @@ -15,6 +16,7 @@ import {
} from '@metamask/snaps-utils';
import { hasProperty, isPlainObject, assert } from '@metamask/utils';

import { createGenericPermissionValidator } from './caveats';
import { SnapEndowments } from './enum';

const permissionName = SnapEndowments.Devices;
Expand All @@ -24,6 +26,7 @@ type DevicesEndowmentSpecification = ValidPermissionSpecification<{
targetName: typeof permissionName;
endowmentGetter: (_options?: any) => null;
allowedCaveats: [SnapCaveatType.DeviceIds];
validator: PermissionValidatorConstraint;
}>;

/**
Expand All @@ -44,6 +47,9 @@ const specificationBuilder: PermissionSpecificationBuilder<
allowedCaveats: [SnapCaveatType.DeviceIds],
endowmentGetter: (_getterOptions?: EndowmentGetterParams) => null,
subjectTypes: [SubjectType.Snap],
validator: createGenericPermissionValidator([
{ type: SnapCaveatType.DeviceIds, optional: true },
]),
};
};

Expand Down Expand Up @@ -99,13 +105,10 @@ export function validateDeviceIdsCaveat(caveat: Caveat<string, any>) {

const { value } = caveat;

if (!hasProperty(value, 'devices') || !isPlainObject(value)) {
throw rpcErrors.invalidParams({
message: 'Expected a plain object.',
});
}

if (!isDeviceSpecificationArray(value.devices)) {
if (
!hasProperty(value, 'devices') ||
!isDeviceSpecificationArray(value.devices)
) {
throw rpcErrors.invalidParams({
message: 'Expected a valid device specification array.',
});
Expand Down
12 changes: 12 additions & 0 deletions packages/snaps-rpc-methods/src/permissions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ describe('buildSnapEndowmentSpecifications', () => {
],
"targetName": "endowment:cronjob",
},
"endowment:devices": {
"allowedCaveats": [
"deviceIds",
],
"endowmentGetter": [Function],
"permissionType": "Endowment",
"subjectTypes": [
"snap",
],
"targetName": "endowment:devices",
"validator": [Function],
},
"endowment:ethereum-provider": {
"allowedCaveats": null,
"endowmentGetter": [Function],
Expand Down
Loading

0 comments on commit f6875d6

Please sign in to comment.