use clap::Parser;
use clap::CommandFactory;
use super::{
Cli, Commands, DiffOutputFormat, DiffShow, HeaderCase, JsonMode, MetadataFormat, OutputFormat,
XmlMode,
};
#[test]
fn help_output_includes_author_and_crates_io_link() {
let help = Cli::command().render_help().to_string();
assert!(help.contains("Daniele Olmisani <daniele.olmisani@gmail.com>"));
assert!(help.contains("https://crates.io/crates/query-forge"));
}
#[test]
fn version_output_includes_author_and_crates_io_link() {
let version = Cli::command().render_version().to_string();
assert!(version.contains("Daniele Olmisani <daniele.olmisani@gmail.com>"));
assert!(version.contains("https://crates.io/crates/query-forge"));
}
#[test]
fn parses_configurable_type_inference_flags() {
let cli = Cli::parse_from([
"qf",
"query",
"--input",
"data.csv",
"--sql",
"SELECT * FROM table",
"--all-text",
"--decimal-comma",
"--date-format",
"%d/%m/%Y",
"--null-values",
"NULL,N/A",
"--true-values",
"YES,Y",
"--false-values",
"NO,N",
]);
let Commands::Query(cmd) = cli.command else {
panic!("expected query command");
};
assert!(cmd.all_text);
assert!(cmd.decimal_comma);
assert_eq!(cmd.date_format.as_deref(), Some("%d/%m/%Y"));
assert_eq!(cmd.null_values, vec!["NULL", "N/A"]);
assert_eq!(cmd.true_values, vec!["YES", "Y"]);
assert_eq!(cmd.false_values, vec!["NO", "N"]);
}
#[test]
fn parses_input_normalization_flags() {
let cli = Cli::parse_from([
"qf",
"query",
"--input",
"data.csv",
"--sql",
"SELECT * FROM table",
"--trim",
"--skip-empty-rows",
"--normalize-headers",
"--header-case",
"snake",
"--dedupe-headers",
]);
let Commands::Query(cmd) = cli.command else {
panic!("expected query command");
};
assert!(cmd.trim);
assert!(cmd.skip_empty_rows);
assert!(cmd.normalize_headers);
assert!(matches!(cmd.header_case, Some(HeaderCase::Snake)));
assert!(cmd.dedupe_headers);
}
#[test]
fn parses_header_case_camel() {
let cli = Cli::parse_from([
"qf",
"query",
"--input",
"data.csv",
"--sql",
"SELECT * FROM table",
"--normalize-headers",
"--header-case",
"camel",
]);
let Commands::Query(cmd) = cli.command else {
panic!("expected query command");
};
assert!(matches!(cmd.header_case, Some(HeaderCase::Camel)));
}
#[test]
fn parses_header_case_pascal() {
let cli = Cli::parse_from([
"qf",
"query",
"--input",
"data.csv",
"--sql",
"SELECT * FROM table",
"--normalize-headers",
"--header-case",
"pascal",
]);
let Commands::Query(cmd) = cli.command else {
panic!("expected query command");
};
assert!(matches!(cmd.header_case, Some(HeaderCase::Pascal)));
}
#[test]
fn parses_header_case_screaming_snake() {
let cli = Cli::parse_from([
"qf",
"query",
"--input",
"data.csv",
"--sql",
"SELECT * FROM table",
"--normalize-headers",
"--header-case",
"screaming-snake",
]);
let Commands::Query(cmd) = cli.command else {
panic!("expected query command");
};
assert!(matches!(cmd.header_case, Some(HeaderCase::ScreamingSnake)));
}
#[test]
fn rejects_infer_types_and_all_text_together() {
let parsed = Cli::try_parse_from([
"qf",
"query",
"--input",
"data.csv",
"--sql",
"SELECT * FROM table",
"--infer-types",
"--all-text",
]);
assert!(parsed.is_err());
}
#[test]
fn parses_sql_file_flag() {
let cli = Cli::parse_from([
"qf",
"query",
"--input",
"data.csv",
"--sql-file",
"query.sql",
]);
let Commands::Query(cmd) = cli.command else {
panic!("expected query command");
};
assert_eq!(
cmd.sql_file.as_deref(),
Some(std::path::Path::new("query.sql"))
);
}
#[test]
fn parses_json_mode_flag() {
let cli = Cli::parse_from([
"qf",
"query",
"--input",
"data.json",
"--sql",
"SELECT * FROM table",
"--json-mode",
"flatten",
]);
let Commands::Query(cmd) = cli.command else {
panic!("expected query command");
};
assert!(matches!(cmd.json_mode, Some(JsonMode::Flatten)));
}
#[test]
fn parses_xml_mode_flag() {
let cli = Cli::parse_from([
"qf",
"query",
"--input",
"data.xml",
"--sql",
"SELECT * FROM table",
"--xml-mode",
"attributes",
]);
let Commands::Query(cmd) = cli.command else {
panic!("expected query command");
};
assert!(matches!(cmd.xml_mode, Some(XmlMode::Attributes)));
}
#[test]
fn json_mode_defaults_to_none_when_not_specified() {
let cli = Cli::parse_from([
"qf",
"query",
"--input",
"data.json",
"--sql",
"SELECT * FROM table",
]);
let Commands::Query(cmd) = cli.command else {
panic!("expected query command");
};
assert!(cmd.json_mode.is_none());
assert!(cmd.xml_mode.is_none());
}
#[test]
fn parses_meta_flag() {
let cli = Cli::parse_from([
"qf",
"query",
"--input",
"data.csv",
"--sql",
"SELECT * FROM table",
"--meta",
]);
let Commands::Query(cmd) = cli.command else {
panic!("expected query command");
};
assert!(cmd.meta);
assert!(cmd.meta_format.is_none());
}
#[test]
fn parses_meta_with_json_format() {
let cli = Cli::parse_from([
"qf",
"query",
"--input",
"data.csv",
"--sql",
"SELECT * FROM table",
"--meta",
"--meta-format",
"json",
]);
let Commands::Query(cmd) = cli.command else {
panic!("expected query command");
};
assert!(cmd.meta);
assert!(matches!(cmd.meta_format, Some(MetadataFormat::Json)));
}
#[test]
fn parses_meta_with_text_format() {
let cli = Cli::parse_from([
"qf",
"query",
"--input",
"data.csv",
"--sql",
"SELECT * FROM table",
"--meta",
"--meta-format",
"text",
]);
let Commands::Query(cmd) = cli.command else {
panic!("expected query command");
};
assert!(cmd.meta);
assert!(matches!(cmd.meta_format, Some(MetadataFormat::Text)));
}
#[test]
fn parses_feather_output_format() {
let cli = Cli::parse_from([
"qf",
"query",
"--input",
"data.csv",
"--sql",
"SELECT * FROM table",
"--format",
"feather",
"--output",
"result.feather",
]);
let Commands::Query(cmd) = cli.command else {
panic!("expected query command");
};
assert!(matches!(cmd.format, Some(OutputFormat::Feather)));
}
#[test]
fn parses_md_alias_as_markdown_output_format() {
let cli = Cli::parse_from([
"qf",
"query",
"--input",
"data.csv",
"--sql",
"SELECT * FROM table",
"--format",
"md",
]);
let Commands::Query(cmd) = cli.command else {
panic!("expected query command");
};
assert!(matches!(cmd.format, Some(OutputFormat::Markdown)));
}
#[test]
fn meta_flag_is_false_when_not_specified() {
let cli = Cli::parse_from([
"qf",
"query",
"--input",
"data.csv",
"--sql",
"SELECT * FROM table",
]);
let Commands::Query(cmd) = cli.command else {
panic!("expected query command");
};
assert!(!cmd.meta);
assert!(cmd.meta_format.is_none());
}
#[test]
fn parses_diff_command_with_keys_and_show_filter() {
let cli = Cli::parse_from([
"qf",
"diff",
"--key",
"id,tenant",
"--show",
"added,changed",
"--ignore-columns",
"updated_at",
"before.csv",
"after.csv",
]);
let Commands::Diff(cmd) = cli.command else {
panic!("expected diff command");
};
assert_eq!(cmd.left, "before.csv");
assert_eq!(cmd.right, "after.csv");
assert_eq!(cmd.key, vec!["id", "tenant"]);
assert_eq!(cmd.ignore_columns, vec!["updated_at"]);
assert_eq!(cmd.show.len(), 2);
assert!(matches!(cmd.show[0], DiffShow::Added));
assert!(matches!(cmd.show[1], DiffShow::Changed));
}
#[test]
fn parses_diff_schema_only_side_by_side_format() {
let cli = Cli::parse_from([
"qf",
"diff",
"--schema-only",
"--side-by-side",
"--format",
"markdown",
"left.json",
"right.json",
]);
let Commands::Diff(cmd) = cli.command else {
panic!("expected diff command");
};
assert!(cmd.schema_only);
assert!(cmd.side_by_side);
assert!(matches!(cmd.format, Some(DiffOutputFormat::Markdown)));
}
#[test]
fn parses_diff_md_alias_as_markdown_format() {
let cli = Cli::parse_from([
"qf",
"diff",
"--format",
"md",
"left.json",
"right.json",
]);
let Commands::Diff(cmd) = cli.command else {
panic!("expected diff command");
};
assert!(matches!(cmd.format, Some(DiffOutputFormat::Markdown)));
}
#[test]
fn parses_inspect_md_alias_as_markdown_format() {
let cli = Cli::parse_from([
"qf",
"inspect",
"--input",
"data.csv",
"--format",
"md",
]);
let Commands::Inspect(cmd) = cli.command else {
panic!("expected inspect command");
};
assert!(matches!(cmd.format, super::InspectionFormat::Markdown));
}
#[test]
fn parses_tables_md_alias_as_markdown_format() {
let cli = Cli::parse_from([
"qf",
"tables",
"--input",
"data.csv",
"--format",
"md",
]);
let Commands::Tables(cmd) = cli.command else {
panic!("expected tables command");
};
assert!(matches!(cmd.format, super::InspectionFormat::Markdown));
}
#[test]
fn parses_schema_md_alias_as_markdown_format() {
let cli = Cli::parse_from([
"qf",
"schema",
"--input",
"data.csv",
"--format",
"md",
]);
let Commands::Schema(cmd) = cli.command else {
panic!("expected schema command");
};
assert!(matches!(cmd.format, super::InspectionFormat::Markdown));
}
#[test]
fn parses_pivot_command_count_query() {
let cli = Cli::parse_from([
"qf",
"pivot",
"--input",
"data.csv",
"--query",
"PIVOT COUNT(*) FROM table GROUP BY category",
]);
let Commands::Pivot(cmd) = cli.command else {
panic!("expected pivot command");
};
assert_eq!(
cmd.query.as_deref(),
Some("PIVOT COUNT(*) FROM table GROUP BY category")
);
assert!(cmd.query_file.is_none());
}
#[test]
fn parses_pivot_command_sum_query() {
let cli = Cli::parse_from([
"qf",
"pivot",
"--input",
"data.csv",
"--query",
"PIVOT SUM(stock) FOR active FROM table GROUP BY category",
]);
let Commands::Pivot(cmd) = cli.command else {
panic!("expected pivot command");
};
assert_eq!(
cmd.query.as_deref(),
Some("PIVOT SUM(stock) FOR active FROM table GROUP BY category")
);
}
#[test]
fn parses_pivot_show_sql_flag() {
let cli = Cli::parse_from([
"qf",
"pivot",
"--input",
"data.csv",
"--query",
"PIVOT COUNT(*) FROM table GROUP BY category",
"--show-sql",
]);
let Commands::Pivot(cmd) = cli.command else {
panic!("expected pivot command");
};
assert!(cmd.show_sql);
}
#[test]
fn parses_pivot_query_file() {
let cli = Cli::parse_from([
"qf",
"pivot",
"--input",
"data.csv",
"--query-file",
"./pivot.sql",
]);
let Commands::Pivot(cmd) = cli.command else {
panic!("expected pivot command");
};
assert!(cmd.query.is_none());
assert!(cmd.query_file.is_some());
}
#[test]
fn pivot_requires_query_or_query_file() {
let result = Cli::try_parse_from(["qf", "pivot", "--input", "data.csv"]);
assert!(result.is_err(), "pivot without --query or --query-file should fail");
}
#[test]
fn pivot_requires_input_argument() {
let result = Cli::try_parse_from([
"qf",
"pivot",
"--query",
"PIVOT COUNT(*) FROM table GROUP BY category",
]);
assert!(result.is_err(), "pivot without --input should fail");
}
#[test]
fn pivot_query_and_query_file_are_exclusive() {
let result = Cli::try_parse_from([
"qf",
"pivot",
"--input",
"data.csv",
"--query",
"PIVOT COUNT(*) FROM table GROUP BY category",
"--query-file",
"./pivot.sql",
]);
assert!(result.is_err(), "--query and --query-file should be mutually exclusive");
}
#[test]
fn parses_global_ai_mode_flag() {
let cli = Cli::parse_from([
"qf",
"--ai-mode",
"query",
"--input",
"data.csv",
"--sql",
"SELECT * FROM table",
]);
assert!(cli.ai_mode);
let Commands::Query(_) = cli.command else {
panic!("expected query command");
};
}
#[test]
fn parses_capabilities_command() {
let cli = Cli::parse_from(["qf", "capabilities"]);
assert!(!cli.ai_mode);
let Commands::Capabilities(_) = cli.command else {
panic!("expected capabilities command");
};
}