diff options
author | Ruben Davila <rdavila84@gmail.com> | 2017-05-02 21:20:41 +0300 |
---|---|---|
committer | Ruben Davila <rdavila84@gmail.com> | 2017-05-02 21:20:41 +0300 |
commit | bbfbcebdf69261bac6aa142f430719955e8e86b9 (patch) | |
tree | 25916a1a761cb6c8fcdac4b20b76017a7f4506d3 /spec/lib | |
parent | 3b82444eb7791e58e3e0ba2f08b8ccde48e3d4c6 (diff) | |
parent | 920d55b9f8afd35e16351fb57d671acf66092e89 (diff) |
Merge branch 'master' into 28433-internationalise-cycle-analytics-page
Diffstat (limited to 'spec/lib')
38 files changed, 1104 insertions, 306 deletions
diff --git a/spec/lib/banzai/filter/emoji_filter_spec.rb b/spec/lib/banzai/filter/emoji_filter_spec.rb index 707212e07fd..086a006c45f 100644 --- a/spec/lib/banzai/filter/emoji_filter_spec.rb +++ b/spec/lib/banzai/filter/emoji_filter_spec.rb @@ -68,9 +68,9 @@ describe Banzai::Filter::EmojiFilter, lib: true do expect(doc.css('gl-emoji').size).to eq 1 end - it 'matches multiple emoji in a row' do + it 'does not match multiple emoji in a row' do doc = filter(':see_no_evil::hear_no_evil::speak_no_evil:') - expect(doc.css('gl-emoji').size).to eq 3 + expect(doc.css('gl-emoji').size).to eq 0 end it 'unicode matches multiple emoji in a row' do @@ -83,6 +83,12 @@ describe Banzai::Filter::EmojiFilter, lib: true do expect(doc.css('gl-emoji').size).to eq 6 end + it 'does not match emoji in a string' do + doc = filter("'2a00:a4c0:100::1'") + + expect(doc.css('gl-emoji').size).to eq 0 + end + it 'has a data-name attribute' do doc = filter(':-1:') expect(doc.css('gl-emoji').first.attr('data-name')).to eq 'thumbsdown' diff --git a/spec/lib/banzai/filter/issuable_state_filter_spec.rb b/spec/lib/banzai/filter/issuable_state_filter_spec.rb index 0df259333e4..9c2399815b9 100644 --- a/spec/lib/banzai/filter/issuable_state_filter_spec.rb +++ b/spec/lib/banzai/filter/issuable_state_filter_spec.rb @@ -6,11 +6,23 @@ describe Banzai::Filter::IssuableStateFilter, lib: true do let(:user) { create(:user) } let(:context) { { current_user: user, issuable_state_filter_enabled: true } } + let(:closed_issue) { create_issue(:closed) } + let(:project) { create(:empty_project, :public) } + let(:other_project) { create(:empty_project, :public) } def create_link(text, data) link_to(text, '', class: 'gfm has-tooltip', data: data) end + def create_issue(state) + create(:issue, state, project: project) + end + + def create_merge_request(state) + create(:merge_request, state, + source_project: project, target_project: project) + end + it 'ignores non-GFM links' do html = %(See <a href="https://google.com/">Google</a>) doc = filter(html, current_user: user) @@ -19,7 +31,6 @@ describe Banzai::Filter::IssuableStateFilter, lib: true do end it 'ignores non-issuable links' do - project = create(:empty_project, :public) link = create_link('text', project: project, reference_type: 'issue') doc = filter(link, context) @@ -27,100 +38,160 @@ describe Banzai::Filter::IssuableStateFilter, lib: true do end it 'ignores issuable links with empty content' do - issue = create(:issue, :closed) - link = create_link('', issue: issue.id, reference_type: 'issue') + link = create_link('', issue: closed_issue.id, reference_type: 'issue') doc = filter(link, context) expect(doc.css('a').last.text).to eq('') end - it 'adds text with standard formatting' do - issue = create(:issue, :closed) + it 'ignores issuable links with custom anchor' do + link = create_link('something', issue: closed_issue.id, reference_type: 'issue') + doc = filter(link, context) + + expect(doc.css('a').last.text).to eq('something') + end + + it 'ignores issuable links to specific comments' do + link = create_link("#{closed_issue.to_reference} (comment 1)", issue: closed_issue.id, reference_type: 'issue') + doc = filter(link, context) + + expect(doc.css('a').last.text).to eq("#{closed_issue.to_reference} (comment 1)") + end + + it 'ignores merge request links to diffs tab' do + merge_request = create(:merge_request, :closed) link = create_link( - 'something <strong>else</strong>'.html_safe, - issue: issue.id, - reference_type: 'issue' + "#{merge_request.to_reference} (diffs)", + merge_request: merge_request.id, + reference_type: 'merge_request' ) doc = filter(link, context) - expect(doc.css('a').last.inner_html). - to eq('something <strong>else</strong> [closed]') + expect(doc.css('a').last.text).to eq("#{merge_request.to_reference} (diffs)") + end + + it 'handles cross project references' do + link = create_link(closed_issue.to_reference(other_project), issue: closed_issue.id, reference_type: 'issue') + doc = filter(link, context.merge(project: other_project)) + + expect(doc.css('a').last.text).to eq("#{closed_issue.to_reference(other_project)} (closed)") end it 'does not append state when filter is not enabled' do - issue = create(:issue, :closed) - link = create_link('text', issue: issue.id, reference_type: 'issue') + link = create_link('text', issue: closed_issue.id, reference_type: 'issue') context = { current_user: user } doc = filter(link, context) expect(doc.css('a').last.text).to eq('text') end + context 'when project is in pending delete' do + before do + project.update!(pending_delete: true) + end + + it 'does not append issue state' do + link = create_link('text', issue: closed_issue.id, reference_type: 'issue') + doc = filter(link, context) + + expect(doc.css('a').last.text).to eq('text') + end + end + context 'for issue references' do it 'ignores open issue references' do - issue = create(:issue) - link = create_link('text', issue: issue.id, reference_type: 'issue') + issue = create_issue(:opened) + link = create_link(issue.to_reference, issue: issue.id, reference_type: 'issue') doc = filter(link, context) - expect(doc.css('a').last.text).to eq('text') + expect(doc.css('a').last.text).to eq(issue.to_reference) end it 'ignores reopened issue references' do - reopened_issue = create(:issue, :reopened) - link = create_link('text', issue: reopened_issue.id, reference_type: 'issue') + issue = create_issue(:reopened) + link = create_link(issue.to_reference, issue: issue.id, reference_type: 'issue') doc = filter(link, context) - expect(doc.css('a').last.text).to eq('text') + expect(doc.css('a').last.text).to eq(issue.to_reference) end - it 'appends [closed] to closed issue references' do - closed_issue = create(:issue, :closed) - link = create_link('text', issue: closed_issue.id, reference_type: 'issue') + it 'appends state to closed issue references' do + link = create_link(closed_issue.to_reference, issue: closed_issue.id, reference_type: 'issue') doc = filter(link, context) - expect(doc.css('a').last.text).to eq('text [closed]') + expect(doc.css('a').last.text).to eq("#{closed_issue.to_reference} (closed)") end end context 'for merge request references' do it 'ignores open merge request references' do - mr = create(:merge_request) - link = create_link('text', merge_request: mr.id, reference_type: 'merge_request') + merge_request = create_merge_request(:opened) + + link = create_link( + merge_request.to_reference, + merge_request: merge_request.id, + reference_type: 'merge_request' + ) + doc = filter(link, context) - expect(doc.css('a').last.text).to eq('text') + expect(doc.css('a').last.text).to eq(merge_request.to_reference) end it 'ignores reopened merge request references' do - mr = create(:merge_request, :reopened) - link = create_link('text', merge_request: mr.id, reference_type: 'merge_request') + merge_request = create_merge_request(:reopened) + + link = create_link( + merge_request.to_reference, + merge_request: merge_request.id, + reference_type: 'merge_request' + ) + doc = filter(link, context) - expect(doc.css('a').last.text).to eq('text') + expect(doc.css('a').last.text).to eq(merge_request.to_reference) end it 'ignores locked merge request references' do - mr = create(:merge_request, :locked) - link = create_link('text', merge_request: mr.id, reference_type: 'merge_request') + merge_request = create_merge_request(:locked) + + link = create_link( + merge_request.to_reference, + merge_request: merge_request.id, + reference_type: 'merge_request' + ) + doc = filter(link, context) - expect(doc.css('a').last.text).to eq('text') + expect(doc.css('a').last.text).to eq(merge_request.to_reference) end - it 'appends [closed] to closed merge request references' do - mr = create(:merge_request, :closed) - link = create_link('text', merge_request: mr.id, reference_type: 'merge_request') + it 'appends state to closed merge request references' do + merge_request = create_merge_request(:closed) + + link = create_link( + merge_request.to_reference, + merge_request: merge_request.id, + reference_type: 'merge_request' + ) + doc = filter(link, context) - expect(doc.css('a').last.text).to eq('text [closed]') + expect(doc.css('a').last.text).to eq("#{merge_request.to_reference} (closed)") end - it 'appends [merged] to merged merge request references' do - mr = create(:merge_request, :merged) - link = create_link('text', merge_request: mr.id, reference_type: 'merge_request') + it 'appends state to merged merge request references' do + merge_request = create_merge_request(:merged) + + link = create_link( + merge_request.to_reference, + merge_request: merge_request.id, + reference_type: 'merge_request' + ) + doc = filter(link, context) - expect(doc.css('a').last.text).to eq('text [merged]') + expect(doc.css('a').last.text).to eq("#{merge_request.to_reference} (merged)") end end end diff --git a/spec/lib/banzai/filter/plantuml_filter_spec.rb b/spec/lib/banzai/filter/plantuml_filter_spec.rb index f85a5dcbd8b..9b8ecb201f3 100644 --- a/spec/lib/banzai/filter/plantuml_filter_spec.rb +++ b/spec/lib/banzai/filter/plantuml_filter_spec.rb @@ -5,7 +5,7 @@ describe Banzai::Filter::PlantumlFilter, lib: true do it 'should replace plantuml pre tag with img tag' do stub_application_setting(plantuml_enabled: true, plantuml_url: "http://localhost:8080") - input = '<pre class="plantuml"><code>Bob -> Sara : Hello</code><pre>' + input = '<pre><code lang="plantuml">Bob -> Sara : Hello</code></pre>' output = '<div class="imageblock"><div class="content"><img class="plantuml" src="http://localhost:8080/png/U9npoazIqBLJ24uiIbImKl18pSd91m0rkGMq"></div></div>' doc = filter(input) @@ -14,8 +14,8 @@ describe Banzai::Filter::PlantumlFilter, lib: true do it 'should not replace plantuml pre tag with img tag if disabled' do stub_application_setting(plantuml_enabled: false) - input = '<pre class="plantuml"><code>Bob -> Sara : Hello</code><pre>' - output = '<pre class="plantuml"><code>Bob -> Sara : Hello</code><pre></pre></pre>' + input = '<pre><code lang="plantuml">Bob -> Sara : Hello</code></pre>' + output = '<pre><code lang="plantuml">Bob -> Sara : Hello</code></pre>' doc = filter(input) expect(doc.to_s).to eq output @@ -23,7 +23,7 @@ describe Banzai::Filter::PlantumlFilter, lib: true do it 'should not replace plantuml pre tag with img tag if url is invalid' do stub_application_setting(plantuml_enabled: true, plantuml_url: "invalid") - input = '<pre class="plantuml"><code>Bob -> Sara : Hello</code><pre>' + input = '<pre><code lang="plantuml">Bob -> Sara : Hello</code></pre>' output = '<div class="listingblock"><div class="content"><pre class="plantuml plantuml-error"> PlantUML Error: cannot connect to PlantUML server at "invalid"</pre></div></div>' doc = filter(input) diff --git a/spec/lib/banzai/object_renderer_spec.rb b/spec/lib/banzai/object_renderer_spec.rb index 4817fcd031a..dd2674f9f20 100644 --- a/spec/lib/banzai/object_renderer_spec.rb +++ b/spec/lib/banzai/object_renderer_spec.rb @@ -4,13 +4,13 @@ describe Banzai::ObjectRenderer do let(:project) { create(:empty_project) } let(:user) { project.owner } let(:renderer) { described_class.new(project, user, custom_value: 'value') } - let(:object) { Note.new(note: 'hello', note_html: '<p>hello</p>') } + let(:object) { Note.new(note: 'hello', note_html: '<p dir="auto">hello</p>', cached_markdown_version: CacheMarkdownField::CACHE_VERSION) } describe '#render' do it 'renders and redacts an Array of objects' do renderer.render([object], :note) - expect(object.redacted_note_html).to eq '<p>hello</p>' + expect(object.redacted_note_html).to eq '<p dir="auto">hello</p>' expect(object.user_visible_reference_count).to eq 0 end diff --git a/spec/lib/banzai/redactor_spec.rb b/spec/lib/banzai/redactor_spec.rb index 6d2c141e18b..e6f2963193c 100644 --- a/spec/lib/banzai/redactor_spec.rb +++ b/spec/lib/banzai/redactor_spec.rb @@ -42,6 +42,31 @@ describe Banzai::Redactor do end end + context 'when project is in pending delete' do + let!(:issue) { create(:issue, project: project) } + let(:redactor) { described_class.new(project, user) } + + before do + project.update(pending_delete: true) + end + + it 'redacts an issue attached' do + doc = Nokogiri::HTML.fragment("<a class='gfm' data-reference-type='issue' data-issue='#{issue.id}'>foo</a>") + + redactor.redact([doc]) + + expect(doc.to_html).to eq('foo') + end + + it 'redacts an external issue' do + doc = Nokogiri::HTML.fragment("<a class='gfm' data-reference-type='issue' data-external-issue='#{issue.id}' data-project='#{project.id}'>foo</a>") + + redactor.redact([doc]) + + expect(doc.to_html).to eq('foo') + end + end + context 'when reference visible to user' do it 'does not redact an array of documents' do doc1_html = '<a class="gfm" data-reference-type="issue">foo</a>' diff --git a/spec/lib/banzai/renderer_spec.rb b/spec/lib/banzai/renderer_spec.rb index aaa6b12e67e..e6f8d2a1fed 100644 --- a/spec/lib/banzai/renderer_spec.rb +++ b/spec/lib/banzai/renderer_spec.rb @@ -1,73 +1,36 @@ require 'spec_helper' describe Banzai::Renderer do - def expect_render(project = :project) - expected_context = { project: project } - expect(renderer).to receive(:cacheless_render) { :html }.with(:markdown, expected_context) - end - - def expect_cache_update - expect(object).to receive(:update_column).with("field_html", :html) - end - - def fake_object(*features) - markdown = :markdown if features.include?(:markdown) - html = :html if features.include?(:html) - - object = double( - "object", - banzai_render_context: { project: :project }, - field: markdown, - field_html: html - ) + def fake_object(fresh:) + object = double('object') - allow(object).to receive(:markdown_cache_field_for).with(:field).and_return("field_html") - allow(object).to receive(:new_record?).and_return(features.include?(:new)) - allow(object).to receive(:destroyed?).and_return(features.include?(:destroyed)) + allow(object).to receive(:cached_html_up_to_date?).with(:field).and_return(fresh) + allow(object).to receive(:cached_html_for).with(:field).and_return('field_html') object end - describe "#render_field" do + describe '#render_field' do let(:renderer) { Banzai::Renderer } - let(:subject) { renderer.render_field(object, :field) } + subject { renderer.render_field(object, :field) } - context "with an empty cache" do - let(:object) { fake_object(:markdown) } - it "caches and returns the result" do - expect_render - expect_cache_update - expect(subject).to eq(:html) - end - end + context 'with a stale cache' do + let(:object) { fake_object(fresh: false) } - context "with a filled cache" do - let(:object) { fake_object(:markdown, :html) } + it 'caches and returns the result' do + expect(object).to receive(:refresh_markdown_cache!).with(do_update: true) - it "uses the cache" do - expect_render.never - expect_cache_update.never - should eq(:html) + is_expected.to eq('field_html') end end - context "new object" do - let(:object) { fake_object(:new, :markdown) } - - it "doesn't cache the result" do - expect_render - expect_cache_update.never - expect(subject).to eq(:html) - end - end + context 'with an up-to-date cache' do + let(:object) { fake_object(fresh: true) } - context "destroyed object" do - let(:object) { fake_object(:destroyed, :markdown) } + it 'uses the cache' do + expect(object).to receive(:refresh_markdown_cache!).never - it "doesn't cache the result" do - expect_render - expect_cache_update.never - expect(subject).to eq(:html) + is_expected.to eq('field_html') end end end diff --git a/spec/lib/constraints/group_url_constrainer_spec.rb b/spec/lib/constraints/group_url_constrainer_spec.rb index 96dacdc5cd2..f95adf3a84b 100644 --- a/spec/lib/constraints/group_url_constrainer_spec.rb +++ b/spec/lib/constraints/group_url_constrainer_spec.rb @@ -17,6 +17,13 @@ describe GroupUrlConstrainer, lib: true do it { expect(subject.matches?(request)).to be_truthy } end + context 'valid request for nested group with reserved top level name' do + let!(:nested_group) { create(:group, path: 'api', parent: group) } + let!(:request) { build_request('gitlab/api') } + + it { expect(subject.matches?(request)).to be_truthy } + end + context 'invalid request' do let(:request) { build_request('foo') } diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb index bca57105d1d..0f47fb2fbd9 100644 --- a/spec/lib/gitlab/asciidoc_spec.rb +++ b/spec/lib/gitlab/asciidoc_spec.rb @@ -22,26 +22,9 @@ module Gitlab expect(Asciidoctor).to receive(:convert) .with(input, expected_asciidoc_opts).and_return(html) - expect( render(input, context) ).to eql html + expect(render(input)).to eq(html) end - context "with asciidoc_opts" do - let(:asciidoc_opts) { { safe: :safe, attributes: ['foo'] } } - - it "merges the options with default ones" do - expected_asciidoc_opts = { - safe: :safe, - backend: :gitlab_html5, - attributes: described_class::DEFAULT_ADOC_ATTRS + ['foo'] - } - - expect(Asciidoctor).to receive(:convert) - .with(input, expected_asciidoc_opts).and_return(html) - - render(input, context, asciidoc_opts) - end - end - context "XSS" do links = { 'links' => { @@ -60,7 +43,7 @@ module Gitlab links.each do |name, data| it "does not convert dangerous #{name} into HTML" do - expect(render(data[:input], context)).to eql data[:output] + expect(render(data[:input])).to eq(data[:output]) end end end diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb index 03c4879ed6f..d4a43192d03 100644 --- a/spec/lib/gitlab/auth_spec.rb +++ b/spec/lib/gitlab/auth_spec.rb @@ -118,7 +118,7 @@ describe Gitlab::Auth, lib: true do it 'succeeds for OAuth tokens with the `api` scope' do expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'oauth2') - expect(gl_auth.find_for_git_client("oauth2", token_w_api_scope.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(user, nil, :oauth, read_authentication_abilities)) + expect(gl_auth.find_for_git_client("oauth2", token_w_api_scope.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(user, nil, :oauth, full_authentication_abilities)) end it 'fails for OAuth tokens with other scopes' do diff --git a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb index fced253dd01..b386852b196 100644 --- a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb +++ b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb @@ -1,8 +1,9 @@ require 'spec_helper' -describe Gitlab::Cache::Ci::ProjectPipelineStatus do +describe Gitlab::Cache::Ci::ProjectPipelineStatus, :redis do let(:project) { create(:project) } let(:pipeline_status) { described_class.new(project) } + let(:cache_key) { "projects/#{project.id}/pipeline_status" } describe '.load_for_project' do it "loads the status" do @@ -12,12 +13,110 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus do end end + describe 'loading in batches' do + let(:status) { 'success' } + let(:sha) { '424d1b73bc0d3cb726eb7dc4ce17a4d48552f8c6' } + let(:ref) { 'master' } + let(:pipeline_info) { { sha: sha, status: status, ref: ref } } + let(:project_without_status) { create(:project) } + + describe '.load_in_batch_for_projects' do + it 'preloads pipeline_status on projects' do + described_class.load_in_batch_for_projects([project]) + + # Don't call the accessor that would lazy load the variable + expect(project.instance_variable_get('@pipeline_status')).to be_a(described_class) + end + + describe 'without a status in redis' do + it 'loads the status from a commit when it was not in redis' do + empty_status = { sha: nil, status: nil, ref: nil } + fake_pipeline = described_class.new( + project_without_status, + pipeline_info: empty_status, + loaded_from_cache: false + ) + + expect(described_class).to receive(:new). + with(project_without_status, + pipeline_info: empty_status, + loaded_from_cache: false). + and_return(fake_pipeline) + expect(fake_pipeline).to receive(:load_from_project) + expect(fake_pipeline).to receive(:store_in_cache) + + described_class.load_in_batch_for_projects([project_without_status]) + end + + it 'only connects to redis twice' do + # Once to load, once to store in the cache + expect(Gitlab::Redis).to receive(:with).exactly(2).and_call_original + + described_class.load_in_batch_for_projects([project_without_status]) + + expect(project_without_status.pipeline_status).not_to be_nil + end + end + + describe 'when a status was cached in redis' do + before do + Gitlab::Redis.with do |redis| + redis.mapped_hmset(cache_key, + { sha: sha, status: status, ref: ref }) + end + end + + it 'loads the correct status' do + described_class.load_in_batch_for_projects([project]) + + pipeline_status = project.instance_variable_get('@pipeline_status') + + expect(pipeline_status.sha).to eq(sha) + expect(pipeline_status.status).to eq(status) + expect(pipeline_status.ref).to eq(ref) + end + + it 'only connects to redis once' do + expect(Gitlab::Redis).to receive(:with).exactly(1).and_call_original + + described_class.load_in_batch_for_projects([project]) + + expect(project.pipeline_status).not_to be_nil + end + + it "doesn't load the status separatly" do + expect_any_instance_of(described_class).not_to receive(:load_from_project) + expect_any_instance_of(described_class).not_to receive(:load_from_cache) + + described_class.load_in_batch_for_projects([project]) + end + end + end + + describe '.cached_results_for_projects' do + it 'loads a status from redis for all projects' do + Gitlab::Redis.with do |redis| + redis.mapped_hmset(cache_key, { sha: sha, status: status, ref: ref }) + end + + result = [{ loaded_from_cache: false, pipeline_info: { sha: nil, status: nil, ref: nil } }, + { loaded_from_cache: true, pipeline_info: pipeline_info }] + + expect(described_class.cached_results_for_projects([project_without_status, project])).to eq(result) + end + end + end + describe '.update_for_pipeline' do it 'refreshes the cache if nescessary' do - pipeline = build_stubbed(:ci_pipeline, sha: '123456', status: 'success') + pipeline = build_stubbed(:ci_pipeline, + sha: '123456', status: 'success', ref: 'master') fake_status = double expect(described_class).to receive(:new). - with(pipeline.project, sha: '123456', status: 'success', ref: 'master'). + with(pipeline.project, + pipeline_info: { + sha: '123456', status: 'success', ref: 'master' + }). and_return(fake_status) expect(fake_status).to receive(:store_in_cache_if_needed) @@ -110,7 +209,7 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus do pipeline_status.status = 'failed' pipeline_status.store_in_cache - read_sha, read_status = Gitlab::Redis.with { |redis| redis.hmget("projects/#{project.id}/build_status", :sha, :status) } + read_sha, read_status = Gitlab::Redis.with { |redis| redis.hmget(cache_key, :sha, :status) } expect(read_sha).to eq('123456') expect(read_status).to eq('failed') @@ -120,10 +219,10 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus do describe '#store_in_cache_if_needed', :redis do it 'stores the state in the cache when the sha is the HEAD of the project' do create(:ci_pipeline, :success, project: project, sha: project.commit.sha) - build_status = described_class.load_for_project(project) + pipeline_status = described_class.load_for_project(project) - build_status.store_in_cache_if_needed - sha, status, ref = Gitlab::Redis.with { |redis| redis.hmget("projects/#{project.id}/build_status", :sha, :status, :ref) } + pipeline_status.store_in_cache_if_needed + sha, status, ref = Gitlab::Redis.with { |redis| redis.hmget(cache_key, :sha, :status, :ref) } expect(sha).not_to be_nil expect(status).not_to be_nil @@ -131,10 +230,13 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus do end it "doesn't store the status in redis when the sha is not the head of the project" do - other_status = described_class.new(project, sha: "123456", status: "failed") + other_status = described_class.new( + project, + pipeline_info: { sha: "123456", status: "failed" } + ) other_status.store_in_cache_if_needed - sha, status = Gitlab::Redis.with { |redis| redis.hmget("projects/#{project.id}/build_status", :sha, :status) } + sha, status = Gitlab::Redis.with { |redis| redis.hmget(cache_key, :sha, :status) } expect(sha).to be_nil expect(status).to be_nil @@ -142,11 +244,18 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus do it "deletes the cache if the repository doesn't have a head commit" do empty_project = create(:empty_project) - Gitlab::Redis.with { |redis| redis.mapped_hmset("projects/#{empty_project.id}/build_status", { sha: "sha", status: "pending", ref: 'master' }) } - other_status = described_class.new(empty_project, sha: "123456", status: "failed") + Gitlab::Redis.with do |redis| + redis.mapped_hmset(cache_key, + { sha: 'sha', status: 'pending', ref: 'master' }) + end + + other_status = described_class.new(empty_project, + pipeline_info: { + sha: "123456", status: "failed" + }) other_status.store_in_cache_if_needed - sha, status, ref = Gitlab::Redis.with { |redis| redis.hmget("projects/#{empty_project.id}/build_status", :sha, :status, :ref) } + sha, status, ref = Gitlab::Redis.with { |redis| redis.hmget("projects/#{empty_project.id}/pipeline_status", :sha, :status, :ref) } expect(sha).to be_nil expect(status).to be_nil @@ -157,9 +266,13 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus do describe "with a status in redis", :redis do let(:status) { 'success' } let(:sha) { '424d1b73bc0d3cb726eb7dc4ce17a4d48552f8c6' } + let(:ref) { 'master' } before do - Gitlab::Redis.with { |redis| redis.mapped_hmset("projects/#{project.id}/build_status", { sha: sha, status: status }) } + Gitlab::Redis.with do |redis| + redis.mapped_hmset(cache_key, + { sha: sha, status: status, ref: ref }) + end end describe '#load_from_cache' do @@ -168,6 +281,7 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus do expect(pipeline_status.sha).to eq(sha) expect(pipeline_status.status).to eq(status) + expect(pipeline_status.ref).to eq(ref) end end @@ -181,7 +295,7 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus do it 'deletes values from redis' do pipeline_status.delete_from_cache - key_exists = Gitlab::Redis.with { |redis| redis.exists("projects/#{project.id}/build_status") } + key_exists = Gitlab::Redis.with { |redis| redis.exists(cache_key) } expect(key_exists).to be_falsy end diff --git a/spec/lib/gitlab/checks/force_push_spec.rb b/spec/lib/gitlab/checks/force_push_spec.rb index 7a84bbebd02..bc66ce83d4a 100644 --- a/spec/lib/gitlab/checks/force_push_spec.rb +++ b/spec/lib/gitlab/checks/force_push_spec.rb @@ -1,19 +1,19 @@ require 'spec_helper' -describe Gitlab::Checks::ChangeAccess, lib: true do +describe Gitlab::Checks::ForcePush, lib: true do let(:project) { create(:project, :repository) } context "exit code checking" do it "does not raise a runtime error if the `popen` call to git returns a zero exit code" do allow(Gitlab::Popen).to receive(:popen).and_return(['normal output', 0]) - expect { Gitlab::Checks::ForcePush.force_push?(project, 'oldrev', 'newrev') }.not_to raise_error + expect { described_class.force_push?(project, 'oldrev', 'newrev') }.not_to raise_error end it "raises a runtime error if the `popen` call to git returns a non-zero exit code" do allow(Gitlab::Popen).to receive(:popen).and_return(['error', 1]) - expect { Gitlab::Checks::ForcePush.force_push?(project, 'oldrev', 'newrev') }.to raise_error(RuntimeError) + expect { described_class.force_push?(project, 'oldrev', 'newrev') }.to raise_error(RuntimeError) end end end diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb index a044b871730..737fac14f92 100644 --- a/spec/lib/gitlab/database/migration_helpers_spec.rb +++ b/spec/lib/gitlab/database/migration_helpers_spec.rb @@ -726,4 +726,37 @@ describe Gitlab::Database::MigrationHelpers, lib: true do expect(model.column_for(:users, :kittens)).to be_nil end end + + describe '#replace_sql' do + context 'using postgres' do + before do + allow(Gitlab::Database).to receive(:mysql?).and_return(false) + end + + it 'builds the sql with correct functions' do + expect(model.replace_sql(Arel::Table.new(:users)[:first_name], "Alice", "Eve").to_s). + to include('regexp_replace') + end + end + + context 'using mysql' do + before do + allow(Gitlab::Database).to receive(:mysql?).and_return(true) + end + + it 'builds the sql with the correct functions' do + expect(model.replace_sql(Arel::Table.new(:users)[:first_name], "Alice", "Eve").to_s). + to include('locate', 'insert') + end + end + + describe 'results' do + let!(:user) { create(:user, name: 'Kathy Alice Aliceson') } + + it 'replaces the correct part of the string' do + model.update_column_in_batches(:users, :name, model.replace_sql(Arel::Table.new(:users)[:name], 'Alice', 'Eve')) + expect(user.reload.name).to eq('Kathy Eve Aliceson') + end + end + end end diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb new file mode 100644 index 00000000000..64bc5fc0429 --- /dev/null +++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb @@ -0,0 +1,197 @@ +require 'spec_helper' + +describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase do + let(:migration) { FakeRenameReservedPathMigrationV1.new } + let(:subject) { described_class.new(['the-path'], migration) } + + before do + allow(migration).to receive(:say) + end + + def migration_namespace(namespace) + Gitlab::Database::RenameReservedPathsMigration::V1::MigrationClasses:: + Namespace.find(namespace.id) + end + + def migration_project(project) + Gitlab::Database::RenameReservedPathsMigration::V1::MigrationClasses:: + Project.find(project.id) + end + + describe "#remove_last_ocurrence" do + it "removes only the last occurance of a string" do + input = "this/is/a-word-to-replace/namespace/with/a-word-to-replace" + + expect(subject.remove_last_occurrence(input, "a-word-to-replace")) + .to eq("this/is/a-word-to-replace/namespace/with/") + end + end + + describe '#remove_cached_html_for_projects' do + let(:project) { create(:empty_project, description_html: 'Project description') } + + it 'removes description_html from projects' do + subject.remove_cached_html_for_projects([project.id]) + + expect(project.reload.description_html).to be_nil + end + + it 'removes issue descriptions' do + issue = create(:issue, project: project, description_html: 'Issue description') + + subject.remove_cached_html_for_projects([project.id]) + + expect(issue.reload.description_html).to be_nil + end + + it 'removes merge request descriptions' do + merge_request = create(:merge_request, + source_project: project, + target_project: project, + description_html: 'MergeRequest description') + + subject.remove_cached_html_for_projects([project.id]) + + expect(merge_request.reload.description_html).to be_nil + end + + it 'removes note html' do + note = create(:note, + project: project, + noteable: create(:issue, project: project), + note_html: 'note description') + + subject.remove_cached_html_for_projects([project.id]) + + expect(note.reload.note_html).to be_nil + end + + it 'removes milestone description' do + milestone = create(:milestone, + project: project, + description_html: 'milestone description') + + subject.remove_cached_html_for_projects([project.id]) + + expect(milestone.reload.description_html).to be_nil + end + end + + describe '#rename_path_for_routable' do + context 'for namespaces' do + let(:namespace) { create(:namespace, path: 'the-path') } + it "renames namespaces called the-path" do + subject.rename_path_for_routable(migration_namespace(namespace)) + + expect(namespace.reload.path).to eq("the-path0") + end + + it "renames the route to the namespace" do + subject.rename_path_for_routable(migration_namespace(namespace)) + + expect(Namespace.find(namespace.id).full_path).to eq("the-path0") + end + + it "renames the route for projects of the namespace" do + project = create(:project, path: "project-path", namespace: namespace) + + subject.rename_path_for_routable(migration_namespace(namespace)) + + expect(project.route.reload.path).to eq("the-path0/project-path") + end + + it 'returns the old & the new path' do + old_path, new_path = subject.rename_path_for_routable(migration_namespace(namespace)) + + expect(old_path).to eq('the-path') + expect(new_path).to eq('the-path0') + end + + context "the-path namespace -> subgroup -> the-path0 project" do + it "updates the route of the project correctly" do + subgroup = create(:group, path: "subgroup", parent: namespace) + project = create(:project, path: "the-path0", namespace: subgroup) + + subject.rename_path_for_routable(migration_namespace(namespace)) + + expect(project.route.reload.path).to eq("the-path0/subgroup/the-path0") + end + end + end + + context 'for projects' do + let(:parent) { create(:namespace, path: 'the-parent') } + let(:project) { create(:empty_project, path: 'the-path', namespace: parent) } + + it 'renames the project called `the-path`' do + subject.rename_path_for_routable(migration_project(project)) + + expect(project.reload.path).to eq('the-path0') + end + + it 'renames the route for the project' do + subject.rename_path_for_routable(project) + + expect(project.reload.route.path).to eq('the-parent/the-path0') + end + + it 'returns the old & new path' do + old_path, new_path = subject.rename_path_for_routable(migration_project(project)) + + expect(old_path).to eq('the-parent/the-path') + expect(new_path).to eq('the-parent/the-path0') + end + end + end + + describe '#move_pages' do + it 'moves the pages directory' do + expect(subject).to receive(:move_folders) + .with(TestEnv.pages_path, 'old-path', 'new-path') + + subject.move_pages('old-path', 'new-path') + end + end + + describe "#move_uploads" do + let(:test_dir) { File.join(Rails.root, 'tmp', 'tests', 'rename_reserved_paths') } + let(:uploads_dir) { File.join(test_dir, 'public', 'uploads') } + + it 'moves subdirectories in the uploads folder' do + expect(subject).to receive(:uploads_dir).and_return(uploads_dir) + expect(subject).to receive(:move_folders).with(uploads_dir, 'old_path', 'new_path') + + subject.move_uploads('old_path', 'new_path') + end + + it "doesn't move uploads when they are stored in object storage" do + expect(subject).to receive(:file_storage?).and_return(false) + expect(subject).not_to receive(:move_folders) + + subject.move_uploads('old_path', 'new_path') + end + end + + describe '#move_folders' do + let(:test_dir) { File.join(Rails.root, 'tmp', 'tests', 'rename_reserved_paths') } + let(:uploads_dir) { File.join(test_dir, 'public', 'uploads') } + + before do + FileUtils.remove_dir(test_dir) if File.directory?(test_dir) + FileUtils.mkdir_p(uploads_dir) + allow(subject).to receive(:uploads_dir).and_return(uploads_dir) + end + + it 'moves a folder with files' do + source = File.join(uploads_dir, 'parent-group', 'sub-group') + FileUtils.mkdir_p(source) + destination = File.join(uploads_dir, 'parent-group', 'moved-group') + FileUtils.touch(File.join(source, 'test.txt')) + expected_file = File.join(destination, 'test.txt') + + subject.move_folders(uploads_dir, File.join('parent-group', 'sub-group'), File.join('parent-group', 'moved-group')) + + expect(File.exist?(expected_file)).to be(true) + end + end +end diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb new file mode 100644 index 00000000000..a25c5da488a --- /dev/null +++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb @@ -0,0 +1,171 @@ +require 'spec_helper' + +describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces do + let(:migration) { FakeRenameReservedPathMigrationV1.new } + let(:subject) { described_class.new(['the-path'], migration) } + + before do + allow(migration).to receive(:say) + end + + def migration_namespace(namespace) + Gitlab::Database::RenameReservedPathsMigration::V1::MigrationClasses:: + Namespace.find(namespace.id) + end + + describe '#namespaces_for_paths' do + context 'nested namespaces' do + let(:subject) { described_class.new(['parent/the-Path'], migration) } + + it 'includes the namespace' do + parent = create(:namespace, path: 'parent') + child = create(:namespace, path: 'the-path', parent: parent) + + found_ids = subject.namespaces_for_paths(type: :child). + map(&:id) + expect(found_ids).to contain_exactly(child.id) + end + end + + context 'for child namespaces' do + it 'only returns child namespaces with the correct path' do + _root_namespace = create(:namespace, path: 'THE-path') + _other_path = create(:namespace, + path: 'other', + parent: create(:namespace)) + namespace = create(:namespace, + path: 'the-path', + parent: create(:namespace)) + + found_ids = subject.namespaces_for_paths(type: :child). + map(&:id) + expect(found_ids).to contain_exactly(namespace.id) + end + end + + context 'for top levelnamespaces' do + it 'only returns child namespaces with the correct path' do + root_namespace = create(:namespace, path: 'the-path') + _other_path = create(:namespace, path: 'other') + _child_namespace = create(:namespace, + path: 'the-path', + parent: create(:namespace)) + + found_ids = subject.namespaces_for_paths(type: :top_level). + map(&:id) + expect(found_ids).to contain_exactly(root_namespace.id) + end + end + end + + describe '#move_repositories' do + let(:namespace) { create(:group, name: 'hello-group') } + it 'moves a project for a namespace' do + create(:project, namespace: namespace, path: 'hello-project') + expected_path = File.join(TestEnv.repos_path, 'bye-group', 'hello-project.git') + + subject.move_repositories(namespace, 'hello-group', 'bye-group') + + expect(File.directory?(expected_path)).to be(true) + end + + it 'moves a namespace in a subdirectory correctly' do + child_namespace = create(:group, name: 'sub-group', parent: namespace) + create(:project, namespace: child_namespace, path: 'hello-project') + + expected_path = File.join(TestEnv.repos_path, 'hello-group', 'renamed-sub-group', 'hello-project.git') + + subject.move_repositories(child_namespace, 'hello-group/sub-group', 'hello-group/renamed-sub-group') + + expect(File.directory?(expected_path)).to be(true) + end + + it 'moves a parent namespace with subdirectories' do + child_namespace = create(:group, name: 'sub-group', parent: namespace) + create(:project, namespace: child_namespace, path: 'hello-project') + expected_path = File.join(TestEnv.repos_path, 'renamed-group', 'sub-group', 'hello-project.git') + + subject.move_repositories(child_namespace, 'hello-group', 'renamed-group') + + expect(File.directory?(expected_path)).to be(true) + end + end + + describe "#child_ids_for_parent" do + it "collects child ids for all levels" do + parent = create(:namespace) + first_child = create(:namespace, parent: parent) + second_child = create(:namespace, parent: parent) + third_child = create(:namespace, parent: second_child) + all_ids = [parent.id, first_child.id, second_child.id, third_child.id] + + collected_ids = subject.child_ids_for_parent(parent, ids: [parent.id]) + + expect(collected_ids).to contain_exactly(*all_ids) + end + end + + describe "#rename_namespace" do + let(:namespace) { create(:namespace, path: 'the-path') } + + it 'renames paths & routes for the namespace' do + expect(subject).to receive(:rename_path_for_routable). + with(namespace). + and_call_original + + subject.rename_namespace(namespace) + + expect(namespace.reload.path).to eq('the-path0') + end + + it "moves the the repository for a project in the namespace" do + create(:project, namespace: namespace, path: "the-path-project") + expected_repo = File.join(TestEnv.repos_path, "the-path0", "the-path-project.git") + + subject.rename_namespace(namespace) + + expect(File.directory?(expected_repo)).to be(true) + end + + it "moves the uploads for the namespace" do + expect(subject).to receive(:move_uploads).with("the-path", "the-path0") + + subject.rename_namespace(namespace) + end + + it "moves the pages for the namespace" do + expect(subject).to receive(:move_pages).with("the-path", "the-path0") + + subject.rename_namespace(namespace) + end + + it 'invalidates the markdown cache of related projects' do + project = create(:empty_project, namespace: namespace, path: "the-path-project") + + expect(subject).to receive(:remove_cached_html_for_projects).with([project.id]) + + subject.rename_namespace(namespace) + end + end + + describe '#rename_namespaces' do + let!(:top_level_namespace) { create(:namespace, path: 'the-path') } + let!(:child_namespace) do + create(:namespace, path: 'the-path', parent: create(:namespace)) + end + + it 'renames top level namespaces the namespace' do + expect(subject).to receive(:rename_namespace). + with(migration_namespace(top_level_namespace)) + + subject.rename_namespaces(type: :top_level) + end + + it 'renames child namespaces' do + expect(subject).to receive(:rename_namespace). + with(migration_namespace(child_namespace)) + + subject.rename_namespaces(type: :child) + end + end +end diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb new file mode 100644 index 00000000000..59e8de2712d --- /dev/null +++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb @@ -0,0 +1,102 @@ +require 'spec_helper' + +describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects do + let(:migration) { FakeRenameReservedPathMigrationV1.new } + let(:subject) { described_class.new(['the-path'], migration) } + + before do + allow(migration).to receive(:say) + end + + describe '#projects_for_paths' do + it 'searches using nested paths' do + namespace = create(:namespace, path: 'hello') + project = create(:empty_project, path: 'THE-path', namespace: namespace) + + result_ids = described_class.new(['Hello/the-path'], migration). + projects_for_paths.map(&:id) + + expect(result_ids).to contain_exactly(project.id) + end + + it 'includes the correct projects' do + project = create(:empty_project, path: 'THE-path') + _other_project = create(:empty_project) + + result_ids = subject.projects_for_paths.map(&:id) + + expect(result_ids).to contain_exactly(project.id) + end + end + + describe '#rename_projects' do + let!(:projects) { create_list(:empty_project, 2, path: 'the-path') } + + it 'renames each project' do + expect(subject).to receive(:rename_project).twice + + subject.rename_projects + end + + it 'invalidates the markdown cache of related projects' do + expect(subject).to receive(:remove_cached_html_for_projects). + with(projects.map(&:id)) + + subject.rename_projects + end + end + + describe '#rename_project' do + let(:project) do + create(:empty_project, + path: 'the-path', + namespace: create(:namespace, path: 'known-parent' )) + end + + it 'renames path & route for the project' do + expect(subject).to receive(:rename_path_for_routable). + with(project). + and_call_original + + subject.rename_project(project) + + expect(project.reload.path).to eq('the-path0') + end + + it 'moves the wiki & the repo' do + expect(subject).to receive(:move_repository). + with(project, 'known-parent/the-path.wiki', 'known-parent/the-path0.wiki') + expect(subject).to receive(:move_repository). + with(project, 'known-parent/the-path', 'known-parent/the-path0') + + subject.rename_project(project) + end + + it 'moves uploads' do + expect(subject).to receive(:move_uploads). + with('known-parent/the-path', 'known-parent/the-path0') + + subject.rename_project(project) + end + + it 'moves pages' do + expect(subject).to receive(:move_pages). + with('known-parent/the-path', 'known-parent/the-path0') + + subject.rename_project(project) + end + end + + describe '#move_repository' do + let(:known_parent) { create(:namespace, path: 'known-parent') } + let(:project) { create(:project, path: 'the-path', namespace: known_parent) } + + it 'moves the repository for a project' do + expected_path = File.join(TestEnv.repos_path, 'known-parent', 'new-repo.git') + + subject.move_repository(project, 'known-parent/the-path', 'known-parent/new-repo') + + expect(File.directory?(expected_path)).to be(true) + end + end +end diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb new file mode 100644 index 00000000000..f8cc1eb91ec --- /dev/null +++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +shared_examples 'renames child namespaces' do |type| + it 'renames namespaces' do + rename_namespaces = double + expect(described_class::RenameNamespaces). + to receive(:new).with(['first-path', 'second-path'], subject). + and_return(rename_namespaces) + expect(rename_namespaces).to receive(:rename_namespaces). + with(type: :child) + + subject.rename_wildcard_paths(['first-path', 'second-path']) + end +end + +describe Gitlab::Database::RenameReservedPathsMigration::V1 do + let(:subject) { FakeRenameReservedPathMigrationV1.new } + + before do + allow(subject).to receive(:say) + end + + describe '#rename_child_paths' do + it_behaves_like 'renames child namespaces' + end + + describe '#rename_wildcard_paths' do + it_behaves_like 'renames child namespaces' + + it 'should rename projects' do + rename_projects = double + expect(described_class::RenameProjects). + to receive(:new).with(['the-path'], subject). + and_return(rename_projects) + + expect(rename_projects).to receive(:rename_projects) + + subject.rename_wildcard_paths(['the-path']) + end + end + + describe '#rename_root_paths' do + it 'should rename namespaces' do + rename_namespaces = double + expect(described_class::RenameNamespaces). + to receive(:new).with(['the-path'], subject). + and_return(rename_namespaces) + expect(rename_namespaces).to receive(:rename_namespaces). + with(type: :top_level) + + subject.rename_root_paths('the-path') + end + end +end diff --git a/spec/lib/gitlab/diff/position_tracer_spec.rb b/spec/lib/gitlab/diff/position_tracer_spec.rb index c166f83664a..a10a251dc4a 100644 --- a/spec/lib/gitlab/diff/position_tracer_spec.rb +++ b/spec/lib/gitlab/diff/position_tracer_spec.rb @@ -569,13 +569,8 @@ describe Gitlab::Diff::PositionTracer, lib: true do # 1 1 BB # 2 2 A - it "returns the new position" do - expect_new_position( - old_path: file_name, - new_path: new_file_name, - old_line: old_position.new_line, - new_line: old_position.new_line - ) + it "returns nil since the line doesn't exist in the new diffs anymore" do + expect(subject).to be_nil end end diff --git a/spec/lib/gitlab/email/receiver_spec.rb b/spec/lib/gitlab/email/receiver_spec.rb index 2a86b427806..f127e45ae6a 100644 --- a/spec/lib/gitlab/email/receiver_spec.rb +++ b/spec/lib/gitlab/email/receiver_spec.rb @@ -7,9 +7,17 @@ describe Gitlab::Email::Receiver, lib: true do context "when we cannot find a capable handler" do let(:email_raw) { fixture_file('emails/valid_reply.eml').gsub(mail_key, "!!!") } - it "raises a UnknownIncomingEmail" do + it "raises an UnknownIncomingEmail error" do expect { receiver.execute }.to raise_error(Gitlab::Email::UnknownIncomingEmail) end + + context "and the email contains no references header" do + let(:email_raw) { fixture_file("emails/auto_reply.eml").gsub(mail_key, "!!!") } + + it "raises an UnknownIncomingEmail error" do + expect { receiver.execute }.to raise_error(Gitlab::Email::UnknownIncomingEmail) + end + end end context "when the email is blank" do diff --git a/spec/lib/gitlab/git/encoding_helper_spec.rb b/spec/lib/gitlab/git/encoding_helper_spec.rb index 27bcc241b82..f6ac7b23d1d 100644 --- a/spec/lib/gitlab/git/encoding_helper_spec.rb +++ b/spec/lib/gitlab/git/encoding_helper_spec.rb @@ -56,6 +56,10 @@ describe Gitlab::Git::EncodingHelper do expect(r.encoding.name).to eq('UTF-8') end end + + it 'returns empty string on conversion errors' do + expect { ext_class.encode_utf8('') }.not_to raise_error(ArgumentError) + end end describe '#clean' do diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 3d6d7292b42..1b78910fa3c 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1031,6 +1031,35 @@ describe Gitlab::Git::Repository, seed_helper: true do end end + describe '#find_commits' do + it 'should return a return a collection of commits' do + commits = repository.find_commits + + expect(commits).not_to be_empty + expect(commits).to all( be_a_kind_of(Gitlab::Git::Commit) ) + end + + context 'while applying a sort order based on the `order` option' do + it "allows ordering topologically (no parents shown before their children)" do + expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_TOPO) + + repository.find_commits(order: :topo) + end + + it "allows ordering by date" do + expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_DATE) + + repository.find_commits(order: :date) + end + + it "applies no sorting by default" do + expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_NONE) + + repository.find_commits + end + end + end + describe '#branches with deleted branch' do before(:each) do ref = double() @@ -1045,20 +1074,8 @@ describe Gitlab::Git::Repository, seed_helper: true do end describe '#branch_count' do - before(:each) do - valid_ref = double(:ref) - invalid_ref = double(:ref) - - allow(valid_ref).to receive_messages(name: 'master', target: double(:target)) - - allow(invalid_ref).to receive_messages(name: 'bad-branch') - allow(invalid_ref).to receive(:target) { raise Rugged::ReferenceError } - - allow(repository.rugged).to receive_messages(branches: [valid_ref, invalid_ref]) - end - it 'returns the number of branches' do - expect(repository.branch_count).to eq(1) + expect(repository.branch_count).to eq(9) end end diff --git a/spec/lib/git_ref_validator_spec.rb b/spec/lib/gitlab/git_ref_validator_spec.rb index cc8daa535d6..cc8daa535d6 100644 --- a/spec/lib/git_ref_validator_spec.rb +++ b/spec/lib/gitlab/git_ref_validator_spec.rb diff --git a/spec/lib/gitlab/healthchecks/db_check_spec.rb b/spec/lib/gitlab/health_checks/db_check_spec.rb index 33c6c24449c..33c6c24449c 100644 --- a/spec/lib/gitlab/healthchecks/db_check_spec.rb +++ b/spec/lib/gitlab/health_checks/db_check_spec.rb diff --git a/spec/lib/gitlab/healthchecks/fs_shards_check_spec.rb b/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb index 4cd8cf313a5..4cd8cf313a5 100644 --- a/spec/lib/gitlab/healthchecks/fs_shards_check_spec.rb +++ b/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb diff --git a/spec/lib/gitlab/healthchecks/redis_check_spec.rb b/spec/lib/gitlab/health_checks/redis_check_spec.rb index 734cdcb893e..734cdcb893e 100644 --- a/spec/lib/gitlab/healthchecks/redis_check_spec.rb +++ b/spec/lib/gitlab/health_checks/redis_check_spec.rb diff --git a/spec/lib/gitlab/healthchecks/simple_check_shared.rb b/spec/lib/gitlab/health_checks/simple_check_shared.rb index 1fa6d0faef9..1fa6d0faef9 100644 --- a/spec/lib/gitlab/healthchecks/simple_check_shared.rb +++ b/spec/lib/gitlab/health_checks/simple_check_shared.rb diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json index 7a0b0b06d4b..bfecfa28ed1 100644 --- a/spec/lib/gitlab/import_export/project.json +++ b/spec/lib/gitlab/import_export/project.json @@ -6981,28 +6981,6 @@ ], "services": [ { - "id": 164, - "title": null, - "project_id": 5, - "created_at": "2016-06-14T15:02:07.372Z", - "updated_at": "2016-06-14T15:02:07.372Z", - "active": false, - "properties": { - - }, - "template": false, - "push_events": true, - "issues_events": true, - "merge_requests_events": true, - "tag_push_events": true, - "note_events": true, - "build_events": true, - "category": "issue_tracker", - "type": "CustomIssueTrackerService", - "default": true, - "wiki_page_events": true - }, - { "id": 100, "title": "JetBrains TeamCity CI", "project_id": 5, @@ -7019,6 +6997,7 @@ "tag_push_events": true, "note_events": true, "build_events": true, + "type": "TeamcityService", "category": "ci", "default": false, "wiki_page_events": true @@ -7040,6 +7019,7 @@ "tag_push_events": true, "note_events": true, "pipeline_events": true, + "type": "SlackService", "category": "common", "default": false, "wiki_page_events": true @@ -7061,6 +7041,7 @@ "tag_push_events": true, "note_events": true, "build_events": true, + "type": "RedmineService", "category": "issue_tracker", "default": false, "wiki_page_events": true @@ -7082,6 +7063,7 @@ "tag_push_events": true, "note_events": true, "build_events": true, + "type": "PushoverService", "category": "common", "default": false, "wiki_page_events": true @@ -7103,6 +7085,7 @@ "tag_push_events": true, "note_events": true, "build_events": true, + "type": "PivotalTrackerService", "category": "common", "default": false, "wiki_page_events": true @@ -7125,6 +7108,7 @@ "tag_push_events": true, "note_events": true, "build_events": true, + "type": "JiraService", "category": "issue_tracker", "default": false, "wiki_page_events": true @@ -7146,6 +7130,7 @@ "tag_push_events": true, "note_events": true, "build_events": true, + "type": "IrkerService", "category": "common", "default": false, "wiki_page_events": true @@ -7167,6 +7152,7 @@ "tag_push_events": true, "note_events": true, "pipeline_events": true, + "type": "HipchatService", "category": "common", "default": false, "wiki_page_events": true @@ -7188,6 +7174,7 @@ "tag_push_events": true, "note_events": true, "build_events": true, + "type": "GemnasiumService", "category": "common", "default": false, "wiki_page_events": true @@ -7209,6 +7196,7 @@ "tag_push_events": true, "note_events": true, "build_events": true, + "type": "FlowdockService", "category": "common", "default": false, "wiki_page_events": true @@ -7230,6 +7218,7 @@ "tag_push_events": true, "note_events": true, "build_events": true, + "type": "ExternalWikiService", "category": "common", "default": false, "wiki_page_events": true @@ -7251,6 +7240,7 @@ "tag_push_events": true, "note_events": true, "build_events": true, + "type": "EmailsOnPushService", "category": "common", "default": false, "wiki_page_events": true @@ -7272,6 +7262,7 @@ "tag_push_events": true, "note_events": true, "build_events": true, + "type": "DroneCiService", "category": "ci", "default": false, "wiki_page_events": true @@ -7293,6 +7284,7 @@ "tag_push_events": true, "note_events": true, "build_events": true, + "type": "CustomIssueTrackerService", "category": "issue_tracker", "default": false, "wiki_page_events": true @@ -7314,6 +7306,7 @@ "tag_push_events": true, "note_events": true, "build_events": true, + "type": "CampfireService", "category": "common", "default": false, "wiki_page_events": true @@ -7335,6 +7328,7 @@ "tag_push_events": true, "note_events": true, "build_events": true, + "type": "BuildkiteService", "category": "ci", "default": false, "wiki_page_events": true @@ -7356,6 +7350,7 @@ "tag_push_events": true, "note_events": true, "build_events": true, + "type": "BambooService", "category": "ci", "default": false, "wiki_page_events": true @@ -7377,6 +7372,7 @@ "tag_push_events": true, "note_events": true, "build_events": true, + "type": "AssemblaService", "category": "common", "default": false, "wiki_page_events": true @@ -7398,6 +7394,7 @@ "tag_push_events": true, "note_events": true, "build_events": true, + "type": "AssemblaService", "category": "common", "default": false, "wiki_page_events": true diff --git a/spec/lib/gitlab/import_export/relation_factory_spec.rb b/spec/lib/gitlab/import_export/relation_factory_spec.rb index fcc23a75ca1..06cd8ab87ed 100644 --- a/spec/lib/gitlab/import_export/relation_factory_spec.rb +++ b/spec/lib/gitlab/import_export/relation_factory_spec.rb @@ -60,7 +60,7 @@ describe Gitlab::ImportExport::RelationFactory, lib: true do end context 'original service exists' do - let(:service_id) { Service.create(project: project).id } + let(:service_id) { create(:service, project: project).id } it 'does not have the original service_id' do expect(created_object.service_id).not_to eq(service_id) diff --git a/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb index 071e5fac3f0..071e5fac3f0 100644 --- a/spec/lib/gitlab/import_export/wiki_repo_bundler_spec.rb +++ b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb diff --git a/spec/lib/gitlab/issuable_sorter_spec.rb b/spec/lib/gitlab/issuable_sorter_spec.rb new file mode 100644 index 00000000000..c9a434b2bcf --- /dev/null +++ b/spec/lib/gitlab/issuable_sorter_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' + +describe Gitlab::IssuableSorter, lib: true do + let(:namespace1) { build(:namespace, id: 1) } + let(:project1) { build(:project, id: 1, namespace: namespace1) } + + let(:project2) { build(:project, id: 2, path: "a", namespace: project1.namespace) } + let(:project3) { build(:project, id: 3, path: "b", namespace: project1.namespace) } + + let(:namespace2) { build(:namespace, id: 2, path: "a") } + let(:namespace3) { build(:namespace, id: 3, path: "b") } + let(:project4) { build(:project, id: 4, path: "a", namespace: namespace2) } + let(:project5) { build(:project, id: 5, path: "b", namespace: namespace2) } + let(:project6) { build(:project, id: 6, path: "a", namespace: namespace3) } + + let(:unsorted) { [sorted[2], sorted[3], sorted[0], sorted[1]] } + + let(:sorted) do + [build(:issue, iid: 1, project: project1), + build(:issue, iid: 2, project: project1), + build(:issue, iid: 10, project: project1), + build(:issue, iid: 20, project: project1)] + end + + it 'sorts references by a given key' do + expect(described_class.sort(project1, unsorted)).to eq(sorted) + end + + context 'for JIRA issues' do + let(:sorted) do + [ExternalIssue.new('JIRA-1', project1), + ExternalIssue.new('JIRA-2', project1), + ExternalIssue.new('JIRA-10', project1), + ExternalIssue.new('JIRA-20', project1)] + end + + it 'sorts references by a given key' do + expect(described_class.sort(project1, unsorted)).to eq(sorted) + end + end + + context 'for references from multiple projects and namespaces' do + let(:sorted) do + [build(:issue, iid: 1, project: project1), + build(:issue, iid: 2, project: project1), + build(:issue, iid: 10, project: project1), + build(:issue, iid: 1, project: project2), + build(:issue, iid: 1, project: project3), + build(:issue, iid: 1, project: project4), + build(:issue, iid: 1, project: project5), + build(:issue, iid: 1, project: project6)] + end + let(:unsorted) do + [sorted[3], sorted[1], sorted[4], sorted[2], + sorted[6], sorted[5], sorted[0], sorted[7]] + end + + it 'sorts references by project and then by a given key' do + expect(subject.sort(project1, unsorted)).to eq(sorted) + end + end +end diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb index 346cf0d117c..f4aab429931 100644 --- a/spec/lib/gitlab/ldap/user_spec.rb +++ b/spec/lib/gitlab/ldap/user_spec.rb @@ -108,6 +108,31 @@ describe Gitlab::LDAP::User, lib: true do it "creates a new user if not found" do expect{ ldap_user.save }.to change{ User.count }.by(1) end + + context 'when signup is disabled' do + before do + stub_application_setting signup_enabled: false + end + + it 'creates the user' do + ldap_user.save + + expect(gl_user).to be_persisted + end + end + + context 'when user confirmation email is enabled' do + before do + stub_application_setting send_user_confirmation_email: true + end + + it 'creates and confirms the user anyway' do + ldap_user.save + + expect(gl_user).to be_persisted + expect(gl_user).to be_confirmed + end + end end describe 'updating email' do diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb index 8f09266c3b3..828c953197d 100644 --- a/spec/lib/gitlab/o_auth/user_spec.rb +++ b/spec/lib/gitlab/o_auth/user_spec.rb @@ -40,6 +40,35 @@ describe Gitlab::OAuth::User, lib: true do let(:provider) { 'twitter' } describe 'signup' do + context 'when signup is disabled' do + before do + stub_application_setting signup_enabled: false + end + + it 'creates the user' do + stub_omniauth_config(allow_single_sign_on: ['twitter']) + + oauth_user.save + + expect(gl_user).to be_persisted + end + end + + context 'when user confirmation email is enabled' do + before do + stub_application_setting send_user_confirmation_email: true + end + + it 'creates and confirms the user anyway' do + stub_omniauth_config(allow_single_sign_on: ['twitter']) + + oauth_user.save + + expect(gl_user).to be_persisted + expect(gl_user).to be_confirmed + end + end + it 'marks user as having password_automatically_set' do stub_omniauth_config(allow_single_sign_on: ['twitter'], external_providers: ['twitter']) diff --git a/spec/lib/gitlab/other_markup.rb b/spec/lib/gitlab/other_markup_spec.rb index 8f5a353b381..d6d53e8586c 100644 --- a/spec/lib/gitlab/other_markup.rb +++ b/spec/lib/gitlab/other_markup_spec.rb @@ -1,17 +1,19 @@ require 'spec_helper' describe Gitlab::OtherMarkup, lib: true do + let(:context) { {} } + context "XSS Checks" do links = { 'links' => { file: 'file.rdoc', input: 'XSS[JaVaScriPt:alert(1)]', - output: '<p><a>XSS</a></p>' + output: "\n" + '<p><a>XSS</a></p>' + "\n" } } links.each do |name, data| it "does not convert dangerous #{name} into HTML" do - expect(render(data[:file], data[:input], context)).to eql data[:output] + expect(render(data[:file], data[:input])).to eq(data[:output]) end end end diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb index 127cd8c78d8..72e947f2cc2 100644 --- a/spec/lib/gitlab/regex_spec.rb +++ b/spec/lib/gitlab/regex_spec.rb @@ -45,8 +45,8 @@ describe Gitlab::Regex, lib: true do it { is_expected.not_to match('foo-') } end - describe 'FULL_NAMESPACE_REGEX_STR' do - subject { %r{\A#{Gitlab::Regex::FULL_NAMESPACE_REGEX_STR}\z} } + describe '.full_namespace_regex' do + subject { described_class.full_namespace_regex } it { is_expected.to match('gitlab.org') } it { is_expected.to match('gitlab.org/gitlab-git') } diff --git a/spec/lib/gitlab/request_profiler_spec.rb b/spec/lib/gitlab/request_profiler_spec.rb new file mode 100644 index 00000000000..ae9c06ebb7d --- /dev/null +++ b/spec/lib/gitlab/request_profiler_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe Gitlab::RequestProfiler, lib: true do + describe '.profile_token' do + it 'returns a token' do + expect(described_class.profile_token).to be_present + end + + it 'caches the token' do + expect(Rails.cache).to receive(:fetch).with('profile-token') + + described_class.profile_token + end + end + + describe '.remove_all_profiles' do + it 'removes Gitlab::RequestProfiler::PROFILES_DIR directory' do + dir = described_class::PROFILES_DIR + FileUtils.mkdir_p(dir) + + expect(Dir.exist?(dir)).to be true + + described_class.remove_all_profiles + expect(Dir.exist?(dir)).to be false + end + end +end diff --git a/spec/lib/gitlab/saml/user_spec.rb b/spec/lib/gitlab/saml/user_spec.rb index 4f6ef3c10fc..b106d156b75 100644 --- a/spec/lib/gitlab/saml/user_spec.rb +++ b/spec/lib/gitlab/saml/user_spec.rb @@ -211,6 +211,31 @@ describe Gitlab::Saml::User, lib: true do end end end + + context 'when signup is disabled' do + before do + stub_application_setting signup_enabled: false + end + + it 'creates the user' do + saml_user.save + + expect(gl_user).to be_persisted + end + end + + context 'when user confirmation email is enabled' do + before do + stub_application_setting send_user_confirmation_email: true + end + + it 'creates and confirms the user anyway' do + saml_user.save + + expect(gl_user).to be_persisted + expect(gl_user).to be_confirmed + end + end end describe 'blocking' do diff --git a/spec/lib/gitlab/backend/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb index 6675d26734e..6675d26734e 100644 --- a/spec/lib/gitlab/backend/shell_spec.rb +++ b/spec/lib/gitlab/shell_spec.rb diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb index 611cdbbc865..2b27ff66c09 100644 --- a/spec/lib/gitlab/user_access_spec.rb +++ b/spec/lib/gitlab/user_access_spec.rb @@ -87,10 +87,10 @@ describe Gitlab::UserAccess, lib: true do expect(access.can_push_to_branch?(branch.name)).to be_falsey end - it 'returns true if branch does not exist and user has permission to merge' do + it 'returns false if branch does not exist' do project.team << [user, :developer] - expect(access.can_push_to_branch?(not_existing_branch.name)).to be_truthy + expect(access.can_push_to_branch?(not_existing_branch.name)).to be_falsey end end diff --git a/spec/lib/light_url_builder_spec.rb b/spec/lib/light_url_builder_spec.rb deleted file mode 100644 index 3fe8cf43934..00000000000 --- a/spec/lib/light_url_builder_spec.rb +++ /dev/null @@ -1,119 +0,0 @@ -require 'spec_helper' - -describe Gitlab::UrlBuilder, lib: true do - describe '.build' do - context 'when passing a Commit' do - it 'returns a proper URL' do - commit = build_stubbed(:commit) - - url = described_class.build(commit) - - expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.path_with_namespace}/commit/#{commit.id}" - end - end - - context 'when passing an Issue' do - it 'returns a proper URL' do - issue = build_stubbed(:issue, iid: 42) - - url = described_class.build(issue) - - expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}" - end - end - - context 'when passing a MergeRequest' do - it 'returns a proper URL' do - merge_request = build_stubbed(:merge_request, iid: 42) - - url = described_class.build(merge_request) - - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}" - end - end - - context 'when passing a Note' do - context 'on a Commit' do - it 'returns a proper URL' do - note = build_stubbed(:note_on_commit) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" - end - end - - context 'on a Commit Diff' do - it 'returns a proper URL' do - note = build_stubbed(:diff_note_on_commit) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}" - end - end - - context 'on an Issue' do - it 'returns a proper URL' do - issue = create(:issue, iid: 42) - note = build_stubbed(:note_on_issue, noteable: issue) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}#note_#{note.id}" - end - end - - context 'on a MergeRequest' do - it 'returns a proper URL' do - merge_request = create(:merge_request, iid: 42) - note = build_stubbed(:note_on_merge_request, noteable: merge_request) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" - end - end - - context 'on a MergeRequest Diff' do - it 'returns a proper URL' do - merge_request = create(:merge_request, iid: 42) - note = build_stubbed(:diff_note_on_merge_request, noteable: merge_request) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}" - end - end - - context 'on a ProjectSnippet' do - it 'returns a proper URL' do - project_snippet = create(:project_snippet) - note = build_stubbed(:note_on_project_snippet, noteable: project_snippet) - - url = described_class.build(note) - - expect(url).to eq "#{Settings.gitlab['url']}/#{project_snippet.project.path_with_namespace}/snippets/#{note.noteable_id}#note_#{note.id}" - end - end - - context 'on another object' do - it 'returns a proper URL' do - project = build_stubbed(:empty_project) - - expect { described_class.build(project) }. - to raise_error(NotImplementedError, 'No URL builder defined for Project') - end - end - end - - context 'when passing a WikiPage' do - it 'returns a proper URL' do - wiki_page = build(:wiki_page) - url = described_class.build(wiki_page) - - expect(url).to eq "#{Gitlab.config.gitlab.url}#{wiki_page.wiki.wiki_base_path}/#{wiki_page.slug}" - end - end - end -end |