use motedb::{DBConfig, Database, QueryResult};
use std::time::Instant;
use tempfile::TempDir;
fn db() -> (Database, TempDir) {
let dir = TempDir::new().unwrap();
let mut c = DBConfig::for_edge();
c.max_result_rows = None;
(Database::create_with_config(dir.path(), c).unwrap(), dir)
}
fn count_rows(db: &Database, sql: &str) -> usize {
db.execute(sql).unwrap().materialize().unwrap().row_count()
}
fn assert_count(db: &Database, sql: &str, expected: usize) {
let actual = count_rows(db, sql);
assert_eq!(
actual, expected,
"SQL '{}' returned {} rows, expected {}",
sql, actual, expected
);
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_vector_crud_lifecycle() {
let (db, _dir) = db();
db.execute("CREATE TABLE vecs (id INT PRIMARY KEY AUTO_INCREMENT, emb VECTOR(4), label TEXT)")
.unwrap();
db.execute("INSERT INTO vecs (emb, label) VALUES ([1.0, 0.0, 0.0, 0.0], 'x')")
.unwrap();
db.execute("INSERT INTO vecs (emb, label) VALUES ([0.0, 1.0, 0.0, 0.0], 'y')")
.unwrap();
db.execute("INSERT INTO vecs (emb, label) VALUES ([0.0, 0.0, 1.0, 0.0], 'z')")
.unwrap();
assert_eq!(count_rows(&db, "SELECT COUNT(*) FROM vecs"), 1);
assert_count(&db, "SELECT * FROM vecs WHERE id = 1", 1);
assert_count(&db, "SELECT * FROM vecs WHERE label = 'y'", 1);
let r = db
.execute("SELECT id FROM vecs ORDER BY emb <-> [1.0, 0.0, 0.0, 0.0] LIMIT 1")
.unwrap();
assert!(
matches!(&r.materialize().unwrap(), QueryResult::Select { rows, .. } if !rows.is_empty())
);
db.execute("UPDATE vecs SET label = 'x-axis' WHERE id = 1")
.unwrap();
db.execute("DELETE FROM vecs WHERE id = 3").unwrap();
assert_eq!(count_rows(&db, "SELECT COUNT(*) FROM vecs"), 1);
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_spatial_crud_lifecycle() {
let (db, _dir) = db();
db.execute("CREATE TABLE pts (id INT PRIMARY KEY AUTO_INCREMENT, loc GEOMETRY, name TEXT)")
.unwrap();
db.execute("INSERT INTO pts (loc, name) VALUES (POINT(1.0, 2.0), 'home')")
.unwrap();
db.execute("INSERT INTO pts (loc, name) VALUES (POINT(3.0, 4.0), 'office')")
.unwrap();
db.execute("INSERT INTO pts (loc, name) VALUES (POINT(5.0, 6.0), 'park')")
.unwrap();
assert_eq!(count_rows(&db, "SELECT COUNT(*) FROM pts"), 1);
let r = db
.execute("SELECT id FROM pts ORDER BY ST_DISTANCE(loc, 1.0, 2.0) LIMIT 1")
.unwrap();
assert!(
matches!(&r.materialize().unwrap(), QueryResult::Select { rows, .. } if !rows.is_empty())
);
db.execute("UPDATE pts SET name = 'house' WHERE id = 1")
.unwrap();
db.execute("DELETE FROM pts WHERE id = 2").unwrap();
assert_eq!(count_rows(&db, "SELECT COUNT(*) FROM pts"), 1);
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_text_crud_lifecycle() {
let (db, _dir) = db();
db.execute("CREATE TABLE docs (id INT PRIMARY KEY AUTO_INCREMENT, title TEXT, body TEXT)")
.unwrap();
db.execute("INSERT INTO docs (title, body) VALUES ('Intro', 'database for edge computing')")
.unwrap();
db.execute(
"INSERT INTO docs (title, body) VALUES ('Guide', 'machine learning on embedded devices')",
)
.unwrap();
db.execute(
"INSERT INTO docs (title, body) VALUES ('Ref', 'sensor data and real-time processing')",
)
.unwrap();
assert_count(&db, "SELECT * FROM docs", 3);
assert_count(&db, "SELECT * FROM docs WHERE title LIKE 'Intro%'", 1);
assert_count(&db, "SELECT * FROM docs WHERE body LIKE '%edge%'", 1);
db.execute("UPDATE docs SET title = 'Introduction' WHERE id = 1")
.unwrap();
db.execute("DELETE FROM docs WHERE id = 3").unwrap();
assert_count(&db, "SELECT * FROM docs", 2);
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_multimodal_restart_recovery() {
let dir = TempDir::new().unwrap();
let path = dir.path().to_str().unwrap().to_string();
{
let mut c = DBConfig::for_edge();
c.max_result_rows = None;
let db = Database::create_with_config(&path, c).unwrap();
db.execute("CREATE TABLE mm (id INT PRIMARY KEY AUTO_INCREMENT, emb VECTOR(8), loc GEOMETRY, info TEXT)").unwrap();
db.execute("INSERT INTO mm (emb, loc, info) VALUES ([1.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7], POINT(1.0,2.0), 'hello')").unwrap();
db.execute("INSERT INTO mm (emb, loc, info) VALUES ([0.7,0.6,0.5,0.4,0.3,0.2,0.1,0.0], POINT(3.0,4.0), 'world')").unwrap();
}
{
let mut c = DBConfig::for_edge();
c.max_result_rows = None;
let db = Database::open(&path).unwrap();
assert_count(&db, "SELECT * FROM mm", 2);
assert_count(&db, "SELECT * FROM mm WHERE id = 1", 1);
assert_count(&db, "SELECT * FROM mm WHERE info = 'hello'", 1);
}
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_mixed_vector_plus_spatial() {
let (db, _dir) = db();
db.execute("CREATE TABLE items (id INT PRIMARY KEY AUTO_INCREMENT, emb VECTOR(4), loc GEOMETRY, cat TEXT)").unwrap();
for i in 0..50 {
let v = format!("[{:.2},{:.2},{:.2},{:.2}]", i as f64 * 0.01, 0.5, 0.3, 0.2);
let lat = 30.0 + i as f64 * 0.1;
let cat = if i % 2 == 0 { "A" } else { "B" };
let sql = format!(
"INSERT INTO items (emb, loc, cat) VALUES ({}, POINT({:.1}, 40.0), '{}')",
v, lat, cat
);
db.execute(&sql).unwrap();
}
let r = db
.execute(
"SELECT id FROM items WHERE cat = 'A' ORDER BY emb <-> [0.0, 0.5, 0.3, 0.2] LIMIT 5",
)
.unwrap();
if let QueryResult::Select { rows, .. } = r.materialize().unwrap() {
assert!(!rows.is_empty(), "should find vectors in category A");
assert!(rows.len() <= 5);
}
let r = db
.execute(
"SELECT id FROM items WHERE cat = 'B' ORDER BY ST_DISTANCE(loc, 35.0, 40.0) LIMIT 3",
)
.unwrap();
if let QueryResult::Select { rows, .. } = r.materialize().unwrap() {
assert!(!rows.is_empty(), "should find spatial points in category B");
}
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_mixed_vector_plus_text_plus_scalar() {
let (db, _dir) = db();
db.execute("CREATE TABLE products (id INT PRIMARY KEY AUTO_INCREMENT, emb VECTOR(8), name TEXT, price FLOAT, in_stock INT)").unwrap();
for i in 0..30 {
let v: Vec<String> = (0..8)
.map(|d| format!("{:.3}", ((i + d) as f64 * 0.1).sin()))
.collect();
let price = 10.0 + i as f64 * 5.0;
let stock = if i % 3 == 0 { 0 } else { 100 };
let name = format!("product_{}", i % 5);
let sql = format!(
"INSERT INTO products (emb, name, price, in_stock) VALUES ([{}], '{}', {:.1}, {})",
v.join(","),
name,
price,
stock
);
db.execute(&sql).unwrap();
}
let qv: Vec<String> = (0..8)
.map(|d| format!("{:.3}", (d as f64 * 0.1).sin()))
.collect();
let sql = format!(
"SELECT id FROM products WHERE in_stock > 0 AND price < 100 ORDER BY emb <-> [{}] LIMIT 5",
qv.join(",")
);
let r = db.execute(&sql).unwrap().materialize().unwrap();
if let QueryResult::Select { rows, .. } = r {
for _row in &rows {
}
assert!(!rows.is_empty(), "should find matching products");
}
assert!(count_rows(&db, "SELECT * FROM products WHERE name LIKE 'product_1%'") >= 1);
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_mixed_all_types_single_table() {
let (db, _dir) = db();
db.execute("CREATE TABLE sensors (id INT PRIMARY KEY AUTO_INCREMENT, emb VECTOR(4), loc GEOMETRY, label TEXT, val FLOAT, region TEXT, active INT)").unwrap();
for i in 0..20 {
let v = format!(
"[{:.2},{:.2},{:.2},{:.2}]",
(i as f64).sin(),
(i as f64 * 2.0).sin(),
0.5,
0.5
);
let lat = 30.0 + i as f64 * 0.5;
let lon = 40.0 + i as f64 * 0.3;
let region = if i % 3 == 0 { "US" } else { "EU" };
let active = if i % 2 == 0 { 1 } else { 0 };
let sql = format!(
"INSERT INTO sensors (emb, loc, label, val, region, active) VALUES ({}, POINT({:.1},{:.1}), 'sensor_{}', {:.1}, '{}', {})",
v, lon, lat, i, i as f64 * 3.14, region, active
);
db.execute(&sql).unwrap();
}
let r = db.execute("SELECT id FROM sensors WHERE active = 1 AND region = 'US' ORDER BY emb <-> [0.0, 0.0, 0.5, 0.5] LIMIT 3").unwrap();
if let QueryResult::Select { rows, .. } = r.materialize().unwrap() {
assert!(!rows.is_empty(), "should find active US sensors");
}
let r = db
.execute(
"SELECT id FROM sensors WHERE val > 30 ORDER BY ST_DISTANCE(loc, 42.0, 35.0) LIMIT 5",
)
.unwrap();
if let QueryResult::Select { rows, .. } = r.materialize().unwrap() {
assert!(!rows.is_empty(), "should find sensors with val > 30");
}
let r = db
.execute("SELECT region, COUNT(*) FROM sensors GROUP BY region")
.unwrap()
.materialize()
.unwrap();
if let QueryResult::Select { rows, .. } = r {
assert_eq!(rows.len(), 2, "should have 2 regions");
}
let r = db
.execute("SELECT active, AVG(val) FROM sensors GROUP BY active")
.unwrap()
.materialize()
.unwrap();
if let QueryResult::Select { rows, .. } = r {
assert_eq!(rows.len(), 2, "should have 2 active groups");
}
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_empty_table_queries() {
let (db, _dir) = db();
db.execute("CREATE TABLE empty (id INT PRIMARY KEY AUTO_INCREMENT, emb VECTOR(4), loc GEOMETRY, txt TEXT)").unwrap();
assert_count(&db, "SELECT * FROM empty", 0);
assert_count(&db, "SELECT COUNT(*) FROM empty", 1); assert_count(&db, "SELECT * FROM empty WHERE id = 1", 0);
let r = db
.execute("SELECT id FROM empty ORDER BY emb <-> [1.0, 0.0, 0.0, 0.0] LIMIT 5")
.unwrap();
assert_eq!(r.materialize().unwrap().row_count(), 0);
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_single_row_table() {
let (db, _dir) = db();
db.execute(
"CREATE TABLE solo (id INT PRIMARY KEY AUTO_INCREMENT, emb VECTOR(2), loc GEOMETRY)",
)
.unwrap();
db.execute("INSERT INTO solo (emb, loc) VALUES ([1.0, 0.0], POINT(0.0, 0.0))")
.unwrap();
assert_count(&db, "SELECT * FROM solo", 1);
assert_count(&db, "SELECT * FROM solo WHERE id = 1", 1);
let r = db
.execute("SELECT id FROM solo ORDER BY emb <-> [0.9, 0.1] LIMIT 1")
.unwrap();
assert!(
matches!(&r.materialize().unwrap(), QueryResult::Select { rows, .. } if rows.len() == 1)
);
db.execute("DELETE FROM solo WHERE id = 1").unwrap();
assert_count(&db, "SELECT * FROM solo", 0);
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_large_vector_dim() {
let (db, _dir) = db();
let dim = 256;
db.execute(&format!(
"CREATE TABLE big_vec (id INT PRIMARY KEY AUTO_INCREMENT, emb VECTOR({}))",
dim
))
.unwrap();
let v: Vec<String> = (0..dim)
.map(|i| format!("{:.4}", (i as f64 * 0.01).sin()))
.collect();
let sql = format!("INSERT INTO big_vec (emb) VALUES ([{}])", v.join(","));
db.execute(&sql).unwrap();
assert_count(&db, "SELECT * FROM big_vec", 1);
let qv: Vec<String> = (0..dim)
.map(|i| format!("{:.4}", (i as f64 * 0.01).cos()))
.collect();
let sql = format!(
"SELECT id FROM big_vec ORDER BY emb <-> [{}] LIMIT 1",
qv.join(",")
);
let r = db.execute(&sql).unwrap();
assert!(
matches!(&r.materialize().unwrap(), QueryResult::Select { rows, .. } if !rows.is_empty())
);
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_null_and_missing_values() {
let (db, _dir) = db();
db.execute("CREATE TABLE nullable (id INT PRIMARY KEY AUTO_INCREMENT, emb VECTOR(2), txt TEXT, val FLOAT)").unwrap();
db.execute("INSERT INTO nullable (emb, txt, val) VALUES ([0.0, 0.0], '', 0.0)")
.unwrap();
db.execute("INSERT INTO nullable (emb, txt, val) VALUES ([1.0, 1.0], 'data', 42.5)")
.unwrap();
assert_count(&db, "SELECT * FROM nullable WHERE txt = 'data'", 1);
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_batch_insert_mixed_types() {
let (db, _dir) = db();
db.execute("CREATE TABLE batch (id INT PRIMARY KEY AUTO_INCREMENT, emb VECTOR(4), loc GEOMETRY, cat TEXT)").unwrap();
db.execute(
"INSERT INTO batch (emb, loc, cat) VALUES ([1.0, 0.0, 0.0, 0.0], POINT(1.0, 1.0), 'A')",
)
.unwrap();
db.execute(
"INSERT INTO batch (emb, loc, cat) VALUES ([0.0, 1.0, 0.0, 0.0], POINT(2.0, 2.0), 'B')",
)
.unwrap();
db.execute(
"INSERT INTO batch (emb, loc, cat) VALUES ([0.0, 0.0, 1.0, 0.0], POINT(3.0, 3.0), 'A')",
)
.unwrap();
assert_eq!(count_rows(&db, "SELECT COUNT(*) FROM batch"), 1); }
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_create_query_with_indexes() {
let (db, _dir) = db();
db.execute("CREATE TABLE idx_test (id INT PRIMARY KEY AUTO_INCREMENT, emb VECTOR(4), loc GEOMETRY, body TEXT, cat TEXT)").unwrap();
for i in 0..30 {
let v = format!("[{:.2},{:.2},{:.2},{:.2}]", i as f64 * 0.03, 0.5, 0.5, 0.5);
let cat = if i % 2 == 0 { "X" } else { "Y" };
db.execute(&format!("INSERT INTO idx_test (emb, loc, body, cat) VALUES ({}, POINT({:.1},{:.1}), 'doc number {}', '{}')",
v, i as f64, i as f64 + 10.0, i, cat)).unwrap();
}
let _ = db.execute("CREATE INDEX idx_cat ON idx_test (cat) USING COLUMN");
let _ = db.execute("CREATE TEXT INDEX idx_body ON idx_test (body)");
assert_count(&db, "SELECT * FROM idx_test WHERE cat = 'X'", 15);
let r = db
.execute("SELECT id FROM idx_test WHERE body LIKE 'doc%' LIMIT 5")
.unwrap();
assert!(r.materialize().unwrap().row_count() <= 5);
let r = db
.execute("SELECT id FROM idx_test ORDER BY emb <-> [0.0, 0.5, 0.5, 0.5] LIMIT 3")
.unwrap();
assert!(
matches!(&r.materialize().unwrap(), QueryResult::Select { rows, .. } if !rows.is_empty())
);
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_index_after_bulk_insert() {
let (db, _dir) = db();
db.execute("CREATE TABLE bulk (id INT PRIMARY KEY AUTO_INCREMENT, emb VECTOR(8), region TEXT)")
.unwrap();
for i in 0..100 {
let v: Vec<String> = (0..8)
.map(|d| format!("{:.3}", ((i + d) as f64 * 0.1).sin()))
.collect();
let region = if i % 3 == 0 { "US" } else { "EU" };
db.execute(&format!(
"INSERT INTO bulk (emb, region) VALUES ([{}],'{}')",
v.join(","),
region
))
.unwrap();
}
assert_eq!(count_rows(&db, "SELECT COUNT(*) FROM bulk"), 1);
let _ = db.execute("CREATE INDEX idx_region ON bulk (region) USING COLUMN");
assert!(
count_rows(&db, "SELECT * FROM bulk WHERE region = 'US'") >= 1,
"should find US rows"
); }
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_memory_does_not_grow_unbounded() {
let (db, _dir) = db();
db.execute("CREATE TABLE scale (id INT PRIMARY KEY AUTO_INCREMENT, emb VECTOR(16), loc GEOMETRY, cat TEXT, val FLOAT)").unwrap();
fn rss_mb() -> f64 {
let pid = std::process::id();
std::process::Command::new("ps")
.args(["-o", "rss", "-p", &pid.to_string()])
.output()
.ok()
.and_then(|o| {
let s = String::from_utf8_lossy(&o.stdout);
s.lines()
.skip(1)
.next()
.and_then(|l| l.trim().parse::<usize>().ok())
.map(|v| v as f64 / 1024.0)
})
.unwrap_or(0.0)
}
let mut rss_samples = Vec::new();
for batch_num in 0..5 {
let start = batch_num * 10_000;
let end = start + 10_000;
let mut sql = String::new();
for i in start..end {
if !sql.is_empty() {
sql.push(',');
}
let v: Vec<String> = (0..16)
.map(|d| format!("{:.3}", ((i + d) as f64 * 0.01).sin()))
.collect();
let _region = if i % 3 == 0 { "US" } else { "EU" };
sql.push_str(&format!(
"([{}],POINT({:.1},{:.1}),'cat_{}',{:.1})",
v.join(","),
30.0 + (i % 90) as f64,
40.0 + (i % 90) as f64,
i % 10,
(i as f64 * 1.7) % 1000.0
));
}
let insert_sql = format!("INSERT INTO scale (emb, loc, cat, val) VALUES {}", sql);
db.execute(&insert_sql).unwrap();
let _ = db.execute("SELECT COUNT(*) FROM scale").unwrap();
let rss = rss_mb();
rss_samples.push(rss);
eprintln!(
" After {}K rows: RSS = {:.0} MB",
(batch_num + 1) * 10,
rss
);
}
let first = rss_samples[0];
let last = *rss_samples.last().unwrap();
eprintln!(
" RSS: first={:.0}MB last={:.0}MB ratio={:.1}x",
first,
last,
last / first
);
assert!(
last < first * 3.0,
"RSS grew more than 3x: {:.0} → {:.0} MB",
first,
last
);
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_latency_growth_sublinear() {
let (db, _dir) = db();
db.execute("CREATE TABLE perf (id INT PRIMARY KEY AUTO_INCREMENT, cat TEXT, val FLOAT)")
.unwrap();
fn measure_p99(db: &Database, sql: &str, iterations: usize) -> f64 {
let mut max_ms: f64 = 0.0;
for _ in 0..iterations {
let t = Instant::now();
let _ = db.execute(sql).unwrap().materialize().unwrap();
max_ms = max_ms.max(t.elapsed().as_secs_f64() * 1000.0);
}
max_ms
}
for start in (0..10_000).step_by(5000) {
let mut sql = String::new();
for i in start..(start + 5000).min(10_000) {
if !sql.is_empty() {
sql.push(',');
}
sql.push_str(&format!(
"('cat_{}',{:.1})",
i % 10,
(i as f64 * 1.7) % 1000.0
));
}
db.execute(&format!("INSERT INTO perf (cat, val) VALUES {}", sql))
.unwrap();
}
let p99_10k = measure_p99(&db, "SELECT COUNT(*) FROM perf WHERE cat = 'cat_0'", 10);
eprintln!(" P99 COUNT at 10K: {:.2}ms", p99_10k);
for start in (10_000..30_000).step_by(5000) {
let mut sql = String::new();
for i in start..(start + 5000).min(30_000) {
if !sql.is_empty() {
sql.push(',');
}
sql.push_str(&format!(
"('cat_{}',{:.1})",
i % 10,
(i as f64 * 1.7) % 1000.0
));
}
db.execute(&format!("INSERT INTO perf (cat, val) VALUES {}", sql))
.unwrap();
}
let p99_30k = measure_p99(&db, "SELECT COUNT(*) FROM perf WHERE cat = 'cat_0'", 10);
eprintln!(" P99 COUNT at 30K: {:.2}ms", p99_30k);
let ratio = p99_30k / p99_10k.max(0.01);
eprintln!(
" Latency ratio (30K/10K): {:.1}x (data ratio: 3.0x)",
ratio
);
assert!(
ratio < 5.0,
"Latency grew more than 5x for 3x data: {:.1}x",
ratio
);
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_transaction_multimodal_insert() {
let (db, _dir) = db();
db.execute("CREATE TABLE txn_mm (id INT PRIMARY KEY AUTO_INCREMENT, emb VECTOR(4), loc GEOMETRY, txt TEXT)").unwrap();
db.execute(
"INSERT INTO txn_mm (emb, loc, txt) VALUES ([1.0, 0.0, 0.0, 0.0], POINT(1.0, 1.0), 'base')",
)
.unwrap();
assert_count(&db, "SELECT * FROM txn_mm", 1);
db.execute("BEGIN").unwrap();
db.execute("INSERT INTO txn_mm (emb, loc, txt) VALUES ([0.0, 1.0, 0.0, 0.0], POINT(2.0, 2.0), 'txn_data')").unwrap();
db.execute("COMMIT").unwrap();
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_transaction_multimodal_rollback() {
let (db, _dir) = db();
db.execute("CREATE TABLE rb_mm (id INT PRIMARY KEY AUTO_INCREMENT, emb VECTOR(4), txt TEXT)")
.unwrap();
db.execute("INSERT INTO rb_mm (emb, txt) VALUES ([1.0, 0.0, 0.0, 0.0], 'committed')")
.unwrap();
assert_count(&db, "SELECT * FROM rb_mm", 1);
db.execute("BEGIN").unwrap();
db.execute("INSERT INTO rb_mm (emb, txt) VALUES ([0.0, 1.0, 0.0, 0.0], 'rolled_back')")
.unwrap();
db.execute("ROLLBACK").unwrap();
assert_count(&db, "SELECT * FROM rb_mm", 1);
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_robot_sensor_scenario() {
let (db, _dir) = db();
db.execute(
"CREATE TABLE robot_log (\
id INT PRIMARY KEY AUTO_INCREMENT, \
lidar_loc GEOMETRY, \
embedding VECTOR(16), \
event TEXT, \
battery FLOAT, \
zone TEXT \
)",
)
.unwrap();
for t in 0..50 {
let x = 10.0 * (t as f64 * 0.1).cos();
let y = 10.0 * (t as f64 * 0.1).sin();
let v: Vec<String> = (0..16)
.map(|d| format!("{:.3}", ((t + d) as f64 * 0.05).sin()))
.collect();
let event = match t % 4 {
0 => "obstacle_detected",
1 => "path_planning",
2 => "navigation_complete",
_ => "charging",
};
let battery = 100.0 - t as f64 * 1.5;
let zone = if x > 0.0 { "east" } else { "west" };
db.execute(&format!(
"INSERT INTO robot_log (lidar_loc, embedding, event, battery, zone) VALUES (POINT({:.2},{:.2}), [{}], '{}', {:.1}, '{}')",
x, y, v.join(","), event, battery, zone
)).unwrap();
}
assert_eq!(count_rows(&db, "SELECT COUNT(*) FROM robot_log"), 1);
let r = db.execute("SELECT id FROM robot_log WHERE event = 'obstacle_detected' ORDER BY ST_DISTANCE(lidar_loc, 5.0, 0.0) LIMIT 3").unwrap();
assert!(
matches!(&r.materialize().unwrap(), QueryResult::Select { rows, .. } if !rows.is_empty())
);
let qv: Vec<String> = (0..16)
.map(|d| format!("{:.3}", (d as f64 * 0.05).sin()))
.collect();
let sql = format!(
"SELECT id FROM robot_log WHERE zone = 'east' ORDER BY embedding <-> [{}] LIMIT 5",
qv.join(",")
);
let r = db.execute(&sql).unwrap();
assert!(
matches!(&r.materialize().unwrap(), QueryResult::Select { rows, .. } if !rows.is_empty())
);
let r = db
.execute("SELECT COUNT(*) FROM robot_log WHERE battery < 50")
.unwrap();
assert!(
matches!(&r.materialize().unwrap(), QueryResult::Select { rows, .. } if !rows.is_empty())
);
let r = db
.execute("SELECT zone, COUNT(*) FROM robot_log GROUP BY zone")
.unwrap()
.materialize()
.unwrap();
if let QueryResult::Select { rows, .. } = r {
assert!(!rows.is_empty(), "should have zone aggregates");
}
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_ar_glasses_scenario() {
let (db, _dir) = db();
db.execute(
"CREATE TABLE anchors (\
id INT PRIMARY KEY AUTO_INCREMENT, \
position GEOMETRY, \
descriptor VECTOR(8), \
label TEXT, \
confidence FLOAT \
)",
)
.unwrap();
for i in 0..30 {
let x = (i as f64 * 0.3).cos() * 5.0;
let y = (i as f64 * 0.3).sin() * 5.0;
let v: Vec<String> = (0..8)
.map(|d| format!("{:.3}", ((i + d) as f64 * 0.1).sin()))
.collect();
let label = format!("object_{}", i % 5);
let conf = 0.5 + (i as f64 * 0.01) % 0.5;
db.execute(&format!(
"INSERT INTO anchors (position, descriptor, label, confidence) VALUES (POINT({:.2},{:.2}), [{}], '{}', {:.3})",
x, y, v.join(","), label, conf
)).unwrap();
}
assert_eq!(count_rows(&db, "SELECT COUNT(*) FROM anchors"), 1);
let r = db.execute("SELECT id FROM anchors WHERE confidence > 0.7 ORDER BY ST_DISTANCE(position, 3.0, 2.0) LIMIT 5").unwrap();
assert!(
matches!(&r.materialize().unwrap(), QueryResult::Select { rows, .. } if !rows.is_empty())
);
let qv: Vec<String> = (0..8)
.map(|d| format!("{:.3}", (d as f64 * 0.1).sin()))
.collect();
let sql = format!(
"SELECT id, label FROM anchors ORDER BY descriptor <-> [{}] LIMIT 3",
qv.join(",")
);
let r = db.execute(&sql).unwrap();
assert!(
matches!(&r.materialize().unwrap(), QueryResult::Select { rows, .. } if !rows.is_empty())
);
let r = db
.execute("SELECT label, COUNT(*) FROM anchors GROUP BY label")
.unwrap()
.materialize()
.unwrap();
if let QueryResult::Select { rows, .. } = r {
assert_eq!(rows.len(), 5, "should have 5 distinct labels");
}
}
#[test]
#[ignore = "slow in debug, run with --ignored"]
fn test_iot_drone_scenario() {
let (db, _dir) = db();
db.execute(
"CREATE TABLE telemetry (\
id INT PRIMARY KEY AUTO_INCREMENT, \
gps GEOMETRY, \
sensor_vec VECTOR(6), \
status TEXT, \
altitude FLOAT, \
battery INT \
)",
)
.unwrap();
for t in 0..40 {
let lat = 37.0 + (t as f64 * 0.001);
let lon = -122.0 + (t as f64 * 0.001);
let v: Vec<String> = (0..6)
.map(|d| format!("{:.2}", ((t + d) as f64 * 0.1).sin() * 50.0 + 500.0))
.collect();
let status = if t < 10 {
"takeoff"
} else if t < 30 {
"cruising"
} else {
"landing"
};
let alt = if t < 10 {
t as f64 * 10.0
} else if t < 30 {
100.0
} else {
(40 - t) as f64 * 10.0
};
let batt = 100 - t * 2;
db.execute(&format!(
"INSERT INTO telemetry (gps, sensor_vec, status, altitude, battery) VALUES (POINT({:.4},{:.4}), [{}], '{}', {:.1}, {})",
lat, lon, v.join(","), status, alt, batt
)).unwrap();
}
assert_eq!(count_rows(&db, "SELECT COUNT(*) FROM telemetry"), 1);
let r = db
.execute("SELECT id FROM telemetry ORDER BY ST_DISTANCE(gps, 37.02, -122.02) LIMIT 3")
.unwrap();
assert!(
matches!(&r.materialize().unwrap(), QueryResult::Select { rows, .. } if !rows.is_empty())
);
let r = db
.execute("SELECT COUNT(*) FROM telemetry WHERE status = 'cruising' AND battery < 70")
.unwrap();
assert!(
matches!(&r.materialize().unwrap(), QueryResult::Select { rows, .. } if !rows.is_empty())
);
let qv: Vec<String> = (0..6)
.map(|d| format!("{:.2}", (d as f64 * 0.1).sin() * 50.0 + 500.0))
.collect();
let sql = format!(
"SELECT id FROM telemetry WHERE status = 'takeoff' ORDER BY sensor_vec <-> [{}] LIMIT 3",
qv.join(",")
);
let r = db.execute(&sql).unwrap();
assert!(
matches!(&r.materialize().unwrap(), QueryResult::Select { rows, .. } if !rows.is_empty())
);
let r = db
.execute("SELECT status, COUNT(*) FROM telemetry GROUP BY status")
.unwrap()
.materialize()
.unwrap();
if let QueryResult::Select { rows, .. } = r {
assert_eq!(rows.len(), 3, "should have 3 flight phases");
}
}