Skip to content

Commit

Permalink
Config wasm no errors (#146)
Browse files Browse the repository at this point in the history
* Added empty and json test

* Added additional logging to debug config parser not erroring

* Added optional parameter to force either legacy or scripting parsing

- If the parameter is not passed it will attempt to parse yaml as both v1 and v2. If legacy is true it only parses v1, false only v2.

* Added tests for the new optional config-wasm parameter to force legacy or scripting parsing

* Updated code to not support empty yaml file

- Changed the functionality to mirror the old code that would fail on an emtpy file
- Removed the default empty endpoints array in the yaml parse
- Added a new error if an empty endpoints section is provided
- Updated tests to support changes
  • Loading branch information
tkmcmaster authored Sep 7, 2023
1 parent 2419eb9 commit 7c8ca09
Show file tree
Hide file tree
Showing 8 changed files with 315 additions and 18 deletions.
60 changes: 57 additions & 3 deletions lib/config-gen/tests/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,63 @@ loggers:

#[wasm_bindgen_test]
fn other_test() {
let json_str = "{\"vars\":{\"rampTime\":\"${e:RAMP_TIME}\",\"loadTime\":\"${e:LOAD_TIME}\",\"peakLoad\":\"${e:PEAK_LOAD}\",\"sessionId\":\"${e:SESSIONID}\"},\"config\":{\"client\":{\"headers\":{\"User-Agent\":\"Pewpew Performance Load Test\"}},\"general\":{\"bucket_size\":\"1m\",\"log_provider_stats\":true}},\"load_pattern\":[{\"linear\":{\"from\":\"10%\",\"to\":\"100%\",\"over\":\"15m\"}},{\"linear\":{\"from\":\"100%\",\"to\":\"100%\",\"over\":\"15m\"}}],\"loggers\":{\"httpErrors\":{\"query\":{\"select\":{\"timestamp\":\"epoch(\\\"ms\\\")\",\"rtt\":\"stats.rtt\",\"request\":\"request[\\\"start-line\\\"]\",\"requestHeaders\":\"request.headers\",\"requestBody\":\"request.body\",\"response\":\"response[\\\"start-line\\\"]\",\"status\":\"response.status\",\"responseHeaders\":\"response.headers\"},\"where\":\"response.status >= 400\"},\"to\":\"stderr\",\"limit\":200},\"testEnd\":{\"query\":{\"select\":{\"timestamp\":\"epoch(\\\"ms\\\")\",\"status\":\"response.status\",\"request\":\"request[\\\"start-line\\\"]\",\"response\":\"response[\\\"start-line\\\"]\"},\"where\":\"response.status >= 500\"},\"to\":\"stderr\",\"limit\":50,\"kill\":true}}}";
let _lt: LoadTest = serde_json::from_str(json_str).unwrap();
let yaml = load_test_yaml_from_js(json_str, None)
let json = json!({
"vars":{
"rampTime":"${e:RAMP_TIME}",
"loadTime":"${e:LOAD_TIME}",
"peakLoad":"${e:PEAK_LOAD}",
"sessionId":"${e:SESSIONID}"},
"config":{
"client":{"headers":{"User-Agent":"Pewpew Performance Load Test"}},
"general":{"bucket_size":"1m","log_provider_stats":true}
},
"load_pattern":[
{"linear":{"from":"10%","to":"100%","over":"15m"}},
{"linear":{"from":"100%","to":"100%","over":"15m"}}
],
"endpoints": [
{
"url": "localhost",
"peak_load": "1.1hps"
}
],
"loggers":{
"httpErrors":{
"query":{
"select":{
"timestamp":"epoch(\"ms\")",
"rtt":"stats.rtt","request":
"request[\"start-line\"]",
"requestHeaders":"request.headers",
"requestBody":"request.body",
"response":"response[\"start-line\"]",
"status":"response.status",
"responseHeaders":"response.headers"
},
"where":"response.status >= 400"
},
"to":"stderr",
"limit":200
},
"testEnd":{
"query":{
"select":{
"timestamp":"epoch(\"ms\")",
"status":"response.status",
"request":"request[\"start-line\"]",
"response":"response[\"start-line\"]"
},
"where":"response.status >= 500"
},
"to":"stderr",
"limit":50,
"kill":true
}
}
});
let json_str = serde_json::to_string(&json).unwrap();
let _lt: LoadTest = serde_json::from_str(&json_str).unwrap();
let yaml = load_test_yaml_from_js(&json_str, None)
.map_err(JsValue::from)
.unwrap();

Expand Down
9 changes: 7 additions & 2 deletions lib/config-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,16 @@ impl Config {
bytes: &[u8],
env_vars: Map,
log_level: Option<String>,
validate_legacy_only: Option<bool>,
) -> Result<Config, JsValue> {
init_logging(log_level);
let env_vars = serde_wasm_bindgen::from_value(env_vars.into())?;
let load_test = LoadTestEither::parse(std::str::from_utf8(bytes).expect("TODO"), &env_vars)
.map_err(|e| JsValue::from_str(&format!("{e:?}")))?;
let load_test = LoadTestEither::parse(
std::str::from_utf8(bytes).expect("TODO"),
&env_vars,
validate_legacy_only,
)
.map_err(|e| JsValue::from_str(&format!("{e:?}")))?;
Ok(Config(load_test))
}

Expand Down
2 changes: 1 addition & 1 deletion lib/config-wasm/test-node.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ set -x
wasm-pack build --release -t nodejs --scope fs
cd tests/
npm install
npm test
npm test "$@"
cd ..
110 changes: 110 additions & 0 deletions lib/config-wasm/tests/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,4 +233,114 @@ const suite = describe("config-wasm", () => {
done(error);
}
});

describe("Bad Yaml Tests", () => {
describe("Legacy Tests", () => {
it("should throw error on empty yaml", (done) => {
try {
config = new Config(Buffer.from("", "utf-8"), varMap, undefined, true);
config.checkOk();
done(new Error("empty.yaml should have failed"));
} catch (error) {
expect(`${error}`).to.include("YamlDeserialize");
done();
}
});

it("should throw error on bad yaml", (done) => {
try {
config = new Config(Buffer.from("var:\nendpoint:\n", "utf-8"), varMap, undefined, true);
config.checkOk();
done(new Error("bad.yaml should have failed"));
} catch (error) {
expect(`${error}`).to.include("UnrecognizedKey");
done();
}
});

it("should throw error on json file", (done) => {
try {
config = new Config(Buffer.from(`{"message":"Test file to upload."}`, "utf-8"), varMap, undefined, true);
config.checkOk();
done(new Error("file.json should have failed"));
} catch (error) {
expect(`${error}`).to.include("UnrecognizedKey");
done();
}
});
});

describe("Scripting Tests", () => {
it("should throw error on empty yaml", (done) => {
try {
config = new Config(Buffer.from("", "utf-8"), varMap, undefined, false);
config.checkOk();
done(new Error("empty.yaml should have failed"));
} catch (error) {
expect(`${error}`).to.include("YamlParse");
done();
}
});

it("should throw error on bad yaml", (done) => {
try {
config = new Config(Buffer.from("var:\nendpoint:\n", "utf-8"), varMap, undefined, false);
config.checkOk();
done(new Error("bad.yaml should have failed"));
} catch (error) {
expect(`${error}`).to.include("YamlParse");
done();
}
});

it("should throw error on json file", (done) => {
try {
config = new Config(Buffer.from(`{"message":"Test file to upload."}`, "utf-8"), varMap, undefined, false);
config.checkOk();
done(new Error("file.json should have failed"));
} catch (error) {
expect(`${error}`).to.include("YamlParse");
done();
}
});
});

describe("Both Legacy and Scripting Tests", () => {
it("should throw error on empty yaml", (done) => {
try {
config = new Config(Buffer.from("", "utf-8"), varMap, undefined, undefined);
config.checkOk();
done(new Error("empty.yaml should have failed"));
} catch (error) {
expect(`${error}`).to.include("YamlDeserialize");
expect(`${error}`).to.include("YamlParse");
done();
}
});

it("should throw error on bad yaml", (done) => {
try {
config = new Config(Buffer.from("var:\nendpoint:\n", "utf-8"), varMap, undefined, undefined);
config.checkOk();
done(new Error("bad.yaml should have failed"));
} catch (error) {
expect(`${error}`).to.include("UnrecognizedKey");
expect(`${error}`).to.include("YamlParse");
done();
}
});

it("should throw error on json file", (done) => {
try {
config = new Config(Buffer.from(`{"message":"Test file to upload."}`, "utf-8"), varMap, undefined, undefined);
config.checkOk();
done(new Error("file.json should have failed"));
} catch (error) {
expect(`${error}`).to.include("UnrecognizedKey");
expect(`${error}`).to.include("YamlParse");
done();
}
});
});
});
});
3 changes: 3 additions & 0 deletions lib/config/src/configv1/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ pub enum Error {
UnknownLogger(String, Marker),
UnrecognizedKey(String, Option<String>, Marker),
YamlDeserialize(Option<String>, Marker),
// Used by the config-wasm when only passing back a V2 error
OtherErr(String),
}

