79 lines
2.7 KiB
TypeScript
79 lines
2.7 KiB
TypeScript
import { describe, expect, it, vi } from 'vitest';
|
|
import type { BrowserContext } from 'playwright';
|
|
import { createMockLogger } from './mockLogger.js';
|
|
import {
|
|
MatrixRemoteLoginRequiredError,
|
|
MatrixTokenRefresher,
|
|
} from '../src/services/chat/matrix/token-refresher.js';
|
|
import type { MatrixCredentials } from '../src/services/chat/matrix/types.js';
|
|
|
|
const credentials: MatrixCredentials = {
|
|
homeserver: 'https://matrix.redditspace.com',
|
|
accessToken: 'fresh-token',
|
|
userId: '@t2_bot:reddit.com',
|
|
capturedAt: 123,
|
|
};
|
|
|
|
function setup(options: { sessionValid?: boolean; loginSucceeds?: boolean } = {}) {
|
|
const context = {} as BrowserContext;
|
|
const browser = {
|
|
getContext: vi.fn(() => context),
|
|
isSessionValid: vi.fn(async () => options.sessionValid ?? true),
|
|
login: vi.fn(async () => options.loginSucceeds ?? true),
|
|
};
|
|
const extract = vi.fn(async () => credentials);
|
|
const applyCredentials = vi.fn();
|
|
const logger = createMockLogger();
|
|
const refresher = new MatrixTokenRefresher(
|
|
browser,
|
|
logger,
|
|
applyCredentials,
|
|
extract,
|
|
);
|
|
|
|
return { refresher, browser, context, extract, applyCredentials, logger };
|
|
}
|
|
|
|
describe('MatrixTokenRefresher', () => {
|
|
it('repairs an expired Reddit browser session before capturing a Matrix token', async () => {
|
|
const { refresher, browser, context, extract, applyCredentials } = setup({
|
|
sessionValid: false,
|
|
loginSucceeds: true,
|
|
});
|
|
|
|
await refresher.refresh();
|
|
|
|
expect(browser.login).toHaveBeenCalledOnce();
|
|
expect(extract).toHaveBeenCalledWith(context, expect.anything());
|
|
expect(applyCredentials).toHaveBeenCalledWith(credentials);
|
|
});
|
|
|
|
it('fails fast with remote-login guidance when the Reddit session cannot be restored', async () => {
|
|
const { refresher, browser, extract } = setup({
|
|
sessionValid: false,
|
|
loginSucceeds: false,
|
|
});
|
|
|
|
await expect(refresher.refresh()).rejects.toBeInstanceOf(MatrixRemoteLoginRequiredError);
|
|
await expect(refresher.refresh()).rejects.toThrow(/dashboard.*noVNC/i);
|
|
expect(browser.login).toHaveBeenCalledTimes(2);
|
|
expect(extract).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('coalesces concurrent refreshes so only one browser capture runs', async () => {
|
|
const { refresher, extract, applyCredentials } = setup();
|
|
let release: ((value: MatrixCredentials) => void) | undefined;
|
|
extract.mockImplementation(
|
|
() => new Promise<MatrixCredentials>((resolve) => { release = resolve; }),
|
|
);
|
|
|
|
const first = refresher.refresh();
|
|
const second = refresher.refresh();
|
|
await vi.waitFor(() => expect(extract).toHaveBeenCalledOnce());
|
|
release?.(credentials);
|
|
await Promise.all([first, second]);
|
|
|
|
expect(applyCredentials).toHaveBeenCalledOnce();
|
|
});
|
|
});
|