iocaine 2.2.0

The deadliest poison known to AI
Documentation
// SPDX-FileCopyrightText: 2025 Gergely Nagy
// SPDX-FileContributor: Gergely Nagy
//
// SPDX-License-Identifier: MIT

#[cfg(test)]
mod test {
    use axum::http::HeaderMap;
    use roto::{FileTree, Val, Verdict};
    use std::sync::Arc;

    use crate::means_of_production::{Error, IocaineContext, MeansOfProduction, Outcome, Request};

    fn run_init(script: &str) -> Result<IocaineContext, Error> {
        let runtime = MeansOfProduction::new_runtime()?;
        let mut compiled =
            FileTree::test_file(file!(), script, line!() as usize - 1).compile(runtime)?;

        let init = compiled.get_function::<IocaineContext, fn() -> Verdict<(), Arc<str>>>("init");
        let mut context = IocaineContext::default();

        if let Ok(init) = init {
            init.call(&mut context)
                .into_result()
                .map_err(|e| Error::String(format!("init script failed: {e}")))?;
        }

        Ok(context)
    }

    fn new_handler(script: &str) -> Result<MeansOfProduction, Error> {
        let context = run_init(script)?;
        let runtime = MeansOfProduction::new_runtime()?;

        let mut compiled =
            FileTree::test_file(file!(), script, line!() as usize - 1).compile(runtime)?;
        let decider = compiled
            .get_function::<IocaineContext, fn(Val<Request>) -> Verdict<Val<Outcome>, Val<Outcome>>>(
                "decide",
            )
            .map_err(|e| e.to_string())?;

        Ok(MeansOfProduction { decider, context })
    }

    fn assert_verdict(headers: HeaderMap, script: &str, v: Result<Outcome, Outcome>) {
        let handler = new_handler(script)
            .inspect_err(|e| eprintln!("{e}"))
            .unwrap();
        let res = handler.decide(headers, Some("/".to_string()), "GET");
        assert_eq!(res, v);
    }

    #[test]
    fn test_handler_static_verdict() {
        assert_verdict(
            HeaderMap::new(),
            r#"
              function decide(request: Request) -> Verdict[Outcome, Outcome] {
                accept Outcome.garbage()
              }
            "#,
            Ok(Outcome::Garbage),
        );
        assert_verdict(
            HeaderMap::new(),
            r#"
              function decide(request: Request) -> Verdict[Outcome, Outcome] {
                reject Outcome.not_for_us()
              }
            "#,
            Err(Outcome::NotForUs),
        );
    }

    #[test]
    fn test_handler_failable_init() {
        let result = run_init(r#"function init() -> Verdict[Unit, String] { reject "testing"  }"#);
        assert!(result.is_err());

        let result = run_init(r#"function init() -> Verdict[Unit, String] { accept }"#);
        assert!(result.is_ok());
    }

    #[test]
    fn test_handler_context() {
        let init_script = r#"
          function init() -> Verdict[Unit, String] {
            let list = MutableStringList.new();
            list.push("testing");
            iocaine_patterns.insert("tests", PatternFinder.new(list));
            iocaine_regexes.insert("tests", RegexFinder.new("^testing$"));

            accept
          }
        "#;

        let classify_script = r#"
          function decide(request: Request) -> Verdict[Outcome, Outcome] {
            if iocaine_patterns.get("none").is_match("nope") {
              reject Outcome.not_for_us()
            }
            if iocaine_regexes.get("none").is_match(".") {
              reject Outcome.not_for_us()
            }
            if iocaine_patterns.get("tests").is_match("testing") {
              accept Outcome.garbage()
            }
            if iocaine_regexes.get("tests").is_match("testing") {
              accept Outcome.garbage()
            }

            reject Outcome.not_for_us()
          }
        "#;

        assert_verdict(HeaderMap::new(), classify_script, Err(Outcome::NotForUs));

        assert_verdict(
            HeaderMap::new(),
            &format!("{init_script} {classify_script}"),
            Ok(Outcome::Garbage),
        );
    }

    #[test]
    fn test_handler_request() {
        fn make_script(body: &str) -> String {
            format!(
                r#"
                  function decide(request: Request) -> Verdict[Outcome, Outcome] {{
                    {body}
                  }}
                "#
            )
        }

        let mut headers = HeaderMap::new();
        headers.insert("user-agent", "tester".parse().unwrap());

        assert_verdict(
            headers,
            &make_script(
                r#"
                  if request.method() != "GET" {
                    reject Outcome.not_for_us()
                  }
                  if request.path() != "/" {
                    reject Outcome.not_for_us()
                  }
                  if request.header("user-agent") != "tester" {
                    reject Outcome.not_for_us()
                  }
                  accept Outcome.garbage()
                "#,
            ),
            Ok(Outcome::Garbage),
        );
    }
}