fn test_publish_readiness_all_pass() {
let score = make_score(500, 500, 0, 1.0, 99.0, Grade::APlus, 3);
let checks = check_publish_readiness(&score);
assert!(checks.iter().all(|c| c.passed));
}
#[test]
fn test_publish_readiness_rate_below_99() {
let score = make_score(100, 95, 5, 0.95, 92.0, Grade::A, 3);
let checks = check_publish_readiness(&score);
let rate_check = &checks[0];
assert!(!rate_check.passed);
assert!(rate_check.value.contains("95.0"));
}
#[test]
fn test_publish_readiness_score_below_90() {
let score = make_score(200, 200, 0, 1.0, 85.0, Grade::B, 3);
let checks = check_publish_readiness(&score);
let score_check = &checks[1];
assert!(!score_check.passed);
}
#[test]
fn test_publish_readiness_missing_formats() {
let score = make_score(200, 200, 0, 1.0, 99.0, Grade::APlus, 2);
let checks = check_publish_readiness(&score);
let format_check = &checks[2];
assert!(!format_check.passed);
}
#[test]
fn test_publish_readiness_has_failures() {
let score = make_score(200, 195, 5, 0.975, 92.0, Grade::A, 3);
let checks = check_publish_readiness(&score);
let fail_check = &checks[3];
assert!(!fail_check.passed);
assert!(fail_check.value.contains("5"));
}
#[test]
fn test_publish_readiness_corpus_too_small() {
let score = make_score(50, 50, 0, 1.0, 99.0, Grade::APlus, 3);
let checks = check_publish_readiness(&score);
let size_check = &checks[4];
assert!(!size_check.passed);
}
#[test]
fn test_format_publish_checks_all_pass() {
let checks = vec![
PublishCheck {
name: "Check A",
passed: true,
value: "ok".to_string(),
},
PublishCheck {
name: "Check B",
passed: true,
value: "ok".to_string(),
},
];
let table = format_publish_checks(&checks);
assert!(table.contains("PASS"));
assert!(table.contains("Ready to publish"));
assert!(!table.contains("FAIL"));
}
#[test]
fn test_format_publish_checks_mixed() {
let checks = vec![
PublishCheck {
name: "Good check",
passed: true,
value: "ok".to_string(),
},
PublishCheck {
name: "Bad check 1",
passed: false,
value: "bad".to_string(),
},
PublishCheck {
name: "Bad check 2",
passed: false,
value: "bad".to_string(),
},
];
let table = format_publish_checks(&checks);
assert!(table.contains("PASS"));
assert!(table.contains("FAIL"));
assert!(table.contains("2 check(s) failed"));
assert!(!table.contains("Ready to publish"));
}
#[test]
fn test_dataset_info_empty_registry() {
let registry = CorpusRegistry { entries: vec![] };
let info = dataset_info(®istry);
assert_eq!(info.total_entries, 0);
for (_fmt, count) in &info.format_counts {
assert_eq!(*count, 0);
}
}
#[test]
fn test_dataset_info_all_formats() {
let entries = vec![
make_entry("B-001", CorpusFormat::Bash),
make_entry("B-002", CorpusFormat::Bash),
make_entry("M-001", CorpusFormat::Makefile),
make_entry("D-001", CorpusFormat::Dockerfile),
make_entry("D-002", CorpusFormat::Dockerfile),
make_entry("D-003", CorpusFormat::Dockerfile),
];
let registry = CorpusRegistry { entries };
let info = dataset_info(®istry);
assert_eq!(info.total_entries, 6);
assert_eq!(info.format_counts.len(), 3);
assert_eq!(info.format_counts[0], ("bash".to_string(), 2));
assert_eq!(info.format_counts[1], ("makefile".to_string(), 1));
assert_eq!(info.format_counts[2], ("dockerfile".to_string(), 3));
}
#[test]
fn test_dataset_info_schema_fields() {
let registry = CorpusRegistry { entries: vec![] };
let info = dataset_info(®istry);
assert_eq!(info.schema_fields.len(), 18);
assert_eq!(info.schema_fields[0].0, "id");
assert_eq!(info.schema_fields[17].0, "date");
}
#[test]
fn test_format_dataset_info_contains_structure() {
let entries = vec![
make_entry("B-001", CorpusFormat::Bash),
make_entry("M-001", CorpusFormat::Makefile),
];
let registry = CorpusRegistry { entries };
let info = dataset_info(®istry);
let table = format_dataset_info(&info);
assert!(table.contains("Corpus: 2 entries"));
assert!(table.contains("Dataset Schema"));
assert!(table.contains("Field"));
assert!(table.contains("Type"));
assert!(table.contains("Description"));
assert!(table.contains("Export formats: json, jsonl, csv"));
}
#[test]
fn test_format_dataset_info_shows_format_counts() {
let entries = vec![
make_entry("B-001", CorpusFormat::Bash),
make_entry("B-002", CorpusFormat::Bash),
];
let registry = CorpusRegistry { entries };
let info = dataset_info(®istry);
let table = format_dataset_info(&info);
assert!(table.contains("bash"));
assert!(table.contains("2 entries"));
}
#[test]
fn test_days_to_ymd_epoch() {
assert_eq!(days_to_ymd(0), (1970, 1, 1));
}
#[test]
fn test_days_to_ymd_day_one() {
assert_eq!(days_to_ymd(1), (1970, 1, 2));
}
#[test]
fn test_days_to_ymd_end_of_january() {
assert_eq!(days_to_ymd(30), (1970, 1, 31));
}
#[test]
fn test_days_to_ymd_february_1() {
assert_eq!(days_to_ymd(31), (1970, 2, 1));
}
#[test]
fn test_days_to_ymd_dec_31_non_leap() {
assert_eq!(days_to_ymd(364), (1970, 12, 31));
}
#[test]
fn test_days_to_ymd_jan_1_next_year() {
assert_eq!(days_to_ymd(365), (1971, 1, 1));
}
#[test]
fn test_days_to_ymd_leap_year_feb_29() {
assert_eq!(days_to_ymd(789), (1972, 2, 29));
}
#[test]
fn test_days_to_ymd_leap_year_mar_1() {
assert_eq!(days_to_ymd(790), (1972, 3, 1));
}
#[test]
fn test_days_to_ymd_year_2000_leap() {
assert_eq!(days_to_ymd(10957), (2000, 1, 1));
}
#[test]
fn test_days_to_ymd_year_2026() {
let (y, m, d) = days_to_ymd(20_507);
assert_eq!(y, 2026);
assert_eq!(m, 2);
assert_eq!(d, 23);
}
#[test]
fn test_leap_year_standard() {
assert!(is_leap_year(2024));
assert!(is_leap_year(2028));
}
#[test]
fn test_leap_year_century_non_leap() {
assert!(!is_leap_year(1900));
assert!(!is_leap_year(2100));
}
#[test]
fn test_leap_year_400_year() {
assert!(is_leap_year(2000));
assert!(is_leap_year(1600));
}
#[test]
fn test_non_leap_year() {
assert!(!is_leap_year(2025));
assert!(!is_leap_year(2023));
}
#[test]
fn test_export_format_display_all() {
assert_eq!(format!("{}", ExportFormat::JsonLines), "jsonl");
assert_eq!(format!("{}", ExportFormat::Csv), "csv");
assert_eq!(format!("{}", ExportFormat::Json), "json");
}
#[test]
fn test_export_format_equality() {
assert_eq!(ExportFormat::JsonLines, ExportFormat::JsonLines);
assert_ne!(ExportFormat::JsonLines, ExportFormat::Csv);
assert_ne!(ExportFormat::Csv, ExportFormat::Json);
}
#[test]
fn test_dataset_row_all_fields_serialize() {
let row = DatasetRow {
id: "B-999".into(),
name: "edge-case".into(),
tier: 4,
format: "bash".into(),
input_rust: "fn main() { println!(\"hello\"); }".into(),
expected_output: "#!/bin/sh\necho hello\n".into(),
actual_output: "#!/bin/sh\necho hello\n".into(),
transpiled: true,
output_correct: true,
lint_clean: true,
deterministic: true,
score: 95.0,
grade: "A".into(),
safety_index: 0,
safety_label: "safe".into(),
bashrs_version: "7.0.0".into(),
commit_sha: "deadbeef".into(),
date: "2026-02-23".into(),
};
let json = serde_json::to_string(&row).unwrap();
assert!(json.contains("B-999"));
assert!(json.contains("deadbeef"));
assert!(json.contains("7.0.0"));
}
#[test]
fn test_csv_export_row_with_commas_in_name() {
let mut entry = make_entry("B-050", CorpusFormat::Bash);
entry.name = "test, with comma".to_string();
let row = build_row(&entry, None, "1.0.0", "abc", "2026-01-01");
let output = export_csv(&[row]);
assert!(output.contains("\"test, with comma\""));
}
#[test]
fn test_current_date_iso8601() {
let date = current_date();
assert_eq!(date.len(), 10);
assert_eq!(&date[4..5], "-");
assert_eq!(&date[7..8], "-");
let year: u32 = date[0..4].parse().unwrap();
let month: u32 = date[5..7].parse().unwrap();
let day: u32 = date[8..10].parse().unwrap();
assert!(year >= 2024);
assert!((1..=12).contains(&month));
assert!((1..=31).contains(&day));
}