Commit 8cc25e55 authored by Kerri Miller's avatar Kerri Miller

Add #sectional_entry_for_path for path

Additionally extracts tests into shared_examples and standardizes the
response as an array, rather than a bare entry or nil.
parent 69b4dba6
...@@ -19,13 +19,30 @@ module Gitlab ...@@ -19,13 +19,30 @@ module Gitlab
end end
def entry_for_path(path) def entry_for_path(path)
return sectional_entry_for_path(path) if sectional_codeowners?
path = "/#{path}" unless path.start_with?('/') path = "/#{path}" unless path.start_with?('/')
matching_pattern = parsed_data.keys.reverse.detect do |pattern| matching_pattern = parsed_data.keys.reverse.detect do |pattern|
path_matches?(pattern, path) path_matches?(pattern, path)
end end
parsed_data[matching_pattern].dup if matching_pattern matching_pattern ? [parsed_data[matching_pattern].dup] : []
end
def sectional_entry_for_path(path)
path = "/#{path}" unless path.start_with?('/')
matches = []
parsed_data.each do |_, section_entries|
matching_pattern = section_entries.keys.reverse.detect do |pattern|
path_matches?(pattern, path)
end
matches << section_entries[matching_pattern].dup if matching_pattern
end
matches
end end
def path def path
......
...@@ -143,6 +143,7 @@ describe Gitlab::CodeOwners::File do ...@@ -143,6 +143,7 @@ describe Gitlab::CodeOwners::File do
end end
describe '#entry_for_path' do describe '#entry_for_path' do
shared_examples_for "returns expected matches" do
context 'for a path without matches' do context 'for a path without matches' do
let(:file_content) do let(:file_content) do
<<~CONTENT <<~CONTENT
...@@ -150,81 +151,82 @@ describe Gitlab::CodeOwners::File do ...@@ -150,81 +151,82 @@ describe Gitlab::CodeOwners::File do
CONTENT CONTENT
end end
it 'returns an nil for an unmatched path' do it 'returns an empty array for an unmatched path' do
entry = file.entry_for_path('no_matches') entry = file.entry_for_path('no_matches')
expect(entry).to be_nil expect(entry).to be_a Array
expect(entry).to be_empty
end end
end end
it 'matches random files to a pattern' do it 'matches random files to a pattern' do
entry = file.entry_for_path('app/assets/something.vue') entry = file.entry_for_path('app/assets/something.vue').first
expect(entry.pattern).to eq('*') expect(entry.pattern).to eq('*')
expect(entry.owner_line).to include('default-codeowner') expect(entry.owner_line).to include('default-codeowner')
end end
it 'uses the last pattern if multiple patterns match' do it 'uses the last pattern if multiple patterns match' do
entry = file.entry_for_path('hello.rb') entry = file.entry_for_path('hello.rb').first
expect(entry.pattern).to eq('*.rb') expect(entry.pattern).to eq('*.rb')
expect(entry.owner_line).to eq('@ruby-owner') expect(entry.owner_line).to eq('@ruby-owner')
end end
it 'returns the usernames for a file matching a pattern with a glob' do it 'returns the usernames for a file matching a pattern with a glob' do
entry = file.entry_for_path('app/models/repository.rb') entry = file.entry_for_path('app/models/repository.rb').first
expect(entry.owner_line).to eq('@ruby-owner') expect(entry.owner_line).to eq('@ruby-owner')
end end
it 'allows specifying multiple users' do it 'allows specifying multiple users' do
entry = file.entry_for_path('CODEOWNERS') entry = file.entry_for_path('CODEOWNERS').first
expect(entry.owner_line).to include('multiple', 'owners', 'tab-separated') expect(entry.owner_line).to include('multiple', 'owners', 'tab-separated')
end end
it 'returns emails and usernames for a matched pattern' do it 'returns emails and usernames for a matched pattern' do
entry = file.entry_for_path('LICENSE') entry = file.entry_for_path('LICENSE').first
expect(entry.owner_line).to include('legal', 'janedoe@gitlab.com') expect(entry.owner_line).to include('legal', 'janedoe@gitlab.com')
end end
it 'allows escaping the pound sign used for comments' do it 'allows escaping the pound sign used for comments' do
entry = file.entry_for_path('examples/#file_with_pound.rb') entry = file.entry_for_path('examples/#file_with_pound.rb').first
expect(entry.owner_line).to include('owner-file-with-pound') expect(entry.owner_line).to include('owner-file-with-pound')
end end
it 'returns the usernames for a file nested in a directory' do it 'returns the usernames for a file nested in a directory' do
entry = file.entry_for_path('docs/projects/index.md') entry = file.entry_for_path('docs/projects/index.md').first
expect(entry.owner_line).to include('all-docs') expect(entry.owner_line).to include('all-docs')
end end
it 'returns the usernames for a pattern matched with a glob in a folder' do it 'returns the usernames for a pattern matched with a glob in a folder' do
entry = file.entry_for_path('docs/index.md') entry = file.entry_for_path('docs/index.md').first
expect(entry.owner_line).to include('root-docs') expect(entry.owner_line).to include('root-docs')
end end
it 'allows matching files nested anywhere in the repository', :aggregate_failures do it 'allows matching files nested anywhere in the repository', :aggregate_failures do
lib_entry = file.entry_for_path('lib/gitlab/git/repository.rb') lib_entry = file.entry_for_path('lib/gitlab/git/repository.rb').first
other_lib_entry = file.entry_for_path('ee/lib/gitlab/git/repository.rb') other_lib_entry = file.entry_for_path('ee/lib/gitlab/git/repository.rb').first
expect(lib_entry.owner_line).to include('lib-owner') expect(lib_entry.owner_line).to include('lib-owner')
expect(other_lib_entry.owner_line).to include('lib-owner') expect(other_lib_entry.owner_line).to include('lib-owner')
end end
it 'allows allows limiting the matching files to the root of the repository', :aggregate_failures do it 'allows allows limiting the matching files to the root of the repository', :aggregate_failures do
config_entry = file.entry_for_path('config/database.yml') config_entry = file.entry_for_path('config/database.yml').first
other_config_entry = file.entry_for_path('other/config/database.yml') other_config_entry = file.entry_for_path('other/config/database.yml').first
expect(config_entry.owner_line).to include('config-owner') expect(config_entry.owner_line).to include('config-owner')
expect(other_config_entry.owner_line).to eq('@default-codeowner') expect(other_config_entry.owner_line).to eq('@default-codeowner')
end end
it 'correctly matches paths with spaces' do it 'correctly matches paths with spaces' do
entry = file.entry_for_path('path with spaces/README.md') entry = file.entry_for_path('path with spaces/README.md').first
expect(entry.owner_line).to eq('@space-owner') expect(entry.owner_line).to eq('@space-owner')
end end
...@@ -235,7 +237,7 @@ describe Gitlab::CodeOwners::File do ...@@ -235,7 +237,7 @@ describe Gitlab::CodeOwners::File do
end end
it 'parses correctly' do it 'parses correctly' do
entry = file.entry_for_path('a/weird path with/ @username / and-email@lookalikes.com /test.rb') entry = file.entry_for_path('a/weird path with/ @username / and-email@lookalikes.com /test.rb').first
expect(entry.owner_line).to include('user-1', 'user-2', 'email@gitlab.org') expect(entry.owner_line).to include('user-1', 'user-2', 'email@gitlab.org')
expect(entry.owner_line).not_to include('username', 'and-email@lookalikes.com') expect(entry.owner_line).not_to include('username', 'and-email@lookalikes.com')
...@@ -248,17 +250,16 @@ describe Gitlab::CodeOwners::File do ...@@ -248,17 +250,16 @@ describe Gitlab::CodeOwners::File do
end end
it 'matches files in the root directory' do it 'matches files in the root directory' do
entry = file.entry_for_path('README.md') entry = file.entry_for_path('README.md').first
expect(entry.owner_line).to include('user-1', 'user-2') expect(entry.owner_line).to include('user-1', 'user-2')
end end
it 'does not match nested files' do it 'does not match nested files' do
entry = file.entry_for_path('nested/path/README.md') entry = file.entry_for_path('nested/path/README.md').first
expect(entry).to be_nil expect(entry).to be_nil
end end
end
context 'partial matches' do context 'partial matches' do
let(:file_content) do let(:file_content) do
...@@ -266,15 +267,33 @@ describe Gitlab::CodeOwners::File do ...@@ -266,15 +267,33 @@ describe Gitlab::CodeOwners::File do
end end
it 'does not match a file in a folder that looks the same' do it 'does not match a file in a folder that looks the same' do
entry = file.entry_for_path('fufoo/bar') entry = file.entry_for_path('fufoo/bar').first
expect(entry).to be_nil expect(entry).to be_nil
end end
it 'matches the file in any folder' do it 'matches the file in any folder' do
expect(file.entry_for_path('baz/foo/bar').owner_line).to include('user-1', 'user-2') expect(file.entry_for_path('baz/foo/bar').first.owner_line).to include('user-1', 'user-2')
expect(file.entry_for_path('/foo/bar').owner_line).to include('user-1', 'user-2') expect(file.entry_for_path('/foo/bar').first.owner_line).to include('user-1', 'user-2')
end
end
end
end end
context "when feature flag `:sectional_codeowners` is enabled" do
before do
stub_feature_flags(sectional_codeowners: true)
end
it_behaves_like "returns expected matches"
end
context "when feature flag `:sectional_codeowners` is disabled" do
before do
stub_feature_flags(sectional_codeowners: false)
end
it_behaves_like "returns expected matches"
end end
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment