Merge branch 'dev' into test

This commit is contained in:
Marek Maslowski
2026-04-04 00:53:24 +02:00
committed by GitHub
8 changed files with 59 additions and 37 deletions

View File

@@ -509,10 +509,12 @@ export function registerTools(server: McpServer, userId: number): void {
place_id: z.number().int().positive().optional().describe('Hotel place to link (hotel type only)'),
start_day_id: z.number().int().positive().optional().describe('Check-in day (hotel type only; requires place_id and end_day_id)'),
end_day_id: z.number().int().positive().optional().describe('Check-out day (hotel type only; requires place_id and start_day_id)'),
check_in: z.string().max(10).optional().describe('Check-in time (e.g. "15:00", hotel type only)'),
check_out: z.string().max(10).optional().describe('Check-out time (e.g. "11:00", hotel type only)'),
assignment_id: z.number().int().positive().optional().describe('Link to a day assignment (restaurant, train, car, cruise, event, tour, activity, other)'),
},
},
async ({ tripId, title, type, reservation_time, location, confirmation_number, notes, day_id, place_id, start_day_id, end_day_id, assignment_id }) => {
async ({ tripId, title, type, reservation_time, location, confirmation_number, notes, day_id, place_id, start_day_id, end_day_id, check_in, check_out, assignment_id }) => {
if (isDemoUser(userId)) return demoDenied();
if (!canAccessTrip(tripId, userId)) return noAccess();
@@ -542,8 +544,8 @@ export function registerTools(server: McpServer, userId: number): void {
let accommodationId: number | null = null;
if (type === 'hotel' && place_id && start_day_id && end_day_id) {
const accResult = db.prepare(
'INSERT INTO day_accommodations (trip_id, place_id, start_day_id, end_day_id, confirmation) VALUES (?, ?, ?, ?, ?)'
).run(tripId, place_id, start_day_id, end_day_id, confirmation_number || null);
'INSERT INTO day_accommodations (trip_id, place_id, start_day_id, end_day_id, check_in, check_out, confirmation) VALUES (?, ?, ?, ?, ?, ?, ?)'
).run(tripId, place_id, start_day_id, end_day_id, check_in || null, check_out || null, confirmation_number || null);
accommodationId = accResult.lastInsertRowid as number;
}
const result = db.prepare(`
@@ -599,9 +601,11 @@ export function registerTools(server: McpServer, userId: number): void {
place_id: z.number().int().positive().describe('The hotel place to link'),
start_day_id: z.number().int().positive().describe('Check-in day ID'),
end_day_id: z.number().int().positive().describe('Check-out day ID'),
check_in: z.string().max(10).optional().describe('Check-in time (e.g. "15:00")'),
check_out: z.string().max(10).optional().describe('Check-out time (e.g. "11:00")'),
},
},
async ({ tripId, reservationId, place_id, start_day_id, end_day_id }) => {
async ({ tripId, reservationId, place_id, start_day_id, end_day_id, check_in, check_out }) => {
if (isDemoUser(userId)) return demoDenied();
if (!canAccessTrip(tripId, userId)) return noAccess();
const reservation = db.prepare('SELECT * FROM reservations WHERE id = ? AND trip_id = ?').get(reservationId, tripId) as Record<string, unknown> | undefined;
@@ -619,12 +623,12 @@ export function registerTools(server: McpServer, userId: number): void {
const isNewAccommodation = !accommodationId;
db.transaction(() => {
if (accommodationId) {
db.prepare('UPDATE day_accommodations SET place_id = ?, start_day_id = ?, end_day_id = ? WHERE id = ?')
.run(place_id, start_day_id, end_day_id, accommodationId);
db.prepare('UPDATE day_accommodations SET place_id = ?, start_day_id = ?, end_day_id = ?, check_in = COALESCE(?, check_in), check_out = COALESCE(?, check_out) WHERE id = ?')
.run(place_id, start_day_id, end_day_id, check_in || null, check_out || null, accommodationId);
} else {
const accResult = db.prepare(
'INSERT INTO day_accommodations (trip_id, place_id, start_day_id, end_day_id, confirmation) VALUES (?, ?, ?, ?, ?)'
).run(tripId, place_id, start_day_id, end_day_id, reservation.confirmation_number || null);
'INSERT INTO day_accommodations (trip_id, place_id, start_day_id, end_day_id, check_in, check_out, confirmation) VALUES (?, ?, ?, ?, ?, ?, ?)'
).run(tripId, place_id, start_day_id, end_day_id, check_in || null, check_out || null, reservation.confirmation_number || null);
accommodationId = accResult.lastInsertRowid as number;
}
db.prepare('UPDATE reservations SET place_id = ?, accommodation_id = ? WHERE id = ?')

View File

@@ -74,9 +74,26 @@ router.post('/', authenticate, (req: Request, res: Response) => {
if (!checkPermission('trip_create', authReq.user.role, null, authReq.user.id, false))
return res.status(403).json({ error: 'No permission to create trips' });
const { title, description, start_date, end_date, currency, reminder_days } = req.body;
const { title, description, currency, reminder_days } = req.body;
if (!title) return res.status(400).json({ error: 'Title is required' });
if (start_date && end_date && new Date(end_date) < new Date(start_date))
const toDateStr = (d: Date) => d.toISOString().slice(0, 10);
const addDays = (d: Date, n: number) => { const r = new Date(d); r.setDate(r.getDate() + n); return r; };
let start_date: string | null = req.body.start_date || null;
let end_date: string | null = req.body.end_date || null;
if (!start_date && !end_date) {
const tomorrow = addDays(new Date(), 1);
start_date = toDateStr(tomorrow);
end_date = toDateStr(addDays(tomorrow, 7));
} else if (start_date && !end_date) {
end_date = toDateStr(addDays(new Date(start_date), 7));
} else if (!start_date && end_date) {
start_date = toDateStr(addDays(new Date(end_date), -7));
}
if (new Date(end_date!) < new Date(start_date!))
return res.status(400).json({ error: 'End date must be after start date' });
const { trip, tripId, reminderDays } = createTrip(authReq.user.id, { title, description, start_date, end_date, currency, reminder_days });

View File

@@ -89,9 +89,15 @@ describe('Create trip', () => {
expect(days[4].date).toBe('2026-06-05');
});
it('TRIP-002 — POST /api/trips without dates returns 201 and no date-specific days', async () => {
it('TRIP-002 — POST /api/trips without dates returns 201 and defaults to a 7-day window', async () => {
const { user } = createUser(testDb);
const addDays = (d: Date, n: number) => { const r = new Date(d); r.setDate(r.getDate() + n); return r; };
const toDateStr = (d: Date) => d.toISOString().slice(0, 10);
const tomorrow = addDays(new Date(), 1);
const expectedStart = toDateStr(tomorrow);
const expectedEnd = toDateStr(addDays(tomorrow, 7));
const res = await request(app)
.post('/api/trips')
.set('Cookie', authCookie(user.id))
@@ -99,12 +105,12 @@ describe('Create trip', () => {
expect(res.status).toBe(201);
expect(res.body.trip).toBeDefined();
expect(res.body.trip.start_date).toBeNull();
expect(res.body.trip.end_date).toBeNull();
expect(res.body.trip.start_date).toBe(expectedStart);
expect(res.body.trip.end_date).toBe(expectedEnd);
// Days with explicit dates should not be present
// Should have 8 days (start through end inclusive)
const daysWithDate = testDb.prepare('SELECT * FROM days WHERE trip_id = ? AND date IS NOT NULL').all(res.body.trip.id) as any[];
expect(daysWithDate).toHaveLength(0);
expect(daysWithDate).toHaveLength(8);
});
it('TRIP-001 — POST /api/trips requires a title, returns 400 without one', async () => {