use crate::bindings::payload::PayloadBinding;
use crate::input::{Format, Input};
use crate::value::IntoValue;
use crate::{Expression, HandlerAttributesBinding, ScriptingEngine, Value};
use classy::hl::{HeadersHandler, HttpClientResponse, PropertyAccessor};
use serde::Deserialize;
use std::collections::HashMap;
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_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_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)
}
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 }
}
}