testing_conventions/
lib.rs1pub mod config;
2pub mod location;
3
4use std::path::{Path, PathBuf};
5
6use clap::{Parser, Subcommand};
7
8#[derive(Parser, Debug)]
9#[command(
10 name = "testing-conventions",
11 version,
12 about = "Enforce testing conventions in libraries (Python, TypeScript, and Rust).",
13 long_about = None,
14)]
15pub struct Cli {
16 #[command(subcommand)]
17 command: Option<Command>,
18}
19
20#[derive(Subcommand, Debug)]
21enum Command {
22 Check,
24 UnitLocation {
26 path: PathBuf,
28 #[arg(long, value_enum, default_value = "python")]
30 lang: location::Language,
31 },
32}
33
34pub fn run<I, T>(args: I) -> anyhow::Result<i32>
35where
36 I: IntoIterator<Item = T>,
37 T: Into<std::ffi::OsString> + Clone,
38{
39 let cli = Cli::try_parse_from(args)?;
40 match cli.command {
41 Some(Command::Check) | None => Ok(0),
44 Some(Command::UnitLocation { path, lang }) => run_unit_location(&path, lang),
45 }
46}
47
48fn run_unit_location(root: &Path, language: location::Language) -> anyhow::Result<i32> {
53 let orphans = location::missing_unit_tests(root, language)?;
54 if orphans.is_empty() {
55 return Ok(0);
56 }
57 for orphan in &orphans {
58 eprintln!("missing colocated unit test: {}", orphan.display());
59 }
60 eprintln!(
61 "error: {} source file(s) missing a colocated unit test",
62 orphans.len()
63 );
64 Ok(1)
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn no_args_returns_ok_zero() {
73 assert_eq!(run(["testing-conventions"]).unwrap(), 0);
74 }
75
76 #[test]
77 fn check_returns_ok_zero() {
78 assert_eq!(run(["testing-conventions", "check"]).unwrap(), 0);
79 }
80
81 #[test]
82 fn unknown_flag_errors() {
83 assert!(run(["testing-conventions", "--bogus"]).is_err());
84 }
85
86 #[test]
87 fn help_flag_returns_clap_display_help() {
88 let err = run(["testing-conventions", "--help"]).expect_err("--help should bubble");
89 let clap_err = err
90 .downcast_ref::<clap::Error>()
91 .expect("error should be a clap::Error");
92 assert_eq!(clap_err.kind(), clap::error::ErrorKind::DisplayHelp);
93 }
94
95 #[test]
96 fn version_flag_returns_clap_display_version() {
97 let err = run(["testing-conventions", "--version"]).expect_err("--version should bubble");
98 let clap_err = err
99 .downcast_ref::<clap::Error>()
100 .expect("error should be a clap::Error");
101 assert_eq!(clap_err.kind(), clap::error::ErrorKind::DisplayVersion);
102 }
103}