diff --git a/.cargo/config.toml b/.cargo/config.toml index f0ccbc9..3b8bc0d 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,3 @@ [alias] -xtask = "run --package xtask --" \ No newline at end of file +xtask = "run --package xtask --" +test-clean = "run --package xtask -- test-clean" diff --git a/Cargo.toml b/Cargo.toml index 36c1189..626ce20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,3 +33,6 @@ sha2 = "0.10" thiserror = "2" url = "2" web-time = "=1.1.0" +# dev dependencies +tokio = { version = "1.5", features = ["macros", "rt", "rt-multi-thread"] } + diff --git a/README.md b/README.md index a07c726..a9fc390 100644 --- a/README.md +++ b/README.md @@ -41,4 +41,26 @@ docker run --rm \ --additional-properties useSingleRequestParameter=true ``` +### Testing + +Make sure you have a Typesense server up and running: + +```bash +docker compose up +``` + +Then run this command in the root folder to run the integration tests: + +```bash +cargo test-clean -- --all-features +``` + +This is an alias command which will run a script to clean up your Typesense server after the tests finish. You can pass any arguments of `cargo test` after the `--`. + +To run test for wasm (chrome, headless): + +```bash +cargo test-clean --wasm +``` + If you'd like to contribute, please join our [Slack Community](https://join.slack.com/t/typesense-community/shared_invite/zt-mx4nbsbn-AuOL89O7iBtvkz136egSJg) and say hello! diff --git a/typesense/Cargo.toml b/typesense/Cargo.toml index b25914f..7c88c38 100644 --- a/typesense/Cargo.toml +++ b/typesense/Cargo.toml @@ -48,7 +48,7 @@ trybuild = "1.0.42" # native-only dev deps [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] -tokio = { version = "1.5", features = ["macros", "rt", "rt-multi-thread"] } +tokio = { workspace = true} wiremock = "0.6" # wasm test deps diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index f13f365..a37b55b 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -10,3 +10,5 @@ clap = { workspace = true } reqwest = { version = "0.12", features = ["blocking"] } # "blocking" is simpler for scripts serde = { workspace = true } serde_yaml = { workspace = true } +typesense = { path = "../typesense"} +tokio = { workspace = true} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index b90865b..29f84c9 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -3,8 +3,11 @@ use clap::{Parser, ValueEnum}; use std::{env, fs, process::Command}; mod add_vendor_attributes; mod preprocess_openapi; +mod test_clean; mod vendor_attributes; + use preprocess_openapi::preprocess_openapi_file; +use test_clean::test_clean; const SPEC_URL: &str = "https://raw.githubusercontent.com/typesense/typesense-api-spec/master/openapi.yml"; @@ -27,6 +30,14 @@ struct Cli { /// The list of tasks to run in sequence. #[arg(required = true, value_enum)] tasks: Vec, + + /// Flag to run tests for wasm. + #[arg(long)] + wasm: bool, + + /// Arguments to forward to cargo test + #[arg(last(true))] + test_args: Vec, } #[derive(ValueEnum, Clone, Debug)] @@ -38,11 +49,15 @@ enum Task { Fetch, /// Preprocesses fetched OpenAPI spec file into a new one Preprocess, + /// Clean up test artifacts, e.g., collections + TestClean, } fn main() -> Result<()> { let cli = Cli::parse(); + let rt = tokio::runtime::Runtime::new().unwrap(); + for task in cli.tasks { println!("▶️ Running task: {:?}", task); match task { @@ -50,6 +65,13 @@ fn main() -> Result<()> { Task::Fetch => task_fetch_api_spec()?, Task::Preprocess => preprocess_openapi_file(INPUT_SPEC_FILE, OUTPUT_PREPROCESSED_FILE) .expect("Preprocess failed, aborting!"), + Task::TestClean => { + let test_args = cli.test_args.clone(); + let is_wasm = cli.wasm; + rt.block_on(async move { + test_clean(is_wasm, test_args).await; + }); + } } } Ok(()) diff --git a/xtask/src/test_clean.rs b/xtask/src/test_clean.rs new file mode 100644 index 0000000..9acabf1 --- /dev/null +++ b/xtask/src/test_clean.rs @@ -0,0 +1,68 @@ +use std::time::Duration; +use typesense::{Client, ExponentialBackoff, models::GetCollectionsParameters}; + +async fn clean_test_artifacts() { + let client = Client::builder() + .nodes(vec!["http://localhost:8108"]) + .api_key("xyz") + .healthcheck_interval(Duration::from_secs(5)) + .retry_policy(ExponentialBackoff::builder().build_with_max_retries(3)) + .connection_timeout(Duration::from_secs(3)) + .build() + .expect("Failed to create Typesense client"); + + let collections = client + .collections() + .retrieve(GetCollectionsParameters::new()) + .await + .expect("Get all collections failed!"); + + println!("Cleaning up test collections..."); + + let mut collection_count = 0; + + for collection in collections.iter() { + if !collection.name.starts_with("test_") { + continue; + } + + if let Err(err) = client + .collection_schemaless(&collection.name) + .delete() + .await + { + eprintln!("Failed to delete {}: {}", collection.name, err); + } else { + collection_count += 1; + println!("Deleted {}", collection.name); + } + } + println!("Deleted {} test collections.", collection_count); + println!("✅ Cleanup complete."); +} + +pub async fn test_clean(is_wasm: bool, args: Vec) { + let status = if is_wasm { + println!("Running wasm-pack test..."); + std::process::Command::new("wasm-pack") + .arg("test") + .arg("--headless") + .arg("--chrome") + .args(&args) + .arg("typesense") + .status() + .expect("Failed to run wasm-pack test") + } else { + println!("Running cargo test with arguments: {}", args.join(" ")); + std::process::Command::new("cargo") + .arg("test") + .args(&args) + .status() + .expect("Failed to run cargo test") + }; + + clean_test_artifacts().await; + + // Propagate cargo test exit code + std::process::exit(status.code().unwrap_or(1)); +}