use std::time::Duration; use libsql::Database; use serde_json::json; use tempfile::tempdir; use turmoil::Builder; use crate::common::{http::Client, net::TurmoilConnector}; use super::make_primary; #[test] fn replicated_config() { let mut sim = Builder::new() .simulation_duration(Duration::from_secs(1000)) .build(); crate::cluster::make_cluster(&mut sim, 1, false); sim.client("client", async { let client = Client::new(); client .post("http://primary:9090/v1/namespaces/foo/create", json!({})) .await .unwrap(); // Update the config since we can't pass these specific items // to create. client .post( "http://primary:9090/v1/namespaces/foo/config", json!({ "block_reads": true, "block_writes": false, }), ) .await?; // Query primary { let foo = Database::open_remote_with_connector( "http://foo.primary:8080", "", TurmoilConnector, )?; let foo_conn = foo.connect()?; foo_conn.execute("select 1", ()).await.unwrap_err(); } // Query replica { let foo = Database::open_remote_with_connector( "http://foo.replica1:8080", "", TurmoilConnector, )?; let foo_conn = foo.connect()?; foo_conn.execute("select 1", ()).await.unwrap_err(); } Ok(()) }); sim.run().unwrap(); } #[test] fn meta_store() { let mut sim = Builder::new() .simulation_duration(Duration::from_secs(1000)) .build(); let tmp = tempdir().unwrap(); make_primary(&mut sim, tmp.path().to_path_buf()); sim.client("client", async { let client = Client::new(); // STEP 1: create namespace and check that it can be read from client .post( "http://primary:9090/v1/namespaces/foo/create", json!({ "max_db_size": "5mb" }), ) .await?; { let foo = Database::open_remote_with_connector( "http://foo.primary:8080", "", TurmoilConnector, )?; let foo_conn = foo.connect()?; foo_conn.execute("select 1", ()).await.unwrap(); } // STEP 2: update namespace config to block reads client .post( "http://primary:9090/v1/namespaces/foo/config", json!({ "block_reads": true, "block_writes": false, }), ) .await?; { let foo = Database::open_remote_with_connector( "http://foo.primary:8080", "", TurmoilConnector, )?; let foo_conn = foo.connect()?; foo_conn.execute("select 1", ()).await.unwrap_err(); } // STEP 3: update config again to un-block reads client .post( "http://primary:9090/v1/namespaces/foo/config", json!({ "block_reads": false, "block_writes": false, }), ) .await?; { let foo = Database::open_remote_with_connector( "http://foo.primary:8080", "", TurmoilConnector, )?; let foo_conn = foo.connect()?; foo_conn.execute("select 1", ()).await.unwrap(); } Ok(()) }); sim.run().unwrap(); } #[test] fn meta_attach() { let mut sim = Builder::new() .simulation_duration(Duration::from_secs(1000)) .build(); let tmp = tempdir().unwrap(); make_primary(&mut sim, tmp.path().to_path_buf()); sim.client("client", async { let client = Client::new(); // STEP 1: create namespace and check that it can be read from client .post( "http://primary:9090/v1/namespaces/foo/create", json!({ "max_db_size": "5mb" }), ) .await?; { let foo = Database::open_remote_with_connector( "http://foo.primary:8080", "", TurmoilConnector, )?; let foo_conn = foo.connect()?; foo_conn.execute("select 1", ()).await.unwrap(); } // STEP 2: try attaching a database { let foo = Database::open_remote_with_connector( "http://foo.primary:8080", "", TurmoilConnector, )?; let foo_conn = foo.connect()?; foo_conn.execute("attach foo as foo", ()).await.unwrap_err(); } // STEP 3: update config to allow attaching databases client .post( "http://primary:9090/v1/namespaces/foo/config", json!({ "block_reads": false, "block_writes": false, "allow_attach": true, }), ) .await?; { let foo = Database::open_remote_with_connector( "http://foo.primary:8080", "", TurmoilConnector, )?; let foo_conn = foo.connect()?; foo_conn .execute_batch("attach foo as foo; select * from foo.sqlite_master") .await .unwrap(); } Ok(()) }); sim.run().unwrap(); } #[test] fn meta_attach_shared_schema() { let mut sim = Builder::new() .simulation_duration(Duration::from_secs(1000)) .build(); let tmp = tempdir().unwrap(); make_primary(&mut sim, tmp.path().to_path_buf()); sim.client("client", async { let client = Client::new(); // STEP 1: create two namespaces, one for the shared schema and one that borrows that // shared schema and check that it can be read from client .post( "http://primary:9090/v1/namespaces/schema/create", json!({ "shared_schema": true }), ) .await?; client .post( "http://primary:9090/v1/namespaces/foo/create", json!({ "shared_schema_name": "schema" }), ) .await?; // STEP 3: update config to allow attaching databases client .post( "http://primary:9090/v1/namespaces/foo/config", json!({ "block_reads": false, "block_writes": false, "allow_attach": true, }), ) .await?; { let foo = Database::open_remote_with_connector( "http://foo.primary:8080", "", TurmoilConnector, )?; let foo_conn = foo.connect()?; foo_conn .execute_batch("attach foo as foo; select * from foo.sqlite_master") .await .unwrap(); } Ok(()) }); sim.run().unwrap(); } #[test] fn meta_config_update() { let mut sim = Builder::new() .simulation_duration(Duration::from_secs(1000)) .build(); let tmp = tempdir().unwrap(); make_primary(&mut sim, tmp.path().to_path_buf()); sim.client("client", async { let client = Client::new(); // STEP 1: create two namespaces, one for the shared schema and one that borrows that // shared schema and check that it can be read from client .post( "http://primary:9090/v1/namespaces/foo/create", json!({ "max_db_size": "5mb" }), ) .await?; // STEP 3: update config to allow attaching databases client .post( "http://primary:9090/v1/namespaces/foo/config", json!({ "block_reads": false, "block_writes": false, }), ) .await?; // STEP 4: update config again client .post( "http://primary:9090/v1/namespaces/foo/config", json!({ "block_reads": false, "block_writes": true, }), ) .await?; Ok(()) }); sim.run().unwrap(); }