use super::*;
#[test]
fn create_roadmap_is_workspace_fenced_and_never_steals() {
let root = tempfile::TempDir::new().unwrap();
let ws_a = tempfile::TempDir::new().unwrap();
let ws_b = tempfile::TempDir::new().unwrap();
let store_a = ConversationStore::new(root.path(), ws_a.path(), 100).unwrap();
let store_b = ConversationStore::new(root.path(), ws_b.path(), 100).unwrap();
let id = "1783727322129749288-shared";
store_a
.create_roadmap(id, "A's roadmap", &crate::plan::Plan::default())
.unwrap();
store_b
.create_roadmap(id, "B's roadmap", &crate::plan::Plan::default())
.unwrap();
assert_eq!(
store_a.load_roadmap(id).unwrap().unwrap().title,
"A's roadmap",
"workspace A's roadmap must survive B's import of the same id"
);
assert_eq!(
store_b.load_roadmap(id).unwrap().unwrap().title,
"B's roadmap"
);
assert_eq!(store_a.list_roadmaps().unwrap().len(), 1);
assert_eq!(store_b.list_roadmaps().unwrap().len(), 1);
}
#[test]
fn create_roadmap_overwrites_within_the_same_workspace() {
let root = tempfile::TempDir::new().unwrap();
let ws = tempfile::TempDir::new().unwrap();
let store = ConversationStore::new(root.path(), ws.path(), 100).unwrap();
let id = "rm-1";
store
.create_roadmap(id, "first", &crate::plan::Plan::default())
.unwrap();
store
.create_roadmap(id, "second", &crate::plan::Plan::default())
.unwrap();
assert_eq!(store.load_roadmap(id).unwrap().unwrap().title, "second");
assert_eq!(store.list_roadmaps().unwrap().len(), 1);
}
#[test]
fn migrate_roadmaps_pk_rebuilds_legacy_table_losslessly() {
let dir = tempfile::TempDir::new().unwrap();
let conn = Connection::open(dir.path().join("t.db")).unwrap();
conn.execute_batch(
"CREATE TABLE roadmaps (
id TEXT PRIMARY KEY,
workspace_key TEXT NOT NULL,
title TEXT NOT NULL DEFAULT '',
tree TEXT NOT NULL DEFAULT '',
schema_version INTEGER NOT NULL DEFAULT 1,
created_at_claim INTEGER NOT NULL DEFAULT 0,
updated_at_claim INTEGER NOT NULL DEFAULT 0
);
INSERT INTO roadmaps (id, workspace_key, title) VALUES ('x', 'wsA', 'kept');",
)
.unwrap();
migrate_roadmaps_pk(&conn).unwrap();
let sql: String = conn
.query_row(
"SELECT sql FROM sqlite_master WHERE type='table' AND name='roadmaps'",
[],
|r| r.get(0),
)
.unwrap();
assert!(
sql.to_ascii_lowercase()
.contains("primary key (id, workspace_key)"),
"PK must be composite after migration: {sql}"
);
let title: String = conn
.query_row("SELECT title FROM roadmaps WHERE id='x'", [], |r| r.get(0))
.unwrap();
assert_eq!(title, "kept");
conn.execute(
"INSERT INTO roadmaps (id, workspace_key, title) VALUES ('x', 'wsB', 'other')",
[],
)
.unwrap();
let n: i64 = conn
.query_row("SELECT COUNT(*) FROM roadmaps WHERE id='x'", [], |r| {
r.get(0)
})
.unwrap();
assert_eq!(n, 2, "same id can coexist across workspaces");
migrate_roadmaps_pk(&conn).unwrap();
let n2: i64 = conn
.query_row("SELECT COUNT(*) FROM roadmaps", [], |r| r.get(0))
.unwrap();
assert_eq!(n2, 2);
}
#[test]
fn fresh_store_roadmaps_table_is_already_composite() {
let root = tempfile::TempDir::new().unwrap();
let ws = tempfile::TempDir::new().unwrap();
let _store = ConversationStore::new(root.path(), ws.path(), 100).unwrap();
let conn = Connection::open(root.path().join(DB_FILE)).unwrap();
let sql: String = conn
.query_row(
"SELECT sql FROM sqlite_master WHERE type='table' AND name='roadmaps'",
[],
|r| r.get(0),
)
.unwrap();
assert!(sql
.to_ascii_lowercase()
.contains("primary key (id, workspace_key)"));
}