// impl Error {
Expand Down Expand Up @@ -160,6 +162,7 @@ impl fmt::Display for Error {
UnrecognizedKey(k, None, m) => write!(f, "unrecognized key `{}` at line {} column {}", k, m.line(), m.col()),
YamlDeserialize(Some(name), m) => write!(f, "unexpected value for `{}` at line {} column {}", name, m.line(), m.col()),
YamlDeserialize(None, m) => write!(f, "unexpected value for field at line {} column {}", m.line(), m.col()),
OtherErr(s) => write!(f, "Error {}", s),
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions lib/config/src/configv2/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ pub enum LoadTestGenError {
VarsError(#[from] VarsError),
#[error("error loading external js: {0}")]
LibLoad(#[from] Arc<io::Error>),
#[error("endpoints are required")]
NoEndpoints(),
// Used by the config-wasm when only passing back a V1 error
#[error("error {0}")]
OtherErr(String),
}

#[derive(Debug, Error, Clone, PartialEq, Eq)]
Expand Down
73 changes: 68 additions & 5 deletions lib/config/src/configv2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub struct LoadTest<VD: Bool = True, ED: Bool = True> {
pub loggers: BTreeMap<Arc<str>, Logger<VD>>,
#[serde(default = "BTreeMap::new")]
pub providers: BTreeMap<ProviderName, ProviderType<VD>>,
#[serde(default = "Vec::new")]
#[serde()] // Don't have a default here
pub endpoints: Vec<Endpoint<VD>>,
/// Tracks errors that would prevent a full Load Test
#[serde(skip)]
Expand Down Expand Up @@ -166,7 +166,10 @@ impl LoadTest<True, True> {
file_path: Arc<Path>,
env_vars: &BTreeMap<String, String>,
) -> Result<Self, LoadTestGenError> {
use LoadTestGenError::NoEndpoints;
// TODO: Why isn't this causing errors on empty
let mut pre_envs: LoadTest<False, False> = serde_yaml::from_str(yaml)?;
log::debug!("from_yaml pre_envs: {:?}", pre_envs);
// init lib js
scripting::set_source(std::mem::take(&mut pre_envs.lib_src))?;

Expand All @@ -191,6 +194,10 @@ impl LoadTest<True, True> {
let lp = &lt.load_pattern;
let ep = &mut lt.endpoints;
let headers = &lt.config.client.headers;
// Check for no endpoints
if ep.is_empty() {
return Err(NoEndpoints());
}
ep.iter_mut().enumerate().for_each(|(id, endpoint)| {
endpoint.insert_load_pattern(lp.as_ref());
endpoint.insert_special_tags(id);
Expand Down Expand Up @@ -464,7 +471,27 @@ mod tests {
}

#[test]
fn basic() {
fn empty() {
let input = r#"
"#;
let err = LoadTest::from_yaml(input, empty_path(), &BTreeMap::new()).unwrap_err();
assert!(matches!(err, LoadTestGenError::YamlParse(_)));
assert!(format!("{:?}", err).contains("missing field `endpoints`"));
}

#[test]
fn basic_no_endpoints() {
let input = r#"
config:
client: {}
general: {}
providers: {}
loggers: {}
vars: {}
"#;
let err = LoadTest::from_yaml(input, empty_path(), &BTreeMap::new()).unwrap_err();
assert!(matches!(err, LoadTestGenError::YamlParse(_)));
assert!(format!("{:?}", err).contains("missing field `endpoints`"));
let input = r#"
config:
client: {}
Expand All @@ -474,6 +501,28 @@ mod tests {
loggers: {}
vars: {}
"#;
let err = LoadTest::from_yaml(input, empty_path(), &BTreeMap::new()).unwrap_err();
assert!(matches!(err, LoadTestGenError::NoEndpoints()));
}

#[test]
fn basic() {
let input = r#"
config:
client: {}
general: {}
providers: {}
load_pattern:
- !linear
to: 50%
over: 1m
endpoints:
- method: GET
url: localhost:8000
peak_load: 4hps
loggers: {}
vars: {}
"#;
let lt = LoadTest::from_yaml(input, empty_path(), &BTreeMap::new()).unwrap();
lt.ok_for_loadtest().unwrap();
}
Expand Down Expand Up @@ -602,15 +651,21 @@ mod tests {
fn get_test_duration() {
use std::time::Duration;
let input = r#"
endpoints: []
endpoints:
- method: GET
url: localhost:8000
peak_load: 4hps
loggers: {}
vars: {}
load_pattern: []
"#;
let lt = LoadTest::from_yaml(input, empty_path(), &BTreeMap::new()).unwrap();
assert_eq!(lt.get_duration(), Duration::default());
let input = r#"
endpoints: []
endpoints:
- method: GET
url: localhost:8000
peak_load: 4hps
loggers: {}
vars: {}
load_pattern:
Expand All @@ -620,7 +675,7 @@ mod tests {
over: 12h
"#;
let lt = LoadTest::from_yaml(input, empty_path(), &BTreeMap::new()).unwrap();
assert_eq!(lt.get_duration(), Duration::default());
assert_eq!(lt.get_duration(), Duration::from_secs(12 * 60 * 60));
let input = r#"
endpoints:
- url: localhost:8080
Expand Down Expand Up @@ -668,6 +723,10 @@ mod tests {
- !linear
to: '${x:inline2(${v:from_custom})}%'
over: 1s
endpoints:
- method: GET
url: localhost:8000
peak_load: 4hps
"#;
let lt = LoadTest::from_yaml(input, empty_path(), &BTreeMap::new()).unwrap();
let lp0 = lt.load_pattern.unwrap().into_iter().next().unwrap();
Expand All @@ -683,6 +742,10 @@ mod tests {
- !linear
to: '${x:foo_custom(${v:foo})}%'
over: 1s
endpoints:
- method: GET
url: localhost:8000
peak_load: 4hps
"#;
let lt = LoadTest::from_yaml(input, empty_path(), &BTreeMap::new()).unwrap();
let lp0 = lt.load_pattern.unwrap().into_iter().next().unwrap();
Expand Down
Loading

0 comments on commit 7c8ca09

Please sign in to comment.