use std::path::{Path, PathBuf}; use googletest::matchers::{contains, eq, is_true}; use googletest::{OrFail, expect_that, gtest}; use status::Code; use natu::SourceFile; fn fixture_source(path: &str) -> googletest::Result { Ok(SourceFile { path: PathBuf::from(path), source: std::fs::read_to_string(fixture_path(path)?).or_fail()?, }) } fn fixture_path(path: &str) -> googletest::Result { if Path::new(path).exists() { return Ok(PathBuf::from(path)); } let runfiles = PathBuf::from(std::env::var("TEST_SRCDIR").or_fail()?); let workspace = std::env::var("TEST_WORKSPACE").or_fail()?; Ok(runfiles.join(workspace).join("src/natu").join(path)) } #[gtest] fn natu_accepts_clean_source() -> googletest::Result<()> { let report = natu::future_sight(vec![fixture_source("testdata/clean.nix")?]).or_fail()?; expect_that!(report.is_clean(), is_true()); Ok(()) } #[gtest] fn natu_rejects_invalid_source() -> googletest::Result<()> { let error = natu::future_sight(vec![fixture_source("testdata/invalid.nix")?]) .err() .or_fail()?; expect_that!(error.code(), eq(Code::FailedPrecondition)); Ok(()) } #[gtest] fn natu_reports_out_of_order_let_bindings() -> googletest::Result<()> { let report = natu::future_sight(vec![fixture_source("testdata/lint_findings.nix")?]).or_fail()?; let let_order_count = report .diagnostics .iter() .filter(|diagnostic| diagnostic.finding.rule_id == "let_order") .count(); expect_that!(let_order_count, eq(2)); expect_that!( report.to_string(), eq(concat!( "testdata/lint_findings.nix:11 [manual_inherit] Use `inherit` for this assignment\n", "testdata/lint_findings.nix:12 [manual_inherit_from] Use `inherit` for this selected attribute\n", "testdata/lint_findings.nix:13 [unquoted_uri] URI literals should be quoted\n", "testdata/lint_findings.nix [let_order] Let bindings must follow dependency order\n", " let at line 1: first, second, nested\n", " let at line 4: nestedSecond, nestedFirst\n", )) ); Ok(()) } #[gtest] fn natu_preserves_suppressed_bindings() -> googletest::Result<()> { let report = natu::future_sight(vec![SourceFile { path: PathBuf::from("testdata/nolint.nix"), source: "{ config, ... }: let # NOLINT(redundant_let)\n cfg = config.example; # NOLINT(let_order)\n value = cfg;\nin value\n".into(), }]) .or_fail()?; expect_that!(report.is_clean(), is_true()); Ok(()) } #[gtest] fn natu_reports_select_dependency_order() -> googletest::Result<()> { let report = natu::future_sight(vec![SourceFile { path: PathBuf::from("testdata/select_dependencies.nix"), source: r#" let dependency = { value = 1; }; entry = dependency.value; in entry "# .into(), }]) .or_fail()?; let count = report .diagnostics .iter() .filter(|diagnostic| diagnostic.finding.rule_id == "let_order") .count(); expect_that!(count, eq(1)); Ok(()) } #[gtest] fn natu_reports_redundant_let() -> googletest::Result<()> { let report = natu::future_sight(vec![SourceFile { path: PathBuf::from("testdata/redundant_let.nix"), source: r#" let helper = 1; value = helper; in value "# .into(), }]) .or_fail()?; let count = report .diagnostics .iter() .filter(|diagnostic| diagnostic.finding.rule_id == "redundant_let") .count(); expect_that!(count, eq(1)); Ok(()) } #[gtest] fn natu_accepts_nonredundant_let() -> googletest::Result<()> { let report = natu::future_sight(vec![ SourceFile { path: PathBuf::from("testdata/recursive_let.nix"), source: r#" let recursive = recursive; in recursive "# .into(), }, SourceFile { path: PathBuf::from("testdata/dependent_let.nix"), source: r#" let value = 1; dependent = value; in value "# .into(), }, ]) .or_fail()?; let count = report .diagnostics .iter() .filter(|diagnostic| diagnostic.finding.rule_id == "redundant_let") .count(); expect_that!(count, eq(0)); Ok(()) } #[gtest] fn natu_reports_redundant_parameter_forwarding() -> googletest::Result<()> { let report = natu::future_sight(vec![ SourceFile { path: PathBuf::from("testdata/attribute_parameter.nix"), source: r#" { hosts, ... }: let collect = { hosts }: hosts; in collect { inherit hosts; } "# .into(), }, SourceFile { path: PathBuf::from("testdata/plain_parameter.nix"), source: r#" value: let transform = value: value; in transform value "# .into(), }, SourceFile { path: PathBuf::from("testdata/curried_parameter.nix"), source: r#" { hosts, ... }: let transform = prefix: hosts: suffix: hosts; in transform "prefix" hosts "suffix" "# .into(), }, SourceFile { path: PathBuf::from("testdata/local_parameter.nix"), source: r#" let transform = value: value; in transform value "# .into(), }, ]) .or_fail()?; let count = report .diagnostics .iter() .filter(|diagnostic| diagnostic.finding.rule_id == "redundant_parameter_forwarding") .count(); expect_that!(count, eq(3)); Ok(()) } #[gtest] fn natu_reports_list_to_attrs_after_map() -> googletest::Result<()> { let report = natu::future_sight(vec![SourceFile { path: PathBuf::from("testdata/list_to_attrs.nix"), source: r#" let generated = lib.listToAttrs ( map (item: lib.nameValuePair item item) items ); generated_pairs = lib.listToAttrs ( concatLists ( mapAttrsToList (name: _: lib.nameValuePair name name) attrs ) ); precomputed = lib.listToAttrs pairs; in { inherit generated generated_pairs precomputed; } "# .into(), }]) .or_fail()?; let count = report .diagnostics .iter() .filter(|diagnostic| diagnostic.finding.rule_id == "list_to_attrs_map") .count(); expect_that!(count, eq(1)); Ok(()) } #[gtest] fn natu_reports_unquoted_uri() -> googletest::Result<()> { let report = natu::future_sight(vec![fixture_source("testdata/lint_findings.nix")?]).or_fail()?; let findings = report .diagnostics .iter() .map(|diagnostic| { ( report.files.get(diagnostic.file_id).path.clone(), diagnostic.finding.rule_id, ) }) .collect::>(); expect_that!( findings, contains(eq(&( PathBuf::from("testdata/lint_findings.nix"), "unquoted_uri" ))) ); Ok(()) } #[gtest] fn natu_reports_manual_inherit() -> googletest::Result<()> { let report = natu::future_sight(vec![fixture_source("testdata/lint_findings.nix")?]).or_fail()?; let manual_inherit_count = report .diagnostics .iter() .filter(|diagnostic| diagnostic.finding.rule_id == "manual_inherit") .count(); expect_that!(manual_inherit_count, eq(1)); Ok(()) } #[gtest] fn natu_reports_manual_inherit_from() -> googletest::Result<()> { let report = natu::future_sight(vec![fixture_source("testdata/lint_findings.nix")?]).or_fail()?; let manual_inherit_from_count = report .diagnostics .iter() .filter(|diagnostic| diagnostic.finding.rule_id == "manual_inherit_from") .count(); expect_that!(manual_inherit_from_count, eq(1)); Ok(()) }