This folder contains a Vitest harness that boots the worker-side MTProto stack
in Node so you can call apiManager.invokeApi(...) from a test.
The live Bot API fixture used to exercise Ephemeral Messages is documented in
../fixtures/ephemeralBot/README.md.
There is no auth flow in Node — you log in once in the browser, export the auth keys, and feed them to the harness as a JSON seed.
-
VITE_API_ID/VITE_API_HASHin.env.localat the repo root (Vitest reads.env*via Vite). Without these the harness throws. -
A seed JSON file with auth keys for the account you want to drive.
Open https://web.telegram.org/k/?test=1 (test DC) or https://web.telegram.org/k/,
log in, then run this snippet in DevTools console:
(async () => {
const prefix = location.search.includes('test=1') ? 't_' : '';
const get = (k) => { try { return JSON.parse(localStorage.getItem(prefix + k)); } catch { return localStorage.getItem(prefix + k); } };
const acc = get('account1') || {};
const dcId = acc.dcId;
const authKeys = {};
for (let i = 1; i <= 5; i++) {
const key = acc[`dc${i}_auth_key`] || get(`dc${i}_auth_key`);
const salt = acc[`dc${i}_server_salt`] || get(`dc${i}_server_salt`);
if (key && salt) authKeys[i] = { key, salt };
}
const seed = {
userId: Number(acc.userId),
dcId,
authKeys,
timeOffset: get('server_time_offset')
};
console.log(JSON.stringify(seed, null, 2));
})();Save the output as tmp/seed.json (gitignored).
TG_API_TEST=1 TG_API_SEED=./tmp/seed.json pnpm test src/tests/apiBy default the harness flips Modes.test = true. To talk to production DCs
instead, also set TG_API_PROD_DC=1 and use a production seed.
When TG_API_TEST is unset the api tests are skipped, so pnpm test keeps
working in environments without credentials.
import {createTestClient} from './harness';
const client = await createTestClient({ seed, testDc: true });
const result = await client.apiManager.invokeApi('users.getFullUser', {
id: { _: 'inputUserSelf' }
});client.managers is the same registry the SharedWorker hands to the UI —
use appMessagesManager, appUsersManager, etc. directly.
nodeEnv.ts— installs polyfills forWebSocket(viaws),indexedDB(viafake-indexeddb),caches,BroadcastChannel,crypto.subtle.inlineCrypto.ts— registers the same crypto methods thatcrypto.worker.tsuses, so MTProto crypto runs in-process instead of in a separate worker.harness.ts— bootstrapsAppStateManager+AppStoragesManagerand callscreateManagers(...)directly, bypassing the SharedWorker /apiManagerProxylayers entirely.- The
MTProtoMessagePort.postMessagepath no-ops in test mode (superMessagePort.tsalready gates onimport.meta.env.MODE === 'test'), so manager → main-thread mirror calls are silently dropped.
fake-indexeddbis in-memory only; storage is reset every test run.- File downloads, CacheStorage and BroadcastChannel are stubbed — anything that exercises media or cross-tab sync is out of scope.
- Crypto runs inline on the event loop, not in
worker_threads. Heavy auth flows can block the main thread briefly.