Files
steam-chat/test/steam-lifecycle.test.ts

111 lines
3.4 KiB
TypeScript

'use strict';
import type { EventEmitter as EventEmitterType } from 'node:events';
const assert = require('node:assert/strict');
const { EventEmitter } = require('node:events');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const test = require('node:test');
const {
buildLogOnOptions,
createSteamLifecycle,
isRecoverableLoginError,
isUnrecoverableLoginError
} = require('../src/steam/lifecycle');
type LogOnOptions = {
accountName?: string;
password?: string;
refreshToken?: string;
logonID?: number;
steamID?: string;
};
type ScheduledTimer = {
fn: () => void;
delay: number;
};
type LifecycleTestUser = EventEmitterType & {
logOn: (options: LogOnOptions) => void;
webLogOn: () => void;
logOff: () => void;
};
test('buildLogOnOptions prefers refresh.token over account password', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'steam-chat-life-'));
const tokenPath = path.join(dir, 'refresh.token');
fs.writeFileSync(tokenPath, 'refresh-value\n');
assert.deepEqual(buildLogOnOptions({
accountName: 'account',
password: 'secret',
logonID: 42,
steamID: '7656'
}, tokenPath), {
refreshToken: 'refresh-value',
logonID: 42,
steamID: '7656'
});
});
test('login error classification separates recoverable and unrecoverable errors', () => {
assert.equal(isRecoverableLoginError(new Error('ECONNRESET socket closed')), true);
assert.equal(isRecoverableLoginError(new Error('InvalidPassword')), false);
assert.equal(isUnrecoverableLoginError(new Error('SteamGuard required')), true);
});
test('createSteamLifecycle logs on, resolves web session, saves refresh token, and avoids duplicate retry timers', async () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'steam-chat-life-'));
const tokenPath = path.join(dir, 'refresh.token');
const user = new EventEmitter() as LifecycleTestUser;
const community = { cookies: null as string[] | null, setCookies(cookies: string[]) { this.cookies = cookies; } };
const logOnCalls: LogOnOptions[] = [];
let scheduled: ScheduledTimer | null = null;
let clearCount = 0;
user.logOn = (options: LogOnOptions) => logOnCalls.push(options);
user.webLogOn = () => user.emit('webSession', 'session-id', ['a=b']);
user.logOff = () => {};
const lifecycle = createSteamLifecycle({
steamUser: user,
steamCommunity: community,
config: { accountName: 'name', password: 'pass' },
refreshTokenPath: tokenPath,
logger: { info() {}, warn() {}, error() {} },
timers: {
setTimeout(fn: () => void, delay: number) {
scheduled = { fn, delay };
return 7;
},
clearTimeout() {
clearCount += 1;
}
}
});
lifecycle.start();
assert.equal(logOnCalls.length, 1);
assert.equal(logOnCalls[0].accountName, 'name');
user.emit('error', new Error('timeout'));
user.emit('error', new Error('timeout again'));
assert.ok(scheduled);
assert.equal(scheduled.delay, 5000);
scheduled.fn();
assert.equal(logOnCalls.length, 2);
user.emit('loggedOn');
await lifecycle.waitForLogin();
const webSession = await lifecycle.waitForWebSession();
assert.deepEqual(webSession, { sessionID: 'session-id', cookies: ['a=b'] });
assert.deepEqual(community.cookies, ['a=b']);
assert.equal(clearCount, 0);
user.emit('refreshToken', 'next-token');
assert.equal(fs.readFileSync(tokenPath, 'utf8'), 'next-token\n');
});