mirror of
https://github.com/PR0M3TH3AN/Marlin.git
synced 2025-09-09 15:48:43 +00:00
237 lines
6.3 KiB
Rust
237 lines
6.3 KiB
Rust
// libmarlin/src/db_tests.rs
|
|
|
|
use super::db;
|
|
use rusqlite::Connection;
|
|
use tempfile::tempdir;
|
|
|
|
fn open_mem() -> Connection {
|
|
// helper to open an in-memory DB with migrations applied
|
|
db::open(":memory:").expect("open in-memory DB")
|
|
}
|
|
|
|
#[test]
|
|
fn ensure_tag_path_creates_hierarchy() {
|
|
let conn = open_mem();
|
|
// create foo/bar/baz
|
|
let leaf = db::ensure_tag_path(&conn, "foo/bar/baz").unwrap();
|
|
// foo should exist as a root tag
|
|
let foo: i64 = conn
|
|
.query_row(
|
|
"SELECT id FROM tags WHERE name='foo' AND parent_id IS NULL",
|
|
[],
|
|
|r| r.get(0),
|
|
)
|
|
.unwrap();
|
|
// bar should be child of foo
|
|
let bar: i64 = conn
|
|
.query_row(
|
|
"SELECT id FROM tags WHERE name='bar' AND parent_id = ?1",
|
|
[foo],
|
|
|r| r.get(0),
|
|
)
|
|
.unwrap();
|
|
// baz should be child of bar, and its ID is what we got back
|
|
let baz: i64 = conn
|
|
.query_row(
|
|
"SELECT id FROM tags WHERE name='baz' AND parent_id = ?1",
|
|
[bar],
|
|
|r| r.get(0),
|
|
)
|
|
.unwrap();
|
|
assert_eq!(leaf, baz);
|
|
}
|
|
|
|
#[test]
|
|
fn upsert_attr_inserts_and_updates() {
|
|
let conn = open_mem();
|
|
// insert a dummy file
|
|
conn.execute(
|
|
"INSERT INTO files(path, size, mtime) VALUES (?1, 0, 0)",
|
|
["a.txt"],
|
|
)
|
|
.unwrap();
|
|
let fid: i64 = conn
|
|
.query_row("SELECT id FROM files WHERE path='a.txt'", [], |r| r.get(0))
|
|
.unwrap();
|
|
|
|
// insert
|
|
db::upsert_attr(&conn, fid, "k", "v").unwrap();
|
|
let v1: String = conn
|
|
.query_row(
|
|
"SELECT value FROM attributes WHERE file_id=?1 AND key='k'",
|
|
[fid],
|
|
|r| r.get(0),
|
|
)
|
|
.unwrap();
|
|
assert_eq!(v1, "v");
|
|
|
|
// update
|
|
db::upsert_attr(&conn, fid, "k", "v2").unwrap();
|
|
let v2: String = conn
|
|
.query_row(
|
|
"SELECT value FROM attributes WHERE file_id=?1 AND key='k'",
|
|
[fid],
|
|
|r| r.get(0),
|
|
)
|
|
.unwrap();
|
|
assert_eq!(v2, "v2");
|
|
}
|
|
|
|
#[test]
|
|
fn file_id_returns_id_and_errors_on_missing() {
|
|
let conn = open_mem();
|
|
|
|
// insert a single file
|
|
conn.execute(
|
|
"INSERT INTO files(path, size, mtime) VALUES (?1, 0, 0)",
|
|
["exist.txt"],
|
|
)
|
|
.unwrap();
|
|
|
|
// fetch its id via raw SQL
|
|
let fid: i64 = conn
|
|
.query_row("SELECT id FROM files WHERE path='exist.txt'", [], |r| {
|
|
r.get(0)
|
|
})
|
|
.unwrap();
|
|
|
|
// db::file_id should return the same id for existing paths
|
|
let looked_up = db::file_id(&conn, "exist.txt").unwrap();
|
|
assert_eq!(looked_up, fid);
|
|
|
|
// querying a missing path should yield an error
|
|
assert!(db::file_id(&conn, "missing.txt").is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn add_and_remove_links_and_backlinks() {
|
|
let conn = open_mem();
|
|
// create two files
|
|
conn.execute(
|
|
"INSERT INTO files(path, size, mtime) VALUES (?1, 0, 0)",
|
|
["one.txt"],
|
|
)
|
|
.unwrap();
|
|
conn.execute(
|
|
"INSERT INTO files(path, size, mtime) VALUES (?1, 0, 0)",
|
|
["two.txt"],
|
|
)
|
|
.unwrap();
|
|
let src: i64 = conn
|
|
.query_row("SELECT id FROM files WHERE path='one.txt'", [], |r| {
|
|
r.get(0)
|
|
})
|
|
.unwrap();
|
|
let dst: i64 = conn
|
|
.query_row("SELECT id FROM files WHERE path='two.txt'", [], |r| {
|
|
r.get(0)
|
|
})
|
|
.unwrap();
|
|
|
|
// add a link of type "ref"
|
|
db::add_link(&conn, src, dst, Some("ref")).unwrap();
|
|
let out = db::list_links(&conn, "one%", None, None).unwrap();
|
|
assert_eq!(out.len(), 1);
|
|
assert_eq!(out[0].2.as_deref(), Some("ref"));
|
|
|
|
// backlinks should mirror
|
|
let back = db::find_backlinks(&conn, "two%").unwrap();
|
|
assert_eq!(back.len(), 1);
|
|
assert_eq!(back[0].1.as_deref(), Some("ref"));
|
|
|
|
// remove it
|
|
db::remove_link(&conn, src, dst, Some("ref")).unwrap();
|
|
let empty = db::list_links(&conn, "one%", None, None).unwrap();
|
|
assert!(empty.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn collections_roundtrip() {
|
|
let conn = open_mem();
|
|
// create collection "C"
|
|
let cid = db::ensure_collection(&conn, "C").unwrap();
|
|
|
|
// add a file
|
|
conn.execute(
|
|
"INSERT INTO files(path, size, mtime) VALUES (?1, 0, 0)",
|
|
["f.txt"],
|
|
)
|
|
.unwrap();
|
|
let fid: i64 = conn
|
|
.query_row("SELECT id FROM files WHERE path='f.txt'", [], |r| r.get(0))
|
|
.unwrap();
|
|
|
|
db::add_file_to_collection(&conn, cid, fid).unwrap();
|
|
let files = db::list_collection(&conn, "C").unwrap();
|
|
assert_eq!(files, vec!["f.txt".to_string()]);
|
|
}
|
|
|
|
#[test]
|
|
fn views_save_and_query() {
|
|
let conn = open_mem();
|
|
db::save_view(&conn, "v1", "some_query").unwrap();
|
|
let all = db::list_views(&conn).unwrap();
|
|
assert_eq!(all, vec![("v1".to_string(), "some_query".to_string())]);
|
|
|
|
let q = db::view_query(&conn, "v1").unwrap();
|
|
assert_eq!(q, "some_query");
|
|
}
|
|
|
|
#[test]
|
|
fn backup_and_restore_cycle() {
|
|
let tmp = tempdir().unwrap();
|
|
let db_path = tmp.path().join("data.db");
|
|
let live = db::open(&db_path).unwrap();
|
|
|
|
// insert a file
|
|
live.execute(
|
|
"INSERT INTO files(path, size, mtime) VALUES (?1, 0, 0)",
|
|
["x.bin"],
|
|
)
|
|
.unwrap();
|
|
|
|
// backup
|
|
let backup = db::backup(&db_path).unwrap();
|
|
// remove original
|
|
std::fs::remove_file(&db_path).unwrap();
|
|
// restore
|
|
db::restore(&backup, &db_path).unwrap();
|
|
|
|
// reopen and check that x.bin survived
|
|
let conn2 = db::open(&db_path).unwrap();
|
|
let cnt: i64 = conn2
|
|
.query_row("SELECT COUNT(*) FROM files WHERE path='x.bin'", [], |r| {
|
|
r.get(0)
|
|
})
|
|
.unwrap();
|
|
assert_eq!(cnt, 1);
|
|
}
|
|
|
|
mod dirty_helpers {
|
|
use super::{db, open_mem};
|
|
|
|
#[test]
|
|
fn mark_and_take_dirty_works() {
|
|
let conn = open_mem();
|
|
conn.execute(
|
|
"INSERT INTO files(path, size, mtime) VALUES (?1, 0, 0)",
|
|
["dummy.txt"],
|
|
)
|
|
.unwrap();
|
|
let fid: i64 = conn
|
|
.query_row("SELECT id FROM files WHERE path='dummy.txt'", [], |r| {
|
|
r.get(0)
|
|
})
|
|
.unwrap();
|
|
|
|
db::mark_dirty(&conn, fid).unwrap();
|
|
db::mark_dirty(&conn, fid).unwrap();
|
|
|
|
let dirty = db::take_dirty(&conn).unwrap();
|
|
assert_eq!(dirty, vec![fid]);
|
|
|
|
let empty = db::take_dirty(&conn).unwrap();
|
|
assert!(empty.is_empty());
|
|
}
|
|
}
|