require_relative "test_helper" require "json" class FinderTest < Minitest::Test include AdrTestHelpers def adr(id:, title:, status: "Accepted", paths: [], components: []) <<~MD --- id: "#{id}" date: 2026-07-03 status: #{status} supersedes: superseded-by: affected-paths: [#{paths.join(', ')}] affected-components: [#{components.join(', ')}] --- # #{id} — #{title} ## Context c ## Decision d ## Consequences q ## Alternatives rejected a MD end def storage_project(&block) with_project( "0001-storage.md" => adr(id: "0001", title: "Storage", paths: %w[lib/storage db/], components: %w[storage]), "0002-old-storage.md" => adr(id: "0002", title: "Old storage", status: "Superseded", paths: %w[lib/storage]), "0003-auth.md" => adr(id: "0003", title: "Auth", paths: %w[lib/auth], components: %w[auth]), &block ) end def finder(root, graph: nil) Adr::Finder.new(repository: Adr::Repository.new(root: root), graph_path: graph || File.join(root, "graphify-out", "graph.json")) end def test_path_prefix_match_narrows storage_project do |root| results = finder(root).find(paths: ["lib/storage/adapter.rb"]) assert_equal ["0001"], results.map { |r| r["id"] } assert_equal ["path"], results.first["matched_on"] end end def test_directory_query_matches_file_scoped_adr storage_project do |root| results = finder(root).find(paths: ["db"]) assert_equal ["0001"], results.map { |r| r["id"] } end end def test_component_match storage_project do |root| results = finder(root).find(components: ["auth"]) assert_equal ["0003"], results.map { |r| r["id"] } assert_equal ["component"], results.first["matched_on"] end end def test_accepted_only_by_default_with_override storage_project do |root| default = finder(root).find(paths: ["lib/storage"]) assert_equal ["0001"], default.map { |r| r["id"] } all = finder(root).find(paths: ["lib/storage"], all_statuses: true) assert_equal %w[0001 0002], all.map { |r| r["id"] }.sort end end def test_no_match_returns_empty storage_project do |root| assert_empty finder(root).find(paths: ["src/ui"]) end end GRAPH = { "nodes" => [ { "id" => "n1", "source_file" => "src/api/handler.rb" }, { "id" => "n2", "source_file" => "lib/storage/adapter.rb" }, { "id" => "n3", "source_file" => "lib/auth/session.rb" } ], "links" => [ { "source" => "n1", "target" => "n2" } ] }.freeze def test_graph_layer_expands_one_hop storage_project do |root| graph_dir = File.join(root, "graphify-out") FileUtils.mkdir_p(graph_dir) File.write(File.join(graph_dir, "graph.json"), JSON.generate(GRAPH)) # src/api/handler.rb is in no ADR's affected-paths, but is one hop from # lib/storage/adapter.rb which 0001 covers. results = finder(root).find(paths: ["src/api/handler.rb"]) assert_equal ["0001"], results.map { |r| r["id"] } assert_equal ["graph"], results.first["matched_on"] end end def test_degrades_gracefully_without_graph storage_project do |root| assert_empty finder(root).find(paths: ["src/api/handler.rb"]) assert_equal ["0001"], finder(root).find(paths: ["lib/storage"]).map { |r| r["id"] } end end def test_degrades_gracefully_on_corrupt_graph storage_project do |root| graph_dir = File.join(root, "graphify-out") FileUtils.mkdir_p(graph_dir) File.write(File.join(graph_dir, "graph.json"), "{not json") assert_equal ["0001"], finder(root).find(paths: ["lib/storage"]).map { |r| r["id"] } end end end