diff options
| author | James Lopez <james@jameslopez.es> | 2016-04-04 20:03:25 +0200 |
|---|---|---|
| committer | James Lopez <james@jameslopez.es> | 2016-04-04 20:03:25 +0200 |
| commit | 7f7769172e81dc8bfdb037965ec7bf51c95578ec (patch) | |
| tree | 0a0f8a6f704ddb79edf8cc600a72b4ccf760a4c2 /spec | |
| parent | 4835e680a4624ab8de3316b367b8375bb5a270a0 (diff) | |
| parent | 531e4bdac8c409a25aa862c644dcab00960c82c4 (diff) | |
| download | gitlab-ce-7f7769172e81dc8bfdb037965ec7bf51c95578ec.tar.gz | |
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into fix/project-import_url
# Conflicts:
# db/schema.rb
Diffstat (limited to 'spec')
48 files changed, 1275 insertions, 198 deletions
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 5b1f65d7aff..9ef8ba1b097 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -1,15 +1,14 @@ require 'spec_helper' describe Admin::UsersController do - let(:admin) { create(:admin) } + let(:user) { create(:user) } before do - sign_in(admin) + sign_in(create(:admin)) end describe 'DELETE #user with projects' do - let(:user) { create(:user) } - let(:project) { create(:project, namespace: user.namespace) } + let(:project) { create(:empty_project, namespace: user.namespace) } before do project.team << [user, :developer] @@ -23,8 +22,6 @@ describe Admin::UsersController do end describe 'PUT block/:id' do - let(:user) { create(:user) } - it 'blocks user' do put :block, id: user.username user.reload @@ -50,8 +47,6 @@ describe Admin::UsersController do end context 'manually blocked users' do - let(:user) { create(:user) } - before do user.block end @@ -66,8 +61,6 @@ describe Admin::UsersController do end describe 'PUT unlock/:id' do - let(:user) { create(:user) } - before do request.env["HTTP_REFERER"] = "/" user.lock_access! @@ -95,8 +88,6 @@ describe Admin::UsersController do end describe 'PATCH disable_two_factor' do - let(:user) { create(:user) } - it 'disables 2FA for the user' do expect(user).to receive(:disable_two_factor!) allow(subject).to receive(:user).and_return(user) diff --git a/spec/controllers/ci/projects_controller_spec.rb b/spec/controllers/ci/projects_controller_spec.rb index db0748f323f..5022a3e2c80 100644 --- a/spec/controllers/ci/projects_controller_spec.rb +++ b/spec/controllers/ci/projects_controller_spec.rb @@ -5,6 +5,27 @@ describe Ci::ProjectsController do let!(:project) { create(:project, visibility, ci_id: 1) } let(:ci_id) { project.ci_id } + describe '#index' do + context 'user signed in' do + before do + sign_in(create(:user)) + get(:index) + end + + it 'redirects to /' do + expect(response).to redirect_to(root_path) + end + end + + context 'user not signed in' do + before { get(:index) } + + it 'redirects to sign in page' do + expect(response).to redirect_to(new_user_session_path) + end + end + end + ## # Specs for *deprecated* CI badge # diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index c5b034dc064..75e6b6f45a7 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -63,7 +63,7 @@ describe Projects::MergeRequestsController do id: merge_request.iid, format: format) - expect(response.body).to eq((merge_request.send(:"to_#{format}",user)).to_s) + expect(response.body).to eq((merge_request.send(:"to_#{format}")).to_s) end it "should not escape Html" do diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb new file mode 100644 index 00000000000..0f32a30f18b --- /dev/null +++ b/spec/controllers/projects/snippets_controller_spec.rb @@ -0,0 +1,107 @@ +require 'spec_helper' + +describe Projects::SnippetsController do + let(:project) { create(:project_empty_repo, :public, snippets_enabled: true) } + let(:user) { create(:user) } + let(:user2) { create(:user) } + + before do + project.team << [user, :master] + project.team << [user2, :master] + end + + describe 'GET #index' do + context 'when the project snippet is private' do + let!(:project_snippet) { create(:project_snippet, :private, project: project, author: user) } + + context 'when anonymous' do + it 'does not include the private snippet' do + get :index, namespace_id: project.namespace.path, project_id: project.path + + expect(assigns(:snippets)).not_to include(project_snippet) + expect(response.status).to eq(200) + end + end + + context 'when signed in as the author' do + before { sign_in(user) } + + it 'renders the snippet' do + get :index, namespace_id: project.namespace.path, project_id: project.path + + expect(assigns(:snippets)).to include(project_snippet) + expect(response.status).to eq(200) + end + end + + context 'when signed in as a project member' do + before { sign_in(user2) } + + it 'renders the snippet' do + get :index, namespace_id: project.namespace.path, project_id: project.path + + expect(assigns(:snippets)).to include(project_snippet) + expect(response.status).to eq(200) + end + end + end + end + + %w[show raw].each do |action| + describe "GET ##{action}" do + context 'when the project snippet is private' do + let(:project_snippet) { create(:project_snippet, :private, project: project, author: user) } + + context 'when anonymous' do + it 'responds with status 404' do + get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param + + expect(response.status).to eq(404) + end + end + + context 'when signed in as the author' do + before { sign_in(user) } + + it 'renders the snippet' do + get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param + + expect(assigns(:snippet)).to eq(project_snippet) + expect(response.status).to eq(200) + end + end + + context 'when signed in as a project member' do + before { sign_in(user2) } + + it 'renders the snippet' do + get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param + + expect(assigns(:snippet)).to eq(project_snippet) + expect(response.status).to eq(200) + end + end + end + + context 'when the project snippet does not exist' do + context 'when anonymous' do + it 'responds with status 404' do + get action, namespace_id: project.namespace.path, project_id: project.path, id: 42 + + expect(response.status).to eq(404) + end + end + + context 'when signed in' do + before { sign_in(user) } + + it 'responds with status 404' do + get action, namespace_id: project.namespace.path, project_id: project.path, id: 42 + + expect(response.status).to eq(404) + end + end + end + end + end +end diff --git a/spec/factories/file_uploader.rb b/spec/factories/file_uploader.rb new file mode 100644 index 00000000000..1b36e21f2b0 --- /dev/null +++ b/spec/factories/file_uploader.rb @@ -0,0 +1,20 @@ +FactoryGirl.define do + factory :file_uploader do + project + secret nil + + transient do + fixture { 'rails_sample.jpg' } + path { File.join(Rails.root, 'spec/fixtures', fixture) } + file { Rack::Test::UploadedFile.new(path) } + end + + after(:build) do |uploader, evaluator| + uploader.store!(evaluator.file) + end + + initialize_with do + new(project, secret) + end + end +end diff --git a/spec/factories_spec.rb b/spec/factories_spec.rb index 457859dedaf..62de081661d 100644 --- a/spec/factories_spec.rb +++ b/spec/factories_spec.rb @@ -1,9 +1,17 @@ require 'spec_helper' -FactoryGirl.factories.map(&:name).each do |factory_name| - describe "#{factory_name} factory" do - it 'should be valid' do - expect(build(factory_name)).to be_valid +describe 'factories' do + FactoryGirl.factories.each do |factory| + describe "#{factory.name} factory" do + let(:entity) { build(factory.name) } + + it 'does not raise error when created 'do + expect { entity }.to_not raise_error + end + + it 'should be valid', if: factory.build_class < ActiveRecord::Base do + expect(entity).to be_valid + end end end end diff --git a/spec/features/issues/filter_by_milestone_spec.rb b/spec/features/issues/filter_by_milestone_spec.rb index f6e33f651c4..99445185893 100644 --- a/spec/features/issues/filter_by_milestone_spec.rb +++ b/spec/features/issues/filter_by_milestone_spec.rb @@ -11,7 +11,41 @@ feature 'Issue filtering by Milestone', feature: true do visit_issues(project) filter_by_milestone(Milestone::None.title) - expect(page).to have_css('.issue .title', count: 1) + expect(page).to have_css('.issue', count: 1) + end + + context 'filters by upcoming milestone', js: true do + it 'should not show issues with no expiry' do + create(:issue, project: project) + create(:issue, project: project, milestone: milestone) + + visit_issues(project) + filter_by_milestone(Milestone::Upcoming.title) + + expect(page).to have_css('.issue', count: 0) + end + + it 'should show issues in future' do + milestone = create(:milestone, project: project, due_date: Date.tomorrow) + create(:issue, project: project) + create(:issue, project: project, milestone: milestone) + + visit_issues(project) + filter_by_milestone(Milestone::Upcoming.title) + + expect(page).to have_css('.issue', count: 1) + end + + it 'should not show issues in past' do + milestone = create(:milestone, project: project, due_date: Date.yesterday) + create(:issue, project: project) + create(:issue, project: project, milestone: milestone) + + visit_issues(project) + filter_by_milestone(Milestone::Upcoming.title) + + expect(page).to have_css('.issue', count: 0) + end end scenario 'filters by a specific Milestone', js: true do @@ -21,7 +55,7 @@ feature 'Issue filtering by Milestone', feature: true do visit_issues(project) filter_by_milestone(milestone.title) - expect(page).to have_css('.issue .title', count: 1) + expect(page).to have_css('.issue', count: 1) end def visit_issues(project) @@ -30,8 +64,6 @@ feature 'Issue filtering by Milestone', feature: true do def filter_by_milestone(title) find(".js-milestone-select").click - sleep 0.5 - find(".milestone-filter a", text: title).click - sleep 1 + find(".milestone-filter .dropdown-content a", text: title).click end end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index e844e681ebf..db46657c36a 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -34,20 +34,7 @@ describe 'Issues', feature: true do fill_in 'issue_title', with: 'bug 345' fill_in 'issue_description', with: 'bug description' end - - it 'does not change issue count' do - expect { click_button 'Save changes' }.to_not change { Issue.count } - end - - it 'should update issue fields' do - click_button 'Save changes' - - expect(page).to have_content @user.name - expect(page).to have_content 'bug 345' - expect(page).to have_content project.name - end end - end describe 'Editing issue assignee' do @@ -70,7 +57,7 @@ describe 'Issues', feature: true do click_button 'Save changes' page.within('.assignee') do - expect(page).to have_content 'None' + expect(page).to have_content 'No assignee - assign yourself' end expect(issue.reload.assignee).to be_nil @@ -198,20 +185,26 @@ describe 'Issues', feature: true do end describe 'update assignee from issue#show' do - let(:issue) { create(:issue, project: project, author: @user) } + let(:issue) { create(:issue, project: project, author: @user, assignee: @user) } context 'by autorized user' do - it 'with dropdown menu' do + it 'allows user to select unassigned', js: true do visit namespace_project_issue_path(project.namespace, project, issue) - find('.issuable-sidebar #issue_assignee_id'). - set project.team.members.first.id - click_button 'Update Issue' + page.within('.assignee') do + expect(page).to have_content "#{@user.name}" + end + + find('.block.assignee .edit-link').click + sleep 2 # wait for ajax stuff to complete + first('.dropdown-menu-user-link').click + sleep 2 + page.within('.assignee') do + expect(page).to have_content 'No assignee' + end - expect(page).to have_content 'Assignee' - has_select?('issue_assignee_id', - selected: project.team.members.first.name) + expect(issue.reload.assignee).to be_nil end end @@ -221,8 +214,6 @@ describe 'Issues', feature: true do before :each do project.team << [[guest], :guest] - issue.assignee = @user - issue.save end it 'shows assignee text', js: true do @@ -241,20 +232,23 @@ describe 'Issues', feature: true do context 'by authorized user' do - it 'with dropdown menu' do - visit namespace_project_issue_path(project.namespace, project, issue) - find('.issuable-sidebar'). - select(milestone.title, from: 'issue_milestone_id') - click_button 'Update Issue' + it 'allows user to select unassigned', js: true do + visit namespace_project_issue_path(project.namespace, project, issue) - expect(page).to have_content "Milestone changed to #{milestone.title}" + page.within('.milestone') do + expect(page).to have_content "None" + end + find('.block.milestone .edit-link').click + sleep 2 # wait for ajax stuff to complete + first('.dropdown-content li').click + sleep 2 page.within('.milestone') do - expect(page).to have_content milestone.title + expect(page).to have_content 'None' end - has_select?('issue_assignee_id', selected: milestone.title) + expect(issue.reload.milestone).to be_nil end end @@ -283,25 +277,6 @@ describe 'Issues', feature: true do issue.assignee = user2 issue.save end - - it 'allows user to remove assignee', js: true do - visit namespace_project_issue_path(project.namespace, project, issue) - - page.within('.assignee') do - expect(page).to have_content user2.name - end - - find('.assignee .edit-link').click - sleep 2 # wait for ajax stuff to complete - first('.user-result').click - - page.within('.assignee') do - expect(page).to have_content 'None' - end - - sleep 2 # wait for ajax stuff to complete - expect(issue.reload.assignee).to be_nil - end end end diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb new file mode 100644 index 00000000000..fd02d584848 --- /dev/null +++ b/spec/features/merge_requests/create_new_mr_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +feature 'Create New Merge Request', feature: true, js: false do + let(:user) { create(:user) } + let(:project) { create(:project, :public) } + + before do + project.team << [user, :master] + + login_as user + visit namespace_project_merge_requests_path(project.namespace, project) + end + + it 'generates a diff for an orphaned branch' do + click_link 'New Merge Request' + select "orphaned-branch", from: "merge_request_source_branch" + select "master", from: "merge_request_target_branch" + click_button "Compare branches" + + expect(page).to have_content "README.md" + expect(page).to have_content "wm.png" + + fill_in "merge_request_title", with: "Orphaned MR test" + click_button "Submit merge request" + + expect(page).to have_content 'git checkout -b orphaned-branch origin/orphaned-branch' + end +end diff --git a/spec/features/merge_requests/filter_by_milestone_spec.rb b/spec/features/merge_requests/filter_by_milestone_spec.rb index b76e4c74c79..c57ab5f3b03 100644 --- a/spec/features/merge_requests/filter_by_milestone_spec.rb +++ b/spec/features/merge_requests/filter_by_milestone_spec.rb @@ -11,7 +11,41 @@ feature 'Merge Request filtering by Milestone', feature: true do visit_merge_requests(project) filter_by_milestone(Milestone::None.title) - expect(page).to have_css('.merge-request-title', count: 1) + expect(page).to have_css('.merge-request', count: 1) + end + + context 'filters by upcoming milestone', js: true do + it 'should not show issues with no expiry' do + create(:merge_request, :with_diffs, source_project: project) + create(:merge_request, :simple, source_project: project, milestone: milestone) + + visit_merge_requests(project) + filter_by_milestone(Milestone::Upcoming.title) + + expect(page).to have_css('.merge-request', count: 0) + end + + it 'should show issues in future' do + milestone = create(:milestone, project: project, due_date: Date.tomorrow) + create(:merge_request, :with_diffs, source_project: project) + create(:merge_request, :simple, source_project: project, milestone: milestone) + + visit_merge_requests(project) + filter_by_milestone(Milestone::Upcoming.title) + + expect(page).to have_css('.merge-request', count: 1) + end + + it 'should not show issues in past' do + milestone = create(:milestone, project: project, due_date: Date.yesterday) + create(:merge_request, :with_diffs, source_project: project) + create(:merge_request, :simple, source_project: project, milestone: milestone) + + visit_merge_requests(project) + filter_by_milestone(Milestone::Upcoming.title) + + expect(page).to have_css('.merge-request', count: 0) + end end scenario 'filters by a specific Milestone', js: true do @@ -21,7 +55,7 @@ feature 'Merge Request filtering by Milestone', feature: true do visit_merge_requests(project) filter_by_milestone(milestone.title) - expect(page).to have_css('.merge-request-title', count: 1) + expect(page).to have_css('.merge-request', count: 1) end def visit_merge_requests(project) diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb index 84c036e59c0..3e6289a46b1 100644 --- a/spec/features/search_spec.rb +++ b/spec/features/search_spec.rb @@ -1,19 +1,46 @@ require 'spec_helper' describe "Search", feature: true do + let(:user) { create(:user) } + let(:project) { create(:project, namespace: user.namespace) } + before do - login_as :user - @project = create(:project, namespace: @user.namespace) - @project.team << [@user, :reporter] + login_with(user) + project.team << [user, :reporter] visit search_path + end - page.within '.search-holder' do - fill_in "search", with: @project.name[0..3] - click_button "Search" + describe 'searching for Projects' do + it 'finds a project' do + page.within '.search-holder' do + fill_in "search", with: project.name[0..3] + click_button "Search" + end + + expect(page).to have_content project.name end end - it "should show project in search results" do - expect(page).to have_content @project.name + context 'search for comments' do + it 'finds a snippet' do + snippet = create(:project_snippet, :private, project: project, author: user, title: 'Some title') + note = create(:note, + noteable: snippet, + author: user, + note: 'Supercalifragilisticexpialidocious', + project: project) + # Must visit project dashboard since global search won't search + # everything (e.g. comments, snippets, etc.) + visit namespace_project_path(project.namespace, project) + + page.within '.search' do + fill_in 'search', with: note.note + click_button 'Go' + end + + click_link 'Comments' + + expect(page).to have_link(snippet.title) + end end end diff --git a/spec/features/security/project/snippet/internal_access_spec.rb b/spec/features/security/project/snippet/internal_access_spec.rb new file mode 100644 index 00000000000..db53a9cec97 --- /dev/null +++ b/spec/features/security/project/snippet/internal_access_spec.rb @@ -0,0 +1,78 @@ +require 'spec_helper' + +describe "Internal Project Snippets Access", feature: true do + include AccessMatchers + + let(:project) { create(:project, :internal) } + + let(:owner) { project.owner } + let(:master) { create(:user) } + let(:developer) { create(:user) } + let(:reporter) { create(:user) } + let(:guest) { create(:user) } + let(:internal_snippet) { create(:project_snippet, :internal, project: project, author: owner) } + let(:private_snippet) { create(:project_snippet, :private, project: project, author: owner) } + + before do + project.team << [master, :master] + project.team << [developer, :developer] + project.team << [reporter, :reporter] + project.team << [guest, :guest] + end + + describe "GET /:project_path/snippets" do + subject { namespace_project_snippets_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/snippets/new" do + subject { new_namespace_project_snippet_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/snippets/:id for an internal snippet" do + subject { namespace_project_snippet_path(project.namespace, project, internal_snippet) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/snippets/:id for a private snippet" do + subject { namespace_project_snippet_path(project.namespace, project, private_snippet) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end +end diff --git a/spec/features/security/project/snippet/private_access_spec.rb b/spec/features/security/project/snippet/private_access_spec.rb new file mode 100644 index 00000000000..d23d645c8e5 --- /dev/null +++ b/spec/features/security/project/snippet/private_access_spec.rb @@ -0,0 +1,63 @@ +require 'spec_helper' + +describe "Private Project Snippets Access", feature: true do + include AccessMatchers + + let(:project) { create(:project, :private) } + + let(:owner) { project.owner } + let(:master) { create(:user) } + let(:developer) { create(:user) } + let(:reporter) { create(:user) } + let(:guest) { create(:user) } + let(:private_snippet) { create(:project_snippet, :private, project: project, author: owner) } + + before do + project.team << [master, :master] + project.team << [developer, :developer] + project.team << [reporter, :reporter] + project.team << [guest, :guest] + end + + describe "GET /:project_path/snippets" do + subject { namespace_project_snippets_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/snippets/new" do + subject { new_namespace_project_snippet_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/snippets/:id for a private snippet" do + subject { namespace_project_snippet_path(project.namespace, project, private_snippet) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end +end diff --git a/spec/features/security/project/snippet/public_access_spec.rb b/spec/features/security/project/snippet/public_access_spec.rb new file mode 100644 index 00000000000..e3665b6116a --- /dev/null +++ b/spec/features/security/project/snippet/public_access_spec.rb @@ -0,0 +1,93 @@ +require 'spec_helper' + +describe "Public Project Snippets Access", feature: true do + include AccessMatchers + + let(:project) { create(:project, :public) } + + let(:owner) { project.owner } + let(:master) { create(:user) } + let(:developer) { create(:user) } + let(:reporter) { create(:user) } + let(:guest) { create(:user) } + let(:public_snippet) { create(:project_snippet, :public, project: project, author: owner) } + let(:internal_snippet) { create(:project_snippet, :internal, project: project, author: owner) } + let(:private_snippet) { create(:project_snippet, :private, project: project, author: owner) } + + before do + project.team << [master, :master] + project.team << [developer, :developer] + project.team << [reporter, :reporter] + project.team << [guest, :guest] + end + + describe "GET /:project_path/snippets" do + subject { namespace_project_snippets_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :external } + it { is_expected.to be_allowed_for :visitor } + end + + describe "GET /:project_path/snippets/new" do + subject { new_namespace_project_snippet_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/snippets/:id for a public snippet" do + subject { namespace_project_snippet_path(project.namespace, project, public_snippet) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :external } + it { is_expected.to be_allowed_for :visitor } + end + + describe "GET /:project_path/snippets/:id for an internal snippet" do + subject { namespace_project_snippet_path(project.namespace, project, internal_snippet) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/snippets/:id for a private snippet" do + subject { namespace_project_snippet_path(project.namespace, project, private_snippet) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end +end diff --git a/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml new file mode 100644 index 00000000000..39d5cefbc2a --- /dev/null +++ b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml @@ -0,0 +1,42 @@ +Return-Path: <jake@adventuretime.ooo> +Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400 +Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400 +Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <reply@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700 +Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700 +Date: Thu, 13 Jun 2013 17:03:48 -0400 +From: Jake the Dog <jake@adventuretime.ooo> +To: reply@appmail.adventuretime.ooo +Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com> +In-Reply-To: <issue_1@localhost> +References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> +Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' +Mime-Version: 1.0 +Content-Type: text/plain; + charset=ISO-8859-1 +Content-Transfer-Encoding: 7bit +X-Sieve: CMU Sieve 2.2 +X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu, + 13 Jun 2013 14:03:48 -0700 (PDT) +X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 + +I could not disagree more. I am obviously biased but adventure time is the +greatest show ever created. Everyone should watch it. + +- Jake out + + +On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta +<reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo> wrote: +> +> +> +> eviltrout posted in 'Adventure Time Sux' on Discourse Meta: +> +> --- +> hey guys everyone knows adventure time sucks! +> +> --- +> Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3 +> +> To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences). +> diff --git a/spec/fixtures/emails/valid_reply.eml b/spec/fixtures/emails/valid_reply.eml index 1e696389954..980e10a8812 100644 --- a/spec/fixtures/emails/valid_reply.eml +++ b/spec/fixtures/emails/valid_reply.eml @@ -7,6 +7,8 @@ Date: Thu, 13 Jun 2013 17:03:48 -0400 From: Jake the Dog <jake@adventuretime.ooo> To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com> +In-Reply-To: <issue_1@localhost> +References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' Mime-Version: 1.0 Content-Type: text/plain; @@ -37,4 +39,4 @@ On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta > Please visit this link to respond: http://localhost:3000/t/adventure-time-sux/1234/3 > > To unsubscribe from these emails, visit your [user preferences](http://localhost:3000/user_preferences). ->
\ No newline at end of file +> diff --git a/spec/lib/award_emoji_spec.rb b/spec/lib/award_emoji_spec.rb new file mode 100644 index 00000000000..330678f7f16 --- /dev/null +++ b/spec/lib/award_emoji_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe AwardEmoji do + describe '.urls' do + subject { AwardEmoji.urls } + + it { is_expected.to be_an_instance_of(Array) } + it { is_expected.to_not be_empty } + + context 'every Hash in the Array' do + it 'has the correct keys and values' do + subject.each do |hash| + expect(hash[:name]).to be_an_instance_of(String) + expect(hash[:path]).to be_an_instance_of(String) + end + end + end + end +end diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb index f38fadda9ba..566035c60d0 100644 --- a/spec/lib/extracts_path_spec.rb +++ b/spec/lib/extracts_path_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe ExtractsPath, lib: true do include ExtractsPath include RepoHelpers - include Gitlab::Application.routes.url_helpers + include Gitlab::Routing.url_helpers let(:project) { double('project') } diff --git a/spec/lib/gitlab/badge/build_spec.rb b/spec/lib/gitlab/badge/build_spec.rb new file mode 100644 index 00000000000..b78c2b6224f --- /dev/null +++ b/spec/lib/gitlab/badge/build_spec.rb @@ -0,0 +1,72 @@ +require 'spec_helper' + +describe Gitlab::Badge::Build do + let(:project) { create(:project) } + let(:sha) { project.commit.sha } + let(:badge) { described_class.new(project, 'master') } + + describe '#type' do + subject { badge.type } + it { is_expected.to eq 'image/svg+xml' } + end + + context 'build exists' do + let(:ci_commit) { create(:ci_commit, project: project, sha: sha) } + let!(:build) { create(:ci_build, commit: ci_commit) } + + + context 'build success' do + before { build.success! } + + describe '#to_s' do + subject { badge.to_s } + it { is_expected.to eq 'build-success' } + end + + describe '#data' do + let(:data) { badge.data } + + it 'contains infromation about success' do + expect(status_node(data, 'success')).to be_truthy + end + end + end + + context 'build failed' do + before { build.drop! } + + describe '#to_s' do + subject { badge.to_s } + it { is_expected.to eq 'build-failed' } + end + + describe '#data' do + let(:data) { badge.data } + + it 'contains infromation about failure' do + expect(status_node(data, 'failed')).to be_truthy + end + end + end + end + + context 'build does not exist' do + describe '#to_s' do + subject { badge.to_s } + it { is_expected.to eq 'build-unknown' } + end + + describe '#data' do + let(:data) { badge.data } + + it 'contains infromation about unknown build' do + expect(status_node(data, 'unknown')).to be_truthy + end + end + end + + def status_node(data, status) + xml = Nokogiri::XML.parse(data) + xml.at(%Q{text:contains("#{status}")}) + end +end diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index 844fd79c991..a1f51429a79 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -236,6 +236,6 @@ describe Gitlab::ClosingIssueExtractor, lib: true do end def urls - Gitlab::Application.routes.url_helpers + Gitlab::Routing.url_helpers end end diff --git a/spec/lib/gitlab/email/receiver_spec.rb b/spec/lib/gitlab/email/receiver_spec.rb index abe179cd4af..36267faeb93 100644 --- a/spec/lib/gitlab/email/receiver_spec.rb +++ b/spec/lib/gitlab/email/receiver_spec.rb @@ -3,6 +3,7 @@ require "spec_helper" describe Gitlab::Email::Receiver, lib: true do before do stub_incoming_email_setting(enabled: true, address: "reply+%{key}@appmail.adventuretime.ooo") + stub_config_setting(host: 'localhost') end let(:reply_key) { "59d8df8370b7e95c5a49fbf86aeb2c93" } @@ -137,5 +138,27 @@ describe Gitlab::Email::Receiver, lib: true do expect(note.note).to include(markdown) end + + context 'when sub-addressing is not supported' do + before do + stub_incoming_email_setting(enabled: true, address: nil) + end + + shared_examples 'an email that contains a reply key' do |header| + it "fetches the reply key from the #{header} header and creates a comment" do + expect { receiver.execute }.to change { noteable.notes.count }.by(1) + note = noteable.notes.last + + expect(note.author).to eq(sent_notification.recipient) + expect(note.note).to include('I could not disagree more.') + end + end + + context 'reply key is in the References header' do + let(:email_raw) { fixture_file('emails/reply_without_subaddressing_and_key_inside_references.eml') } + + it_behaves_like 'an email that contains a reply key', 'References' + end + end end end diff --git a/spec/lib/gitlab/fogbugz_import/client_spec.rb b/spec/lib/gitlab/fogbugz_import/client_spec.rb new file mode 100644 index 00000000000..2dc71be0254 --- /dev/null +++ b/spec/lib/gitlab/fogbugz_import/client_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe Gitlab::FogbugzImport::Client, lib: true do + + let(:client) { described_class.new(uri: '', token: '') } + let(:one_user) { { 'people' => { 'person' => { "ixPerson" => "2", "sFullName" => "James" } } } } + let(:two_users) { { 'people' => { 'person' => [one_user, { "ixPerson" => "3" }] } } } + + it 'retrieves user_map with one user' do + stub_api(one_user) + + expect(client.user_map.count).to eq(1) + end + + it 'retrieves user_map with two users' do + stub_api(two_users) + + expect(client.user_map.count).to eq(2) + end + + def stub_api(users) + allow_any_instance_of(::Fogbugz::Interface).to receive(:command).with(:listPeople).and_return(users) + end +end diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb new file mode 100644 index 00000000000..eda956e6f0a --- /dev/null +++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +describe Gitlab::Gfm::UploadsRewriter do + let(:user) { create(:user) } + let(:old_project) { create(:project) } + let(:new_project) { create(:project) } + let(:rewriter) { described_class.new(text, old_project, user) } + + context 'text contains links to uploads' do + let(:image_uploader) do + build(:file_uploader, project: old_project) + end + + let(:zip_uploader) do + build(:file_uploader, project: old_project, + fixture: 'ci_build_artifacts.zip') + end + + let(:text) do + "Text and #{image_uploader.to_markdown} and #{zip_uploader.to_markdown}" + end + + describe '#rewrite' do + let!(:new_text) { rewriter.rewrite(new_project) } + + let(:old_files) { [image_uploader, zip_uploader].map(&:file) } + let(:new_files) do + described_class.new(new_text, new_project, user).files + end + + let(:old_paths) { old_files.map(&:path) } + let(:new_paths) { new_files.map(&:path) } + + it 'rewrites content' do + expect(new_text).to_not eq text + expect(new_text.length).to eq text.length + end + + it 'copies files' do + expect(new_files).to all(exist) + expect(old_paths).to_not match_array new_paths + expect(old_paths).to all(include(old_project.path_with_namespace)) + expect(new_paths).to all(include(new_project.path_with_namespace)) + end + + it 'does not remove old files' do + expect(old_files).to all(exist) + end + + it 'generates a new secret for each file' do + expect(new_paths).to_not include image_uploader.secret + expect(new_paths).to_not include zip_uploader.secret + end + end + + describe '#needs_rewrite?' do + subject { rewriter.needs_rewrite? } + it { is_expected.to eq true } + end + + describe '#files' do + subject { rewriter.files } + it { is_expected.to be_an(Array) } + end + end +end diff --git a/spec/lib/gitlab/incoming_email_spec.rb b/spec/lib/gitlab/incoming_email_spec.rb index bcdba8d4c12..afb3e26f8fb 100644 --- a/spec/lib/gitlab/incoming_email_spec.rb +++ b/spec/lib/gitlab/incoming_email_spec.rb @@ -7,24 +7,8 @@ describe Gitlab::IncomingEmail, lib: true do stub_incoming_email_setting(enabled: true) end - context "when the address is valid" do - before do - stub_incoming_email_setting(address: "replies+%{key}@example.com") - end - - it "returns true" do - expect(described_class.enabled?).to be_truthy - end - end - - context "when the address is invalid" do - before do - stub_incoming_email_setting(address: "replies@example.com") - end - - it "returns false" do - expect(described_class.enabled?).to be_falsey - end + it 'returns true' do + expect(described_class.enabled?).to be_truthy end end @@ -58,4 +42,10 @@ describe Gitlab::IncomingEmail, lib: true do expect(described_class.key_from_address("replies+key@example.com")).to eq("key") end end + + context 'self.key_from_fallback_reply_message_id' do + it 'returns reply key' do + expect(described_class.key_from_fallback_reply_message_id('reply-key@localhost')).to eq('key') + end + end end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 9b47acfe0cd..631b5094f42 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -35,7 +35,9 @@ describe Notify do subject { Notify.new_issue_email(issue.assignee_id, issue.id) } it_behaves_like 'an assignee email' - it_behaves_like 'an email starting a new thread', 'issue' + it_behaves_like 'an email starting a new thread with reply-by-email enabled' do + let(:model) { issue } + end it_behaves_like 'it should show Gmail Actions View Issue link' it_behaves_like 'an unsubscribeable thread' @@ -73,9 +75,11 @@ describe Notify do subject { Notify.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id, current_user.id) } it_behaves_like 'a multiple recipients email' - it_behaves_like 'an answer to an existing thread', 'issue' + it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do + let(:model) { issue } + end it_behaves_like 'it should show Gmail Actions View Issue link' - it_behaves_like "an unsubscribeable thread" + it_behaves_like 'an unsubscribeable thread' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -104,7 +108,9 @@ describe Notify do subject { Notify.relabeled_issue_email(recipient.id, issue.id, %w[foo bar baz], current_user.id) } it_behaves_like 'a multiple recipients email' - it_behaves_like 'an answer to an existing thread', 'issue' + it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do + let(:model) { issue } + end it_behaves_like 'it should show Gmail Actions View Issue link' it_behaves_like 'a user cannot unsubscribe through footer link' it_behaves_like 'an email with a labels subscriptions link in its footer' @@ -132,7 +138,9 @@ describe Notify do let(:status) { 'closed' } subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user.id) } - it_behaves_like 'an answer to an existing thread', 'issue' + it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do + let(:model) { issue } + end it_behaves_like 'it should show Gmail Actions View Issue link' it_behaves_like 'an unsubscribeable thread' @@ -163,7 +171,9 @@ describe Notify do let(:new_issue) { create(:issue) } subject { Notify.issue_moved_email(recipient, issue, new_issue, current_user) } - it_behaves_like 'an answer to an existing thread', 'issue' + it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do + let(:model) { issue } + end it_behaves_like 'it should show Gmail Actions View Issue link' it_behaves_like 'an unsubscribeable thread' @@ -196,9 +206,11 @@ describe Notify do subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) } it_behaves_like 'an assignee email' - it_behaves_like 'an email starting a new thread', 'merge_request' + it_behaves_like 'an email starting a new thread with reply-by-email enabled' do + let(:model) { merge_request } + end it_behaves_like 'it should show Gmail Actions View Merge request link' - it_behaves_like "an unsubscribeable thread" + it_behaves_like 'an unsubscribeable thread' it 'has the correct subject' do is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ @@ -216,10 +228,6 @@ describe Notify do is_expected.to have_body_text /#{merge_request.target_branch}/ end - it 'has the correct message-id set' do - is_expected.to have_header 'Message-ID', "<merge_request_#{merge_request.id}@#{Gitlab.config.gitlab.host}>" - end - context 'when enabled email_author_in_body' do before do allow(current_application_settings).to receive(:email_author_in_body).and_return(true) @@ -247,7 +255,9 @@ describe Notify do subject { Notify.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user.id) } it_behaves_like 'a multiple recipients email' - it_behaves_like 'an answer to an existing thread', 'merge_request' + it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do + let(:model) { merge_request } + end it_behaves_like 'it should show Gmail Actions View Merge request link' it_behaves_like "an unsubscribeable thread" @@ -278,7 +288,9 @@ describe Notify do subject { Notify.relabeled_merge_request_email(recipient.id, merge_request.id, %w[foo bar baz], current_user.id) } it_behaves_like 'a multiple recipients email' - it_behaves_like 'an answer to an existing thread', 'merge_request' + it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do + let(:model) { merge_request } + end it_behaves_like 'it should show Gmail Actions View Merge request link' it_behaves_like 'a user cannot unsubscribe through footer link' it_behaves_like 'an email with a labels subscriptions link in its footer' @@ -306,9 +318,11 @@ describe Notify do let(:status) { 'reopened' } subject { Notify.merge_request_status_email(recipient.id, merge_request.id, status, current_user.id) } - it_behaves_like 'an answer to an existing thread', 'merge_request' + it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do + let(:model) { merge_request } + end it_behaves_like 'it should show Gmail Actions View Merge request link' - it_behaves_like "an unsubscribeable thread" + it_behaves_like 'an unsubscribeable thread' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -337,9 +351,11 @@ describe Notify do subject { Notify.merged_merge_request_email(recipient.id, merge_request.id, merge_author.id) } it_behaves_like 'a multiple recipients email' - it_behaves_like 'an answer to an existing thread', 'merge_request' + it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do + let(:model) { merge_request } + end it_behaves_like 'it should show Gmail Actions View Merge request link' - it_behaves_like "an unsubscribeable thread" + it_behaves_like 'an unsubscribeable thread' it 'is sent as the merge author' do sender = subject.header[:from].addrs[0] @@ -456,9 +472,11 @@ describe Notify do subject { Notify.note_commit_email(recipient.id, note.id) } it_behaves_like 'a note email' - it_behaves_like 'an answer to an existing thread', 'commit' + it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do + let(:model) { commit } + end it_behaves_like 'it should show Gmail Actions View Commit link' - it_behaves_like "a user cannot unsubscribe through footer link" + it_behaves_like 'a user cannot unsubscribe through footer link' it 'has the correct subject' do is_expected.to have_subject /#{commit.title} \(#{commit.short_id}\)/ @@ -477,7 +495,9 @@ describe Notify do subject { Notify.note_merge_request_email(recipient.id, note.id) } it_behaves_like 'a note email' - it_behaves_like 'an answer to an existing thread', 'merge_request' + it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do + let(:model) { merge_request } + end it_behaves_like 'it should show Gmail Actions View Merge request link' it_behaves_like 'an unsubscribeable thread' @@ -498,7 +518,9 @@ describe Notify do subject { Notify.note_issue_email(recipient.id, note.id) } it_behaves_like 'a note email' - it_behaves_like 'an answer to an existing thread', 'issue' + it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do + let(:model) { issue } + end it_behaves_like 'it should show Gmail Actions View Issue link' it_behaves_like 'an unsubscribeable thread' diff --git a/spec/mailers/shared/notify.rb b/spec/mailers/shared/notify.rb index 6019af544d3..56a6dbf96f9 100644 --- a/spec/mailers/shared/notify.rb +++ b/spec/mailers/shared/notify.rb @@ -10,6 +10,13 @@ shared_context 'gitlab email notification' do ActionMailer::Base.deliveries.clear email = recipient.emails.create(email: "notifications@example.com") recipient.update_attribute(:notification_email, email.email) + stub_incoming_email_setting(enabled: true, address: "reply+%{key}@#{Gitlab.config.gitlab.host}") + end +end + +shared_context 'reply-by-email is enabled with incoming address without %{key}' do + before do + stub_incoming_email_setting(enabled: true, address: "reply@#{Gitlab.config.gitlab.host}") end end @@ -46,25 +53,76 @@ shared_examples 'an email with X-GitLab headers containing project details' do end end -shared_examples 'an email starting a new thread' do |message_id_prefix| - include_examples 'an email with X-GitLab headers containing project details' +shared_examples 'a new thread email with reply-by-email enabled' do + let(:regex) { /\A<reply\-(.*)@#{Gitlab.config.gitlab.host}>\Z/ } + + it 'has a Message-ID header' do + is_expected.to have_header 'Message-ID', "<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}>" + end - it 'has a discussion identifier' do - is_expected.to have_header 'Message-ID', /<#{message_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/ + it 'has a References header' do + is_expected.to have_header 'References', regex end end -shared_examples 'an answer to an existing thread' do |thread_id_prefix| +shared_examples 'a thread answer email with reply-by-email enabled' do include_examples 'an email with X-GitLab headers containing project details' + let(:regex) { /\A<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}> <reply\-(.*)@#{Gitlab.config.gitlab.host}>\Z/ } + + it 'has a Message-ID header' do + is_expected.to have_header 'Message-ID', /\A<(.*)@#{Gitlab.config.gitlab.host}>\Z/ + end + + it 'has a In-Reply-To header' do + is_expected.to have_header 'In-Reply-To', "<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}>" + end + + it 'has a References header' do + is_expected.to have_header 'References', regex + end it 'has a subject that begins with Re: ' do is_expected.to have_subject /^Re: / end +end + +shared_examples 'an email starting a new thread with reply-by-email enabled' do + include_examples 'an email with X-GitLab headers containing project details' + include_examples 'a new thread email with reply-by-email enabled' + + context 'when reply-by-email is enabled with incoming address with %{key}' do + it 'has a Reply-To header' do + is_expected.to have_header 'Reply-To', /<reply+(.*)@#{Gitlab.config.gitlab.host}>\Z/ + end + end + + context 'when reply-by-email is enabled with incoming address without %{key}' do + include_context 'reply-by-email is enabled with incoming address without %{key}' + include_examples 'a new thread email with reply-by-email enabled' + + it 'has a Reply-To header' do + is_expected.to have_header 'Reply-To', /<reply@#{Gitlab.config.gitlab.host}>\Z/ + end + end +end + +shared_examples 'an answer to an existing thread with reply-by-email enabled' do + include_examples 'an email with X-GitLab headers containing project details' + include_examples 'a thread answer email with reply-by-email enabled' + + context 'when reply-by-email is enabled with incoming address with %{key}' do + it 'has a Reply-To header' do + is_expected.to have_header 'Reply-To', /<reply+(.*)@#{Gitlab.config.gitlab.host}>\Z/ + end + end + + context 'when reply-by-email is enabled with incoming address without %{key}' do + include_context 'reply-by-email is enabled with incoming address without %{key}' + include_examples 'a thread answer email with reply-by-email enabled' - it 'has headers that reference an existing thread' do - is_expected.to have_header 'Message-ID', /<(.*)@#{Gitlab.config.gitlab.host}>/ - is_expected.to have_header 'References', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/ - is_expected.to have_header 'In-Reply-To', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/ + it 'has a Reply-To header' do + is_expected.to have_header 'Reply-To', /<reply@#{Gitlab.config.gitlab.host}>\Z/ + end end end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index b1764d7ac09..520cf1b75de 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -12,7 +12,6 @@ # updated_at :datetime # home_page_url :string(255) # 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 diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index be29b6d66ff..b16ccc6e305 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -9,6 +9,7 @@ describe Issue, "Issuable" do it { is_expected.to belong_to(:author) } it { is_expected.to belong_to(:assignee) } it { is_expected.to have_many(:notes).dependent(:destroy) } + it { is_expected.to have_many(:todos).dependent(:destroy) } end describe "Validation" do diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb index fd1513cab1b..56a9fbe9720 100644 --- a/spec/models/hooks/system_hook_spec.rb +++ b/spec/models/hooks/system_hook_spec.rb @@ -20,24 +20,27 @@ require "spec_helper" describe SystemHook, models: true do describe "execute" do - before(:each) do - @system_hook = create(:system_hook) - WebMock.stub_request(:post, @system_hook.url) + let(:system_hook) { create(:system_hook) } + let(:user) { create(:user) } + let(:project) { create(:project, namespace: user.namespace) } + let(:group) { create(:group) } + + before do + WebMock.stub_request(:post, system_hook.url) end it "project_create hook" do - Projects::CreateService.new(create(:user), name: 'empty').execute - expect(WebMock).to have_requested(:post, @system_hook.url).with( + Projects::CreateService.new(user, name: 'empty').execute + expect(WebMock).to have_requested(:post, system_hook.url).with( body: /project_create/, headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' } ).once end it "project_destroy hook" do - user = create(:user) - project = create(:empty_project, namespace: user.namespace) Projects::DestroyService.new(project, user, {}).pending_delete! - expect(WebMock).to have_requested(:post, @system_hook.url).with( + + expect(WebMock).to have_requested(:post, system_hook.url).with( body: /project_destroy/, headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' } ).once @@ -45,37 +48,36 @@ describe SystemHook, models: true do it "user_create hook" do create(:user) - expect(WebMock).to have_requested(:post, @system_hook.url).with( + + expect(WebMock).to have_requested(:post, system_hook.url).with( body: /user_create/, headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' } ).once end it "user_destroy hook" do - user = create(:user) user.destroy - expect(WebMock).to have_requested(:post, @system_hook.url).with( + + expect(WebMock).to have_requested(:post, system_hook.url).with( body: /user_destroy/, headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' } ).once end it "project_create hook" do - user = create(:user) - project = create(:project) project.team << [user, :master] - expect(WebMock).to have_requested(:post, @system_hook.url).with( + + expect(WebMock).to have_requested(:post, system_hook.url).with( body: /user_add_to_team/, headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' } ).once end it "project_destroy hook" do - user = create(:user) - project = create(:project) project.team << [user, :master] project.project_members.destroy_all - expect(WebMock).to have_requested(:post, @system_hook.url).with( + + expect(WebMock).to have_requested(:post, system_hook.url).with( body: /user_remove_from_team/, headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' } ).once @@ -83,41 +85,39 @@ describe SystemHook, models: true do it 'group create hook' do create(:group) - expect(WebMock).to have_requested(:post, @system_hook.url).with( + + expect(WebMock).to have_requested(:post, system_hook.url).with( body: /group_create/, headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' } ).once end it 'group destroy hook' do - group = create(:group) group.destroy - expect(WebMock).to have_requested(:post, @system_hook.url).with( + + expect(WebMock).to have_requested(:post, system_hook.url).with( body: /group_destroy/, headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' } ).once end it 'group member create hook' do - group = create(:group) - user = create(:user) group.add_master(user) - expect(WebMock).to have_requested(:post, @system_hook.url).with( + + expect(WebMock).to have_requested(:post, system_hook.url).with( body: /user_add_to_group/, headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' } ).once end it 'group member destroy hook' do - group = create(:group) - user = create(:user) group.add_master(user) group.group_members.destroy_all - expect(WebMock).to have_requested(:post, @system_hook.url).with( + + expect(WebMock).to have_requested(:post, system_hook.url).with( body: /user_remove_from_group/, headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' } ).once end - end end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index bd0a4ebe337..6f5d912fe5d 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -224,22 +224,22 @@ describe MergeRequest, models: true do ['WIP ', 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', ' [WIP] WIP [WIP] WIP: WIP '].each do |wip_prefix| it "detects the '#{wip_prefix}' prefix" do subject.title = "#{wip_prefix}#{subject.title}" - expect(subject).to be_work_in_progress + expect(subject.work_in_progress?).to eq true end end it "doesn't detect WIP for words starting with WIP" do subject.title = "Wipwap #{subject.title}" - expect(subject).not_to be_work_in_progress + expect(subject.work_in_progress?).to eq false end it "doesn't detect WIP for words containing with WIP" do subject.title = "WupWipwap #{subject.title}" - expect(subject).not_to be_work_in_progress + expect(subject.work_in_progress?).to eq false end it "doesn't detect WIP by default" do - expect(subject).not_to be_work_in_progress + expect(subject.work_in_progress?).to eq false end end diff --git a/spec/models/project_services/slack_service/issue_message_spec.rb b/spec/models/project_services/slack_service/issue_message_spec.rb index 97e6f03e308..f648cbe2dee 100644 --- a/spec/models/project_services/slack_service/issue_message_spec.rb +++ b/spec/models/project_services/slack_service/issue_message_spec.rb @@ -27,6 +27,16 @@ describe SlackService::IssueMessage, models: true do let(:color) { '#345' } + context '#initialize' do + before do + args[:object_attributes][:description] = nil + end + + it 'returns a non-null description' do + expect(subject.description).to eq('') + end + end + context 'open' do it 'returns a message regarding opening of issues' do expect(subject.pretext).to eq( diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 55f1c665b86..f29c389e094 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -104,6 +104,15 @@ describe Project, models: true do end end + describe 'default_scope' do + it 'excludes projects pending deletion from the results' do + project = create(:empty_project) + create(:empty_project, pending_delete: true) + + expect(Project.all).to eq [project] + end + end + describe 'project token' do it 'should set an random token if none provided' do project = FactoryGirl.create :empty_project, runners_token: '' diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index f10d671104c..c5d5a1c2492 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -303,7 +303,7 @@ describe Repository, models: true do describe 'when there are no branches' do before do - allow(repository.raw_repository).to receive(:branch_count).and_return(0) + allow(repository).to receive(:branch_count).and_return(0) end it { is_expected.to eq(false) } @@ -311,13 +311,13 @@ describe Repository, models: true do describe 'when there are branches' do it 'returns true' do - expect(repository.raw_repository).to receive(:branch_count).and_return(3) + expect(repository).to receive(:branch_count).and_return(3) expect(subject).to eq(true) end it 'caches the output' do - expect(repository.raw_repository).to receive(:branch_count). + expect(repository).to receive(:branch_count). once. and_return(3) @@ -436,7 +436,7 @@ describe Repository, models: true do it 'expires the visible content cache' do repository.has_visible_content? - expect(repository.raw_repository).to receive(:branch_count). + expect(repository).to receive(:branch_count). once. and_return(0) @@ -558,7 +558,7 @@ describe Repository, models: true do end it 'flushes the exists cache' do - expect(repository).to receive(:expire_exists_cache) + expect(repository).to receive(:expire_exists_cache).twice repository.before_delete end @@ -865,4 +865,21 @@ describe Repository, models: true do repository.build_cache end end + + describe '#local_branches' do + it 'returns the local branches' do + masterrev = repository.find_branch('master').target + create_remote_branch('joe', 'remote_branch', masterrev) + repository.add_branch(user, 'local_branch', masterrev) + + expect(repository.local_branches.any? { |branch| branch.name == 'remote_branch' }).to eq(false) + expect(repository.local_branches.any? { |branch| branch.name == 'local_branch' }).to eq(true) + end + end + + def create_remote_branch(remote_name, branch_name, target) + rugged = repository.rugged + rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target) + end + end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 0ab7fd88ce6..8b2fb77e28e 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -173,6 +173,13 @@ describe User, models: true do expect(user).to be_invalid end end + + context 'owns_notification_email' do + it 'accepts temp_oauth_email emails' do + user = build(:user, email: "temp-email-for-oauth@example.com") + expect(user).to be_valid + end + end end end diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index ce55cb7b0ae..822d3ad3017 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -318,6 +318,17 @@ describe API::API, api: true do 'is too long (maximum is 255 characters)' ]) end + + context 'when an admin or owner makes the request' do + it "accepts the creation date to be set" do + post api("/projects/#{project.id}/issues", user), + title: 'new issue', labels: 'label, label2', created_at: 2.weeks.ago + + expect(response.status).to eq(201) + # this take about a second, so probably not equal + expect(Time.parse(json_response['created_at'])).to be <= 2.weeks.ago + end + end end describe 'POST /projects/:id/issues with spam filtering' do diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index 667f0dbea5c..6943ff9d26c 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -23,13 +23,25 @@ describe API::API, api: true do end describe 'POST /projects/:id/labels' do - it 'should return created label' do + it 'should return created label when all params' do + post api("/projects/#{project.id}/labels", user), + name: 'Foo', + color: '#FFAABB', + description: 'test' + expect(response.status).to eq(201) + expect(json_response['name']).to eq('Foo') + expect(json_response['color']).to eq('#FFAABB') + expect(json_response['description']).to eq('test') + end + + it 'should return created label when only required params' do post api("/projects/#{project.id}/labels", user), name: 'Foo', color: '#FFAABB' expect(response.status).to eq(201) expect(json_response['name']).to eq('Foo') expect(json_response['color']).to eq('#FFAABB') + expect(json_response['description']).to be_nil end it 'should return a 400 bad request if name not given' do @@ -94,14 +106,16 @@ describe API::API, api: true do end describe 'PUT /projects/:id/labels' do - it 'should return 200 if name and colors are changed' do + it 'should return 200 if name and colors and description are changed' do put api("/projects/#{project.id}/labels", user), name: 'label1', new_name: 'New Label', - color: '#FFFFFF' + color: '#FFFFFF', + description: 'test' expect(response.status).to eq(200) expect(json_response['name']).to eq('New Label') expect(json_response['color']).to eq('#FFFFFF') + expect(json_response['description']).to eq('test') end it 'should return 200 if name is changed' do @@ -122,6 +136,15 @@ describe API::API, api: true do expect(json_response['color']).to eq('#FFFFFF') end + it 'should return 200 if description is changed' do + put api("/projects/#{project.id}/labels", user), + name: 'label1', + description: 'test' + expect(response.status).to eq(200) + expect(json_response['name']).to eq(label1.name) + expect(json_response['description']).to eq('test') + end + it 'should return 404 if label does not exist' do put api("/projects/#{project.id}/labels", user), name: 'label2', diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index c9175a4d6eb..25fa30b2f21 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -118,6 +118,7 @@ describe API::API, api: true do expect(response.status).to eq(200) expect(json_response['title']).to eq(merge_request.title) expect(json_response['iid']).to eq(merge_request.iid) + expect(json_response['work_in_progress']).to eq(false) expect(json_response['merge_status']).to eq('can_be_merged') end @@ -133,6 +134,16 @@ describe API::API, api: true do get api("/projects/#{project.id}/merge_requests/999", user) expect(response.status).to eq(404) end + + context 'Work in Progress' do + let!(:merge_request_wip) { create(:merge_request, author: user, assignee: user, source_project: project, target_project: project, title: "WIP: Test", created_at: base_time + 1.second) } + + it "should return merge_request" do + get api("/projects/#{project.id}/merge_requests/#{merge_request_wip.id}", user) + expect(response.status).to eq(200) + expect(json_response['work_in_progress']).to eq(true) + end + end end describe 'GET /projects/:id/merge_requests/:merge_request_id/commits' do diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index a5d4985dc78..be2034e0f39 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -948,6 +948,78 @@ describe API::API, api: true do end end + describe 'POST /projects/:id/archive' do + context 'on an unarchived project' do + it 'archives the project' do + post api("/projects/#{project.id}/archive", user) + + expect(response.status).to eq(201) + expect(json_response['archived']).to be_truthy + end + end + + context 'on an archived project' do + before do + project.archive! + end + + it 'remains archived' do + post api("/projects/#{project.id}/archive", user) + + expect(response.status).to eq(201) + expect(json_response['archived']).to be_truthy + end + end + + context 'user without archiving rights to the project' do + before do + project.team << [user3, :developer] + end + + it 'rejects the action' do + post api("/projects/#{project.id}/archive", user3) + + expect(response.status).to eq(403) + end + end + end + + describe 'POST /projects/:id/unarchive' do + context 'on an unarchived project' do + it 'remains unarchived' do + post api("/projects/#{project.id}/unarchive", user) + + expect(response.status).to eq(201) + expect(json_response['archived']).to be_falsey + end + end + + context 'on an archived project' do + before do + project.archive! + end + + it 'unarchives the project' do + post api("/projects/#{project.id}/unarchive", user) + + expect(response.status).to eq(201) + expect(json_response['archived']).to be_falsey + end + end + + context 'user without archiving rights to the project' do + before do + project.team << [user3, :developer] + end + + it 'rejects the action' do + post api("/projects/#{project.id}/unarchive", user3) + + expect(response.status).to eq(403) + end + end + end + describe 'DELETE /projects/:id' do context 'when authenticated as user' do it 'should remove project' do diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index 9b0c73aaf37..2a5e4ac3ec4 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -160,6 +160,20 @@ describe Issues::MoveService, services: true do .to eq "Note with reference to merge request #{old_project.to_reference}!1" end end + + context 'issue description with uploads' do + let(:uploader) { build(:file_uploader, project: old_project) } + let(:description) { "Text and #{uploader.to_markdown}" } + + include_context 'issue move executed' + + it 'rewrites uploads in description' do + expect(new_issue.description).to_not eq description + expect(new_issue.description) + .to match(/Text and #{FileUploader::MARKDOWN_PATTERN}/) + expect(new_issue.description).to_not include uploader.secret + end + end end describe 'rewritting references' do diff --git a/spec/services/projects/unlink_fork_service_spec.rb b/spec/services/projects/unlink_fork_service_spec.rb new file mode 100644 index 00000000000..23f5555d3e0 --- /dev/null +++ b/spec/services/projects/unlink_fork_service_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe Projects::UnlinkForkService, services: true do + subject { Projects::UnlinkForkService.new(fork_project, user) } + + let(:fork_link) { create(:forked_project_link) } + let(:fork_project) { fork_link.forked_to_project } + let(:user) { create(:user) } + + context 'with opened merge request on the source project' do + let(:merge_request) { create(:merge_request, source_project: fork_project, target_project: fork_link.forked_from_project) } + let(:mr_close_service) { MergeRequests::CloseService.new(fork_project, user) } + + before do + allow(MergeRequests::CloseService).to receive(:new). + with(fork_project, user). + and_return(mr_close_service) + end + + it 'close all pending merge requests' do + expect(mr_close_service).to receive(:execute).with(merge_request) + + subject.execute + end + end + + it 'remove fork relation' do + expect(fork_project.forked_project_link).to receive(:destroy) + + subject.execute + end +end diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb index b4728807b8b..82b7fbfa816 100644 --- a/spec/services/todo_service_spec.rb +++ b/spec/services/todo_service_spec.rb @@ -2,22 +2,25 @@ require 'spec_helper' describe TodoService, services: true do let(:author) { create(:user) } - let(:john_doe) { create(:user, username: 'john_doe') } - let(:michael) { create(:user, username: 'michael') } - let(:stranger) { create(:user, username: 'stranger') } + let(:assignee) { create(:user) } + let(:non_member) { create(:user) } + let(:member) { create(:user) } + let(:admin) { create(:admin) } + let(:john_doe) { create(:user) } let(:project) { create(:project) } - let(:mentions) { [author.to_reference, john_doe.to_reference, michael.to_reference, stranger.to_reference].join(' ') } + let(:mentions) { [author, assignee, john_doe, member, non_member, admin].map(&:to_reference).join(' ') } let(:service) { described_class.new } before do project.team << [author, :developer] + project.team << [member, :developer] project.team << [john_doe, :developer] - project.team << [michael, :developer] end describe 'Issues' do let(:issue) { create(:issue, project: project, assignee: john_doe, author: author, description: mentions) } let(:unassigned_issue) { create(:issue, project: project, assignee: nil) } + let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee, description: mentions) } describe '#new_issue' do it 'creates a todo if assigned' do @@ -37,10 +40,20 @@ describe TodoService, services: true do it 'creates a todo for each valid mentioned user' do service.new_issue(issue, author) - should_create_todo(user: michael, target: issue, action: Todo::MENTIONED) + should_create_todo(user: member, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED) - should_not_create_todo(user: stranger, target: issue, action: Todo::MENTIONED) + should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED) + end + + it 'does not create todo for non project members when issue is confidential' do + service.new_issue(confidential_issue, john_doe) + + should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::ASSIGNED) + should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) + should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) + should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) + should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) end end @@ -48,16 +61,26 @@ describe TodoService, services: true do it 'creates a todo for each valid mentioned user' do service.update_issue(issue, author) - should_create_todo(user: michael, target: issue, action: Todo::MENTIONED) + should_create_todo(user: member, target: issue, action: Todo::MENTIONED) should_create_todo(user: john_doe, target: issue, action: Todo::MENTIONED) should_not_create_todo(user: author, target: issue, action: Todo::MENTIONED) - should_not_create_todo(user: stranger, target: issue, action: Todo::MENTIONED) + should_not_create_todo(user: non_member, target: issue, action: Todo::MENTIONED) end it 'does not create a todo if user was already mentioned' do - create(:todo, :mentioned, user: michael, project: project, target: issue, author: author) + create(:todo, :mentioned, user: member, project: project, target: issue, author: author) - expect { service.update_issue(issue, author) }.not_to change(michael.todos, :count) + expect { service.update_issue(issue, author) }.not_to change(member.todos, :count) + end + + it 'does not create todo for non project members when issue is confidential' do + service.update_issue(confidential_issue, john_doe) + + should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) + should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) + should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) + should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) + should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) end end @@ -109,8 +132,10 @@ describe TodoService, services: true do describe '#new_note' do let!(:first_todo) { create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author) } let!(:second_todo) { create(:todo, :assigned, user: john_doe, project: project, target: issue, author: author) } + let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignee: assignee) } let(:note) { create(:note, project: project, noteable: issue, author: john_doe, note: mentions) } let(:note_on_commit) { create(:note_on_commit, project: project, author: john_doe, note: mentions) } + let(:note_on_confidential_issue) { create(:note_on_issue, noteable: confidential_issue, project: project, note: mentions) } let(:note_on_project_snippet) { create(:note_on_project_snippet, project: project, author: john_doe, note: mentions) } let(:award_note) { create(:note, :award, project: project, noteable: issue, author: john_doe, note: 'thumbsup') } let(:system_note) { create(:system_note, project: project, noteable: issue) } @@ -142,19 +167,29 @@ describe TodoService, services: true do it 'creates a todo for each valid mentioned user' do service.new_note(note, john_doe) - should_create_todo(user: michael, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) + should_create_todo(user: member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) should_create_todo(user: author, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) should_not_create_todo(user: john_doe, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) - should_not_create_todo(user: stranger, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) + should_not_create_todo(user: non_member, target: issue, author: john_doe, action: Todo::MENTIONED, note: note) + end + + it 'does not create todo for non project members when leaving a note on a confidential issue' do + service.new_note(note_on_confidential_issue, john_doe) + + should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) + should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) + should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) + should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) + should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue) end it 'creates a todo for each valid mentioned user when leaving a note on commit' do service.new_note(note_on_commit, john_doe) - should_create_todo(user: michael, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) + should_create_todo(user: member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) should_create_todo(user: author, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) should_not_create_todo(user: john_doe, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) - should_not_create_todo(user: stranger, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) + should_not_create_todo(user: non_member, target_id: nil, target_type: 'Commit', commit_id: note_on_commit.commit_id, author: john_doe, action: Todo::MENTIONED, note: note_on_commit) end it 'does not create todo when leaving a note on snippet' do @@ -185,10 +220,10 @@ describe TodoService, services: true do it 'creates a todo for each valid mentioned user' do service.new_merge_request(mr_assigned, author) - should_create_todo(user: michael, target: mr_assigned, action: Todo::MENTIONED) + should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED) - should_not_create_todo(user: stranger, target: mr_assigned, action: Todo::MENTIONED) + should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED) end end @@ -196,16 +231,16 @@ describe TodoService, services: true do it 'creates a todo for each valid mentioned user' do service.update_merge_request(mr_assigned, author) - should_create_todo(user: michael, target: mr_assigned, action: Todo::MENTIONED) + should_create_todo(user: member, target: mr_assigned, action: Todo::MENTIONED) should_create_todo(user: john_doe, target: mr_assigned, action: Todo::MENTIONED) should_not_create_todo(user: author, target: mr_assigned, action: Todo::MENTIONED) - should_not_create_todo(user: stranger, target: mr_assigned, action: Todo::MENTIONED) + should_not_create_todo(user: non_member, target: mr_assigned, action: Todo::MENTIONED) end it 'does not create a todo if user was already mentioned' do - create(:todo, :mentioned, user: michael, project: project, target: mr_assigned, author: author) + create(:todo, :mentioned, user: member, project: project, target: mr_assigned, author: author) - expect { service.update_merge_request(mr_assigned, author) }.not_to change(michael.todos, :count) + expect { service.update_merge_request(mr_assigned, author) }.not_to change(member.todos, :count) end end diff --git a/spec/support/carrierwave.rb b/spec/support/carrierwave.rb new file mode 100644 index 00000000000..aa89afd8fb3 --- /dev/null +++ b/spec/support/carrierwave.rb @@ -0,0 +1,7 @@ +CarrierWave.root = 'tmp/tests/uploads' + +RSpec.configure do |config| + config.after(:suite) do + FileUtils.rm_rf('tmp/tests/uploads') + end +end diff --git a/spec/support/filter_spec_helper.rb b/spec/support/filter_spec_helper.rb index ef5ea7d626e..e849a9633b9 100644 --- a/spec/support/filter_spec_helper.rb +++ b/spec/support/filter_spec_helper.rb @@ -78,6 +78,6 @@ module FilterSpecHelper # Shortcut to Rails' auto-generated routes helpers, to avoid including the # module def urls - Gitlab::Application.routes.url_helpers + Gitlab::Routing.url_helpers end end diff --git a/spec/support/markdown_feature.rb b/spec/support/markdown_feature.rb index 73c6792b65f..b87cd6bbca2 100644 --- a/spec/support/markdown_feature.rb +++ b/spec/support/markdown_feature.rb @@ -106,7 +106,7 @@ class MarkdownFeature end def urls - Gitlab::Application.routes.url_helpers + Gitlab::Routing.url_helpers end def raw_markdown diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 0d1bd030f3c..71664bb192e 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -15,6 +15,7 @@ module TestEnv 'lfs' => 'be93687', 'master' => '5937ac0', "'test'" => 'e56497b', + 'orphaned-branch' => '45127a9', } # gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 320be9a0b61..05fc4c4554f 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -7,8 +7,12 @@ describe 'gitlab:app namespace rake task' do Rake.application.rake_require 'tasks/gitlab/backup' Rake.application.rake_require 'tasks/gitlab/shell' Rake.application.rake_require 'tasks/gitlab/db' + # empty task as env is already loaded Rake::Task.define_task :environment + + # We need this directory to run `gitlab:backup:create` task + FileUtils.mkdir_p('public/uploads') end def run_rake_task(task_name) diff --git a/spec/workers/merge_worker_spec.rb b/spec/workers/merge_worker_spec.rb index b11c5de94e3..1abd87d7d33 100644 --- a/spec/workers/merge_worker_spec.rb +++ b/spec/workers/merge_worker_spec.rb @@ -22,6 +22,8 @@ describe MergeWorker do merge_request.reload expect(merge_request).to be_merged + + source_project.repository.expire_branches_cache expect(source_project.repository.branch_names).not_to include('markdown') end end diff --git a/spec/workers/project_cache_worker_spec.rb b/spec/workers/project_cache_worker_spec.rb new file mode 100644 index 00000000000..7e59bd2fced --- /dev/null +++ b/spec/workers/project_cache_worker_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe ProjectCacheWorker do + let(:project) { create(:project) } + + subject { described_class.new } + + describe '#perform' do + it 'updates project cache data' do + + expect_any_instance_of(Repository).to receive(:size) + expect_any_instance_of(Repository).to receive(:commit_count) + + expect_any_instance_of(Project).to receive(:update_repository_size) + expect_any_instance_of(Project).to receive(:update_commit_count) + + subject.perform(project.id) + end + + it 'handles missing repository data' do + expect_any_instance_of(Repository).to receive(:exists?).and_return(false) + expect_any_instance_of(Repository).not_to receive(:size) + + subject.perform(project.id) + end + end +end |
