Skip to content

Commit

Permalink
Allows missing response schemas and matching to only certain paths. P…
Browse files Browse the repository at this point in the history
…arallel execution and using rustls instead of openssl
  • Loading branch information
JordiPolo committed Feb 8, 2020
1 parent b746eeb commit 5a610fe
Show file tree
Hide file tree
Showing 9 changed files with 332 additions and 277 deletions.
533 changes: 276 additions & 257 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
serde_yaml = "*"
reqwest = { version = "0.10", features = ["blocking"] }
reqwest = { version = "0.10", default-features = false, features = ["blocking", "json", "rustls-tls"] }
structopt = "0.3"
termcolor = "1.0"
uuid = { version = ">= 0.6", features = ["v4"] }
Expand All @@ -40,6 +40,7 @@ shellexpand = "1.0"
itertools = "0.8"
comfy-table = "0.0.6"
lazy_static = "*"
rayon = "1.1"

[patch.crates-io]
openapiv3 = { git = "https://github.com/glademiller/openapiv3", branch = "master" }
Expand Down
15 changes: 15 additions & 0 deletions src/cli_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@ pub struct CLIArgs {
)]
pub dry_run: bool,

#[structopt(
short = "amrs",
long = "allow-missing-rs",
help = "Do not fail the test if the response body do not have a schema defining it. Useful if the API does not document the application error responses."
)]
pub allow_missing_rs: bool,

#[structopt(
short = "m",
long = "matches",
help = "Only run on paths matching certain paths.",
default_value = "/"
)]
pub matches: String,

#[structopt(
short = "s",
long = "server",
Expand Down
25 changes: 19 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ mod validator;

use log::debug;
use openapi_utils::{ReferenceOrExt, ServerExt, SpecExt};
use rayon::prelude::*;
use std::time::Instant;


use crate::service::Service;
fn main() {
env_logger::init();
Expand All @@ -28,13 +30,20 @@ fn main() {
let mutator = mutation::Mutator::new(&config.conv_filename);
let mut results = Vec::new();


// Create endpoints from the spec file.
let endpoints: Vec<operation::Endpoint> = spec.paths.iter().flat_map(|(path_name, methods)| {
operation::Endpoint::create_supported_endpoint(path_name, methods.to_item_ref())
}).collect();
let endpoints: Vec<operation::Endpoint> = spec
.paths
.iter()
.filter(|p| p.0.contains(&config.matches))
.flat_map(|(path_name, methods)| {
operation::Endpoint::create_supported_endpoint(path_name, methods.to_item_ref())
})
.collect();

let scenarios = endpoints.iter().flat_map(|e| mutator.mutate(e));
let scenarios: Vec<_> = endpoints
.par_iter()
.flat_map(|e| mutator.mutate(e))
.collect();

let start = Instant::now();
for scenario in scenarios {
Expand All @@ -55,7 +64,11 @@ fn main() {
results.push((path, false));
}
Ok(real_response) => {
match validator::validate(real_response, scenario.expectation()) {
match validator::validate(
real_response,
scenario.expectation(),
config.allow_missing_rs,
) {
Err(error) => {
debug!("{:?}", scenario.endpoint);
reporter::test_failed(error);
Expand Down
19 changes: 11 additions & 8 deletions src/mutation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use crate::service::Request;
use http::StatusCode;
use instructions::{Mutagen, MutagenInstruction, RequestPart};
use itertools::Itertools;
use lazy_static::lazy_static;
use log::debug;
use openapi_utils::{OperationExt, ParameterExt, ReferenceOrExt};
use std::cmp::Ordering;
use lazy_static::lazy_static;

mod bool_type;
pub mod instructions;
Expand Down Expand Up @@ -115,7 +115,6 @@ impl Mutator {
mutations: &[Mutation],
query_mutations: &[Mutation],
) -> Vec<Scenario<'a>> {

let mut scenarios = vec![];
let mut query_params: Vec<Vec<&Mutation>> = Vec::new();
let mut non_query_params: Vec<Vec<&Mutation>> = Vec::new();
Expand Down Expand Up @@ -155,17 +154,17 @@ impl Mutator {
// return scenarios;
// }


// We do not do combinations anymore
// We do not do combinations anymore
let mut combinations = Vec::new();

let mut params = non_query_params;
params.append(&mut query_params);

for i in 0..params.len()-1 {
for j in 1..params[i].len() { //Start from 1 becase we will be choosing the element 0 in inner loop
for i in 0..params.len() - 1 {
for j in 1..params[i].len() {
//Start from 1 becase we will be choosing the element 0 in inner loop
let mut temp = Vec::new();
for z in 0..params.len()-1 {
for z in 0..params.len() - 1 {
if i == z {
continue;
}
Expand Down Expand Up @@ -414,7 +413,11 @@ impl Mutator {
Mutagen::PathRandom => {
if path.contains('}') {
//let re = regex::Regex::new(r"\{.*?\}").unwrap();
Some(VARIABLE_FINDER.replace_all(path, "wrongPathItemHere").to_string())
Some(
VARIABLE_FINDER
.replace_all(path, "wrongPathItemHere")
.to_string(),
)
} else {
None // We can't make random something that's is not there
}
Expand Down
2 changes: 1 addition & 1 deletion src/mutation/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl fmt::Display for RequestPart {
match *self {
RequestPart::Path => f.write_str("The path"),
RequestPart::AnyParam => f.write_str("Query parameter"),
RequestPart::OptionalParam => f.write_str( "Optional query parameter"),
RequestPart::OptionalParam => f.write_str("Optional query parameter"),
RequestPart::RequiredParam => f.write_str("Required query parameter"),
RequestPart::Endpoint => f.write_str("The endpoint"),
RequestPart::Method => f.write_str("The HTTP method"),
Expand Down
1 change: 0 additions & 1 deletion src/mutation/string_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ pub fn mutate(param: &openapiv3::Parameter, string_type: &openapiv3::StringType)
}
mutations.push("", Mutagen::NotEnumerationElement);
mutations.push("NotInAnyEnum", Mutagen::NotEnumerationElement);

} else if string_type.format == openapiv3::VariantOrUnknownOrEmpty::Empty {
if let Some(min) = string_type.min_length {
if min > 1 {
Expand Down
6 changes: 3 additions & 3 deletions src/reporter.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::fmt::Display;
use std::io::Write;
use termcolor::{Color, ColorChoice, ColorSpec, BufferedStandardStream, WriteColor};
use termcolor::{BufferedStandardStream, Color, ColorChoice, ColorSpec, WriteColor};

use crate::error::Disparity;
use crate::scenario::Scenario;
Expand Down Expand Up @@ -62,7 +62,7 @@ pub fn print_mutation_scenario(scenario: &Scenario) {
use itertools::Itertools;

pub fn run_summary(results: &[(String, bool)], start: std::time::Instant) {
let failed = results.iter().filter(|&x| !x.1 ).count();
let failed = results.iter().filter(|&x| !x.1).count();
let by_path = results.iter().group_by(|x| &x.0);

let mut table = Table::new();
Expand All @@ -71,7 +71,7 @@ pub fn run_summary(results: &[(String, bool)], start: std::time::Instant) {
for (path, results) in &by_path {
// let total = &results.into_iter().len();
let (pfailed, ppassed): (Vec<&(String, bool)>, Vec<&(String, bool)>) =
results.partition(|&x| !x.1 );
results.partition(|&x| !x.1);
let the_size = pfailed.len() + ppassed.len();

table.add_row(vec![
Expand Down
5 changes: 5 additions & 0 deletions src/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::service;
pub fn validate(
response: service::ServiceResponse,
expectation: crate::scenario::ScenarioExpectation,
allow_missing_rs: bool,
) -> Result<(), Disparity> {
if response.status != expectation.status_code {
return Err(error::status_disparity(
Expand All @@ -35,6 +36,10 @@ pub fn validate(

let response_body = response.body.map_err(|_| Disparity::JsonError)?;

if allow_missing_rs {
return Ok(());
}

let schema_body = extract_schema(expectation.status_code, expectation.body)?;

validate_schema(&response_body, schema_body)
Expand Down

0 comments on commit 5a610fe

Please sign in to comment.