mirror of
https://github.com/tursodatabase/libsql.git
synced 2024-11-22 03:26:15 +00:00
438 lines
12 KiB
Rust
438 lines
12 KiB
Rust
//! Tests for standalone primary configuration
|
|
#![allow(deprecated)]
|
|
|
|
use crate::common::net::{SimServer, TurmoilAcceptor};
|
|
use crate::common::{http::Client, snapshot_metrics};
|
|
|
|
use super::common;
|
|
|
|
use std::{sync::Arc, time::Duration};
|
|
|
|
use insta::assert_debug_snapshot;
|
|
use libsql::{params, Connection, Database, Value};
|
|
use tempfile::tempdir;
|
|
use tokio::sync::Notify;
|
|
|
|
use libsql_server::config::{AdminApiConfig, UserApiConfig};
|
|
|
|
use common::net::{init_tracing, TestServer, TurmoilConnector};
|
|
|
|
mod admin;
|
|
mod attach;
|
|
mod auth;
|
|
|
|
async fn make_standalone_server() -> Result<(), Box<dyn std::error::Error>> {
|
|
init_tracing();
|
|
let tmp = tempdir()?;
|
|
let server = TestServer {
|
|
path: tmp.path().to_owned().into(),
|
|
user_api_config: UserApiConfig {
|
|
hrana_ws_acceptor: None,
|
|
..Default::default()
|
|
},
|
|
admin_api_config: Some(AdminApiConfig {
|
|
acceptor: TurmoilAcceptor::bind(([0, 0, 0, 0], 9090)).await.unwrap(),
|
|
connector: TurmoilConnector,
|
|
disable_metrics: true,
|
|
auth_key: None,
|
|
}),
|
|
disable_namespaces: false,
|
|
..Default::default()
|
|
};
|
|
|
|
server.start_sim(8080).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn basic_query() {
|
|
let mut sim = turmoil::Builder::new()
|
|
.simulation_duration(Duration::from_secs(1000))
|
|
.build();
|
|
|
|
sim.host("primary", make_standalone_server);
|
|
|
|
sim.client("test", async {
|
|
let db = Database::open_remote_with_connector("http://primary:8080", "", TurmoilConnector)?;
|
|
let conn = db.connect()?;
|
|
|
|
conn.execute("create table test (x)", ()).await?;
|
|
conn.execute("insert into test values (12)", ()).await?;
|
|
|
|
let mut rows = conn.query("select count(*) from test", ()).await?;
|
|
|
|
assert!(matches!(
|
|
rows.next().await.unwrap().unwrap().get_value(0).unwrap(),
|
|
libsql::Value::Integer(1)
|
|
));
|
|
|
|
Ok(())
|
|
});
|
|
|
|
sim.run().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn basic_metrics() {
|
|
let mut sim = turmoil::Builder::new()
|
|
.simulation_duration(Duration::from_secs(1000))
|
|
.build();
|
|
|
|
sim.host("primary", make_standalone_server);
|
|
|
|
sim.client("test", async {
|
|
let db = Database::open_remote_with_connector("http://primary:8080", "", TurmoilConnector)?;
|
|
let conn = db.connect()?;
|
|
|
|
conn.execute("create table test (x)", ()).await?;
|
|
conn.execute("insert into test values (12)", ()).await?;
|
|
|
|
let mut rows = conn.query("select count(*) from test", ()).await?;
|
|
|
|
assert!(matches!(
|
|
rows.next().await.unwrap().unwrap().get_value(0).unwrap(),
|
|
libsql::Value::Integer(1)
|
|
));
|
|
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
|
|
let snapshot = snapshot_metrics();
|
|
snapshot.assert_counter("libsql_server_libsql_execute_program", 3);
|
|
snapshot.assert_counter("libsql_server_user_http_response", 3);
|
|
|
|
for (key, (_, _, val)) in snapshot.snapshot() {
|
|
if key.kind() == metrics_util::MetricKind::Counter
|
|
&& key.key().name() == "libsql_client_version"
|
|
{
|
|
let label = key.key().labels().next().unwrap();
|
|
assert!(label.value().starts_with("libsql-remote-"));
|
|
assert_eq!(val, &metrics_util::debugging::DebugValue::Counter(3));
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
});
|
|
|
|
sim.run().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn primary_serializability() {
|
|
let mut sim = turmoil::Builder::new()
|
|
.simulation_duration(Duration::from_secs(1000))
|
|
.build();
|
|
|
|
sim.host("primary", make_standalone_server);
|
|
let notify = Arc::new(Notify::new());
|
|
|
|
sim.client("writer", {
|
|
let notify = notify.clone();
|
|
async move {
|
|
let db =
|
|
Database::open_remote_with_connector("http://primary:8080", "", TurmoilConnector)?;
|
|
let conn = db.connect()?;
|
|
conn.execute("create table test (x)", ()).await?;
|
|
conn.execute("insert into test values (12)", ()).await?;
|
|
|
|
notify.notify_waiters();
|
|
|
|
Ok(())
|
|
}
|
|
});
|
|
|
|
sim.client("reader", {
|
|
async move {
|
|
let db =
|
|
Database::open_remote_with_connector("http://primary:8080", "", TurmoilConnector)?;
|
|
let conn = db.connect()?;
|
|
|
|
notify.notified().await;
|
|
|
|
let mut rows = conn.query("select count(*) from test", ()).await?;
|
|
|
|
assert!(matches!(
|
|
rows.next().await.unwrap().unwrap().get_value(0).unwrap(),
|
|
Value::Integer(1)
|
|
));
|
|
|
|
Ok(())
|
|
}
|
|
});
|
|
|
|
sim.run().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn basic_query_fail() {
|
|
let mut sim = turmoil::Builder::new()
|
|
.simulation_duration(Duration::from_secs(1000))
|
|
.build();
|
|
|
|
sim.host("primary", make_standalone_server);
|
|
|
|
sim.client("test", async {
|
|
let db = Database::open_remote_with_connector("http://primary:8080", "", TurmoilConnector)?;
|
|
let conn = db.connect()?;
|
|
|
|
conn.execute("create table test (x)", ()).await?;
|
|
conn.execute("create unique index test_index on test(x)", ())
|
|
.await?;
|
|
conn.execute("insert into test values (12)", ()).await?;
|
|
let e = conn
|
|
.execute("insert into test values (12)", ())
|
|
.await
|
|
.unwrap_err();
|
|
assert_debug_snapshot!(e);
|
|
|
|
Ok(())
|
|
});
|
|
|
|
sim.run().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn begin_commit() {
|
|
let mut sim = turmoil::Builder::new()
|
|
.simulation_duration(Duration::from_secs(1000))
|
|
.build();
|
|
|
|
sim.host("primary", make_standalone_server);
|
|
|
|
sim.client("test", async {
|
|
let db = Database::open_remote_with_connector("http://primary:8080", "", TurmoilConnector)?;
|
|
let conn = db.connect()?;
|
|
|
|
conn.execute("create table test (x)", ()).await?;
|
|
|
|
conn.execute("begin;", ()).await?;
|
|
conn.execute("insert into test values (12);", ()).await?;
|
|
|
|
// we can read the inserted row
|
|
let mut rows = conn.query("select count(*) from test", ()).await?;
|
|
assert_eq!(
|
|
rows.next().await.unwrap().unwrap().get_value(0).unwrap(),
|
|
Value::Integer(1)
|
|
);
|
|
assert!(rows.next().await.unwrap().is_none());
|
|
|
|
conn.execute("commit;", ()).await?;
|
|
|
|
// after rollback row is no longer there
|
|
let mut rows = conn.query("select count(*) from test", ()).await?;
|
|
assert_eq!(
|
|
rows.next().await.unwrap().unwrap().get_value(0).unwrap(),
|
|
Value::Integer(1)
|
|
);
|
|
|
|
Ok(())
|
|
});
|
|
|
|
sim.run().unwrap();
|
|
}
|
|
#[test]
|
|
fn begin_rollback() {
|
|
let mut sim = turmoil::Builder::new()
|
|
.simulation_duration(Duration::from_secs(1000))
|
|
.build();
|
|
|
|
sim.host("primary", make_standalone_server);
|
|
|
|
sim.client("test", async {
|
|
let db = Database::open_remote_with_connector("http://primary:8080", "", TurmoilConnector)?;
|
|
let conn = db.connect()?;
|
|
|
|
conn.execute("create table test (x)", ()).await?;
|
|
|
|
conn.execute("begin;", ()).await?;
|
|
conn.execute("insert into test values (12);", ()).await?;
|
|
|
|
// we can read the inserted row
|
|
let mut rows = conn.query("select count(*) from test", ()).await?;
|
|
assert_eq!(
|
|
rows.next().await.unwrap().unwrap().get_value(0).unwrap(),
|
|
Value::Integer(1)
|
|
);
|
|
|
|
conn.execute("rollback;", ()).await?;
|
|
|
|
// after rollback row is no longer there
|
|
let mut rows = conn.query("select count(*) from test", ()).await?;
|
|
assert_eq!(
|
|
rows.next().await.unwrap().unwrap().get_value(0).unwrap(),
|
|
Value::Integer(0)
|
|
);
|
|
|
|
Ok(())
|
|
});
|
|
|
|
sim.run().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn is_autocommit() {
|
|
let mut sim = turmoil::Builder::new()
|
|
.simulation_duration(Duration::from_secs(1000))
|
|
.build();
|
|
|
|
sim.host("primary", make_standalone_server);
|
|
|
|
sim.client("test", async {
|
|
let db = Database::open_remote_with_connector("http://primary:8080", "", TurmoilConnector)?;
|
|
let conn = db.connect()?;
|
|
|
|
assert!(conn.is_autocommit());
|
|
conn.execute("create table test (x)", ()).await?;
|
|
|
|
conn.execute("begin;", ()).await?;
|
|
assert!(!conn.is_autocommit());
|
|
conn.execute("insert into test values (12);", ()).await?;
|
|
conn.execute("commit;", ()).await?;
|
|
assert!(conn.is_autocommit());
|
|
|
|
// make an explicit transaction
|
|
{
|
|
let tx = conn.transaction().await?;
|
|
assert!(!tx.is_autocommit());
|
|
assert!(conn.is_autocommit()); // connection is still autocommit
|
|
|
|
tx.execute("insert into test values (12);", ()).await?;
|
|
// transaction rolls back
|
|
}
|
|
|
|
assert!(conn.is_autocommit());
|
|
|
|
let mut rows = conn.query("select count(*) from test", ()).await?;
|
|
assert_eq!(
|
|
rows.next().await.unwrap().unwrap().get_value(0).unwrap(),
|
|
Value::Integer(1)
|
|
);
|
|
|
|
Ok(())
|
|
});
|
|
|
|
sim.run().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn random_rowid() {
|
|
let mut sim = turmoil::Builder::new()
|
|
.simulation_duration(Duration::from_secs(1000))
|
|
.build();
|
|
|
|
sim.host("primary", make_standalone_server);
|
|
|
|
sim.client("test", async {
|
|
let db = Database::open_remote_with_connector("http://primary:8080", "", TurmoilConnector)?;
|
|
let conn = db.connect()?;
|
|
|
|
conn.execute(
|
|
"CREATE TABLE shopping_list(item text, quantity int) RANDOM ROWID",
|
|
(),
|
|
)
|
|
.await?;
|
|
|
|
Ok(())
|
|
});
|
|
|
|
sim.run().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn dirty_startup_dont_prevent_namespace_creation() {
|
|
let mut sim = turmoil::Builder::new()
|
|
.simulation_duration(Duration::from_secs(1000))
|
|
.build();
|
|
|
|
sim.host("primary", || async {
|
|
init_tracing();
|
|
let tmp = tempdir()?;
|
|
let server = TestServer {
|
|
path: tmp.path().to_owned().into(),
|
|
user_api_config: UserApiConfig {
|
|
hrana_ws_acceptor: None,
|
|
..Default::default()
|
|
},
|
|
admin_api_config: Some(AdminApiConfig {
|
|
acceptor: TurmoilAcceptor::bind(([0, 0, 0, 0], 9090)).await.unwrap(),
|
|
connector: TurmoilConnector,
|
|
disable_metrics: true,
|
|
auth_key: None,
|
|
}),
|
|
disable_default_namespace: true,
|
|
disable_namespaces: false,
|
|
..Default::default()
|
|
};
|
|
|
|
tokio::fs::File::create(tmp.path().join(".sentinel"))
|
|
.await
|
|
.unwrap();
|
|
server.start_sim(8080).await?;
|
|
|
|
Ok(())
|
|
});
|
|
|
|
sim.client("test", async {
|
|
let client = Client::new();
|
|
let resp = client
|
|
.post(
|
|
"http://primary:9090/v1/namespaces/test/create",
|
|
serde_json::json!({}),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
assert!(resp.status().is_success());
|
|
Ok(())
|
|
});
|
|
|
|
sim.run().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn row_count() {
|
|
let mut sim = turmoil::Builder::new()
|
|
.simulation_duration(Duration::from_secs(1000))
|
|
.build();
|
|
|
|
sim.host("primary", make_standalone_server);
|
|
|
|
sim.client("test", async {
|
|
let db = Database::open_remote_with_connector("http://primary:8080", "", TurmoilConnector)?;
|
|
let conn = db.connect()?;
|
|
|
|
conn.execute("CREATE TABLE test(a int, b int);", ()).await?;
|
|
conn.execute("BEGIN;", ()).await?;
|
|
insert_rows(&conn, 0, 10).await?;
|
|
insert_rows_with_args(&conn, 10, 10).await?;
|
|
assert_rows_count(&conn, 20).await?;
|
|
|
|
Ok(())
|
|
});
|
|
|
|
sim.run().unwrap();
|
|
}
|
|
|
|
async fn insert_rows(conn: &Connection, start: u32, count: u32) -> libsql::Result<()> {
|
|
for i in start..(start + count) {
|
|
conn.execute(&format!("INSERT INTO test(a, b) VALUES({i},'{i}')"), ())
|
|
.await?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
async fn insert_rows_with_args(conn: &Connection, start: u32, count: u32) -> libsql::Result<()> {
|
|
for i in start..(start + count) {
|
|
let mut stmt = conn.prepare("INSERT INTO test(a, b) VALUES(?,?)").await?;
|
|
stmt.execute(params![i, i]).await?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
async fn assert_rows_count(conn: &Connection, expected: u32) -> libsql::Result<()> {
|
|
let mut q = conn.query("SELECT COUNT(*) FROM test", ()).await?;
|
|
let row = q.next().await?.unwrap();
|
|
let count: u32 = row.get(0)?;
|
|
assert_eq!(count, expected);
|
|
Ok(())
|
|
}
|