devscout-rs 0.5.0

Fast code index for C# and TypeScript codebases -- map, find, refs, impact from the CLI.
Documentation
use super::arg_count_fact::def_json_keys;
use super::*;

// --- testMethods -----------------------------------------------------------

#[test]
fn test_coverage_xunit_fact_and_theory_land_in_test_methods_serialized_last_after_bases() {
    let e = extract_src(
            "namespace App.Tests;\n\npublic class WidgetTests : TestBase\n{\n  [Fact]\n  public void ComputesTotal() { }\n\n  [Theory]\n  [InlineData(1)]\n  public void RejectsEmptyCart(int n) { }\n\n  public void Helper() { }\n}\n",
        );
    let d = find_def(&e, "App.Tests.WidgetTests").expect("WidgetTests def present");
    assert_eq!(
        d.test_methods,
        vec!["ComputesTotal", "RejectsEmptyCart"],
        "source order; the unattributed helper is a method but not a test"
    );
    assert_eq!(
        def_json_keys(d),
        vec![
            "id",
            "name",
            "namespace",
            "kind",
            "line",
            "methods",
            "bases",
            "testMethods"
        ],
        "testMethods lands LAST -- after bases, which was the final fact before this stage"
    );
}

#[test]
fn test_coverage_suffixed_qualified_targeted_and_shared_bracket_attribute_forms_all_match() {
    let e = extract_src(
            "namespace App.Tests;\n\npublic class SpellingTests\n{\n  [FactAttribute]\n  public void Suffixed() { }\n\n  [Xunit.Fact]\n  public void Qualified() { }\n\n  [method: Fact]\n  public void Targeted() { }\n\n  [Fact, Trait(\"speed\", \"fast\")]\n  public void SharesABracket() { }\n\n  [method: Xunit.FactAttribute]\n  public void EveryFormAtOnce() { }\n}\n",
        );
    let d = find_def(&e, "App.Tests.SpellingTests").expect("SpellingTests def present");
    assert_eq!(
            d.test_methods,
            vec!["Suffixed", "Qualified", "Targeted", "SharesABracket", "EveryFormAtOnce"],
            "the last dotted segment is compared with and without the Attribute suffix, per attribute node, not per bracket pair"
        );
}

#[test]
fn test_coverage_nunit_attributes_match_without_a_test_fixture_on_the_class() {
    let e = extract_src(
            "namespace App.Tests;\n\npublic class OrderServiceTests\n{\n  [Test]\n  public void Renders() { }\n\n  [TestCase(1, 2)]\n  public void Adds(int a, int b) { }\n\n  [TestCaseSource(nameof(Cases))]\n  public void Divides(int a) { }\n}\n",
        );
    let d = find_def(&e, "App.Tests.OrderServiceTests").expect("OrderServiceTests def present");
    assert_eq!(
        d.test_methods,
        vec!["Renders", "Adds", "Divides"],
        "NUnit makes the class attribute optional, so requiring one would drop real tests"
    );
}

#[test]
fn test_coverage_lifecycle_data_source_and_class_container_attributes_never_mark_a_method() {
    let e = extract_src(
            "namespace App.Tests;\n\npublic class NotTests\n{\n  [SetUp]\n  public void Prepare() { }\n\n  [TearDown]\n  public void Cleanup() { }\n\n  [OneTimeSetUp]\n  public void Once() { }\n\n  [OneTimeTearDown]\n  public void Finally() { }\n\n  [TestInitialize]\n  public void Init() { }\n\n  [TestCleanup]\n  public void Done() { }\n\n  [InlineData(1)]\n  public void OnlyInline(int n) { }\n\n  [MemberData(nameof(Cases))]\n  public void OnlyMember(int n) { }\n\n  [ClassData(typeof(Cases))]\n  public void OnlyClassData(int n) { }\n\n  [DataRow(1)]\n  public void OnlyRow(int n) { }\n\n  [DynamicData(nameof(Cases))]\n  public void OnlyDynamic(int n) { }\n\n  [TestFixture]\n  public void FixtureOnAMethod() { }\n\n  [TestClass]\n  public void ClassMarkerOnAMethod() { }\n}\n",
        );
    let d = find_def(&e, "App.Tests.NotTests").expect("NotTests def present");
    assert!(d.test_methods.is_empty());
    assert_eq!(
            def_json_keys(d),
            vec!["id", "name", "namespace", "kind", "line", "methods"],
            "not one of these marks a test, so the key is absent entirely and the def keeps its pre-stage bytes"
        );
}

