use crate::bindings::payload::PayloadBinding;
use crate::input::{Format, Input};
use crate::value::IntoValue;
use crate::{
EvaluationError, Expression, HandlerAttributesBinding, Script, ScriptingEngine, Value,
};
use classy::hl::{HeadersHandler, HttpClientResponse, PropertyAccessor};
use serde::Deserialize;
use std::collections::HashMap;
use std::vec;
use test_case::test_case;
#[test]
pub fn explicit_constant() {
let config = get_config(r##"P[[":str", "0-3", "foo"], "#['foo']"]"##);
let script = ScriptingEngine::script(&config.exp).compile().unwrap();
let evaluator = script.evaluator();
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "foo".into_value());
}
#[test]
pub fn bind_var() {
let config = get_config(
r##"P[[".", "0-13",[":ref", "0-10", "vars"],[":str", "11-13", "foo"]], "#[vars.foo]"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Vars("foo"))
.compile()
.unwrap();
let mut evaluator = script.evaluator();
evaluator.bind_vars("foo", "Hello");
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "Hello".into_value());
}
#[test]
pub fn bind_two_vars() {
let config = get_config(
r##"P[[".", "0-13",[":ref", "0-10", "vars"],[":str", "11-13", "bar"]], "#[vars.foo]"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Vars("foo"))
.input(Input::Vars("bar"))
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
evaluator.bind_vars("foo", "foo");
}
evaluator.bind_vars("bar", "bar");
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "bar".into_value());
}
#[test]
pub fn missing_bind() {
let config = get_config(r##"P[[":ref", "0-3", "foo"], "#[foo]"]"##);
let script = ScriptingEngine::script(&config.exp).compile();
assert!(script.is_err());
}
#[test]
pub fn bind_text_payload() {
let config = get_config(
r##"P[[":apply", "0-31", [":ref", "20-22", "++"], [":apply", "0-19", [":ref", "9-11", "++"], [".", "0-8", [":ref", "0-4", "vars"], [":str", "5-8", "foo"]], [":ref", "12-19", "payload"]], [".", "23-31", [":ref", "23-27", "vars"], [":str", "28-31", "bar"]]], "#[vars.foo ++ payload ++ vars.bar]"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::PlainText))
.input(Input::Vars("foo"))
.input(Input::Vars("bar"))
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
evaluator.bind_vars("foo", "foo");
}
{
evaluator.bind_payload(&r#"-"#.as_bytes());
}
evaluator.bind_vars("bar", "bar");
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), r#"foo-bar"#.into_value());
}
#[test]
pub fn bind_json_payload() {
let config = get_config(
r##"P[[".", "0-13",[":ref", "0-10", "payload"],[":str", "11-13", "foo"]], "#[payload.foo]"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::Json))
.compile()
.unwrap();
let mut evaluator = script.evaluator();
evaluator.bind_payload(&r#"{"foo": "bar"}"#.as_bytes());
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "bar".into_value());
}
#[test]
pub fn bind_xml_payload_root() {
let config = get_config(r##"P[[":ref", "0-10", "payload"], "#[payload]"]"##);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::Xml))
.compile()
.unwrap();
let mut evaluator = script.evaluator();
evaluator.bind_payload(&r#"<envelope><foo>bar</foo></envelope>"#.as_bytes());
assert!(evaluator.is_ready());
assert_eq!(
evaluator.eval().unwrap(),
"<envelope><foo>bar</foo></envelope>".into_value()
);
}
#[test]
pub fn bind_xml_payload_envelope() {
let config = get_config(
r##"P[[".", "0-13",[":ref", "0-10", "payload"],[":str", "11-13", "envelope"]], "#[payload.envelope]"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::Xml))
.compile()
.unwrap();
let mut evaluator = script.evaluator();
evaluator.bind_payload(&r#"<envelope><foo>bar</foo></envelope>"#.as_bytes());
assert!(evaluator.is_ready());
assert_eq!(
evaluator.eval().unwrap(),
"<?xml version='1.0' encoding='UTF-8'?>\n<foo>\n bar\n</foo>".into_value()
);
}
#[test]
pub fn bind_xml_payload_envelope_first() {
let config = get_config(
r##"P[[".", "0-13",
[".", "0-13",
[":ref", "0-10", "payload"],
[":str", "11-13", "envelope"]
],
[":str", "11-13", "foo"]
], "#[payload.envelope.foo]"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::Xml))
.compile()
.unwrap();
let mut evaluator = script.evaluator();
evaluator.bind_payload(&r#"<envelope><foo><last>bar</last></foo></envelope>"#.as_bytes());
assert!(evaluator.is_ready());
assert_eq!(
evaluator.eval().unwrap(),
"<?xml version='1.0' encoding='UTF-8'?>\n<last>\n bar\n</last>".into_value()
);
}
#[test]
pub fn bind_xml_payload_envelope_last() {
let config = get_config(
r##"P[[".", "0-13",
[".", "0-13",
[":ref", "0-10", "payload"],
[":str", "11-13", "envelope"]
],
[":str", "11-13", "foo"]
], "#[payload.envelope.foo]"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::Xml))
.compile()
.unwrap();
let mut evaluator = script.evaluator();
evaluator.bind_payload(&r#"<envelope><foo>bar</foo></envelope>"#.as_bytes());
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "bar".into_value());
}
#[test]
pub fn bind_xml_payload_leaf() {
let config = get_config(
r##"P[[".", "0-13",[":ref", "0-10", "payload"],[":str", "11-13", "foo"]], "#[payload.foo]"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::Xml))
.compile()
.unwrap();
let mut evaluator = script.evaluator();
evaluator.bind_payload(&r#"<foo>bar</foo>"#.as_bytes());
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "bar".into_value());
}
#[test]
pub fn bind_xml_payload_multi_child_fails() {
let config = get_config(
r##"P[[".", "0-13",[":ref", "0-10", "payload"],[":str", "11-13", "Envelope"]], "#[payload.Envelope]"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::Xml))
.compile()
.unwrap();
let mut evaluator = script.evaluator();
evaluator.bind_payload(&r#"
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://service.soap.mule.org/">
<soapenv:Header><first>some</first></soapenv:Header>
<soapenv:Body>
<ser:uploadAttachment>
<!--Optional:-->
<attachment>cid:879473018216</attachment>
</ser:uploadAttachment>
</soapenv:Body>
</soapenv:Envelope>"#.as_bytes());
assert!(evaluator.is_ready());
match evaluator.eval() {
Err(EvaluationError::TypeMismatch) => {}
_ => panic!("test failed"),
}
}
#[test]
pub fn bind_xml_payload_single_child() {
let config = get_config(
r##"P[[".", "0-13",[":ref", "0-10", "payload"],[":str", "11-13", "wrap"]], "#[payload.wrap]"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::Xml))
.compile()
.unwrap();
let mut evaluator = script.evaluator();
evaluator.bind_payload(&r#"
<wrap>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://service.soap.mule.org/" xmlns:ser2="http://service.soap.mule.org2/">
<soapenv:Header ser:r="some" ser2:r="some"><first>some</first></soapenv:Header>
<soapenv:Body>
<ser:uploadAttachment>
<!--Optional:-->
<attachment>cid:879473018216</attachment>
</ser:uploadAttachment>
</soapenv:Body>
</soapenv:Envelope>
</wrap>"#.as_bytes());
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), r#"<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header xmlns:ser="http://service.soap.mule.org/" ser:r="some" xmlns:ser2="http://service.soap.mule.org2/" ser2:r="some">
<first>
some
</first>
</soapenv:Header>
<soapenv:Body>
<ser:uploadAttachment xmlns:ser="http://service.soap.mule.org/">
<attachment>
cid:879473018216
</attachment>
</ser:uploadAttachment>
</soapenv:Body>
</soapenv:Envelope>"#.into_value());
}
#[test_case(r##"P[["\u003e", "0-41", [".", "0-26", [".", "0-18", [":ref", "0-10", "attributes"], [":str", "11-18", "headers"]], [":str", "19-25", "test"]], [".", "29-41", [":ref", "29-36", "payload"], [":str", "37-41", "test"]]], "#[attributes.headers['test'] \u003e payload.test]"]"##, true; "greater_than")]
#[test_case(r##"P[["==", "0-41", [".", "0-26", [".", "0-18", [":ref", "0-10", "attributes"], [":str", "11-18", "headers"]], [":str", "19-25", "test"]], [".", "29-41", [":ref", "29-36", "payload"], [":str", "37-41", "test"]]], "#[attributes.headers['test'] == payload.test]"]"##, false; "equals")]
#[test_case(r##"P[["\u003c", "0-41", [".", "0-12", [":ref", "0-7", "payload"], [":str", "8-12", "test"]], [".", "15-41", [".", "15-33", [":ref", "15-25", "attributes"], [":str", "26-33", "headers"]], [":str", "34-40", "test"]]], "#[payload.test \u003c attributes.headers['test']]"]"##, true; "less_than")]
pub fn bind_attributes_and_json_payload(pel: &str, result: bool) {
let config = get_config(pel);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::Json))
.input(Input::Attributes)
.compile()
.unwrap();
let header_state = AttributesHelper::new(&[("test", "2")]);
let stream = StreamHelper::default();
let payload = r#"{"test": 1}"#.as_bytes();
let handler = HandlerAttributesBinding::new(&header_state, &stream);
let mut evaluator = script.evaluator();
evaluator.bind_attributes(&handler);
assert!(!evaluator.is_ready());
evaluator.bind_payload(&payload);
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), result.into_value());
let mut evaluator = script.evaluator();
evaluator.bind_payload(&payload);
assert!(!evaluator.is_ready());
evaluator.bind_attributes(&handler);
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), result.into_value());
}
#[test]
pub fn bind_multi_payload() {
let config = get_config(
r##"P[[".", "0-13",[":ref", "0-10", "payload"],[":str", "11-13", "foo"]], "#[payload.foo]"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.input(Input::Payload(Format::Xml))
.input(Input::Payload(Format::Json))
.compile()
.unwrap();
let mut evaluator = script.evaluator();
evaluator.bind_payload(&r#"<foo>bar</foo>"#.as_bytes());
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "bar".into_value());
let mut evaluator = script.evaluator();
evaluator.bind_payload(&r#"{"foo": "bar"}"#.as_bytes());
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "bar".into_value());
let header_state = AttributesHelper::new(&[("content-type", "application/xml")]);
let stream = StreamHelper::default();
let handler = HandlerAttributesBinding::new(&header_state, &stream);
let mut evaluator = script.evaluator();
evaluator.bind_attributes(&handler);
evaluator.bind_payload(&r#"{"foo": "bar"}"#.as_bytes());
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), Value::Null);
}
#[test]
pub fn bind_body_handler() {
let config = get_config(
r##"P[[".", "0-13",[":ref", "0-10", "payload"],[":str", "11-13", "foo"]], "#[payload.foo]"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::Json))
.compile()
.unwrap();
let mut evaluator = script.evaluator();
let body_handler = PayloadHelper::new(r#"{"foo": "bar"}"#.as_bytes());
evaluator.bind_payload(&body_handler);
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "bar".into_value());
}
#[test]
pub fn bind_client_response() {
let config = get_config(
r##"P[[".", "0-13",[":ref", "0-10", "payload"],[":str", "11-13", "foo"]], "#[payload.foo]"]"##,
);
let client_response =
HttpClientResponse::new(HashMap::new(), r#"{"foo": "bar"}"#.as_bytes().to_vec());
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::Json))
.compile()
.unwrap();
let mut evaluator = script.evaluator();
evaluator.bind_payload(&client_response);
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "bar".into_value());
}
#[test]
pub fn bind_attributes() {
let config = get_config(
r##"P[[":apply", "0-48", [":ref", "23-25", "++"], [".", "0-22", [".", "0-18", [":ref", "0-10", "attributes"], [":str", "11-18", "headers"]], [":str", "19-22", "foo"]], [".", "26-48", [".", "26-44", [":ref", "26-36", "attributes"], [":str", "37-44", "headers"]], [":str", "45-48", "bar"]]], "#[attributes.headers.foo ++ attributes.headers.bar]"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let headers = HashMap::from([
("foo".to_string(), "foo".to_string()),
("bar".to_string(), "bar".to_string()),
]);
let client_response = HttpClientResponse::new(headers, vec![]);
evaluator.bind_attributes(&client_response);
}
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "foobar".into_value());
}
#[test]
pub fn bind_request_headers_attributes() {
let config = get_config(
r##"P[[":apply", "0-51", [":ref", "23-25", "++"], [".", "0-22", [".", "0-18", [":ref", "0-10", "attributes"], [":str", "11-18", "headers"]], [":str", "19-22", "foo"]], [".", "26-51", [":ref", "26-36", "attributes"], [":str", "37-51", "remoteAddress"]]], "#[attributes.headers.foo ++ attributes.remoteAddress]"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(&[("foo", "foo")]);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
assert!(evaluator.is_ready());
assert_eq!(
evaluator.eval().unwrap(),
"foo172.18.0.1:60686".into_value()
);
}
#[test]
fn attributes_inexistent() {
let pel = r##"P[[".", "0-22",[":ref", "0-10", "attributes"],[":str", "11-22", "inexistent"]],"#[attributes.inexistent]"]"##;
let config = get_config(pel);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(HEADERS);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), Value::Null);
}
#[test]
fn attributes_headers() {
let pel = r##"P[[".", "0-24",[".", "0-22",[":ref", "0-10", "attributes"],[":str", "11-22", "headers"]],[":str", "23-24", "Content-Type"]], "#[attributes.headers.'Content-Type']"]"##;
let config = get_config(pel);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(HEADERS);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "text/html".into_value());
}
#[test]
fn attributes_headers_inexistent() {
let pel = r##"P[[".", "0-24",[".", "0-22",[":ref", "0-10", "attributes"],[":str", "11-22", "headers"]],[":str", "23-24", "inexistent"]],"#[attributes.headers.inexistent]"]"##;
let config = get_config(pel);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(HEADERS);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), Value::Null);
}
#[test]
fn attributes_method() {
let pel = r##"P[[".", "0-24",[":ref", "0-10", "attributes"],[":str", "11-22", "method"]],"#[attributes.method]"]"##;
let config = get_config(pel);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(HEADERS);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "GET".into_value());
}
#[test]
fn attributes_query_param() {
let pel = r##"P[[".", "0-24",[".", "0-22",[":ref", "0-10", "attributes"],[":str", "11-22", "queryParams"]],[":str", "23-24", "foo"]],"#[attributes.queryParams.foo]"]"##;
let config = get_config(pel);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(HEADERS);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "bar".into_value());
}
#[test]
fn attributes_query_param_inexistent() {
let pel = r##"P[[".", "0-24",[".", "0-22",[":ref", "0-10", "attributes"],[":str", "11-22", "queryParams"]],[":str", "23-24", "inexistent"]],"#[attributes.queryParams.inexistent]"]"##;
let config = get_config(pel);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(HEADERS);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), Value::Null);
}
#[test]
fn attributes_query_string() {
let pel = r##"P[[".", "0-22",[":ref", "0-10", "attributes"],[":str", "12-22", "queryString"]],"#[attributes.queryString]"]"##;
let config = get_config(pel);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(HEADERS);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "baz=bal&foo=bar".into_value());
}
#[test]
fn attributes_request_path() {
let pel = r##"P[[".", "0-22",[":ref", "0-10", "attributes"],[":str", "11-22", "requestPath"]],"#[attributes.requestPath]"]"##;
let config = get_config(pel);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(HEADERS);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "/something".into_value());
}
#[test]
fn attributes_request_uri() {
let pel = r##"P[[".", "0-22",[":ref", "0-10", "attributes"],[":str", "11-22", "requestUri"]],"#[attributes.requestUri]"]"##;
let config = get_config(pel);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(HEADERS);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
assert!(evaluator.is_ready());
assert_eq!(
evaluator.eval().unwrap(),
"/something?baz=bal&foo=bar".into_value()
);
}
#[test]
fn attributes_remote_address() {
let pel = r##"P[[".", "0-22",[":ref", "0-10", "attributes"],[":str", "11-22", "remoteAddress"]],"#[attributes.remoteAddress]"]"##;
let config = get_config(pel);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(HEADERS);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "172.18.0.1:60686".into_value());
}
#[test]
fn attributes_local_address() {
let pel = r##"P[[".", "0-22",[":ref", "0-10", "attributes"],[":str", "11-22", "localAddress"]],"#[attributes.remoteAddress]"]"##;
let config = get_config(pel);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(HEADERS);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "172.25.0.7:7890".into_value());
}
#[test]
fn attributes_query() {
let pel = r##"P[[".", "0-22",[":ref", "0-10", "attributes"],[":str", "11-22", "queryString"]],"#[attributes.remoteAddress]"]"##;
let config = get_config(pel);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(HEADERS);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "baz=bal&foo=bar".into_value());
}
#[test]
fn attributes_scheme() {
let pel = r##"P[[".", "0-22",[":ref", "0-10", "attributes"],[":str", "11-22", "scheme"]],"#[attributes.remoteAddress]"]"##;
let config = get_config(pel);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(HEADERS);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "http".into_value());
}
#[test]
fn attributes_version() {
let pel = r##"P[[".", "0-22",[":ref", "0-10", "attributes"],[":str", "11-22", "version"]],"#[attributes.version]"]"##;
let config = get_config(pel);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(HEADERS);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), "HTTP/1.1".into_value());
}
#[test]
fn attributes_status_code() {
let pel = r##"P[[".", "0-22",[":ref", "0-10", "attributes"],[":str", "11-22", "statusCode"]],"#[attributes.statusCode]"]"##;
let config = get_config(pel);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(HEADERS);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), 207_f64.into_value());
}
#[test]
fn parse_empty_string() {
let config = get_config("");
let value = ScriptingEngine::script(&config.exp)
.compile()
.unwrap()
.evaluator()
.eval()
.unwrap();
assert_eq!(value, Value::Null)
}
#[test]
pub fn append_to_json_payload() {
let config = get_config(
r##"P[[":apply", "0-0",[":ref", "0-0", "++"],[":ref", "0-10", "payload"],[":str", "0-0", "foo"]], "#[payload ++ 'foo']"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::Json))
.compile()
.unwrap();
#[cfg(feature = "experimental_coerced_type")]
{
assert_evaluation(&script, r#"{"foo": "bar"}"#, r#"{"foo": "bar"}foo"#);
assert_evaluation(&script, r#"["foo", "bar"]"#, r#"["foo", "bar"]foo"#);
}
assert_evaluation(&script, r#"1.2"#, r#"1.2foo"#);
assert_evaluation(&script, r#"true"#, r#"truefoo"#);
assert_evaluation(&script, "\"some\"", "somefoo");
}
#[test]
pub fn append_to_json_payload_second_level() {
let config = get_config(
r##"P[[":apply", "0-0",
[":ref", "0-0", "++"],
[".", "0-0", [":ref", "0-10", "payload"], [":nbr", "0-10", "0"]],
[":str", "0-0", "foo"]],
"#[payload ++ 'foo']"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::Json))
.compile()
.unwrap();
assert_evaluation(&script, r#"["foo", "bar"]"#, "foofoo");
assert_error(&script, r#"{"foo": {"bar": "bar"}"#);
}
#[test]
pub fn append_to_text_payload() {
let config = get_config(
r##"P[[":apply", "0-0",[":ref", "0-0", "++"],[":ref", "0-10", "payload"],[":str", "0-0", "foo"]], "#[payload ++ 'foo']"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::PlainText))
.compile()
.unwrap();
assert_evaluation(&script, r#"{"foo": "bar"}"#, r#"{"foo": "bar"}foo"#);
assert_evaluation(&script, r#"["foo", "bar"]"#, r#"["foo", "bar"]foo"#);
assert_evaluation(&script, r#"1.2"#, r#"1.2foo"#);
assert_evaluation(&script, r#"true"#, r#"truefoo"#);
assert_evaluation(&script, "\"some\"", "\"some\"foo");
}
#[test]
pub fn append_to_text_payload_second_level() {
let config = get_config(
r##"P[[":apply", "0-0",
[":ref", "0-0", "++"],
[".", "0-0", [":ref", "0-10", "payload"], [":nbr", "0-10", "0"]],
[":str", "0-0", "foo"]],
"#[payload ++ 'foo']"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::PlainText))
.compile()
.unwrap();
assert_evaluation(&script, r#"["foo", "bar"]"#, "[foo");
assert_evaluation(&script, r#"{"foo": {"bar": "bar"}"#, "{foo");
}
#[cfg(feature = "experimental_coerced_type")]
#[test]
pub fn append_to_xml_payload() {
let config = get_config(
r##"P[[":apply", "0-0",[":ref", "0-0", "++"],[":ref", "0-10", "payload"],[":str", "0-0", "foo"]], "#[payload ++ 'foo']"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::Xml))
.compile()
.unwrap();
assert_evaluation(&script, r#"<some/>"#, r#"<some/>foo"#);
assert_evaluation(&script, r#"<some>some</some>"#, r#"<some>some</some>foo"#);
}
#[test]
pub fn append_to_xml_payload_second_level() {
let config = get_config(
r##"P[[":apply", "0-0",
[":ref", "0-0", "++"],
[".", "0-0", [":ref", "0-10", "payload"], [":str", "0-10", "some"]],
[":str", "0-0", "foo"]],
"#[payload ++ 'foo']"]"##,
);
let script = ScriptingEngine::script(&config.exp)
.input(Input::Payload(Format::Xml))
.compile()
.unwrap();
assert_error(&script, r#"<some/>"#); assert_evaluation(&script, r#"<some>some</some>"#, r#"somefoo"#);
assert_error(&script, r#"<some><other>some</other></some>"#);
}
pub fn subtract(first: Value, second: Value) -> Result<Value, EvaluationError> {
let config = get_config(pdk_unit::dw2pel("vars.first -- vars.second").as_str());
let script = ScriptingEngine::script(&config.exp)
.input(Input::Vars("first"))
.input(Input::Vars("second"))
.compile()
.unwrap();
let mut evaluator = script.evaluator();
evaluator.bind_vars("first", first);
evaluator.bind_vars("second", second);
assert!(evaluator.is_ready());
evaluator.eval()
}
#[test]
pub fn attributes_headers_subtract_array() {
let config = get_config(pdk_unit::dw2pel("attributes.headers -- [\"client_id\"]").as_str());
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(&[
("Authorization", "Bearer token123"),
("client_id", "secret"),
("Content-Type", "application/json"),
]);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
assert!(evaluator.is_ready());
let mut expected = HashMap::new();
expected.insert(
"Authorization".to_string(),
Value::String("Bearer token123".into()),
);
expected.insert(
"Content-Type".to_string(),
Value::String("application/json".into()),
);
assert_eq!(evaluator.eval().unwrap(), Value::Object(expected));
}
#[test]
pub fn attributes_headers_subtract_object() {
let config = get_config(pdk_unit::dw2pel("attributes.headers -- vars.remove").as_str());
let script = ScriptingEngine::script(&config.exp)
.input(Input::Attributes)
.input(Input::Vars("remove"))
.compile()
.unwrap();
let mut evaluator = script.evaluator();
{
let header_state = AttributesHelper::new(&[
("Authorization", "Bearer token123"),
("client_id", "secret"),
("Content-Type", "application/json"),
]);
let stream = StreamHelper::default();
evaluator.bind_attributes(&HandlerAttributesBinding::new(&header_state, &stream));
}
let mut remove = HashMap::new();
remove.insert("client_id".to_string(), Value::String("secret".into()));
remove.insert(
"Authorization".to_string(),
Value::String("wrong_value".into()),
);
evaluator.bind_vars("remove", Value::Object(remove));
assert!(evaluator.is_ready());
let mut expected = HashMap::new();
expected.insert(
"Authorization".to_string(),
Value::String("Bearer token123".into()),
);
expected.insert(
"Content-Type".to_string(),
Value::String("application/json".into()),
);
assert_eq!(evaluator.eval().unwrap(), Value::Object(expected));
}
#[test]
pub fn subtract_array_array() {
let array = Value::Array(vec![Value::String("1".into())]);
let mut map = HashMap::new();
map.insert("key".to_string(), Value::String("1".into()));
let obj = Value::Object(map);
let first = Value::Array(vec![
Value::String("1".into()),
array.clone(),
obj.clone(),
Value::String("other".into()),
]);
let second = Value::Array(vec![array, obj, Value::String("1".into())]);
let result = subtract(first, second);
assert_eq!(
result.unwrap(),
Value::Array(vec![Value::String("other".into())])
);
}
#[test]
pub fn subtract_array_array_casted() {
let array = Value::Array(vec![Value::String("1".into())]);
let mut map = HashMap::new();
map.insert("key".to_string(), Value::String("1".into()));
let obj = Value::Object(map);
let first = Value::Array(vec![
Value::String("1".into()),
Value::String("true".into()),
array,
obj,
Value::String("other".into()),
]);
let array = Value::Array(vec![Value::Number(1f64)]);
let mut map = HashMap::new();
map.insert("key".to_string(), Value::Number(1f64));
let obj = Value::Object(map);
let second = Value::Array(vec![array, obj, Value::Number(1f64), Value::Bool(true)]);
let result = subtract(first, second);
assert_eq!(
result.unwrap(),
Value::Array(vec![Value::String("other".into())])
);
}
#[test]
pub fn subtract_array_array_casted_reverse() {
let array = Value::Array(vec![Value::Number(1f64)]);
let mut map = HashMap::new();
map.insert("key".to_string(), Value::Number(1f64));
let obj = Value::Object(map);
let first = Value::Array(vec![
array,
obj,
Value::Number(1f64),
Value::Bool(true),
Value::String("other".into()),
]);
let array = Value::Array(vec![Value::String("1".into())]);
let mut map = HashMap::new();
map.insert("key".to_string(), Value::String("1".into()));
let obj = Value::Object(map);
let second = Value::Array(vec![
Value::String("1".into()),
Value::String("true".into()),
array,
obj,
]);
let result = subtract(first, second);
assert_eq!(
result.unwrap(),
Value::Array(vec![Value::String("other".into())])
);
}
#[test]
pub fn subtract_mismatch() {
assert_mismatch(subtract(Value::Array(vec![]), Value::Bool(true)));
assert_mismatch(subtract(
Value::Array(vec![]),
Value::Object(HashMap::new()),
));
assert_mismatch(subtract(Value::Object(HashMap::new()), Value::Bool(true)));
assert_mismatch(subtract(
Value::Object(HashMap::new()),
Value::Array(vec![Value::Array(vec![])]),
));
assert_mismatch(subtract(Value::String("true".into()), Value::Bool(true)));
assert_mismatch(subtract(Value::Number(1f64), Value::Bool(true)));
assert_mismatch(subtract(Value::Bool(true), Value::Bool(true)));
}
#[test]
pub fn subtract_object_array() {
let mut map = HashMap::new();
map.insert("first".to_string(), Value::String("value".into()));
map.insert("second".to_string(), Value::String("value".into()));
map.insert("1".to_string(), Value::String("1".into()));
let first = Value::Object(map);
let second = Value::Array(vec![
Value::String("first".into()),
Value::Number(1f64),
Value::String("extra".into()),
]);
let result = subtract(first, second);
let mut map = HashMap::new();
map.insert("second".to_string(), Value::String("value".into()));
assert_eq!(result.unwrap(), Value::Object(map));
}
#[test]
pub fn subtract_object_object() {
let mut inner = HashMap::new();
inner.insert("key".to_string(), Value::String("1".into()));
let mut map = HashMap::new();
map.insert("first".to_string(), Value::String("first".into()));
map.insert("second".to_string(), Value::Bool(true));
map.insert(
"third".to_string(),
Value::Array(vec![Value::String("1".into())]),
);
map.insert(
"forth".to_string(),
Value::Array(vec![Value::String("1".into()), Value::String("1".into())]),
);
map.insert("fifth".to_string(), Value::Object(inner.clone()));
map.insert("sixth".to_string(), Value::Object(inner));
let first = Value::Object(map);
let mut inner = HashMap::new();
inner.insert("key".to_string(), Value::Number(1f64));
let mut map = HashMap::new();
map.insert("first".to_string(), Value::String("first".into()));
map.insert("second".to_string(), Value::String("true".into()));
map.insert("third".to_string(), Value::Array(vec![Value::Number(1f64)]));
map.insert(
"forth".to_string(),
Value::Array(vec![Value::String("1".into())]),
);
map.insert("fifth".to_string(), Value::Object(inner));
map.insert("sixth".to_string(), Value::Object(HashMap::new()));
let second = Value::Object(map);
let result = subtract(first, second);
let mut inner = HashMap::new();
inner.insert("key".to_string(), Value::String("1".into()));
let mut map = HashMap::new();
map.insert(
"forth".to_string(),
Value::Array(vec![Value::String("1".into()), Value::String("1".into())]),
);
map.insert("sixth".to_string(), Value::Object(inner));
assert_eq!(result.unwrap(), Value::Object(map));
}
fn assert_mismatch(result: Result<Value, EvaluationError>) {
assert!(result.unwrap_err().to_string().contains("Type mismatch"));
}
fn assert_evaluation(script: &Script, input: &str, output: &str) {
let mut evaluator = script.evaluator();
let body_handler = PayloadHelper::new(input.as_bytes());
evaluator.bind_payload(&body_handler);
assert!(evaluator.is_ready());
assert_eq!(evaluator.eval().unwrap(), output.into_value());
}
fn assert_error(script: &Script, input: &str) {
let mut evaluator = script.evaluator();
let body_handler = PayloadHelper::new(input.as_bytes());
evaluator.bind_payload(&body_handler);
assert!(evaluator.is_ready());
let err = evaluator.eval().err().unwrap();
assert!(matches!(err, EvaluationError::Error(_)));
}
static HEADERS: &[(&str, &str)] = &[
(":method", "GET"),
(":path", "/something?baz=bal&foo=bar"),
("Content-Length", "1024"),
("Content-Type", "application/json"),
("Content-Type", "text/html"),
(":status", "207"),
];
#[derive(Deserialize)]
struct Config {
exp: Expression,
}
fn get_config(pel: &str) -> Config {
let pel = serde_json::json!({ "exp": pel });
serde_json::from_str(pel.to_string().as_str()).unwrap()
}
#[derive(Default)]
struct StreamHelper {}
impl PropertyAccessor for StreamHelper {
fn read_property(&self, path: &[&str]) -> Option<Vec<u8>> {
match path {
["destination", "address"] => Some("172.25.0.7:7890".as_bytes().to_vec()),
["request", "query"] => Some("baz=bal&foo=bar".as_bytes().to_vec()),
["request", "scheme"] => Some("http".as_bytes().to_vec()),
["request", "protocol"] => Some("HTTP/1.1".as_bytes().to_vec()),
["source", "address"] => Some("172.18.0.1:60686".as_bytes().to_vec()),
_ => None,
}
}
fn set_property(&self, _path: &[&str], _value: Option<&[u8]>) {}
}
struct PayloadHelper<'a> {
bytes: &'a [u8],
}
impl<'a> PayloadHelper<'a> {
pub fn new(bytes: &'a [u8]) -> Self {
Self { bytes }
}
}
impl PayloadBinding for PayloadHelper<'_> {
fn as_bytes(&self) -> Vec<u8> {
self.bytes.to_vec()
}
}
#[derive(Default)]
struct AttributesHelper {
headers: HashMap<String, String>,
}
impl HeadersHandler for AttributesHelper {
fn headers(&self) -> Vec<(String, String)> {
self.headers
.iter()
.map(|(key, value)| (key.clone(), value.clone()))
.collect()
}
fn header(&self, name: &str) -> Option<String> {
self.headers.get(name).cloned()
}
fn add_header(&self, _name: &str, _value: &str) {
panic!()
}
fn set_header(&self, _name: &str, _value: &str) {
panic!()
}
fn set_headers(&self, _headers: Vec<(&str, &str)>) {
panic!()
}
fn remove_header(&self, _name: &str) {
panic!()
}
}
impl AttributesHelper {
pub fn new(input: &[(&str, &str)]) -> Self {
let mut headers = HashMap::new();
for (key, value) in input {
headers.insert(key.to_string(), value.to_string());
}
Self { headers }
}
}