SnowLuma v2.4.2 source snapshot
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run
This commit is contained in:
commit
fa9c73363d
1367 files changed
+355735
No files matched your search
@@ -0,0 +1,130 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { StateBus, type StateResource } from '../src/webui/state-bus';
|
||||
|
||||
describe('StateBus', () => {
|
||||
it('delivers a publish to a single subscriber with the resource kind', () => {
|
||||
const bus = new StateBus();
|
||||
const seen: StateResource[] = [];
|
||||
bus.subscribe((r) => seen.push(r));
|
||||
|
||||
bus.publish('processes');
|
||||
bus.publish('qq-list');
|
||||
bus.publish('connections');
|
||||
|
||||
expect(seen).toEqual(['processes', 'qq-list', 'connections']);
|
||||
});
|
||||
|
||||
it('fans out one publish to every subscriber', () => {
|
||||
const bus = new StateBus();
|
||||
const a = vi.fn();
|
||||
const b = vi.fn();
|
||||
const c = vi.fn();
|
||||
bus.subscribe(a);
|
||||
bus.subscribe(b);
|
||||
bus.subscribe(c);
|
||||
|
||||
bus.publish('processes');
|
||||
|
||||
expect(a).toHaveBeenCalledExactlyOnceWith('processes');
|
||||
expect(b).toHaveBeenCalledExactlyOnceWith('processes');
|
||||
expect(c).toHaveBeenCalledExactlyOnceWith('processes');
|
||||
});
|
||||
|
||||
it('returns an unsubscribe handle that stops future emits without touching the others', () => {
|
||||
const bus = new StateBus();
|
||||
const a = vi.fn();
|
||||
const b = vi.fn();
|
||||
const unsubA = bus.subscribe(a);
|
||||
bus.subscribe(b);
|
||||
|
||||
bus.publish('processes');
|
||||
unsubA();
|
||||
bus.publish('qq-list');
|
||||
|
||||
expect(a).toHaveBeenCalledExactlyOnceWith('processes');
|
||||
expect(b).toHaveBeenCalledTimes(2);
|
||||
expect(b.mock.calls).toEqual([['processes'], ['qq-list']]);
|
||||
});
|
||||
|
||||
it('is idempotent on double-unsubscribe', () => {
|
||||
const bus = new StateBus();
|
||||
const a = vi.fn();
|
||||
const unsubA = bus.subscribe(a);
|
||||
unsubA();
|
||||
unsubA();
|
||||
bus.publish('processes');
|
||||
expect(a).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('isolates subscriber exceptions — one throwing listener does not block the others or future publishes', () => {
|
||||
const bus = new StateBus();
|
||||
const before = vi.fn();
|
||||
const thrower = vi.fn(() => {
|
||||
throw new Error('boom');
|
||||
});
|
||||
const after = vi.fn();
|
||||
bus.subscribe(before);
|
||||
bus.subscribe(thrower);
|
||||
bus.subscribe(after);
|
||||
|
||||
expect(() => bus.publish('processes')).not.toThrow();
|
||||
expect(before).toHaveBeenCalledExactlyOnceWith('processes');
|
||||
expect(after).toHaveBeenCalledExactlyOnceWith('processes');
|
||||
|
||||
bus.publish('qq-list');
|
||||
expect(before).toHaveBeenCalledTimes(2);
|
||||
expect(after).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('dispose() drops every subscriber and silently swallows further publishes', () => {
|
||||
const bus = new StateBus();
|
||||
const a = vi.fn();
|
||||
bus.subscribe(a);
|
||||
bus.dispose();
|
||||
bus.publish('processes');
|
||||
expect(a).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('a subscriber that subscribes a second time inside its own callback observes the FOLLOWING publish, not this one', () => {
|
||||
const bus = new StateBus();
|
||||
const second = vi.fn();
|
||||
const first = vi.fn(() => {
|
||||
bus.subscribe(second);
|
||||
});
|
||||
bus.subscribe(first);
|
||||
|
||||
bus.publish('processes');
|
||||
expect(first).toHaveBeenCalledExactlyOnceWith('processes');
|
||||
// The just-added subscriber must not receive the in-flight publish — that
|
||||
// would surprise SSE handlers that subscribe lazily during an emit fan-out.
|
||||
expect(second).not.toHaveBeenCalled();
|
||||
|
||||
bus.publish('qq-list');
|
||||
expect(second).toHaveBeenCalledExactlyOnceWith('qq-list');
|
||||
});
|
||||
|
||||
it('a subscriber that unsubscribes a peer mid-emit lets the survivor still receive THIS publish', () => {
|
||||
const bus = new StateBus();
|
||||
const order: string[] = [];
|
||||
let unsubB!: () => void;
|
||||
bus.subscribe(() => {
|
||||
order.push('a');
|
||||
unsubB();
|
||||
});
|
||||
unsubB = bus.subscribe(() => {
|
||||
order.push('b');
|
||||
});
|
||||
bus.subscribe(() => {
|
||||
order.push('c');
|
||||
});
|
||||
|
||||
bus.publish('processes');
|
||||
// Snapshot semantics: the fan-out iterates a copy, so peer-unsubscribe
|
||||
// does not skip any other listener that was attached when publish started.
|
||||
expect(order).toEqual(['a', 'b', 'c']);
|
||||
|
||||
order.length = 0;
|
||||
bus.publish('qq-list');
|
||||
expect(order).toEqual(['a', 'c']);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user