import { describe, expect, it, vi } from 'vitest'; import { EventBus } from '../src/core/EventBus'; import { Meters, freshNightState } from '../src/core/meters'; const setup = () => { const bus = new EventBus(); const state = freshNightState('theRoyal', 0, 80); const meters = new Meters(bus, state); return { bus, state, meters }; }; describe('Meters', () => { it('applies deltas and emits meters:changed', () => { const { bus, state } = setup(); const fn = vi.fn(); bus.on('meters:changed', fn); bus.emit('meters:delta', { vibe: 10, aggro: 5 }); expect(state.vibe).toBe(60); expect(state.aggro).toBe(5); expect(fn).toHaveBeenCalledWith({ vibe: 60, aggro: 5, hype: 1 }); }); it('clamps vibe/aggro to 0..100 and hype to 1..3', () => { const { bus, state } = setup(); bus.emit('meters:delta', { vibe: 999, aggro: -50, hype: 99 }); expect(state.vibe).toBe(100); expect(state.aggro).toBe(0); expect(state.hype).toBe(3); bus.emit('meters:delta', { vibe: -999 }); expect(state.vibe).toBe(0); }); it('hype multiplies positive vibe only — never softens a hit', () => { const { bus, state } = setup(); bus.emit('meters:delta', { hype: 1 }); // hype -> 2 bus.emit('meters:delta', { vibe: 10 }); expect(state.vibe).toBe(70); // 50 + 10*2 bus.emit('meters:delta', { vibe: -10 }); expect(state.vibe).toBe(60); // full -10, unscaled }); it('collects heat strikes and tracks clicker drift', () => { const { bus, state } = setup(); bus.emit('heat:strike', { reason: 'minor admitted' }); expect(state.heatStrikes).toHaveLength(1); bus.emit('door:clicker', { direction: 'in' }); bus.emit('door:clicker', { direction: 'in' }); bus.emit('door:clicker', { direction: 'out' }); expect(state.capacity.clickerShown).toBe(1); bus.emit('door:clicker', { direction: 'out' }); bus.emit('door:clicker', { direction: 'out' }); expect(state.capacity.clickerShown).toBe(0); // clicker can't go negative }); it('destroy() detaches all listeners', () => { const { bus, state, meters } = setup(); meters.destroy(); bus.emit('meters:delta', { vibe: 10 }); expect(state.vibe).toBe(50); }); });