From fb208a88e24aed5665774ad2cb59eb218640b3da Mon Sep 17 00:00:00 2001 From: Maurice Date: Tue, 20 Oct 2026 18:19:00 +0200 Subject: [PATCH] test(tours): read the MCP fields and stage edits the way the other tests do The confirmed field is checked through z.object(...).pick() as the dawarich and doc-sync tests read their tool shapes, and the stage edits pass typed waypoints through the request contract as the walk tests do. The previous forms sent the test typecheck out of memory. --- server/tests/unit/nest/tour-change-save.mcp.test.ts | 8 +++----- server/tests/unit/nest/tour-change-undo.mcp.test.ts | 6 ++---- server/tests/unit/nest/tours.service.test.ts | 12 ++++++------ 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/server/tests/unit/nest/tour-change-save.mcp.test.ts b/server/tests/unit/nest/tour-change-save.mcp.test.ts index 5142d4b17..b692d6b3c 100644 --- a/server/tests/unit/nest/tour-change-save.mcp.test.ts +++ b/server/tests/unit/nest/tour-change-save.mcp.test.ts @@ -413,12 +413,10 @@ describe('TourChangeMcp.saveTourChange (MCP-T03)', () => { ]; for (const term of terms) expect(options.description).toContain(term); // The fields are zod schemas from @trek/shared, read here by what they answer. - const shape = options.inputSchema as ZodRawShape; const fields = ['tripId', 'placeId', 'changes', 'expected_revision', 'proposal_id', 'confirmed']; - expect(Object.keys(shape)).toEqual(fields); - const confirmed = shape.confirmed; - if (!confirmed) throw new Error('the tool takes no confirmed field'); - const yes = [true, false, undefined].map((value) => z.safeParse(confirmed, value).success); + expect(Object.keys(options.inputSchema as ZodRawShape)).toEqual(fields); + const confirmed = z.object(options.inputSchema as ZodRawShape).pick({ confirmed: true }); + const yes = [true, false, undefined].map((value) => confirmed.safeParse({ confirmed: value }).success); expect(yes).toEqual([true, false, false]); const isAddonEnabled = vi.fn(async (id: string) => id === 'tours'); expect(await options.when?.(ctx(1), { addons: { isAddonEnabled } })).toBe(true); diff --git a/server/tests/unit/nest/tour-change-undo.mcp.test.ts b/server/tests/unit/nest/tour-change-undo.mcp.test.ts index 319ac8d01..ced6579f4 100644 --- a/server/tests/unit/nest/tour-change-undo.mcp.test.ts +++ b/server/tests/unit/nest/tour-change-undo.mcp.test.ts @@ -508,10 +508,8 @@ describe('TourChangeUndoMcp (MCP-T04)', () => { const entry = getEntry(TourChangeUndoMcp as unknown as ClassRef, 'undoTourChange'); if (!entry || entry.kind !== 'tool') throw new Error('undoTourChange is no MCP tool'); // The fields are zod schemas from @trek/shared, read here by what they answer. - const shape = entry.options.inputSchema as ZodRawShape; - const confirmed = shape.confirmed; - if (!confirmed) throw new Error('the tool takes no confirmed field'); - const yes = [true, false, undefined].map((value) => z.safeParse(confirmed, value).success); + const confirmed = z.object(entry.options.inputSchema as ZodRawShape).pick({ confirmed: true }); + const yes = [true, false, undefined].map((value) => confirmed.safeParse({ confirmed: value }).success); expect(yes).toEqual([true, false, false]); for (const term of ['prepare_tour_undo', 'expected_revision', 'conflict', 'get_tour_plan']) { expect(entry.options.description).toContain(term); diff --git a/server/tests/unit/nest/tours.service.test.ts b/server/tests/unit/nest/tours.service.test.ts index cbc9ce720..599989aac 100644 --- a/server/tests/unit/nest/tours.service.test.ts +++ b/server/tests/unit/nest/tours.service.test.ts @@ -33,7 +33,7 @@ import { } from '../../helpers/tours-repos'; import type { EntityClass } from '@mikro-orm/core'; import { Logger, NotFoundException } from '@nestjs/common'; -import { tourCreateRequestSchema, type TourCreateRequest } from '@trek/shared'; +import { tourCreateRequestSchema, type TourCreateInput, type TourCreateRequest, type TourWaypoint } from '@trek/shared'; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; @@ -786,8 +786,7 @@ describe('ToursService stages on days (MDT-02)', () => { } /** An edit of the hut-to-hut tour as the API takes it: the points a read gave back go through the request contract. */ - const editOf = (changes: Record): TourCreateRequest => - tourCreateRequestSchema.parse({ ...hutToHut, ...changes }); + const editOf = (overrides: Partial) => tourCreateRequestSchema.parse({ ...hutToHut, ...overrides }); const heldStages = async (placeId: number) => (await findRows(await sharedTestOrm(testDb), DayAssignments, { place: placeId }, { id: 'asc' })).map( @@ -811,7 +810,7 @@ describe('ToursService stages on days (MDT-02)', () => { it('MDT-SVC-005: a saved change takes only the stages it took away off their days, and tells every client', async () => { const { created, placeId, keys, days, held } = await onThreeDays(); // The third point no longer ends a stage: the stage that started there is gone, the others keep their keys. - const joined = created.waypoints.map(({ stage_end: _end, ...point }, index) => + const joined: TourWaypoint[] = created.waypoints.map(({ stage_end: _end, ...point }, index) => index === 1 ? { ...point, stage_end: true } : point, ); await service.updateTour(tripId, String(placeId), editOf({ waypoints: joined }), 'socket'); @@ -829,7 +828,8 @@ describe('ToursService stages on days (MDT-02)', () => { it('MDT-SVC-006: a change that keeps every stage leaves every day as it was and says nothing', async () => { const { created, placeId, keys } = await onThreeDays(); - await service.updateTour(tripId, String(placeId), editOf({ name: 'Renamed', waypoints: created.waypoints })); + const kept: TourWaypoint[] = created.waypoints; + await service.updateTour(tripId, String(placeId), editOf({ name: 'Renamed', waypoints: kept })); expect(await heldStages(placeId)).toEqual(keys.slice(0, 3)); expect(broadcast.mock.calls.filter(([, event]) => event === 'assignment:deleted')).toEqual([]); }); @@ -854,7 +854,7 @@ describe('ToursService stages on days (MDT-02)', () => { if (event === 'assignment:deleted') throw new Error('socket closed'); }); const warn = vi.spyOn(Logger.prototype, 'warn').mockImplementation(() => undefined); - const dayTour = created.waypoints.map(({ stage_end: _end, ...point }) => point); + const dayTour: TourWaypoint[] = created.waypoints.map(({ stage_end: _end, ...point }) => point); await expect( service.updateTour(tripId, String(placeId), editOf({ duration_seconds: 3600, waypoints: dayTour })), ).resolves.toMatchObject({ tour: { stage_count: null } });