v1.4.0: per-player snapshot + shared market view with owner info (ISS-112)
CI / build + unit + e2e (pull_request) Successful in 14m29s
CI / build + unit + e2e (pull_request) Successful in 14m29s
This commit is contained in:
+38
-18
@@ -1,45 +1,65 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { broadcast, subscribe } from './sse';
|
||||
import { broadcast, subscribedPlayerIds, subscribe } from './sse';
|
||||
|
||||
// ISS-129 (v0.57.0): the fan-out registry behind /api/events — plain Set semantics, so no DB or
|
||||
// server needed. Subscribers leak across tests in this file (module-level state), which is fine:
|
||||
// each test only asserts on its own arrays.
|
||||
// ISS-129 (v0.57.0): the fan-out registry behind /api/events — no DB or server needed. Subscribers
|
||||
// leak across tests in this file (module-level state), which is fine: each test asserts on its own
|
||||
// arrays and uses distinct player ids where isolation matters. v1.4.0 (ISS-112): keyed by PLAYER ID
|
||||
// — a frame for one player must never reach another player's streams.
|
||||
|
||||
describe('lib/sse', () => {
|
||||
it('delivers broadcasts to subscribers and stops after unsubscribe', () => {
|
||||
it('delivers broadcasts to subscribers of the SAME player only, and stops after unsubscribe', () => {
|
||||
const a: string[] = [];
|
||||
const b: string[] = [];
|
||||
const unsubA = subscribe((t) => a.push(t));
|
||||
const unsubB = subscribe((t) => b.push(t));
|
||||
const b: string[] = []; // same player as a — both get its frames
|
||||
const c: string[] = []; // different player — never sees their frames
|
||||
const unsubA = subscribe('p-a', (t) => a.push(t));
|
||||
const unsubB = subscribe('p-a', (t) => b.push(t));
|
||||
const unsubC = subscribe('p-c', (t) => c.push(t));
|
||||
|
||||
broadcast('x');
|
||||
broadcast(new Map([['p-a', 'x']]));
|
||||
expect(a).toEqual(['x']);
|
||||
expect(b).toEqual(['x']);
|
||||
expect(b).toEqual(['x']); // same group
|
||||
expect(c).toEqual([]); // other player's frame never crosses over
|
||||
|
||||
unsubA();
|
||||
broadcast('y');
|
||||
broadcast(new Map([['p-a', 'y'], ['p-c', 'z']]));
|
||||
expect(a).toEqual(['x']); // unsubscribed — no more events
|
||||
expect(b).toEqual(['x', 'y']);
|
||||
expect(c).toEqual(['z']);
|
||||
unsubB();
|
||||
unsubC();
|
||||
});
|
||||
|
||||
it('a throwing subscriber does not break the others', () => {
|
||||
it('subscribedPlayerIds lists exactly the players with live streams, dropping empty groups', () => {
|
||||
const got: string[] = [];
|
||||
subscribe(() => {
|
||||
const unsub1 = subscribe('p-ids-1', (t) => got.push(t));
|
||||
const unsub2 = subscribe('p-ids-1', (t) => got.push(t)); // second member of the same group
|
||||
const unsub3 = subscribe('p-ids-2', (t) => got.push(t));
|
||||
expect(subscribedPlayerIds().sort()).toEqual(['p-ids-1', 'p-ids-2']);
|
||||
|
||||
unsub1(); // p-ids-1 still has one member — id stays listed
|
||||
expect(subscribedPlayerIds().sort()).toEqual(['p-ids-1', 'p-ids-2']);
|
||||
unsub3(); // p-ids-2's last stream goes → its projections stop
|
||||
expect(subscribedPlayerIds()).toEqual(['p-ids-1']);
|
||||
unsub2();
|
||||
});
|
||||
|
||||
it('a throwing subscriber does not break the others in its group', () => {
|
||||
const got: string[] = [];
|
||||
subscribe('p-boom', () => {
|
||||
throw new Error('boom');
|
||||
});
|
||||
const unsub = subscribe((t) => got.push(t));
|
||||
broadcast('z');
|
||||
const unsub = subscribe('p-boom', (t) => got.push(t));
|
||||
broadcast(new Map([['p-boom', 'z']]));
|
||||
expect(got).toEqual(['z']);
|
||||
unsub();
|
||||
});
|
||||
|
||||
it('unsubscribe is idempotent', () => {
|
||||
it('unsubscribe is idempotent and broadcasting to an unknown player is a no-op', () => {
|
||||
const got: string[] = [];
|
||||
const unsub = subscribe((t) => got.push(t));
|
||||
const unsub = subscribe('p-idem', (t) => got.push(t));
|
||||
unsub();
|
||||
unsub(); // second call must not throw
|
||||
broadcast('w');
|
||||
broadcast(new Map([['nobody-subscribed-here', 'w']])); // no group → dropped, no throw
|
||||
expect(got).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user