import { describe, it, expect } from 'vitest'
import { parseTakStreamFrame, parseTraditionalXmlFrame, parseCotPayload } from '../../../server/utils/cotParser.js'
const encodeVarint = (value, bytes = []) => {
const byte = value & 0x7F
const remaining = value >>> 7
if (remaining === 0) {
return [...bytes, byte]
}
return encodeVarint(remaining, [...bytes, byte | 0x80])
}
function buildTakFrame(payload) {
const buf = Buffer.from(payload, 'utf8')
const varint = encodeVarint(buf.length)
return Buffer.concat([Buffer.from([0xBF]), Buffer.from(varint), buf])
}
describe('cotParser', () => {
describe('parseTakStreamFrame', () => {
it('parses valid frame', () => {
const payload = ''
const frame = buildTakFrame(payload)
const result = parseTakStreamFrame(frame)
expect(result).not.toBeNull()
expect(result.payload.toString('utf8')).toBe(payload)
expect(result.bytesConsumed).toBe(frame.length)
})
it('returns null for incomplete buffer', () => {
const frame = buildTakFrame('')
const partial = frame.subarray(0, 2)
expect(parseTakStreamFrame(partial)).toBeNull()
})
it('returns null for wrong magic', () => {
const payload = ''
const buf = Buffer.concat([Buffer.from([0x00]), Buffer.from([payload.length]), Buffer.from(payload)])
expect(parseTakStreamFrame(buf)).toBeNull()
})
it('returns null for payload length exceeding max', () => {
const hugeLen = 64 * 1024 + 1
const varint = encodeVarint(hugeLen)
const buf = Buffer.concat([Buffer.from([0xBF]), Buffer.from(varint)])
expect(parseTakStreamFrame(buf)).toBeNull()
})
})
describe('parseTraditionalXmlFrame', () => {
it('parses one XML message delimited by ', () => {
const xml = ''
const buf = Buffer.from(xml, 'utf8')
const result = parseTraditionalXmlFrame(buf)
expect(result).not.toBeNull()
expect(result.payload.toString('utf8')).toBe(xml)
expect(result.bytesConsumed).toBe(buf.length)
})
it('returns null when buffer does not start with <', () => {
expect(parseTraditionalXmlFrame(Buffer.from('x'))).toBeNull()
expect(parseTraditionalXmlFrame(Buffer.from([0xBF, 0x00]))).toBeNull()
})
it('returns null when not yet received', () => {
const partial = Buffer.from('', 'utf8')
expect(parseTraditionalXmlFrame(partial)).toBeNull()
})
it('extracted payload parses as auth CoT', () => {
const xml = ''
const buf = Buffer.from(xml, 'utf8')
const result = parseTraditionalXmlFrame(buf)
expect(result).not.toBeNull()
const parsed = parseCotPayload(result.payload)
expect(parsed).not.toBeNull()
expect(parsed.type).toBe('auth')
expect(parsed.username).toBe('itak')
expect(parsed.password).toBe('mypass')
})
})
describe('parseCotPayload', () => {
it('parses position CoT XML', () => {
const xml = ''
const result = parseCotPayload(Buffer.from(xml, 'utf8'))
expect(result).not.toBeNull()
expect(result.type).toBe('cot')
expect(result.id).toBe('device-1')
expect(result.lat).toBe(37.7)
expect(result.lng).toBe(-122.4)
expect(result.label).toBe('Bravo')
})
it('parses auth CoT with detail.auth', () => {
const xml = ''
const result = parseCotPayload(Buffer.from(xml, 'utf8'))
expect(result).not.toBeNull()
expect(result.type).toBe('auth')
expect(result.username).toBe('user1')
expect(result.password).toBe('secret123')
})
it('parses auth CoT with __auth', () => {
const xml = '<__auth username="u2" password="p2"/>'
const result = parseCotPayload(Buffer.from(xml, 'utf8'))
expect(result).not.toBeNull()
expect(result.type).toBe('auth')
expect(result.username).toBe('u2')
expect(result.password).toBe('p2')
})
it('returns null for auth with empty username', () => {
const xml = ''
const result = parseCotPayload(Buffer.from(xml, 'utf8'))
expect(result).toBeNull()
})
it('parses position with point.lat and point.lon (no @_ prefix)', () => {
const xml = ''
const result = parseCotPayload(Buffer.from(xml, 'utf8'))
expect(result).not.toBeNull()
expect(result.lat).toBe(5)
expect(result.lng).toBe(10)
})
it('returns null for non-XML payload', () => {
expect(parseCotPayload(Buffer.from('not xml'))).toBeNull()
})
it('uses uid as label when no contact/callsign', () => {
const xml = ''
const result = parseCotPayload(Buffer.from(xml, 'utf8'))
expect(result).not.toBeNull()
expect(result.type).toBe('cot')
expect(result.label).toBe('device-99')
})
it('uses point inside event when not at root', () => {
const xml = ''
const result = parseCotPayload(Buffer.from(xml, 'utf8'))
expect(result).not.toBeNull()
expect(result.lat).toBe(10)
expect(result.lng).toBe(20)
})
})
})