chore: apply prettier on the entire project

This commit is contained in:
jubnl
2026-05-25 21:59:42 +02:00
parent c130ed41be
commit 6bcdfbc34b
488 changed files with 82986 additions and 45830 deletions
+154 -145
View File
@@ -2,9 +2,31 @@
* Trips API integration tests.
* Covers TRIP-001 through TRIP-022.
*/
import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vitest';
import request from 'supertest';
import { createApp } from '../../src/app';
import { runMigrations } from '../../src/db/migrations';
import { createTables } from '../../src/db/schema';
import { loginAttempts, mfaAttempts } from '../../src/routes/auth';
import { invalidatePermissionsCache } from '../../src/services/permissions';
import { authCookie } from '../helpers/auth';
import {
createUser,
createAdmin,
createTrip,
addTripMember,
createPlace,
createReservation,
createTag,
createDayAccommodation,
createBudgetItem,
createPackingItem,
createDayNote,
createDayAssignment,
} from '../helpers/factories';
import { resetTestDb } from '../helpers/test-db';
import type { Application } from 'express';
import request from 'supertest';
import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vitest';
// ─────────────────────────────────────────────────────────────────────────────
// Step 1: Bare in-memory DB — schema applied in beforeAll after mocks register
@@ -21,16 +43,32 @@ const { testDb, dbMock } = vi.hoisted(() => {
closeDb: () => {},
reinitialize: () => {},
getPlaceWithTags: (placeId: number) => {
const place: any = db.prepare(`
const place: any = db
.prepare(
`
SELECT p.*, c.name as category_name, c.color as category_color, c.icon as category_icon
FROM places p LEFT JOIN categories c ON p.category_id = c.id WHERE p.id = ?
`).get(placeId);
`,
)
.get(placeId);
if (!place) return null;
const tags = db.prepare(`SELECT t.* FROM tags t JOIN place_tags pt ON t.id = pt.tag_id WHERE pt.place_id = ?`).all(placeId);
return { ...place, category: place.category_id ? { id: place.category_id, name: place.category_name, color: place.category_color, icon: place.category_icon } : null, tags };
const tags = db
.prepare(`SELECT t.* FROM tags t JOIN place_tags pt ON t.id = pt.tag_id WHERE pt.place_id = ?`)
.all(placeId);
return {
...place,
category: place.category_id
? { id: place.category_id, name: place.category_name, color: place.category_color, icon: place.category_icon }
: null,
tags,
};
},
canAccessTrip: (tripId: any, userId: number) =>
db.prepare(`SELECT t.id, t.user_id FROM trips t LEFT JOIN trip_members m ON m.trip_id = t.id AND m.user_id = ? WHERE t.id = ? AND (t.user_id = ? OR m.user_id IS NOT NULL)`).get(userId, tripId, userId),
db
.prepare(
`SELECT t.id, t.user_id FROM trips t LEFT JOIN trip_members m ON m.trip_id = t.id AND m.user_id = ? WHERE t.id = ? AND (t.user_id = ? OR m.user_id IS NOT NULL)`,
)
.get(userId, tripId, userId),
isOwner: (tripId: any, userId: number) =>
!!db.prepare('SELECT id FROM trips WHERE id = ? AND user_id = ?').get(tripId, userId),
};
@@ -45,25 +83,21 @@ vi.mock('../../src/config', () => ({
updateJwtSecret: () => {},
}));
import { createApp } from '../../src/app';
import { createTables } from '../../src/db/schema';
import { runMigrations } from '../../src/db/migrations';
import { resetTestDb } from '../helpers/test-db';
import { createUser, createAdmin, createTrip, addTripMember, createPlace, createReservation, createTag, createDayAccommodation, createBudgetItem, createPackingItem, createDayNote, createDayAssignment } from '../helpers/factories';
import { authCookie } from '../helpers/auth';
import { loginAttempts, mfaAttempts } from '../../src/routes/auth';
import { invalidatePermissionsCache } from '../../src/services/permissions';
const app: Application = createApp();
beforeAll(() => { createTables(testDb); runMigrations(testDb); });
beforeAll(() => {
createTables(testDb);
runMigrations(testDb);
});
beforeEach(() => {
resetTestDb(testDb);
loginAttempts.clear();
mfaAttempts.clear();
invalidatePermissionsCache();
});
afterAll(() => { testDb.close(); });
afterAll(() => {
testDb.close();
});
// ─────────────────────────────────────────────────────────────────────────────
// Create trip (TRIP-001, TRIP-002, TRIP-003)
@@ -103,7 +137,9 @@ describe('Create trip', () => {
expect(res.body.trip.end_date).toBeNull();
// Should have 7 dateless placeholder days
const days = testDb.prepare('SELECT * FROM days WHERE trip_id = ? ORDER BY day_number').all(res.body.trip.id) as any[];
const days = testDb
.prepare('SELECT * FROM days WHERE trip_id = ? ORDER BY day_number')
.all(res.body.trip.id) as any[];
expect(days).toHaveLength(7);
expect(days[0].date).toBeNull();
});
@@ -170,10 +206,7 @@ describe('Create trip', () => {
testDb.prepare("INSERT INTO app_settings (key, value) VALUES ('perm_trip_create', 'admin')").run();
invalidatePermissionsCache();
const res = await request(app)
.post('/api/trips')
.set('Cookie', authCookie(admin.id))
.send({ title: 'Admin Trip' });
const res = await request(app).post('/api/trips').set('Cookie', authCookie(admin.id)).send({ title: 'Admin Trip' });
expect(res.status).toBe(201);
});
@@ -201,18 +234,14 @@ describe('List trips', () => {
// Add member to one of stranger's trips
addTripMember(testDb, memberTrip.id, member.id);
const ownerRes = await request(app)
.get('/api/trips')
.set('Cookie', authCookie(owner.id));
const ownerRes = await request(app).get('/api/trips').set('Cookie', authCookie(owner.id));
expect(ownerRes.status).toBe(200);
const ownerTripIds = ownerRes.body.trips.map((t: any) => t.id);
expect(ownerTripIds).toContain(ownTrip.id);
expect(ownerTripIds).not.toContain(memberTrip.id);
const memberRes = await request(app)
.get('/api/trips')
.set('Cookie', authCookie(member.id));
const memberRes = await request(app).get('/api/trips').set('Cookie', authCookie(member.id));
expect(memberRes.status).toBe(200);
const memberTripIds = memberRes.body.trips.map((t: any) => t.id);
@@ -229,9 +258,7 @@ describe('List trips', () => {
// Archive the second trip directly in the DB
testDb.prepare('UPDATE trips SET is_archived = 1 WHERE id = ?').run(archivedTrip.id);
const res = await request(app)
.get('/api/trips')
.set('Cookie', authCookie(user.id));
const res = await request(app).get('/api/trips').set('Cookie', authCookie(user.id));
expect(res.status).toBe(200);
const tripIds = res.body.trips.map((t: any) => t.id);
@@ -247,9 +274,7 @@ describe('List trips', () => {
testDb.prepare('UPDATE trips SET is_archived = 1 WHERE id = ?').run(archivedTrip.id);
const res = await request(app)
.get('/api/trips?archived=1')
.set('Cookie', authCookie(user.id));
const res = await request(app).get('/api/trips?archived=1').set('Cookie', authCookie(user.id));
expect(res.status).toBe(200);
const tripIds = res.body.trips.map((t: any) => t.id);
@@ -267,9 +292,7 @@ describe('Get trip', () => {
const { user } = createUser(testDb);
const trip = createTrip(testDb, user.id, { title: 'My Trip', description: 'A lovely trip' });
const res = await request(app)
.get(`/api/trips/${trip.id}`)
.set('Cookie', authCookie(user.id));
const res = await request(app).get(`/api/trips/${trip.id}`).set('Cookie', authCookie(user.id));
expect(res.status).toBe(200);
expect(res.body.trip).toBeDefined();
@@ -283,24 +306,19 @@ describe('Get trip', () => {
const { user: other } = createUser(testDb);
const trip = createTrip(testDb, owner.id, { title: "Owner's Trip" });
const res = await request(app)
.get(`/api/trips/${trip.id}`)
.set('Cookie', authCookie(other.id));
const res = await request(app).get(`/api/trips/${trip.id}`).set('Cookie', authCookie(other.id));
expect(res.status).toBe(404);
expect(res.body.error).toMatch(/not found/i);
});
it('TRIP-017 — Member can access trip → 200', async () => {
const { user: owner } = createUser(testDb);
const { user: member } = createUser(testDb);
const trip = createTrip(testDb, owner.id, { title: 'Shared Trip' });
addTripMember(testDb, trip.id, member.id);
const res = await request(app)
.get(`/api/trips/${trip.id}`)
.set('Cookie', authCookie(member.id));
const res = await request(app).get(`/api/trips/${trip.id}`).set('Cookie', authCookie(member.id));
expect(res.status).toBe(200);
expect(res.body.trip.id).toBe(trip.id);
@@ -310,9 +328,7 @@ describe('Get trip', () => {
it('TRIP-006 — GET /api/trips/:id for non-existent trip returns 404', async () => {
const { user } = createUser(testDb);
const res = await request(app)
.get('/api/trips/999999')
.set('Cookie', authCookie(user.id));
const res = await request(app).get('/api/trips/999999').set('Cookie', authCookie(user.id));
expect(res.status).toBe(404);
});
@@ -350,9 +366,7 @@ describe('Update trip', () => {
expect(archiveRes.body.trip.is_archived).toBe(1);
// Should not appear in the normal list
const listRes = await request(app)
.get('/api/trips')
.set('Cookie', authCookie(user.id));
const listRes = await request(app).get('/api/trips').set('Cookie', authCookie(user.id));
const tripIds = listRes.body.trips.map((t: any) => t.id);
expect(tripIds).not.toContain(trip.id);
@@ -375,9 +389,7 @@ describe('Update trip', () => {
expect(unarchiveRes.body.trip.is_archived).toBe(0);
// Should appear in the normal list again
const listRes = await request(app)
.get('/api/trips')
.set('Cookie', authCookie(user.id));
const listRes = await request(app).get('/api/trips').set('Cookie', authCookie(user.id));
const tripIds = listRes.body.trips.map((t: any) => t.id);
expect(tripIds).toContain(trip.id);
@@ -435,7 +447,10 @@ describe('Update trip', () => {
const { user } = createUser(testDb);
const trip = createTrip(testDb, user.id, { start_date: '2026-08-01', end_date: '2026-08-05' });
const days = testDb.prepare('SELECT * FROM days WHERE trip_id = ? ORDER BY day_number').all(trip.id) as { id: number; date: string }[];
const days = testDb.prepare('SELECT * FROM days WHERE trip_id = ? ORDER BY day_number').all(trip.id) as {
id: number;
date: string;
}[];
expect(days).toHaveLength(5);
const place = createPlace(testDb, trip.id);
@@ -450,15 +465,28 @@ describe('Update trip', () => {
expect(res.status).toBe(200);
const daysAfter = testDb.prepare('SELECT * FROM days WHERE trip_id = ? ORDER BY day_number').all(trip.id) as { id: number; date: string | null }[];
const daysAfter = testDb.prepare('SELECT * FROM days WHERE trip_id = ? ORDER BY day_number').all(trip.id) as {
id: number;
date: string | null;
}[];
expect(daysAfter).toHaveLength(5);
expect(daysAfter.map(d => d.date)).toEqual(['2026-08-11', '2026-08-12', '2026-08-13', '2026-08-14', '2026-08-15']);
expect(daysAfter.map((d) => d.date)).toEqual([
'2026-08-11',
'2026-08-12',
'2026-08-13',
'2026-08-14',
'2026-08-15',
]);
const assignmentsAfter = testDb.prepare('SELECT * FROM day_assignments WHERE id = ?').get(assignment.id) as { day_id: number } | undefined;
const assignmentsAfter = testDb.prepare('SELECT * FROM day_assignments WHERE id = ?').get(assignment.id) as
| { day_id: number }
| undefined;
expect(assignmentsAfter).toBeDefined();
expect(assignmentsAfter!.day_id).toBe(daysAfter[0].id);
const notesAfter = testDb.prepare('SELECT * FROM day_notes WHERE id = ?').get(note.id) as { day_id: number } | undefined;
const notesAfter = testDb.prepare('SELECT * FROM day_notes WHERE id = ?').get(note.id) as
| { day_id: number }
| undefined;
expect(notesAfter).toBeDefined();
expect(notesAfter!.day_id).toBe(daysAfter[1].id);
});
@@ -467,7 +495,9 @@ describe('Update trip', () => {
const { user } = createUser(testDb);
const trip = createTrip(testDb, user.id, { start_date: '2026-09-01', end_date: '2026-09-05' });
const days = testDb.prepare('SELECT * FROM days WHERE trip_id = ? ORDER BY day_number').all(trip.id) as { id: number }[];
const days = testDb.prepare('SELECT * FROM days WHERE trip_id = ? ORDER BY day_number').all(trip.id) as {
id: number;
}[];
const place = createPlace(testDb, trip.id);
const a4 = createDayAssignment(testDb, days[3].id, place.id);
const a5 = createDayAssignment(testDb, days[4].id, place.id);
@@ -480,12 +510,17 @@ describe('Update trip', () => {
expect(res.status).toBe(200);
const daysAfter = testDb.prepare('SELECT * FROM days WHERE trip_id = ? ORDER BY day_number').all(trip.id) as { id: number; date: string | null }[];
const daysAfter = testDb.prepare('SELECT * FROM days WHERE trip_id = ? ORDER BY day_number').all(trip.id) as {
id: number;
date: string | null;
}[];
expect(daysAfter).toHaveLength(3);
expect(daysAfter.every(d => d.date !== null)).toBe(true);
expect(daysAfter.every((d) => d.date !== null)).toBe(true);
// Overflow days and their assignments deleted
const all = testDb.prepare('SELECT * FROM day_assignments WHERE id IN (?, ?)').all(a4.id, a5.id) as { id: number }[];
const all = testDb.prepare('SELECT * FROM day_assignments WHERE id IN (?, ?)').all(a4.id, a5.id) as {
id: number;
}[];
expect(all).toHaveLength(0);
});
});
@@ -499,17 +534,13 @@ describe('Delete trip', () => {
const { user } = createUser(testDb);
const trip = createTrip(testDb, user.id, { title: 'To Delete' });
const deleteRes = await request(app)
.delete(`/api/trips/${trip.id}`)
.set('Cookie', authCookie(user.id));
const deleteRes = await request(app).delete(`/api/trips/${trip.id}`).set('Cookie', authCookie(user.id));
expect(deleteRes.status).toBe(200);
expect(deleteRes.body.success).toBe(true);
// Trip should no longer be accessible
const getRes = await request(app)
.get(`/api/trips/${trip.id}`)
.set('Cookie', authCookie(user.id));
const getRes = await request(app).get(`/api/trips/${trip.id}`).set('Cookie', authCookie(user.id));
expect(getRes.status).toBe(404);
});
@@ -519,9 +550,7 @@ describe('Delete trip', () => {
const { user: other } = createUser(testDb);
const trip = createTrip(testDb, owner.id, { title: "Owner's Trip" });
const res = await request(app)
.delete(`/api/trips/${trip.id}`)
.set('Cookie', authCookie(other.id));
const res = await request(app).delete(`/api/trips/${trip.id}`).set('Cookie', authCookie(other.id));
// getTripOwner finds the trip (it exists); checkPermission fails for non-members → 403
expect(res.status).toBe(403);
@@ -537,9 +566,7 @@ describe('Delete trip', () => {
const trip = createTrip(testDb, owner.id, { title: 'Shared Trip' });
addTripMember(testDb, trip.id, member.id);
const res = await request(app)
.delete(`/api/trips/${trip.id}`)
.set('Cookie', authCookie(member.id));
const res = await request(app).delete(`/api/trips/${trip.id}`).set('Cookie', authCookie(member.id));
expect(res.status).toBe(403);
expect(res.body.error).toMatch(/permission/i);
@@ -553,9 +580,7 @@ describe('Delete trip', () => {
createPlace(testDb, trip.id, { name: 'Eiffel Tower' });
createReservation(testDb, trip.id, { title: 'Hotel Booking', type: 'hotel' });
const deleteRes = await request(app)
.delete(`/api/trips/${trip.id}`)
.set('Cookie', authCookie(user.id));
const deleteRes = await request(app).delete(`/api/trips/${trip.id}`).set('Cookie', authCookie(user.id));
expect(deleteRes.status).toBe(200);
expect(deleteRes.body.success).toBe(true);
@@ -573,9 +598,7 @@ describe('Delete trip', () => {
const { user: owner } = createUser(testDb);
const trip = createTrip(testDb, owner.id, { title: "User's Trip" });
const res = await request(app)
.delete(`/api/trips/${trip.id}`)
.set('Cookie', authCookie(admin.id));
const res = await request(app).delete(`/api/trips/${trip.id}`).set('Cookie', authCookie(admin.id));
expect(res.status).toBe(200);
expect(res.body.success).toBe(true);
@@ -584,9 +607,7 @@ describe('Delete trip', () => {
it('TRIP-018 — DELETE /api/trips/:id for non-existent trip returns 404', async () => {
const { user } = createUser(testDb);
const res = await request(app)
.delete('/api/trips/999999')
.set('Cookie', authCookie(user.id));
const res = await request(app).delete('/api/trips/999999').set('Cookie', authCookie(user.id));
expect(res.status).toBe(404);
});
@@ -603,9 +624,7 @@ describe('Trip members', () => {
const trip = createTrip(testDb, owner.id, { title: 'Team Trip' });
addTripMember(testDb, trip.id, member.id);
const res = await request(app)
.get(`/api/trips/${trip.id}/members`)
.set('Cookie', authCookie(owner.id));
const res = await request(app).get(`/api/trips/${trip.id}/members`).set('Cookie', authCookie(owner.id));
expect(res.status).toBe(200);
expect(res.body.owner).toBeDefined();
@@ -631,7 +650,9 @@ describe('Trip members', () => {
expect(res.body.member.role).toBe('member');
// Verify in DB
const dbEntry = testDb.prepare('SELECT * FROM trip_members WHERE trip_id = ? AND user_id = ?').get(trip.id, invitee.id);
const dbEntry = testDb
.prepare('SELECT * FROM trip_members WHERE trip_id = ? AND user_id = ?')
.get(trip.id, invitee.id);
expect(dbEntry).toBeDefined();
});
@@ -705,7 +726,9 @@ describe('Trip members', () => {
expect(res.body.success).toBe(true);
// Verify removal in DB
const dbEntry = testDb.prepare('SELECT * FROM trip_members WHERE trip_id = ? AND user_id = ?').get(trip.id, member.id);
const dbEntry = testDb
.prepare('SELECT * FROM trip_members WHERE trip_id = ? AND user_id = ?')
.get(trip.id, member.id);
expect(dbEntry).toBeUndefined();
});
@@ -731,7 +754,9 @@ describe('Trip members', () => {
addTripMember(testDb, trip.id, member.id);
// Restrict member management to trip_owner (default)
testDb.prepare("INSERT OR REPLACE INTO app_settings (key, value) VALUES ('perm_member_manage', 'trip_owner')").run();
testDb
.prepare("INSERT OR REPLACE INTO app_settings (key, value) VALUES ('perm_member_manage', 'trip_owner')")
.run();
invalidatePermissionsCache();
const res = await request(app)
@@ -748,9 +773,7 @@ describe('Trip members', () => {
const { user: stranger } = createUser(testDb);
const trip = createTrip(testDb, owner.id, { title: 'Private Trip' });
const res = await request(app)
.get(`/api/trips/${trip.id}/members`)
.set('Cookie', authCookie(stranger.id));
const res = await request(app).get(`/api/trips/${trip.id}/members`).set('Cookie', authCookie(stranger.id));
expect(res.status).toBe(404);
});
@@ -765,10 +788,7 @@ describe('Copy trip', () => {
const { user } = createUser(testDb);
const trip = createTrip(testDb, user.id, { title: 'Original Trip', description: 'Desc' });
const res = await request(app)
.post(`/api/trips/${trip.id}/copy`)
.set('Cookie', authCookie(user.id))
.send({});
const res = await request(app).post(`/api/trips/${trip.id}/copy`).set('Cookie', authCookie(user.id)).send({});
expect(res.status).toBe(201);
expect(res.body.trip).toBeDefined();
@@ -795,10 +815,7 @@ describe('Copy trip', () => {
const trip = createTrip(testDb, owner.id, { title: 'Shared Trip' });
addTripMember(testDb, trip.id, member.id);
const res = await request(app)
.post(`/api/trips/${trip.id}/copy`)
.set('Cookie', authCookie(member.id))
.send({});
const res = await request(app).post(`/api/trips/${trip.id}/copy`).set('Cookie', authCookie(member.id)).send({});
expect(res.status).toBe(201);
const newTrip = testDb.prepare('SELECT * FROM trips WHERE id = ?').get(res.body.trip.id) as any;
@@ -810,10 +827,7 @@ describe('Copy trip', () => {
const { user: stranger } = createUser(testDb);
const trip = createTrip(testDb, owner.id, { title: 'Private Trip' });
const res = await request(app)
.post(`/api/trips/${trip.id}/copy`)
.set('Cookie', authCookie(stranger.id))
.send({});
const res = await request(app).post(`/api/trips/${trip.id}/copy`).set('Cookie', authCookie(stranger.id)).send({});
expect(res.status).toBe(404);
});
@@ -821,10 +835,7 @@ describe('Copy trip', () => {
it('TRIP-024 — copy of non-existent trip returns 404', async () => {
const { user } = createUser(testDb);
const res = await request(app)
.post('/api/trips/999999/copy')
.set('Cookie', authCookie(user.id))
.send({});
const res = await request(app).post('/api/trips/999999/copy').set('Cookie', authCookie(user.id)).send({});
expect(res.status).toBe(404);
});
@@ -839,9 +850,7 @@ describe('ICS export', () => {
const { user } = createUser(testDb);
const trip = createTrip(testDb, user.id, { title: 'Calendar Trip' });
const res = await request(app)
.get(`/api/trips/${trip.id}/export.ics`)
.set('Cookie', authCookie(user.id));
const res = await request(app).get(`/api/trips/${trip.id}/export.ics`).set('Cookie', authCookie(user.id));
expect(res.status).toBe(200);
expect(res.headers['content-type']).toMatch(/text\/calendar/);
@@ -854,9 +863,7 @@ describe('ICS export', () => {
const { user: stranger } = createUser(testDb);
const trip = createTrip(testDb, owner.id, { title: 'Private Trip' });
const res = await request(app)
.get(`/api/trips/${trip.id}/export.ics`)
.set('Cookie', authCookie(stranger.id));
const res = await request(app).get(`/api/trips/${trip.id}/export.ics`).set('Cookie', authCookie(stranger.id));
expect(res.status).toBe(404);
});
@@ -892,9 +899,9 @@ describe('Copy trip with data', () => {
testDb.prepare('INSERT INTO place_tags (place_id, tag_id) VALUES (?, ?)').run(place.id, tag.id);
// Day assignment
testDb.prepare(
'INSERT INTO day_assignments (day_id, place_id, order_index, notes) VALUES (?, ?, 0, ?)'
).run(days[0].id, place.id, 'Visit in morning');
testDb
.prepare('INSERT INTO day_assignments (day_id, place_id, order_index, notes) VALUES (?, ?, 0, ?)')
.run(days[0].id, place.id, 'Visit in morning');
// Accommodation spanning days 0→1
createDayAccommodation(testDb, trip.id, place.id, days[0].id, days[1].id);
@@ -930,15 +937,15 @@ describe('Copy trip with data', () => {
expect(newPlaces[0].name).toBe('Tower Bridge');
// Place tag copied
const newTags = testDb.prepare(
'SELECT pt.* FROM place_tags pt JOIN places p ON p.id = pt.place_id WHERE p.trip_id = ?'
).all(newId) as any[];
const newTags = testDb
.prepare('SELECT pt.* FROM place_tags pt JOIN places p ON p.id = pt.place_id WHERE p.trip_id = ?')
.all(newId) as any[];
expect(newTags).toHaveLength(1);
// Assignment copied
const newAssignments = testDb.prepare(
'SELECT da.* FROM day_assignments da JOIN days d ON d.id = da.day_id WHERE d.trip_id = ?'
).all(newId) as any[];
const newAssignments = testDb
.prepare('SELECT da.* FROM day_assignments da JOIN days d ON d.id = da.day_id WHERE d.trip_id = ?')
.all(newId) as any[];
expect(newAssignments).toHaveLength(1);
// Accommodation copied
@@ -969,15 +976,21 @@ describe('Copy trip with data', () => {
const trip = createTrip(testDb, user.id, { title: 'Todo Trip' });
// Two todos: one checked and assigned — both should arrive unchecked and unassigned
testDb.prepare(
'INSERT INTO todo_items (trip_id, name, checked, category, sort_order, due_date, description, priority) VALUES (?, ?, ?, ?, ?, ?, ?, ?)'
).run(trip.id, 'Buy tickets', 0, 'Transport', 0, '2026-06-01', 'Check Ryanair', 1);
testDb.prepare(
'INSERT INTO todo_items (trip_id, name, checked, category, sort_order, assigned_user_id, priority) VALUES (?, ?, ?, ?, ?, ?, ?)'
).run(trip.id, 'Book hotel', 1, 'Accommodation', 1, user.id, 0);
testDb
.prepare(
'INSERT INTO todo_items (trip_id, name, checked, category, sort_order, due_date, description, priority) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
)
.run(trip.id, 'Buy tickets', 0, 'Transport', 0, '2026-06-01', 'Check Ryanair', 1);
testDb
.prepare(
'INSERT INTO todo_items (trip_id, name, checked, category, sort_order, assigned_user_id, priority) VALUES (?, ?, ?, ?, ?, ?, ?)',
)
.run(trip.id, 'Book hotel', 1, 'Accommodation', 1, user.id, 0);
// Two budget category order rows
const insOrder = testDb.prepare('INSERT INTO budget_category_order (trip_id, category, sort_order) VALUES (?, ?, ?)');
const insOrder = testDb.prepare(
'INSERT INTO budget_category_order (trip_id, category, sort_order) VALUES (?, ?, ?)',
);
insOrder.run(trip.id, 'Transport', 0);
insOrder.run(trip.id, 'Accommodation', 1);
@@ -990,7 +1003,9 @@ describe('Copy trip with data', () => {
const newId = res.body.trip.id;
// Todos copied with checked reset and assigned_user_id nulled
const newTodos = testDb.prepare('SELECT * FROM todo_items WHERE trip_id = ? ORDER BY sort_order').all(newId) as any[];
const newTodos = testDb
.prepare('SELECT * FROM todo_items WHERE trip_id = ? ORDER BY sort_order')
.all(newId) as any[];
expect(newTodos).toHaveLength(2);
expect(newTodos[0].name).toBe('Buy tickets');
expect(newTodos[0].category).toBe('Transport');
@@ -1004,7 +1019,9 @@ describe('Copy trip with data', () => {
expect(newTodos[1].assigned_user_id).toBeNull();
// Budget category order copied
const newOrder = testDb.prepare('SELECT category, sort_order FROM budget_category_order WHERE trip_id = ? ORDER BY sort_order').all(newId) as any[];
const newOrder = testDb
.prepare('SELECT category, sort_order FROM budget_category_order WHERE trip_id = ? ORDER BY sort_order')
.all(newId) as any[];
expect(newOrder).toHaveLength(2);
expect(newOrder[0]).toMatchObject({ category: 'Transport', sort_order: 0 });
expect(newOrder[1]).toMatchObject({ category: 'Accommodation', sort_order: 1 });
@@ -1020,9 +1037,7 @@ describe('Trip bundle', () => {
const { user } = createUser(testDb);
const trip = createTrip(testDb, user.id, { start_date: '2026-07-01', end_date: '2026-07-03' });
const res = await request(app)
.get(`/api/trips/${trip.id}/bundle`)
.set('Cookie', authCookie(user.id));
const res = await request(app).get(`/api/trips/${trip.id}/bundle`).set('Cookie', authCookie(user.id));
expect(res.status).toBe(200);
expect(res.body.trip).toBeDefined();
@@ -1040,9 +1055,7 @@ describe('Trip bundle', () => {
it('BUNDLE-002 — returns 404 for trip that does not exist', async () => {
const { user } = createUser(testDb);
const res = await request(app)
.get('/api/trips/999999/bundle')
.set('Cookie', authCookie(user.id));
const res = await request(app).get('/api/trips/999999/bundle').set('Cookie', authCookie(user.id));
expect(res.status).toBe(404);
});
@@ -1052,9 +1065,7 @@ describe('Trip bundle', () => {
const { user: other } = createUser(testDb);
const trip = createTrip(testDb, owner.id);
const res = await request(app)
.get(`/api/trips/${trip.id}/bundle`)
.set('Cookie', authCookie(other.id));
const res = await request(app).get(`/api/trips/${trip.id}/bundle`).set('Cookie', authCookie(other.id));
expect(res.status).toBe(404);
});
@@ -1065,9 +1076,7 @@ describe('Trip bundle', () => {
const trip = createTrip(testDb, owner.id);
testDb.prepare('INSERT INTO trip_members (trip_id, user_id) VALUES (?, ?)').run(trip.id, member.id);
const res = await request(app)
.get(`/api/trips/${trip.id}/bundle`)
.set('Cookie', authCookie(member.id));
const res = await request(app).get(`/api/trips/${trip.id}/bundle`).set('Cookie', authCookie(member.id));
expect(res.status).toBe(200);
expect(res.body.trip.id).toBe(trip.id);