From e17e15685c314135cd0bdd168d44079ad1eed3e7 Mon Sep 17 00:00:00 2001 From: fuleinist Date: Fri, 24 Jul 2026 11:10:22 +1000 Subject: [PATCH] fix(flight_plan_utils): preserve full runway in addCompanyRoute when ')' is missing addCompanyRoute computed the runway substring with segments[0].indexOf(')') as the end index of slice(). When there is an opening '(' but no closing ')' before the first '.', indexOf returns -1, which String.prototype.slice interprets as 'count from the end'. The result was that the final runway character was silently dropped (e.g. 'ABC(RW26.WPT1' produced runway 'RW2' instead of 'RW26'). Fall back to the end of the segment (undefined end index) when the closing paren is absent, so malformed input no longer loses data. Fixes #478 --- lib/utils/flight_plan_utils.test.ts | 38 +++++++++++++++++++++++++++++ lib/utils/flight_plan_utils.ts | 9 ++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/lib/utils/flight_plan_utils.test.ts b/lib/utils/flight_plan_utils.test.ts index 1c80f21..96d7ca2 100644 --- a/lib/utils/flight_plan_utils.test.ts +++ b/lib/utils/flight_plan_utils.test.ts @@ -26,3 +26,41 @@ describe('FlightPlanUtils.parseHeader', () => { expect(decodeResult.formatted.items[1].label).toBe('Aircraft Route'); }); }); + +describe('FlightPlanUtils.processFlightPlan company route', () => { + const makeDecodeResult = (): DecodeResult => ({ + decoded: true, + decoder: { name: 'test', type: 'pattern-match', decodeLevel: 'full' }, + formatted: { description: 'Test', items: [] }, + raw: {}, + remaining: {}, + }); + + test('preserves full runway when closing paren is present', () => { + const decodeResult = makeDecodeResult(); + + FlightPlanUtils.processFlightPlan(decodeResult, [ + 'RP', + 'CR', + 'ABC(RW26).WPT1.WPT2', + ]); + + expect(decodeResult.raw.company_route.name).toBe('ABC'); + expect(decodeResult.raw.company_route.runway).toBe('RW26'); + }); + + test('does not drop the last runway character when closing paren is missing', () => { + const decodeResult = makeDecodeResult(); + + // Malformed input: '(' without a matching ')' before the first '.'. + FlightPlanUtils.processFlightPlan(decodeResult, [ + 'RP', + 'CR', + 'ABC(RW26.WPT1.WPT2', + ]); + + expect(decodeResult.raw.company_route.name).toBe('ABC'); + // Regression for issue: previously slice(4, -1) truncated to 'RW2'. + expect(decodeResult.raw.company_route.runway).toBe('RW26'); + }); +}); diff --git a/lib/utils/flight_plan_utils.ts b/lib/utils/flight_plan_utils.ts index 4eb9f1d..cc1bccf 100644 --- a/lib/utils/flight_plan_utils.ts +++ b/lib/utils/flight_plan_utils.ts @@ -179,7 +179,14 @@ function addCompanyRoute(decodeResult: DecodeResult, value: string) { name = segments[0]; } else { name = segments[0].slice(0, parens_idx); - runway = segments[0].slice(parens_idx + 1, segments[0].indexOf(')')); + const close_paren_idx = segments[0].indexOf(')'); + // When ')' is missing, indexOf returns -1, which slice() would + // interpret as "count from the end", silently dropping the final + // character. Fall back to the end of the segment to preserve data. + runway = segments[0].slice( + parens_idx + 1, + close_paren_idx === -1 ? undefined : close_paren_idx, + ); } let waypoints; if (segments.length > 1) {