import { test } from 'node:test' import assert from 'node:assert/strict' import { berechneVertraege, erzeugeZaehler, CHAT_POLL_MS } from '../public/neu/zaehler.js' test('Verträge: Rechnungen plus Rückfragen; "!" nur bei paymentProblem, nie bei switchPending (V3)', async () => { assert.deepEqual(berechneVertraege({ rechnungen: [{ balance: 50 }, { balance: 0 }, { balance: '12.50' }], rueckfragen: [{ status: 'rueckfrage' }, { status: 'offen' }, { status: 'rueckfrage' }] }), { zahl: 4, ausruf: false }) assert.deepEqual(berechneVertraege({ paymentProblem: true }), { zahl: 0, ausruf: true }) const karte = { '/api/invoices': { invoices: [{ id: 'i1', number: 'RE-1', balance: 50 }] }, '/api/abo/takeover-credits': { requests: [{ status: 'rueckfrage' }] }, '/api/abo': { subscription: { paymentProblem: false, switchPending: { since: 'x', paymentUrl: null } } } } const z = erzeugeZaehler({ api: async (p) => karte[p], onChange() {} }); await z.aktualisiere('vertraege') assert.deepEqual(z.stand().vertraege, { zahl: 2, ausruf: false }) }) test('Fehler einer Quelle behalten ihren letzten Wert (Review Focus 5)', async () => { let kaputt = false const api = async (p) => { if (p === '/api/invoices') { if (kaputt) throw Object.assign(new Error('x'), { status: 500 }); return { invoices: [{ id: 'i1', number: 'RE-1', balance: 50 }] } } return p === '/api/abo' ? { subscription: null } : { requests: [] } } const z = erzeugeZaehler({ api, onChange() {} }); await z.aktualisiere('vertraege'); kaputt = true; await z.aktualisiere('vertraege') assert.deepEqual(z.stand().vertraege, { zahl: 1, ausruf: false }) }) test('Chat: Poll alle 45 s, nicht im Chat und nicht bei verstecktem Tab; setzeChat(0) sofort', async () => { const timer = []; const staende = []; let sicht = true; let chat = false; let aufrufe = 0 const z = erzeugeZaehler({ api: async (p) => { if (p === '/api/chat/unread-count') { aufrufe += 1; return { unread: 3 } } return p === '/api/abo' ? { subscription: null } : p === '/api/invoices' ? { invoices: [] } : { requests: [] } }, onChange: (s) => staende.push(s.chat), sichtbar: () => sicht, imChat: () => chat, zeitgeber: { setze: (fn, ms) => timer.push({ fn, ms }) - 1, loesche() {} } }) await z.start(); assert.deepEqual([timer.map((t) => t.ms), z.stand().chat], [[CHAT_POLL_MS], 3]) await timer[0].fn(); assert.equal(aufrufe, 2) sicht = false; await timer[0].fn(); sicht = true; chat = true; await timer[0].fn(); assert.equal(aufrufe, 2) z.setzeChat(0); assert.deepEqual([z.stand().chat, staende.at(-1)], [0, 0]) }) test('Zähler: onChange nur bei Änderung, "!" vor Zahl, ältere Antworten überholen keine neueren, stop räumt den Zeitgeber ab', async () => { const meldungen = []; const geloescht = []; let gelaufen = 0 const antworten = { '/api/invoices': [], '/api/abo': { subscription: { paymentProblem: true } } } const wartend = [] // die ersten zwei Chat-Antworten löst der Test selbst auf const api = async (p) => { if (p === '/api/chat/unread-count') return wartend.length < 2 ? new Promise((ok) => wartend.push(ok)) : { unread: 5 } return p === '/api/invoices' ? { invoices: antworten[p] } : p === '/api/abo' ? antworten[p] : { requests: [] } } const z = erzeugeZaehler({ api, onChange: (s) => meldungen.push(JSON.stringify(s)), zeitgeber: { setze: () => { gelaufen += 1; return 7 }, loesche: (id) => geloescht.push(id) } }) await z.aktualisiere('vertraege'); await z.aktualisiere('vertraege') assert.deepEqual(meldungen, [JSON.stringify({ chat: 0, vertraege: { zahl: 0, ausruf: true } })]) // zweiter Lauf ohne Änderung meldet nichts const alt = z.aktualisiere('chat'); const neu = z.aktualisiere('chat') wartend[1]({ unread: 5 }); await neu; wartend[0]({ unread: 2 }); await alt assert.equal(z.stand().chat, 5) // die zuerst gestartete, später eingetroffene Antwort gilt nicht await z.start(); await z.start(); assert.equal(gelaufen, 1) // Zeitgeber nur einmal z.stop(); z.stop(); assert.deepEqual(geloescht, [7]) })