#[cfg(test)]
mod alternatives_tests {
use super::*;
#[test]
fn test_REPL_013_003_alternatives_mkdir() {
let transformation_title = "mkdir → mkdir -p (idempotent)";
let alternatives = generate_idempotency_alternatives(transformation_title);
assert!(!alternatives.is_empty());
assert_eq!(alternatives.len(), 2);
assert_eq!(alternatives[0].approach, "Check before creating");
assert!(alternatives[0].example.contains("[ -d"));
assert_eq!(alternatives[1].approach, "Use mkdir with error suppression");
}
#[test]
fn test_REPL_013_003_alternatives_random() {
let transformation_title = "$RANDOM → Seeded random (deterministic)";
let alternatives = generate_determinism_alternatives(transformation_title);
assert!(!alternatives.is_empty());
assert_eq!(alternatives.len(), 4);
assert_eq!(alternatives[0].approach, "Use UUID for unique IDs");
assert!(alternatives[1].approach.contains("timestamp"));
assert!(alternatives[2].approach.contains("hash"));
assert!(alternatives[3].approach.contains("counter"));
}
#[test]
fn test_REPL_013_003_alternatives_quoting() {
let transformation_title = "$var → \"$var\" (quoted)";
let alternatives = generate_safety_alternatives(transformation_title);
assert!(!alternatives.is_empty());
assert_eq!(alternatives.len(), 3);
assert!(alternatives[0].approach.contains("printf"));
assert!(alternatives[1].approach.contains("arrays"));
assert!(alternatives[2].approach.contains("Validate"));
}
#[test]
fn test_REPL_013_003_alternative_builder() {
let alternative = Alternative::new(
"Use conditional mkdir",
"[ -d /tmp/app ] || mkdir /tmp/app",
"When you need explicit control",
)
.add_pro("Explicit logic")
.add_pro("Works in POSIX sh")
.add_con("More verbose");
assert_eq!(alternative.approach, "Use conditional mkdir");
assert_eq!(alternative.example, "[ -d /tmp/app ] || mkdir /tmp/app");
assert_eq!(alternative.when_to_use, "When you need explicit control");
assert_eq!(alternative.pros.len(), 2);
assert_eq!(alternative.cons.len(), 1);
assert_eq!(alternative.pros[0], "Explicit logic");
assert_eq!(alternative.pros[1], "Works in POSIX sh");
assert_eq!(alternative.cons[0], "More verbose");
}
#[test]
fn test_REPL_013_003_format_alternatives() {
let alternatives = vec![Alternative::new(
"Use mkdir -p",
"mkdir -p /tmp/app",
"When you want simple idempotency",
)
.add_pro("Simple and concise")
.add_con("No explicit error handling")];
let formatted = format_alternatives(&alternatives);
assert!(!formatted.is_empty());
assert!(formatted.contains("Alternative Approaches:"));
assert!(formatted.contains("1. Use mkdir -p"));
assert!(formatted.contains("Example: mkdir -p /tmp/app"));
assert!(formatted.contains("Pros:"));
assert!(formatted.contains("+ Simple and concise"));
assert!(formatted.contains("Cons:"));
assert!(formatted.contains("- No explicit error handling"));
}
#[test]
fn test_REPL_013_003_explanation_with_alternatives() {
let explanation = TransformationExplanation::new(
TransformationCategory::Idempotency,
"mkdir → mkdir -p",
"mkdir /tmp/app",
"mkdir -p /tmp/app",
"Added -p flag",
"Makes operation idempotent",
);
let alternatives = vec![
Alternative::new(
"Use conditional mkdir",
"[ -d /tmp/app ] || mkdir /tmp/app",
"When you need explicit control",
)
.add_pro("Explicit logic")
.add_con("More verbose"),
Alternative::new(
"Use mkdir -p",
"mkdir -p /tmp/app",
"When you want simplicity",
)
.add_pro("Simple and concise")
.add_con("No explicit check"),
];
let explanation_with_alts = explanation.with_alternatives(alternatives.clone());
assert_eq!(explanation_with_alts.alternatives.len(), 2);
assert_eq!(
explanation_with_alts.alternatives[0].approach,
"Use conditional mkdir"
);
assert_eq!(
explanation_with_alts.alternatives[1].approach,
"Use mkdir -p"
);
}
}
#[cfg(test)]
mod alternatives_property_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn prop_alternatives_always_provided(
title in "(mkdir|rm|ln|\\$RANDOM|\\$\\$|date|quote).*"
) {
let alternatives = if title.contains("mkdir") {
generate_idempotency_alternatives(&title)
} else if title.contains("$RANDOM") || title.contains("$$") || title.contains("date") {
generate_determinism_alternatives(&title)
} else {
generate_safety_alternatives(&title)
};
prop_assert!(!alternatives.is_empty());
}
}
proptest! {
#[test]
fn prop_format_never_panics(
approach in "[a-zA-Z ]{1,50}",
example in "[a-zA-Z0-9 $/.-]{1,100}",
when_to_use in "[a-zA-Z ]{1,100}"
) {
let alternatives = vec![
Alternative::new(approach, example, when_to_use)
.add_pro("Test pro")
.add_con("Test con")
];
let formatted = format_alternatives(&alternatives);
prop_assert!(!formatted.is_empty());
prop_assert!(formatted.contains("Alternative Approaches:"));
}
}
}
#[cfg(test)]
mod purify_and_lint_tests {
use super::*;
#[test]
fn test_REPL_014_001_purify_and_lint_mkdir() {
let input = "mkdir /tmp/test";
let result = purify_and_lint(input);
assert!(result.is_ok());
let result = result.unwrap();
assert!(result.purified_code.contains("mkdir -p"));
assert!(result.is_clean, "Purified mkdir should be clean");
assert_eq!(result.critical_violations(), 0);
}
#[test]
fn test_REPL_014_001_purify_and_lint_random() {
let input = "echo $RANDOM";
let result = purify_and_lint(input);
assert!(result.is_ok());
let result = result.unwrap();
assert!(!result.purified_code.contains("$RANDOM"));
assert!(result.is_clean, "Purified random should be clean");
assert_eq!(result.det_violations().len(), 0);
}
#[test]
fn test_REPL_014_001_lint_result_structure() {
let input = "echo hello";
let result = purify_and_lint(input);
assert!(result.is_ok());
let result = result.unwrap();
assert!(!result.purified_code.is_empty());
let _ = result.lint_result.diagnostics.len();
let _ = result.is_clean;
}
#[test]
fn test_REPL_014_001_violation_helpers() {
let input = "mkdir /tmp/test";
let result = purify_and_lint(input);
assert!(result.is_ok());
let result = result.unwrap();
let _ = result.det_violations();
let _ = result.idem_violations();
let _ = result.sec_violations();
let _ = result.critical_violations();
assert!(result.det_violations().len() <= result.lint_result.diagnostics.len());
}
#[test]
fn test_REPL_014_001_is_clean_flag() {
let input = "echo hello";
let result = purify_and_lint(input);
assert!(result.is_ok());
let result = result.unwrap();
if result.is_clean {
assert_eq!(result.critical_violations(), 0);
}
}
#[test]
fn test_REPL_014_001_format_result() {
let input = "mkdir /tmp/test";
let result = purify_and_lint(input).unwrap();
let formatted = format_purified_lint_result(&result);
assert!(formatted.contains("mkdir -p"));
assert!(formatted.contains("CLEAN") || formatted.contains("✓"));
}
#[test]
fn test_REPL_014_001_purify_and_lint_integration() {
let input = "mkdir /app/releases\necho hello";
let result = purify_and_lint(input);
assert!(result.is_ok());
let result = result.unwrap();
assert!(
!result.purified_code.is_empty(),
"Should produce purified code"
);
let _ = result.lint_result.diagnostics.len();
assert!(result.det_violations().len() <= result.lint_result.diagnostics.len());
assert!(result.idem_violations().len() <= result.lint_result.diagnostics.len());
assert!(result.sec_violations().len() <= result.lint_result.diagnostics.len());
}
#[test]
fn test_REPL_014_002_zero_det_violations() {
let input = "echo hello";
let result = purify_and_validate(input);
assert!(result.is_ok(), "Clean input should pass validation");
let purified = result.unwrap();
assert!(purified.contains("echo"));
}
#[test]
fn test_REPL_014_002_zero_idem_violations() {
let input = "mkdir -p /tmp/test";
let result = purify_and_validate(input);
assert!(result.is_ok(), "Idempotent input should pass validation");
}
#[test]
fn test_REPL_014_002_zero_sec_violations() {
let input = "echo \"$var\"";
let result = purify_and_validate(input);
assert!(result.is_ok(), "Quoted variable should pass validation");
}
#[test]
fn test_REPL_014_002_fails_with_violations() {
let test_cases = vec![
("echo $RANDOM", "DET violation"),
("rm /nonexistent", "IDEM violation"),
("eval $user_input", "SEC violation"),
];
for (input, description) in test_cases {
let result = purify_and_validate(input);
if let Err(err) = result {
let purif_err = err.downcast_ref::<PurificationError>();
assert!(
purif_err.is_some(),
"Error should be PurificationError for: {}",
description
);
}
}
}
#[test]
fn test_REPL_014_002_error_details() {
let input = "echo $RANDOM; eval $cmd; rm /tmp/file";
let result = purify_and_validate(input);
if let Err(e) = result {
if let Some(purif_err) = e.downcast_ref::<PurificationError>() {
assert!(purif_err.total_violations() > 0);
let msg = purif_err.to_string();
assert!(msg.contains("violation"));
}
}
}
}
include!("purifier_transforms_tests_inline_tests_prop_purify.rs");