From c80152f41d49eebec5b5c3d99ddcbac4fabe4a9d Mon Sep 17 00:00:00 2001 From: Maurice Date: Tue, 20 Oct 2026 14:51:23 +0200 Subject: [PATCH] fix(tours): keep a non-canonical sac_scale as written instead of lowercasing it A value such as Alpine_Hiking is not one of the six SAC values word for word, so it now reads as an unclear grade with the raw value OpenStreetMap holds, rather than as a known T4 the planner normalised itself. --- server/src/nest/tours/tour-trail-grade.service.ts | 11 ++++++----- .../tests/unit/nest/tour-trail-grade.service.test.ts | 8 +++++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/server/src/nest/tours/tour-trail-grade.service.ts b/server/src/nest/tours/tour-trail-grade.service.ts index a8a455757..80010de2b 100644 --- a/server/src/nest/tours/tour-trail-grade.service.ts +++ b/server/src/nest/tours/tour-trail-grade.service.ts @@ -40,15 +40,16 @@ function written(value: string | undefined): string | null { } /** - * MAP-03: a way's `sac_scale` as one of the scale's six values, read as - * OpenStreetMap wrote it. A value that names no single step (`T3`, - * `hiking;mountain_hiking`, a typo) is handed on as written and never read to - * a step, so the planner says unclear rather than picking one. + * MAP-03: a way's `sac_scale` as one of the scale's six values, read exactly + * as OpenStreetMap wrote it (only trimmed). A value that is not one of them + * word for word (`T3`, `Alpine_Hiking`, `hiking;mountain_hiking`, a typo) is + * handed on as written and never read to a step, so the planner says unclear + * and shows what was written rather than a value TREK made up. */ function sacOf(value: string | undefined): Pick { const text = written(value); if (!text) return { sac_scale: null, sac_scale_unclear: null }; - const known = sacScaleSchema.safeParse(text.toLowerCase()); + const known = sacScaleSchema.safeParse(text); if (known.success) return { sac_scale: known.data, sac_scale_unclear: null }; return { sac_scale: null, sac_scale_unclear: text }; } diff --git a/server/tests/unit/nest/tour-trail-grade.service.test.ts b/server/tests/unit/nest/tour-trail-grade.service.test.ts index 1470cfc8d..101ef02cc 100644 --- a/server/tests/unit/nest/tour-trail-grade.service.test.ts +++ b/server/tests/unit/nest/tour-trail-grade.service.test.ts @@ -95,7 +95,7 @@ describe('TourTrailGradeService (BIK-05)', () => { expect(query).not.toHaveBeenCalled(); }); - it('TOUR-GRADE-007: reads sac_scale as OpenStreetMap wrote it and hands an unclear one on as written (MAP-03)', async () => { + it('TOUR-GRADE-007: reads sac_scale exactly as OpenStreetMap wrote it and hands any other value on as written (MAP-03)', async () => { query.mockResolvedValue([ way(1, { sac_scale: 'demanding_mountain_hiking' }), way(2, { sac_scale: ' Alpine_Hiking ', 'mtb:scale': '3' }), @@ -103,14 +103,16 @@ describe('TourTrailGradeService (BIK-05)', () => { way(4, { sac_scale: 'hiking;mountain_hiking' }), way(5, { sac_scale: ' ' }), way(6, { sac_scale: `x${'y'.repeat(80)}` }), + way(7, { sac_scale: ' mountain_hiking ' }), ]); - const answer = await service.grades({ way_ids: [1, 2, 3, 4, 5, 6] }); + const answer = await service.grades({ way_ids: [1, 2, 3, 4, 5, 6, 7] }); expect(answer.ways).toEqual([ { ...NO_MTB, ...NO_WAY, id: 1, sac_scale: 'demanding_mountain_hiking', sac_scale_unclear: null }, - { ...NO_MTB, ...NO_WAY, id: 2, mtb_scale: '3', sac_scale: 'alpine_hiking', sac_scale_unclear: null }, + { ...NO_MTB, ...NO_WAY, id: 2, mtb_scale: '3', sac_scale: null, sac_scale_unclear: 'Alpine_Hiking' }, { ...NO_MTB, ...NO_WAY, id: 3, sac_scale: null, sac_scale_unclear: 'T3' }, { ...NO_MTB, ...NO_WAY, id: 4, sac_scale: null, sac_scale_unclear: 'hiking;mountain_hiking' }, { ...NO_MTB, ...NO_WAY, id: 6, sac_scale: null, sac_scale_unclear: `x${'y'.repeat(63)}` }, + { ...NO_MTB, ...NO_WAY, id: 7, sac_scale: 'mountain_hiking', sac_scale_unclear: null }, ]); });