From e91082a56f96da7a2e595c1d4bc0e68bb7831888 Mon Sep 17 00:00:00 2001 From: fuleinist Date: Wed, 12 Aug 2026 05:05:13 +1000 Subject: [PATCH] fix(ResultFormatter): guard heading/airspeed/groundspeed/mach/day/month/departureDay/arrivalDay against NaN (issue #494) --- lib/utils/result_formatter.test.ts | 54 ++++++++++++++++++++++++++++++ lib/utils/result_formatter.ts | 24 +++++++++++++ 2 files changed, 78 insertions(+) diff --git a/lib/utils/result_formatter.test.ts b/lib/utils/result_formatter.test.ts index 510c5c5..fce4381 100644 --- a/lib/utils/result_formatter.test.ts +++ b/lib/utils/result_formatter.test.ts @@ -95,3 +95,57 @@ describe('ResultFormatter timestamp family NaN guards', () => { expect(dr.formatted.items[0].value).toBe('02:08:00'); }); }); + +describe('ResultFormatter numeric formatters NaN guards', () => { + // Non-numeric ACARS fields ('---', '***', whitespace) become NaN via + // Number()/parseInt(); these must disappear from formatted.items rather + // than surfacing as literal "NaN" strings (issue #494). + const cases: Array<[string, (dr: DecodeResult) => void, string]> = [ + ['heading', (dr) => ResultFormatter.heading(dr, NaN), 'heading'], + ['groundspeed', (dr) => ResultFormatter.groundspeed(dr, NaN), 'groundspeed'], + ['airspeed', (dr) => ResultFormatter.airspeed(dr, NaN), 'airspeed'], + ['mach', (dr) => ResultFormatter.mach(dr, NaN), 'mach'], + ['day', (dr) => ResultFormatter.day(dr, NaN), 'day'], + ['month', (dr) => ResultFormatter.month(dr, NaN), 'month'], + ['departureDay', (dr) => ResultFormatter.departureDay(dr, NaN), 'departure_day'], + ['arrivalDay', (dr) => ResultFormatter.arrivalDay(dr, NaN), 'arrival_day'], + ]; + + test.each(cases)( + '%s skips NaN instead of pushing a "NaN" item', + (_name, invoke, rawKey) => { + const dr = makeDecodeResult(); + invoke(dr); + expect(dr.formatted.items.length).toBe(0); + expect((dr.raw as Record)[rawKey]).toBeUndefined(); + }, + ); + + test('heading still formats a valid value', () => { + const dr = makeDecodeResult(); + ResultFormatter.heading(dr, 270); + expect(dr.raw.heading).toBe(270); + expect(dr.formatted.items[0].value).toBe('270'); + }); + + test('groundspeed still formats a valid value', () => { + const dr = makeDecodeResult(); + ResultFormatter.groundspeed(dr, 420); + expect(dr.raw.groundspeed).toBe(420); + expect(dr.formatted.items[0].value).toBe('420 knots'); + }); + + test('mach still formats a valid value', () => { + const dr = makeDecodeResult(); + ResultFormatter.mach(dr, 0.84); + expect(dr.raw.mach).toBe(0.84); + expect(dr.formatted.items[0].value).toBe('0.84 mach'); + }); + + test('day still formats a valid value', () => { + const dr = makeDecodeResult(); + ResultFormatter.day(dr, 12); + expect(dr.raw.day).toBe(12); + expect(dr.formatted.items[0].value).toBe('12'); + }); +}); diff --git a/lib/utils/result_formatter.ts b/lib/utils/result_formatter.ts index 515d4c4..1fddc18 100644 --- a/lib/utils/result_formatter.ts +++ b/lib/utils/result_formatter.ts @@ -303,6 +303,9 @@ export class ResultFormatter { } static groundspeed(decodeResult: DecodeResult, value: number) { + if (isNaN(value)) { + return; + } decodeResult.raw.groundspeed = value; decodeResult.formatted.items.push({ type: 'aircraft_groundspeed', @@ -313,6 +316,9 @@ export class ResultFormatter { } static airspeed(decodeResult: DecodeResult, value: number) { + if (isNaN(value)) { + return; + } decodeResult.raw.airspeed = value; decodeResult.formatted.items.push({ type: 'airspeed', @@ -323,6 +329,9 @@ export class ResultFormatter { } static mach(decodeResult: DecodeResult, value: number) { + if (isNaN(value)) { + return; + } decodeResult.raw.mach = value; decodeResult.formatted.items.push({ type: 'mach', @@ -363,6 +372,9 @@ export class ResultFormatter { } static heading(decodeResult: DecodeResult, value: number) { + if (isNaN(value)) { + return; + } decodeResult.raw.heading = value; decodeResult.formatted.items.push({ type: 'heading', @@ -460,6 +472,9 @@ export class ResultFormatter { } static day(decodeResult: DecodeResult, day: number) { + if (isNaN(day)) { + return; + } decodeResult.raw.day = day; decodeResult.formatted.items.push({ type: 'day', @@ -470,6 +485,9 @@ export class ResultFormatter { } static month(decodeResult: DecodeResult, month: number) { + if (isNaN(month)) { + return; + } decodeResult.raw.month = month; decodeResult.formatted.items.push({ type: 'month', @@ -480,6 +498,9 @@ export class ResultFormatter { } static departureDay(decodeResult: DecodeResult, day: number) { + if (isNaN(day)) { + return; + } decodeResult.raw.departure_day = day; decodeResult.formatted.items.push({ type: 'day', @@ -490,6 +511,9 @@ export class ResultFormatter { } static arrivalDay(decodeResult: DecodeResult, day: number) { + if (isNaN(day)) { + return; + } decodeResult.raw.arrival_day = day; decodeResult.formatted.items.push({ type: 'day',