Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { describe, it, expect } from 'vitest';

import { clearStateHandler, clearStateSpec } from './clear-state.ts';
import {
collectGarbageHandler,
collectGarbageSpec,
} from './collect-garbage.ts';
import {
executeDBQueryHandler,
executeDBQuerySpec,
} from './execute-db-query.ts';
import { getStatusHandler, getStatusSpec } from './get-status.ts';
import { handlers, methodSpecs } from './index.ts';
import { launchVatHandler, launchVatSpec } from './launch-vat.ts';
import { pingVatHandler, pingVatSpec } from './ping-vat.ts';
import { queueMessageHandler, queueMessageSpec } from './queue-message.ts';
import { reloadConfigHandler, reloadConfigSpec } from './reload-config.ts';
import { restartVatHandler, restartVatSpec } from './restart-vat.ts';
import {
terminateAllVatsHandler,
terminateAllVatsSpec,
} from './terminate-all-vats.ts';
import { terminateVatHandler, terminateVatSpec } from './terminate-vat.ts';
import {
updateClusterConfigHandler,
updateClusterConfigSpec,
} from './update-cluster-config.ts';

describe('handlers/index', () => {
it('should export all handler functions', () => {
expect(handlers).toStrictEqual({
clearState: clearStateHandler,
executeDBQuery: executeDBQueryHandler,
getStatus: getStatusHandler,
launchVat: launchVatHandler,
pingVat: pingVatHandler,
reload: reloadConfigHandler,
restartVat: restartVatHandler,
queueMessage: queueMessageHandler,
terminateAllVats: terminateAllVatsHandler,
collectGarbage: collectGarbageHandler,
terminateVat: terminateVatHandler,
updateClusterConfig: updateClusterConfigHandler,
});
});

it('should have all handlers with the correct method property', () => {
const handlerEntries = Object.entries(handlers);

handlerEntries.forEach(([key, handler]) => {
expect(handler).toHaveProperty('method');
expect(handler.method).toBe(key);
});
});

it('should export all method specs', () => {
expect(methodSpecs).toStrictEqual({
clearState: clearStateSpec,
executeDBQuery: executeDBQuerySpec,
getStatus: getStatusSpec,
launchVat: launchVatSpec,
pingVat: pingVatSpec,
reload: reloadConfigSpec,
restartVat: restartVatSpec,
queueMessage: queueMessageSpec,
terminateAllVats: terminateAllVatsSpec,
collectGarbage: collectGarbageSpec,
terminateVat: terminateVatSpec,
updateClusterConfig: updateClusterConfigSpec,
});
});

it('should have the same keys as handlers', () => {
expect(Object.keys(methodSpecs).sort()).toStrictEqual(
Object.keys(handlers).sort(),
);
});
});
3 changes: 3 additions & 0 deletions packages/extension/src/kernel-integration/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from './execute-db-query.ts';
import { getStatusHandler, getStatusSpec } from './get-status.ts';
import { launchVatHandler, launchVatSpec } from './launch-vat.ts';
import { pingVatHandler, pingVatSpec } from './ping-vat.ts';
import { queueMessageHandler, queueMessageSpec } from './queue-message.ts';
import { reloadConfigHandler, reloadConfigSpec } from './reload-config.ts';
import { restartVatHandler, restartVatSpec } from './restart-vat.ts';
Expand All @@ -30,6 +31,7 @@ export const handlers = {
executeDBQuery: executeDBQueryHandler,
getStatus: getStatusHandler,
launchVat: launchVatHandler,
pingVat: pingVatHandler,
reload: reloadConfigHandler,
restartVat: restartVatHandler,
queueMessage: queueMessageHandler,
Expand All @@ -47,6 +49,7 @@ export const methodSpecs = {
executeDBQuery: executeDBQuerySpec,
getStatus: getStatusSpec,
launchVat: launchVatSpec,
pingVat: pingVatSpec,
reload: reloadConfigSpec,
restartVat: restartVatSpec,
queueMessage: queueMessageSpec,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { Kernel } from '@metamask/ocap-kernel';
import { describe, it, expect, vi, beforeEach } from 'vitest';

import { pingVatHandler } from './ping-vat.ts';

describe('pingVatHandler', () => {
let mockKernel: Kernel;

beforeEach(() => {
mockKernel = {
pingVat: vi.fn().mockResolvedValue('pong'),
} as unknown as Kernel;
});

it('should ping vat and return result', async () => {
const params = { id: 'v0' } as const;
const result = await pingVatHandler.implementation(
{ kernel: mockKernel },
params,
);

expect(mockKernel.pingVat).toHaveBeenCalledWith(params.id);
expect(result).toBe('pong');
});

it('should propagate errors from kernel.pingVat', async () => {
const error = new Error('Ping failed');
vi.mocked(mockKernel.pingVat).mockRejectedValueOnce(error);

const params = { id: 'v0' } as const;
await expect(
pingVatHandler.implementation({ kernel: mockKernel }, params),
).rejects.toThrow(error);
});
});
29 changes: 29 additions & 0 deletions packages/extension/src/kernel-integration/handlers/ping-vat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { Handler, MethodSpec } from '@metamask/kernel-rpc-methods';
import type { Kernel, VatId } from '@metamask/ocap-kernel';
import { VatIdStruct } from '@metamask/ocap-kernel';
import { vatMethodSpecs } from '@metamask/ocap-kernel/rpc';
import type { PingVatResult } from '@metamask/ocap-kernel/rpc';
import { object } from '@metamask/superstruct';

export type PingVatHooks = {
kernel: Kernel;
};

export const pingVatSpec: MethodSpec<'pingVat', { id: VatId }, string> = {
method: 'pingVat',
params: object({ id: VatIdStruct }),
result: vatMethodSpecs.ping.result,
};

export const pingVatHandler: Handler<
'pingVat',
{ id: VatId },
Promise<PingVatResult>,
PingVatHooks
> = {
...pingVatSpec,
hooks: { kernel: true },
implementation: async ({ kernel }, params): Promise<PingVatResult> => {
return kernel.pingVat(params.id);
},
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { JsonRpcEngine } from '@metamask/json-rpc-engine';
import type { KernelDatabase } from '@metamask/kernel-store';
import type { Kernel } from '@metamask/ocap-kernel';
import type { ClusterConfig, Kernel } from '@metamask/ocap-kernel';
import type { JsonRpcRequest } from '@metamask/utils';
import { describe, it, expect, vi, beforeEach } from 'vitest';

Expand All @@ -13,16 +13,51 @@ const { mockAssertHasMethod, mockExecute } = vi.hoisted(() => ({

vi.mock('@metamask/kernel-rpc-methods', () => ({
RpcService: class MockRpcService {
readonly #dependencies: Record<string, unknown>;

constructor(
_handlers: Record<string, unknown>,
dependencies: Record<string, unknown>,
) {
this.#dependencies = dependencies;
}

assertHasMethod = mockAssertHasMethod;

execute = mockExecute;
execute = (method: string, params: unknown) => {
// For updateClusterConfig test, call the actual implementation
if (method === 'updateClusterConfig' && params) {
const updateFn = this.#dependencies.updateClusterConfig as (
config: unknown,
) => void;
updateFn(params);
return Promise.resolve();
}

// For executeDBQuery test, call the actual implementation
if (
method === 'executeDBQuery' &&
typeof params === 'object' &&
params !== null
) {
const { sql } = params as { sql: string };
const executeQueryFn = this.#dependencies.executeDBQuery as (
sql: string,
) => Promise<unknown>;
return executeQueryFn(sql);
}

return mockExecute(method, params);
};
},
}));

vi.mock('../handlers/index.ts', () => ({
handlers: {
testMethod1: { method: 'testMethod1' },
testMethod2: { method: 'testMethod2' },
updateClusterConfig: { method: 'updateClusterConfig' },
executeDBQuery: { method: 'executeDBQuery' },
},
}));

Expand All @@ -33,8 +68,12 @@ describe('createPanelMessageMiddleware', () => {

beforeEach(() => {
// Set up mocks
mockKernel = {} as Kernel;
mockKernelDatabase = {} as KernelDatabase;
mockKernel = {
clusterConfig: {} as ClusterConfig,
} as Kernel;
mockKernelDatabase = {
executeQuery: vi.fn(),
} as unknown as KernelDatabase;

// Create a new JSON-RPC engine with our middleware
engine = new JsonRpcEngine();
Expand Down Expand Up @@ -209,4 +248,63 @@ describe('createPanelMessageMiddleware', () => {
}),
});
});

it('should update kernel.clusterConfig when updateClusterConfig is called', async () => {
// Create a test cluster config that matches the expected structure
const testConfig = {
bootstrap: 'test-bootstrap',
vats: {
test: {
bundleSpec: 'test-bundle',
},
},
forceReset: true,
} as ClusterConfig;

// Create a request to update cluster config
const request = {
id: 7,
jsonrpc: '2.0',
method: 'updateClusterConfig',
params: testConfig,
} as JsonRpcRequest;

// Process the request
await engine.handle(request);

// Verify that kernel.clusterConfig was updated with the provided config
expect(mockKernel.clusterConfig).toStrictEqual(testConfig);
});

it('should call kernelDatabase.executeQuery when executeDBQuery is called', async () => {
// Set up mock database response
const mockQueryResult = [{ id: '1', name: 'test' }];
vi.mocked(mockKernelDatabase.executeQuery).mockResolvedValueOnce(
mockQueryResult,
);

// Test SQL query
const testSql = 'SELECT * FROM test_table';

// Create a request to execute DB query
const request = {
id: 8,
jsonrpc: '2.0',
method: 'executeDBQuery',
params: { sql: testSql },
} as JsonRpcRequest;

// Process the request
const response = await engine.handle(request);

// Verify that kernelDatabase.executeQuery was called with the correct SQL
expect(mockKernelDatabase.executeQuery).toHaveBeenCalledWith(testSql);

// Verify the response contains the query result
expect(response).toStrictEqual({
id: 8,
jsonrpc: '2.0',
result: mockQueryResult,
});
});
});
Loading
Loading