use crate::notebook::execution::CellExecutionResult;
use crate::notebook::persistence::{Checkpoint, TransactionResult};
use crate::runtime::repl::Repl;
use std::collections::HashMap;
use std::path::PathBuf;
use std::time::Instant;
#[derive(Debug)]
pub struct NotebookEngine {
repl: Repl,
}
impl NotebookEngine {
pub fn new() -> anyhow::Result<Self> {
let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("/tmp"));
let repl = Repl::new(current_dir)?;
Ok(Self { repl })
}
pub fn execute_cell(&mut self, code: &str) -> anyhow::Result<String> {
let trimmed = code.trim();
if trimmed.is_empty() || trimmed.starts_with("//") {
return Ok(String::new());
}
self.repl.eval(code)
}
pub fn execute_cell_detailed(&mut self, code: &str) -> CellExecutionResult {
let start = Instant::now();
let trimmed = code.trim();
if trimmed.is_empty() || trimmed.starts_with("//") {
return CellExecutionResult::success(
String::new(),
String::new(),
String::new(),
start.elapsed(),
);
}
match self.repl.eval(code) {
Ok(output) => CellExecutionResult::success(
output,
String::new(), String::new(), start.elapsed(),
),
Err(e) => CellExecutionResult::failure(e.to_string(), start.elapsed()),
}
}
pub fn create_checkpoint(&self, name: String) -> Checkpoint {
let bindings = self.repl.get_bindings();
let mut state_data = HashMap::new();
for (key, value) in bindings {
state_data.insert(key.clone(), value.to_string());
}
Checkpoint::with_state(name, state_data)
}
pub fn restore_checkpoint(&mut self, checkpoint: &Checkpoint) {
self.repl.clear_bindings();
for (key, value) in checkpoint.state_data() {
let code = format!("let {key} = {value}");
let _ = self.repl.eval(&code);
}
}
pub fn execute_transaction(&mut self, code: &str) -> TransactionResult<String> {
let checkpoint = self.create_checkpoint("transaction_savepoint".to_string());
match self.execute_cell(code) {
Ok(result) => TransactionResult::Success(result),
Err(e) => {
self.restore_checkpoint(&checkpoint);
TransactionResult::RolledBack {
error: e.to_string(),
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_notebook_001_engine_creation() {
let engine = NotebookEngine::new();
assert!(engine.is_ok());
}
#[test]
fn test_notebook_001_engine_debug_format() {
let engine = NotebookEngine::new().expect("operation should succeed in test");
let debug_str = format!("{engine:?}");
assert!(debug_str.contains("NotebookEngine"));
}
#[test]
fn test_notebook_001_execute_simple_expression() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("42");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_execute_arithmetic() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("1 + 1");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_execute_variable_binding() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("let x = 42");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_state_persists_across_cells() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result1 = engine.execute_cell("let x = 10");
assert!(result1.is_ok());
let result2 = engine.execute_cell("x + 5");
assert!(result2.is_ok());
}
#[test]
fn test_notebook_001_execute_string_expression() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("\"hello world\"");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_execute_boolean_expression() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("true");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_execute_invalid_syntax() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("let x = ");
assert!(result.is_err());
}
#[test]
fn test_notebook_001_execute_undefined_variable() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("undefined_var");
assert!(result.is_err());
}
#[test]
fn test_notebook_001_execute_function_definition() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("fn add(a, b) { a + b }");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_function_persists_across_cells() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result1 = engine.execute_cell("fn double(x) { x * 2 }");
assert!(result1.is_ok());
let result2 = engine.execute_cell("double(21)");
assert!(result2.is_ok());
}
#[test]
fn test_notebook_001_execute_if_expression() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("if true { 1 } else { 0 }");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_execute_match_expression() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("match 42 { 42 => true, _ => false }");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_execute_array_literal() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("[1, 2, 3]");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_execute_object_literal() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("{ a: 1, b: 2 }");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_execute_for_loop() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("for i in [1, 2, 3] { i }");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_execute_while_loop() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("let mut x = 0; while x < 3 { x = x + 1 }");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_multiple_statements_per_cell() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("let x = 10; let y = 20; x + y");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_state_isolation_between_engines() {
let mut engine1 = NotebookEngine::new().expect("operation should succeed in test");
let mut engine2 = NotebookEngine::new().expect("operation should succeed in test");
engine1
.execute_cell("let x = 100")
.expect("operation should succeed in test");
let result = engine2.execute_cell("x");
assert!(result.is_err());
}
#[test]
fn test_notebook_001_execute_empty_cell() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_execute_comment_only() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("// This is a comment");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_execute_whitespace_only() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell(" \n\t ");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_complex_state_mutation() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
engine
.execute_cell("let mut count = 0")
.expect("operation should succeed in test");
engine
.execute_cell("count = count + 1")
.expect("operation should succeed in test");
engine
.execute_cell("count = count + 1")
.expect("operation should succeed in test");
let result = engine.execute_cell("count");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_closure_state_persistence() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
engine
.execute_cell("let increment = |x| x + 1")
.expect("operation should succeed in test");
let result = engine.execute_cell("increment(41)");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_error_doesnt_break_engine() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
engine
.execute_cell("let x = 10")
.expect("operation should succeed in test");
let _ = engine.execute_cell("invalid syntax here");
let result = engine.execute_cell("x + 5");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_execute_multiline_function() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let code = r"
fn factorial(n) {
if n <= 1 {
1
} else {
n * factorial(n - 1)
}
}
";
let result = engine.execute_cell(code);
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_execute_nested_structures() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell("[[1, 2], [3, 4]]");
assert!(result.is_ok());
}
#[test]
fn test_notebook_001_execute_struct_literal() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let code = r"
struct Point { x: i64, y: i64 }
Point { x: 10, y: 20 }
";
let result = engine.execute_cell(code);
assert!(result.is_ok());
}
#[test]
fn test_notebook_003_create_empty_checkpoint() {
let engine = NotebookEngine::new().expect("operation should succeed in test");
let checkpoint = engine.create_checkpoint("empty".to_string());
assert_eq!(checkpoint.name(), "empty");
assert!(checkpoint.is_empty());
}
#[test]
fn test_notebook_003_create_checkpoint_with_state() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
engine
.execute_cell("let x = 42")
.expect("operation should succeed in test");
engine
.execute_cell("let y = 100")
.expect("operation should succeed in test");
let checkpoint = engine.create_checkpoint("with_state".to_string());
assert_eq!(checkpoint.name(), "with_state");
assert!(!checkpoint.is_empty());
assert!(checkpoint.has_variable("x"));
assert!(checkpoint.has_variable("y"));
}
#[test]
fn test_notebook_003_restore_checkpoint() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
engine
.execute_cell("let x = 10")
.expect("operation should succeed in test");
let checkpoint = engine.create_checkpoint("save".to_string());
engine
.execute_cell("x = 99")
.expect("operation should succeed in test");
assert_eq!(
engine
.execute_cell("x")
.expect("operation should succeed in test"),
"99"
);
engine.restore_checkpoint(&checkpoint);
assert_eq!(
engine
.execute_cell("x")
.expect("operation should succeed in test"),
"10"
);
}
#[test]
fn test_notebook_003_restore_multiple_variables() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
engine
.execute_cell("let a = 1")
.expect("operation should succeed in test");
engine
.execute_cell("let b = 2")
.expect("operation should succeed in test");
engine
.execute_cell("let c = 3")
.expect("operation should succeed in test");
let checkpoint = engine.create_checkpoint("multi".to_string());
engine
.execute_cell("a = 100")
.expect("operation should succeed in test");
engine
.execute_cell("b = 200")
.expect("operation should succeed in test");
engine.restore_checkpoint(&checkpoint);
assert_eq!(
engine
.execute_cell("a")
.expect("operation should succeed in test"),
"1"
);
assert_eq!(
engine
.execute_cell("b")
.expect("operation should succeed in test"),
"2"
);
assert_eq!(
engine
.execute_cell("c")
.expect("operation should succeed in test"),
"3"
);
}
#[test]
fn test_notebook_003_transaction_success() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
engine
.execute_cell("let x = 10")
.expect("operation should succeed in test");
let result = engine.execute_transaction("x + 5");
assert!(result.is_success());
assert_eq!(result.success_value(), Some("15".to_string()));
}
#[test]
fn test_notebook_003_transaction_failure_rollback() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
engine
.execute_cell("let x = 10")
.expect("operation should succeed in test");
let result = engine.execute_transaction("x = invalid_syntax");
assert!(!result.is_success());
assert!(result.is_rolled_back());
assert!(result.error().is_some());
assert_eq!(
engine
.execute_cell("x")
.expect("operation should succeed in test"),
"10"
);
}
#[test]
fn test_notebook_003_transaction_preserves_state_on_error() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
engine
.execute_cell("let a = 1")
.expect("operation should succeed in test");
engine
.execute_cell("let b = 2")
.expect("operation should succeed in test");
let _result = engine.execute_transaction("let c = undefined_var");
assert_eq!(
engine
.execute_cell("a")
.expect("operation should succeed in test"),
"1"
);
assert_eq!(
engine
.execute_cell("b")
.expect("operation should succeed in test"),
"2"
);
assert!(engine.execute_cell("c").is_err());
}
#[test]
fn test_notebook_003_multiple_checkpoints() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
engine
.execute_cell("let x = 1")
.expect("operation should succeed in test");
let cp1 = engine.create_checkpoint("checkpoint1".to_string());
engine
.execute_cell("x = 2")
.expect("operation should succeed in test");
let cp2 = engine.create_checkpoint("checkpoint2".to_string());
engine
.execute_cell("x = 3")
.expect("operation should succeed in test");
engine.restore_checkpoint(&cp2);
assert_eq!(
engine
.execute_cell("x")
.expect("operation should succeed in test"),
"2"
);
engine.restore_checkpoint(&cp1);
assert_eq!(
engine
.execute_cell("x")
.expect("operation should succeed in test"),
"1"
);
}
#[test]
fn test_notebook_003_checkpoint_independence() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
engine
.execute_cell("let x = 42")
.expect("operation should succeed in test");
let cp1 = engine.create_checkpoint("cp1".to_string());
let cp2 = engine.create_checkpoint("cp2".to_string());
assert_eq!(cp1.name(), "cp1");
assert_eq!(cp2.name(), "cp2");
assert_eq!(cp1.variable_count(), cp2.variable_count());
}
#[test]
fn test_notebook_003_transaction_modifies_state_on_success() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
engine
.execute_cell("let x = 10")
.expect("operation should succeed in test");
let result = engine.execute_transaction("x = 20");
assert!(result.is_success());
assert_eq!(
engine
.execute_cell("x")
.expect("operation should succeed in test"),
"20"
);
}
#[test]
fn test_notebook_002_detailed_execution_success() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell_detailed("42");
assert!(result.is_success());
assert_eq!(result.output(), "42");
assert!(result.duration_ms() < 1000);
assert!(!result.has_stdout());
assert!(!result.has_stderr());
}
#[test]
fn test_notebook_002_detailed_execution_error() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell_detailed("undefined_variable");
assert!(!result.is_success());
assert!(result.error().is_some());
assert!(result
.error()
.expect("operation should succeed in test")
.contains("Undefined variable"));
}
#[test]
fn test_notebook_002_detailed_execution_empty_cell() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell_detailed("");
assert!(result.is_success());
assert_eq!(result.output(), "");
assert!(result.duration_ms() < 10);
}
#[test]
fn test_notebook_002_detailed_execution_timing() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell_detailed("1 + 1");
assert!(result.is_success());
assert!(result.duration_ms() < 50);
assert!(!result.is_slow());
}
#[test]
fn test_notebook_002_detailed_preserves_state() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result1 = engine.execute_cell_detailed("let x = 100");
assert!(result1.is_success());
let result2 = engine.execute_cell_detailed("x + 50");
assert!(result2.is_success());
assert_eq!(result2.output(), "150");
}
#[test]
fn test_notebook_002_detailed_complex_expression() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell_detailed("if true { 42 } else { 0 }");
assert!(result.is_success());
assert_eq!(result.output(), "42");
}
#[test]
fn test_notebook_002_detailed_multiline_code() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let code = r"
let a = 10
let b = 20
a + b
";
let result = engine.execute_cell_detailed(code);
assert!(result.is_success());
assert_eq!(result.output(), "30");
}
#[test]
fn test_notebook_002_detailed_error_recovery() {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result1 = engine.execute_cell_detailed("let x = 5");
assert!(result1.is_success());
let result2 = engine.execute_cell_detailed("invalid syntax");
assert!(!result2.is_success());
let result3 = engine.execute_cell_detailed("x + 10");
assert!(result3.is_success());
assert_eq!(result3.output(), "15");
}
#[cfg(test)]
mod property_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn notebook_engine_never_panics_on_any_input(code: String) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let _ = engine.execute_cell(&code);
}
#[test]
fn notebook_engine_handles_any_expression(expr in "[0-9]{1,9}") {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell(&expr);
prop_assert!(result.is_ok());
}
#[test]
fn notebook_engine_state_persists(
var_name in "[a-z][a-z0-9_]{0,10}",
value in 0i64..1000
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let define = format!("let {var_name} = {value}");
if engine.execute_cell(&define).is_ok() {
let use_var = engine.execute_cell(&var_name);
prop_assert!(use_var.is_ok());
}
}
#[test]
fn notebook_engine_handles_whitespace_variations(
spaces_before in 0usize..10,
spaces_after in 0usize..10
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let code = format!("{}42{}", " ".repeat(spaces_before), " ".repeat(spaces_after));
let result = engine.execute_cell(&code);
prop_assert!(result.is_ok() || result.is_err());
}
#[test]
fn notebook_engine_arithmetic_operations(
a in 1i64..100,
b in 1i64..100
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let add = format!("{a} + {b}");
prop_assert!(engine.execute_cell(&add).is_ok());
let sub = format!("{a} - {b}");
prop_assert!(engine.execute_cell(&sub).is_ok());
let mul = format!("{a} * {b}");
prop_assert!(engine.execute_cell(&mul).is_ok());
let div = format!("{a} / {b}");
prop_assert!(engine.execute_cell(&div).is_ok());
}
#[test]
fn notebook_engine_string_literals(s in ".*") {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let escaped = s.replace('\\', "\\\\").replace('"', "\\\"");
let code = format!("\"{escaped}\"");
let _ = engine.execute_cell(&code);
}
#[test]
fn notebook_engine_boolean_operations(
a: bool,
b: bool
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let and = format!("{a} && {b}");
prop_assert!(engine.execute_cell(&and).is_ok());
let or = format!("{a} || {b}");
prop_assert!(engine.execute_cell(&or).is_ok());
let not = format!("!{a}");
prop_assert!(engine.execute_cell(¬).is_ok());
}
#[test]
fn notebook_engine_multiple_cells_consistency(
operations in prop::collection::vec("[0-9]+ [+\\-*/] [0-9]+", 1..10)
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
for op in operations {
let _ = engine.execute_cell(&op);
}
let result = engine.execute_cell("42");
prop_assert!(result.is_ok());
}
#[test]
fn notebook_engine_error_recovery(
valid_code in "[0-9]{1,9}",
invalid_code in "[+\\-*/]+",
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
prop_assert!(engine.execute_cell(&valid_code).is_ok());
let _ = engine.execute_cell(&invalid_code);
prop_assert!(engine.execute_cell(&valid_code).is_ok());
}
#[test]
fn notebook_engine_comment_handling(
comment_text in ".*"
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let code = format!("// {comment_text}");
prop_assert!(engine.execute_cell(&code).is_ok());
}
#[test]
fn notebook_engine_detailed_never_panics(code: String) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let _ = engine.execute_cell_detailed(&code);
}
#[test]
fn notebook_engine_detailed_timing_is_reasonable(
expr in "[0-9]{1,5}"
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell_detailed(&expr);
prop_assert!(result.duration_ms() < 1000);
}
#[test]
fn notebook_engine_detailed_success_has_output(
value in 1i64..1000
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let code = format!("{value}");
let result = engine.execute_cell_detailed(&code);
if result.is_success() {
prop_assert!(result.error().is_none());
prop_assert!(!result.output().is_empty() || result.output() == "()");
}
}
#[test]
fn notebook_engine_detailed_failure_has_error(
invalid in "[+\\-*/]{3,10}"
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell_detailed(&invalid);
if !result.is_success() {
prop_assert!(result.error().is_some());
prop_assert!(!result.error().expect("operation should succeed in test").is_empty());
}
}
#[test]
fn notebook_engine_detailed_preserves_timing_order(
operations in prop::collection::vec("[0-9]{1,3}", 5..15)
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let mut timings = Vec::new();
for op in operations {
let result = engine.execute_cell_detailed(&op);
timings.push(result.duration_ms());
}
for timing in timings {
prop_assert!(timing < 100);
}
}
#[test]
fn notebook_engine_detailed_empty_is_fast(
spaces in 0usize..20
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let code = " ".repeat(spaces);
let result = engine.execute_cell_detailed(&code);
prop_assert!(result.is_success());
prop_assert!(result.duration_ms() < 10);
prop_assert_eq!(result.output(), "");
}
#[test]
fn notebook_engine_detailed_arithmetic_always_timed(
a in 1i64..100,
b in 1i64..100,
op in "[+\\-*/]"
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let code = format!("{} {} {}", a, op.chars().next().expect("operation should succeed in test"), b);
let result = engine.execute_cell_detailed(&code);
let _ = result.duration_ms();
}
#[test]
fn notebook_engine_detailed_consistent_with_basic(
expr in "[0-9]{1,5}"
) {
let mut engine1 = NotebookEngine::new().expect("operation should succeed in test");
let mut engine2 = NotebookEngine::new().expect("operation should succeed in test");
let basic_result = engine1.execute_cell(&expr);
let detailed_result = engine2.execute_cell_detailed(&expr);
if basic_result.is_ok() {
prop_assert!(detailed_result.is_success());
prop_assert_eq!(detailed_result.output(), basic_result.expect("operation should succeed in test"));
} else {
prop_assert!(!detailed_result.is_success());
}
}
#[test]
fn notebook_engine_detailed_state_consistency(
var_name in "[a-z][a-z0-9]{0,8}",
value1 in 0i64..100,
value2 in 0i64..100
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let def_result = engine.execute_cell_detailed(&format!("let {var_name} = {value1}"));
if def_result.is_success() {
let use_result = engine.execute_cell_detailed(&var_name);
prop_assert!(use_result.is_success());
let mod_result = engine.execute_cell_detailed(&format!("{var_name} = {value2}"));
if mod_result.is_success() {
let check_result = engine.execute_cell_detailed(&var_name);
prop_assert!(check_result.is_success());
}
}
}
#[test]
fn notebook_engine_detailed_error_metadata_complete(
invalid in ".*"
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let result = engine.execute_cell_detailed(&invalid);
let _ = result.duration_ms();
if result.is_success() {
prop_assert!(result.error().is_none());
} else {
prop_assert!(result.error().is_some());
prop_assert!(!result.error().expect("operation should succeed in test").is_empty());
}
}
#[test]
fn notebook_checkpoint_preserves_variable_count(
var_count in 0usize..20
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let baseline = engine.create_checkpoint("baseline".to_string()).variable_count();
for i in 0..var_count {
let _ = engine.execute_cell(&format!("let var{i} = {i}"));
}
let checkpoint = engine.create_checkpoint("test".to_string());
prop_assert!(checkpoint.variable_count() >= baseline + var_count);
}
#[test]
fn notebook_restore_recovers_all_variables(
var_name in "[a-z][a-z0-9]{0,8}",
value in 0i64..1000
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let def = format!("let {var_name} = {value}");
if engine.execute_cell(&def).is_ok() {
let checkpoint = engine.create_checkpoint("save".to_string());
let _ = engine.execute_cell(&format!("{var_name} = 9999"));
engine.restore_checkpoint(&checkpoint);
if let Ok(result) = engine.execute_cell(&var_name) {
prop_assert_eq!(result, value.to_string());
}
}
}
#[test]
fn notebook_transaction_success_preserves_result(
value in 1i64..1000
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let code = format!("{value}");
let result = engine.execute_transaction(&code);
if result.is_success() {
prop_assert_eq!(result.success_value(), Some(value.to_string()));
}
}
#[test]
fn notebook_transaction_failure_preserves_state(
initial_value in 0i64..100,
invalid_code in "[+\\-*/]{3,10}"
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
if engine.execute_cell(&format!("let x = {initial_value}")).is_ok() {
let result = engine.execute_transaction(&invalid_code);
if result.is_rolled_back() {
if let Ok(value) = engine.execute_cell("x") {
prop_assert_eq!(value, initial_value.to_string());
}
}
}
}
#[test]
fn notebook_checkpoint_names_are_preserved(
name in "[a-zA-Z0-9_]{1,20}"
) {
let engine = NotebookEngine::new().expect("operation should succeed in test");
let checkpoint = engine.create_checkpoint(name.clone());
prop_assert_eq!(checkpoint.name(), name);
}
#[test]
fn notebook_multiple_checkpoints_independent(
vars in prop::collection::vec(("[a-z]{1,5}", 0i64..100), 1..10)
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
for (name, value) in &vars {
let _ = engine.execute_cell(&format!("let {name} = {value}"));
}
let cp1 = engine.create_checkpoint("cp1".to_string());
let cp2 = engine.create_checkpoint("cp2".to_string());
prop_assert_eq!(cp1.name(), "cp1");
prop_assert_eq!(cp2.name(), "cp2");
prop_assert_eq!(cp1.variable_count(), cp2.variable_count());
}
#[test]
fn notebook_restore_is_idempotent(
value in 1i64..100
) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
if engine.execute_cell(&format!("let x = {value}")).is_ok() {
let checkpoint = engine.create_checkpoint("save".to_string());
engine.restore_checkpoint(&checkpoint);
engine.restore_checkpoint(&checkpoint);
engine.restore_checkpoint(&checkpoint);
if let Ok(result) = engine.execute_cell("x") {
prop_assert_eq!(result, value.to_string());
}
}
}
#[test]
fn notebook_transaction_never_panics(code: String) {
let mut engine = NotebookEngine::new().expect("operation should succeed in test");
let _ = engine.execute_transaction(&code);
}
#[test]
fn notebook_checkpoint_timestamps_are_ordered(
delay_ms in 0u64..10
) {
use std::thread;
use std::time::Duration;
let engine = NotebookEngine::new().expect("operation should succeed in test");
let cp1 = engine.create_checkpoint("first".to_string());
thread::sleep(Duration::from_millis(delay_ms));
let cp2 = engine.create_checkpoint("second".to_string());
prop_assert!(cp2.timestamp() >= cp1.timestamp());
}
#[test]
fn notebook_transaction_consistent_with_direct_execution(
expr in "[0-9]{1,5}"
) {
let mut engine1 = NotebookEngine::new().expect("operation should succeed in test");
let mut engine2 = NotebookEngine::new().expect("operation should succeed in test");
let direct_result = engine1.execute_cell(&expr);
let transaction_result = engine2.execute_transaction(&expr);
if direct_result.is_ok() {
prop_assert!(transaction_result.is_success());
prop_assert_eq!(
transaction_result.success_value(),
direct_result.ok()
);
}
}
}
}
}