chore: Port `json-schema-test-suite` as a local dependency
Signed-off-by: Dmitry Dygalo <dadygalo@gmail.com>
This commit is contained in:
parent
28b71f64e3
commit
bf17e1c8e0
|
@ -2,9 +2,11 @@
|
|||
/jsonschema/target
|
||||
/bindings/*/target
|
||||
/bench_helpers/target
|
||||
/jsonschema-test-suite/target
|
||||
.hypothesis
|
||||
.benchmarks
|
||||
/jsonschema/Cargo.lock
|
||||
/jsonschema-test-suite/Cargo.lock
|
||||
|
||||
# IDEs
|
||||
/.idea
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
# Changelog
|
||||
|
||||
## 0.3.0 (2020-06-29)
|
||||
|
||||
* Ignore tests accepts regular expressions
|
||||
|
||||
## 0.2.0 (2020-06-22)
|
||||
|
||||
* Fix documentation links
|
||||
* Fix test_name definition in case of mulitple nested directories
|
||||
|
||||
## 0.1.0 (2020-06-22)
|
||||
|
||||
* Initial Release
|
|
@ -0,0 +1,31 @@
|
|||
[package]
|
||||
name = "json_schema_test_suite"
|
||||
version = "0.3.0"
|
||||
authors = ["Samuele Maci <macisamuele@gmail.com>"]
|
||||
edition = "2018"
|
||||
publish = true
|
||||
readme = "README.md"
|
||||
keywords = ["jsonschema"]
|
||||
categories = ["development-tools::procedural-macro-helpers", "development-tools::testing"]
|
||||
description = "Procedural Macro Attribute to run all the test cases described in JSON-Schema-Test-Suite"
|
||||
repository = "https://github.com/macisamuele/json-schema-test-suite-rs"
|
||||
documentation = "http://docs.rs/json-schema-test-suite"
|
||||
license = "MIT"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dev-dependencies]
|
||||
lazy_static = "1"
|
||||
mockito = "0"
|
||||
reqwest = { version = "0", default-features = false, features = [ "blocking", "json" ] }
|
||||
serde_json = "1"
|
||||
|
||||
[dependencies]
|
||||
json_schema_test_suite_proc_macro = { path = "./proc_macro", version = "=0.3.0" }
|
||||
json_schema_test_suite_test_case = { path = "./test_case", version = "=0.3.0" }
|
||||
serde_json = "1"
|
||||
|
||||
[workspace]
|
||||
members = ["proc_macro", "test_case"]
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Samuele Maci
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,75 @@
|
|||
# json_schema_test_suite
|
||||
|
||||
[![ci](https://github.com/macisamuele/json-schema-test-suite-rs/workflows/ci/badge.svg)](https://github.com/macisamuele/json-schema-test-suite-rs/actions)
|
||||
[![codecov](https://codecov.io/gh/macisamuele/json-schema-test-suite-rs/branch/master/graph/badge.svg)](https://codecov.io/gh/macisamuele/json-schema-test-suite-rs)
|
||||
[![Crates.io](https://img.shields.io/crates/v/json-schema-test-suite.svg)](https://crates.io/crates/json-schema-test-suite)
|
||||
[![docs.rs](https://docs.rs/json_schema_test_suite/badge.svg)](https://docs.rs/json-schema-test-suite/)
|
||||
|
||||
The crate provides a procedural macro attribute that allow to generate all the test cases
|
||||
described by [JSON-Schema-Test-Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite).
|
||||
|
||||
The main objective is to ensure that for each test a mock server is started and will be able to
|
||||
capture all the requests, ensuring that tests can be ran only interacting with `cargo test`
|
||||
|
||||
In order to ude the procedural macro attribute there are few assumptions that are made:
|
||||
|
||||
* [`lazy_static`](https://crates.io/crates/lazy_static) dependency is added into your `[dev-dependencies]`
|
||||
section of the `Cargo.toml` file
|
||||
* [`mockito`](https://crates.io/crates/mockito) dependency is added into your `[dev-dependencies]`
|
||||
section of the `Cargo.toml` file
|
||||
* [`serde-json`](https://crates.io/crates/serde-json) dependency is added into your `[dev-dependencies]`
|
||||
section of the `Cargo.toml` file
|
||||
* the annotated method signature should be: `fn (&str, json_schema_test_suite::TestCase) -> ()`
|
||||
|
||||
## How to use
|
||||
|
||||
### Cargo.toml
|
||||
|
||||
```toml
|
||||
# Ensure that the following lines are present into your Cargo.toml file
|
||||
[dev-dependencies]
|
||||
json_schema_test_suite = "0"
|
||||
# Be careful with dependencies version (using `*` version is never recommended).
|
||||
# The proc-macro uses nothing fancy with the dependencies, so any version should work well :)
|
||||
lazy_static = "*"
|
||||
mockito = "*"
|
||||
serde_json = "*"
|
||||
```
|
||||
|
||||
### Rust test file example
|
||||
|
||||
```rust
|
||||
use json_schema_test_suite::{json_schema_test_suite, TestCase};
|
||||
|
||||
// If no tests should be ignored
|
||||
#[json_schema_test_suite(
|
||||
// path separator is assumed to be `/` (regardless of the run platform)
|
||||
"path/to/JSON-Schema-Test-Suite/repository",
|
||||
// test directory (in <JSON-Schema-Test-Suite>/tests/<test_directory>)
|
||||
"draft7"
|
||||
)]
|
||||
// If some tests needs to be ignored, just defined them into `{ ... }` as follow
|
||||
#[json_schema_test_suite(
|
||||
// path separator is assumed to be `/` (regardless of the run platform)
|
||||
"path/to/JSON-Schema-Test-Suite/repository",
|
||||
// test directory (in <JSON-Schema-Test-Suite>/tests/<test_directory>)
|
||||
"draft6",
|
||||
{ // Names, as generated by the macro, of the tests to ignore
|
||||
// NOTE: They can be regular expression as well.
|
||||
"name of the tests to ignore",
|
||||
}
|
||||
)]
|
||||
fn my_simple_test(
|
||||
// address of the HTTP server providing the remote files of JSON-Schema-Test-Suite.
|
||||
// The format will be: `hostname:port`.
|
||||
// This parameter is passed because by starting a mock server we might not start it
|
||||
// into `localhost:1234` as expected by JSON-Schema-Test-Suite
|
||||
server_address: &str,
|
||||
// Representation of the test case
|
||||
test_case: TestCase,
|
||||
) {
|
||||
// TODO: Add here your testing logic
|
||||
}
|
||||
```
|
||||
|
||||
License: MIT
|
|
@ -0,0 +1,2 @@
|
|||
/Cargo.lock
|
||||
/target
|
|
@ -0,0 +1,31 @@
|
|||
[package]
|
||||
name = "json_schema_test_suite_proc_macro"
|
||||
version = "0.3.0"
|
||||
authors = ["Samuele Maci <macisamuele@gmail.com>"]
|
||||
edition = "2018"
|
||||
publish = true
|
||||
keywords = ["jsonschema"]
|
||||
categories = ["development-tools::procedural-macro-helpers", "development-tools::testing"]
|
||||
description = "Procedural Macro Attribute to run all the test cases described in JSON-Schema-Test-Suite"
|
||||
repository = "https://github.com/macisamuele/json-schema-test-suite-rs"
|
||||
documentation = "http://docs.rs/json-schema-test-suite-proc-macro"
|
||||
license = "MIT"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[lib]
|
||||
proc-macro = true
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dev-dependencies]
|
||||
lazy_static = "1"
|
||||
mockito = "0"
|
||||
serde_json = "1"
|
||||
|
||||
[dependencies]
|
||||
json_schema_test_suite_test_case = { path = "../test_case", version = "=0.3.0" }
|
||||
proc-macro2 = "1"
|
||||
regex = "1"
|
||||
quote = "1"
|
||||
serde = { version = "1", features = [ "derive" ] }
|
||||
serde_json = "1"
|
||||
syn = "1"
|
|
@ -0,0 +1,8 @@
|
|||
# json_schema_test_suite_proc_macro
|
||||
|
||||
This crate is supposed to support [`json_schema_test_suite`](https://crates.io/crates/json_schema_test_suite)
|
||||
by exporting `json_schema_test_suite` procedural macro.
|
||||
|
||||
Please refer to [`json-schema-test-suite-proc-macro` docs](https://docs.rs/json-schema-test-suite) for more informaton.
|
||||
|
||||
License: MIT
|
|
@ -0,0 +1,48 @@
|
|||
use regex::Regex;
|
||||
use std::path::{Path, PathBuf, MAIN_SEPARATOR};
|
||||
use syn::{
|
||||
braced,
|
||||
parse::{Parse, ParseStream},
|
||||
LitStr, Token,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AttrConfig {
|
||||
pub(crate) json_schema_test_suite_path: PathBuf,
|
||||
pub(crate) draft_folder: String,
|
||||
pub(crate) tests_to_exclude_regex: Vec<Regex>,
|
||||
}
|
||||
|
||||
impl Parse for AttrConfig {
|
||||
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
|
||||
let json_schema_test_suite_path_str: String = input.parse::<LitStr>()?.value();
|
||||
let _ = input.parse::<Token![,]>()?;
|
||||
let draft_folder: String = input.parse::<LitStr>()?.value();
|
||||
let tests_to_exclude_regex: Vec<Regex> = if input.parse::<Token![,]>().is_ok() {
|
||||
let tests_to_exclude_tokens = {
|
||||
let braced_content;
|
||||
braced!(braced_content in input);
|
||||
#[allow(clippy::redundant_closure_for_method_calls)]
|
||||
let res: syn::punctuated::Punctuated<LitStr, Token![,]> =
|
||||
braced_content.parse_terminated(|v| v.parse())?;
|
||||
res
|
||||
};
|
||||
tests_to_exclude_tokens
|
||||
.iter()
|
||||
.filter_map(|content| Regex::new(&format!("^{}$", content.value())).ok())
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
let json_schema_test_suite_path =
|
||||
Path::new(&json_schema_test_suite_path_str.replace("/", &MAIN_SEPARATOR.to_string()))
|
||||
.to_path_buf();
|
||||
|
||||
Ok(Self {
|
||||
json_schema_test_suite_path,
|
||||
draft_folder,
|
||||
tests_to_exclude_regex,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
//! This crate is supposed to support [`json_schema_test_suite`](https://crates.io/crates/json_schema_test_suite)
|
||||
//! by exporting `json_schema_test_suite` procedural macro.
|
||||
//!
|
||||
//! Please refer to [`json-schema-test-suite-proc-macro` docs](https://docs.rs/json-schema-test-suite) for more informaton.
|
||||
#![warn(
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::doc_markdown,
|
||||
clippy::explicit_iter_loop,
|
||||
clippy::match_same_arms,
|
||||
clippy::needless_borrow,
|
||||
clippy::needless_pass_by_value,
|
||||
clippy::option_map_unwrap_or,
|
||||
clippy::option_map_unwrap_or_else,
|
||||
clippy::option_unwrap_used,
|
||||
clippy::pedantic,
|
||||
clippy::print_stdout,
|
||||
clippy::redundant_closure,
|
||||
clippy::result_map_unwrap_or_else,
|
||||
clippy::result_unwrap_used,
|
||||
clippy::trivially_copy_pass_by_ref,
|
||||
missing_debug_implementations,
|
||||
missing_docs,
|
||||
trivial_casts,
|
||||
unreachable_pub,
|
||||
unsafe_code,
|
||||
unused_extern_crates,
|
||||
unused_import_braces,
|
||||
unused_qualifications,
|
||||
unused_results,
|
||||
variant_size_differences
|
||||
)]
|
||||
mod attribute_config;
|
||||
mod mockito_mocks;
|
||||
mod test_case;
|
||||
|
||||
use json_schema_test_suite_test_case::TestCase;
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::{format_ident, quote};
|
||||
use regex::Regex;
|
||||
use syn::{parse_macro_input, Ident, ItemFn};
|
||||
|
||||
fn test_token_stream(
|
||||
tests_to_ignore_regex: &[Regex],
|
||||
wrapped_test_ident: &Ident,
|
||||
test: &TestCase,
|
||||
) -> TokenStream2 {
|
||||
let name = Ident::new(&test.name, Span::call_site());
|
||||
|
||||
let maybe_ignore = if tests_to_ignore_regex
|
||||
.iter()
|
||||
.any(|regex| regex.is_match(&test.name))
|
||||
{
|
||||
quote! { #[ignore] }
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
let wrapped_test_case = test_case::WrappedTestCase::from(test);
|
||||
quote! {
|
||||
#[test]
|
||||
#maybe_ignore
|
||||
fn #name() {
|
||||
setup_mocks();
|
||||
|
||||
super::#wrapped_test_ident(
|
||||
&mockito::server_address().to_string(),
|
||||
#wrapped_test_case,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Procedural macro that allows a test to be executed for all the configurations defined
|
||||
/// by [JSON-Schema-Test-Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite)
|
||||
///
|
||||
/// The `proc_macro_attribute` should be used on a function with the current signature
|
||||
/// ```rust
|
||||
/// # use json_schema_test_suite_test_case::TestCase;
|
||||
/// fn my_simple_test(
|
||||
/// // address of the HTTP server providing the remote files of JSON-Schema-Test-Suite. The format will be: `hostname:port`
|
||||
/// // This parameter is passed because by starting a mock server we might not start it into `localhost:1234` as expected by JSON-Schema-Test-Suite
|
||||
/// server_address: &str,
|
||||
/// // Representation of the test case (includes draft_version, descriptions, schema, instance, expected_valid)
|
||||
/// test_case: TestCase,
|
||||
/// ) {
|
||||
/// // TODO: Add here your testing logic
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_attribute]
|
||||
pub fn json_schema_test_suite(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let proc_macro_attributes = parse_macro_input!(attr as attribute_config::AttrConfig);
|
||||
let item_fn = parse_macro_input!(item as ItemFn);
|
||||
|
||||
let original_function_ident = &item_fn.sig.ident;
|
||||
let tests_token_stream: Vec<TokenStream2> = test_case::load(
|
||||
&proc_macro_attributes.json_schema_test_suite_path,
|
||||
&proc_macro_attributes.draft_folder,
|
||||
)
|
||||
.iter()
|
||||
.map(|test| {
|
||||
test_token_stream(
|
||||
&proc_macro_attributes.tests_to_exclude_regex,
|
||||
original_function_ident,
|
||||
test,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let setup_mockito_mocks_token_stream =
|
||||
mockito_mocks::setup(&proc_macro_attributes.json_schema_test_suite_path);
|
||||
|
||||
let mod_name = format_ident!(
|
||||
"{}_{}",
|
||||
original_function_ident,
|
||||
proc_macro_attributes.draft_folder.replace("-", "_")
|
||||
);
|
||||
|
||||
let output = quote! {
|
||||
#item_fn
|
||||
|
||||
mod #mod_name {
|
||||
lazy_static::lazy_static! {
|
||||
static ref MOCKS: Vec<mockito::Mock> = vec![
|
||||
#(#setup_mockito_mocks_token_stream),*
|
||||
];
|
||||
}
|
||||
|
||||
fn setup_mocks() {
|
||||
// Dereference to ensure that lazy_static actually invokes the mocks creation
|
||||
let _ = *MOCKS;
|
||||
}
|
||||
|
||||
#(#tests_token_stream)*
|
||||
}
|
||||
};
|
||||
output.into()
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use std::{fs, path::Path};
|
||||
|
||||
pub(crate) fn setup(json_schema_test_suite_path: &Path) -> Vec<TokenStream> {
|
||||
fn remote_paths(dir: &Path) -> Vec<String> {
|
||||
let mut paths = vec![];
|
||||
for result_entry in fs::read_dir(dir)
|
||||
.unwrap_or_else(|_| panic!("Remotes directory not found: {}", dir.display()))
|
||||
{
|
||||
if let Ok(entry) = result_entry {
|
||||
let path = entry.path();
|
||||
if entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false) {
|
||||
paths.extend(remote_paths(&path));
|
||||
} else {
|
||||
paths.push(path.to_str().map_or_else(
|
||||
|| {
|
||||
panic!(
|
||||
"No issues are expected while converting path={} to string",
|
||||
path.display()
|
||||
);
|
||||
},
|
||||
ToString::to_string,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
paths
|
||||
}
|
||||
|
||||
let remote_base_path = json_schema_test_suite_path.join("remotes");
|
||||
let base_path = remote_base_path.to_str().unwrap_or_else(|| {
|
||||
panic!(
|
||||
"No issues are expected while converting remote_base_path={} to string",
|
||||
remote_base_path.display()
|
||||
);
|
||||
});
|
||||
remote_paths(&remote_base_path)
|
||||
.iter()
|
||||
.filter_map(|remote_path| {
|
||||
let path = remote_path
|
||||
.trim_start_matches(base_path)
|
||||
.replace(std::path::MAIN_SEPARATOR, "/");
|
||||
if let Ok(file_content) = std::fs::read_to_string(remote_path) {
|
||||
Some(quote! {
|
||||
mockito::mock("GET", #path)
|
||||
.with_body(
|
||||
#file_content
|
||||
// Replace static links to localhost:1234 to the mockito generated server address
|
||||
.replace("localhost:1234", &mockito::server_address().to_string())
|
||||
)
|
||||
.create()
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
pub(crate) use json_schema_test_suite_test_case::TestCase;
|
||||
use serde::Deserialize;
|
||||
use serde_json::{from_reader, Value};
|
||||
use std::{ffi::OsStr, fs, fs::File, path::Path};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct JSONSchemaTest {
|
||||
data: Value,
|
||||
description: String,
|
||||
valid: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct JSONSchemaTestGroup {
|
||||
description: String,
|
||||
schema: Value,
|
||||
tests: Vec<JSONSchemaTest>,
|
||||
}
|
||||
|
||||
/// Extract the draft version from the path of the test file.
|
||||
fn draft_version(json_schema_test_suite_path: &Path, file_path: &Path) -> String {
|
||||
file_path
|
||||
.strip_prefix(json_schema_test_suite_path.join("tests"))
|
||||
.ok()
|
||||
.and_then(Path::to_str)
|
||||
.map(|v| v.split(std::path::MAIN_SEPARATOR))
|
||||
.and_then(|mut v| v.next())
|
||||
.map_or_else(
|
||||
|| {
|
||||
panic!(
|
||||
"No issues are expected while extracting the draft-version from the file_path. json_schema_test_suite_path={}, file_path={}",
|
||||
json_schema_test_suite_path.display(),
|
||||
file_path.display()
|
||||
)
|
||||
},
|
||||
ToString::to_string,
|
||||
)
|
||||
}
|
||||
|
||||
fn load_inner(json_schema_test_suite_path: &Path, dir: &Path, prefix: &str) -> Vec<TestCase> {
|
||||
let mut tests = vec![];
|
||||
for result_entry in
|
||||
fs::read_dir(dir).unwrap_or_else(|_| panic!("Tests directory not found: {}", dir.display()))
|
||||
{
|
||||
if let Ok(entry) = result_entry {
|
||||
let path = entry.path();
|
||||
if entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false) {
|
||||
tests.extend(load_inner(
|
||||
json_schema_test_suite_path,
|
||||
&path,
|
||||
&format!(
|
||||
"{}{}_",
|
||||
prefix,
|
||||
path.file_name().and_then(OsStr::to_str).unwrap_or_else(|| {
|
||||
panic!(
|
||||
"No issues are expected while extracting the filename from path={}",
|
||||
path.display()
|
||||
)
|
||||
})
|
||||
),
|
||||
));
|
||||
} else if let Ok(file_reader) = File::open(&path) {
|
||||
let test_groups: Vec<JSONSchemaTestGroup> = from_reader(file_reader).unwrap_or_else(|_| {
|
||||
panic!(
|
||||
r#"{} does not contain valid content. Expected something like [{{"schema": ..., "tests": [{{"data": ..., "is_valid": ...}}, ...]}}]"#,
|
||||
path.display()
|
||||
);
|
||||
});
|
||||
|
||||
tests.extend(test_groups.iter().enumerate().flat_map(|(gid, test_group)| {
|
||||
test_group
|
||||
.tests
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(tid, test_case)| TestCase {
|
||||
name: format!(
|
||||
"{}{}_{}_{}",
|
||||
prefix,
|
||||
path.file_stem()
|
||||
.and_then(OsStr::to_str)
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"No issues are expected while extracting the filename (without extension) from path={}",
|
||||
path.display()
|
||||
);
|
||||
})
|
||||
.replace('-', "_"),
|
||||
gid,
|
||||
tid
|
||||
),
|
||||
draft_version: draft_version(json_schema_test_suite_path, &path),
|
||||
group_description: test_group.description.clone(),
|
||||
test_case_description: test_case.description.clone(),
|
||||
schema: test_group.schema.clone(),
|
||||
instance: test_case.data.clone(),
|
||||
is_valid: test_case.valid,
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
tests
|
||||
}
|
||||
|
||||
/// Load all the test cases present into `draft_folder`
|
||||
pub(crate) fn load(json_schema_test_suite_path: &Path, draft_folder: &str) -> Vec<TestCase> {
|
||||
load_inner(
|
||||
json_schema_test_suite_path,
|
||||
&json_schema_test_suite_path.join("tests").join(draft_folder),
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) struct WrappedTestCase<'a>(&'a TestCase);
|
||||
impl<'a> From<&'a TestCase> for WrappedTestCase<'a> {
|
||||
fn from(value: &'a TestCase) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
impl<'a> ToTokens for WrappedTestCase<'a> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let name = &self.0.name;
|
||||
let draft_version = &self.0.draft_version;
|
||||
let group_description = &self.0.group_description;
|
||||
let test_case_description = &self.0.test_case_description;
|
||||
let schema_str = self.0.schema.to_string();
|
||||
let instance_str = self.0.instance.to_string();
|
||||
let is_valid = self.0.is_valid;
|
||||
|
||||
let output = quote! {
|
||||
json_schema_test_suite::TestCase {
|
||||
name: #name.to_string(),
|
||||
draft_version: #draft_version.to_string(),
|
||||
group_description: #group_description.to_string(),
|
||||
test_case_description: #test_case_description.to_string(),
|
||||
schema: serde_json::from_str(#schema_str).unwrap(),
|
||||
instance: serde_json::from_str(#instance_str).unwrap(),
|
||||
is_valid: #is_valid,
|
||||
}
|
||||
};
|
||||
tokens.extend(output);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
//! [![ci](https://github.com/macisamuele/json-schema-test-suite-rs/workflows/ci/badge.svg)](https://github.com/macisamuele/json-schema-test-suite-rs/actions)
|
||||
//! [![codecov](https://codecov.io/gh/macisamuele/json-schema-test-suite-rs/branch/master/graph/badge.svg)](https://codecov.io/gh/macisamuele/json-schema-test-suite-rs)
|
||||
//! [![Crates.io](https://img.shields.io/crates/v/json-schema-test-suite.svg)](https://crates.io/crates/json-schema-test-suite)
|
||||
//! [![docs.rs](https://docs.rs/json_schema_test_suite/badge.svg)](https://docs.rs/json-schema-test-suite/)
|
||||
//!
|
||||
//! The crate provides a procedural macro attribute that allow to generate all the test cases
|
||||
//! described by [JSON-Schema-Test-Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite).
|
||||
//!
|
||||
//! The main objective is to ensure that for each test a mock server is started and will be able to
|
||||
//! capture all the requests, ensuring that tests can be ran only interacting with `cargo test`
|
||||
//!
|
||||
//! In order to ude the procedural macro attribute there are few assumptions that are made:
|
||||
//!
|
||||
//! * [`lazy_static`](https://crates.io/crates/lazy_static) dependency is added into your `[dev-dependencies]`
|
||||
//! section of the `Cargo.toml` file
|
||||
//! * [`mockito`](https://crates.io/crates/mockito) dependency is added into your `[dev-dependencies]`
|
||||
//! section of the `Cargo.toml` file
|
||||
//! * [`serde-json`](https://crates.io/crates/serde-json) dependency is added into your `[dev-dependencies]`
|
||||
//! section of the `Cargo.toml` file
|
||||
//! * the annotated method signature should be: `fn (&str, json_schema_test_suite::TestCase) -> ()`
|
||||
//!
|
||||
//! # How to use
|
||||
//!
|
||||
//! ## Cargo.toml
|
||||
//!
|
||||
//! ```toml
|
||||
//! # Ensure that the following lines are present into your Cargo.toml file
|
||||
//! [dev-dependencies]
|
||||
//! json_schema_test_suite = "0"
|
||||
//! # Be careful with dependencies version (using `*` version is never recommended).
|
||||
//! # The proc-macro uses nothing fancy with the dependencies, so any version should work well :)
|
||||
//! lazy_static = "*"
|
||||
//! mockito = "*"
|
||||
//! serde_json = "*"
|
||||
//! ```
|
||||
//!
|
||||
//! ## Rust test file example
|
||||
//!
|
||||
//! ```rust
|
||||
//! use json_schema_test_suite::{json_schema_test_suite, TestCase};
|
||||
//!
|
||||
//! // If no tests should be ignored
|
||||
//! #[json_schema_test_suite(
|
||||
//! // path separator is assumed to be `/` (regardless of the run platform)
|
||||
//! "path/to/JSON-Schema-Test-Suite/repository",
|
||||
//! // test directory (in <JSON-Schema-Test-Suite>/tests/<test_directory>)
|
||||
//! "draft7"
|
||||
//! )]
|
||||
//! // If some tests needs to be ignored, just defined them into `{ ... }` as follow
|
||||
//! #[json_schema_test_suite(
|
||||
//! // path separator is assumed to be `/` (regardless of the run platform)
|
||||
//! "path/to/JSON-Schema-Test-Suite/repository",
|
||||
//! // test directory (in <JSON-Schema-Test-Suite>/tests/<test_directory>)
|
||||
//! "draft6",
|
||||
//! { // Names, as generated by the macro, of the tests to ignore
|
||||
//! // NOTE: They can be regular expression as well.
|
||||
//! "name of the tests to ignore",
|
||||
//! }
|
||||
//! )]
|
||||
//! fn my_simple_test(
|
||||
//! // address of the HTTP server providing the remote files of JSON-Schema-Test-Suite.
|
||||
//! // The format will be: `hostname:port`.
|
||||
//! // This parameter is passed because by starting a mock server we might not start it
|
||||
//! // into `localhost:1234` as expected by JSON-Schema-Test-Suite
|
||||
//! server_address: &str,
|
||||
//! // Representation of the test case
|
||||
//! test_case: TestCase,
|
||||
//! ) {
|
||||
//! // TODO: Add here your testing logic
|
||||
//! }
|
||||
//! ```
|
||||
#![warn(
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::doc_markdown,
|
||||
clippy::explicit_iter_loop,
|
||||
clippy::match_same_arms,
|
||||
clippy::needless_borrow,
|
||||
clippy::needless_pass_by_value,
|
||||
clippy::option_map_unwrap_or,
|
||||
clippy::option_map_unwrap_or_else,
|
||||
clippy::option_unwrap_used,
|
||||
clippy::pedantic,
|
||||
clippy::print_stdout,
|
||||
clippy::redundant_closure,
|
||||
clippy::result_map_unwrap_or_else,
|
||||
clippy::result_unwrap_used,
|
||||
clippy::trivially_copy_pass_by_ref,
|
||||
missing_debug_implementations,
|
||||
missing_docs,
|
||||
trivial_casts,
|
||||
unreachable_pub,
|
||||
unsafe_code,
|
||||
unused_extern_crates,
|
||||
unused_import_braces,
|
||||
unused_qualifications,
|
||||
unused_results,
|
||||
variant_size_differences
|
||||
)]
|
||||
|
||||
pub use json_schema_test_suite_proc_macro::json_schema_test_suite;
|
||||
pub use json_schema_test_suite_test_case::TestCase;
|
|
@ -0,0 +1,2 @@
|
|||
/Cargo.lock
|
||||
/target
|
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "json_schema_test_suite_test_case"
|
||||
version = "0.3.0"
|
||||
authors = ["Samuele Maci <macisamuele@gmail.com>"]
|
||||
edition = "2018"
|
||||
publish = true
|
||||
keywords = ["jsonschema"]
|
||||
categories = ["development-tools::procedural-macro-helpers", "development-tools::testing"]
|
||||
description = "Procedural Macro Attribute to run all the test cases described in JSON-Schema-Test-Suite"
|
||||
repository = "https://github.com/macisamuele/json-schema-test-suite-rs"
|
||||
documentation = "http://docs.rs/json-schema-test-suite-test-case"
|
||||
license = "MIT"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1", features = [ "derive" ] }
|
||||
serde_json = "1"
|
|
@ -0,0 +1,9 @@
|
|||
# json_schema_test_suite_test_case
|
||||
|
||||
This crate is supposed to support [`json_schema_test_suite`](https://crates.io/crates/json_schema_test_suite)
|
||||
by exporting the base struct to allow users of `json_schema_test_suite` to have a single entity representing the test information.
|
||||
|
||||
This crate is needed because currently, at the time of writing, we are not allowed to export structs from a proc-macro library
|
||||
Please refer to [`json-schema-test-suite` docs](https://docs.rs/json-schema-test-suite) for more informaton.
|
||||
|
||||
License: MIT
|
|
@ -0,0 +1,54 @@
|
|||
//! This crate is supposed to support [`json_schema_test_suite`](https://crates.io/crates/json_schema_test_suite)
|
||||
//! by exporting the base struct to allow users of `json_schema_test_suite` to have a single entity representing the test information.
|
||||
//!
|
||||
//! This crate is needed because currently, at the time of writing, we are not allowed to export structs from a proc-macro library
|
||||
//! Please refer to [`json-schema-test-suite` docs](https://docs.rs/json-schema-test-suite) for more informaton.
|
||||
#![warn(
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::doc_markdown,
|
||||
clippy::explicit_iter_loop,
|
||||
clippy::match_same_arms,
|
||||
clippy::needless_borrow,
|
||||
clippy::needless_pass_by_value,
|
||||
clippy::option_map_unwrap_or,
|
||||
clippy::option_map_unwrap_or_else,
|
||||
clippy::option_unwrap_used,
|
||||
clippy::pedantic,
|
||||
clippy::print_stdout,
|
||||
clippy::redundant_closure,
|
||||
clippy::result_map_unwrap_or_else,
|
||||
clippy::result_unwrap_used,
|
||||
clippy::trivially_copy_pass_by_ref,
|
||||
missing_debug_implementations,
|
||||
missing_docs,
|
||||
trivial_casts,
|
||||
unreachable_pub,
|
||||
unsafe_code,
|
||||
unused_extern_crates,
|
||||
unused_import_braces,
|
||||
unused_qualifications,
|
||||
unused_results,
|
||||
variant_size_differences
|
||||
)]
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
/// Detailed information about the individual test case in JSON-Schema-Test-Suite
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TestCase {
|
||||
/// Test name (unique identifier)
|
||||
pub name: String,
|
||||
/// String representation of the draft version (equialent to the test directory)
|
||||
/// This is usefull in case your test needs to be aware of the draft version under test
|
||||
pub draft_version: String,
|
||||
/// Description of the test group as provided by JSON-Schema-Test-Suite
|
||||
pub group_description: String,
|
||||
/// Description of the test group as provided by JSON-Schema-Test-Suite
|
||||
pub test_case_description: String,
|
||||
/// Schema to be tested
|
||||
pub schema: Value,
|
||||
/// Instance to be validated against the `schema
|
||||
pub instance: Value,
|
||||
/// Expected validity status as from JSON-Schema-Test-Suite
|
||||
pub is_valid: bool,
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
use json_schema_test_suite::{json_schema_test_suite, TestCase};
|
||||
|
||||
#[json_schema_test_suite("path/to/JSON-Schema-Test-Suite/repository", "draft7", {"ref_0_0", r"optional_format_regex_0_\d+"})]
|
||||
fn no_op_test_with_some_failures(server_address: &str, test_case: TestCase) {
|
||||
if test_case.name == "ref_0_0" {
|
||||
// Test that failure is properly ignored via the macro argument
|
||||
panic!("The test should fail, but we ensure that this is ignored")
|
||||
} else if test_case.name == "ref_0_1" {
|
||||
// Test that mocks work as expected
|
||||
match reqwest::blocking::get(&format!("http://{}/integer.json", server_address))
|
||||
.and_then(reqwest::blocking::Response::json::<serde_json::Value>)
|
||||
{
|
||||
Ok(json_resp) => assert_eq!(json_resp, serde_json::json!({"type": "integer"})),
|
||||
Err(err) => panic!("Issue while interacting with mocks: {:?}", err),
|
||||
}
|
||||
} else if test_case.name.starts_with("optional_format_regex_0_") {
|
||||
panic!("We want to force a falure for all the test named like 'optional_format_regex_0_.*' to ensure that the test ignore list includes them")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
use json_schema_test_suite::{json_schema_test_suite, TestCase};
|
||||
|
||||
#[json_schema_test_suite("path/to/JSON-Schema-Test-Suite/repository", "draft7")]
|
||||
fn no_op_test(_server_address: &str, _test_case: TestCase) {}
|
|
@ -39,7 +39,7 @@ fraction = { version = "0.8", default-features = false, features = ["with-bigint
|
|||
[dev-dependencies]
|
||||
criterion = ">= 0.1"
|
||||
mockito = ">= 0"
|
||||
json_schema_test_suite = ">= 0.3"
|
||||
json_schema_test_suite = { version = ">= 0.3", path = "../jsonschema-test-suite"}
|
||||
jsonschema-valid = "0.4.0"
|
||||
bench_helpers = { path = "../bench_helpers" }
|
||||
valico = "3.6.0"
|
||||
|
|
Loading…
Reference in New Issue