From d110319cf4f16b89379ca3865ed74c74c113eea8 Mon Sep 17 00:00:00 2001 From: bwees Date: Sat, 14 Nov 2026 14:26:46 -0600 Subject: [PATCH] fix(sync): parse RoutesV2 lines --- src/lib/api/sync-lines.test.ts | 26 +++++++++++++++++++++++++- src/lib/api/sync-lines.ts | 13 +++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/lib/api/sync-lines.test.ts b/src/lib/api/sync-lines.test.ts index 71cd3908..07918349 100644 --- a/src/lib/api/sync-lines.test.ts +++ b/src/lib/api/sync-lines.test.ts @@ -1,4 +1,6 @@ -import { describe, expect, it } from 'vitest'; +import { describe, expect, expectTypeOf, it } from 'vitest'; +import type { z } from 'zod'; +import type { SyncEntityType } from './client'; import { syncStreamLineSchema } from './sync-lines'; const timetableLine = { @@ -7,13 +9,35 @@ const timetableLine = { data: { id: 't', stopId: 's', serviceDate: '2026-10-10', departures: null }, }; +const routeLine = { + type: 'RouteV2', + ack: 'RouteV2|1', + data: { + id: 'r', + shortName: 'AGR', + longName: 'Gameday Agronomy', + lightColor: '#f7941d', + darkColor: '#f7941d', + active: true, + isLoop: false, + }, +}; + describe('syncStreamLineSchema', () => { it('accepts a line the server sends', () => { expect(syncStreamLineSchema.parse(timetableLine)).toEqual(timetableLine); }); + it('accepts a RoutesV2 line', () => { + expect(syncStreamLineSchema.parse(routeLine)).toEqual(routeLine); + }); + it('rejects a line missing a column', () => { const { stopId: _, ...data } = timetableLine.data; expect(syncStreamLineSchema.safeParse({ ...timetableLine, data }).success).toBe(false); }); + + it('covers every line type in the api spec', () => { + expectTypeOf['type']>().toEqualTypeOf(); + }); }); diff --git a/src/lib/api/sync-lines.ts b/src/lib/api/sync-lines.ts index d5c0f006..32ff4544 100644 --- a/src/lib/api/sync-lines.ts +++ b/src/lib/api/sync-lines.ts @@ -18,6 +18,19 @@ export const syncStreamLineSchema = z.discriminatedUnion('type', [ }), ), line('RouteDeleteV1', z.object({ routeId: z.string() })), + line( + 'RouteV2', + z.object({ + id: z.string(), + shortName: z.string(), + longName: z.string(), + lightColor: z.string(), + darkColor: z.string(), + active: z.boolean(), + isLoop: z.boolean(), + }), + ), + line('RouteDeleteV2', z.object({ routeId: z.string() })), line( 'DirectionV1', z.object({