Files
TREK/server/tests/unit/db/reservation-ingest-state-migration.test.ts
Maurice 724c88c201 style: format server and shared with prettier
A mechanical prettier --write over server/src, server/tests, server/scripts
and shared/src with the workspaces' .prettierrc; nothing but layout and
import order changes, and every file keeps the same set of imports. shared
is formatted throughout and its format baseline is empty. The server
baseline keeps the 35 files that Prettier would grow past their lint:size
limit (they need a split first, which is not a formatting change), and the
generated entity files stay as their generator writes them.
2026-10-10 21:23:37 +02:00

77 lines
3.2 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.
*
* Ported off the legacy runner (Task 0 triage: PORT) onto the real
* `Migration20200101031900_reservations_an_automated_ingest_parked_for_review`:
* migrate to the step immediately before it, seed rows with raw SQL, apply
* just that one migration, assert. The replay case runs the migration class
* directly a second time (bypassing the Migrator's own "already applied, skip"
* bookkeeping) to prove the `addColumnIfMissing` guard is what the legacy
* test's schema_version rewind was really exercising.
*/
import { Migration20200101031900_reservations_an_automated_ingest_parked_for_review as TargetMigration } from '../../../src/db/migrations/Migration20200101031900_reservations_an_automated_ingest_parked_for_review';
import {
createMigrationOrm,
migrateTo,
pendingNames,
rawExec,
rawQuery,
runMigrationDirect,
} from '../../helpers/migration-step';
import type { MikroORM } from '@mikro-orm/sqlite';
import { describe, it, expect } from 'vitest';
const TARGET = 'Migration20200101031900_reservations_an_automated_ingest_parked_for_review';
async function ormWithSeededReservation(): Promise<MikroORM> {
const orm = await createMigrationOrm();
const names = await pendingNames(orm);
const idx = names.indexOf(TARGET);
expect(idx).toBeGreaterThan(0);
await migrateTo(orm, names[idx - 1]);
await rawExec(orm, "INSERT INTO users (id, username, email, password_hash) VALUES (1, 'u', 'u@example.test', 'x')");
await rawExec(orm, "INSERT INTO trips (id, user_id, title) VALUES (1, 1, 'T')");
await rawExec(orm, "INSERT INTO reservations (id, trip_id, title, type) VALUES (1, 1, 'Old Flight', 'flight')");
return orm;
}
describe('reservations ingest_state migration', () => {
it('MIGRATE-INGEST-001: every pre-existing row comes out live', async () => {
const orm = await ormWithSeededReservation();
try {
await migrateTo(orm, TARGET);
const rows = await rawQuery<{ ingest_state: string }>(orm, 'SELECT ingest_state FROM reservations WHERE id = 1');
expect(rows[0].ingest_state).toBe('live');
} finally {
await orm.close(true);
}
}, 30000);
it('MIGRATE-INGEST-002: running the migration twice is a no-op', async () => {
const orm = await ormWithSeededReservation();
try {
await migrateTo(orm, TARGET);
// Run the migration class directly a second time against a table that
// already has the column. Without the pragma_table_info guard the ALTER
// throws.
await runMigrationDirect(orm, TargetMigration);
const cols = await rawQuery(
orm,
"SELECT name FROM pragma_table_info('reservations') WHERE name = 'ingest_state'",
);
expect(cols).toHaveLength(1);
const rows = await rawQuery<{ ingest_state: string }>(orm, 'SELECT ingest_state FROM reservations WHERE id = 1');
expect(rows[0].ingest_state).toBe('live');
} finally {
await orm.close(true);
}
}, 30000);
});