#![allow(missing_docs)]
use std::fs;
use std::path::Path;
use ruchy::notebook::engine::NotebookEngine;
use ruchy::notebook::types::{CellType, Notebook};
fn load_notebook(path: &Path) -> Result<Notebook, String> {
let content = fs::read_to_string(path)
.map_err(|e| format!("Failed to read {}: {}", path.display(), e))?;
serde_json::from_str(&content).map_err(|e| format!("Failed to parse notebook: {e}"))
}
fn execute_notebook(notebook: &Notebook) -> Vec<(usize, String, Result<String, String>)> {
let mut engine = match NotebookEngine::new() {
Ok(e) => e,
Err(err) => {
eprintln!("Failed to create notebook engine: {err}");
return Vec::new();
}
};
let mut results = Vec::new();
for (idx, cell) in notebook.cells.iter().enumerate() {
if cell.cell_type == CellType::Code {
let source = cell.source.clone();
let result = engine.execute_cell(&source);
let output = match result {
Ok(output_str) => Ok(output_str),
Err(e) => Err(format!("{e}")),
};
results.push((idx, source, output));
}
}
results
}
fn calculate_pass_rate(results: &[(usize, String, Result<String, String>)]) -> f64 {
let total = results.len();
if total == 0 {
return 100.0;
}
let passed = results
.iter()
.filter(|(_, _, result)| result.is_ok())
.count();
(passed as f64 / total as f64) * 100.0
}
#[test]
fn test_validate_01_literals_notebook() {
let notebook_path = Path::new("notebooks/01-literals.rnb");
let notebook = load_notebook(notebook_path).expect("Failed to load 01-literals.rnb");
let results = execute_notebook(¬ebook);
println!("\n📊 Validation Report: 01-literals.rnb");
println!("Total code cells: {}", results.len());
let mut passed = 0;
let mut failed = 0;
for (idx, source, result) in &results {
match result {
Ok(output) => {
passed += 1;
println!(" ✅ Cell {idx}: {source} → {output}");
}
Err(e) => {
failed += 1;
println!(" ❌ Cell {idx}: {source}");
println!(" Error: {e}");
}
}
}
let pass_rate = calculate_pass_rate(&results);
println!(
"\nPass Rate: {:.1}% ({passed}/{} cells)",
pass_rate,
passed + failed
);
assert!(
pass_rate >= 90.0,
"Pass rate {pass_rate:.1}% below 90% threshold"
);
}
#[test]
#[ignore = "RED phase: notebook 01-variables.rnb missing or incomplete - NOTEBOOK-009"]
fn test_validate_01_variables_notebook() {
let notebook_path = Path::new("notebooks/01-variables.rnb");
let notebook = load_notebook(notebook_path).expect("Failed to load 01-variables.rnb");
let results = execute_notebook(¬ebook);
let pass_rate = calculate_pass_rate(&results);
println!(
"\n📊 01-variables.rnb: {:.1}% pass rate ({}/{} cells)",
pass_rate,
results.iter().filter(|(_, _, r)| r.is_ok()).count(),
results.len()
);
assert!(
pass_rate >= 90.0,
"Pass rate {pass_rate:.1}% below 90% threshold"
);
}
#[test]
fn test_validate_02_arithmetic_notebook() {
let notebook_path = Path::new("notebooks/02-arithmetic.rnb");
let notebook = load_notebook(notebook_path).expect("Failed to load 02-arithmetic.rnb");
let results = execute_notebook(¬ebook);
let pass_rate = calculate_pass_rate(&results);
println!(
"\n📊 02-arithmetic.rnb: {:.1}% pass rate ({}/{} cells)",
pass_rate,
results.iter().filter(|(_, _, r)| r.is_ok()).count(),
results.len()
);
assert!(
pass_rate >= 90.0,
"Pass rate {pass_rate:.1}% below 90% threshold"
);
}
#[test]
fn test_validate_03_if_else_notebook() {
let notebook_path = Path::new("notebooks/03-if-else.rnb");
let notebook = load_notebook(notebook_path).expect("Failed to load 03-if-else.rnb");
let results = execute_notebook(¬ebook);
let pass_rate = calculate_pass_rate(&results);
println!(
"\n📊 03-if-else.rnb: {:.1}% pass rate ({}/{} cells)",
pass_rate,
results.iter().filter(|(_, _, r)| r.is_ok()).count(),
results.len()
);
assert!(
pass_rate >= 90.0,
"Pass rate {pass_rate:.1}% below 90% threshold"
);
}
#[test]
fn test_validate_all_notebooks_comprehensive() {
let notebook_files = vec![
"notebooks/01-literals.rnb",
"notebooks/01-variables.rnb",
"notebooks/02-arithmetic.rnb",
"notebooks/03-if-else.rnb",
];
let mut total_cells = 0;
let mut total_passed = 0;
let mut total_failed = 0;
println!("\n📊 Comprehensive Notebook Validation");
println!("=====================================\n");
for notebook_file in ¬ebook_files {
let path = Path::new(notebook_file);
match load_notebook(path) {
Ok(notebook) => {
let results = execute_notebook(¬ebook);
let passed = results.iter().filter(|(_, _, r)| r.is_ok()).count();
let failed = results.len() - passed;
total_cells += results.len();
total_passed += passed;
total_failed += failed;
let pass_rate = calculate_pass_rate(&results);
println!(
" {} - {:.1}% ({}/{})",
path.file_name().unwrap().to_str().unwrap(),
pass_rate,
passed,
results.len()
);
}
Err(e) => {
println!(" ❌ {}: {e}", path.display());
}
}
}
println!("\n📈 Overall Statistics:");
println!(" Total notebooks: {}", notebook_files.len());
println!(" Total code cells: {total_cells}");
println!(" Passed: {total_passed}");
println!(" Failed: {total_failed}");
let overall_pass_rate = if total_cells > 0 {
(total_passed as f64 / total_cells as f64) * 100.0
} else {
100.0
};
println!(" Overall Pass Rate: {overall_pass_rate:.1}%");
assert!(
overall_pass_rate >= 90.0,
"Overall pass rate {overall_pass_rate:.1}% below 90% threshold"
);
}