From 969d98fb7b1d7cd87e65db6803be9cca312238fb Mon Sep 17 00:00:00 2001 From: "daniel.hartig" Date: Sat, 17 Dec 2022 21:46:55 -0600 Subject: [PATCH 1/3] Proposed changes to use database and remove trait --- src/lib.rs | 65 +++++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8d06d7f..20bcbcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,27 +22,27 @@ #![warn(missing_docs)] -use couch_rs::{document::TypedCouchDocument, error::CouchError, Client}; +use std::error::Error; +use couch_rs::{database::Database, document::TypedCouchDocument, error::CouchError, Client}; use rand::{distributions::Alphanumeric, Rng}; use tokio_util::sync::CancellationToken; -/// A wrapper for a struct that manages on CouchDB database -/// -/// This trait should be implemented on a struct that manages a CouchDB database, thus enabling that -/// struct to be used in [TestRepo]. -pub trait CouchDBWrapper { - /// Create a new instance of the underlying struct, by passing the sepcifications for the couch_rs - /// client and the name of the database. - fn wrap(uri: &str, username: &str, password: &str, db_name: &str) -> Self; - /// Return the underlying client - fn client(&self) -> &Client; - /// Return the underlying database name - fn dbname(&self) -> &str; -} - -/// Configuration for [TestRepo]. This configuration is also passed to the underlying struct on which -/// [CouchDBWrapper] is implemented, in order to create a new [couch_rs::Client](https://docs.rs/couch_rs/latest/couch_rs/struct.Client.html) -/// and identify the associated database. +// /// A wrapper for a struct that manages on CouchDB database +// /// +// /// This trait should be implemented on a struct that manages a CouchDB database, thus enabling that +// /// struct to be used in [TestRepo]. +// pub trait CouchDBWrapper { +// /// Create a new instance of the underlying struct, by passing the sepcifications for the couch_rs +// /// client and the name of the database. +// fn wrap(uri: &str, username: &str, password: &str, db_name: &str) -> Self; +// /// Return the underlying client +// fn client(&self) -> &Client; +// /// Return the underlying database name +// fn dbname(&self) -> &str; +// } + +/// Configuration for [TestRepo]. This configuration is to create a new [couch_rs::Client](https://docs.rs/couch_rs/latest/couch_rs/struct.Client.html) +/// and name the associated [couch_rs::database::Database](https://docs.rs/couch_rs/latest/couch_rs/database/struct.Database.html). #[derive(Clone)] pub struct TestRepoConfig { uri: String, @@ -79,17 +79,17 @@ impl TestRepoConfig { /// Creation of a new instance of this struct will create a unique, associated CouchDB database. Internally, /// this struct implements a drop token and watcher to determine when this struct is de-allocated, thus /// triggering destruction of the associated CouchDB database. -pub struct TestRepo { +pub struct TestRepo { /// A struct that encapsulates the functionality of an application's data layer. Test using /// this wrapper should call for this struct in order to perform test actions against this /// TestRepo's ephemeral CouchDB database. - pub repo: T, + pub db: Database, drop_token: CancellationToken, dropped_token: CancellationToken, } -impl TestRepo { +impl TestRepo { /// Creates a new instance of TestRepo wrapping a new instance of the type paramter (an implementation /// of [CouchDBWrapper]). This function will create a new [couch_rs::Client](https://docs.rs/couch_rs/latest/couch_rs/struct.Client.html) /// from the parameters passed @@ -102,7 +102,7 @@ impl TestRepo { /// The watcher spawn an asynchronous thread that will observe the drop token every 100 milliseconds. /// when this instance is deallocated, the drop token is destroyed and the watcher will trigger the /// destruction of the database instance created by this method. - pub async fn new(arg_cfg: TestRepoConfig) -> TestRepo { + pub async fn new(arg_cfg: TestRepoConfig) -> Result> { // create random identifier for database and append to db name let test_identifier = rand::thread_rng() .sample_iter(&Alphanumeric) @@ -114,17 +114,17 @@ impl TestRepo { let db_unique_name = format!("{}-{}", arg_cfg.db_name, test_identifier); let cfg = arg_cfg.with_name(db_unique_name); - let wrapped = T::wrap(&cfg.uri, &cfg.username, &cfg.password, &cfg.db_name); + let client = Client::new(&cfg.uri, &cfg.username, &cfg.password)?; + let drop_token = CancellationToken::new(); - let dropped_token = TestRepo::::start_drop_watcher(&drop_token, cfg.clone()).await; + let dropped_token = TestRepo::start_drop_watcher(&drop_token, cfg.clone()).await; // connect to database and return wrapping repository log::info!("Creating database {} for testing", cfg.db_name); // create test database - panic on fail - let c = wrapped.client(); - match c.make_db(&cfg.db_name).await { + match client.make_db(&cfg.db_name).await { Ok(_) => {} Err(e) => { match e.status() { @@ -146,11 +146,11 @@ impl TestRepo { } }; - TestRepo { - repo: wrapped, + Ok(TestRepo { + db: client.db(&cfg.db_name).await?, drop_token: drop_token, dropped_token: dropped_token, - } + }) } /// Pushes data to the unique database associated with this instance. Data is pushed via the @@ -160,8 +160,7 @@ impl TestRepo { &self, data: &mut [S], ) -> Result { - let db = self.repo.client().db(&self.repo.dbname()).await?; - let result = db.bulk_docs(data).await?; + let result = self.db.bulk_docs(data).await?; return Ok(result.len()); } @@ -179,7 +178,7 @@ impl TestRepo { tokio::time::sleep(std::time::Duration::from_millis(100)).await; } - TestRepo::::drop(cfg).await; + TestRepo::drop(cfg).await; dropped_token.cancel(); }); @@ -203,7 +202,7 @@ impl TestRepo { } -impl Drop for TestRepo { +impl Drop for TestRepo { fn drop(&mut self) { self.drop_token.cancel(); From 48277178a77811c35c1a402736c745f227e5a4ac Mon Sep 17 00:00:00 2001 From: "daniel.hartig" Date: Tue, 27 Dec 2022 14:44:52 -0600 Subject: [PATCH 2/3] Re-write docs --- src/lib.rs | 92 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 20bcbcb..91f74a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,21 +4,43 @@ //! CouchDB instance. For a given database name, the created databases append a random string to guarantee //! uniqueness of multiple tests in parallel in the same CouchDB instance. //! -//! In a 'data layer' application model, there is a discrete structure that is used to represent the data -//! layer of an application. When using this package, that data layer is a CouchDB instance. Advanced -//! functionality can be built into the data layer by implementing methods on the data layer struct. A -//! example use is defining a commonly used but complex query as a method. +//! ```rust +//! use serde_json::json; +//! use couch_rs_test::{TestRepo, TestRepoConfig}; //! -//! If the data layer application model is used, a helper trait [CouchDBWrapper] can be applied to the data -//! layer struct; this struct would wraps a [couch_rs::Client](https://docs.rs/couch_rs/latest/couch_rs/struct.Client.html) -//! and the name of a database to use. For testing, -//! implementing the [CouchDBWrapper] trait allows it to be wrapped in the [TestRepo] class -//! to provide automatic creationg and destruction of tables during integration tests. +//! async fn new_database( +//! config_uri: &str, +//! config_username: &str, +//! config_password: &str, +//! config_dbname: &str, +//! ) -> TestRepo { //! -//! The struct against which [CouchDBWrapper] is implemented should only refer to a single CouchDB database. -//! Even though the primary interface is the [couch_rs::Client](https://docs.rs/couch_rs/latest/couch_rs/struct.Client.html), -//! and not the couch_rs database, the automatic -//! creation and destruction implies that only one underlying database is managed by one struct. +//! let repo: TestRepo = match TestRepo::new( +//! TestRepoConfig::new( +//! config_uri, +//! config_username, +//! config_password, +//! config_dbname, +//! ) +//! ).await { +//! Ok(r) => r, +//! // if db creation fails, test will fail, so just panic +//! Err(e) => panic!("Failed to create test database: {}", e), +//! }; +//! +//! // write test data into the newly created database +//! let data = &mut vec!{ +//! json!{{"some": "data"}}, +//! json!{{"some": "other-data"}} +//! }; +//! +//! match repo.with_data(data).await { +//! Ok(cnt) => log::info!("Added {} entries to test database {}", cnt, repo.db.name()), +//! Err(e) => panic!("Failed to set up database: {}", e), +//! }; +//! repo +//! } +//! ``` #![warn(missing_docs)] @@ -27,20 +49,6 @@ use couch_rs::{database::Database, document::TypedCouchDocument, error::CouchErr use rand::{distributions::Alphanumeric, Rng}; use tokio_util::sync::CancellationToken; -// /// A wrapper for a struct that manages on CouchDB database -// /// -// /// This trait should be implemented on a struct that manages a CouchDB database, thus enabling that -// /// struct to be used in [TestRepo]. -// pub trait CouchDBWrapper { -// /// Create a new instance of the underlying struct, by passing the sepcifications for the couch_rs -// /// client and the name of the database. -// fn wrap(uri: &str, username: &str, password: &str, db_name: &str) -> Self; -// /// Return the underlying client -// fn client(&self) -> &Client; -// /// Return the underlying database name -// fn dbname(&self) -> &str; -// } - /// Configuration for [TestRepo]. This configuration is to create a new [couch_rs::Client](https://docs.rs/couch_rs/latest/couch_rs/struct.Client.html) /// and name the associated [couch_rs::database::Database](https://docs.rs/couch_rs/latest/couch_rs/database/struct.Database.html). #[derive(Clone)] @@ -52,8 +60,8 @@ pub struct TestRepoConfig { } impl TestRepoConfig { - /// Create a new configuration; identifying the uri, username and password for the CouchDB instance as - /// well as the database name. + /// Create a new configuration; identifying the uri, username and password for the CouchDB client + /// instance as well as the database name for the underlying database. pub fn new(uri: &str, uname: &str, pwd: &str, dbname: &str) -> TestRepoConfig { TestRepoConfig { uri: uri.to_string(), @@ -73,15 +81,12 @@ impl TestRepoConfig { /// A wrapper for a struct that encapsulates functionality of an application's data layer. /// -/// The type parameter is the type of the data layer struct to be wrapped. The data layer struct must -/// implement the [CouchDBWrapper] trait. -/// -/// Creation of a new instance of this struct will create a unique, associated CouchDB database. Internally, -/// this struct implements a drop token and watcher to determine when this struct is de-allocated, thus -/// triggering destruction of the associated CouchDB database. +/// Creation of a new instance of this struct will create a unique [couch_rs::database::Database](https://docs.rs/couch_rs/latest/couch_rs/database/struct.Database.html). +/// Internally, this struct implements a drop token and watcher to determine when this struct is de-allocated, +/// thus triggering destruction of the associated CouchDB database. pub struct TestRepo { - /// A struct that encapsulates the functionality of an application's data layer. Test using - /// this wrapper should call for this struct in order to perform test actions against this + /// A [couch_rs::database::Database](https://docs.rs/couch_rs/latest/couch_rs/database/struct.Database.html). + /// Tests using this wrapper should access this struct in order to perform test actions against this /// TestRepo's ephemeral CouchDB database. pub db: Database, @@ -90,13 +95,12 @@ pub struct TestRepo { } impl TestRepo { - /// Creates a new instance of TestRepo wrapping a new instance of the type paramter (an implementation - /// of [CouchDBWrapper]). This function will create a new [couch_rs::Client](https://docs.rs/couch_rs/latest/couch_rs/struct.Client.html) - /// from the parameters passed - /// as part of the [TestRepoConfig] argument. It will then create a new database in CouchDB using the - /// client connection and a database name consisting of the name defined in config plus a random suffix. - /// This randomization of database names helps prevent collisions during parallel test runs against - /// the same CouchDB instance. + /// Creates a new instance of TestRepo wrapping a new instance of [couch_rs::database::Database](https://docs.rs/couch_rs/latest/couch_rs/database/struct.Database.html). + /// This function will create a new [couch_rs::Client](https://docs.rs/couch_rs/latest/couch_rs/struct.Client.html) + /// from the parameters passed as part of the [TestRepoConfig] argument and then create a new database + /// in CouchDB using the client connection and a database name consisting of the name defined in config + /// plus a random suffix.This randomization of database names helps prevent collisions during parallel + /// test excutions against the same CouchDB instance. /// /// This function also creates a drop token and watcher to determine when this instance is de-allocated /// The watcher spawn an asynchronous thread that will observe the drop token every 100 milliseconds. From eafdcb724ea1124482d7e23924ee01759b9af1a4 Mon Sep 17 00:00:00 2001 From: "daniel.hartig" Date: Tue, 27 Dec 2022 15:00:57 -0600 Subject: [PATCH 3/3] Add repository information --- Cargo.toml | 7 ++++++- src/lib.rs | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 34afcff..075a83d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,12 @@ [package] name = "couch_rs_test" -version = "0.1.0" +version = "0.2.0" edition = "2021" +license = "MIT" +description = "Tools for testing a CouchDB repository implemented in Rust with couch_rs." +repository = "https://github.com/kingledion/couch_rs_test" +readme = "README.md" +keywords = ["couchdb", "testing"] [dependencies] http = "0.2" diff --git a/src/lib.rs b/src/lib.rs index 91f74a2..1d06f6d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,7 +49,9 @@ use couch_rs::{database::Database, document::TypedCouchDocument, error::CouchErr use rand::{distributions::Alphanumeric, Rng}; use tokio_util::sync::CancellationToken; -/// Configuration for [TestRepo]. This configuration is to create a new [couch_rs::Client](https://docs.rs/couch_rs/latest/couch_rs/struct.Client.html) +/// Configuration for [TestRepo]. +/// +/// This configuration is to create a new [couch_rs::Client](https://docs.rs/couch_rs/latest/couch_rs/struct.Client.html) /// and name the associated [couch_rs::database::Database](https://docs.rs/couch_rs/latest/couch_rs/database/struct.Database.html). #[derive(Clone)] pub struct TestRepoConfig {