Skip to content

Commit 7df9736

Browse files
its-mashMohammod Al Amin Ashik
authored andcommitted
test(storage): cover applying migrations to an existing older DB
Adds a schema-presence guard (inbound_client_api_keys table + binding_type + locked_space_id columns exist after a migrate) and reproduces the field upgrade path that surfaced "no such table": a DB rolled back to pre-020, reopened, must re-apply 020/021/022 and recreate the table. Our other migration tests only ever used a fresh in-memory DB, so this migrate-an-existing-DB path was uncovered. Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
1 parent 281f386 commit 7df9736

1 file changed

Lines changed: 79 additions & 0 deletions

File tree

tests/rust/tests/database/migrations.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,82 @@ fn test_018_rewrites_stale_starter_description_only() {
151151
"operator-customized copy must be preserved"
152152
);
153153
}
154+
155+
// ---------------------------------------------------------------------------
156+
// Upgrade path — applying NEW migrations to an EXISTING (older) on-disk DB.
157+
//
158+
// Every other test here uses a FRESH in-memory DB, where all migrations run at
159+
// once — so they never catch a migration that fails to apply when a real user
160+
// opens a database created by a previous release. These two do.
161+
// ---------------------------------------------------------------------------
162+
163+
fn table_exists(db: &Database, name: &str) -> bool {
164+
db.connection()
165+
.query_row(
166+
"SELECT COUNT(*) > 0 FROM sqlite_master WHERE type='table' AND name=?1",
167+
[name],
168+
|r| r.get::<_, bool>(0),
169+
)
170+
.unwrap_or(false)
171+
}
172+
173+
fn column_exists(db: &Database, table: &str, column: &str) -> bool {
174+
let sql = format!("SELECT COUNT(*) > 0 FROM pragma_table_info('{table}') WHERE name=?1");
175+
db.connection()
176+
.query_row(&sql, [column], |r| r.get::<_, bool>(0))
177+
.unwrap_or(false)
178+
}
179+
180+
#[test]
181+
fn test_new_schema_objects_exist_after_migration() {
182+
// A fresh migrate must produce every object the API-key + mapping features
183+
// depend on — a regression guard against a migration being dropped or broken.
184+
let db = Database::open_in_memory().expect("open");
185+
assert!(
186+
table_exists(&db, "inbound_client_api_keys"),
187+
"migration 020 must create inbound_client_api_keys"
188+
);
189+
assert!(
190+
column_exists(&db, "workspace_bindings", "binding_type"),
191+
"migration 021 must add workspace_bindings.binding_type"
192+
);
193+
assert!(
194+
column_exists(&db, "inbound_clients", "locked_space_id"),
195+
"migration 022 must add inbound_clients.locked_space_id"
196+
);
197+
}
198+
199+
#[test]
200+
fn test_pending_migrations_apply_to_an_existing_older_database() {
201+
// Reproduce the real upgrade that surfaced "no such table:
202+
// inbound_client_api_keys" in the field: a DB created before 020/021/022
203+
// existed, reopened by a newer build. The pending migrations MUST apply.
204+
let dir = tempfile::tempdir().expect("tempdir");
205+
let path = dir.path().join("mcpmux.db");
206+
207+
// 1. Fully migrate, then roll the schema back to a pre-020 state.
208+
{
209+
let db = Database::open(&path).expect("open");
210+
db.connection()
211+
.execute_batch(
212+
"DELETE FROM schema_migrations WHERE version >= 20;
213+
DROP TABLE IF EXISTS inbound_client_api_keys;
214+
ALTER TABLE workspace_bindings DROP COLUMN binding_type;
215+
ALTER TABLE inbound_clients DROP COLUMN locked_space_id;",
216+
)
217+
.expect("roll schema back to pre-020");
218+
assert!(
219+
!table_exists(&db, "inbound_client_api_keys"),
220+
"precondition: the rolled-back DB is missing the table"
221+
);
222+
}
223+
224+
// 2. Reopen — run_migrations() must re-apply 020/021/022.
225+
let db = Database::open(&path).expect("reopen older DB");
226+
assert!(
227+
table_exists(&db, "inbound_client_api_keys"),
228+
"reopening an older DB must re-create inbound_client_api_keys"
229+
);
230+
assert!(column_exists(&db, "workspace_bindings", "binding_type"));
231+
assert!(column_exists(&db, "inbound_clients", "locked_space_id"));
232+
}

0 commit comments

Comments
 (0)