#![allow(missing_docs)]
use assert_cmd::Command;
use std::fs;
use std::path::Path;
use tempfile::TempDir;
fn extract_code_blocks(markdown: &str, language: &str) -> Vec<(usize, String)> {
let mut blocks = Vec::new();
let mut in_code_block = false;
let mut current_block = String::new();
let mut block_start_line = 0;
let mut current_lang = String::new();
for (line_num, line) in markdown.lines().enumerate() {
if line.starts_with("```") {
if in_code_block {
if current_lang == language {
blocks.push((block_start_line, current_block.clone()));
}
in_code_block = false;
current_block.clear();
} else {
in_code_block = true;
block_start_line = line_num + 1;
current_lang = line.trim_start_matches("```").trim().to_string();
}
} else if in_code_block {
current_block.push_str(line);
current_block.push('\n');
}
}
blocks
}
fn ruchy_cmd() -> Command {
assert_cmd::cargo::cargo_bin_cmd!("ruchy")
}
#[test]
fn test_readme_exists() {
let readme = Path::new("README.md");
assert!(readme.exists(), "README.md must exist in project root");
}
#[test]
fn test_readme_not_empty() {
let content = fs::read_to_string("README.md").expect("Failed to read README.md");
assert!(
content.len() > 100,
"README.md must contain substantial content (found {} bytes)",
content.len()
);
}
#[test]
fn test_readme_required_sections() {
let content = fs::read_to_string("README.md").expect("Failed to read README.md");
let required_sections = vec![
"# Ruchy", "## Features", "## Installation", "## CLI Commands", ];
for section in required_sections {
assert!(
content.contains(section),
"README.md must contain section: {section}"
);
}
}
#[test]
fn test_readme_ruchy_examples_all_work() {
let readme_content = fs::read_to_string("README.md").expect("Failed to read README.md");
let examples = extract_code_blocks(&readme_content, "ruchy");
assert!(
!examples.is_empty(),
"README.md must contain at least one ```ruchy code example"
);
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let mut passed = 0;
let mut failed = Vec::new();
for (line_num, code) in &examples {
if code.contains("// NOT IMPLEMENTED") || code.contains("// TODO") {
continue;
}
let test_file = temp_dir
.path()
.join(format!("readme_line_{line_num}.ruchy"));
fs::write(&test_file, code).expect("Failed to write test file");
let result = ruchy_cmd().arg("run").arg(&test_file).assert();
if result.get_output().status.success() {
passed += 1;
} else {
failed.push((*line_num, code.clone()));
}
}
if !failed.is_empty() {
eprintln!("\n❌ README.md VALIDATION FAILED");
eprintln!("Passed: {}/{}", passed, examples.len());
eprintln!("\nFailing examples:");
for (line_num, code) in &failed {
eprintln!("\nLine {line_num}: ```ruchy");
eprintln!("{}", code.trim());
eprintln!("```");
}
panic!(
"README.md contains {} non-working examples. Fix implementation or remove from README.",
failed.len()
);
}
println!("✅ All {passed} README.md examples validated successfully");
}
#[test]
fn test_readme_no_false_claims() {
let content = fs::read_to_string("README.md").expect("Failed to read README.md");
let false_claims: Vec<&str> = vec![
];
for claim in false_claims {
assert!(
!content.contains(claim),
"README.md falsely claims: '{claim}'. Either implement it or remove the claim."
);
}
}
#[test]
fn test_readme_installation_instructions() {
let content = fs::read_to_string("README.md").expect("Failed to read README.md");
assert!(
content.contains("cargo install") || content.contains("Installation"),
"README.md must contain installation instructions"
);
}
#[test]
fn test_readme_syntax_check_all_examples() {
let readme_content = fs::read_to_string("README.md").expect("Failed to read README.md");
let examples = extract_code_blocks(&readme_content, "ruchy");
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let mut syntax_errors = Vec::new();
for (line_num, code) in &examples {
if code.contains("// NOT IMPLEMENTED") || code.contains("// TODO") {
continue;
}
let test_file = temp_dir.path().join(format!("syntax_{line_num}.ruchy"));
fs::write(&test_file, code).expect("Failed to write test file");
let result = ruchy_cmd().arg("check").arg(&test_file).assert();
if !result.get_output().status.success() {
syntax_errors.push((*line_num, code.clone()));
}
}
if !syntax_errors.is_empty() {
eprintln!("\n❌ README.md SYNTAX VALIDATION FAILED");
eprintln!("\nExamples with syntax errors:");
for (line_num, code) in &syntax_errors {
eprintln!("\nLine {line_num}: ```ruchy");
eprintln!("{}", code.trim());
eprintln!("```");
}
panic!(
"README.md contains {} examples with syntax errors",
syntax_errors.len()
);
}
}
#[test]
fn test_readme_stability() {
let content = fs::read_to_string("README.md").expect("Failed to read README.md");
assert!(
content.len() > 1000,
"README.md seems too short ({} bytes). Should be comprehensive.",
content.len()
);
let ruchy_examples = extract_code_blocks(&content, "ruchy");
assert!(
!ruchy_examples.is_empty(),
"README.md should have at least 1 Ruchy example (found {})",
ruchy_examples.len()
);
}
#[test]
fn test_readme_dataframe_accuracy() {
let content = fs::read_to_string("README.md").expect("Failed to read README.md");
if content.contains("DataFrame") || content.contains("dataframe") {
let df_examples: Vec<_> = extract_code_blocks(&content, "ruchy")
.into_iter()
.filter(|(_, code)| code.contains("DataFrame") || code.contains("df!"))
.collect();
println!(
"Found {} DataFrame examples in README.md",
df_examples.len()
);
}
}
#[test]
fn test_readme_version_consistency() {
let content = fs::read_to_string("README.md").expect("Failed to read README.md");
let cargo_toml = fs::read_to_string("Cargo.toml").expect("Failed to read Cargo.toml");
let version_line = cargo_toml
.lines()
.find(|line| line.starts_with("version = "))
.expect("Cargo.toml must have version");
let version = version_line
.split('"')
.nth(1)
.expect("Version must be in quotes");
if content.contains("version") || content.contains("v3.") || content.contains("v4.") {
println!("README.md may contain version info - verify manually");
println!("Current version: {version}");
}
}
#[test]
fn test_readme_code_block_formatting() {
let content = fs::read_to_string("README.md").expect("Failed to read README.md");
let lines: Vec<&str> = content.lines().collect();
let mut bare_code_blocks = Vec::new();
for (i, line) in lines.iter().enumerate() {
if line.trim() == "```" {
bare_code_blocks.push(i + 1);
}
}
if !bare_code_blocks.is_empty() {
eprintln!(
"⚠️ WARNING: Found {} code blocks without language tags at lines: {:?}",
bare_code_blocks.len(),
bare_code_blocks
);
eprintln!("Use ```ruchy, ```bash, etc. for better syntax highlighting");
}
}
#[cfg(test)]
mod property_tests {
use super::*;
#[test]
fn test_readme_examples_idempotent() {
let readme_content = fs::read_to_string("README.md").expect("Failed to read README.md");
let examples = extract_code_blocks(&readme_content, "ruchy");
let temp_dir = TempDir::new().expect("Failed to create temp dir");
for (line_num, code) in examples.iter().take(3) {
if code.contains("// NOT IMPLEMENTED") {
continue;
}
let test_file = temp_dir.path().join(format!("idempotent_{line_num}.ruchy"));
fs::write(&test_file, code).expect("Failed to write test file");
let result1 = ruchy_cmd()
.arg("run")
.arg(&test_file)
.output()
.expect("Failed to run first time");
let result2 = ruchy_cmd()
.arg("run")
.arg(&test_file)
.output()
.expect("Failed to run second time");
assert_eq!(
result1.status.success(),
result2.status.success(),
"Example at line {line_num} should be deterministic"
);
}
}
}