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 },
29}
30
31pub fn run<I, T>(args: I) -> anyhow::Result<i32>
32where
33 I: IntoIterator<Item = T>,
34 T: Into<std::ffi::OsString> + Clone,
35{
36 let cli = Cli::try_parse_from(args)?;
37 match cli.command {
38 Some(Command::Check) | None => Ok(0),
41 Some(Command::UnitLocation { path }) => run_unit_location(&path),
42 }
43}
44
45fn run_unit_location(root: &Path) -> anyhow::Result<i32> {
50 let orphans = location::missing_unit_tests(root)?;
51 if orphans.is_empty() {
52 return Ok(0);
53 }
54 for orphan in &orphans {
55 eprintln!("missing colocated unit test: {}", orphan.display());
56 }
57 eprintln!(
58 "error: {} source file(s) missing a colocated `_test.py`",
59 orphans.len()
60 );
61 Ok(1)
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn no_args_returns_ok_zero() {
70 assert_eq!(run(["testing-conventions"]).unwrap(), 0);
71 }
72
73 #[test]
74 fn check_returns_ok_zero() {
75 assert_eq!(run(["testing-conventions", "check"]).unwrap(), 0);
76 }
77
78 #[test]
79 fn unknown_flag_errors() {
80 assert!(run(["testing-conventions", "--bogus"]).is_err());
81 }
82
83 #[test]
84 fn help_flag_returns_clap_display_help() {
85 let err = run(["testing-conventions", "--help"]).expect_err("--help should bubble");
86 let clap_err = err
87 .downcast_ref::<clap::Error>()
88 .expect("error should be a clap::Error");
89 assert_eq!(clap_err.kind(), clap::error::ErrorKind::DisplayHelp);
90 }
91
92 #[test]
93 fn version_flag_returns_clap_display_version() {
94 let err = run(["testing-conventions", "--version"]).expect_err("--version should bubble");
95 let clap_err = err
96 .downcast_ref::<clap::Error>()
97 .expect("error should be a clap::Error");
98 assert_eq!(clap_err.kind(), clap::error::ErrorKind::DisplayVersion);
99 }
100}