Skip to content

Commit

Permalink
fix(MessageProxy): timeout channels list (#2036)
Browse files Browse the repository at this point in the history
* fix(MessageProxy): timeout channels list

Signed-off-by: axel7083 <[email protected]>

* fix: tests

Signed-off-by: axel7083 <[email protected]>

* fix: tests

Signed-off-by: axel7083 <[email protected]>

---------

Signed-off-by: axel7083 <[email protected]>
  • Loading branch information
axel7083 authored Nov 4, 2024
1 parent 57ecc27 commit 599ef6f
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 16 deletions.
4 changes: 4 additions & 0 deletions packages/backend/src/studio.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ import * as fs from 'node:fs';

vi.mock('./managers/modelsManager');

vi.mock('@shared/src/messages/NoTimeoutChannels', () => ({
noTimeoutChannels: [],
}));

const mockedExtensionContext = {
subscriptions: [],
storagePath: 'dummy-storage-path',
Expand Down
4 changes: 4 additions & 0 deletions packages/frontend/src/stores/rpcReadable.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ vi.mock('../utils/client', async () => {
};
});

vi.mock('@shared/src/messages/NoTimeoutChannels', () => ({
noTimeoutChannels: [],
}));

beforeEach(() => {
vi.clearAllMocks();
});
Expand Down
105 changes: 92 additions & 13 deletions packages/shared/src/messages/MessageProxy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,20 @@
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/

import { test, expect, beforeAll, vi } from 'vitest';
import { RpcBrowser, RpcExtension } from './MessageProxy';
import { test, expect, vi, describe, beforeEach, afterEach } from 'vitest';
import { getChannel, RpcBrowser, RpcExtension } from './MessageProxy';
import type { Webview } from '@podman-desktop/api';
import * as defaultNoTimeoutChannels from './NoTimeoutChannels';

let webview: Webview;
let window: Window;
let api: PodmanDesktopApi;

beforeAll(() => {
vi.mock('./NoTimeoutChannels', async () => ({
noTimeoutChannels: [],
}));

beforeEach(() => {
let windowListener: (message: unknown) => void;
let webviewListener: (message: unknown) => void;

Expand All @@ -42,7 +47,6 @@ beforeAll(() => {
expect(channel).toBe('message');
windowListener = listener;
},
setTimeout: vi.fn(),
} as unknown as Window;

api = {
Expand Down Expand Up @@ -170,16 +174,91 @@ test('Test raising exception', async () => {
await expect(rpcBrowser.invoke('raiseError')).rejects.toThrow('big error');
});

test('A noTimeoutChannel should not call the setTimeout', async () => {
const rpcExtension = new RpcExtension(webview);
rpcExtension.init();
const rpcBrowser = new RpcBrowser(window, api);
test('getChannel should use CHANNEL property of classType provided', () => {
class Dummy {
static readonly CHANNEL: string = 'dummy';
async ping(): Promise<'pong'> {
return new Promise(vi.fn());
}
}

const channel = getChannel(Dummy, 'ping');
expect(channel).toBe('dummy-ping');
});

describe('no timeout channel', () => {
beforeEach(() => {
vi.resetAllMocks();
vi.useFakeTimers();

rpcExtension.register('openDialog', () => {
return Promise.resolve();
(defaultNoTimeoutChannels.noTimeoutChannels as string[]) = [];
});
const setTimeoutMock = vi.spyOn(window, 'setTimeout');

await rpcBrowser.invoke('openDialog');
expect(setTimeoutMock).not.toHaveBeenCalled();
afterEach(() => {
vi.restoreAllMocks();
});

test('default function should have a timeout', async () => {
class Dummy {
static readonly CHANNEL: string = 'dummy';
async ping(): Promise<'pong'> {
return new Promise(vi.fn());
}
}

const rpcExtension = new RpcExtension(webview);
rpcExtension.init();
const rpcBrowser = new RpcBrowser(window, api);

rpcExtension.registerInstance(Dummy, new Dummy());

const proxy = rpcBrowser.getProxy<Dummy>(Dummy);

let error: Error | undefined;
proxy.ping().catch((err: unknown) => {
error = err as Error;
});

await vi.advanceTimersByTimeAsync(5_000);
expect(error?.message).toBe('Timeout');
});

test('noTimeoutChannels should not have a timeout', async () => {
class Dummy {
static readonly CHANNEL: string = 'dummy';
async ping(): Promise<'pong'> {
return new Promise(resolve => {
setTimeout(resolve.bind(undefined, 'pong'), 8_000);
});
}
}

// fake the noTimeoutChannels
(defaultNoTimeoutChannels.noTimeoutChannels as string[]) = [`${Dummy.CHANNEL}-ping`];

const rpcExtension = new RpcExtension(webview);
rpcExtension.init();
const rpcBrowser = new RpcBrowser(window, api);

rpcExtension.registerInstance(Dummy, new Dummy());

const proxy = rpcBrowser.getProxy<Dummy>(Dummy);

let error: Error | undefined;
let result: 'pong' | undefined;
proxy
.ping()
.then(mResult => {
result = mResult;
})
.catch((err: unknown) => {
error = err as Error;
});

await vi.advanceTimersByTimeAsync(5_000);
expect(error).toBeUndefined();
await vi.advanceTimersByTimeAsync(5_000);
expect(error).toBeUndefined();
expect(result).toBe('pong');
});
});
8 changes: 6 additions & 2 deletions packages/shared/src/messages/MessageProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ export interface ISubscribedMessage {
body: any;
}

export function getChannel<T>(classType: { CHANNEL: string; prototype: T }, method: keyof T): string {
return `${classType.CHANNEL}-${String(method)}`;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type UnaryRPC = (...args: any[]) => Promise<unknown>;

Expand Down Expand Up @@ -115,7 +119,7 @@ export class RpcExtension implements Disposable {

methodNames.forEach(name => {
const method = (instance[name as keyof T] as any).bind(instance);
this.register(`${classType.CHANNEL}-${name}`, method);
this.register(getChannel(classType, name as keyof T), method);
});
}

Expand Down Expand Up @@ -180,7 +184,7 @@ export class RpcBrowser {
if (typeof prop === 'string') {
return (...args: unknown[]) => {
const channel = prop.toString();
return thisRef.invoke(`${classType.CHANNEL}-${channel}`, ...args);
return thisRef.invoke(getChannel(classType, channel as keyof T), ...args);
};
}
return Reflect.get(target, prop, receiver);
Expand Down
4 changes: 3 additions & 1 deletion packages/shared/src/messages/NoTimeoutChannels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
import { StudioAPI } from '../StudioAPI';
import { getChannel } from './MessageProxy';

export const noTimeoutChannels: string[] = ['openDialog'];
export const noTimeoutChannels: string[] = [getChannel(StudioAPI, 'openDialog')];

0 comments on commit 599ef6f

Please sign in to comment.