Files
RedditBot/tests/login-status.test.ts
T

109 lines
3.3 KiB
TypeScript

import express from 'express';
import type { AddressInfo } from 'node:net';
import { afterEach, describe, expect, it } from 'vitest';
import { classifyRedditSessionError } from '../src/browser/client.js';
import { createLoginRouter } from '../src/web/routes/login.js';
const servers: Array<ReturnType<express.Express['listen']>> = [];
async function request(app: express.Express, path: string): Promise<Response> {
const server = app.listen(0);
servers.push(server);
await new Promise<void>((resolve) => server.once('listening', resolve));
const { port } = server.address() as AddressInfo;
return fetch(`http://127.0.0.1:${port}${path}`);
}
afterEach(async () => {
await Promise.all(
servers.splice(0).map(
(server) => new Promise<void>((resolve) => server.close(() => resolve())),
),
);
delete process.env.NOVNC_PUBLIC_URL;
});
function appWithBrowser(state: 'authenticated' | 'login-required' | 'unreachable') {
const app = express();
app.use(
createLoginRouter({
services: {
accountAuth: {
ensureFullyAuthenticated: async () => state === 'authenticated',
},
browser: {
isSessionValid: async () => state === 'authenticated',
getSessionState: () => state,
},
},
logger: { error: () => undefined },
} as never),
);
return app;
}
function appWithPartialAuthentication() {
const app = express();
app.use(createLoginRouter({
services: {
accountAuth: { ensureFullyAuthenticated: async () => false },
browser: {
getSessionState: () => 'authenticated',
},
},
logger: { error: () => undefined },
} as never));
return app;
}
describe('Reddit session status', () => {
it('reports when Reddit is unreachable instead of presenting it as logged out', async () => {
const response = await request(appWithBrowser('unreachable'), '/login/status');
expect(response.status).toBe(200);
expect(await response.json()).toEqual({
ready: true,
loggedIn: false,
status: 'unreachable',
});
});
it('reports when an interactive Reddit login is required', async () => {
const response = await request(appWithBrowser('login-required'), '/login/status');
expect(await response.json()).toMatchObject({
loggedIn: false,
status: 'login-required',
});
});
it('does not report authenticated when only the Reddit browser session is valid', async () => {
const response = await request(appWithPartialAuthentication(), '/login/status');
expect(await response.json()).toEqual({
ready: true,
loggedIn: false,
status: 'login-required',
});
});
it('returns the configured public noVNC URL', async () => {
process.env.NOVNC_PUBLIC_URL = 'https://red-vnc.apps.jbrunton.co.uk';
const response = await request(appWithBrowser('authenticated'), '/login/info');
expect(await response.json()).toMatchObject({
publicUrl: 'https://red-vnc.apps.jbrunton.co.uk',
});
});
it('classifies Reddit navigation failures as unreachable', () => {
expect(
classifyRedditSessionError(
new Error('page.goto: net::ERR_HTTP_RESPONSE_CODE_FAILURE at https://www.reddit.com/'),
),
).toBe('unreachable');
expect(classifyRedditSessionError(new Error('login form was not found'))).toBe('login-required');
});
});