From e8dd67d6500f56d5571b29bc34501d1bc07ddc34 Mon Sep 17 00:00:00 2001 From: doorgan Date: Tue, 3 Mar 2026 13:52:45 -0300 Subject: [PATCH] fix: only index tests if ExUnit.Case is in scope --- .../search/indexer/extractors/ex_unit.ex | 77 +++++++++++++------ .../indexer/extractors/ex_unit_test.exs | 71 ++++++++++++++++- 2 files changed, 124 insertions(+), 24 deletions(-) diff --git a/apps/engine/lib/engine/search/indexer/extractors/ex_unit.ex b/apps/engine/lib/engine/search/indexer/extractors/ex_unit.ex index 35c2451f..c0abd595 100644 --- a/apps/engine/lib/engine/search/indexer/extractors/ex_unit.ex +++ b/apps/engine/lib/engine/search/indexer/extractors/ex_unit.ex @@ -12,45 +12,76 @@ defmodule Engine.Search.Indexer.Extractors.ExUnit do # setup block i.e. setup do... or setup arg do... def extract({setup_fn, _, args} = setup, %Reducer{} = reducer) when setup_fn in [:setup, :setup_all] and length(args) > 0 do - {:ok, module} = Analyzer.current_module(reducer.analysis, Reducer.position(reducer)) - arity = arity_for(args) - subject = Formats.mfa(module, setup_fn, arity) - setup_type = :"ex_unit_#{setup_fn}" - - case Metadata.location(setup) do - {:block, _, _, _} -> block_entry(reducer, setup, setup_type, subject) - {:expression, _} -> expression_entry(reducer, setup, setup_type, subject) + position = Reducer.position(reducer) + + with true <- exunit_in_scope?(reducer, position), + {:ok, module} <- Analyzer.current_module(reducer.analysis, position) do + arity = arity_for(args) + subject = Formats.mfa(module, setup_fn, arity) + setup_type = :"ex_unit_#{setup_fn}" + + case Metadata.location(setup) do + {:block, _, _, _} -> block_entry(reducer, setup, setup_type, subject) + {:expression, _} -> expression_entry(reducer, setup, setup_type, subject) + end + else + _ -> :ignored end end # Test block test "test name" do ... or test "test name", arg do def extract({:test, _, [{_, _, [test_name]} | _] = args} = test, %Reducer{} = reducer) when is_binary(test_name) do - {:ok, module} = Analyzer.current_module(reducer.analysis, Reducer.position(reducer)) - arity = arity_for(args) - module_name = Formats.module(module) - subject = "#{module_name}.[\"#{test_name}\"]/#{arity}" - - case Metadata.location(test) do - {:block, _, _, _} -> block_entry(reducer, test, :ex_unit_test, subject) - {:expression, _} -> expression_entry(reducer, test, :ex_unit_test, subject) + position = Reducer.position(reducer) + + with true <- exunit_in_scope?(reducer, position), + {:ok, module} <- Analyzer.current_module(reducer.analysis, position) do + arity = arity_for(args) + module_name = Formats.module(module) + subject = "#{module_name}.[\"#{test_name}\"]/#{arity}" + + case Metadata.location(test) do + {:block, _, _, _} -> block_entry(reducer, test, :ex_unit_test, subject) + {:expression, _} -> expression_entry(reducer, test, :ex_unit_test, subject) + end + else + _ -> :ignored end end # describe blocks - def extract({:describe, _, [{_, _, [describe_name]} | _] = args} = test, %Reducer{} = reducer) do - {:ok, module} = Analyzer.current_module(reducer.analysis, Reducer.position(reducer)) - arity = arity_for(args) - module_name = Formats.module(module) - subject = "#{module_name}[\"#{describe_name}\"]/#{arity}" - - block_entry(reducer, test, :ex_unit_describe, subject) + def extract({:describe, _, [{_, _, [describe_name]} | _] = args} = test, %Reducer{} = reducer) + when is_binary(describe_name) do + position = Reducer.position(reducer) + + with true <- exunit_in_scope?(reducer, position), + {:ok, module} <- Analyzer.current_module(reducer.analysis, position) do + arity = arity_for(args) + module_name = Formats.module(module) + subject = "#{module_name}[\"#{describe_name}\"]/#{arity}" + + block_entry(reducer, test, :ex_unit_describe, subject) + else + _ -> :ignored + end end def extract(_ign, _) do :ignored end + defp exunit_in_scope?(%Reducer{} = reducer, %Position{} = position) do + ExUnit.Case in Analyzer.uses_at(reducer.analysis, position) or + ExUnit.Case in Analyzer.requires_at(reducer.analysis, position) or + exunit_imported?(reducer, position) + end + + defp exunit_imported?(%Reducer{} = reducer, %Position{} = position) do + Enum.any?(Analyzer.imports_at(reducer.analysis, position), fn {mod, _, _} -> + mod == ExUnit.Case + end) + end + defp expression_entry(%Reducer{} = reducer, ast, type, subject) do path = reducer.analysis.document.path block = Reducer.current_block(reducer) diff --git a/apps/engine/test/engine/search/indexer/extractors/ex_unit_test.exs b/apps/engine/test/engine/search/indexer/extractors/ex_unit_test.exs index 52e45f6f..434d98c5 100644 --- a/apps/engine/test/engine/search/indexer/extractors/ex_unit_test.exs +++ b/apps/engine/test/engine/search/indexer/extractors/ex_unit_test.exs @@ -29,6 +29,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do {:ok, [setup], doc} = ~q[ defmodule SomeTest do + use ExUnit.Case setup do :ok end @@ -46,6 +47,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do {:ok, [setup], doc} = ~q[ defmodule SomeTest do + use ExUnit.Case setup arg do :ok end @@ -63,6 +65,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do {:ok, [setup], doc} = ~q[ defmodule SomeTest do + use ExUnit.Case setup :other_function end ] @@ -78,6 +81,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do {:ok, [setup], doc} = ~q{ defmodule SomeTest do + use ExUnit.Case setup [:other_function, :second_function] end } @@ -93,6 +97,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do {:ok, [setup], doc} = ~q[ defmodule SomeTest do + use ExUnit.Case setup {OtherModule, :setup} end ] @@ -108,6 +113,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do {:ok, [test], _doc} = ~q[ defmodule SomeTest do + use ExUnit.Case test "something" do setup = 3 setup @@ -125,6 +131,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do {:ok, [setup], doc} = ~q[ defmodule SomeTest do + use ExUnit.Case setup_all do :ok end @@ -142,6 +149,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do {:ok, [setup], doc} = ~q[ defmodule SomeTest do + use ExUnit.Case setup_all arg do :ok end @@ -159,6 +167,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do {:ok, [setup], doc} = ~q[ defmodule SomeTest do + use ExUnit.Case setup_all :other_function end ] @@ -175,6 +184,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do {:ok, [setup], doc} = ~q{ defmodule SomeTest do + use ExUnit.Case setup_all [:other_function, :second_function] end } @@ -191,6 +201,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do {:ok, [setup], doc} = ~q[ defmodule SomeTest do + use ExUnit.Case setup_all {OtherModule, :setup} end ] @@ -209,6 +220,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do {:ok, [describe], doc} = ~q[ defmodule SomeTest do + use ExUnit.Case describe "something" do end end @@ -225,6 +237,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do {:ok, [describe, _test], doc} = ~q[ defmodule SomeTest do + use ExUnit.Case describe "something" do test "something" end @@ -247,6 +260,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do {:ok, [test], doc} = ~q[ defmodule SomeTest do + use ExUnit.Case test "my test" end ] @@ -263,6 +277,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do {:ok, [test], doc} = ~q[ defmodule SomeTest do + use ExUnit.Case test "my test" do end end @@ -280,6 +295,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do {:ok, [test], doc} = ~q[ defmodule SomeTest do + use ExUnit.Case test "my test", context do end end @@ -299,9 +315,10 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do describe "block structure" do test "describe contains tests" do - {:ok, [module, describe, test], _} = + {:ok, all_entries, _} = ~q[ defmodule SomeTexst do + use ExUnit.Case describe "outer" do test "my test", context do end @@ -310,6 +327,8 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do ] |> index_with_structure() + [module, describe, test] = Enum.filter(all_entries, &(&1.subtype == :definition)) + assert module.type == :module assert module.block_id == :root @@ -340,6 +359,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do assert {:ok, [], _doc} = ~q[ defmodule SomeTest do + use ExUnit.Case test "my test" do assert true end @@ -347,4 +367,53 @@ defmodule Engine.Search.Indexer.Extractors.ExUnitTest do |> index_definitions() end end + + describe "when ExUnit.Case is in scope" do + test "indexes test/describe/setup" do + {:ok, entries, _doc} = + ~q[ + defmodule SomeTest do + use ExUnit.Case + + setup do + :ok + end + + describe "some group" do + test "my test" do + :ok + end + end + end + ] + |> index_definitions() + + types = Enum.map(entries, & &1.type) + assert :ex_unit_setup in types + assert :ex_unit_describe in types + assert :ex_unit_test in types + end + end + + describe "when ExUnit.Case is not in scope" do + test "does not index" do + {:ok, entries, _doc} = + ~q[ + defmodule NotATest do + def setup(arg), do: arg + + def describe(name) do + name + end + + def test(name, _context) do + name + end + end + ] + |> index_definitions() + + assert entries == [] + end + end end