diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-11 18:10:20 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-11 18:10:20 +0300 |
commit | e3042fc5ced749e693ccef81b3f5838c55d5480c (patch) | |
tree | e004dca26da0ec413d5c9ebf174962a008fde0bb /spec/lib/banzai | |
parent | c33a9adb709ffb40f816e66eb0c98cc750d6cd43 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/lib/banzai')
4 files changed, 158 insertions, 98 deletions
diff --git a/spec/lib/banzai/cross_project_reference_spec.rb b/spec/lib/banzai/cross_project_reference_spec.rb index 95b78ceb5d5..60ff15a88e0 100644 --- a/spec/lib/banzai/cross_project_reference_spec.rb +++ b/spec/lib/banzai/cross_project_reference_spec.rb @@ -4,10 +4,12 @@ require 'spec_helper' RSpec.describe Banzai::CrossProjectReference do let(:including_class) { Class.new.include(described_class).new } + let(:reference_cache) { Banzai::Filter::References::ReferenceCache.new(including_class, {})} before do allow(including_class).to receive(:context).and_return({}) allow(including_class).to receive(:parent_from_ref).and_call_original + allow(including_class).to receive(:reference_cache).and_return(reference_cache) end describe '#parent_from_ref' do @@ -47,5 +49,18 @@ RSpec.describe Banzai::CrossProjectReference do expect(including_class.parent_from_ref('cross/reference')).to eq project2 end end + + context 'when reference cache is loaded' do + let(:project2) { double('referenced project') } + + before do + allow(reference_cache).to receive(:cache_loaded?).and_return(true) + allow(reference_cache).to receive(:parent_per_reference).and_return({ 'cross/reference' => project2 }) + end + + it 'pulls from the reference cache' do + expect(including_class.parent_from_ref('cross/reference')).to eq project2 + end + end end end diff --git a/spec/lib/banzai/filter/references/abstract_reference_filter_spec.rb b/spec/lib/banzai/filter/references/abstract_reference_filter_spec.rb index d10b52bf7d0..3cb3ebc42a6 100644 --- a/spec/lib/banzai/filter/references/abstract_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/references/abstract_reference_filter_spec.rb @@ -8,18 +8,6 @@ RSpec.describe Banzai::Filter::References::AbstractReferenceFilter do let(:doc) { Nokogiri::HTML.fragment('') } let(:filter) { described_class.new(doc, project: project) } - describe '#references_per_parent' do - let(:doc) { Nokogiri::HTML.fragment("#1 #{project.full_path}#2 #2") } - - it 'returns a Hash containing references grouped per parent paths' do - expect(described_class).to receive(:object_class).exactly(6).times.and_return(Issue) - - refs = filter.references_per_parent - - expect(refs).to match(a_hash_including(project.full_path => contain_exactly(1, 2))) - end - end - describe '#data_attributes_for' do let_it_be(:issue) { create(:issue, project: project) } @@ -32,74 +20,6 @@ RSpec.describe Banzai::Filter::References::AbstractReferenceFilter do end end - describe '#parent_per_reference' do - it 'returns a Hash containing projects grouped per parent paths' do - expect(filter).to receive(:references_per_parent) - .and_return({ project.full_path => Set.new([1]) }) - - expect(filter.parent_per_reference) - .to eq({ project.full_path => project }) - end - end - - describe '#find_for_paths' do - context 'with RequestStore disabled' do - it 'returns a list of Projects for a list of paths' do - expect(filter.find_for_paths([project.full_path])) - .to eq([project]) - end - - it "return an empty array for paths that don't exist" do - expect(filter.find_for_paths(['nonexistent/project'])) - .to eq([]) - end - end - - context 'with RequestStore enabled', :request_store do - it 'returns a list of Projects for a list of paths' do - expect(filter.find_for_paths([project.full_path])) - .to eq([project]) - end - - context 'when no project with that path exists' do - it 'returns no value' do - expect(filter.find_for_paths(['nonexistent/project'])) - .to eq([]) - end - - it 'adds the ref to the project refs cache' do - project_refs_cache = {} - allow(filter).to receive(:refs_cache).and_return(project_refs_cache) - - filter.find_for_paths(['nonexistent/project']) - - expect(project_refs_cache).to eq({ 'nonexistent/project' => nil }) - end - - context 'when the project refs cache includes nil values' do - before do - # adds { 'nonexistent/project' => nil } to cache - filter.from_ref_cached('nonexistent/project') - end - - it "return an empty array for paths that don't exist" do - expect(filter.find_for_paths(['nonexistent/project'])) - .to eq([]) - end - end - end - end - end - - describe '#current_parent_path' do - it 'returns the path of the current parent' do - doc = Nokogiri::HTML.fragment('') - filter = described_class.new(doc, project: project) - - expect(filter.current_parent_path).to eq(project.full_path) - end - end - context 'abstract methods' do describe '#find_object' do it 'raises NotImplementedError' do diff --git a/spec/lib/banzai/filter/references/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/references/issue_reference_filter_spec.rb index 0e1cb1ade74..88c2494b243 100644 --- a/spec/lib/banzai/filter/references/issue_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/references/issue_reference_filter_spec.rb @@ -470,24 +470,6 @@ RSpec.describe Banzai::Filter::References::IssueReferenceFilter do end end - describe '#records_per_parent' do - context 'using an internal issue tracker' do - it 'returns a Hash containing the issues per project' do - doc = Nokogiri::HTML.fragment('') - filter = described_class.new(doc, project: project) - - expect(filter).to receive(:parent_per_reference) - .and_return({ project.full_path => project }) - - expect(filter).to receive(:references_per_parent) - .and_return({ project.full_path => Set.new([issue.iid]) }) - - expect(filter.records_per_parent) - .to eq({ project => { issue.iid => issue } }) - end - end - end - describe '.references_in' do let(:merge_request) { create(:merge_request) } diff --git a/spec/lib/banzai/filter/references/reference_cache_spec.rb b/spec/lib/banzai/filter/references/reference_cache_spec.rb new file mode 100644 index 00000000000..2e37e34bba5 --- /dev/null +++ b/spec/lib/banzai/filter/references/reference_cache_spec.rb @@ -0,0 +1,143 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Banzai::Filter::References::ReferenceCache do + let_it_be(:project) { create(:project) } + let_it_be(:project2) { create(:project) } + let_it_be(:issue1) { create(:issue, project: project) } + let_it_be(:issue2) { create(:issue, project: project) } + let_it_be(:issue3) { create(:issue, project: project2) } + let_it_be(:doc) { Nokogiri::HTML.fragment("#{issue1.to_reference} #{issue2.to_reference} #{issue3.to_reference(full: true)}") } + + let(:filter_class) { Banzai::Filter::References::IssueReferenceFilter } + let(:filter) { filter_class.new(doc, project: project) } + let(:cache) { described_class.new(filter, { project: project }) } + + describe '#load_references_per_parent' do + it 'loads references grouped per parent paths' do + cache.load_references_per_parent(filter.nodes) + + expect(cache.references_per_parent).to eq({ project.full_path => [issue1.iid, issue2.iid].to_set, + project2.full_path => [issue3.iid].to_set }) + end + end + + describe '#load_parent_per_reference' do + it 'returns a Hash containing projects grouped per parent paths' do + cache.load_references_per_parent(filter.nodes) + cache.load_parent_per_reference + + expect(cache.parent_per_reference).to match({ project.full_path => project, project2.full_path => project2 }) + end + end + + describe '#load_records_per_parent' do + it 'returns a Hash containing projects grouped per parent paths' do + cache.load_references_per_parent(filter.nodes) + cache.load_parent_per_reference + cache.load_records_per_parent + + expect(cache.records_per_parent).to match({ project => { issue1.iid => issue1, issue2.iid => issue2 }, + project2 => { issue3.iid => issue3 } }) + end + end + + describe '#initialize_reference_cache' do + it 'does not have an N+1 query problem with cross projects' do + doc_single = Nokogiri::HTML.fragment("#1") + filter_single = filter_class.new(doc_single, project: project) + cache_single = described_class.new(filter_single, { project: project }) + + control_count = ActiveRecord::QueryRecorder.new do + cache_single.load_references_per_parent(filter_single.nodes) + cache_single.load_parent_per_reference + cache_single.load_records_per_parent + end.count + + # Since this is an issue filter that is not batching issue queries + # across projects, we have to account for that. + # 1 for both projects, 1 for issues in each project == 3 + max_count = control_count + 1 + + expect do + cache.load_references_per_parent(filter.nodes) + cache.load_parent_per_reference + cache.load_records_per_parent + end.not_to exceed_query_limit(max_count) + end + end + + describe '#find_for_paths' do + context 'with RequestStore disabled' do + it 'returns a list of Projects for a list of paths' do + expect(cache.find_for_paths([project.full_path])) + .to eq([project]) + end + + it 'return an empty array for paths that do not exist' do + expect(cache.find_for_paths(['nonexistent/project'])) + .to eq([]) + end + end + + context 'with RequestStore enabled', :request_store do + it 'returns a list of Projects for a list of paths' do + expect(cache.find_for_paths([project.full_path])) + .to eq([project]) + end + + context 'when no project with that path exists' do + it 'returns no value' do + expect(cache.find_for_paths(['nonexistent/project'])) + .to eq([]) + end + + it 'adds the ref to the project refs cache' do + project_refs_cache = {} + allow(cache).to receive(:refs_cache).and_return(project_refs_cache) + + cache.find_for_paths(['nonexistent/project']) + + expect(project_refs_cache).to eq({ 'nonexistent/project' => nil }) + end + end + end + end + + describe '#current_parent_path' do + it 'returns the path of the current parent' do + expect(cache.current_parent_path).to eq project.full_path + end + end + + describe '#current_project_namespace_path' do + it 'returns the path of the current project namespace' do + expect(cache.current_project_namespace_path).to eq project.namespace.full_path + end + end + + describe '#full_project_path' do + it 'returns current parent path when no ref specified' do + expect(cache.full_project_path('something', nil)).to eq cache.current_parent_path + end + + it 'returns combined namespace and project ref' do + expect(cache.full_project_path('something', 'cool')).to eq 'something/cool' + end + + it 'returns uses default namespace and project ref when namespace nil' do + expect(cache.full_project_path(nil, 'cool')).to eq "#{project.namespace.full_path}/cool" + end + end + + describe '#full_group_path' do + it 'returns current parent path when no group ref specified' do + expect(cache.full_group_path(nil)).to eq cache.current_parent_path + end + + it 'returns group ref' do + expect(cache.full_group_path('cool_group')).to eq 'cool_group' + end + end +end |