#[test]
fn test_coverage_mstest_methods_count_only_inside_a_test_class() {
    let source = |class_attribute: &str| {
        format!(
                "namespace App.Tests;\n\n{class_attribute}public class CartTests\n{{\n  [TestMethod]\n  public void Places() {{ }}\n\n  [DataTestMethod]\n  [DataRow(1)]\n  public void Prices(int n) {{ }}\n}}\n"
            )
    };
    let ungated = extract_src(&source(""));
    assert!(
            find_def(&ungated, "App.Tests.CartTests").expect("CartTests def present").test_methods.is_empty(),
            "MSTest does not discover a [TestMethod] whose class lacks [TestClass] -- neither does this"
        );

    let gated = extract_src(&source("[TestClass]\n"));
    assert_eq!(
        find_def(&gated, "App.Tests.CartTests")
            .expect("CartTests def present")
            .test_methods,
        vec!["Places", "Prices"]
    );
}

#[test]
fn test_coverage_a_nested_type_does_not_inherit_an_enclosing_test_class() {
    let e = extract_src(
            "namespace App.Tests;\n\n[TestClass]\npublic class OuterTests\n{\n  [TestMethod]\n  public void Outer() { }\n\n  public class Inner\n  {\n    [TestMethod]\n    public void Nested() { }\n  }\n}\n",
        );
    assert_eq!(
        find_def(&e, "App.Tests.OuterTests")
            .expect("OuterTests def present")
            .test_methods,
        vec!["Outer"]
    );
    assert!(
        find_def(&e, "App.Tests.OuterTests+Inner")
            .expect("Inner def present")
            .test_methods
            .is_empty(),
        "each type computes its own list from its OWN attribute_list"
    );
}

#[test]
fn test_coverage_an_interface_or_enum_body_never_emits_test_methods() {
    let e = extract_src(
            "namespace App.Tests;\n\npublic interface ITestContract\n{\n  [Fact]\n  void Runs();\n}\n\npublic enum Speed\n{\n  Fast,\n  Slow,\n}\n",
        );
    assert!(find_def(&e, "App.Tests.ITestContract")
        .expect("interface def present")
        .test_methods
        .is_empty());
    assert!(find_def(&e, "App.Tests.Speed")
        .expect("enum def present")
        .test_methods
        .is_empty());
    assert!(find_def(&e, "App.Tests.Speed.Fast")
        .expect("enum-member def present")
        .test_methods
        .is_empty());
}

#[test]
fn test_coverage_a_struct_and_a_record_carry_test_methods_too_and_a_local_function_never_does() {
    let e = extract_src(
            "namespace App.Tests;\n\npublic struct ValueTests\n{\n  [Fact]\n  public void Holds() { }\n}\n\npublic record RecordTests\n{\n  [Fact]\n  public void Keeps()\n  {\n    [Fact]\n    void Inner() { }\n  }\n}\n",
        );
    assert_eq!(
        find_def(&e, "App.Tests.ValueTests")
            .expect("struct def present")
            .test_methods,
        vec!["Holds"]
    );
    assert_eq!(
        find_def(&e, "App.Tests.RecordTests")
            .expect("record def present")
            .test_methods,
        vec!["Keeps"],
        "a local function is not a method_declaration at type body level"
    );
}