const test = require('node:test'); const assert = require('node:assert/strict'); const logs = require('../frontend/web/static/js/log-view.js'); test('state event view includes transitions only, never periodic snapshots or unknown types', () => { const events=[{event_type:'snapshot'},{event_type:'transition',state:'up'},{event_type:'other'},{}]; assert.deepEqual(logs.transitionsOnly(events),[events[1]]); assert.equal(events.length,4); }); test('newest logs first without mutating source; invalid timestamps last', () => { const items = [ { id: 1, timestamp: '2026-09-07T01:00:00Z' }, { id: 2, timestamp: 'invalid' }, { id: 3, timestamp: '2026-09-07T02:00:00Z' }, { id: 4, timestamp: '2026-09-07T02:00:00Z' }, { id: 5 }, ]; assert.deepEqual(logs.orderedEvents(items).map(e => e.id), [3, 4, 1, 2, 5]); assert.deepEqual(items.map(e => e.id), [1, 2, 3, 4, 5]); }); test('system summary counts only returned raw severity values', () => { const summary = logs.summaryFor('system', [ { severity: 'info' }, { severity: 'INFO' }, { severity: 'warning' }, { severity: 'critical' }, {}, { severity: 'other' }, ]); assert.deepEqual(summary.map(e => [e.key, e.count]), [['info', 2], ['warning', 1], ['critical', 1]]); assert.equal(logs.summaryFor('system', []).every(e => e.count === 0), true); }); test('port and link summaries use directions and actual state, not translated labels', () => { assert.deepEqual(logs.summaryFor('port', [ { direction: 'RX', event_type: 'traffic' }, { direction: 'tx' }, { direction: 'CONNECTED', event_type: 'session' }, { event_type: 'session' }, ]).map(e => e.count), [1, 1, 2]); assert.deepEqual(logs.summaryFor('link', [ { state: 'UP', previous_state: 'down' }, { state: 'down' }, { state: 'unknown' }, {}, ]).map(e => e.count), [1, 1, 2]); }); test('timestamps respect device time zone across midnight', () => { assert.deepEqual(logs.timestampParts('2026-09-06T16:02:03Z', 'Asia/Seoul'), { date: '2026-09-07', time: '01:02:03', unset: false, }); assert.deepEqual(logs.timestampParts('2026-09-07T00:00:00Z', 'UTC'), { date: '2026-09-07', time: '00:00:00', unset: false, }); }); test('unconfigured clocks stay in the past; missing dates do not become today', () => { assert.deepEqual(logs.timestampParts('1970-01-01T00:00:00Z', 'UTC'), { date: '1970-01-01', time: '00:00:00', unset: true, }); for (const value of [null, undefined, '']) { assert.deepEqual(logs.timestampParts(value, 'UTC'), { date: '—', time: '—', unset: false }); } assert.equal(logs.timestampParts('invalid', 'UTC').time, 'invalid'); assert.equal(logs.timestampParts('2026-09-07T00:00:00Z', 'invalid-zone').time, '2026-09-07T00:00:00Z'); }); test('highlighting is literal, case-insensitive and preserves the full original text', () => { const original = ' a.b A.B [x]'; const parts = logs.highlightParts(original, 'a.b'); assert.equal(parts.map(p => p.text).join(''), original); assert.deepEqual(parts.filter(p => p.match).map(p => p.text), ['a.b', 'A.B']); assert.equal(logs.highlightParts(original, '[x]').filter(p => p.match)[0].text, '[x]'); assert.deepEqual(logs.highlightParts(original, ' '), [{ text: original, match: false }]); assert.deepEqual(logs.highlightParts('오류: 연결 오류', '오류').filter(p => p.match).map(p => p.text), ['오류', '오류']); });