fix(db): append the incoming leg mode migration so upgrades pick it up

This commit is contained in:
Maurice
2026-08-12 09:05:11 +02:00
committed by Maurice
parent 15c8675959
commit 27ffd3ec68
2 changed files with 35 additions and 11 deletions
+14 -11
View File
@@ -3772,17 +3772,6 @@ function runMigrations(db: Database.Database): void {
if (!err.message?.includes('duplicate column name')) throw err;
}
},
// Per-segment travel mode for boundary legs: a leg whose ORIGIN is not a place
// (booking arrival, morning hotel) stores its mode on the DESTINATION stop.
// NULL = inherit the day default. INERT whenever the previous timeline element
// is itself a place (that place's outgoing leg_transport_mode wins).
() => {
try {
db.exec('ALTER TABLE day_assignments ADD COLUMN incoming_leg_transport_mode TEXT');
} catch (err: any) {
if (!err.message?.includes('duplicate column name')) throw err;
}
},
// School holidays are a visual Vacay calendar layer. Keep them separate from
// public holidays so applyHolidayCalendars never removes vacation entries for
// school-break dates. Appended LAST: this branch was cut before #1435/#1281
@@ -3876,6 +3865,20 @@ function runMigrations(db: Database.Database): void {
const hasColor = db.prepare("SELECT 1 FROM pragma_table_info('day_notes') WHERE name = 'color'").get();
if (!hasColor) db.exec('ALTER TABLE day_notes ADD COLUMN color TEXT');
},
// Per-segment travel mode for boundary legs: a leg whose ORIGIN is not a place
// (booking arrival, morning hotel) stores its mode on the DESTINATION stop.
// NULL = inherit the day default. INERT whenever the previous timeline element
// is itself a place (that place's outgoing leg_transport_mode wins).
// Appended LAST: the array is index-addressed against schema_version, so a slot
// inserted anywhere above this line is simply skipped on every existing database.
() => {
try {
db.exec('ALTER TABLE day_assignments ADD COLUMN incoming_leg_transport_mode TEXT');
} catch (err: any) {
if (!err.message?.includes('duplicate column name')) throw err;
}
},
];
if (currentVersion < migrations.length) {
@@ -32,6 +32,27 @@ describe('incoming_leg_transport_mode migration', () => {
const cols = testDb.prepare(`PRAGMA table_info(day_assignments)`).all() as { name: string }[];
expect(cols.map(c => c.name)).toContain('incoming_leg_transport_mode');
});
// The case above starts from an empty database and runs every migration, so it
// passes wherever this one sits in the array. Existing installs replay only the
// slots above their schema_version, which is why the migration has to be the
// trailing entry: from anywhere but the end it is silently skipped on upgrade.
it('still lands on a database that already holds every earlier migration', () => {
const upgraded = new Database(':memory:');
upgraded.exec('PRAGMA foreign_keys = ON');
createTables(upgraded);
runMigrations(upgraded);
const { version } = upgraded.prepare('SELECT version FROM schema_version').get() as { version: number };
upgraded.exec('ALTER TABLE day_assignments DROP COLUMN incoming_leg_transport_mode');
upgraded.prepare('UPDATE schema_version SET version = ?').run(version - 1);
runMigrations(upgraded);
const cols = upgraded.prepare(`PRAGMA table_info(day_assignments)`).all() as { name: string }[];
expect(cols.map(c => c.name)).toContain('incoming_leg_transport_mode');
upgraded.close();
});
});
// ─────────────────────────────────────────────────────────────────────────────