The session manager logs each session's creation, readiness time and removal with its reason. A streamer that exits during startup now fails the start at once instead of after 60 seconds, and the log carries its exit code and last output; containers are no longer auto-removed, so that output survives until teardown. A failed start reaches the app as a 502.
85 lines
3.7 KiB
TypeScript
85 lines
3.7 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import { test } from 'node:test';
|
|
import { SessionError, SessionManager } from '../src/sessions.ts';
|
|
import { catalog, FakeDriver, fastTimings, MemoryLog, sleep } from './fakes.ts';
|
|
|
|
test('rejects unstreamable games and enforces the session cap', async () => {
|
|
const sessions = new SessionManager(new FakeDriver(), catalog, 1, fastTimings);
|
|
await assert.rejects(sessions.create('flash'), (e: SessionError) => e.status === 422);
|
|
const { id, size } = await sessions.create('java');
|
|
assert.deepEqual(size, { width: 472, height: 354 });
|
|
await assert.rejects(sessions.create('java'), (e: SessionError) => e.status === 503);
|
|
await sessions.teardown(id);
|
|
await sessions.create('java');
|
|
await sessions.teardownAll();
|
|
});
|
|
|
|
test('removes a session that is never connected to', async () => {
|
|
const driver = new FakeDriver();
|
|
const sessions = new SessionManager(driver, catalog, 4, fastTimings);
|
|
const { id } = await sessions.create('java');
|
|
await sessions.start(id, {});
|
|
await sleep(400);
|
|
assert.equal(sessions.has(id), false);
|
|
assert.deepEqual(driver.removed, [id]);
|
|
});
|
|
|
|
test('keeps a session while connected and survives a quick reconnect', async () => {
|
|
const driver = new FakeDriver();
|
|
const sessions = new SessionManager(driver, catalog, 4, fastTimings);
|
|
const { id } = await sessions.create('java');
|
|
await sessions.start(id, {});
|
|
sessions.socketOpened(id);
|
|
await sleep(400);
|
|
assert.equal(sessions.has(id), true);
|
|
sessions.socketClosed(id);
|
|
await sleep(50);
|
|
sessions.socketOpened(id);
|
|
await sleep(200);
|
|
assert.equal(sessions.has(id), true);
|
|
sessions.socketClosed(id);
|
|
await sleep(200);
|
|
assert.equal(sessions.has(id), false);
|
|
});
|
|
|
|
test('starts with the given saves, pushes changes, and ends with a final push', async () => {
|
|
const driver = new FakeDriver();
|
|
const sessions = new SessionManager(driver, catalog, 4, fastTimings);
|
|
const { id } = await sessions.create('java');
|
|
await sessions.start(id, { 'cookies.txt': 'YQ==' });
|
|
await assert.rejects(sessions.start(id, {}), (e: SessionError) => e.status === 409);
|
|
assert.deepEqual(driver.containers.get(id)!.saves, { 'cookies.txt': 'YQ==' });
|
|
|
|
const seen: unknown[] = [];
|
|
sessions.watchSaves(id, (files) => seen.push(files));
|
|
driver.containers.get(id)!.saves['cookies.txt'] = 'Yg==';
|
|
await sleep(60);
|
|
driver.containers.get(id)!.saves['cookies.txt'] = 'Yw==';
|
|
await sessions.teardown(id);
|
|
assert.deepEqual(seen, [{ 'cookies.txt': 'YQ==' }, { 'cookies.txt': 'Yg==' }, { 'cookies.txt': 'Yw==' }, null]);
|
|
});
|
|
|
|
test('logs a session from creation to removal', async () => {
|
|
const log = new MemoryLog();
|
|
const sessions = new SessionManager(new FakeDriver(), catalog, 4, fastTimings, log);
|
|
const { id } = await sessions.create('java');
|
|
await sessions.start(id, {});
|
|
await sessions.teardown(id, 'player left');
|
|
assert.equal(log.lines.length, 3);
|
|
assert.match(log.lines[0], new RegExp(`Session ${id}: created for game java at 472x354`));
|
|
assert.match(log.lines[1], /ready in \d+\.\ds/);
|
|
assert.match(log.lines[2], /removing \(player left\)/);
|
|
});
|
|
|
|
test('reports a streamer that fails to start, and removes it', async () => {
|
|
const driver = new FakeDriver();
|
|
driver.failStart = new Error('streamer exited with code 1; last output:\nno display');
|
|
const log = new MemoryLog();
|
|
const sessions = new SessionManager(driver, catalog, 4, fastTimings, log);
|
|
const { id } = await sessions.create('java');
|
|
await assert.rejects(sessions.start(id, {}), (e: SessionError) => e.status === 502);
|
|
assert.equal(sessions.has(id), false);
|
|
assert.deepEqual(driver.removed, [id]);
|
|
assert.ok(log.lines.some((line) => line.includes('failed to start: streamer exited with code 1') && line.includes('no display')));
|
|
});
|