mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 06:56:35 +00:00
89 lines
3.6 KiB
TypeScript
89 lines
3.6 KiB
TypeScript
/**
|
|
* Calendar-feed visibility e2e — proves the staged-booking gate holds on the
|
|
* one path that needs no session at all: the public /api/feed/trip/:token.ics.
|
|
*
|
|
* tests/e2e/feeds.e2e.test.ts cannot cover this. It mocks buildTripCalendar and
|
|
* never creates a reservations table, so it owns the calendar parts and the SQL
|
|
* filter is invisible to it. This file runs the REAL CalendarService against a
|
|
* real migrated SQLite instead, and asserts on the bytes a calendar client
|
|
* would receive.
|
|
*/
|
|
import { db } from '../../src/db/database';
|
|
import { TrekExceptionFilter } from '../../src/nest/common/trek-exception.filter';
|
|
import { FeedsModule } from '../../src/nest/feeds/feeds.module';
|
|
import { createTestMikroOrmModule } from '../helpers/test-orm';
|
|
import { TestUnitOfWorkModule } from '../helpers/test-uow';
|
|
import { Test } from '@nestjs/testing';
|
|
|
|
import type { Server } from 'http';
|
|
import request from 'supertest';
|
|
import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest';
|
|
|
|
vi.mock('../../src/db/database', async () => {
|
|
const { createSnapshotTestDb } = await import('../helpers/db-mock');
|
|
const db = createSnapshotTestDb();
|
|
return {
|
|
db,
|
|
canAccessTrip: (tripId: number | string, userId: number) =>
|
|
db.prepare('SELECT id, user_id FROM trips WHERE id = ? AND user_id = ?').get(tripId, userId),
|
|
isOwner: () => true,
|
|
getPlaceWithTags: () => null,
|
|
closeDb: () => {},
|
|
reinitialize: () => {},
|
|
};
|
|
});
|
|
|
|
describe('Calendar feed visibility e2e (real CalendarService over temp SQLite)', () => {
|
|
let server: Server;
|
|
let app: Awaited<ReturnType<typeof build>>;
|
|
let feedToken: string;
|
|
|
|
async function build() {
|
|
const moduleRef = await Test.createTestingModule({
|
|
imports: [await TestUnitOfWorkModule.forRoot(db), await createTestMikroOrmModule(db), FeedsModule],
|
|
}).compile();
|
|
const nest = moduleRef.createNestApplication();
|
|
nest.useGlobalFilters(new TrekExceptionFilter());
|
|
await nest.init();
|
|
return nest;
|
|
}
|
|
|
|
beforeAll(async () => {
|
|
db.prepare(
|
|
"INSERT INTO users (id, username, email, password_hash, role) VALUES (1, 'e2e-user', 'e2e@example.test', 'x', 'user')",
|
|
).run();
|
|
feedToken = 'feed-token-visibility';
|
|
db.prepare(
|
|
"INSERT INTO trips (id, user_id, title, start_date, end_date, feed_token) VALUES (1, 1, 'Kyoto', '2026-09-01', '2026-09-05', ?)",
|
|
).run(feedToken);
|
|
db.prepare("INSERT INTO days (id, trip_id, day_number, date) VALUES (1, 1, 1, '2026-09-01')").run();
|
|
db.prepare(
|
|
`INSERT INTO reservations (trip_id, day_id, title, type, status, reservation_time, confirmation_number, ingest_state)
|
|
VALUES (1, 1, 'Parked Flight', 'flight', 'confirmed', '2026-09-01T08:00', 'SECRET1', 'staged')`,
|
|
).run();
|
|
db.prepare(
|
|
`INSERT INTO reservations (trip_id, day_id, title, type, status, reservation_time, confirmation_number)
|
|
VALUES (1, 1, 'Booked Flight', 'flight', 'confirmed', '2026-09-01T12:00', 'OPEN1')`,
|
|
).run();
|
|
|
|
app = await build();
|
|
server = app.getHttpServer();
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await app.close();
|
|
});
|
|
|
|
it('the public feed serves the live booking and neither the staged one nor its confirmation number', async () => {
|
|
const res = await request(server).get(`/api/feed/trip/${feedToken}.ics`);
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(res.headers['content-type']).toContain('text/calendar');
|
|
expect(res.text).toContain('SUMMARY:Booked Flight');
|
|
expect(res.text).not.toContain('Parked Flight');
|
|
// The number rides in the DESCRIPTION, so search the whole document.
|
|
expect(res.text).not.toContain('SECRET1');
|
|
expect(res.text).toContain('OPEN1');
|
|
});
|
|
});
|