zoi-cli 1.24.6

Advanced Package Manager & Environment Orchestrator
Documentation
use crate::{cmd, pkg};
use anyhow::{Result, anyhow};
use colored::*;
use mlua::{Lua, LuaSerdeExt};

pub fn run(args: &cmd::package::build::BuildCommand) -> Result<()> {
    println!("Testing package from: {}", args.package_file.display());

    let platform = if let Some(p) = args.platform.first() {
        p.clone()
    } else {
        crate::pkg::utils::get_platform()?
    };

    let pkg_for_meta = pkg::lua::parser::parse_lua_package_for_platform(
        args.package_file.to_str().ok_or_else(|| {
            anyhow!(
                "Path contains invalid UTF-8 characters: {:?}",
                args.package_file
            )
        })?,
        &platform,
        args.version_override.as_deref(),
        None,
        false,
    )?;

    let version = if let Some(v) = &args.version_override {
        v.clone()
    } else {
        pkg::resolve::get_default_version(&pkg_for_meta, None)?
    };

    let resolved_build_type = match crate::pkg::package::build::resolve_build_type(
        args.r#type.as_deref(),
        &pkg_for_meta.types,
        &pkg_for_meta.name,
    )? {
        Some(t) => t,
        None => {
            println!(
                "{} Skipping tests for package '{}': no build types supported.",
                "::".bold().yellow(),
                pkg_for_meta.name
            );
            return Ok(());
        }
    };

    let build_dir = tempfile::Builder::new()
        .prefix(&format!("zoi-test-{}-{}", pkg_for_meta.name, platform))
        .tempdir()?;
    println!("Using build directory: {}", build_dir.path().display());
    let staging_dir = build_dir.path().join("staging");
    std::fs::create_dir_all(&staging_dir)?;

    let subs_to_test = if let Some(subs) = &args.sub {
        subs.clone()
    } else if let Some(subs) = &pkg_for_meta.sub_packages {
        subs.clone()
    } else {
        vec!["".to_string()]
    };

    for sub_package in subs_to_test {
        let sub_pkg_name = if sub_package.is_empty() {
            None
        } else {
            Some(sub_package.as_str())
        };

        if !sub_package.is_empty() {
            println!(
                "{} Testing sub-package: {}",
                "::".bold().blue(),
                sub_package.cyan()
            );
        }

        let lua = Lua::new();
        pkg::lua::functions::setup_lua_environment(
            &lua,
            &platform,
            Some(&version),
            args.package_file.to_str(),
            None,
            Some(build_dir.path().to_str().unwrap_or("")),
            Some(staging_dir.to_str().unwrap_or("")),
            sub_pkg_name,
            Some(pkg_for_meta.scope),
            Some(resolved_build_type.as_str()),
            false,
        )
        .map_err(|e| anyhow!(e.to_string()))?;

        let pkg_table = lua
            .to_value(&pkg_for_meta)
            .map_err(|e| anyhow!(e.to_string()))?;
        lua.globals()
            .set("PKG", pkg_table)
            .map_err(|e| anyhow!(e.to_string()))?;

        let lua_code = std::fs::read_to_string(&args.package_file)?;
        lua.load(&lua_code)
            .exec()
            .map_err(|e| anyhow!(e.to_string()))?;

        let lua_args = lua.create_table().map_err(|e| anyhow!(e.to_string()))?;
        if !sub_package.is_empty() {
            lua_args
                .set("sub", sub_package.clone())
                .map_err(|e| anyhow!(e.to_string()))?;
        }

        if let Ok(prepare_fn) = lua.globals().get::<mlua::Function>("prepare") {
            println!("Running prepare()...");
            prepare_fn
                .call::<()>(lua_args.clone())
                .map_err(|e| anyhow!(e.to_string()))?;
        }

        if let Ok(package_fn) = lua.globals().get::<mlua::Function>("package") {
            println!("Running package()...");
            package_fn
                .call::<()>(lua_args.clone())
                .map_err(|e| anyhow!(e.to_string()))?;
        }

        if let Ok(test_fn) = lua.globals().get::<mlua::Function>("test") {
            println!("Running test()...");
            let success: bool = match test_fn.call::<mlua::Value>(lua_args.clone()) {
                Ok(mlua::Value::Boolean(b)) => b,
                Ok(mlua::Value::Nil) => {
                    return Err(anyhow!(
                        "The 'test' function in '{}' returned nil. It must explicitly return a boolean (true or false).",
                        args.package_file.display()
                    ));
                }
                Ok(v) => {
                    return Err(anyhow!(
                        "The 'test' function in '{}' returned a non-boolean value of type {:?}. It must return true or false.",
                        args.package_file.display(),
                        v.type_name()
                    ));
                }
                Err(e) => return Err(anyhow!(e.to_string())),
            };
            if !success {
                return Err(anyhow!(
                    "Package tests failed for sub-package '{}'.",
                    sub_package
                ));
            }
        } else if !sub_package.is_empty() {
            println!(
                "No test() function found for sub-package '{}', skipping.",
                sub_package
            );
        }
    }

    println!("{}", "All tests passed successfully.".green());
    Ok(())
}