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 } });