mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-24 07:53:09 +00:00
fix(tours): a recorded track is neither turned round nor closed by update_tour
A GPX import keeps no points of its own, and the tour read makes up a start and an end from its line. update_tour with reshape turned or closed those two made up points, so the recorded line was replaced by a router line between its ends. A reshape of a tour without points of its own is now refused with the recorded track wording, before anything is planned or saved.
This commit is contained in:
@@ -137,11 +137,14 @@ function repointingRefusal(saved: readonly TourWaypoint[], points: readonly Hike
|
||||
return null;
|
||||
}
|
||||
|
||||
/** The change with the saved points reshaped as its new points, or refused with why (a hike only). */
|
||||
function withReshape(request: TourUpdateRequest, saved: readonly TourWaypoint[], type: string): TourUpdateRequest {
|
||||
/**
|
||||
* The change with the saved points reshaped as its new points, or refused with why (a hike only). A
|
||||
* recorded track has no points of its own to turn: the two its read gives are made up from its line's ends.
|
||||
*/
|
||||
function withReshape(request: TourUpdateRequest, { tour, waypoints }: TourDetailResponse): TourUpdateRequest {
|
||||
const { reshape, ...rest } = request;
|
||||
if (!reshape || type !== 'hike') return rest;
|
||||
const reshaped = reshapedHikePoints(saved, reshape);
|
||||
if (!reshape || tour.tour_type !== 'hike') return rest;
|
||||
const reshaped = reshapedHikePoints(tour.has_waypoints === true ? waypoints : [], reshape);
|
||||
if ('refused' in reshaped) throw badRequest(reshaped.refused);
|
||||
return { ...rest, waypoints: reshaped.points };
|
||||
}
|
||||
@@ -395,7 +398,7 @@ export class HikePlanService {
|
||||
const { placeId } = asked;
|
||||
const saved = await this.tours.getTour(String(tripId), String(placeId));
|
||||
// PR1-01, PR1-02: a reshape is the saved points turned or closed, then planned as new points are.
|
||||
const request = asked.reshape ? withReshape(asked, saved.waypoints, saved.tour.tour_type) : asked;
|
||||
const request = asked.reshape ? withReshape(asked, saved) : asked;
|
||||
const { tour } = saved;
|
||||
if (tour.tour_type !== 'hike') {
|
||||
throw badRequest(
|
||||
|
||||
@@ -4,12 +4,15 @@
|
||||
* points are. The way is planned by a stub of HikeRoutingService; the write
|
||||
* is the real one over a snapshot test db.
|
||||
*/
|
||||
import { TourWaypoints } from '../../../src/db/entities/TourWaypoints.entity';
|
||||
import { Tours } from '../../../src/db/entities/Tours.entity';
|
||||
import type { McpContext } from '../../../src/nest-mcp';
|
||||
import { HikePlanMcp } from '../../../src/nest/tours/hike-plan.mcp';
|
||||
import { HikeRoutingService, type PlannedHike } from '../../../src/nest/tours/hike-routing.service';
|
||||
import { ToursModule } from '../../../src/nest/tours/tours.module';
|
||||
import { createSnapshotTestDb } from '../../helpers/db-mock';
|
||||
import { makePlace } from '../../helpers/factories/places';
|
||||
import { countRows, findRow } from '../../helpers/factories/rows';
|
||||
import { addTourWaypoints, makeTour } from '../../helpers/factories/tours';
|
||||
import { makeTrip } from '../../helpers/factories/trips';
|
||||
import { makeUser } from '../../helpers/factories/users';
|
||||
@@ -20,7 +23,7 @@ import type { GeometryPoint, HikePlanRequest } from '@trek/shared';
|
||||
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
// HIKE-RESHAPE-001 to HIKE-RESHAPE-004
|
||||
// HIKE-RESHAPE-001 to HIKE-RESHAPE-005
|
||||
|
||||
const db = createSnapshotTestDb();
|
||||
const plan = vi.fn<HikeRoutingService['plan']>();
|
||||
@@ -142,4 +145,26 @@ describe('update_tour with reshape', () => {
|
||||
mcp.updateTour({ tripId: trip.id, placeId: place.id, expected_revision: 1, reshape: 'reverse' }, ctx(user.id)),
|
||||
).rejects.toThrow('Only a hike is changed here');
|
||||
});
|
||||
|
||||
it('HIKE-RESHAPE-005: a recorded track is neither turned round nor closed, and nothing is planned or saved', async () => {
|
||||
const { user } = await makeUser(orm());
|
||||
const trip = await makeTrip(orm(), user.id);
|
||||
// A GPX import from A to B: its line only, and no points of its own (its read makes up the two ends).
|
||||
const place = await makePlace(orm(), trip.id, {
|
||||
name: 'Recorded',
|
||||
route_geometry: JSON.stringify([
|
||||
[47.5, 11.1, 800],
|
||||
[47.51, 11.12, 900],
|
||||
[47.52, 11.1, 850],
|
||||
]),
|
||||
});
|
||||
await makeTour(orm(), place.id, { origin: 'gpx' });
|
||||
const reshaped = (reshape: 'reverse' | 'close_loop') =>
|
||||
mcp.updateTour({ tripId: trip.id, placeId: place.id, expected_revision: 1, reshape }, ctx(user.id));
|
||||
await expect(reshaped('reverse')).rejects.toThrow('recorded track without points of its own');
|
||||
await expect(reshaped('close_loop')).rejects.toThrow('recorded track without points of its own');
|
||||
expect(plan).not.toHaveBeenCalled();
|
||||
expect(await countRows(orm(), TourWaypoints, { place: place.id })).toBe(0);
|
||||
expect((await findRow(orm(), Tours, { place: place.id }))?.revision).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user