mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-21 22:31:46 +00:00
chore: apply prettier on the entire project
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user