diff options
Diffstat (limited to 'spec')
33 files changed, 764 insertions, 601 deletions
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 6f4c8987637..c40b2c2a583 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -37,6 +37,20 @@ describe Admin::UsersController do end end + describe 'PUT confirm/:id' do + let(:user) { create(:user, confirmed_at: nil) } + + before do + request.env["HTTP_REFERER"] = "/" + end + + it 'confirms user' do + put :confirm, id: user.username + user.reload + expect(user.confirmed?).to be_truthy + end + end + describe 'PATCH disable_two_factor' do let(:user) { create(:user) } diff --git a/spec/controllers/branches_controller_spec.rb b/spec/controllers/branches_controller_spec.rb index bd4c946b64b..8e06d4bdc77 100644 --- a/spec/controllers/branches_controller_spec.rb +++ b/spec/controllers/branches_controller_spec.rb @@ -54,6 +54,13 @@ describe Projects::BranchesController do let(:ref) { "<script>alert('ref');</script>" } it { is_expected.to render_template('new') } end + + context "valid branch name with encoded slashes" do + let(:branch) { "feature%2Ftest" } + let(:ref) { "<script>alert('ref');</script>" } + it { is_expected.to render_template('new') } + it { project.repository.branch_names.include?('feature/test')} + end end describe "POST destroy" do @@ -74,6 +81,19 @@ describe Projects::BranchesController do it { expect(subject).to render_template('destroy') } end + context "valid branch name with unencoded slashes" do + let(:branch) { "improve/awesome" } + + it { expect(response.status).to eq(200) } + it { expect(subject).to render_template('destroy') } + end + + context "valid branch name with encoded slashes" do + let(:branch) { "improve%2Fawesome" } + + it { expect(response.status).to eq(200) } + it { expect(subject).to render_template('destroy') } + end context "invalid branch name, valid ref" do let(:branch) { "no-branch" } diff --git a/spec/controllers/profile_keys_controller_spec.rb b/spec/controllers/profile_keys_controller_spec.rb index 593d3e9eb56..b6573f105dc 100644 --- a/spec/controllers/profile_keys_controller_spec.rb +++ b/spec/controllers/profile_keys_controller_spec.rb @@ -48,6 +48,17 @@ describe Profiles::KeysController do expect(response.body).not_to eq("") expect(response.body).to eq(user.all_ssh_keys.join("\n")) + + # Unique part of key 1 + expect(response.body).to match(/PWx6WM4lhHNedGfBpPJNPpZ/) + # Key 2 + expect(response.body).to match(/AQDmTillFzNTrrGgwaCKaSj/) + end + + it "should not render the comment of the key" do + get :get_keys, username: user.username + + expect(response.body).not_to match(/dummy@gitlab.com/) end it "should respond with text/plain content type" do diff --git a/spec/factories.rb b/spec/factories.rb index 05e3211d551..200f18f660d 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -100,7 +100,7 @@ FactoryGirl.define do factory :key do title key do - "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" + "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0= dummy@gitlab.com" end factory :deploy_key, class: 'DeployKey' do diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 102678a1d74..1d500a11ad7 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -21,12 +21,13 @@ # import_url :string(255) # visibility_level :integer default(0), not null # archived :boolean default(FALSE), not null +# avatar :string(255) # import_status :string(255) # repository_size :float default(0.0) # star_count :integer default(0), not null # import_type :string(255) # import_source :string(255) -# avatar :string(255) +# commit_count :integer default(0) # FactoryGirl.define do diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb index b8199aa5e61..859a62f740f 100644 --- a/spec/features/markdown_spec.rb +++ b/spec/features/markdown_spec.rb @@ -17,410 +17,215 @@ require 'erb' # -> Post-process HTML # -> `gfm_with_options` helper # -> HTML::Pipeline -# -> Sanitize -# -> RelativeLink -# -> Emoji -# -> Table of Contents -# -> Autolinks -# -> Rinku (http, https, ftp) -# -> Other schemes -# -> ExternalLink -# -> References -# -> TaskList +# -> SanitizationFilter +# -> Other filters, depending on pipeline # -> `html_safe` # -> Template # # See the MarkdownFeature class for setup details. describe 'GitLab Markdown', feature: true do - include ActionView::Helpers::TagHelper - include ActionView::Helpers::UrlHelper include Capybara::Node::Matchers include GitlabMarkdownHelper + include MarkdownMatchers - # `markdown` calls these two methods - def current_user - @feat.user - end - - def user_color_scheme_class - :white - end - - # Let's only parse this thing once - before(:all) do - @feat = MarkdownFeature.new - - # `markdown` expects a `@project` variable - @project = @feat.project - - @md = markdown(@feat.raw_markdown) - @doc = Nokogiri::HTML::DocumentFragment.parse(@md) - end - - after(:all) do - @feat.teardown + # Sometimes it can be useful to see the parsed output of the Markdown document + # for debugging. Call this method to write the output to + # `tmp/capybara/<filename>.html`. + def write_markdown(filename = 'markdown_spec') + File.open(Rails.root.join("tmp/capybara/#{filename}.html"), 'w') do |file| + file.puts @html + end end - # Given a header ID, goes to that element's parent (the header itself), then - # its next sibling element (the body). - def get_section(id) - @doc.at_css("##{id}").parent.next_element + def doc(html = @html) + Nokogiri::HTML::DocumentFragment.parse(html) end - # Sometimes it can be useful to see the parsed output of the Markdown document - # for debugging. Uncomment this block to write the output to - # tmp/capybara/markdown_spec.html. - # - # it 'writes to a file' do - # File.open(Rails.root.join('tmp/capybara/markdown_spec.html'), 'w') do |file| - # file.puts @md - # end - # end - - describe 'Markdown' do - describe 'No Intra Emphasis' do + # Shared behavior that all pipelines should exhibit + shared_examples 'all pipelines' do + describe 'Redcarpet extensions' do it 'does not parse emphasis inside of words' do - body = get_section('no-intra-emphasis') - expect(body.to_html).not_to match('foo<em>bar</em>baz') + expect(doc.to_html).not_to match('foo<em>bar</em>baz') end - end - describe 'Tables' do it 'parses table Markdown' do - body = get_section('tables') - expect(body).to have_selector('th:contains("Header")') - expect(body).to have_selector('th:contains("Row")') - expect(body).to have_selector('th:contains("Example")') + aggregate_failures do + expect(doc).to have_selector('th:contains("Header")') + expect(doc).to have_selector('th:contains("Row")') + expect(doc).to have_selector('th:contains("Example")') + end end it 'allows Markdown in tables' do - expect(@doc.at_css('td:contains("Baz")').children.to_html). + expect(doc.at_css('td:contains("Baz")').children.to_html). to eq '<strong>Baz</strong>' end - end - describe 'Fenced Code Blocks' do it 'parses fenced code blocks' do - expect(@doc).to have_selector('pre.code.highlight.white.c') - expect(@doc).to have_selector('pre.code.highlight.white.python') + aggregate_failures do + expect(doc).to have_selector('pre.code.highlight.white.c') + expect(doc).to have_selector('pre.code.highlight.white.python') + end end - end - describe 'Strikethrough' do it 'parses strikethroughs' do - expect(@doc).to have_selector(%{del:contains("and this text doesn't")}) + expect(doc).to have_selector(%{del:contains("and this text doesn't")}) end - end - describe 'Superscript' do it 'parses superscript' do - body = get_section('superscript') - expect(body.to_html).to match('1<sup>st</sup>') - expect(body.to_html).to match('2<sup>nd</sup>') + expect(doc).to have_selector('sup', count: 2) end end - end - describe 'HTML::Pipeline' do describe 'SanitizationFilter' do - it 'uses a permissive whitelist' do - expect(@doc).to have_selector('b:contains("b tag")') - expect(@doc).to have_selector('em:contains("em tag")') - expect(@doc).to have_selector('code:contains("code tag")') - expect(@doc).to have_selector('kbd:contains("s")') - expect(@doc).to have_selector('strike:contains(Emoji)') - expect(@doc).to have_selector('img[src*="smile.png"]') - expect(@doc).to have_selector('br') - expect(@doc).to have_selector('hr') + it 'permits b elements' do + expect(doc).to have_selector('b:contains("b tag")') end - it 'permits span elements' do - expect(@doc).to have_selector('span:contains("span tag")') + it 'permits em elements' do + expect(doc).to have_selector('em:contains("em tag")') end - it 'permits table alignment' do - expect(@doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center' - expect(@doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right' - expect(@doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left' - - expect(@doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center' - expect(@doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right' - expect(@doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left' + it 'permits code elements' do + expect(doc).to have_selector('code:contains("code tag")') end - it 'removes `rel` attribute from links' do - body = get_section('sanitizationfilter') - expect(body).not_to have_selector('a[rel="bookmark"]') + it 'permits kbd elements' do + expect(doc).to have_selector('kbd:contains("s")') end - it "removes `href` from `a` elements if it's fishy" do - expect(@doc).not_to have_selector('a[href*="javascript"]') + it 'permits strike elements' do + expect(doc).to have_selector('strike:contains(Emoji)') end - end - describe 'Escaping' do - let(:table) { @doc.css('table').last.at_css('tbody') } - - it 'escapes non-tag angle brackets' do - expect(table.at_xpath('.//tr[1]/td[3]').inner_html).to eq '1 < 3 & 5' + it 'permits img elements' do + expect(doc).to have_selector('img[src*="smile.png"]') end - end - - describe 'Edge Cases' do - it 'allows markup inside link elements' do - expect(@doc.at_css('a[href="#link-emphasis"]').to_html). - to eq %{<a href="#link-emphasis"><em>text</em></a>} - - expect(@doc.at_css('a[href="#link-strong"]').to_html). - to eq %{<a href="#link-strong"><strong>text</strong></a>} - expect(@doc.at_css('a[href="#link-code"]').to_html). - to eq %{<a href="#link-code"><code>text</code></a>} + it 'permits br elements' do + expect(doc).to have_selector('br') end - end - describe 'EmojiFilter' do - it 'parses Emoji' do - expect(@doc).to have_selector('img.emoji', count: 10) + it 'permits hr elements' do + expect(doc).to have_selector('hr') end - end - describe 'TableOfContentsFilter' do - it 'creates anchors inside header elements' do - expect(@doc).to have_selector('h1 a#gitlab-markdown') - expect(@doc).to have_selector('h2 a#markdown') - expect(@doc).to have_selector('h3 a#autolinkfilter') + it 'permits span elements' do + expect(doc).to have_selector('span:contains("span tag")') end - end - describe 'AutolinkFilter' do - let(:list) { get_section('autolinkfilter').next_element } - - def item(index) - list.at_css("li:nth-child(#{index})") + it 'permits style attribute in th elements' do + aggregate_failures do + expect(doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center' + expect(doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right' + expect(doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left' + end end - it 'autolinks http://' do - expect(item(1).children.first.name).to eq 'a' - expect(item(1).children.first['href']).to eq 'http://about.gitlab.com/' + it 'permits style attribute in td elements' do + aggregate_failures do + expect(doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center' + expect(doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right' + expect(doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left' + end end - it 'autolinks https://' do - expect(item(2).children.first.name).to eq 'a' - expect(item(2).children.first['href']).to eq 'https://google.com/' + it 'removes `rel` attribute from links' do + expect(doc).not_to have_selector('a[rel="bookmark"]') end - it 'autolinks ftp://' do - expect(item(3).children.first.name).to eq 'a' - expect(item(3).children.first['href']).to eq 'ftp://ftp.us.debian.org/debian/' + it "removes `href` from `a` elements if it's fishy" do + expect(doc).not_to have_selector('a[href*="javascript"]') end + end - it 'autolinks smb://' do - expect(item(4).children.first.name).to eq 'a' - expect(item(4).children.first['href']).to eq 'smb://foo/bar/baz' + describe 'Escaping' do + it 'escapes non-tag angle brackets' do + table = doc.css('table').last.at_css('tbody') + expect(table.at_xpath('.//tr[1]/td[3]').inner_html).to eq '1 < 3 & 5' end + end - it 'autolinks irc://' do - expect(item(5).children.first.name).to eq 'a' - expect(item(5).children.first['href']).to eq 'irc://irc.freenode.net/git' - end + describe 'Edge Cases' do + it 'allows markup inside link elements' do + aggregate_failures do + expect(doc.at_css('a[href="#link-emphasis"]').to_html). + to eq %{<a href="#link-emphasis"><em>text</em></a>} - it 'autolinks short, invalid URLs' do - expect(item(6).children.first.name).to eq 'a' - expect(item(6).children.first['href']).to eq 'http://localhost:3000' - end + expect(doc.at_css('a[href="#link-strong"]').to_html). + to eq %{<a href="#link-strong"><strong>text</strong></a>} - %w(code a kbd).each do |elem| - it "ignores links inside '#{elem}' element" do - body = get_section('autolinkfilter') - expect(body).not_to have_selector("#{elem} a") + expect(doc.at_css('a[href="#link-code"]').to_html). + to eq %{<a href="#link-code"><code>text</code></a>} end end end describe 'ExternalLinkFilter' do - let(:links) { get_section('externallinkfilter').next_element } - it 'adds nofollow to external link' do - expect(links.css('a').first.to_html).to match 'nofollow' + link = doc.at_css('a:contains("Google")') + expect(link.attr('rel')).to match 'nofollow' end it 'ignores internal link' do - expect(links.css('a').last.to_html).not_to match 'nofollow' + link = doc.at_css('a:contains("GitLab Root")') + expect(link.attr('rel')).not_to match 'nofollow' end end + end - describe 'ReferenceFilter' do - it 'handles references in headers' do - header = @doc.at_css('#reference-filters-eg-1').parent - - expect(header.css('a').size).to eq 2 - end - - it "handles references in Markdown" do - body = get_section('reference-filters-eg-1') - expect(body).to have_selector('em a.gfm-merge_request', count: 1) - end - - it 'parses user references' do - body = get_section('userreferencefilter') - expect(body).to have_selector('a.gfm.gfm-project_member', count: 3) - end - - it 'parses issue references' do - body = get_section('issuereferencefilter') - expect(body).to have_selector('a.gfm.gfm-issue', count: 2) - end - - it 'parses merge request references' do - body = get_section('mergerequestreferencefilter') - expect(body).to have_selector('a.gfm.gfm-merge_request', count: 2) - end + context 'default pipeline' do + before(:all) do + @feat = MarkdownFeature.new - it 'parses snippet references' do - body = get_section('snippetreferencefilter') - expect(body).to have_selector('a.gfm.gfm-snippet', count: 2) - end + # `gfm_with_options` depends on a `@project` variable + @project = @feat.project - it 'parses commit range references' do - body = get_section('commitrangereferencefilter') - expect(body).to have_selector('a.gfm.gfm-commit_range', count: 2) - end + @html = markdown(@feat.raw_markdown) + end - it 'parses commit references' do - body = get_section('commitreferencefilter') - expect(body).to have_selector('a.gfm.gfm-commit', count: 2) - end + it_behaves_like 'all pipelines' - it 'parses label references' do - body = get_section('labelreferencefilter') - expect(body).to have_selector('a.gfm.gfm-label', count: 3) - end + it 'includes RelativeLinkFilter' do + expect(doc).to parse_relative_links end - describe 'Task Lists' do - it 'generates task lists' do - body = get_section('task-lists') - expect(body).to have_selector('ul.task-list', count: 2) - expect(body).to have_selector('li.task-list-item', count: 7) - expect(body).to have_selector('input[checked]', count: 3) - end + it 'includes EmojiFilter' do + expect(doc).to parse_emoji end - end -end - -# This is a helper class used by the GitLab Markdown feature spec -# -# Because the feature spec only cares about the output of the Markdown, and the -# test setup and teardown and parsing is fairly expensive, we only want to do it -# once. Unfortunately RSpec will not let you access `let`s in a `before(:all)` -# block, so we fake it by encapsulating all the shared setup in this class. -# -# The class renders `spec/fixtures/markdown.md.erb` using ERB, allowing for -# reference to the factory-created objects. -class MarkdownFeature - include FactoryGirl::Syntax::Methods - - def initialize - DatabaseCleaner.start - end - - def teardown - DatabaseCleaner.clean - end - - def user - @user ||= create(:user) - end - def group - unless @group - @group = create(:group) - @group.add_user(user, Gitlab::Access::DEVELOPER) + it 'includes TableOfContentsFilter' do + expect(doc).to create_header_links end - @group - end - - # Direct references ---------------------------------------------------------- - - def project - @project ||= create(:project) - end - - def issue - @issue ||= create(:issue, project: project) - end - - def merge_request - @merge_request ||= create(:merge_request, :simple, source_project: project) - end - - def snippet - @snippet ||= create(:project_snippet, project: project) - end - - def commit - @commit ||= project.commit - end - - def commit_range - unless @commit_range - commit2 = project.commit('HEAD~3') - @commit_range = CommitRange.new("#{commit.id}...#{commit2.id}", project) + it 'includes AutolinkFilter' do + expect(doc).to create_autolinks end - @commit_range - end - - def simple_label - @simple_label ||= create(:label, name: 'gfm', project: project) - end - - def label - @label ||= create(:label, name: 'awaiting feedback', project: project) - end - - # Cross-references ----------------------------------------------------------- - - def xproject - unless @xproject - namespace = create(:namespace, name: 'cross-reference') - @xproject = create(:project, namespace: namespace) - @xproject.team << [user, :developer] + it 'includes all reference filters' do + aggregate_failures do + expect(doc).to reference_users + expect(doc).to reference_issues + expect(doc).to reference_merge_requests + expect(doc).to reference_snippets + expect(doc).to reference_commit_ranges + expect(doc).to reference_commits + expect(doc).to reference_labels + end end - @xproject - end - - def xissue - @xissue ||= create(:issue, project: xproject) - end - - def xmerge_request - @xmerge_request ||= create(:merge_request, :simple, source_project: xproject) - end - - def xsnippet - @xsnippet ||= create(:project_snippet, project: xproject) - end - - def xcommit - @xcommit ||= xproject.commit - end - - def xcommit_range - unless @xcommit_range - xcommit2 = xproject.commit('HEAD~2') - @xcommit_range = CommitRange.new("#{xcommit.id}...#{xcommit2.id}", xproject) + it 'includes TaskListFilter' do + expect(doc).to parse_task_lists end + end - @xcommit_range + # `markdown` calls these two methods + def current_user + @feat.user end - def raw_markdown - fixture = Rails.root.join('spec/fixtures/markdown.md.erb') - ERB.new(File.read(fixture)).result(binding) + def user_color_scheme_class + :white end end diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb index 02ab46c905a..41d12afa9ce 100644 --- a/spec/fixtures/markdown.md.erb +++ b/spec/fixtures/markdown.md.erb @@ -100,6 +100,13 @@ Markdown should be usable inside a link. Let's try! - [**text**](#link-strong) - [`text`](#link-code) +### RelativeLinkFilter + +Linking to a file relative to this project's repository should work. + +[Relative Link](doc/README.md) + + ### EmojiFilter Because life would be :zzz: without Emoji, right? :rocket: @@ -123,9 +130,9 @@ These are all plain text that should get turned into links: But it shouldn't autolink text inside certain tags: -- <code>http://about.gitlab.com/</code> -- <a>http://about.gitlab.com/</a> -- <kbd>http://about.gitlab.com/</kbd> +- <code>http://code.gitlab.com/</code> +- <a>http://a.gitlab.com/</a> +- <kbd>http://kbd.gitlab.com/</kbd> ### ExternalLinkFilter diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb index e49e4e6d5d8..76009c36099 100644 --- a/spec/helpers/blob_helper_spec.rb +++ b/spec/helpers/blob_helper_spec.rb @@ -6,6 +6,14 @@ describe BlobHelper do let(:no_context_content) { ":type \"assem\"))" } let(:blob_content) { "(make-pathname :defaults name\n#{no_context_content}" } let(:split_content) { blob_content.split("\n") } + let(:multiline_content) do + %q( + def test(input): + """This is line 1 of a multi-line comment. + This is line 2. + """ + ) + end it 'should return plaintext for unknown lexer context' do result = highlight(blob_name, no_context_content, nowrap: true, continue: false) @@ -29,5 +37,15 @@ describe BlobHelper do result = split_content.map{ |content| highlight(blob_name, content, nowrap: true, continue: true) } expect(result).to eq(expected) end + + it 'should highlight multi-line comments' do + result = highlight(blob_name, multiline_content, nowrap: true, continue: false) + html = Nokogiri::HTML(result) + lines = html.search('.s') + expect(lines.count).to eq(3) + expect(lines[0].text).to eq('"""This is line 1 of a multi-line comment.') + expect(lines[1].text).to eq(' This is line 2.') + expect(lines[2].text).to eq(' """') + end end end diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb index 4439775f612..9c115bbfc6a 100644 --- a/spec/lib/extracts_path_spec.rb +++ b/spec/lib/extracts_path_spec.rb @@ -29,6 +29,16 @@ describe ExtractsPath do assign_ref_vars expect(@logs_path).to eq("/#{@project.path_with_namespace}/refs/#{ref}/logs_tree/files/ruby/popen.rb") end + + context 'escaped sequences in ref' do + let(:ref) { "improve%2Fawesome" } + + it "id should have no escape sequences" do + assign_ref_vars + expect(@ref).to eq('improve/awesome') + expect(@logs_path).to eq("/#{@project.path_with_namespace}/refs/#{ref}/logs_tree/files/ruby/popen.rb") + end + end end describe '#extract_ref' do diff --git a/spec/lib/gitlab/markdown/relative_link_filter_spec.rb b/spec/lib/gitlab/markdown/relative_link_filter_spec.rb index 5ee5310825d..7f4d67e403f 100644 --- a/spec/lib/gitlab/markdown/relative_link_filter_spec.rb +++ b/spec/lib/gitlab/markdown/relative_link_filter_spec.rb @@ -1,3 +1,5 @@ +# encoding: UTF-8 + require 'spec_helper' module Gitlab::Markdown @@ -101,6 +103,20 @@ module Gitlab::Markdown expect(doc.at_css('a')['href']).to eq 'http://example.com' end + it 'supports Unicode filenames' do + path = 'files/images/한글.png' + escaped = Addressable::URI.escape(path) + + # Stub these methods so the file doesn't actually need to be in the repo + allow_any_instance_of(described_class).to receive(:file_exists?). + and_return(true) + allow_any_instance_of(described_class). + to receive(:image?).with(path).and_return(true) + + doc = filter(image(escaped)) + expect(doc.at_css('img')['src']).to match '/raw/' + end + context 'when requested path is a file in the repo' do let(:requested_path) { 'doc/api/README.md' } include_examples :relative_to_requested diff --git a/spec/lib/gitlab/note_data_builder_spec.rb b/spec/lib/gitlab/note_data_builder_spec.rb index 5826144e66b..448cd0c6880 100644 --- a/spec/lib/gitlab/note_data_builder_spec.rb +++ b/spec/lib/gitlab/note_data_builder_spec.rb @@ -36,7 +36,6 @@ describe 'Gitlab::NoteDataBuilder' do let(:note) { create(:note_on_issue, noteable_id: issue.id) } it 'returns the note and issue-specific data' do - data[:issue]["updated_at"] = fixed_time expect(data).to have_key(:issue) expect(data[:issue]).to eq(issue.hook_attrs) end @@ -47,7 +46,6 @@ describe 'Gitlab::NoteDataBuilder' do let(:note) { create(:note_on_merge_request, noteable_id: merge_request.id) } it 'returns the note and merge request data' do - data[:merge_request]["updated_at"] = fixed_time expect(data).to have_key(:merge_request) expect(data[:merge_request]).to eq(merge_request.hook_attrs) end @@ -58,7 +56,6 @@ describe 'Gitlab::NoteDataBuilder' do let(:note) { create(:note_on_merge_request_diff, noteable_id: merge_request.id) } it 'returns the note and merge request diff data' do - data[:merge_request]["updated_at"] = fixed_time expect(data).to have_key(:merge_request) expect(data[:merge_request]).to eq(merge_request.hook_attrs) end @@ -69,7 +66,6 @@ describe 'Gitlab::NoteDataBuilder' do let!(:note) { create(:note_on_project_snippet, noteable_id: snippet.id) } it 'returns the note and project snippet data' do - data[:snippet]["updated_at"] = fixed_time expect(data).to have_key(:snippet) expect(data[:snippet]).to eq(snippet.hook_attrs) end diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb index f921dd9cc09..088e34f050c 100644 --- a/spec/lib/gitlab/reference_extractor_spec.rb +++ b/spec/lib/gitlab/reference_extractor_spec.rb @@ -25,7 +25,7 @@ describe Gitlab::ReferenceExtractor do project.team << [@u_bar, :guest] subject.analyze(%Q{ - Inline code: `@foo` + Inline code: `@foo` Code block: @@ -33,7 +33,7 @@ describe Gitlab::ReferenceExtractor do @bar ``` - Quote: + Quote: > @offteam }) @@ -49,8 +49,8 @@ describe Gitlab::ReferenceExtractor do end it 'accesses valid merge requests' do - @m0 = create(:merge_request, source_project: project, target_project: project, source_branch: 'aaa') - @m1 = create(:merge_request, source_project: project, target_project: project, source_branch: 'bbb') + @m0 = create(:merge_request, source_project: project, target_project: project, source_branch: 'markdown') + @m1 = create(:merge_request, source_project: project, target_project: project, source_branch: 'feature_conflict') subject.analyze("!999, !#{@m1.iid}, and !#{@m0.iid}.") expect(subject.merge_requests).to eq([@m1, @m0]) diff --git a/spec/lib/gitlab/satellite/action_spec.rb b/spec/lib/gitlab/satellite/action_spec.rb deleted file mode 100644 index 0a93676edc3..00000000000 --- a/spec/lib/gitlab/satellite/action_spec.rb +++ /dev/null @@ -1,116 +0,0 @@ -require 'spec_helper' - -describe 'Gitlab::Satellite::Action' do - let(:project) { create(:project) } - let(:user) { create(:user) } - - describe '#prepare_satellite!' do - it 'should be able to fetch timeout from conf' do - expect(Gitlab::Satellite::Action::DEFAULT_OPTIONS[:git_timeout]).to eq(30.seconds) - end - - it 'create a repository with a parking branch and one remote: origin' do - repo = project.satellite.repo - - #now lets dirty it up - - starting_remote_count = repo.git.list_remotes.size - expect(starting_remote_count).to be >= 1 - #kind of hookey way to add a second remote - origin_uri = repo.git.remote({ v: true }).split(" ")[1] - - repo.git.remote({ raise: true }, 'add', 'another-remote', origin_uri) - repo.git.branch({ raise: true }, 'a-new-branch') - - expect(repo.heads.size).to be > (starting_remote_count) - expect(repo.git.remote().split(" ").size).to be > (starting_remote_count) - - repo.git.config({}, "user.name", "#{user.name} -- foo") - repo.git.config({}, "user.email", "#{user.email} -- foo") - expect(repo.config['user.name']).to eq("#{user.name} -- foo") - expect(repo.config['user.email']).to eq("#{user.email} -- foo") - - - #These must happen in the context of the satellite directory... - satellite_action = Gitlab::Satellite::Action.new(user, project) - project.satellite.lock do - #Now clean it up, use send to get around prepare_satellite! being protected - satellite_action.send(:prepare_satellite!, repo) - end - - #verify it's clean - heads = repo.heads.map(&:name) - expect(heads.size).to eq(1) - expect(heads.include?(Gitlab::Satellite::Satellite::PARKING_BRANCH)).to eq(true) - remotes = repo.git.remote().split(' ') - expect(remotes.size).to eq(1) - expect(remotes.include?('origin')).to eq(true) - expect(repo.config['user.name']).to eq(user.name) - expect(repo.config['user.email']).to eq(user.email) - end - end - - describe '#in_locked_and_timed_satellite' do - - it 'should make use of a lockfile' do - repo = project.satellite.repo - called = false - - #set assumptions - FileUtils.rm_f(project.satellite.lock_file) - - expect(File.exists?(project.satellite.lock_file)).to be_falsey - - satellite_action = Gitlab::Satellite::Action.new(user, project) - satellite_action.send(:in_locked_and_timed_satellite) do |sat_repo| - expect(repo).to eq(sat_repo) - expect(File.exists? project.satellite.lock_file).to be_truthy - called = true - end - - expect(called).to be_truthy - - end - - it 'should be able to use the satellite after locking' do - repo = project.satellite.repo - called = false - - # Set base assumptions - if File.exists? project.satellite.lock_file - expect(FileLockStatusChecker.new(project.satellite.lock_file).flocked?).to be_falsey - end - - satellite_action = Gitlab::Satellite::Action.new(user, project) - satellite_action.send(:in_locked_and_timed_satellite) do |sat_repo| - called = true - expect(repo).to eq(sat_repo) - expect(File.exists? project.satellite.lock_file).to be_truthy - expect(FileLockStatusChecker.new(project.satellite.lock_file).flocked?).to be_truthy - end - - expect(called).to be_truthy - expect(FileLockStatusChecker.new(project.satellite.lock_file).flocked?).to be_falsey - - end - - class FileLockStatusChecker < File - def flocked?(&block) - status = flock LOCK_EX|LOCK_NB - case status - when false - return true - when 0 - begin - block ? block.call : false - ensure - flock LOCK_UN - end - else - raise SystemCallError, status - end - end - end - - end -end diff --git a/spec/lib/gitlab/satellite/merge_action_spec.rb b/spec/lib/gitlab/satellite/merge_action_spec.rb deleted file mode 100644 index 9b1c9a34e29..00000000000 --- a/spec/lib/gitlab/satellite/merge_action_spec.rb +++ /dev/null @@ -1,104 +0,0 @@ -require 'spec_helper' - -describe 'Gitlab::Satellite::MergeAction' do - include RepoHelpers - - let(:project) { create(:project, namespace: create(:group)) } - let(:fork_project) { create(:project, namespace: create(:group), forked_from_project: project) } - let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } - let(:merge_request_fork) { create(:merge_request, source_project: fork_project, target_project: project) } - - let(:merge_request_with_conflict) { create(:merge_request, :conflict, source_project: project, target_project: project) } - let(:merge_request_fork_with_conflict) { create(:merge_request, :conflict, source_project: project, target_project: project) } - - describe '#commits_between' do - def verify_commits(commits, first_commit_sha, last_commit_sha) - commits.each { |commit| expect(commit.class).to eq(Gitlab::Git::Commit) } - expect(commits.first.id).to eq(first_commit_sha) - expect(commits.last.id).to eq(last_commit_sha) - end - - context 'on fork' do - it 'should get proper commits between' do - commits = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).commits_between - verify_commits(commits, sample_compare.commits.first, sample_compare.commits.last) - end - end - - context 'between branches' do - it 'should raise exception -- not expected to be used by non forks' do - expect { Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).commits_between }.to raise_error(RuntimeError) - end - end - end - - describe '#format_patch' do - def verify_content(patch) - sample_compare.commits.each do |commit| - expect(patch.include?(commit)).to be_truthy - end - end - - context 'on fork' do - it 'should build a format patch' do - patch = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).format_patch - verify_content(patch) - end - end - - context 'between branches' do - it 'should build a format patch' do - patch = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request).format_patch - verify_content(patch) - end - end - end - - describe '#diffs_between_satellite tested against diff_in_satellite' do - def is_a_matching_diff(diff, diffs) - diff_count = diff.scan('diff --git').size - expect(diff_count).to be >= 1 - expect(diffs.size).to eq(diff_count) - diffs.each do |a_diff| - expect(a_diff.class).to eq(Gitlab::Git::Diff) - expect(diff.include? a_diff.diff).to be_truthy - end - end - - context 'on fork' do - it 'should get proper diffs' do - diffs = Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).diffs_between_satellite - diff = Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request_fork).diff_in_satellite - is_a_matching_diff(diff, diffs) - end - end - - context 'between branches' do - it 'should get proper diffs' do - expect{ Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).diffs_between_satellite }.to raise_error(RuntimeError) - end - end - end - - describe '#can_be_merged?' do - context 'on fork' do - it do - expect(Gitlab::Satellite::MergeAction.new(merge_request_fork.author, merge_request_fork).can_be_merged?).to be_truthy - end - - it do - expect(Gitlab::Satellite::MergeAction.new(merge_request_fork_with_conflict.author, merge_request_fork_with_conflict).can_be_merged?).to be_falsey - end - end - - context 'between branches' do - it do - expect(Gitlab::Satellite::MergeAction.new(merge_request.author, merge_request).can_be_merged?).to be_truthy - end - - it do - expect(Gitlab::Satellite::MergeAction.new(merge_request_with_conflict.author, merge_request_with_conflict).can_be_merged?).to be_falsey - end - end - end -end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index d648f4078be..bc14ff98fd8 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -14,11 +14,14 @@ # default_branch_protection :integer default(2) # twitter_sharing_enabled :boolean default(TRUE) # restricted_visibility_levels :text +# version_check_enabled :boolean default(TRUE) # max_attachment_size :integer default(10), not null -# session_expire_delay :integer default(10080), not null # default_project_visibility :integer # default_snippet_visibility :integer # restricted_signup_domains :text +# user_oauth_applications :boolean default(TRUE) +# after_sign_out_path :string(255) +# session_expire_delay :integer default(10080), not null # require 'spec_helper' diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb index 456bf221d62..2f819f60cbb 100644 --- a/spec/models/key_spec.rb +++ b/spec/models/key_spec.rb @@ -32,6 +32,13 @@ describe Key do describe "Methods" do it { is_expected.to respond_to :projects } + it { is_expected.to respond_to :publishable_key } + + describe "#publishable_keys" do + it 'strips all personal information' do + expect(build(:key).publishable_key).not_to match(/dummy@gitlab/) + end + end end context "validation of uniqueness" do diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 76f6d8c54c4..b91687bc09f 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -165,7 +165,7 @@ describe MergeRequest do end it_behaves_like 'an editable mentionable' do - subject { create(:merge_request, source_project: project) } + subject { create(:merge_request) } let(:backref_text) { "merge request #{subject.to_reference}" } let(:set_mentionable_text) { ->(txt){ subject.description = txt } } diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index eba33dd510f..250d1e2da80 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -22,7 +22,7 @@ require 'spec_helper' describe Note do describe 'associations' do it { is_expected.to belong_to(:project) } - it { is_expected.to belong_to(:noteable).touch(true) } + it { is_expected.to belong_to(:noteable) } it { is_expected.to belong_to(:author).class_name('User') } end diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb index 69466b11f09..97b60e19e40 100644 --- a/spec/models/project_services/slack_service_spec.rb +++ b/spec/models/project_services/slack_service_spec.rb @@ -67,7 +67,7 @@ describe SlackService do opts = { title: 'Awesome merge_request', description: 'please fix', - source_branch: 'stable', + source_branch: 'feature', target_branch: 'master' } merge_service = MergeRequests::CreateService.new(project, diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 63091e913ff..2fcbd5ae108 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -21,12 +21,13 @@ # import_url :string(255) # visibility_level :integer default(0), not null # archived :boolean default(FALSE), not null +# avatar :string(255) # import_status :string(255) # repository_size :float default(0.0) # star_count :integer default(0), not null # import_type :string(255) # import_source :string(255) -# avatar :string(255) +# commit_count :integer default(0) # require 'spec_helper' @@ -90,7 +91,6 @@ describe Project do describe 'Respond to' do it { is_expected.to respond_to(:url_to_repo) } it { is_expected.to respond_to(:repo_exists?) } - it { is_expected.to respond_to(:satellite) } it { is_expected.to respond_to(:update_merge_requests) } it { is_expected.to respond_to(:execute_hooks) } it { is_expected.to respond_to(:name_with_namespace) } @@ -111,14 +111,20 @@ describe Project do expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git') end - it 'returns the full web URL for this repo' do - project = Project.new(path: 'somewhere') - expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/somewhere") + describe "#web_url" do + let(:project) { create(:empty_project, path: "somewhere") } + + it 'returns the full web URL for this repo' do + expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.namespace.path}/somewhere") + end end - it 'returns the web URL without the protocol for this repo' do - project = Project.new(path: 'somewhere') - expect(project.web_url_without_protocol).to eq("#{Gitlab.config.gitlab.url.split('://')[1]}/somewhere") + describe "#web_url_without_protocol" do + let(:project) { create(:empty_project, path: "somewhere") } + + it 'returns the web URL without the protocol for this repo' do + expect(project.web_url_without_protocol).to eq("#{Gitlab.config.gitlab.url.split('://')[1]}/#{project.namespace.path}/somewhere") + end end describe 'last_activity methods' do diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index d25351b0f0e..d96244f23e0 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -36,13 +36,13 @@ describe Repository do describe :can_be_merged? do context 'mergeable branches' do - subject { repository.can_be_merged?('feature', 'master') } + subject { repository.can_be_merged?('0b4bc9a49b562e85de7cc9e834518ea6828729b9', 'master') } it { is_expected.to be_truthy } end context 'non-mergeable branches' do - subject { repository.can_be_merged?('feature_conflict', 'feature') } + subject { repository.can_be_merged?('bb5206fee213d983da88c47f9cf4cc6caf9c66dc', 'feature') } it { is_expected.to be_falsey } end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 76f16323e2f..876cfb1204a 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -57,6 +57,7 @@ # otp_backup_codes :text # public_email :string(255) default(""), not null # dashboard :integer default(0) +# project_view :integer default(0) # require 'spec_helper' @@ -183,6 +184,19 @@ describe User do it { is_expected.to respond_to(:private_token) } end + describe '#confirm' do + let(:user) { create(:user, confirmed_at: nil, unconfirmed_email: 'test@gitlab.com') } + + it 'returns unconfirmed' do + expect(user.confirmed?).to be_falsey + end + + it 'confirms a user' do + user.confirm! + expect(user.confirmed?).to be_truthy + end + end + describe '#to_reference' do let(:user) { create(:user) } diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index 6c7860511e8..8cb8790c339 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -110,7 +110,7 @@ describe API::API, api: true do expect(response.status).to eq(400) end - it "should return a 400 if satellite fails to create file" do + it "should return a 400 if fails to create file" do allow_any_instance_of(Repository).to receive(:remove_file).and_return(false) delete api("/projects/#{project.id}/repository/files", user), valid_params diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 29db035b2de..942768fa254 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -148,7 +148,7 @@ describe API::API, api: true do it "should return merge_request" do post api("/projects/#{project.id}/merge_requests", user), title: 'Test merge_request', - source_branch: 'stable', + source_branch: 'feature_conflict', target_branch: 'master', author: user, labels: 'label, label2' @@ -171,20 +171,20 @@ describe API::API, api: true do it "should return 400 when target_branch is missing" do post api("/projects/#{project.id}/merge_requests", user), - title: "Test merge_request", source_branch: "stable", author: user + title: "Test merge_request", source_branch: "markdown", author: user expect(response.status).to eq(400) end it "should return 400 when title is missing" do post api("/projects/#{project.id}/merge_requests", user), - target_branch: 'master', source_branch: 'stable' + target_branch: 'master', source_branch: 'markdown' expect(response.status).to eq(400) end it 'should return 400 on invalid label names' do post api("/projects/#{project.id}/merge_requests", user), title: 'Test merge_request', - source_branch: 'stable', + source_branch: 'markdown', target_branch: 'master', author: user, labels: 'label, ?' @@ -198,7 +198,7 @@ describe API::API, api: true do before do post api("/projects/#{project.id}/merge_requests", user), title: 'Test merge_request', - source_branch: 'stable', + source_branch: 'feature_conflict', target_branch: 'master', author: user @mr = MergeRequest.all.last @@ -208,7 +208,7 @@ describe API::API, api: true do expect do post api("/projects/#{project.id}/merge_requests", user), title: 'New test merge_request', - source_branch: 'stable', + source_branch: 'feature_conflict', target_branch: 'master', author: user end.to change { MergeRequest.count }.by(0) @@ -228,7 +228,8 @@ describe API::API, api: true do it "should return merge_request" do post api("/projects/#{fork_project.id}/merge_requests", user2), - title: 'Test merge_request', source_branch: "stable", target_branch: "master", author: user2, target_project_id: project.id, description: 'Test description for Test merge_request' + title: 'Test merge_request', source_branch: "feature_conflict", target_branch: "master", + author: user2, target_project_id: project.id, description: 'Test description for Test merge_request' expect(response.status).to eq(201) expect(json_response['title']).to eq('Test merge_request') expect(json_response['description']).to eq('Test description for Test merge_request') @@ -258,7 +259,7 @@ describe API::API, api: true do it "should return 400 when title is missing" do post api("/projects/#{fork_project.id}/merge_requests", user2), - target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: project.id + target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: project.id expect(response.status).to eq(400) end @@ -267,7 +268,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/merge_requests", user), title: 'Test merge_request', target_branch: 'master', - source_branch: 'stable', + source_branch: 'markdown', author: user, target_project_id: fork_project.id expect(response.status).to eq(422) @@ -277,7 +278,7 @@ describe API::API, api: true do post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', target_branch: 'master', - source_branch: 'stable', + source_branch: 'markdown', author: user2, target_project_id: unrelated_project.id expect(response.status).to eq(422) @@ -286,7 +287,7 @@ describe API::API, api: true do it "should return 201 when target_branch is specified and for the same project" do post api("/projects/#{fork_project.id}/merge_requests", user2), - title: 'Test merge_request', target_branch: 'master', source_branch: 'stable', author: user2, target_project_id: fork_project.id + title: 'Test merge_request', target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: fork_project.id expect(response.status).to eq(201) end end @@ -302,9 +303,6 @@ describe API::API, api: true do describe "PUT /projects/:id/merge_request/:merge_request_id/merge" do it "should return merge_request in case of success" do - allow_any_instance_of(MergeRequest). - to receive_messages(can_be_merged?: true, automerge!: true) - put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user) expect(response.status).to eq(200) diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index c4dd1f76cf2..f2aa369985e 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -6,6 +6,7 @@ describe API::API, api: true do let(:user) { create(:user) } let(:admin) { create(:admin) } let(:key) { create(:key, user: user) } + let(:email) { create(:email, user: user) } describe "GET /users" do context "when unauthenticated" do @@ -384,6 +385,87 @@ describe API::API, api: true do end end + describe "POST /users/:id/emails" do + before { admin } + + it "should not create invalid email" do + post api("/users/#{user.id}/emails", admin), {} + expect(response.status).to eq(400) + expect(json_response['message']).to eq('400 (Bad request) "email" not given') + end + + it "should create email" do + email_attrs = attributes_for :email + expect do + post api("/users/#{user.id}/emails", admin), email_attrs + end.to change{ user.emails.count }.by(1) + end + end + + describe 'GET /user/:uid/emails' do + before { admin } + + context 'when unauthenticated' do + it 'should return authentication error' do + get api("/users/#{user.id}/emails") + expect(response.status).to eq(401) + end + end + + context 'when authenticated' do + it 'should return 404 for non-existing user' do + get api('/users/999999/emails', admin) + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 User Not Found') + end + + it 'should return array of emails' do + user.emails << email + user.save + get api("/users/#{user.id}/emails", admin) + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['email']).to eq(email.email) + end + end + end + + describe 'DELETE /user/:uid/emails/:id' do + before { admin } + + context 'when unauthenticated' do + it 'should return authentication error' do + delete api("/users/#{user.id}/emails/42") + expect(response.status).to eq(401) + end + end + + context 'when authenticated' do + it 'should delete existing email' do + user.emails << email + user.save + expect do + delete api("/users/#{user.id}/emails/#{email.id}", admin) + end.to change { user.emails.count }.by(-1) + expect(response.status).to eq(200) + end + + it 'should return 404 error if user not found' do + user.emails << email + user.save + delete api("/users/999999/emails/#{email.id}", admin) + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 User Not Found') + end + + it 'should return 404 error if email not foud' do + delete api("/users/#{user.id}/emails/42", admin) + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 Email Not Found') + end + end + end + describe "DELETE /users/:id" do before { admin } @@ -528,6 +610,95 @@ describe API::API, api: true do end end + describe "GET /user/emails" do + context "when unauthenticated" do + it "should return authentication error" do + get api("/user/emails") + expect(response.status).to eq(401) + end + end + + context "when authenticated" do + it "should return array of emails" do + user.emails << email + user.save + get api("/user/emails", user) + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first["email"]).to eq(email.email) + end + end + end + + describe "GET /user/emails/:id" do + it "should return single email" do + user.emails << email + user.save + get api("/user/emails/#{email.id}", user) + expect(response.status).to eq(200) + expect(json_response["email"]).to eq(email.email) + end + + it "should return 404 Not Found within invalid ID" do + get api("/user/emails/42", user) + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 Not found') + end + + it "should return 404 error if admin accesses user's email" do + user.emails << email + user.save + admin + get api("/user/emails/#{email.id}", admin) + expect(response.status).to eq(404) + expect(json_response['message']).to eq('404 Not found') + end + end + + describe "POST /user/emails" do + it "should create email" do + email_attrs = attributes_for :email + expect do + post api("/user/emails", user), email_attrs + end.to change{ user.emails.count }.by(1) + expect(response.status).to eq(201) + end + + it "should return a 401 error if unauthorized" do + post api("/user/emails"), email: 'some email' + expect(response.status).to eq(401) + end + + it "should not create email with invalid email" do + post api("/user/emails", user), {} + expect(response.status).to eq(400) + expect(json_response['message']).to eq('400 (Bad request) "email" not given') + end + end + + describe "DELETE /user/emails/:id" do + it "should delete existed email" do + user.emails << email + user.save + expect do + delete api("/user/emails/#{email.id}", user) + end.to change{user.emails.count}.by(-1) + expect(response.status).to eq(200) + end + + it "should return success if email ID not found" do + delete api("/user/emails/42", user) + expect(response.status).to eq(200) + end + + it "should return 401 error if unauthorized" do + user.emails << email + user.save + delete api("/user/emails/#{email.id}") + expect(response.status).to eq(401) + end + end + describe 'PUT /user/:id/block' do before { admin } it 'should block existing user' do diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 0040718d9be..82f62a8709c 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -210,8 +210,8 @@ end # diffs_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/diffs(.:format) projects/merge_requests#diffs # commits_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/commits(.:format) projects/merge_requests#commits -# automerge_namespace_project_merge_request POST /:namespace_id/:project_id/merge_requests/:id/automerge(.:format) projects/merge_requests#automerge -# automerge_check_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/automerge_check(.:format) projects/merge_requests#automerge_check +# merge_namespace_project_merge_request POST /:namespace_id/:project_id/merge_requests/:id/merge(.:format) projects/merge_requests#merge +# merge_check_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/merge_check(.:format) projects/merge_requests#merge_check # ci_status_namespace_project_merge_request GET /:namespace_id/:project_id/merge_requests/:id/ci_status(.:format) projects/merge_requests#ci_status # toggle_subscription_namespace_project_merge_request POST /:namespace_id/:project_id/merge_requests/:id/toggle_subscription(.:format) projects/merge_requests#toggle_subscription # branch_from_namespace_project_merge_requests GET /:namespace_id/:project_id/merge_requests/branch_from(.:format) projects/merge_requests#branch_from @@ -233,15 +233,15 @@ describe Projects::MergeRequestsController, 'routing' do expect(get('/gitlab/gitlabhq/merge_requests/1/commits')).to route_to('projects/merge_requests#commits', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') end - it 'to #automerge' do - expect(post('/gitlab/gitlabhq/merge_requests/1/automerge')).to route_to( - 'projects/merge_requests#automerge', + it 'to #merge' do + expect(post('/gitlab/gitlabhq/merge_requests/1/merge')).to route_to( + 'projects/merge_requests#merge', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1' ) end - it 'to #automerge_check' do - expect(get('/gitlab/gitlabhq/merge_requests/1/automerge_check')).to route_to('projects/merge_requests#automerge_check', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') + it 'to #merge_check' do + expect(get('/gitlab/gitlabhq/merge_requests/1/merge_check')).to route_to('projects/merge_requests#merge_check', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') end it 'to #branch_from' do diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb index d9bfdf64308..cc64d69361e 100644 --- a/spec/services/merge_requests/create_service_spec.rb +++ b/spec/services/merge_requests/create_service_spec.rb @@ -10,7 +10,7 @@ describe MergeRequests::CreateService do { title: 'Awesome merge_request', description: 'please fix', - source_branch: 'stable', + source_branch: 'feature', target_branch: 'master' } end diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index 0a25fb12f4e..7b564d34d7b 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -24,11 +24,6 @@ describe MergeRequests::MergeService do it { expect(merge_request).to be_valid } it { expect(merge_request).to be_merged } - it 'should execute hooks with merge action' do - expect(service).to have_received(:execute_hooks). - with(merge_request, 'merge') - end - it 'should send email to user2 about merge of new merge_request' do email = ActionMailer::Base.deliveries.last expect(email.to.first).to eq(user2.email) diff --git a/spec/support/markdown_feature.rb b/spec/support/markdown_feature.rb new file mode 100644 index 00000000000..2a868aed73b --- /dev/null +++ b/spec/support/markdown_feature.rb @@ -0,0 +1,106 @@ +# This is a helper class used by the GitLab Markdown feature spec +# +# Because the feature spec only cares about the output of the Markdown, and the +# test setup and teardown and parsing is fairly expensive, we only want to do it +# once. Unfortunately RSpec will not let you access `let`s in a `before(:all)` +# block, so we fake it by encapsulating all the shared setup in this class. +# +# The class renders `spec/fixtures/markdown.md.erb` using ERB, allowing for +# reference to the factory-created objects. +class MarkdownFeature + include FactoryGirl::Syntax::Methods + + def user + @user ||= create(:user) + end + + def group + unless @group + @group = create(:group) + @group.add_user(user, Gitlab::Access::DEVELOPER) + end + + @group + end + + # Direct references ---------------------------------------------------------- + + def project + @project ||= create(:project) + end + + def issue + @issue ||= create(:issue, project: project) + end + + def merge_request + @merge_request ||= create(:merge_request, :simple, source_project: project) + end + + def snippet + @snippet ||= create(:project_snippet, project: project) + end + + def commit + @commit ||= project.commit + end + + def commit_range + unless @commit_range + commit2 = project.commit('HEAD~3') + @commit_range = CommitRange.new("#{commit.id}...#{commit2.id}", project) + end + + @commit_range + end + + def simple_label + @simple_label ||= create(:label, name: 'gfm', project: project) + end + + def label + @label ||= create(:label, name: 'awaiting feedback', project: project) + end + + # Cross-references ----------------------------------------------------------- + + def xproject + unless @xproject + namespace = create(:namespace, name: 'cross-reference') + @xproject = create(:project, namespace: namespace) + @xproject.team << [user, :developer] + end + + @xproject + end + + def xissue + @xissue ||= create(:issue, project: xproject) + end + + def xmerge_request + @xmerge_request ||= create(:merge_request, :simple, source_project: xproject) + end + + def xsnippet + @xsnippet ||= create(:project_snippet, project: xproject) + end + + def xcommit + @xcommit ||= xproject.commit + end + + def xcommit_range + unless @xcommit_range + xcommit2 = xproject.commit('HEAD~2') + @xcommit_range = CommitRange.new("#{xcommit.id}...#{xcommit2.id}", xproject) + end + + @xcommit_range + end + + def raw_markdown + fixture = Rails.root.join('spec/fixtures/markdown.md.erb') + ERB.new(File.read(fixture)).result(binding) + end +end diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb new file mode 100644 index 00000000000..9df226c3af8 --- /dev/null +++ b/spec/support/matchers/markdown_matchers.rb @@ -0,0 +1,156 @@ +# MarkdownMatchers +# +# Custom matchers for our custom HTML::Pipeline filters. These are used to test +# that specific filters are or are not used by our defined pipelines. +# +# Must be included manually. +module MarkdownMatchers + extend RSpec::Matchers::DSL + include Capybara::Node::Matchers + + # RelativeLinkFilter + matcher :parse_relative_links do + set_default_markdown_messages + + match do |actual| + link = actual.at_css('a:contains("Relative Link")') + image = actual.at_css('img[alt="Relative Image"]') + + expect(link['href']).to end_with('master/doc/README.md') + expect(image['src']).to end_with('master/app/assets/images/touch-icon-ipad.png') + end + end + + # EmojiFilter + matcher :parse_emoji do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('img.emoji', count: 10) + end + end + + # TableOfContentsFilter + matcher :create_header_links do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('h1 a#gitlab-markdown') + expect(actual).to have_selector('h2 a#markdown') + expect(actual).to have_selector('h3 a#autolinkfilter') + end + end + + # AutolinkFilter + matcher :create_autolinks do + def have_autolink(link) + have_link(link, href: link) + end + + set_default_markdown_messages + + match do |actual| + expect(actual).to have_autolink('http://about.gitlab.com/') + expect(actual).to have_autolink('https://google.com/') + expect(actual).to have_autolink('ftp://ftp.us.debian.org/debian/') + expect(actual).to have_autolink('smb://foo/bar/baz') + expect(actual).to have_autolink('irc://irc.freenode.net/git') + expect(actual).to have_autolink('http://localhost:3000') + + %w(code a kbd).each do |elem| + expect(body).not_to have_selector("#{elem} a") + end + end + end + + # UserReferenceFilter + matcher :reference_users do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('a.gfm.gfm-project_member', count: 3) + end + end + + # IssueReferenceFilter + matcher :reference_issues do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('a.gfm.gfm-issue', count: 3) + end + end + + # MergeRequestReferenceFilter + matcher :reference_merge_requests do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('a.gfm.gfm-merge_request', count: 3) + expect(actual).to have_selector('em a.gfm-merge_request') + end + end + + # SnippetReferenceFilter + matcher :reference_snippets do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('a.gfm.gfm-snippet', count: 2) + end + end + + # CommitRangeReferenceFilter + matcher :reference_commit_ranges do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('a.gfm.gfm-commit_range', count: 2) + end + end + + # CommitReferenceFilter + matcher :reference_commits do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('a.gfm.gfm-commit', count: 2) + end + end + + # LabelReferenceFilter + matcher :reference_labels do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('a.gfm.gfm-label', count: 3) + end + end + + # TaskListFilter + matcher :parse_task_lists do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('ul.task-list', count: 2) + expect(actual).to have_selector('li.task-list-item', count: 7) + expect(actual).to have_selector('input[checked]', count: 3) + end + end +end + +# Monkeypatch the matcher DSL so that we can reduce some noisy duplication for +# setting the failure messages for these matchers +module RSpec::Matchers::DSL::Macros + def set_default_markdown_messages + failure_message do + # expected to parse emoji, but didn't + "expected to #{description}, but didn't" + end + + failure_message_when_negated do + # expected not to parse task lists, but did + "expected not to #{description}, but did" + end + end +end diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb index f0717e61781..e3de0afb448 100644 --- a/spec/support/mentionable_shared_examples.rb +++ b/spec/support/mentionable_shared_examples.rb @@ -9,7 +9,7 @@ def common_mentionable_setup let(:author) { subject.author } let(:mentioned_issue) { create(:issue, project: project) } - let(:mentioned_mr) { create(:merge_request, :simple, source_project: project) } + let!(:mentioned_mr) { create(:merge_request, :simple, source_project: project) } let(:mentioned_commit) { project.commit } let(:ext_proj) { create(:project, :public) } diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index dab4535e2c7..8dc687c3580 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -35,6 +35,7 @@ module TestEnv clean_test_path FileUtils.mkdir_p(repos_path) + FileUtils.mkdir_p(backup_path) # Setup GitLab shell for test instance setup_gitlab_shell @@ -127,6 +128,10 @@ module TestEnv Gitlab.config.gitlab_shell.repos_path end + def backup_path + Gitlab.config.backup.path + end + def copy_forked_repo_with_submodules(project) base_repo_path = File.expand_path(forked_repo_path_bare) target_repo_path = File.expand_path(repos_path + "/#{project.namespace.path}/#{project.path}.git") diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index cdcfeba8d1f..23f322e0a62 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -15,6 +15,12 @@ describe 'gitlab:app namespace rake task' do Rake.application.invoke_task task_name end + def reenable_backup_sub_tasks + %w{db repo uploads}.each do |subtask| + Rake::Task["gitlab:backup:#{subtask}:create"].reenable + end + end + describe 'backup_restore' do before do # avoid writing task output to spec progress @@ -60,26 +66,47 @@ describe 'gitlab:app namespace rake task' do Dir.glob(File.join(Gitlab.config.backup.path, '*_gitlab_backup.tar')) end - before :all do - # Record the existing backup tars so we don't touch them - existing_tars = tars_glob + def create_backup + FileUtils.rm tars_glob # Redirect STDOUT and run the rake task orig_stdout = $stdout $stdout = StringIO.new + reenable_backup_sub_tasks run_rake_task('gitlab:backup:create') + reenable_backup_sub_tasks $stdout = orig_stdout - @backup_tar = (tars_glob - existing_tars).first + @backup_tar = tars_glob.first end - after :all do + before do + create_backup + end + + after do FileUtils.rm(@backup_tar) end - it 'should set correct permissions on the tar file' do - expect(File.exist?(@backup_tar)).to be_truthy - expect(File::Stat.new(@backup_tar).mode.to_s(8)).to eq('100600') + context 'archive file permissions' do + it 'should set correct permissions on the tar file' do + expect(File.exist?(@backup_tar)).to be_truthy + expect(File::Stat.new(@backup_tar).mode.to_s(8)).to eq('100600') + end + + context 'with custom archive_permissions' do + before do + allow(Gitlab.config.backup).to receive(:archive_permissions).and_return(0651) + # We created a backup in a before(:all) so it got the default permissions. + # We now need to do some work to create a _new_ backup file using our stub. + FileUtils.rm(@backup_tar) + create_backup + end + + it 'uses the custom permissions' do + expect(File::Stat.new(@backup_tar).mode.to_s(8)).to eq('100651') + end + end end it 'should set correct permissions on the tar contents' do @@ -110,12 +137,9 @@ describe 'gitlab:app namespace rake task' do before :all do @origin_cd = Dir.pwd - Rake::Task["gitlab:backup:db:create"].reenable - Rake::Task["gitlab:backup:repo:create"].reenable - Rake::Task["gitlab:backup:uploads:create"].reenable + reenable_backup_sub_tasks - # Record the existing backup tars so we don't touch them - existing_tars = tars_glob + FileUtils.rm tars_glob # Redirect STDOUT and run the rake task orig_stdout = $stdout @@ -124,7 +148,7 @@ describe 'gitlab:app namespace rake task' do run_rake_task('gitlab:backup:create') $stdout = orig_stdout - @backup_tar = (tars_glob - existing_tars).first + @backup_tar = tars_glob.first end after :all do |