diff --git a/server/src/nest/tours/tours.service.ts b/server/src/nest/tours/tours.service.ts index 179f17d60..433d7905b 100644 --- a/server/src/nest/tours/tours.service.ts +++ b/server/src/nest/tours/tours.service.ts @@ -603,13 +603,15 @@ export class ToursService { const ids = waypoints.flatMap((point) => (point.linked_place_id ? [point.linked_place_id] : [])); if (ids.length === 0) return waypoints; const linkable = await this.waypointsRepo.linkablePlaceIds(tripId, ids); - for (const id of ids) { - if (!linkable.has(id) && (await this.toursRepo.findInTrip(tripId, id))) { - throw new DomainError(400, 'A waypoint cannot stand for a tour', { - code: 'waypoint_links_a_tour', - details: { code: 'waypoint_links_a_tour' }, - }); - } + // A place that cannot be linked may be a tour of this trip, which a stop never stands for. + const tours = await Promise.all( + ids.filter((id) => !linkable.has(id)).map((id) => this.toursRepo.findInTrip(tripId, id)), + ); + if (tours.some(Boolean)) { + throw new DomainError(400, 'A waypoint cannot stand for a tour', { + code: 'waypoint_links_a_tour', + details: { code: 'waypoint_links_a_tour' }, + }); } return waypoints.map((point) => point.linked_place_id && !linkable.has(point.linked_place_id) ? { ...point, linked_place_id: null } : point, diff --git a/server/tests/e2e/tours-planned.e2e.test.ts b/server/tests/e2e/tours-planned.e2e.test.ts index 1bd7a8509..caa53338f 100644 --- a/server/tests/e2e/tours-planned.e2e.test.ts +++ b/server/tests/e2e/tours-planned.e2e.test.ts @@ -576,7 +576,8 @@ describe('Tours planned e2e (real guards + temp SQLite)', () => { const preview = await request(server).get(`/api/tours/library/${placeId}`).set('Cookie', sessionCookie(OWNER)); expect(preview.body.line).toHaveLength(400); expect(preview.body.line[0]).toEqual([47, 11]); - expect(preview.body.line[399]).toEqual([long[1199]![0], long[1199]![1]]); + const last = long[long.length - 1] ?? []; + expect(preview.body.line[399]).toEqual([last[0], last[1]]); }); it('takes a tour over as an independent tour of this trip, remembering where it came from', async () => { @@ -728,17 +729,18 @@ describe('Tours planned e2e (real guards + temp SQLite)', () => { .set('Cookie', sessionCookie(OUTSIDER)) .send(body); expect(outsider.status).toBe(404); - for (const bad of [ + const bad = [ { ...body, categories: ['fuel'] }, { ...body, corridor_m: 9000 }, { ...body, line: [[47, 11]] }, { ...body, add: 1 }, - ]) { - expect( - (await request(server).post('/api/trips/5/tours/poi-search').set('Cookie', sessionCookie(OWNER)).send(bad)) - .status, - ).toBe(400); - } + ]; + const refused = await Promise.all( + bad.map((sent) => + request(server).post('/api/trips/5/tours/poi-search').set('Cookie', sessionCookie(OWNER)).send(sent), + ), + ); + expect(refused.map((res) => res.status)).toEqual([400, 400, 400, 400]); expect((await request(server).post('/api/trips/5/tours/poi-search').send(body)).status).toBe(401); }); }); diff --git a/server/tests/unit/nest/place-import/gpx.provider.test.ts b/server/tests/unit/nest/place-import/gpx.provider.test.ts index b934d0e24..86230d590 100644 --- a/server/tests/unit/nest/place-import/gpx.provider.test.ts +++ b/server/tests/unit/nest/place-import/gpx.provider.test.ts @@ -301,7 +301,7 @@ describe('buildTourGpx (PR1-06A)', () => { [47.3, 11.3, 650], ]), ), - )!; + ) ?? ''; expect(XMLValidator.validate(gpx)).toBe(true); const doc = parser.parse(gpx).gpx; expect(doc.metadata).toMatchObject({ @@ -327,7 +327,7 @@ describe('buildTourGpx (PR1-06A)', () => { description: ' ', website: null, stops: [], - })!; + }) ?? ''; const doc = parser.parse(gpx).gpx; expect(doc.metadata).toEqual({ name: 'Grat ' }); expect(doc.trk.desc).toBeUndefined();