Files
Codeman/test/sse-routing-remote.test.ts
T
RandalixandClaude Opus 5.5 aa4217b8b1 test: build every in-process WebServer on port 0 (#440)
The files on the guard's legacy list build new WebServer(0, …) and read server.boundPort after start(), as sse-tile-grid-filter does. Files with several servers read each server's own port; quick-start's local helper lost its port parameter so the literal 0 sits where the server is built. Header comments that named a port say "ephemeral". No assertion changed except where it embedded the port.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 16:28:26 +02:00

57 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @fileoverview Multi-user routing of the `remote:*` SSE family (server.ts `deriveSseHint`).
*
* The wake events carry `hostId`/`label`, which `GET /api/remote-hosts` withholds from
* non-admins, and their toast fires before any session check on the client — so an
* event that falls through to the global branch shows every logged-in user "Waking
* <label>" for a session they do not own. Constructs the server without starting it:
* the hint is a pure function of the event, the payload and the sessions map.
*/
import { describe, expect, it } from 'vitest';
import { WebServer } from '../src/web/server.js';
type Hint = { owner?: string; username?: string; adminOnly?: boolean; sessionScoped?: boolean } | undefined;
function hintFor(event: string, payload: Record<string, unknown>, owners: Record<string, string> = {}): Hint {
const server = new WebServer(0, false, true) as unknown as {
sessions: Map<string, { owner?: string }>;
deriveSseHint(event: string, data: unknown): Hint;
};
for (const [id, owner] of Object.entries(owners)) server.sessions.set(id, { owner });
return server.deriveSseHint(event, payload);
}
describe('deriveSseHint — remote: events are session-scoped', () => {
it('routes a session wake to that session’s owner', () => {
expect(hintFor('remote:hostWaking', { sessionId: 's1', hostId: 'h', label: 'H' }, { s1: 'alice' })).toEqual({
owner: 'alice',
sessionScoped: true,
});
expect(hintFor('remote:sessionReconnected', { sessionId: 's1' }, { s1: 'alice' })).toEqual({
owner: 'alice',
sessionScoped: true,
});
});
it('routes a create/attach wake (no session yet) to the user who asked for it', () => {
expect(hintFor('remote:hostWaking', { forNewSession: true, username: 'bob', hostId: 'h', label: 'H' })).toEqual({
username: 'bob',
sessionScoped: true,
});
expect(hintFor('remote:hostWakeFailed', { forNewSession: true, username: 'bob', hostId: 'h' })).toEqual({
username: 'bob',
sessionScoped: true,
});
});
it('fails closed (admins only) when it names neither a session nor a requester', () => {
const hint = hintFor('remote:hostWaking', { forNewSession: true, hostId: 'h', label: 'H' });
expect(hint).toEqual({ owner: undefined, sessionScoped: true });
});
it('never lets a wake event reach the global branch', () => {
expect(hintFor('remote:hostWaking', {})).not.toBeUndefined();
expect(hintFor('remote:reconnectExhausted', {})).not.toBeUndefined();
});
});