mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
test(server): seed the trips integration suite through the factories
Converts the raw prepare() fixtures and reads in the trips integration suite to the ORM factories and readers: archived trips, permission rows, memberships, the copy's source rows (tagged places, assignments, to-dos, the budget category order, private packing items) and every read of what a request left behind go through insertRow, updateRows, findRow and findRows. The copied place tags are counted per place through the place_tags relation instead of a raw join.
This commit is contained in:
@@ -26,18 +26,42 @@ vi.mock('../../src/config', () => ({
|
||||
vi.mock('../../src/websocket', () => ({ broadcast: vi.fn(), broadcastToUser: vi.fn() }));
|
||||
|
||||
import { db as testDb } from '../../src/db/database';
|
||||
import { MikroORM } from '@mikro-orm/core';
|
||||
import { buildApp } from '../../src/bootstrap';
|
||||
import { resetTestDb, resetRateLimits } from '../helpers/test-db';
|
||||
import type { FactoryOrm } from '../helpers/factories/context';
|
||||
import { createUser, createAdmin, createTrip, addTripMember, createPlace, createReservation, createTag, createDayAccommodation, createBudgetItem, createPackingItem, createDayNote, createDayAssignment } from '../helpers/factories';
|
||||
import { authCookie } from '../helpers/auth';
|
||||
import { countRows, findRow, findRows, insertRow, insertRows, updateRows } from '../helpers/factories/rows';
|
||||
import { readTripDays } from '../helpers/factories/trips';
|
||||
import { readUser } from '../helpers/factories/users';
|
||||
import { setAppSetting } from '../helpers/factories/settings';
|
||||
import { tagPlace } from '../helpers/factories/places';
|
||||
import { AppSettings } from '../../src/db/entities/AppSettings.entity';
|
||||
import { BudgetCategoryOrder } from '../../src/db/entities/BudgetCategoryOrder.entity';
|
||||
import { BudgetItems } from '../../src/db/entities/BudgetItems.entity';
|
||||
import { DayAccommodations } from '../../src/db/entities/DayAccommodations.entity';
|
||||
import { DayAssignments } from '../../src/db/entities/DayAssignments.entity';
|
||||
import { DayNotes } from '../../src/db/entities/DayNotes.entity';
|
||||
import { Days } from '../../src/db/entities/Days.entity';
|
||||
import { PackingItems } from '../../src/db/entities/PackingItems.entity';
|
||||
import { Places } from '../../src/db/entities/Places.entity';
|
||||
import { Reservations } from '../../src/db/entities/Reservations.entity';
|
||||
import { Tags } from '../../src/db/entities/Tags.entity';
|
||||
import { TodoItems } from '../../src/db/entities/TodoItems.entity';
|
||||
import { TripMembers } from '../../src/db/entities/TripMembers.entity';
|
||||
import { Trips } from '../../src/db/entities/Trips.entity';
|
||||
import { Users } from '../../src/db/entities/Users.entity';
|
||||
import { invalidatePermissionsCache } from '../../src/nest/permissions/permissions-cache';
|
||||
|
||||
let nestApp: INestApplication;
|
||||
let app: Application;
|
||||
let orm: FactoryOrm;
|
||||
|
||||
beforeAll(async () => {
|
||||
nestApp = await buildApp();
|
||||
app = nestApp.getHttpAdapter().getInstance();
|
||||
orm = nestApp.get(MikroORM);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
resetTestDb(testDb);
|
||||
@@ -67,7 +91,7 @@ describe('Create trip', () => {
|
||||
expect(res.body.trip.title).toBe('Paris Adventure');
|
||||
|
||||
// Verify days were generated (5 days: Jun 1–5)
|
||||
const days = testDb.prepare('SELECT * FROM days WHERE trip_id = ? ORDER BY date').all(res.body.trip.id) as any[];
|
||||
const days = await findRows(orm, Days, { trip: res.body.trip.id }, { date: 'asc' });
|
||||
expect(days).toHaveLength(5);
|
||||
expect(days[0].date).toBe('2026-06-01');
|
||||
expect(days[4].date).toBe('2026-06-05');
|
||||
@@ -87,7 +111,7 @@ 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 = await readTripDays(orm, res.body.trip.id);
|
||||
expect(days).toHaveLength(7);
|
||||
expect(days[0].date).toBeNull();
|
||||
});
|
||||
@@ -104,7 +128,7 @@ describe('Create trip', () => {
|
||||
expect(res.body.trip.start_date).toBeNull();
|
||||
expect(res.body.trip.day_count).toBe(20);
|
||||
|
||||
const days = testDb.prepare('SELECT * FROM days WHERE trip_id = ?').all(res.body.trip.id) as any[];
|
||||
const days = await findRows(orm, Days, { trip: res.body.trip.id });
|
||||
expect(days).toHaveLength(20);
|
||||
});
|
||||
|
||||
@@ -136,7 +160,7 @@ describe('Create trip', () => {
|
||||
const { user } = createUser(testDb);
|
||||
|
||||
// Restrict trip creation to admins only
|
||||
testDb.prepare("INSERT INTO app_settings (key, value) VALUES ('perm_trip_create', 'admin')").run();
|
||||
await insertRow(orm, AppSettings, { key: 'perm_trip_create', value: 'admin' });
|
||||
await invalidatePermissionsCache();
|
||||
|
||||
const res = await request(app)
|
||||
@@ -151,7 +175,7 @@ describe('Create trip', () => {
|
||||
it('TRIP-003 — trip_create permission set to admin allows admin user', async () => {
|
||||
const { user: admin } = createAdmin(testDb);
|
||||
|
||||
testDb.prepare("INSERT INTO app_settings (key, value) VALUES ('perm_trip_create', 'admin')").run();
|
||||
await insertRow(orm, AppSettings, { key: 'perm_trip_create', value: 'admin' });
|
||||
await invalidatePermissionsCache();
|
||||
|
||||
const res = await request(app)
|
||||
@@ -211,7 +235,7 @@ describe('List trips', () => {
|
||||
const archivedTrip = createTrip(testDb, user.id, { title: 'Archived Trip' });
|
||||
|
||||
// Archive the second trip directly in the DB
|
||||
testDb.prepare('UPDATE trips SET is_archived = 1 WHERE id = ?').run(archivedTrip.id);
|
||||
await updateRows(orm, Trips, { id: archivedTrip.id }, { is_archived: 1 });
|
||||
|
||||
const res = await request(app)
|
||||
.get('/api/trips')
|
||||
@@ -229,7 +253,7 @@ describe('List trips', () => {
|
||||
const activeTrip = createTrip(testDb, user.id, { title: 'Active Trip' });
|
||||
const archivedTrip = createTrip(testDb, user.id, { title: 'Archived Trip' });
|
||||
|
||||
testDb.prepare('UPDATE trips SET is_archived = 1 WHERE id = ?').run(archivedTrip.id);
|
||||
await updateRows(orm, Trips, { id: archivedTrip.id }, { is_archived: 1 });
|
||||
|
||||
const res = await request(app)
|
||||
.get('/api/trips?archived=1')
|
||||
@@ -347,7 +371,7 @@ describe('Update trip', () => {
|
||||
const trip = createTrip(testDb, user.id, { title: 'Archived Trip' });
|
||||
|
||||
// Archive it first
|
||||
testDb.prepare('UPDATE trips SET is_archived = 1 WHERE id = ?').run(trip.id);
|
||||
await updateRows(orm, Trips, { id: trip.id }, { is_archived: 1 });
|
||||
|
||||
// Unarchive via API
|
||||
const unarchiveRes = await request(app)
|
||||
@@ -374,7 +398,7 @@ describe('Update trip', () => {
|
||||
addTripMember(testDb, trip.id, member.id);
|
||||
|
||||
// Restrict archiving to trip_owner only (this is actually the default, but set explicitly)
|
||||
testDb.prepare("INSERT OR REPLACE INTO app_settings (key, value) VALUES ('perm_trip_archive', 'trip_owner')").run();
|
||||
await setAppSetting(orm, 'perm_trip_archive', 'trip_owner');
|
||||
await invalidatePermissionsCache();
|
||||
|
||||
const res = await request(app)
|
||||
@@ -393,7 +417,7 @@ describe('Update trip', () => {
|
||||
addTripMember(testDb, trip.id, member.id);
|
||||
|
||||
// Default trip_edit is trip_owner — members should be blocked
|
||||
testDb.prepare("INSERT OR REPLACE INTO app_settings (key, value) VALUES ('perm_trip_edit', 'trip_owner')").run();
|
||||
await setAppSetting(orm, 'perm_trip_edit', 'trip_owner');
|
||||
await invalidatePermissionsCache();
|
||||
|
||||
const res = await request(app)
|
||||
@@ -419,7 +443,7 @@ 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 = await readTripDays(orm, trip.id);
|
||||
expect(days).toHaveLength(5);
|
||||
|
||||
const place = createPlace(testDb, trip.id);
|
||||
@@ -434,16 +458,16 @@ 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 = await readTripDays(orm, trip.id);
|
||||
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']);
|
||||
|
||||
const assignmentsAfter = testDb.prepare('SELECT * FROM day_assignments WHERE id = ?').get(assignment.id) as { day_id: number } | undefined;
|
||||
expect(assignmentsAfter).toBeDefined();
|
||||
const assignmentsAfter = await findRow(orm, DayAssignments, { id: assignment.id });
|
||||
expect(assignmentsAfter).not.toBeNull();
|
||||
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;
|
||||
expect(notesAfter).toBeDefined();
|
||||
const notesAfter = await findRow(orm, DayNotes, { id: note.id });
|
||||
expect(notesAfter).not.toBeNull();
|
||||
expect(notesAfter!.day_id).toBe(daysAfter[1].id);
|
||||
});
|
||||
|
||||
@@ -451,7 +475,7 @@ 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 = await readTripDays(orm, trip.id);
|
||||
const place = createPlace(testDb, trip.id);
|
||||
const a4 = createDayAssignment(testDb, days[3].id, place.id);
|
||||
const a5 = createDayAssignment(testDb, days[4].id, place.id);
|
||||
@@ -464,12 +488,12 @@ 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 = await readTripDays(orm, trip.id);
|
||||
expect(daysAfter).toHaveLength(3);
|
||||
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 = await findRows(orm, DayAssignments, { id: { $in: [a4.id, a5.id] } });
|
||||
expect(all).toHaveLength(0);
|
||||
});
|
||||
|
||||
@@ -478,11 +502,11 @@ describe('Update trip', () => {
|
||||
// a whole, even with nights still inside the trip, and its booking does not.
|
||||
const { user } = createUser(testDb);
|
||||
const trip = createTrip(testDb, user.id, { start_date: '2026-09-01', end_date: '2026-09-06' });
|
||||
const days = testDb.prepare('SELECT * FROM days WHERE trip_id = ? ORDER BY day_number').all(trip.id) as { id: number }[];
|
||||
const days = await readTripDays(orm, trip.id);
|
||||
const place = createPlace(testDb, trip.id, { name: 'Harbour Hotel' });
|
||||
const stay = createDayAccommodation(testDb, trip.id, place.id, days[3].id, days[5].id);
|
||||
const booking = createReservation(testDb, trip.id, { title: 'Harbour Hotel booking', type: 'hotel', day_id: days[3].id });
|
||||
testDb.prepare('UPDATE reservations SET accommodation_id = ? WHERE id = ?').run(stay.id, booking.id);
|
||||
await updateRows(orm, Reservations, { id: booking.id }, { accommodation_id: String(stay.id) });
|
||||
const sightseeing = createDayAssignment(testDb, days[5].id, place.id);
|
||||
|
||||
const res = await request(app)
|
||||
@@ -491,13 +515,13 @@ describe('Update trip', () => {
|
||||
.send({ start_date: '2026-09-01', end_date: '2026-09-05' });
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
const daysAfter = testDb.prepare('SELECT id, date FROM days WHERE trip_id = ? ORDER BY day_number').all(trip.id) as { id: number; date: string }[];
|
||||
const daysAfter = await readTripDays(orm, trip.id);
|
||||
expect(daysAfter.map(d => d.id)).toEqual(days.slice(0, 5).map(d => d.id));
|
||||
expect(daysAfter.at(-1)!.date).toBe('2026-09-05');
|
||||
expect(testDb.prepare('SELECT 1 FROM day_accommodations WHERE id = ?').get(stay.id)).toBeUndefined();
|
||||
expect(testDb.prepare('SELECT 1 FROM day_assignments WHERE id = ?').get(sightseeing.id)).toBeUndefined();
|
||||
expect(testDb.prepare('SELECT title FROM reservations WHERE id = ?').get(booking.id)).toEqual({ title: 'Harbour Hotel booking' });
|
||||
expect(testDb.prepare('SELECT 1 FROM places WHERE id = ?').get(place.id)).toBeDefined();
|
||||
expect(await findRow(orm, DayAccommodations, { id: stay.id })).toBeNull();
|
||||
expect(await findRow(orm, DayAssignments, { id: sightseeing.id })).toBeNull();
|
||||
expect((await findRow(orm, Reservations, { id: booking.id }))?.title).toBe('Harbour Hotel booking');
|
||||
expect(await findRow(orm, Places, { id: place.id })).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -540,8 +564,8 @@ describe('Delete trip', () => {
|
||||
expect(res.status).toBe(404);
|
||||
|
||||
// Trip still exists
|
||||
const tripInDb = testDb.prepare('SELECT id FROM trips WHERE id = ?').get(trip.id);
|
||||
expect(tripInDb).toBeDefined();
|
||||
const tripInDb = await findRow(orm, Trips, { id: trip.id });
|
||||
expect(tripInDb).not.toBeNull();
|
||||
});
|
||||
|
||||
it('TRIP-019 — Trip member cannot delete trip → 403', async () => {
|
||||
@@ -574,10 +598,10 @@ describe('Delete trip', () => {
|
||||
expect(deleteRes.body.success).toBe(true);
|
||||
|
||||
// Verify cascade: places and reservations should be gone
|
||||
const places = testDb.prepare('SELECT id FROM places WHERE trip_id = ?').all(trip.id);
|
||||
const places = await findRows(orm, Places, { trip: trip.id });
|
||||
expect(places).toHaveLength(0);
|
||||
|
||||
const reservations = testDb.prepare('SELECT id FROM reservations WHERE trip_id = ?').all(trip.id);
|
||||
const reservations = await findRows(orm, Reservations, { trip: trip.id });
|
||||
expect(reservations).toHaveLength(0);
|
||||
});
|
||||
|
||||
@@ -644,8 +668,8 @@ 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);
|
||||
expect(dbEntry).toBeDefined();
|
||||
const dbEntry = await findRow(orm, TripMembers, { trip: trip.id, user: invitee.id });
|
||||
expect(dbEntry).not.toBeNull();
|
||||
});
|
||||
|
||||
it('TRIP-013 — POST /api/trips/:id/members adds a member by username → 201', async () => {
|
||||
@@ -718,8 +742,8 @@ 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);
|
||||
expect(dbEntry).toBeUndefined();
|
||||
const dbEntry = await findRow(orm, TripMembers, { trip: trip.id, user: member.id });
|
||||
expect(dbEntry).toBeNull();
|
||||
});
|
||||
|
||||
it('TRIP-014 — Member can remove themselves from a trip → 200', async () => {
|
||||
@@ -750,9 +774,9 @@ describe('Trip members', () => {
|
||||
expect(res.status).toBe(201);
|
||||
expect(res.body.success).toBe(true);
|
||||
|
||||
const row = testDb.prepare('SELECT user_id FROM trips WHERE id = ?').get(trip.id) as { user_id: number };
|
||||
expect(row.user_id).toBe(member.id);
|
||||
const memberRows = (testDb.prepare('SELECT user_id FROM trip_members WHERE trip_id = ?').all(trip.id) as { user_id: number }[]).map(r => r.user_id);
|
||||
const row = await findRow(orm, Trips, { id: trip.id });
|
||||
expect(row?.user_id).toBe(member.id);
|
||||
const memberRows = (await findRows(orm, TripMembers, { trip: trip.id })).map(r => r.user_id);
|
||||
expect(memberRows).toContain(owner.id);
|
||||
expect(memberRows).not.toContain(member.id);
|
||||
});
|
||||
@@ -808,7 +832,7 @@ describe('Trip members', () => {
|
||||
expect(dir.body.users.some((u: any) => u.id === guestId)).toBe(false);
|
||||
|
||||
// The synthetic email can never authenticate (resolves as an unknown email).
|
||||
const email = (testDb.prepare('SELECT email FROM users WHERE id = ?').get(guestId) as any).email;
|
||||
const email = (await readUser(orm, guestId)).email;
|
||||
const login = await request(app).post('/api/auth/login').send({ email, password: 'anything' });
|
||||
expect(login.status).toBe(401);
|
||||
});
|
||||
@@ -861,13 +885,13 @@ describe('Trip members', () => {
|
||||
.send({ name: 'Junior' });
|
||||
expect(renamed.status).toBe(200);
|
||||
// #1446: the human name lives in display_name now (username is a non-shown uuid handle)
|
||||
expect((testDb.prepare('SELECT display_name FROM users WHERE id = ?').get(guestId) as any).display_name).toBe('Junior');
|
||||
expect((await readUser(orm, guestId)).display_name).toBe('Junior');
|
||||
|
||||
const removed = await request(app)
|
||||
.delete(`/api/trips/${trip.id}/guests/${guestId}`)
|
||||
.set('Cookie', authCookie(owner.id));
|
||||
expect(removed.status).toBe(200);
|
||||
expect(testDb.prepare('SELECT id FROM users WHERE id = ?').get(guestId)).toBeUndefined();
|
||||
expect(await findRow(orm, Users, { id: guestId })).toBeNull();
|
||||
});
|
||||
|
||||
it('TRIP-GUEST-003 — a guest cannot be invited as a member to any trip (#1362)', async () => {
|
||||
@@ -878,7 +902,7 @@ describe('Trip members', () => {
|
||||
.post(`/api/trips/${trip.id}/guests`)
|
||||
.set('Cookie', authCookie(owner.id))
|
||||
.send({ name: 'Eve' });
|
||||
const email = (testDb.prepare('SELECT email FROM users WHERE id = ?').get(created.body.member.id) as any).email;
|
||||
const email = (await readUser(orm, created.body.member.id)).email;
|
||||
|
||||
const invite = await request(app)
|
||||
.post(`/api/trips/${otherTrip.id}/members`)
|
||||
@@ -895,7 +919,7 @@ 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();
|
||||
await setAppSetting(orm, 'perm_member_manage', 'trip_owner');
|
||||
await invalidatePermissionsCache();
|
||||
|
||||
const res = await request(app)
|
||||
@@ -965,8 +989,8 @@ describe('Copy trip', () => {
|
||||
.send({});
|
||||
|
||||
expect(res.status).toBe(201);
|
||||
const newTrip = testDb.prepare('SELECT * FROM trips WHERE id = ?').get(res.body.trip.id) as any;
|
||||
expect(newTrip.user_id).toBe(member.id);
|
||||
const newTrip = await findRow(orm, Trips, { id: res.body.trip.id });
|
||||
expect(newTrip?.user_id).toBe(member.id);
|
||||
});
|
||||
|
||||
it('TRIP-024 — non-member cannot copy a trip → 404', async () => {
|
||||
@@ -1040,7 +1064,7 @@ describe('ICS export', () => {
|
||||
// could read the value straight out of GET /api/trips/:id.
|
||||
const { user } = createUser(testDb);
|
||||
const trip = createTrip(testDb, user.id, { title: 'Trip' });
|
||||
testDb.prepare('UPDATE trips SET feed_token = ? WHERE id = ?').run('secret-feed-token', trip.id);
|
||||
await updateRows(orm, Trips, { id: trip.id }, { feed_token: 'secret-feed-token' });
|
||||
|
||||
const one = await request(app).get(`/api/trips/${trip.id}`).set('Cookie', authCookie(user.id));
|
||||
expect(one.status).toBe(200);
|
||||
@@ -1065,18 +1089,16 @@ describe('Copy trip with data', () => {
|
||||
end_date: '2025-09-03',
|
||||
});
|
||||
|
||||
const days = testDb.prepare('SELECT * FROM days WHERE trip_id = ? ORDER BY day_number').all(trip.id) as any[];
|
||||
const days = await readTripDays(orm, trip.id);
|
||||
expect(days.length).toBe(3);
|
||||
|
||||
// Place with a tag
|
||||
const place = createPlace(testDb, trip.id, { name: 'Tower Bridge' });
|
||||
const tag = createTag(testDb, user.id, { name: 'Landmark' });
|
||||
testDb.prepare('INSERT INTO place_tags (place_id, tag_id) VALUES (?, ?)').run(place.id, tag.id);
|
||||
await tagPlace(orm, 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');
|
||||
await insertRow(orm, DayAssignments, { day: days[0].id, place: place.id, order_index: 0, notes: 'Visit in morning' });
|
||||
|
||||
// Accommodation spanning days 0→1
|
||||
createDayAccommodation(testDb, trip.id, place.id, days[0].id, days[1].id);
|
||||
@@ -1103,45 +1125,43 @@ describe('Copy trip with data', () => {
|
||||
expect(newId).not.toBe(trip.id);
|
||||
|
||||
// Days copied
|
||||
const newDays = testDb.prepare('SELECT * FROM days WHERE trip_id = ? ORDER BY day_number').all(newId) as any[];
|
||||
const newDays = await readTripDays(orm, newId);
|
||||
expect(newDays).toHaveLength(3);
|
||||
|
||||
// Place copied
|
||||
const newPlaces = testDb.prepare('SELECT * FROM places WHERE trip_id = ?').all(newId) as any[];
|
||||
const newPlaces = await findRows(orm, Places, { trip: newId });
|
||||
expect(newPlaces).toHaveLength(1);
|
||||
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[];
|
||||
expect(newTags).toHaveLength(1);
|
||||
// One place_tags row per (place, tag) pair on the copy's places.
|
||||
let newTags = 0;
|
||||
for (const p of newPlaces) newTags += await countRows(orm, Tags, { place_tags_inverse: p.id });
|
||||
expect(newTags).toBe(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 = await findRows(orm, DayAssignments, { day: { trip: newId } });
|
||||
expect(newAssignments).toHaveLength(1);
|
||||
|
||||
// Accommodation copied
|
||||
const newAccom = testDb.prepare('SELECT * FROM day_accommodations WHERE trip_id = ?').all(newId) as any[];
|
||||
const newAccom = await findRows(orm, DayAccommodations, { trip: newId });
|
||||
expect(newAccom).toHaveLength(1);
|
||||
|
||||
// Reservation copied
|
||||
const newResv = testDb.prepare('SELECT * FROM reservations WHERE trip_id = ?').all(newId) as any[];
|
||||
const newResv = await findRows(orm, Reservations, { trip: newId });
|
||||
expect(newResv).toHaveLength(1);
|
||||
|
||||
// Budget copied
|
||||
const newBudget = testDb.prepare('SELECT * FROM budget_items WHERE trip_id = ?').all(newId) as any[];
|
||||
const newBudget = await findRows(orm, BudgetItems, { trip: newId });
|
||||
expect(newBudget).toHaveLength(1);
|
||||
|
||||
// Packing copied (checked reset to 0)
|
||||
const newPacking = testDb.prepare('SELECT * FROM packing_items WHERE trip_id = ?').all(newId) as any[];
|
||||
const newPacking = await findRows(orm, PackingItems, { trip: newId });
|
||||
expect(newPacking).toHaveLength(1);
|
||||
expect(newPacking[0].checked).toBe(0);
|
||||
|
||||
// Day note copied
|
||||
const newNotes = testDb.prepare('SELECT * FROM day_notes WHERE trip_id = ?').all(newId) as any[];
|
||||
const newNotes = await findRows(orm, DayNotes, { trip: newId });
|
||||
expect(newNotes).toHaveLength(1);
|
||||
expect(newNotes[0].text).toBe('Pack early!');
|
||||
});
|
||||
@@ -1151,17 +1171,18 @@ 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);
|
||||
await insertRow(orm, TodoItems, {
|
||||
trip: trip.id, name: 'Buy tickets', checked: 0, category: 'Transport', sort_order: 0, due_date: '2026-06-01', description: 'Check Ryanair', priority: 1,
|
||||
});
|
||||
await insertRow(orm, TodoItems, {
|
||||
trip: trip.id, name: 'Book hotel', checked: 1, category: 'Accommodation', sort_order: 1, assignedUser: user.id, priority: 0,
|
||||
});
|
||||
|
||||
// Two budget category order rows
|
||||
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);
|
||||
await insertRows(orm, BudgetCategoryOrder, [
|
||||
{ trip: trip.id, category: 'Transport', sort_order: 0 },
|
||||
{ trip: trip.id, category: 'Accommodation', sort_order: 1 },
|
||||
]);
|
||||
|
||||
const res = await request(app)
|
||||
.post(`/api/trips/${trip.id}/copy`)
|
||||
@@ -1172,7 +1193,7 @@ 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 = await findRows(orm, TodoItems, { trip: newId }, { sort_order: 'asc' });
|
||||
expect(newTodos).toHaveLength(2);
|
||||
expect(newTodos[0].name).toBe('Buy tickets');
|
||||
expect(newTodos[0].category).toBe('Transport');
|
||||
@@ -1186,7 +1207,7 @@ 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 = await findRows(orm, BudgetCategoryOrder, { trip: newId }, { sort_order: 'asc' });
|
||||
expect(newOrder).toHaveLength(2);
|
||||
expect(newOrder[0]).toMatchObject({ category: 'Transport', sort_order: 0 });
|
||||
expect(newOrder[1]).toMatchObject({ category: 'Accommodation', sort_order: 1 });
|
||||
@@ -1245,7 +1266,7 @@ describe('Trip bundle', () => {
|
||||
const { user: owner } = createUser(testDb);
|
||||
const { user: member } = createUser(testDb);
|
||||
const trip = createTrip(testDb, owner.id);
|
||||
testDb.prepare('INSERT INTO trip_members (trip_id, user_id) VALUES (?, ?)').run(trip.id, member.id);
|
||||
await insertRow(orm, TripMembers, { trip: trip.id, user: member.id });
|
||||
|
||||
const res = await request(app)
|
||||
.get(`/api/trips/${trip.id}/bundle`)
|
||||
@@ -1268,9 +1289,9 @@ describe('Trip bundle', () => {
|
||||
const { user: owner } = createUser(testDb);
|
||||
const { user: member } = createUser(testDb);
|
||||
const trip = createTrip(testDb, owner.id);
|
||||
testDb.prepare('INSERT INTO trip_members (trip_id, user_id) VALUES (?, ?)').run(trip.id, member.id);
|
||||
testDb.prepare('INSERT INTO packing_items (trip_id, name, checked, sort_order) VALUES (?, ?, 0, 0)').run(trip.id, 'Tent');
|
||||
testDb.prepare('INSERT INTO packing_items (trip_id, name, checked, sort_order, is_private, owner_id) VALUES (?, ?, 0, 1, 1, ?)').run(trip.id, 'Secret gift', owner.id);
|
||||
await insertRow(orm, TripMembers, { trip: trip.id, user: member.id });
|
||||
await insertRow(orm, PackingItems, { trip: trip.id, name: 'Tent', checked: 0, sort_order: 0 });
|
||||
await insertRow(orm, PackingItems, { trip: trip.id, name: 'Secret gift', checked: 0, sort_order: 1, is_private: 1, owner: owner.id });
|
||||
|
||||
const ownerView = await request(app).get(`/api/trips/${trip.id}/bundle`).set('Cookie', authCookie(owner.id));
|
||||
expect(ownerView.body.packingItems.map((i: { name: string }) => i.name).sort()).toEqual(['Secret gift', 'Tent']);
|
||||
|
||||
Reference in New Issue
Block a user