mirror of
https://github.com/meilisearch/MeiliSearch
synced 2024-11-22 12:54:26 +01:00
Add meili-snap crate to make writing snapshot tests easier
This commit is contained in:
parent
0af00f6b32
commit
3e4337c91f
7
.gitignore
vendored
7
.gitignore
vendored
@ -7,3 +7,10 @@
|
|||||||
/data.ms
|
/data.ms
|
||||||
/snapshots
|
/snapshots
|
||||||
/dumps
|
/dumps
|
||||||
|
|
||||||
|
|
||||||
|
# Snapshots
|
||||||
|
## ... large
|
||||||
|
*.full.snap
|
||||||
|
## ... unreviewed
|
||||||
|
*.snap.new
|
||||||
|
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -2242,6 +2242,21 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "md5"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "meili-snap"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"insta",
|
||||||
|
"md5",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "meilisearch-auth"
|
name = "meilisearch-auth"
|
||||||
version = "0.29.1"
|
version = "0.29.1"
|
||||||
|
@ -4,6 +4,7 @@ members = [
|
|||||||
"meilisearch-http",
|
"meilisearch-http",
|
||||||
"meilisearch-types",
|
"meilisearch-types",
|
||||||
"meilisearch-auth",
|
"meilisearch-auth",
|
||||||
|
"meili-snap",
|
||||||
"index-scheduler",
|
"index-scheduler",
|
||||||
"dump",
|
"dump",
|
||||||
"file-store",
|
"file-store",
|
||||||
|
11
meili-snap/Cargo.toml
Normal file
11
meili-snap/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "meili-snap"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
insta = { version = "1.19.1", features = ["json", "redactions"] }
|
||||||
|
md5 = "0.7.0"
|
||||||
|
once_cell = "1.15"
|
229
meili-snap/src/lib.rs
Normal file
229
meili-snap/src/lib.rs
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::{collections::HashMap, path::Path};
|
||||||
|
|
||||||
|
static SNAPSHOT_NAMES: Lazy<Mutex<HashMap<PathBuf, usize>>> = Lazy::new(|| Mutex::default());
|
||||||
|
|
||||||
|
/// Return the md5 hash of the given string
|
||||||
|
pub fn hash_snapshot(snap: &str) -> String {
|
||||||
|
let hash = md5::compute(snap.as_bytes());
|
||||||
|
let hash_str = format!("{hash:x}");
|
||||||
|
hash_str
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
pub fn default_snapshot_settings_for_test(name: Option<&str>) -> (insta::Settings, Cow<'_, str>) {
|
||||||
|
let mut settings = insta::Settings::clone_current();
|
||||||
|
settings.set_prepend_module_to_snapshot(false);
|
||||||
|
let path = Path::new(std::panic::Location::caller().file());
|
||||||
|
let filename = path.file_name().unwrap().to_str().unwrap();
|
||||||
|
settings.set_omit_expression(true);
|
||||||
|
|
||||||
|
let test_name = std::thread::current()
|
||||||
|
.name()
|
||||||
|
.unwrap()
|
||||||
|
.rsplit("::")
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
let path = Path::new("snapshots")
|
||||||
|
.join(filename)
|
||||||
|
.join(&test_name)
|
||||||
|
.to_owned();
|
||||||
|
settings.set_snapshot_path(path.clone());
|
||||||
|
let snap_name = if let Some(name) = name {
|
||||||
|
Cow::Borrowed(name)
|
||||||
|
} else {
|
||||||
|
let mut snapshot_names = SNAPSHOT_NAMES.lock().unwrap();
|
||||||
|
let counter = snapshot_names.entry(path).or_default();
|
||||||
|
*counter += 1;
|
||||||
|
Cow::Owned(format!("{counter}"))
|
||||||
|
};
|
||||||
|
|
||||||
|
(settings, snap_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Create a hashed snapshot test.
|
||||||
|
|
||||||
|
## Arguments:
|
||||||
|
|
||||||
|
1. The content of the snapshot. It is an expression whose result implements the `fmt::Display` trait.
|
||||||
|
2. `name: <name>`: the identifier for the snapshot test (optional)
|
||||||
|
3. `@""` to write the hash of the snapshot inline
|
||||||
|
|
||||||
|
## Behaviour
|
||||||
|
The content of the snapshot will be saved both in full and as a hash. The full snapshot will
|
||||||
|
be saved with the name `<name>.full.snap` but will not be saved to the git repository. The hashed
|
||||||
|
snapshot will be saved inline. If `<name>` is not specified, then a global counter is used to give an
|
||||||
|
identifier to the snapshot.
|
||||||
|
|
||||||
|
Running `cargo test` will check whether the old snapshot is identical to the
|
||||||
|
current one. If they are equal, the test passes. Otherwise, the test fails.
|
||||||
|
|
||||||
|
Use the command line `cargo insta` to approve or reject new snapshots.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
```ignore
|
||||||
|
// The full snapshot is saved under 1.full.snap and contains `10`
|
||||||
|
snapshot_hash!(10, @"d3d9446802a44259755d38e6d163e820");
|
||||||
|
// The full snapshot is saved under snap_name.full.snap and contains `hello world`
|
||||||
|
snapshot_hash!("hello world", name: "snap_name", @"5f93f983524def3dca464469d2cf9f3e");
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! snapshot_hash {
|
||||||
|
($value:expr, @$inline:literal) => {
|
||||||
|
let (settings, snap_name) = $crate::default_snapshot_settings_for_test(None);
|
||||||
|
settings.bind(|| {
|
||||||
|
let snap = format!("{}", $value);
|
||||||
|
let hash_snap = $crate::hash_snapshot(&snap);
|
||||||
|
insta::assert_snapshot!(hash_snap, @$inline);
|
||||||
|
insta::assert_snapshot!(format!("{}.full", snap_name), snap);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
($value:expr, name: $name:expr, @$inline:literal) => {
|
||||||
|
let snap_name = format!("{}", $name);
|
||||||
|
let (settings, snap_name) = $crate::default_snapshot_settings_for_test(Some(&snap_name));
|
||||||
|
settings.bind(|| {
|
||||||
|
let snap = format!("{}", $value);
|
||||||
|
let hash_snap = $crate::hash_snapshot(&snap);
|
||||||
|
insta::assert_snapshot!(hash_snap, @$inline);
|
||||||
|
insta::assert_snapshot!(format!("{}.full", snap_name), snap);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Create a hashed snapshot test.
|
||||||
|
|
||||||
|
## Arguments:
|
||||||
|
1. The content of the snapshot. It is an expression whose result implements the `fmt::Display` trait.
|
||||||
|
2. Optionally one of:
|
||||||
|
1. `name: <name>`: the identifier for the snapshot test
|
||||||
|
2. `@""` to write the hash of the snapshot inline
|
||||||
|
|
||||||
|
## Behaviour
|
||||||
|
The content of the snapshot will be saved in full with the given name
|
||||||
|
or using a global counter to give it an identifier.
|
||||||
|
|
||||||
|
Running `cargo test` will check whether the old snapshot is identical to the
|
||||||
|
current one. If they are equal, the test passes. Otherwise, the test fails.
|
||||||
|
|
||||||
|
Use the command line `cargo insta` to approve or reject new snapshots.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
```ignore
|
||||||
|
// The full snapshot is saved under 1.snap and contains `10`
|
||||||
|
snapshot!(10);
|
||||||
|
// The full snapshot is saved under snap_name.snap and contains `10`
|
||||||
|
snapshot!("hello world", name: "snap_name");
|
||||||
|
// The full snapshot is saved inline
|
||||||
|
snapshot!(format!("{:?}", vec![1, 2]), @"[1, 2]");
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! snapshot {
|
||||||
|
($value:expr, name: $name:expr) => {
|
||||||
|
let snap_name = format!("{}", $name);
|
||||||
|
let (settings, snap_name) = $crate::default_snapshot_settings_for_test(Some(&snap_name));
|
||||||
|
settings.bind(|| {
|
||||||
|
let snap = format!("{}", $value);
|
||||||
|
insta::assert_snapshot!(format!("{}", snap_name), snap);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
($value:expr, @$inline:literal) => {
|
||||||
|
// Note that the name given as argument does not matter since it is only an inline snapshot
|
||||||
|
// We don't pass None because otherwise `meili-snap` will try to assign it a unique identifier
|
||||||
|
let (settings, _) = $crate::default_snapshot_settings_for_test(Some("_dummy_argument"));
|
||||||
|
settings.bind(|| {
|
||||||
|
let snap = format!("{}", $value);
|
||||||
|
insta::assert_snapshot!(snap, @$inline);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
($value:expr) => {
|
||||||
|
let (settings, snap_name) = $crate::default_snapshot_settings_for_test(None);
|
||||||
|
settings.bind(|| {
|
||||||
|
let snap = format!("{}", $value);
|
||||||
|
insta::assert_snapshot!(format!("{}", snap_name), snap);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn snap() {
|
||||||
|
snapshot_hash!(10, @"d3d9446802a44259755d38e6d163e820");
|
||||||
|
snapshot_hash!(20, @"98f13708210194c475687be6106a3b84");
|
||||||
|
snapshot_hash!(30, @"34173cb38f07f89ddbebc2ac9128303f");
|
||||||
|
|
||||||
|
snapshot!(40, @"40");
|
||||||
|
snapshot!(50, @"50");
|
||||||
|
snapshot!(60, @"60");
|
||||||
|
|
||||||
|
snapshot!(70);
|
||||||
|
snapshot!(80);
|
||||||
|
snapshot!(90);
|
||||||
|
|
||||||
|
snapshot!(100, name: "snap_name_1");
|
||||||
|
snapshot_hash!(110, name: "snap_name_2", @"5f93f983524def3dca464469d2cf9f3e");
|
||||||
|
|
||||||
|
snapshot!(120);
|
||||||
|
snapshot!(format!("{:?}", vec![1, 2]), @"[1, 2]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently the name of this module is not part of the snapshot path
|
||||||
|
// It does not bother me, but maybe it is worth changing later on.
|
||||||
|
mod snap {
|
||||||
|
#[test]
|
||||||
|
fn some_test() {
|
||||||
|
snapshot_hash!(10, @"d3d9446802a44259755d38e6d163e820");
|
||||||
|
snapshot_hash!(20, @"98f13708210194c475687be6106a3b84");
|
||||||
|
snapshot_hash!(30, @"34173cb38f07f89ddbebc2ac9128303f");
|
||||||
|
|
||||||
|
snapshot!(40, @"40");
|
||||||
|
snapshot!(50, @"50");
|
||||||
|
snapshot!(60, @"60");
|
||||||
|
|
||||||
|
snapshot!(70);
|
||||||
|
snapshot!(80);
|
||||||
|
snapshot!(90);
|
||||||
|
|
||||||
|
snapshot!(100, name: "snap_name_1");
|
||||||
|
snapshot_hash!(110, name: "snap_name_2", @"5f93f983524def3dca464469d2cf9f3e");
|
||||||
|
|
||||||
|
snapshot!(120);
|
||||||
|
|
||||||
|
snapshot_hash!("", name: "", @"d41d8cd98f00b204e9800998ecf8427e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a string from the value by serializing it as Json, optionally
|
||||||
|
/// redacting some parts of it.
|
||||||
|
///
|
||||||
|
/// The second argument to the macro can be an object expression for redaction.
|
||||||
|
/// It's in the form { selector => replacement }. For more information about redactions
|
||||||
|
/// refer to the redactions feature in the `insta` guide.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! json_string {
|
||||||
|
($value:expr, {$($k:expr => $v:expr),*$(,)?}) => {
|
||||||
|
{
|
||||||
|
let (_, snap) = insta::_prepare_snapshot_for_redaction!($value, {$($k => $v),*}, Json, File);
|
||||||
|
snap
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($value:expr) => {{
|
||||||
|
let value = insta::_macro_support::serialize_value(
|
||||||
|
&$value,
|
||||||
|
insta::_macro_support::SerializationFormat::Json,
|
||||||
|
insta::_macro_support::SnapshotLocation::File
|
||||||
|
);
|
||||||
|
value
|
||||||
|
}};
|
||||||
|
}
|
4
meili-snap/src/snapshots/lib.rs/snap/4.snap
Normal file
4
meili-snap/src/snapshots/lib.rs/snap/4.snap
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
source: meili-snap/src/lib.rs
|
||||||
|
---
|
||||||
|
70
|
4
meili-snap/src/snapshots/lib.rs/snap/5.snap
Normal file
4
meili-snap/src/snapshots/lib.rs/snap/5.snap
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
source: meili-snap/src/lib.rs
|
||||||
|
---
|
||||||
|
80
|
4
meili-snap/src/snapshots/lib.rs/snap/6.snap
Normal file
4
meili-snap/src/snapshots/lib.rs/snap/6.snap
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
source: meili-snap/src/lib.rs
|
||||||
|
---
|
||||||
|
90
|
4
meili-snap/src/snapshots/lib.rs/snap/7.snap
Normal file
4
meili-snap/src/snapshots/lib.rs/snap/7.snap
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
source: meili-snap/src/lib.rs
|
||||||
|
---
|
||||||
|
120
|
4
meili-snap/src/snapshots/lib.rs/snap/snap_name_1.snap
Normal file
4
meili-snap/src/snapshots/lib.rs/snap/snap_name_1.snap
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
source: meili-snap/src/lib.rs
|
||||||
|
---
|
||||||
|
100
|
4
meili-snap/src/snapshots/lib.rs/some_test/4.snap
Normal file
4
meili-snap/src/snapshots/lib.rs/some_test/4.snap
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
source: meili-snap/src/lib.rs
|
||||||
|
---
|
||||||
|
70
|
4
meili-snap/src/snapshots/lib.rs/some_test/5.snap
Normal file
4
meili-snap/src/snapshots/lib.rs/some_test/5.snap
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
source: meili-snap/src/lib.rs
|
||||||
|
---
|
||||||
|
80
|
4
meili-snap/src/snapshots/lib.rs/some_test/6.snap
Normal file
4
meili-snap/src/snapshots/lib.rs/some_test/6.snap
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
source: meili-snap/src/lib.rs
|
||||||
|
---
|
||||||
|
90
|
4
meili-snap/src/snapshots/lib.rs/some_test/7.snap
Normal file
4
meili-snap/src/snapshots/lib.rs/some_test/7.snap
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
source: meili-snap/src/lib.rs
|
||||||
|
---
|
||||||
|
120
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
source: meili-snap/src/lib.rs
|
||||||
|
---
|
||||||
|
100
|
@ -1,13 +1,11 @@
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::env;
|
|
||||||
use std::fs;
|
|
||||||
use std::io::{BufReader, Read};
|
use std::io::{BufReader, Read};
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{fmt, fs};
|
use std::{env, fmt, fs};
|
||||||
|
|
||||||
use byte_unit::{Byte, ByteError};
|
use byte_unit::{Byte, ByteError};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
@ -646,6 +644,17 @@ fn load_ocsp(filename: &Option<PathBuf>) -> anyhow::Result<Vec<u8>> {
|
|||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if the key is defined in the environment variables.
|
||||||
|
/// If not, inserts it with the given value.
|
||||||
|
pub fn export_to_env_if_not_present<T>(key: &str, value: T)
|
||||||
|
where
|
||||||
|
T: AsRef<OsStr>,
|
||||||
|
{
|
||||||
|
if let Err(VarError::NotPresent) = std::env::var(key) {
|
||||||
|
std::env::set_var(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Functions used to get default value for `Opt` fields, needs to be function because of serde's default attribute.
|
/// Functions used to get default value for `Opt` fields, needs to be function because of serde's default attribute.
|
||||||
|
|
||||||
fn default_db_path() -> PathBuf {
|
fn default_db_path() -> PathBuf {
|
||||||
|
Loading…
Reference in New Issue
Block a user