mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
15588d20c9
The ICS feed is public with a token that never expires and prints the confirmation number into the DESCRIPTION, and the shared-trip payload does a SELECT * over reservations with share_bookings on by default. Neither filters anything, so whatever lands on a trip is published the moment it is written. That is fine while a person types every booking in, and it stops being fine as soon as something writes on their behalf. reservations.ingest_state is that gate. It defaults to live, SQLite backfills every existing row on the ALTER, and no current writer names the column, so every booking visible today stays visible. Nothing writes staged yet. The predicate takes its table alias because share.service.ts queried reservations without one. A stay is withheld only when bookings point at it and all of them are staged: a hand-added hotel has no booking at all and has to keep showing up.
57 lines
2.3 KiB
TypeScript
57 lines
2.3 KiB
TypeScript
/**
|
|
* Boot migration: reservations.ingest_state.
|
|
*
|
|
* The column gates the two anonymous exports (ICS feed, shared trip) so an
|
|
* automated ingest can park a booking for review without publishing it. Every
|
|
* row that exists before the ALTER has to come out 'live', or the migration
|
|
* would empty a calendar subscription that works today.
|
|
*/
|
|
import { describe, it, expect } from 'vitest';
|
|
import Database from 'better-sqlite3';
|
|
import { createTables } from '../../../src/db/schema';
|
|
import { runMigrations } from '../../../src/db/migrations';
|
|
|
|
function dbWithReservations(): Database.Database {
|
|
const db = new Database(':memory:');
|
|
db.exec('PRAGMA journal_mode = WAL');
|
|
db.exec('PRAGMA busy_timeout = 5000');
|
|
db.exec('PRAGMA foreign_keys = ON');
|
|
createTables(db);
|
|
db.prepare("INSERT INTO users (id, username, email, password_hash) VALUES (1, 'u', 'u@example.test', 'x')").run();
|
|
db.prepare("INSERT INTO trips (id, user_id, title) VALUES (1, 1, 'T')").run();
|
|
db.prepare("INSERT INTO reservations (id, trip_id, title, type) VALUES (1, 1, 'Old Flight', 'flight')").run();
|
|
return db;
|
|
}
|
|
|
|
describe('reservations ingest_state migration', () => {
|
|
it('MIGRATE-INGEST-001: every pre-existing row comes out live', () => {
|
|
const db = dbWithReservations();
|
|
try {
|
|
runMigrations(db);
|
|
const row = db.prepare('SELECT ingest_state FROM reservations WHERE id = 1').get() as { ingest_state: string };
|
|
expect(row.ingest_state).toBe('live');
|
|
} finally {
|
|
db.close();
|
|
}
|
|
});
|
|
|
|
it('MIGRATE-INGEST-002: running the migration twice is a no-op', () => {
|
|
const db = dbWithReservations();
|
|
try {
|
|
runMigrations(db);
|
|
// Rewind the version so the step replays against a table that already has
|
|
// the column. Without the pragma_table_info guard the ALTER throws and
|
|
// runMigrations exits the process.
|
|
const version = (db.prepare('SELECT version FROM schema_version').get() as { version: number }).version;
|
|
db.prepare('UPDATE schema_version SET version = ?').run(version - 1);
|
|
runMigrations(db);
|
|
|
|
const cols = db.prepare("SELECT name FROM pragma_table_info('reservations') WHERE name = 'ingest_state'").all();
|
|
expect(cols).toHaveLength(1);
|
|
expect((db.prepare('SELECT version FROM schema_version').get() as { version: number }).version).toBe(version);
|
|
} finally {
|
|
db.close();
|
|
}
|
|
});
|
|
});
|