use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
use rhai::Engine;
use tempfile::TempDir;
use tera::Context;
use rhai_factory::RhaiFactory;
use tera_factory::TeraFactory;
// Helper function to create a temporary directory with test files
fn setup_test_environment() -> (TempDir, PathBuf, PathBuf) {
// Create a temporary directory for our test files
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let temp_path = temp_dir.path();
// Create a templates directory
let templates_dir = temp_path.join("templates");
std::fs::create_dir(&templates_dir).expect("Failed to create templates directory");
// Create a scripts directory
let scripts_dir = temp_path.join("scripts");
std::fs::create_dir(&scripts_dir).expect("Failed to create scripts directory");
// Create a simple Rhai script with test functions
let script_path = scripts_dir.join("test_functions.rhai");
let mut script_file = File::create(&script_path).expect("Failed to create script file");
script_file.write_all(b"
// Test function to add two numbers
fn add(a, b) {
a + b
}
// Test function to format a string
fn format_greeting(name) {
`Hello, ${name}!`
}
").expect("Failed to write to script file");
// Create a simple template file with .html.tera extension
let template_path = templates_dir.join("test.html.tera");
let mut template_file = File::create(&template_path).expect("Failed to create template file");
template_file.write_all(b"
{{ format_greeting(name=name) }}
The sum of {{ a }} and {{ b }} is: {{ add(a=a, b=b) }}
").expect("Failed to write to template file");
println!("Test environment set up with:");
println!(" Templates directory: {:?}", templates_dir);
println!(" Scripts directory: {:?}", scripts_dir);
(temp_dir, templates_dir, scripts_dir)
}
#[test]
fn test_create_tera_engine() {
println!("Running test_create_tera_engine");
// Set up the test environment
let (temp_dir, templates_dir, _) = setup_test_environment();
// Create a TeraFactory
let factory = TeraFactory::new();
// Create a Tera engine
let tera = factory.create_tera_engine(&[&templates_dir]).expect("Failed to create Tera engine");
// Verify that the template was loaded
assert!(tera.get_template_names().any(|name| name == "test.html.tera"),
"Template 'test.html.tera' was not loaded");
println!("test_create_tera_engine passed");
// Keep temp_dir in scope until the end of the test
drop(temp_dir);
}
#[test]
fn test_create_tera_with_rhai() {
println!("Running test_create_tera_with_rhai");
// Set up the test environment
let (temp_dir, templates_dir, scripts_dir) = setup_test_environment();
// Create factories
let rhai_factory = RhaiFactory::with_caching();
let tera_factory = TeraFactory::new();
// Compile the Rhai script
let script_path = scripts_dir.join("test_functions.rhai");
println!("Compiling script: {:?}", script_path);
let ast = rhai_factory.compile_modules(&[&script_path], None)
.expect("Failed to compile Rhai script");
// Verify that the functions were compiled
let _engine = Engine::new();
let fn_names: Vec = ast.iter_functions().map(|f| f.name.to_string()).collect();
println!("Compiled functions: {:?}", fn_names);
assert!(fn_names.contains(&"add".to_string()), "Function 'add' was not compiled");
assert!(fn_names.contains(&"format_greeting".to_string()), "Function 'format_greeting' was not compiled");
// Create a hot reloadable AST
let hot_ast = Arc::new(RwLock::new(ast));
// Create a Tera engine with Rhai integration
println!("Creating Tera engine with Rhai integration");
let tera = tera_factory.create_tera_with_rhai(&[&templates_dir], hot_ast.clone())
.expect("Failed to create Tera engine with Rhai");
// Render the template
let mut context = Context::new();
context.insert("name", "World");
context.insert("a", &10);
context.insert("b", &5);
println!("Rendering template with context: {:?}", context);
let rendered = tera.render("test.html.tera", &context).expect("Failed to render template");
// Verify the rendered output
println!("Rendered output: {}", rendered);
assert!(rendered.contains("Hello, World!"), "Rendered output does not contain greeting");
assert!(rendered.contains("The sum of 10 and 5 is: 15"), "Rendered output does not contain correct sum");
println!("test_create_tera_with_rhai passed");
// Keep temp_dir in scope until the end of the test
drop(temp_dir);
}
#[test]
fn test_hot_reload() {
println!("Running test_hot_reload");
// Set up the test environment
let (temp_dir, templates_dir, scripts_dir) = setup_test_environment();
// Create factories
let rhai_factory = RhaiFactory::with_hot_reload().expect("Failed to create RhaiFactory with hot reload");
let tera_factory = TeraFactory::new();
// Compile the Rhai script
let script_path = scripts_dir.join("test_functions.rhai");
println!("Compiling script: {:?}", script_path);
let ast = rhai_factory.compile_modules(&[&script_path], None)
.expect("Failed to compile Rhai script");
// Create a hot reloadable AST
let hot_ast = Arc::new(RwLock::new(ast));
// Enable hot reloading
println!("Enabling hot reloading");
let _handle = rhai_factory.enable_hot_reload(
hot_ast.clone(),
&[&script_path],
None,
Some(Box::new(|| println!("Script reloaded!")))
).expect("Failed to enable hot reloading");
// Create a Tera engine with Rhai integration
println!("Creating Tera engine with Rhai integration");
let tera = tera_factory.create_tera_with_rhai(&[&templates_dir], hot_ast.clone())
.expect("Failed to create Tera engine with Rhai");
// Render the template before modification
let mut context = Context::new();
context.insert("name", "World");
context.insert("a", &10);
context.insert("b", &5);
println!("Rendering template before script modification");
let rendered_before = tera.render("test.html.tera", &context).expect("Failed to render template");
// Verify the rendered output before modification
println!("Rendered output before: {}", rendered_before);
assert!(rendered_before.contains("Hello, World!"), "Rendered output does not contain greeting");
assert!(rendered_before.contains("The sum of 10 and 5 is: 15"), "Rendered output does not contain correct sum");
// Modify the script file
println!("Modifying script file");
let mut script_file = File::create(&script_path).expect("Failed to create script file");
script_file.write_all(b"
// Modified test function to add two numbers
fn add(a, b) {
a + b + 1 // Changed to add 1 to the result
}
// Modified test function to format a string
fn format_greeting(name) {
`Greetings, ${name}!` // Changed greeting format
}
").expect("Failed to write to script file");
// Check for changes
println!("Checking for script changes");
let changes_detected = rhai_factory.check_for_changes().expect("Failed to check for changes");
assert!(changes_detected, "Changes were not detected");
// Render the template after modification
println!("Rendering template after script modification");
let rendered_after = tera.render("test.html.tera", &context).expect("Failed to render template");
// Verify the rendered output after modification
println!("Rendered output after: {}", rendered_after);
assert!(rendered_after.contains("Greetings, World!"), "Rendered output does not contain modified greeting");
assert!(rendered_after.contains("The sum of 10 and 5 is: 16"), "Rendered output does not contain modified sum");
println!("test_hot_reload passed");
// Keep temp_dir in scope until the end of the test
drop(temp_dir);
}
#[test]
fn test_error_handling() {
println!("Running test_error_handling");
// Set up the test environment
let (temp_dir, templates_dir, scripts_dir) = setup_test_environment();
// Create factories
let rhai_factory = RhaiFactory::with_caching();
let tera_factory = TeraFactory::new();
// Create a script with syntax errors
let invalid_script_path = scripts_dir.join("invalid.rhai");
let mut invalid_script_file = File::create(&invalid_script_path).expect("Failed to create invalid script file");
invalid_script_file.write_all(b"
// This script has a syntax error
fn broken_function(a, b { // Missing closing parenthesis
a + b
}
").expect("Failed to write to invalid script file");
// Try to compile the invalid script
println!("Attempting to compile invalid script");
let compile_result = rhai_factory.compile_modules(&[&invalid_script_path], None);
// Verify that compilation fails with an error
assert!(compile_result.is_err(), "Compilation of invalid script should fail");
println!("Compilation error (expected): {:?}", compile_result.err());
// Create a template with invalid syntax
let invalid_template_path = templates_dir.join("invalid.html.tera");
let mut invalid_template_file = File::create(&invalid_template_path).expect("Failed to create invalid template file");
invalid_template_file.write_all(b"
{{ unclosed tag
").expect("Failed to write to invalid template file");
// Try to create a Tera engine with the invalid template
println!("Attempting to create Tera engine with invalid template");
let tera_result = tera_factory.create_tera_engine(&[&templates_dir]);
// Verify that Tera creation still succeeds (Tera loads valid templates and logs errors for invalid ones)
assert!(tera_result.is_ok(), "Tera engine creation should succeed even with invalid templates");
println!("test_error_handling passed");
// Keep temp_dir in scope until the end of the test
drop(temp_dir);
}
#[test]
fn test_function_adapter() {
println!("Running test_function_adapter");
// Set up the test environment
let (temp_dir, templates_dir, scripts_dir) = setup_test_environment();
// Create factories
let rhai_factory = RhaiFactory::with_caching();
let tera_factory = TeraFactory::new();
// Create a script with functions that use different parameter types
let types_script_path = scripts_dir.join("types.rhai");
let mut types_script_file = File::create(&types_script_path).expect("Failed to create types script file");
types_script_file.write_all(b"
// Function that returns an integer
fn return_int() {
42
}
// Function that returns a float
fn return_float() {
3.14159
}
// Function that returns a string
fn return_string() {
\"Hello, World!\"
}
// Function that returns a boolean
fn return_bool() {
true
}
// Function that returns an array
fn return_array() {
[1, 2, 3, 4, 5]
}
// Function that returns a map
fn return_map() {
#{
\"name\": \"John\",
\"age\": 30,
\"city\": \"New York\"
}
}
").expect("Failed to write to types script file");
// Create a template that uses these functions with .html.tera extension
let types_template_path = templates_dir.join("types.html.tera");
let mut types_template_file = File::create(&types_template_path).expect("Failed to create types template file");
println!("Created template file at: {:?}", types_template_path);
types_template_file.write_all(b"
Integer: {{ return_int() }}
Float: {{ return_float() }}
String: {{ return_string() }}
Boolean: {{ return_bool() }}
Array: {{ return_array() }}
Map name: {{ return_map().name }}
Map age: {{ return_map().age }}
").expect("Failed to write to types template file");
// Compile the script
println!("Compiling types script");
let ast = rhai_factory.compile_modules(&[&types_script_path], None)
.expect("Failed to compile types script");
// Create a hot reloadable AST
let hot_ast = Arc::new(RwLock::new(ast));
// Create a Tera engine with Rhai integration
println!("Creating Tera engine with Rhai integration for types test");
println!("Template directory exists: {:?}", std::fs::metadata(&templates_dir).is_ok());
println!("Template directory contents:");
if let Ok(entries) = std::fs::read_dir(&templates_dir) {
for entry in entries.filter_map(Result::ok) {
println!(" - {:?}", entry.path());
}
}
let tera = tera_factory.create_tera_with_rhai(&[&templates_dir], hot_ast.clone())
.expect("Failed to create Tera engine with Rhai");
// Print available template names for debugging
println!("Available templates: {:?}", tera.get_template_names().collect::>());
// Render the template
println!("Rendering types template");
let rendered = tera.render("types.html.tera", &Context::new()).expect("Failed to render types template");
// Verify the rendered output for each type
println!("Rendered output: {}", rendered);
assert!(rendered.contains("Integer: 42"), "Integer not correctly rendered");
assert!(rendered.contains("Float: 3.14159"), "Float not correctly rendered");
assert!(rendered.contains("String: Hello, World!"), "String not correctly rendered");
assert!(rendered.contains("Boolean: true"), "Boolean not correctly rendered");
assert!(rendered.contains("Array: [1, 2, 3, 4, 5]"), "Array not correctly rendered");
assert!(rendered.contains("Map name: John"), "Map name not correctly rendered");
assert!(rendered.contains("Map age: 30"), "Map age not correctly rendered");
println!("test_function_adapter passed");
// Keep temp_dir in scope until the end of the test
drop(temp_dir);
}