import { strict as assert } from 'node:assert'; import { test } from 'node:test'; import type { TerminalStatus } from './posStatus'; import { byUrgency, countsOf, filterRows, stateOf, type CounterRow } from './counterRows'; const status = (over: Partial = {}): TerminalStatus => ({ terminalId: 'T1', storeName: 'R mart', presence: 'online', sync: 'synced', state: 'synced', pendingBills: 0, oldestPendingMs: 0, oldestPendingAt: null, silentForMs: 30_000, todayBills: 4, todayAmount: 450, lastBillAt: new Date(), reportedAt: null, receivedAt: null, clockDriftSeconds: null, reason: '', ...over, }) as TerminalStatus; /* Four states, and the one that matters is the split between "never used" and "offline". Read only the heartbeat and they look identical — but one is a counter nobody has switched on yet, and the other is a shop losing sales right now. The old page put them in one bucket, which diluted the urgent case with the routine one. */ test('a counter that has never sold and never reported is "never used"', () => { assert.equal(stateOf(status({ lastBillAt: null, todayBills: 0, silentForMs: null }), false), 'never'); }); test('a counter that HAS sold before but is silent now is offline, not "never used"', () => { // The distinction the page exists to make. assert.equal( stateOf(status({ state: 'offline', lastBillAt: new Date('2026-09-01'), todayBills: 0 }), false), 'offline', ); }); test('a stale counter counts as offline', () => { // Its last word was "fine" and everything since is guesswork. assert.equal(stateOf(status({ state: 'stale' }), false), 'offline'); }); test('unsent bills put a healthy counter into attention', () => { // Connected and holding sales is exactly what makes revenue read low, and it // is invisible if only the connection state is read. assert.equal(stateOf(status({ pendingBills: 3 }), false), 'attention'); }); test('a named problem puts a counter into attention even when it looks fine', () => { assert.equal(stateOf(status(), true), 'attention'); }); test('a counter with no status at all is "never used", not a crash', () => { assert.equal(stateOf(null, false), 'never'); }); /* ── Counting and filtering ───────────────────────────────────────────────── */ const row = (key: string, over: Partial = {}): CounterRow => ({ key, terminalId: key, branchId: 1, branchName: 'R mart', state: 'active', status: status(), periodBills: 0, periodAmount: 0, card: null, ...over, }); test('every counter is counted exactly once', () => { const counts = countsOf([ row('a'), row('b'), row('c', { state: 'attention' }), row('d', { state: 'offline' }), ]); assert.deepEqual(counts, { all: 4, active: 2, attention: 1, never: 0, offline: 1 }); }); test('search finds a counter by the code printed on its receipts', () => { // Usually the only identifier the person standing at it can read. const rows = [row('T5ED0'), row('T8719')]; const found = filterRows(rows, { state: 'all', branchId: null, term: 't5ed', nameFor: () => '' }); assert.equal(found.length, 1); assert.equal(found[0]?.terminalId, 'T5ED0'); }); test('search also finds it by the name the operator gave it', () => { const rows = [row('T5ED0'), row('T8719')]; const found = filterRows(rows, { state: 'all', branchId: null, term: 'main', nameFor: (id) => (id === 'T5ED0' ? 'Main Counter' : 'Counter 2'), }); assert.equal(found[0]?.terminalId, 'T5ED0'); }); test('a branch filter and a state filter both apply', () => { const rows = [ row('a', { branchId: 1, state: 'offline' }), row('b', { branchId: 2, state: 'offline' }), row('c', { branchId: 1, state: 'active' }), ]; const found = filterRows(rows, { state: 'offline', branchId: 1, term: '', nameFor: () => '' }); assert.deepEqual(found.map((r) => r.key), ['a']); }); test('the worst counters sort to the top', () => { const sorted = [row('a'), row('b', { state: 'offline' }), row('c', { state: 'attention' })].sort(byUrgency); assert.deepEqual(sorted.map((r) => r.state), ['offline', 'attention', 'active']); });