diff options
author | Douwe Maan <douwe@gitlab.com> | 2015-10-18 11:31:49 +0200 |
---|---|---|
committer | Douwe Maan <douwe@gitlab.com> | 2015-10-18 11:31:49 +0200 |
commit | df9062d0b50d08a8a484b9780aeb92c53bead530 (patch) | |
tree | 13985b14daf1b1614b35aade3f74a0c80f8eb10c /spec | |
parent | ae4fbae26cefbf10848719ee8c06d418c348420c (diff) | |
parent | 38785046f7ec7d834e22add66e4be88f6e985355 (diff) | |
download | gitlab-ce-df9062d0b50d08a8a484b9780aeb92c53bead530.tar.gz |
Merge branch 'master' into jrochkind/gitlab-ce-fix_2839_send_abuse_report_notify
Diffstat (limited to 'spec')
99 files changed, 2898 insertions, 1246 deletions
diff --git a/spec/benchmarks/finders/trending_projects_finder_spec.rb b/spec/benchmarks/finders/trending_projects_finder_spec.rb new file mode 100644 index 00000000000..551ce21840d --- /dev/null +++ b/spec/benchmarks/finders/trending_projects_finder_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe TrendingProjectsFinder, benchmark: true do + describe '#execute' do + let(:finder) { described_class.new } + let(:user) { create(:user) } + + # to_a is used to force actually running the query (instead of just building + # it). + benchmark_subject { finder.execute(user).non_archived.to_a } + + it { is_expected.to iterate_per_second(500) } + end +end diff --git a/spec/benchmarks/models/project_spec.rb b/spec/benchmarks/models/project_spec.rb new file mode 100644 index 00000000000..cee0949edc5 --- /dev/null +++ b/spec/benchmarks/models/project_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe Project, benchmark: true do + describe '.trending' do + let(:group) { create(:group) } + let(:project1) { create(:empty_project, :public, group: group) } + let(:project2) { create(:empty_project, :public, group: group) } + + let(:iterations) { 500 } + + before do + 2.times do + create(:note_on_commit, project: project1) + end + + create(:note_on_commit, project: project2) + end + + describe 'without an explicit start date' do + benchmark_subject { described_class.trending.to_a } + + it { is_expected.to iterate_per_second(iterations) } + end + + describe 'with an explicit start date' do + let(:date) { 1.month.ago } + + benchmark_subject { described_class.trending(date).to_a } + + it { is_expected.to iterate_per_second(iterations) } + end + end + + describe '.find_with_namespace' do + let(:group) { create(:group, name: 'sisinmaru') } + let(:project) { create(:project, name: 'maru', namespace: group) } + + describe 'using a capitalized namespace' do + benchmark_subject { described_class.find_with_namespace('sisinmaru/MARU') } + + it { is_expected.to iterate_per_second(600) } + end + + describe 'using a lowercased namespace' do + benchmark_subject { described_class.find_with_namespace('sisinmaru/maru') } + + it { is_expected.to iterate_per_second(600) } + end + end +end diff --git a/spec/benchmarks/models/project_team_spec.rb b/spec/benchmarks/models/project_team_spec.rb new file mode 100644 index 00000000000..8b039ef7317 --- /dev/null +++ b/spec/benchmarks/models/project_team_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe ProjectTeam, benchmark: true do + describe '#max_member_access' do + let(:group) { create(:group) } + let(:project) { create(:empty_project, group: group) } + let(:user) { create(:user) } + + before do + project.team << [user, :master] + + 5.times do + project.team << [create(:user), :reporter] + + project.group.add_user(create(:user), :reporter) + end + end + + benchmark_subject { project.team.max_member_access(user.id) } + + it { is_expected.to iterate_per_second(35000) } + end +end diff --git a/spec/benchmarks/models/user_spec.rb b/spec/benchmarks/models/user_spec.rb new file mode 100644 index 00000000000..cc5c3904193 --- /dev/null +++ b/spec/benchmarks/models/user_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe User, benchmark: true do + describe '.by_login' do + before do + %w{Alice Bob Eve}.each do |name| + create(:user, + email: "#{name}@gitlab.com", + username: name, + name: name) + end + end + + # The iteration count is based on the query taking little over 1 ms when + # using PostgreSQL. + let(:iterations) { 900 } + + describe 'using a capitalized username' do + benchmark_subject { User.by_login('Alice') } + + it { is_expected.to iterate_per_second(iterations) } + end + + describe 'using a lowercase username' do + benchmark_subject { User.by_login('alice') } + + it { is_expected.to iterate_per_second(iterations) } + end + + describe 'using a capitalized Email address' do + benchmark_subject { User.by_login('Alice@gitlab.com') } + + it { is_expected.to iterate_per_second(iterations) } + end + + describe 'using a lowercase Email address' do + benchmark_subject { User.by_login('alice@gitlab.com') } + + it { is_expected.to iterate_per_second(iterations) } + end + end +end diff --git a/spec/controllers/ci/commits_controller_spec.rb b/spec/controllers/ci/commits_controller_spec.rb deleted file mode 100644 index cc39ce7687c..00000000000 --- a/spec/controllers/ci/commits_controller_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -require "spec_helper" - -describe Ci::CommitsController do - describe "GET /status" do - it "returns status of commit" do - commit = FactoryGirl.create :ci_commit - get :status, id: commit.sha, ref_id: commit.ref, project_id: commit.project.id - - expect(response).to be_success - expect(response.code).to eq('200') - JSON.parse(response.body)["status"] == "pending" - end - - it "returns not_found status" do - commit = FactoryGirl.create :ci_commit - get :status, id: commit.sha, ref_id: "deploy", project_id: commit.project.id - - expect(response).to be_success - expect(response.code).to eq('200') - JSON.parse(response.body)["status"] == "not_found" - end - end -end diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb index 91856ed0cc0..18a30033ed8 100644 --- a/spec/controllers/projects/repositories_controller_spec.rb +++ b/spec/controllers/projects/repositories_controller_spec.rb @@ -33,33 +33,5 @@ describe Projects::RepositoriesController do expect(response.status).to eq(404) end end - - context "when the service doesn't return a path" do - - before do - allow(service).to receive(:execute).and_return(nil) - end - - it "reloads the page" do - get :archive, namespace_id: project.namespace.path, project_id: project.path, ref: "master", format: "zip" - - expect(response).to redirect_to(archive_namespace_project_repository_path(project.namespace, project, ref: "master", format: "zip")) - end - end - - context "when the service returns a path" do - - let(:path) { Rails.root.join("spec/fixtures/dk.png").to_s } - - before do - allow(service).to receive(:execute).and_return(path) - end - - it "sends the file" do - get :archive, namespace_id: project.namespace.path, project_id: project.path, ref: "master", format: "zip" - - expect(response.body).to eq(File.binread(path)) - end - end end end diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb index 53915856357..a474574c6e5 100644 --- a/spec/controllers/projects/tree_controller_spec.rb +++ b/spec/controllers/projects/tree_controller_spec.rb @@ -88,4 +88,40 @@ describe Projects::TreeController do end end end + + describe '#create_dir' do + render_views + + before do + post(:create_dir, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: 'master', + dir_name: path, + new_branch: target_branch, + commit_message: 'Test commit message') + end + + context 'successful creation' do + let(:path) { 'files/new_dir'} + let(:target_branch) { 'master-test'} + + it 'redirects to the new directory' do + expect(subject). + to redirect_to("/#{project.path_with_namespace}/blob/#{target_branch}/#{path}") + expect(flash[:notice]).to eq('The directory has been successfully created') + end + end + + context 'unsuccessful creation' do + let(:path) { 'README.md' } + let(:target_branch) { 'master'} + + it 'does not allow overwriting of existing files' do + expect(subject). + to redirect_to("/#{project.path_with_namespace}/blob/master") + expect(flash[:alert]).to eq('Directory already exists as a file') + end + end + end end diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb index f51abfedae5..93c4494c660 100644 --- a/spec/controllers/projects/uploads_controller_spec.rb +++ b/spec/controllers/projects/uploads_controller_spec.rb @@ -33,7 +33,7 @@ describe Projects::UploadsController do it 'returns a content with original filename, new link, and correct type.' do expect(response.body).to match '\"alt\":\"rails_sample\"' - expect(response.body).to match "\"url\":\"http://localhost/#{project.path_with_namespace}/uploads" + expect(response.body).to match "\"url\":\"/uploads" expect(response.body).to match '\"is_image\":true' end end @@ -49,7 +49,7 @@ describe Projects::UploadsController do it 'returns a content with original filename, new link, and correct type.' do expect(response.body).to match '\"alt\":\"doc_sample.txt\"' - expect(response.body).to match "\"url\":\"http://localhost/#{project.path_with_namespace}/uploads" + expect(response.body).to match "\"url\":\"/uploads" expect(response.body).to match '\"is_image\":false' end end diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index 99da5a18776..2fcd70182b9 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -27,6 +27,9 @@ FactoryGirl.define do factory :ci_build, class: Ci::Build do + name 'test' + ref 'master' + tag false started_at 'Di 29. Okt 09:51:28 CET 2013' finished_at 'Di 29. Okt 09:53:28 CET 2013' commands 'ls -a' @@ -43,5 +46,9 @@ FactoryGirl.define do started_at nil finished_at nil end + + factory :ci_build_tag do + tag true + end end end diff --git a/spec/factories/ci/commits.rb b/spec/factories/ci/commits.rb index 9c7a0e9cbe0..79e000b7ccb 100644 --- a/spec/factories/ci/commits.rb +++ b/spec/factories/ci/commits.rb @@ -17,60 +17,32 @@ # Read about factories at https://github.com/thoughtbot/factory_girl FactoryGirl.define do - factory :ci_commit, class: Ci::Commit do - ref 'master' - before_sha '76de212e80737a608d939f648d959671fb0a0142' + factory :ci_empty_commit, class: Ci::Commit do sha '97de212e80737a608d939f648d959671fb0a0142' - push_data do - { - ref: 'refs/heads/master', - before: '76de212e80737a608d939f648d959671fb0a0142', - after: '97de212e80737a608d939f648d959671fb0a0142', - user_name: 'Git User', - user_email: 'git@example.com', - repository: { - name: 'test-data', - url: 'ssh://git@gitlab.com/test/test-data.git', - description: '', - homepage: 'http://gitlab.com/test/test-data' - }, - commits: [ - { - id: '97de212e80737a608d939f648d959671fb0a0142', - message: 'Test commit message', - timestamp: '2014-09-23T13:12:25+02:00', - url: 'https://gitlab.com/test/test-data/commit/97de212e80737a608d939f648d959671fb0a0142', - author: { - name: 'Git User', - email: 'git@user.com' - } - } - ], - total_commits_count: 1, - ci_yaml_file: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) - } - end gl_project factory: :empty_project factory :ci_commit_without_jobs do - after(:create) do |commit, evaluator| - commit.push_data[:ci_yaml_file] = YAML.dump({}) - commit.save + after(:build) do |commit| + allow(commit).to receive(:ci_yaml_file) { YAML.dump({}) } end end factory :ci_commit_with_one_job do - after(:create) do |commit, evaluator| - commit.push_data[:ci_yaml_file] = YAML.dump({ rspec: { script: "ls" } }) - commit.save + after(:build) do |commit| + allow(commit).to receive(:ci_yaml_file) { YAML.dump({ rspec: { script: "ls" } }) } end end factory :ci_commit_with_two_jobs do - after(:create) do |commit, evaluator| - commit.push_data[:ci_yaml_file] = YAML.dump({ rspec: { script: "ls" }, spinach: { script: "ls" } }) - commit.save + after(:build) do |commit| + allow(commit).to receive(:ci_yaml_file) { YAML.dump({ rspec: { script: "ls" }, spinach: { script: "ls" } }) } + end + end + + factory :ci_commit do + after(:build) do |commit| + allow(commit).to receive(:ci_yaml_file) { File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) } end end end diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb new file mode 100644 index 00000000000..52de437052d --- /dev/null +++ b/spec/factories/commit_statuses.rb @@ -0,0 +1,15 @@ +FactoryGirl.define do + factory :commit_status, class: CommitStatus do + started_at 'Di 29. Okt 09:51:28 CET 2013' + finished_at 'Di 29. Okt 09:53:28 CET 2013' + name 'default' + status 'success' + description 'commit status' + commit factory: :ci_commit + + factory :generic_commit_status, class: GenericCommitStatus do + name 'generic' + description 'external commit status' + end + end +end diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb new file mode 100644 index 00000000000..154857e77fe --- /dev/null +++ b/spec/features/builds_spec.rb @@ -0,0 +1,89 @@ +require 'spec_helper' + +describe "Builds" do + before do + login_as(:user) + @commit = FactoryGirl.create :ci_commit + @build = FactoryGirl.create :ci_build, commit: @commit + @gl_project = @commit.project.gl_project + @gl_project.team << [@user, :master] + end + + describe "GET /:project/builds" do + context "Running scope" do + before do + @build.run! + visit namespace_project_builds_path(@gl_project.namespace, @gl_project) + end + + it { expect(page).to have_content 'Running' } + it { expect(page).to have_content 'Cancel all' } + it { expect(page).to have_content @build.short_sha } + it { expect(page).to have_content @build.ref } + it { expect(page).to have_content @build.name } + end + + context "Finished scope" do + before do + @build.run! + visit namespace_project_builds_path(@gl_project.namespace, @gl_project, scope: :finished) + end + + it { expect(page).to have_content 'No builds to show' } + it { expect(page).to have_content 'Cancel all' } + end + + context "All builds" do + before do + @gl_project.ci_builds.running_or_pending.each(&:success) + visit namespace_project_builds_path(@gl_project.namespace, @gl_project, scope: :all) + end + + it { expect(page).to have_content 'All' } + it { expect(page).to have_content @build.short_sha } + it { expect(page).to have_content @build.ref } + it { expect(page).to have_content @build.name } + it { expect(page).to_not have_content 'Cancel all' } + end + end + + describe "GET /:project/builds/:id/cancel_all" do + before do + @build.run! + visit cancel_all_namespace_project_builds_path(@gl_project.namespace, @gl_project) + end + + it { expect(page).to have_content 'No builds to show' } + it { expect(page).to_not have_content 'Cancel all' } + end + + describe "GET /:project/builds/:id" do + before do + visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + end + + it { expect(page).to have_content @commit.sha[0..7] } + it { expect(page).to have_content @commit.git_commit_message } + it { expect(page).to have_content @commit.git_author_name } + end + + describe "GET /:project/builds/:id/cancel" do + before do + @build.run! + visit cancel_namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + end + + it { expect(page).to have_content 'canceled' } + it { expect(page).to have_content 'Retry' } + end + + describe "POST /:project/builds/:id/retry" do + before do + visit cancel_namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + click_link 'Retry' + end + + it { expect(page).to have_content 'pending' } + it { expect(page).to have_content 'Cancel' } + end +end diff --git a/spec/features/ci/builds_spec.rb b/spec/features/ci/builds_spec.rb deleted file mode 100644 index d65699dbefa..00000000000 --- a/spec/features/ci/builds_spec.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'spec_helper' - -describe "Builds" do - context :private_project do - before do - @commit = FactoryGirl.create :ci_commit - @build = FactoryGirl.create :ci_build, commit: @commit - login_as :user - @commit.project.gl_project.team << [@user, :master] - end - - describe "GET /:project/builds/:id" do - before do - visit ci_project_build_path(@commit.project, @build) - end - - it { expect(page).to have_content @commit.sha[0..7] } - it { expect(page).to have_content @commit.git_commit_message } - it { expect(page).to have_content @commit.git_author_name } - end - - describe "GET /:project/builds/:id/cancel" do - before do - @build.run! - visit cancel_ci_project_build_path(@commit.project, @build) - end - - it { expect(page).to have_content 'canceled' } - it { expect(page).to have_content 'Retry' } - end - - describe "POST /:project/builds/:id/retry" do - before do - @build.cancel! - visit ci_project_build_path(@commit.project, @build) - click_link 'Retry' - end - - it { expect(page).to have_content 'pending' } - it { expect(page).to have_content 'Cancel' } - end - end - - context :public_project do - describe "Show page public accessible" do - before do - @commit = FactoryGirl.create :ci_commit - @commit.project.public = true - @commit.project.save - - @runner = FactoryGirl.create :ci_specific_runner - @build = FactoryGirl.create :ci_build, commit: @commit, runner: @runner - - stub_gitlab_calls - visit ci_project_build_path(@commit.project, @build) - end - - it { expect(page).to have_content @commit.sha[0..7] } - end - end -end diff --git a/spec/features/ci/commits_spec.rb b/spec/features/ci/commits_spec.rb deleted file mode 100644 index 657a9dabe9e..00000000000 --- a/spec/features/ci/commits_spec.rb +++ /dev/null @@ -1,70 +0,0 @@ -require 'spec_helper' - -describe "Commits" do - include Ci::CommitsHelper - - context "Authenticated user" do - before do - @commit = FactoryGirl.create :ci_commit - @build = FactoryGirl.create :ci_build, commit: @commit - login_as :user - @commit.project.gl_project.team << [@user, :master] - end - - describe "GET /:project/commits/:sha" do - before do - visit ci_commit_path(@commit) - end - - it { expect(page).to have_content @commit.sha[0..7] } - it { expect(page).to have_content @commit.git_commit_message } - it { expect(page).to have_content @commit.git_author_name } - end - - describe "Cancel commit" do - it "cancels commit" do - visit ci_commit_path(@commit) - click_on "Cancel" - - expect(page).to have_content "canceled" - end - end - - describe ".gitlab-ci.yml not found warning" do - it "does not show warning" do - visit ci_commit_path(@commit) - - expect(page).not_to have_content ".gitlab-ci.yml not found in this commit" - end - - it "shows warning" do - @commit.push_data[:ci_yaml_file] = nil - @commit.save - - visit ci_commit_path(@commit) - - expect(page).to have_content ".gitlab-ci.yml not found in this commit" - end - end - end - - context "Public pages" do - before do - @commit = FactoryGirl.create :ci_commit - @commit.project.public = true - @commit.project.save - - @build = FactoryGirl.create :ci_build, commit: @commit - end - - describe "GET /:project/commits/:sha" do - before do - visit ci_commit_path(@commit) - end - - it { expect(page).to have_content @commit.sha[0..7] } - it { expect(page).to have_content @commit.git_commit_message } - it { expect(page).to have_content @commit.git_author_name } - end - end -end diff --git a/spec/features/ci/projects_spec.rb b/spec/features/ci/projects_spec.rb deleted file mode 100644 index c633acf85fb..00000000000 --- a/spec/features/ci/projects_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'spec_helper' - -describe "Projects" do - let(:user) { create(:user) } - - before do - login_as(user) - @project = FactoryGirl.create :ci_project, name: "GitLab / gitlab-shell" - @project.gl_project.team << [user, :master] - end - - describe "GET /ci/projects/:id" do - before do - visit ci_project_path(@project) - end - - it { expect(page).to have_content @project.name } - it { expect(page).to have_content 'All commits' } - end -end diff --git a/spec/features/ci_web_hooks_spec.rb b/spec/features/ci_web_hooks_spec.rb new file mode 100644 index 00000000000..efae0a42c1e --- /dev/null +++ b/spec/features/ci_web_hooks_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe 'CI web hooks' do + let(:user) { create(:user) } + before { login_as(user) } + + before do + @project = FactoryGirl.create :ci_project + @gl_project = @project.gl_project + @gl_project.team << [user, :master] + visit namespace_project_ci_web_hooks_path(@gl_project.namespace, @gl_project) + end + + context 'create a trigger' do + before do + fill_in 'web_hook_url', with: 'http://example.com' + click_on 'Add Web Hook' + end + + it { expect(@project.web_hooks.count).to eq(1) } + + it 'revokes the trigger' do + click_on 'Remove' + expect(@project.web_hooks.count).to eq(0) + end + end +end diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb new file mode 100644 index 00000000000..1adc2cdf70a --- /dev/null +++ b/spec/features/commits_spec.rb @@ -0,0 +1,61 @@ +require 'spec_helper' + +describe "Commits" do + include CiStatusHelper + + let(:project) { create(:project) } + + describe "CI" do + before do + login_as :user + project.team << [@user, :master] + @ci_project = project.ensure_gitlab_ci_project + @commit = FactoryGirl.create :ci_commit, gl_project: project, sha: project.commit.sha + @build = FactoryGirl.create :ci_build, commit: @commit + @generic_status = FactoryGirl.create :generic_commit_status, commit: @commit + end + + before do + stub_ci_commit_to_return_yaml_file + end + + describe "GET /:project/commits/:sha" do + before do + visit ci_status_path(@commit) + end + + it { expect(page).to have_content @commit.sha[0..7] } + it { expect(page).to have_content @commit.git_commit_message } + it { expect(page).to have_content @commit.git_author_name } + end + + describe "Cancel all builds" do + it "cancels commit" do + visit ci_status_path(@commit) + click_on "Cancel all" + expect(page).to have_content "canceled" + end + end + + describe "Cancel build" do + it "cancels build" do + visit ci_status_path(@commit) + click_on "Cancel" + expect(page).to have_content "canceled" + end + end + + describe ".gitlab-ci.yml not found warning" do + it "does not show warning" do + visit ci_status_path(@commit) + expect(page).not_to have_content ".gitlab-ci.yml not found in this commit" + end + + it "shows warning" do + stub_ci_commit_yaml_file(nil) + visit ci_status_path(@commit) + expect(page).to have_content ".gitlab-ci.yml not found in this commit" + end + end + end +end diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb index c557a1061af..fdd8cf07b12 100644 --- a/spec/features/markdown_spec.rb +++ b/spec/features/markdown_spec.rb @@ -220,7 +220,7 @@ describe 'GitLab Markdown', feature: true do end end - # `markdown` calls these two methods + # Fake a `current_user` helper def current_user @feat.user end diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index a362c54b9ad..aac93b17a38 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -50,7 +50,7 @@ feature 'Project', feature: true do end def remove_project - click_link "Remove project" + click_button "Remove project" fill_in 'confirm_name_input', with: project.path click_button 'Confirm' end diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb index db20b23f87d..b1648055462 100644 --- a/spec/finders/issues_finder_spec.rb +++ b/spec/finders/issues_finder_spec.rb @@ -6,9 +6,11 @@ describe IssuesFinder do let(:project1) { create(:project) } let(:project2) { create(:project) } let(:milestone) { create(:milestone, project: project1) } + let(:label) { create(:label, project: project2) } let(:issue1) { create(:issue, author: user, assignee: user, project: project1, milestone: milestone) } let(:issue2) { create(:issue, author: user, assignee: user, project: project2) } let(:issue3) { create(:issue, author: user2, assignee: user2, project: project2) } + let!(:label_link) { create(:label_link, label: label, target: issue2) } before do project1.team << [user, :master] @@ -48,6 +50,24 @@ describe IssuesFinder do expect(issues).to eq([issue1]) end + it 'should filter by no milestone id' do + params = { scope: "all", milestone_title: Milestone::None.title, state: 'opened' } + issues = IssuesFinder.new(user, params).execute + expect(issues).to match_array([issue2, issue3]) + end + + it 'should filter by label name' do + params = { scope: "all", label_name: label.title, state: 'opened' } + issues = IssuesFinder.new(user, params).execute + expect(issues).to eq([issue2]) + end + + it 'should filter by no label name' do + params = { scope: "all", label_name: Label::None.title, state: 'opened' } + issues = IssuesFinder.new(user, params).execute + expect(issues).to match_array([issue1, issue3]) + end + it 'should be empty for unauthorized user' do params = { scope: "all", state: 'opened' } issues = IssuesFinder.new(nil, params).execute diff --git a/spec/finders/trending_projects_finder_spec.rb b/spec/finders/trending_projects_finder_spec.rb new file mode 100644 index 00000000000..a49cbfd5160 --- /dev/null +++ b/spec/finders/trending_projects_finder_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe TrendingProjectsFinder do + let(:user) { build(:user) } + + describe '#execute' do + describe 'without an explicit start date' do + subject { described_class.new } + + it 'returns the trending projects' do + relation = double(:ar_relation) + + allow(subject).to receive(:projects_for) + .with(user) + .and_return(relation) + + allow(relation).to receive(:trending) + .with(an_instance_of(ActiveSupport::TimeWithZone)) + end + end + + describe 'with an explicit start date' do + let(:date) { 2.months.ago } + + subject { described_class.new } + + it 'returns the trending projects' do + relation = double(:ar_relation) + + allow(subject).to receive(:projects_for) + .with(user) + .and_return(relation) + + allow(relation).to receive(:trending) + .with(date) + end + end + end +end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 742420f550e..1dfae0fbd3f 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -99,6 +99,15 @@ describe ApplicationHelper do helper.avatar_icon('foo@example.com', 20) end + + describe 'using a User' do + it 'should return an URL for the avatar' do + user = create(:user, avatar: File.open(avatar_file_path)) + + expect(helper.avatar_icon(user).to_s). + to match("/uploads/user/avatar/#{user.id}/banana_sample.gif") + end + end end describe 'gravatar_icon' do diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb index 20ae29e2bd3..762ec25c4f5 100644 --- a/spec/helpers/gitlab_markdown_helper_spec.rb +++ b/spec/helpers/gitlab_markdown_helper_spec.rb @@ -11,12 +11,15 @@ describe GitlabMarkdownHelper do let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } let(:snippet) { create(:project_snippet, project: project) } - # Helper expects a current_user method. - let(:current_user) { user } - before do + # Ensure the generated reference links aren't redacted + project.team << [user, :master] + # Helper expects a @project instance variable - @project = project + helper.instance_variable_set(:@project, project) + + # Stub the `current_user` helper + allow(helper).to receive(:current_user).and_return(user) end describe "#markdown" do @@ -25,23 +28,23 @@ describe GitlabMarkdownHelper do it "should link to the merge request" do expected = namespace_project_merge_request_path(project.namespace, project, merge_request) - expect(markdown(actual)).to match(expected) + expect(helper.markdown(actual)).to match(expected) end it "should link to the commit" do expected = namespace_project_commit_path(project.namespace, project, commit) - expect(markdown(actual)).to match(expected) + expect(helper.markdown(actual)).to match(expected) end it "should link to the issue" do expected = namespace_project_issue_path(project.namespace, project, issue) - expect(markdown(actual)).to match(expected) + expect(helper.markdown(actual)).to match(expected) end end describe "override default project" do let(:actual) { issue.to_reference } - let(:second_project) { create(:project) } + let(:second_project) { create(:project, :public) } let(:second_issue) { create(:issue, project: second_project) } it 'should link to the issue' do @@ -56,7 +59,7 @@ describe GitlabMarkdownHelper do let(:issues) { create_list(:issue, 2, project: project) } it 'should handle references nested in links with all the text' do - actual = link_to_gfm("This should finally fix #{issues[0].to_reference} and #{issues[1].to_reference} for real", commit_path) + actual = helper.link_to_gfm("This should finally fix #{issues[0].to_reference} and #{issues[1].to_reference} for real", commit_path) doc = Nokogiri::HTML.parse(actual) # Make sure we didn't create invalid markup @@ -86,7 +89,7 @@ describe GitlabMarkdownHelper do end it 'should forward HTML options' do - actual = link_to_gfm("Fixed in #{commit.id}", commit_path, class: 'foo') + actual = helper.link_to_gfm("Fixed in #{commit.id}", commit_path, class: 'foo') doc = Nokogiri::HTML.parse(actual) expect(doc.css('a')).to satisfy do |v| @@ -97,13 +100,13 @@ describe GitlabMarkdownHelper do it "escapes HTML passed in as the body" do actual = "This is a <h1>test</h1> - see #{issues[0].to_reference}" - expect(link_to_gfm(actual, commit_path)). + expect(helper.link_to_gfm(actual, commit_path)). to match('<h1>test</h1>') end it 'ignores reference links when they are the entire body' do text = issues[0].to_reference - act = link_to_gfm(text, '/foo') + act = helper.link_to_gfm(text, '/foo') expect(act).to eq %Q(<a href="/foo">#{issues[0].to_reference}</a>) end diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 53e56ebff44..f2efb528aeb 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -70,4 +70,18 @@ describe ProjectsHelper do expect(helper.send(:readme_cache_key)).to eq("#{project.path_with_namespace}-nil-readme") end end + + describe 'link_to_member' do + let(:group) { create(:group) } + let(:project) { create(:empty_project, group: group) } + let(:user) { create(:user) } + + describe 'using the default options' do + it 'returns an HTML link to the user' do + link = helper.link_to_member(project, user) + + expect(link).to match(%r{/u/#{user.username}}) + end + end + end end diff --git a/spec/helpers/runners_helper_spec.rb b/spec/helpers/runners_helper_spec.rb index b3d635a1932..35f91b7decf 100644 --- a/spec/helpers/runners_helper_spec.rb +++ b/spec/helpers/runners_helper_spec.rb @@ -12,7 +12,7 @@ describe RunnersHelper do end it "returns online text" do - runner = FactoryGirl.build(:ci_runner, contacted_at: 1.hour.ago, active: true) + runner = FactoryGirl.build(:ci_runner, contacted_at: 1.second.ago, active: true) expect(runner_status_icon(runner)).to include("Runner is online") end end diff --git a/spec/javascripts/behaviors/quick_submit_spec.js.coffee b/spec/javascripts/behaviors/quick_submit_spec.js.coffee new file mode 100644 index 00000000000..09708c12ed4 --- /dev/null +++ b/spec/javascripts/behaviors/quick_submit_spec.js.coffee @@ -0,0 +1,70 @@ +#= require behaviors/quick_submit + +describe 'Quick Submit behavior', -> + fixture.preload('behaviors/quick_submit.html') + + beforeEach -> + fixture.load('behaviors/quick_submit.html') + + # Prevent a form submit from moving us off the testing page + $('form').submit (e) -> e.preventDefault() + + @spies = { + submit: spyOnEvent('form', 'submit') + } + + it 'does not respond to other keyCodes', -> + $('input').trigger(keydownEvent(keyCode: 32)) + + expect(@spies.submit).not.toHaveBeenTriggered() + + it 'does not respond to Enter alone', -> + $('input').trigger(keydownEvent(ctrlKey: false, metaKey: false)) + + expect(@spies.submit).not.toHaveBeenTriggered() + + it 'does not respond to repeated events', -> + $('input').trigger(keydownEvent(repeat: true)) + + expect(@spies.submit).not.toHaveBeenTriggered() + + it 'disables submit buttons', -> + $('textarea').trigger(keydownEvent()) + + expect($('input[type=submit]')).toBeDisabled() + expect($('button[type=submit]')).toBeDisabled() + + # We cannot stub `navigator.userAgent` for CI's `rake teaspoon` task, so we'll + # only run the tests that apply to the current platform + if navigator.userAgent.match(/Macintosh/) + it 'responds to Meta+Enter', -> + $('input').trigger(keydownEvent()) + + expect(@spies.submit).toHaveBeenTriggered() + + it 'excludes other modifier keys', -> + $('input').trigger(keydownEvent(altKey: true)) + $('input').trigger(keydownEvent(ctrlKey: true)) + $('input').trigger(keydownEvent(shiftKey: true)) + + expect(@spies.submit).not.toHaveBeenTriggered() + else + it 'responds to Ctrl+Enter', -> + $('input').trigger(keydownEvent()) + + expect(@spies.submit).toHaveBeenTriggered() + + it 'excludes other modifier keys', -> + $('input').trigger(keydownEvent(altKey: true)) + $('input').trigger(keydownEvent(metaKey: true)) + $('input').trigger(keydownEvent(shiftKey: true)) + + expect(@spies.submit).not.toHaveBeenTriggered() + + keydownEvent = (options) -> + if navigator.userAgent.match(/Macintosh/) + defaults = { keyCode: 13, metaKey: true } + else + defaults = { keyCode: 13, ctrlKey: true } + + $.Event('keydown', $.extend({}, defaults, options)) diff --git a/spec/javascripts/fixtures/behaviors/quick_submit.html.haml b/spec/javascripts/fixtures/behaviors/quick_submit.html.haml new file mode 100644 index 00000000000..b80a28a33ea --- /dev/null +++ b/spec/javascripts/fixtures/behaviors/quick_submit.html.haml @@ -0,0 +1,6 @@ +%form{ action: '/foo' } + %input.js-quick-submit{ type: 'text' } + %textarea.js-quick-submit + + %input{ type: 'submit'} Submit + %button.btn{ type: 'submit' } Submit diff --git a/spec/javascripts/fixtures/line_highlighter.html.haml b/spec/javascripts/fixtures/line_highlighter.html.haml index da1ebcdb23c..514877340e4 100644 --- a/spec/javascripts/fixtures/line_highlighter.html.haml +++ b/spec/javascripts/fixtures/line_highlighter.html.haml @@ -1,4 +1,4 @@ -#tree-content-holder +#blob-content-holder .file-content .line-numbers - 1.upto(25) do |i| diff --git a/spec/javascripts/line_highlighter_spec.js.coffee b/spec/javascripts/line_highlighter_spec.js.coffee index 57453c716a5..a073f21e7bc 100644 --- a/spec/javascripts/line_highlighter_spec.js.coffee +++ b/spec/javascripts/line_highlighter_spec.js.coffee @@ -39,7 +39,7 @@ describe 'LineHighlighter', -> expect(spy).toHaveBeenPrevented() it 'handles garbage input from the hash', -> - func = -> new LineHighlighter('#tree-content-holder') + func = -> new LineHighlighter('#blob-content-holder') expect(func).not.toThrow() describe '#clickHandler', -> diff --git a/spec/javascripts/spec_helper.coffee b/spec/javascripts/spec_helper.coffee index 47b41dd2c81..90b02a6aec5 100644 --- a/spec/javascripts/spec_helper.coffee +++ b/spec/javascripts/spec_helper.coffee @@ -9,6 +9,7 @@ # require the specific files that are being used in the spec that tests them. #= require jquery +#= require jquery.turbolinks #= require bootstrap #= require underscore diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 49482ac2b12..2260a6f8130 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -17,13 +17,15 @@ module Ci expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1) expect(config_processor.builds_for_stage_and_ref(type, "master").first).to eq({ stage: "test", + stage_idx: 1, except: nil, name: :rspec, only: nil, - script: "pwd\nrspec", - tags: [], + commands: "pwd\nrspec", + tag_list: [], options: {}, - allow_failure: false + allow_failure: false, + when: "on_success" }) end @@ -115,15 +117,17 @@ module Ci expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ except: nil, stage: "test", + stage_idx: 1, name: :rspec, only: nil, - script: "pwd\nrspec", - tags: [], + commands: "pwd\nrspec", + tag_list: [], options: { image: "ruby:2.1", services: ["mysql"] }, - allow_failure: false + allow_failure: false, + when: "on_success" }) end @@ -141,15 +145,17 @@ module Ci expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ except: nil, stage: "test", + stage_idx: 1, name: :rspec, only: nil, - script: "pwd\nrspec", - tags: [], + commands: "pwd\nrspec", + tag_list: [], options: { image: "ruby:2.5", services: ["postgresql"] }, - allow_failure: false + allow_failure: false, + when: "on_success" }) end end @@ -171,6 +177,21 @@ module Ci end end + describe "When" do + %w(on_success on_failure always).each do |when_state| + it "returns #{when_state} when defined" do + config = YAML.dump({ + rspec: { script: "rspec", when: when_state } + }) + + config_processor = GitlabCiYamlProcessor.new(config) + builds = config_processor.builds_for_stage_and_ref("test", "master") + expect(builds.size).to eq(1) + expect(builds.first[:when]).to eq(when_state) + end + end + end + describe "Error handling" do it "indicates that object is invalid" do expect{GitlabCiYamlProcessor.new("invalid_yaml\n!ccdvlf%612334@@@@")}.to raise_error(GitlabCiYamlProcessor::ValidationError) @@ -308,6 +329,13 @@ module Ci GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") end + + it "returns errors if job when is not on_success, on_failure or always" do + config = YAML.dump({ rspec: { script: "test", when: 1 } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: when parameter should be on_success, on_failure or always") + end end end end diff --git a/spec/lib/gitlab/backend/shell_spec.rb b/spec/lib/gitlab/backend/shell_spec.rb index b6d04330599..b60e23454d6 100644 --- a/spec/lib/gitlab/backend/shell_spec.rb +++ b/spec/lib/gitlab/backend/shell_spec.rb @@ -15,4 +15,17 @@ describe Gitlab::Shell do it { is_expected.to respond_to :fork_repository } it { expect(gitlab_shell.url_to_repo('diaspora')).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + "diaspora.git") } + + describe Gitlab::Shell::KeyAdder do + describe '#add_key' do + it 'normalizes space characters in the key' do + io = spy + adder = described_class.new(io) + + adder.add_key('key-42', "sha-rsa foo\tbar\tbaz") + + expect(io).to have_received(:puts).with("key-42\tsha-rsa foo bar baz") + end + end + end end diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index 5d7ff4f6122..21254f778d3 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -140,28 +140,28 @@ describe Gitlab::ClosingIssueExtractor do message = "Closes #{reference} and fix #{reference2}" expect(subject.closed_by_message(message)). - to eq([issue, other_issue]) + to match_array([issue, other_issue]) end it 'fetches comma-separated issues references in single line message' do message = "Closes #{reference}, closes #{reference2}" expect(subject.closed_by_message(message)). - to eq([issue, other_issue]) + to match_array([issue, other_issue]) end it 'fetches comma-separated issues numbers in single line message' do message = "Closes #{reference}, #{reference2} and #{reference3}" expect(subject.closed_by_message(message)). - to eq([issue, other_issue, third_issue]) + to match_array([issue, other_issue, third_issue]) end it 'fetches issues in multi-line message' do message = "Awesome commit (closes #{reference})\nAlso fixes #{reference2}" expect(subject.closed_by_message(message)). - to eq([issue, other_issue]) + to match_array([issue, other_issue]) end it 'fetches issues in hybrid message' do @@ -169,7 +169,7 @@ describe Gitlab::ClosingIssueExtractor do "Also fixing issues #{reference2}, #{reference3} and #4" expect(subject.closed_by_message(message)). - to eq([issue, other_issue, third_issue]) + to match_array([issue, other_issue, third_issue]) end end end diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb new file mode 100644 index 00000000000..7cdebdf209a --- /dev/null +++ b/spec/lib/gitlab/database_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe Gitlab::Database do + # These are just simple smoke tests to check if the methods work (regardless + # of what they may return). + describe '.mysql?' do + subject { described_class.mysql? } + + it { is_expected.to satisfy { |val| val == true || val == false } } + end + + describe '.postgresql?' do + subject { described_class.postgresql? } + + it { is_expected.to satisfy { |val| val == true || val == false } } + end +end diff --git a/spec/lib/gitlab/email/attachment_uploader_spec.rb b/spec/lib/gitlab/email/attachment_uploader_spec.rb index e8208e15e29..8fb432367b6 100644 --- a/spec/lib/gitlab/email/attachment_uploader_spec.rb +++ b/spec/lib/gitlab/email/attachment_uploader_spec.rb @@ -13,7 +13,6 @@ describe Gitlab::Email::AttachmentUploader do expect(link).not_to be_nil expect(link[:is_image]).to be_truthy expect(link[:alt]).to eq("bricks") - expect(link[:url]).to include("/#{project.path_with_namespace}") expect(link[:url]).to include("bricks.png") end end diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb index fd2e5f6d0e1..b5b56a34952 100644 --- a/spec/lib/gitlab/ldap/user_spec.rb +++ b/spec/lib/gitlab/ldap/user_spec.rb @@ -13,6 +13,17 @@ describe Gitlab::LDAP::User do let(:auth_hash) do OmniAuth::AuthHash.new(uid: 'my-uid', provider: 'ldapmain', info: info) end + let(:ldap_user_upper_case) { Gitlab::LDAP::User.new(auth_hash_upper_case) } + let(:info_upper_case) do + { + name: 'John', + email: 'John@Example.com', # Email address has upper case chars + nickname: 'john' + } + end + let(:auth_hash_upper_case) do + OmniAuth::AuthHash.new(uid: 'my-uid', provider: 'ldapmain', info: info_upper_case) + end describe :changed? do it "marks existing ldap user as changed" do @@ -57,6 +68,16 @@ describe Gitlab::LDAP::User do expect(existing_user.id).to eql ldap_user.gl_user.id end + it 'connects to existing ldap user if the extern_uid changes and email address has upper case characters' do + existing_user = create(:omniauth_user, email: 'john@example.com', extern_uid: 'old-uid', provider: 'ldapmain') + expect{ ldap_user_upper_case.save }.not_to change{ User.count } + + existing_user.reload + expect(existing_user.ldap_identity.extern_uid).to eql 'my-uid' + expect(existing_user.ldap_identity.provider).to eql 'ldapmain' + expect(existing_user.id).to eql ldap_user.gl_user.id + end + it 'maintains an identity per provider' do existing_user = create(:omniauth_user, email: 'john@example.com', provider: 'twitter') expect(existing_user.identities.count).to eql(1) diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb index 3c6c84a0416..e5b8d723fe5 100644 --- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb @@ -4,7 +4,7 @@ module Gitlab::Markdown describe CommitRangeReferenceFilter do include FilterSpecHelper - let(:project) { create(:project) } + let(:project) { create(:project, :public) } let(:commit1) { project.commit } let(:commit2) { project.commit("HEAD~2") } @@ -75,12 +75,20 @@ module Gitlab::Markdown expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit_range' end - it 'includes a data-project-id attribute' do + it 'includes a data-project attribute' do doc = filter("See #{reference}") link = doc.css('a').first - expect(link).to have_attribute('data-project-id') - expect(link.attr('data-project-id')).to eq project.id.to_s + expect(link).to have_attribute('data-project') + expect(link.attr('data-project')).to eq project.id.to_s + end + + it 'includes a data-commit-range attribute' do + doc = filter("See #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-commit-range') + expect(link.attr('data-commit-range')).to eq range.to_reference end it 'supports an :only_path option' do @@ -92,59 +100,45 @@ module Gitlab::Markdown end it 'adds to the results hash' do - result = pipeline_result("See #{reference}") + result = reference_pipeline_result("See #{reference}") expect(result[:references][:commit_range]).not_to be_empty end end context 'cross-project reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } - let(:project2) { create(:project, namespace: namespace) } + let(:project2) { create(:project, :public, namespace: namespace) } let(:reference) { range.to_reference(project) } before do range.project = project2 end - context 'when user can access reference' do - before { allow_cross_reference! } - - it 'links to a valid reference' do - doc = filter("See #{reference}") - - expect(doc.css('a').first.attr('href')). - to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param) - end - - it 'links with adjacent text' do - doc = filter("Fixed (#{reference}.)") - - exp = Regexp.escape("#{project2.to_reference}@#{range.to_s}") - expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/) - end + it 'links to a valid reference' do + doc = filter("See #{reference}") - it 'ignores invalid commit IDs on the referenced project' do - exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}" - expect(filter(act).to_html).to eq exp + expect(doc.css('a').first.attr('href')). + to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param) + end - exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}" - expect(filter(act).to_html).to eq exp - end + it 'links with adjacent text' do + doc = filter("Fixed (#{reference}.)") - it 'adds to the results hash' do - result = pipeline_result("See #{reference}") - expect(result[:references][:commit_range]).not_to be_empty - end + exp = Regexp.escape("#{project2.to_reference}@#{range.to_s}") + expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/) end - context 'when user cannot access reference' do - before { disallow_cross_reference! } + it 'ignores invalid commit IDs on the referenced project' do + exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}" + expect(filter(act).to_html).to eq exp - it 'ignores valid references' do - exp = act = "See #{reference}" + exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}" + expect(filter(act).to_html).to eq exp + end - expect(filter(act).to_html).to eq exp - end + it 'adds to the results hash' do + result = reference_pipeline_result("See #{reference}") + expect(result[:references][:commit_range]).not_to be_empty end end end diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb index 9ed438252b3..d080efbf3d4 100644 --- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb @@ -4,7 +4,7 @@ module Gitlab::Markdown describe CommitReferenceFilter do include FilterSpecHelper - let(:project) { create(:project) } + let(:project) { create(:project, :public) } let(:commit) { project.commit } it 'requires project context' do @@ -71,12 +71,20 @@ module Gitlab::Markdown expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit' end - it 'includes a data-project-id attribute' do + it 'includes a data-project attribute' do doc = filter("See #{reference}") link = doc.css('a').first - expect(link).to have_attribute('data-project-id') - expect(link.attr('data-project-id')).to eq project.id.to_s + expect(link).to have_attribute('data-project') + expect(link.attr('data-project')).to eq project.id.to_s + end + + it 'includes a data-commit attribute' do + doc = filter("See #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-commit') + expect(link.attr('data-commit')).to eq commit.id end it 'supports an :only_path context' do @@ -88,53 +96,39 @@ module Gitlab::Markdown end it 'adds to the results hash' do - result = pipeline_result("See #{reference}") + result = reference_pipeline_result("See #{reference}") expect(result[:references][:commit]).not_to be_empty end end context 'cross-project reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } - let(:project2) { create(:project, namespace: namespace) } + let(:project2) { create(:project, :public, namespace: namespace) } let(:commit) { project2.commit } let(:reference) { commit.to_reference(project) } - context 'when user can access reference' do - before { allow_cross_reference! } - - it 'links to a valid reference' do - doc = filter("See #{reference}") - - expect(doc.css('a').first.attr('href')). - to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id) - end - - it 'links with adjacent text' do - doc = filter("Fixed (#{reference}.)") + it 'links to a valid reference' do + doc = filter("See #{reference}") - exp = Regexp.escape(project2.to_reference) - expect(doc.to_html).to match(/\(<a.+>#{exp}@#{commit.short_id}<\/a>\.\)/) - end + expect(doc.css('a').first.attr('href')). + to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id) + end - it 'ignores invalid commit IDs on the referenced project' do - exp = act = "Committed #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp - end + it 'links with adjacent text' do + doc = filter("Fixed (#{reference}.)") - it 'adds to the results hash' do - result = pipeline_result("See #{reference}") - expect(result[:references][:commit]).not_to be_empty - end + exp = Regexp.escape(project2.to_reference) + expect(doc.to_html).to match(/\(<a.+>#{exp}@#{commit.short_id}<\/a>\.\)/) end - context 'when user cannot access reference' do - before { disallow_cross_reference! } - - it 'ignores valid references' do - exp = act = "See #{reference}" + it 'ignores invalid commit IDs on the referenced project' do + exp = act = "Committed #{invalidate_reference(reference)}" + expect(filter(act).to_html).to eq exp + end - expect(filter(act).to_html).to eq exp - end + it 'adds to the results hash' do + result = reference_pipeline_result("See #{reference}") + expect(result[:references][:commit]).not_to be_empty end end end diff --git a/spec/lib/gitlab/markdown/cross_project_reference_spec.rb b/spec/lib/gitlab/markdown/cross_project_reference_spec.rb index 4698d6138c2..8d4f9e403a6 100644 --- a/spec/lib/gitlab/markdown/cross_project_reference_spec.rb +++ b/spec/lib/gitlab/markdown/cross_project_reference_spec.rb @@ -2,20 +2,16 @@ require 'spec_helper' module Gitlab::Markdown describe CrossProjectReference do - # context in the html-pipeline sense, not in the rspec sense - let(:context) do - { - current_user: double('user'), - project: double('project') - } - end - include described_class describe '#project_from_ref' do context 'when no project was referenced' do it 'returns the project from context' do - expect(project_from_ref(nil)).to eq context[:project] + project = double + + allow(self).to receive(:context).and_return({ project: project }) + + expect(project_from_ref(nil)).to eq project end end @@ -26,29 +22,13 @@ module Gitlab::Markdown end context 'when referenced project exists' do - let(:project2) { double('referenced project') } + it 'returns the referenced project' do + project2 = double('referenced project') - before do expect(Project).to receive(:find_with_namespace). with('cross/reference').and_return(project2) - end - - context 'and the user has permission to read it' do - it 'returns the referenced project' do - expect(self).to receive(:user_can_reference_project?). - with(project2).and_return(true) - - expect(project_from_ref('cross/reference')).to eq project2 - end - end - - context 'and the user does not have permission to read it' do - it 'returns nil' do - expect(self).to receive(:user_can_reference_project?). - with(project2).and_return(false) - expect(project_from_ref('cross/reference')).to be_nil - end + expect(project_from_ref('cross/reference')).to eq project2 end end end diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb index 1dd54f58588..94c80ae6611 100644 --- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb @@ -8,7 +8,7 @@ module Gitlab::Markdown IssuesHelper end - let(:project) { create(:empty_project) } + let(:project) { create(:empty_project, :public) } let(:issue) { create(:issue, project: project) } it 'requires project context' do @@ -68,12 +68,20 @@ module Gitlab::Markdown expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-issue' end - it 'includes a data-project-id attribute' do + it 'includes a data-project attribute' do doc = filter("Issue #{reference}") link = doc.css('a').first - expect(link).to have_attribute('data-project-id') - expect(link.attr('data-project-id')).to eq project.id.to_s + expect(link).to have_attribute('data-project') + expect(link.attr('data-project')).to eq project.id.to_s + end + + it 'includes a data-issue attribute' do + doc = filter("See #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-issue') + expect(link.attr('data-issue')).to eq issue.id.to_s end it 'supports an :only_path context' do @@ -85,60 +93,46 @@ module Gitlab::Markdown end it 'adds to the results hash' do - result = pipeline_result("Fixed #{reference}") + result = reference_pipeline_result("Fixed #{reference}") expect(result[:references][:issue]).to eq [issue] end end context 'cross-project reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } - let(:project2) { create(:empty_project, namespace: namespace) } + let(:project2) { create(:empty_project, :public, namespace: namespace) } let(:issue) { create(:issue, project: project2) } let(:reference) { issue.to_reference(project) } - context 'when user can access reference' do - before { allow_cross_reference! } - - it 'ignores valid references when cross-reference project uses external tracker' do - expect_any_instance_of(Project).to receive(:get_issue). - with(issue.iid).and_return(nil) - - exp = act = "Issue #{reference}" - expect(filter(act).to_html).to eq exp - end + it 'ignores valid references when cross-reference project uses external tracker' do + expect_any_instance_of(Project).to receive(:get_issue). + with(issue.iid).and_return(nil) - it 'links to a valid reference' do - doc = filter("See #{reference}") - - expect(doc.css('a').first.attr('href')). - to eq helper.url_for_issue(issue.iid, project2) - end - - it 'links with adjacent text' do - doc = filter("Fixed (#{reference}.)") - expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/) - end + exp = act = "Issue #{reference}" + expect(filter(act).to_html).to eq exp + end - it 'ignores invalid issue IDs on the referenced project' do - exp = act = "Fixed #{invalidate_reference(reference)}" + it 'links to a valid reference' do + doc = filter("See #{reference}") - expect(filter(act).to_html).to eq exp - end + expect(doc.css('a').first.attr('href')). + to eq helper.url_for_issue(issue.iid, project2) + end - it 'adds to the results hash' do - result = pipeline_result("Fixed #{reference}") - expect(result[:references][:issue]).to eq [issue] - end + it 'links with adjacent text' do + doc = filter("Fixed (#{reference}.)") + expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/) end - context 'when user cannot access reference' do - before { disallow_cross_reference! } + it 'ignores invalid issue IDs on the referenced project' do + exp = act = "Fixed #{invalidate_reference(reference)}" - it 'ignores valid references' do - exp = act = "See #{reference}" + expect(filter(act).to_html).to eq exp + end - expect(filter(act).to_html).to eq exp - end + it 'adds to the results hash' do + result = reference_pipeline_result("Fixed #{reference}") + expect(result[:references][:issue]).to eq [issue] end end end diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb index e32089de376..fc21b65a843 100644 --- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb @@ -5,7 +5,7 @@ module Gitlab::Markdown describe LabelReferenceFilter do include FilterSpecHelper - let(:project) { create(:empty_project) } + let(:project) { create(:empty_project, :public) } let(:label) { create(:label, project: project) } let(:reference) { label.to_reference } @@ -25,12 +25,20 @@ module Gitlab::Markdown expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-label' end - it 'includes a data-project-id attribute' do + it 'includes a data-project attribute' do doc = filter("Label #{reference}") link = doc.css('a').first - expect(link).to have_attribute('data-project-id') - expect(link.attr('data-project-id')).to eq project.id.to_s + expect(link).to have_attribute('data-project') + expect(link.attr('data-project')).to eq project.id.to_s + end + + it 'includes a data-label attribute' do + doc = filter("See #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-label') + expect(link.attr('data-label')).to eq label.id.to_s end it 'supports an :only_path context' do @@ -42,7 +50,7 @@ module Gitlab::Markdown end it 'adds to the results hash' do - result = pipeline_result("Label #{reference}") + result = reference_pipeline_result("Label #{reference}") expect(result[:references][:label]).to eq [label] end diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb index 66616b93368..3ef6cdfff33 100644 --- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb @@ -4,7 +4,7 @@ module Gitlab::Markdown describe MergeRequestReferenceFilter do include FilterSpecHelper - let(:project) { create(:project) } + let(:project) { create(:project, :public) } let(:merge) { create(:merge_request, source_project: project) } it 'requires project context' do @@ -56,12 +56,20 @@ module Gitlab::Markdown expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-merge_request' end - it 'includes a data-project-id attribute' do + it 'includes a data-project attribute' do doc = filter("Merge #{reference}") link = doc.css('a').first - expect(link).to have_attribute('data-project-id') - expect(link.attr('data-project-id')).to eq project.id.to_s + expect(link).to have_attribute('data-project') + expect(link.attr('data-project')).to eq project.id.to_s + end + + it 'includes a data-merge-request attribute' do + doc = filter("See #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-merge-request') + expect(link.attr('data-merge-request')).to eq merge.id.to_s end it 'supports an :only_path context' do @@ -73,53 +81,39 @@ module Gitlab::Markdown end it 'adds to the results hash' do - result = pipeline_result("Merge #{reference}") + result = reference_pipeline_result("Merge #{reference}") expect(result[:references][:merge_request]).to eq [merge] end end context 'cross-project reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } - let(:project2) { create(:project, namespace: namespace) } + let(:project2) { create(:project, :public, namespace: namespace) } let(:merge) { create(:merge_request, source_project: project2) } let(:reference) { merge.to_reference(project) } - context 'when user can access reference' do - before { allow_cross_reference! } - - it 'links to a valid reference' do - doc = filter("See #{reference}") - - expect(doc.css('a').first.attr('href')). - to eq urls.namespace_project_merge_request_url(project2.namespace, - project, merge) - end - - it 'links with adjacent text' do - doc = filter("Merge (#{reference}.)") - expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/) - end - - it 'ignores invalid merge IDs on the referenced project' do - exp = act = "Merge #{invalidate_reference(reference)}" + it 'links to a valid reference' do + doc = filter("See #{reference}") - expect(filter(act).to_html).to eq exp - end + expect(doc.css('a').first.attr('href')). + to eq urls.namespace_project_merge_request_url(project2.namespace, + project, merge) + end - it 'adds to the results hash' do - result = pipeline_result("Merge #{reference}") - expect(result[:references][:merge_request]).to eq [merge] - end + it 'links with adjacent text' do + doc = filter("Merge (#{reference}.)") + expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/) end - context 'when user cannot access reference' do - before { disallow_cross_reference! } + it 'ignores invalid merge IDs on the referenced project' do + exp = act = "Merge #{invalidate_reference(reference)}" - it 'ignores valid references' do - exp = act = "See #{reference}" + expect(filter(act).to_html).to eq exp + end - expect(filter(act).to_html).to eq exp - end + it 'adds to the results hash' do + result = reference_pipeline_result("Merge #{reference}") + expect(result[:references][:merge_request]).to eq [merge] end end end diff --git a/spec/lib/gitlab/markdown/redactor_filter_spec.rb b/spec/lib/gitlab/markdown/redactor_filter_spec.rb new file mode 100644 index 00000000000..eea3f1cf370 --- /dev/null +++ b/spec/lib/gitlab/markdown/redactor_filter_spec.rb @@ -0,0 +1,91 @@ +require 'spec_helper' + +module Gitlab::Markdown + describe RedactorFilter do + include ActionView::Helpers::UrlHelper + include FilterSpecHelper + + it 'ignores non-GFM links' do + html = %(See <a href="https://google.com/">Google</a>) + doc = filter(html, current_user: double) + + expect(doc.css('a').length).to eq 1 + end + + def reference_link(data) + link_to('text', '', class: 'gfm', data: data) + end + + context 'with data-project' do + it 'removes unpermitted Project references' do + user = create(:user) + project = create(:empty_project) + + link = reference_link(project: project.id, reference_filter: Gitlab::Markdown::ReferenceFilter.name) + doc = filter(link, current_user: user) + + expect(doc.css('a').length).to eq 0 + end + + it 'allows permitted Project references' do + user = create(:user) + project = create(:empty_project) + project.team << [user, :master] + + link = reference_link(project: project.id, reference_filter: Gitlab::Markdown::ReferenceFilter.name) + doc = filter(link, current_user: user) + + expect(doc.css('a').length).to eq 1 + end + + it 'handles invalid Project references' do + link = reference_link(project: 12345, reference_filter: Gitlab::Markdown::ReferenceFilter.name) + + expect { filter(link) }.not_to raise_error + end + end + + context "for user references" do + + context 'with data-group' do + it 'removes unpermitted Group references' do + user = create(:user) + group = create(:group) + + link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + doc = filter(link, current_user: user) + + expect(doc.css('a').length).to eq 0 + end + + it 'allows permitted Group references' do + user = create(:user) + group = create(:group) + group.add_developer(user) + + link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + doc = filter(link, current_user: user) + + expect(doc.css('a').length).to eq 1 + end + + it 'handles invalid Group references' do + link = reference_link(group: 12345, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + + expect { filter(link) }.not_to raise_error + end + end + + context 'with data-user' do + it 'allows any User reference' do + user = create(:user) + + link = reference_link(user: user.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + doc = filter(link) + + expect(doc.css('a').length).to eq 1 + end + end + end + end +end diff --git a/spec/lib/gitlab/markdown/reference_gatherer_filter_spec.rb b/spec/lib/gitlab/markdown/reference_gatherer_filter_spec.rb new file mode 100644 index 00000000000..4fa473ad191 --- /dev/null +++ b/spec/lib/gitlab/markdown/reference_gatherer_filter_spec.rb @@ -0,0 +1,89 @@ +require 'spec_helper' + +module Gitlab::Markdown + describe ReferenceGathererFilter do + include ActionView::Helpers::UrlHelper + include FilterSpecHelper + + def reference_link(data) + link_to('text', '', class: 'gfm', data: data) + end + + context "for issue references" do + + context 'with data-project' do + it 'removes unpermitted Project references' do + user = create(:user) + project = create(:empty_project) + issue = create(:issue, project: project) + + link = reference_link(project: project.id, issue: issue.id, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name) + result = pipeline_result(link, current_user: user) + + expect(result[:references][:issue]).to be_empty + end + + it 'allows permitted Project references' do + user = create(:user) + project = create(:empty_project) + issue = create(:issue, project: project) + project.team << [user, :master] + + link = reference_link(project: project.id, issue: issue.id, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name) + result = pipeline_result(link, current_user: user) + + expect(result[:references][:issue]).to eq([issue]) + end + + it 'handles invalid Project references' do + link = reference_link(project: 12345, issue: 12345, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name) + + expect { pipeline_result(link) }.not_to raise_error + end + end + end + + context "for user references" do + + context 'with data-group' do + it 'removes unpermitted Group references' do + user = create(:user) + group = create(:group) + + link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + result = pipeline_result(link, current_user: user) + + expect(result[:references][:user]).to be_empty + end + + it 'allows permitted Group references' do + user = create(:user) + group = create(:group) + group.add_developer(user) + + link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + result = pipeline_result(link, current_user: user) + + expect(result[:references][:user]).to eq([user]) + end + + it 'handles invalid Group references' do + link = reference_link(group: 12345, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + + expect { pipeline_result(link) }.not_to raise_error + end + end + + context 'with data-user' do + it 'allows any User reference' do + user = create(:user) + + link = reference_link(user: user.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + result = pipeline_result(link) + + expect(result[:references][:user]).to eq([user]) + end + end + end + end +end diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb index fd3f0d20fad..9d9652dba46 100644 --- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb @@ -4,7 +4,7 @@ module Gitlab::Markdown describe SnippetReferenceFilter do include FilterSpecHelper - let(:project) { create(:empty_project) } + let(:project) { create(:empty_project, :public) } let(:snippet) { create(:project_snippet, project: project) } let(:reference) { snippet.to_reference } @@ -55,12 +55,20 @@ module Gitlab::Markdown expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-snippet' end - it 'includes a data-project-id attribute' do + it 'includes a data-project attribute' do doc = filter("Snippet #{reference}") link = doc.css('a').first - expect(link).to have_attribute('data-project-id') - expect(link.attr('data-project-id')).to eq project.id.to_s + expect(link).to have_attribute('data-project') + expect(link.attr('data-project')).to eq project.id.to_s + end + + it 'includes a data-snippet attribute' do + doc = filter("See #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-snippet') + expect(link.attr('data-snippet')).to eq snippet.id.to_s end it 'supports an :only_path context' do @@ -72,52 +80,38 @@ module Gitlab::Markdown end it 'adds to the results hash' do - result = pipeline_result("Snippet #{reference}") + result = reference_pipeline_result("Snippet #{reference}") expect(result[:references][:snippet]).to eq [snippet] end end context 'cross-project reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } - let(:project2) { create(:empty_project, namespace: namespace) } + let(:project2) { create(:empty_project, :public, namespace: namespace) } let(:snippet) { create(:project_snippet, project: project2) } let(:reference) { snippet.to_reference(project) } - context 'when user can access reference' do - before { allow_cross_reference! } - - it 'links to a valid reference' do - doc = filter("See #{reference}") - - expect(doc.css('a').first.attr('href')). - to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet) - end - - it 'links with adjacent text' do - doc = filter("See (#{reference}.)") - expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/) - end - - it 'ignores invalid snippet IDs on the referenced project' do - exp = act = "See #{invalidate_reference(reference)}" + it 'links to a valid reference' do + doc = filter("See #{reference}") - expect(filter(act).to_html).to eq exp - end + expect(doc.css('a').first.attr('href')). + to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet) + end - it 'adds to the results hash' do - result = pipeline_result("Snippet #{reference}") - expect(result[:references][:snippet]).to eq [snippet] - end + it 'links with adjacent text' do + doc = filter("See (#{reference}.)") + expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/) end - context 'when user cannot access reference' do - before { disallow_cross_reference! } + it 'ignores invalid snippet IDs on the referenced project' do + exp = act = "See #{invalidate_reference(reference)}" - it 'ignores valid references' do - exp = act = "See #{reference}" + expect(filter(act).to_html).to eq exp + end - expect(filter(act).to_html).to eq exp - end + it 'adds to the results hash' do + result = reference_pipeline_result("Snippet #{reference}") + expect(result[:references][:snippet]).to eq [snippet] end end end diff --git a/spec/lib/gitlab/markdown/upload_link_filter_spec.rb b/spec/lib/gitlab/markdown/upload_link_filter_spec.rb new file mode 100644 index 00000000000..9ae45a6f559 --- /dev/null +++ b/spec/lib/gitlab/markdown/upload_link_filter_spec.rb @@ -0,0 +1,75 @@ +# encoding: UTF-8 + +require 'spec_helper' + +module Gitlab::Markdown + describe UploadLinkFilter do + def filter(doc, contexts = {}) + contexts.reverse_merge!({ + project: project + }) + + described_class.call(doc, contexts) + end + + def image(path) + %(<img src="#{path}" />) + end + + def link(path) + %(<a href="#{path}">#{path}</a>) + end + + let(:project) { create(:project) } + + shared_examples :preserve_unchanged do + it 'does not modify any relative URL in anchor' do + doc = filter(link('README.md')) + expect(doc.at_css('a')['href']).to eq 'README.md' + end + + it 'does not modify any relative URL in image' do + doc = filter(image('files/images/logo-black.png')) + expect(doc.at_css('img')['src']).to eq 'files/images/logo-black.png' + end + end + + it 'does not raise an exception on invalid URIs' do + act = link("://foo") + expect { filter(act) }.not_to raise_error + end + + context 'with a valid repository' do + it 'rebuilds relative URL for a link' do + doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) + expect(doc.at_css('a')['href']). + to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + end + + it 'rebuilds relative URL for an image' do + doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) + expect(doc.at_css('a')['href']). + to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + end + + it 'does not modify absolute URL' do + doc = filter(link('http://example.com')) + expect(doc.at_css('a')['href']).to eq 'http://example.com' + end + + it 'supports Unicode filenames' do + path = '/uploads/한글.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 "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/%ED%95%9C%EA%B8%80.png" + end + end + end +end diff --git a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb index b2155fab59b..d9e0d7c42db 100644 --- a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb @@ -4,7 +4,7 @@ module Gitlab::Markdown describe UserReferenceFilter do include FilterSpecHelper - let(:project) { create(:empty_project) } + let(:project) { create(:empty_project, :public) } let(:user) { create(:user) } let(:reference) { user.to_reference } @@ -39,7 +39,7 @@ module Gitlab::Markdown end it 'adds to the results hash' do - result = pipeline_result("Hey #{reference}") + result = reference_pipeline_result("Hey #{reference}") expect(result[:references][:user]).to eq [project.creator] end end @@ -64,59 +64,40 @@ module Gitlab::Markdown expect(doc.css('a').length).to eq 1 end - it 'includes a data-user-id attribute' do + it 'includes a data-user attribute' do doc = filter("Hey #{reference}") link = doc.css('a').first - expect(link).to have_attribute('data-user-id') - expect(link.attr('data-user-id')).to eq user.namespace.owner_id.to_s + expect(link).to have_attribute('data-user') + expect(link.attr('data-user')).to eq user.namespace.owner_id.to_s end it 'adds to the results hash' do - result = pipeline_result("Hey #{reference}") + result = reference_pipeline_result("Hey #{reference}") expect(result[:references][:user]).to eq [user] end end context 'mentioning a group' do let(:group) { create(:group) } - let(:user) { create(:user) } let(:reference) { group.to_reference } - context 'that the current user can read' do - before do - group.add_developer(user) - end - - it 'links to the Group' do - doc = filter("Hey #{reference}", current_user: user) - expect(doc.css('a').first.attr('href')).to eq urls.group_url(group) - end - - it 'includes a data-group-id attribute' do - doc = filter("Hey #{reference}", current_user: user) - link = doc.css('a').first + it 'links to the Group' do + doc = filter("Hey #{reference}") + expect(doc.css('a').first.attr('href')).to eq urls.group_url(group) + end - expect(link).to have_attribute('data-group-id') - expect(link.attr('data-group-id')).to eq group.id.to_s - end + it 'includes a data-group attribute' do + doc = filter("Hey #{reference}") + link = doc.css('a').first - it 'adds to the results hash' do - result = pipeline_result("Hey #{reference}", current_user: user) - expect(result[:references][:user]).to eq group.users - end + expect(link).to have_attribute('data-group') + expect(link.attr('data-group')).to eq group.id.to_s end - context 'that the current user cannot read' do - it 'ignores references to the Group' do - doc = filter("Hey #{reference}", current_user: user) - expect(doc.to_html).to eq "Hey #{reference}" - end - - it 'does not add to the results hash' do - result = pipeline_result("Hey #{reference}", current_user: user) - expect(result[:references][:user]).to eq [] - end + it 'adds to the results hash' do + result = reference_pipeline_result("Hey #{reference}") + expect(result[:references][:user]).to eq group.users end end diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb index 088e34f050c..ad84d2274e8 100644 --- a/spec/lib/gitlab/reference_extractor_spec.rb +++ b/spec/lib/gitlab/reference_extractor_spec.rb @@ -13,7 +13,7 @@ describe Gitlab::ReferenceExtractor do project.team << [@u_bar, :guest] subject.analyze('@foo, @baduser, @bar, and @offteam') - expect(subject.users).to eq([@u_foo, @u_bar, @u_offteam]) + expect(subject.users).to match_array([@u_foo, @u_bar, @u_offteam]) end it 'ignores user mentions inside specific elements' do @@ -37,7 +37,7 @@ describe Gitlab::ReferenceExtractor do > @offteam }) - expect(subject.users).to eq([]) + expect(subject.users).to match_array([]) end it 'accesses valid issue objects' do @@ -45,7 +45,7 @@ describe Gitlab::ReferenceExtractor do @i1 = create(:issue, project: project) subject.analyze("#{@i0.to_reference}, #{@i1.to_reference}, and #{Issue.reference_prefix}999.") - expect(subject.issues).to eq([@i0, @i1]) + expect(subject.issues).to match_array([@i0, @i1]) end it 'accesses valid merge requests' do @@ -53,7 +53,7 @@ describe Gitlab::ReferenceExtractor do @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]) + expect(subject.merge_requests).to match_array([@m1, @m0]) end it 'accesses valid labels' do @@ -62,7 +62,7 @@ describe Gitlab::ReferenceExtractor do @l2 = create(:label) subject.analyze("~#{@l0.id}, ~999, ~#{@l2.id}, ~#{@l1.id}") - expect(subject.labels).to eq([@l0, @l1]) + expect(subject.labels).to match_array([@l0, @l1]) end it 'accesses valid snippets' do @@ -71,7 +71,7 @@ describe Gitlab::ReferenceExtractor do @s2 = create(:project_snippet) subject.analyze("$#{@s0.id}, $999, $#{@s2.id}, $#{@s1.id}") - expect(subject.snippets).to eq([@s0, @s1]) + expect(subject.snippets).to match_array([@s0, @s1]) end it 'accesses valid commits' do @@ -109,7 +109,7 @@ describe Gitlab::ReferenceExtractor do subject.analyze("this refers issue #{issue.to_reference(project)}") extracted = subject.issues expect(extracted.size).to eq(1) - expect(extracted).to eq([issue]) + expect(extracted).to match_array([issue]) end end end diff --git a/spec/lib/gitlab/uploads_transfer_spec.rb b/spec/lib/gitlab/uploads_transfer_spec.rb new file mode 100644 index 00000000000..260364a513e --- /dev/null +++ b/spec/lib/gitlab/uploads_transfer_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe Gitlab::UploadsTransfer do + before do + @root_dir = File.join(Rails.root, "public", "uploads") + @upload_transfer = Gitlab::UploadsTransfer.new + + @project_path_was = "test_project_was" + @project_path = "test_project" + @namespace_path_was = "test_namespace_was" + @namespace_path = "test_namespace" + end + + after do + FileUtils.rm_rf([ + File.join(@root_dir, @namespace_path), + File.join(@root_dir, @namespace_path_was) + ]) + end + + describe '#move_project' do + it "moves project upload to another namespace" do + FileUtils.mkdir_p(File.join(@root_dir, @namespace_path_was, @project_path)) + @upload_transfer.move_project(@project_path, @namespace_path_was, @namespace_path) + + expected_path = File.join(@root_dir, @namespace_path, @project_path) + expect(Dir.exist?(expected_path)).to be_truthy + end + end + + describe '#rename_project' do + it "renames project" do + FileUtils.mkdir_p(File.join(@root_dir, @namespace_path, @project_path_was)) + @upload_transfer.rename_project(@project_path_was, @project_path, @namespace_path) + + expected_path = File.join(@root_dir, @namespace_path, @project_path) + expect(Dir.exist?(expected_path)).to be_truthy + end + end + + describe '#rename_namespace' do + it "renames namespace" do + FileUtils.mkdir_p(File.join(@root_dir, @namespace_path_was, @project_path)) + @upload_transfer.rename_namespace(@namespace_path_was, @namespace_path) + + expected_path = File.join(@root_dir, @namespace_path, @project_path) + expect(Dir.exist?(expected_path)).to be_truthy + end + end +end diff --git a/spec/models/ci/build_spec.rb b/spec/models/build_spec.rb index ca070a14975..7f5abb83ac2 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/build_spec.rb @@ -31,13 +31,8 @@ describe Ci::Build do let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } let(:build) { FactoryGirl.create :ci_build, commit: commit } - it { is_expected.to belong_to(:commit) } - it { is_expected.to validate_presence_of :status } + it { is_expected.to validate_presence_of :ref } - it { is_expected.to respond_to :success? } - it { is_expected.to respond_to :failed? } - it { is_expected.to respond_to :running? } - it { is_expected.to respond_to :pending? } it { is_expected.to respond_to :trace_html } describe :first_pending do @@ -64,72 +59,6 @@ describe Ci::Build do end end - describe :started? do - subject { build.started? } - - context 'without started_at' do - before { build.started_at = nil } - - it { is_expected.to be_falsey } - end - - %w(running success failed).each do |status| - context "if build status is #{status}" do - before { build.status = status } - - it { is_expected.to be_truthy } - end - end - - %w(pending canceled).each do |status| - context "if build status is #{status}" do - before { build.status = status } - - it { is_expected.to be_falsey } - end - end - end - - describe :active? do - subject { build.active? } - - %w(pending running).each do |state| - context "if build.status is #{state}" do - before { build.status = state } - - it { is_expected.to be_truthy } - end - end - - %w(success failed canceled).each do |state| - context "if build.status is #{state}" do - before { build.status = state } - - it { is_expected.to be_falsey } - end - end - end - - describe :complete? do - subject { build.complete? } - - %w(success failed canceled).each do |state| - context "if build.status is #{state}" do - before { build.status = state } - - it { is_expected.to be_truthy } - end - end - - %w(pending running).each do |state| - context "if build.status is #{state}" do - before { build.status = state } - - it { is_expected.to be_falsey } - end - end - end - describe :ignored? do subject { build.ignored? } @@ -197,31 +126,6 @@ describe Ci::Build do it { is_expected.to eq(commit.project.timeout) } end - describe :duration do - subject { build.duration } - - it { is_expected.to eq(120.0) } - - context 'if the building process has not started yet' do - before do - build.started_at = nil - build.finished_at = nil - end - - it { is_expected.to be_nil } - end - - context 'if the building process has started' do - before do - build.started_at = Time.now - 1.minute - build.finished_at = nil - end - - it { is_expected.to be_a(Float) } - it { is_expected.to be > 0.0 } - end - end - describe :options do let(:options) do { @@ -236,30 +140,6 @@ describe Ci::Build do it { is_expected.to eq(options) } end - describe :ref do - subject { build.ref } - - it { is_expected.to eq(commit.ref) } - end - - describe :sha do - subject { build.sha } - - it { is_expected.to eq(commit.sha) } - end - - describe :short_sha do - subject { build.short_sha } - - it { is_expected.to eq(commit.short_sha) } - end - - describe :before_sha do - subject { build.before_sha } - - it { is_expected.to eq(commit.before_sha) } - end - describe :allow_git_fetch do subject { build.allow_git_fetch } @@ -320,13 +200,34 @@ describe Ci::Build do context 'returns variables' do subject { build.variables } - let(:variables) do + let(:predefined_variables) do + [ + { key: :CI_BUILD_NAME, value: 'test', public: true }, + { key: :CI_BUILD_STAGE, value: 'stage', public: true }, + ] + end + + let(:yaml_variables) do [ { key: :DB_NAME, value: 'postgres', public: true } ] end - it { is_expected.to eq(variables) } + before { build.update_attributes(stage: 'stage') } + + it { is_expected.to eq(predefined_variables + yaml_variables) } + + context 'for tag' do + let(:tag_variable) do + [ + { key: :CI_BUILD_TAG, value: 'master', public: true } + ] + end + + before { build.update_attributes(tag: true) } + + it { is_expected.to eq(tag_variable + predefined_variables + yaml_variables) } + end context 'and secure variables' do let(:secure_variables) do @@ -339,7 +240,7 @@ describe Ci::Build do build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value') end - it { is_expected.to eq(variables + secure_variables) } + it { is_expected.to eq(predefined_variables + yaml_variables + secure_variables) } context 'and trigger variables' do let(:trigger) { FactoryGirl.create :ci_trigger, project: project } @@ -349,14 +250,154 @@ describe Ci::Build do { key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false } ] end + let(:predefined_trigger_variable) do + [ + { key: :CI_BUILD_TRIGGERED, value: 'true', public: true } + ] + end before do build.trigger_request = trigger_request end - it { is_expected.to eq(variables + secure_variables + trigger_variables) } + it { is_expected.to eq(predefined_variables + predefined_trigger_variable + yaml_variables + secure_variables + trigger_variables) } + end + end + end + end + + describe :project_recipients do + let(:pusher_email) { 'pusher@gitlab.test' } + let(:user) { User.new(notification_email: pusher_email) } + subject { build.project_recipients } + + before do + build.update_attributes(user: user) + end + + it 'should return pusher_email as only recipient when no additional recipients are given' do + project.update_attributes(email_add_pusher: true, + email_recipients: '') + is_expected.to eq([pusher_email]) + end + + it 'should return pusher_email and additional recipients' do + project.update_attributes(email_add_pusher: true, + email_recipients: 'rec1 rec2') + is_expected.to eq(['rec1', 'rec2', pusher_email]) + end + + it 'should return recipients' do + project.update_attributes(email_add_pusher: false, + email_recipients: 'rec1 rec2') + is_expected.to eq(['rec1', 'rec2']) + end + + it 'should return unique recipients only' do + project.update_attributes(email_add_pusher: true, + email_recipients: "rec1 rec1 #{pusher_email}") + is_expected.to eq(['rec1', pusher_email]) + end + end + + describe :can_be_served? do + let(:runner) { FactoryGirl.create :ci_specific_runner } + + before { build.project.runners << runner } + + context 'runner without tags' do + it 'can handle builds without tags' do + expect(build.can_be_served?(runner)).to be_truthy + end + + it 'cannot handle build with tags' do + build.tag_list = ['aa'] + expect(build.can_be_served?(runner)).to be_falsey + end + end + + context 'runner with tags' do + before { runner.tag_list = ['bb', 'cc'] } + + it 'can handle builds without tags' do + expect(build.can_be_served?(runner)).to be_truthy + end + + it 'can handle build with matching tags' do + build.tag_list = ['bb'] + expect(build.can_be_served?(runner)).to be_truthy + end + + it 'cannot handle build with not matching tags' do + build.tag_list = ['aa'] + expect(build.can_be_served?(runner)).to be_falsey + end + end + end + + describe :any_runners_online? do + subject { build.any_runners_online? } + + context 'when no runners' do + it { is_expected.to be_falsey } + end + + context 'if there are runner' do + let(:runner) { FactoryGirl.create :ci_specific_runner } + + before do + build.project.runners << runner + runner.update_attributes(contacted_at: 1.second.ago) + end + + it { is_expected.to be_truthy } + + it 'that is inactive' do + runner.update_attributes(active: false) + is_expected.to be_falsey + end + + it 'that is not online' do + runner.update_attributes(contacted_at: nil) + is_expected.to be_falsey + end + + it 'that cannot handle build' do + expect_any_instance_of(Ci::Build).to receive(:can_be_served?).and_return(false) + is_expected.to be_falsey + end + + end + end + + describe :show_warning? do + subject { build.show_warning? } + + %w(pending).each do |state| + context "if commit_status.status is #{state}" do + before { build.status = state } + + it { is_expected.to be_truthy } + + context "and there are specific runner" do + let(:runner) { FactoryGirl.create :ci_specific_runner, contacted_at: 1.second.ago } + + before do + build.project.runners << runner + runner.save + end + + it { is_expected.to be_falsey } end end end + + %w(success failed canceled running).each do |state| + context "if commit_status.status is #{state}" do + before { build.status = state } + + it { is_expected.to be_falsey } + end + end end end diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index c04bbcbadc7..44dbd083f06 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -21,20 +21,35 @@ describe Ci::Commit do let(:project) { FactoryGirl.create :ci_project } let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } - let(:commit_with_project) { FactoryGirl.create :ci_commit, gl_project: gl_project } - let(:config_processor) { Ci::GitlabCiYamlProcessor.new(gitlab_ci_yaml) } it { is_expected.to belong_to(:gl_project) } + it { is_expected.to have_many(:statuses) } + it { is_expected.to have_many(:trigger_requests) } it { is_expected.to have_many(:builds) } - it { is_expected.to validate_presence_of :before_sha } it { is_expected.to validate_presence_of :sha } - it { is_expected.to validate_presence_of :ref } - it { is_expected.to validate_presence_of :push_data } it { is_expected.to respond_to :git_author_name } it { is_expected.to respond_to :git_author_email } it { is_expected.to respond_to :short_sha } + describe :ordered do + let(:project) { FactoryGirl.create :empty_project } + + it 'returns ordered list of commits' do + commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project + commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project + expect(project.ci_commits.ordered).to eq([commit2, commit1]) + end + + it 'returns commits ordered by committed_at and id, with nulls last' do + commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project + commit2 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project + commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project + commit4 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project + expect(project.ci_commits.ordered).to eq([commit2, commit4, commit3, commit1]) + end + end + describe :last_build do subject { commit.last_build } before do @@ -52,57 +67,12 @@ describe Ci::Commit do @second = FactoryGirl.create :ci_build, commit: commit end - it "creates new build" do + it "creates only a new build" do expect(commit.builds.count(:all)).to eq 2 + expect(commit.statuses.count(:all)).to eq 2 commit.retry expect(commit.builds.count(:all)).to eq 3 - end - end - - describe :project_recipients do - - context 'always sending notification' do - it 'should return commit_pusher_email as only recipient when no additional recipients are given' do - project = FactoryGirl.create :ci_project, - email_add_pusher: true, - email_recipients: '' - gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project - commit = FactoryGirl.create :ci_commit, gl_project: gl_project - expected = 'commit_pusher_email' - allow(commit).to receive(:push_data) { { user_email: expected } } - expect(commit.project_recipients).to eq([expected]) - end - - it 'should return commit_pusher_email and additional recipients' do - project = FactoryGirl.create :ci_project, - email_add_pusher: true, - email_recipients: 'rec1 rec2' - gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project - commit = FactoryGirl.create :ci_commit, gl_project: gl_project - expected = 'commit_pusher_email' - allow(commit).to receive(:push_data) { { user_email: expected } } - expect(commit.project_recipients).to eq(['rec1', 'rec2', expected]) - end - - it 'should return recipients' do - project = FactoryGirl.create :ci_project, - email_add_pusher: false, - email_recipients: 'rec1 rec2' - gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project - commit = FactoryGirl.create :ci_commit, gl_project: gl_project - expect(commit.project_recipients).to eq(['rec1', 'rec2']) - end - - it 'should return unique recipients only' do - project = FactoryGirl.create :ci_project, - email_add_pusher: true, - email_recipients: 'rec1 rec1 rec2' - gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project - commit = FactoryGirl.create :ci_commit, gl_project: gl_project - expected = 'rec2' - allow(commit).to receive(:push_data) { { user_email: expected } } - expect(commit.project_recipients).to eq(['rec1', 'rec2']) - end + expect(commit.statuses.count(:all)).to eq 3 end end @@ -117,63 +87,122 @@ describe Ci::Commit do end end - describe :compare? do - subject { commit_with_project.compare? } - - context 'if commit.before_sha are not nil' do - it { is_expected.to be_truthy } - end - end - describe :short_sha do - subject { commit.short_before_sha } + subject { commit.short_sha } it 'has 8 items' do expect(subject.size).to eq(8) end - it { expect(commit.before_sha).to start_with(subject) } + it { expect(commit.sha).to start_with(subject) } end - describe :short_sha do - subject { commit.short_sha } + describe :stage do + subject { commit.stage } - it 'has 8 items' do - expect(subject.size).to eq(8) + before do + @second = FactoryGirl.create :commit_status, commit: commit, name: 'deploy', stage: 'deploy', stage_idx: 1, status: 'pending' + @first = FactoryGirl.create :commit_status, commit: commit, name: 'test', stage: 'test', stage_idx: 0, status: 'pending' + end + + it 'returns first running stage' do + is_expected.to eq('test') + end + + context 'first build succeeded' do + before do + @first.success + end + + it 'returns last running stage' do + is_expected.to eq('deploy') + end + end + + context 'all builds succeeded' do + before do + @first.success + @second.success + end + + it 'returns nil' do + is_expected.to be_nil + end end - it { expect(commit.sha).to start_with(subject) } end describe :create_next_builds do + end + + describe :refs do + subject { commit.refs } + before do - allow(commit).to receive(:config_processor).and_return(config_processor) + FactoryGirl.create :commit_status, commit: commit, name: 'deploy' + FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'develop' + FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'master' end - it "creates builds for next type" do - expect(commit.create_builds).to be_truthy - commit.builds.reload - expect(commit.builds.size).to eq(2) + it 'returns all refs' do + is_expected.to contain_exactly('master', 'develop', nil) + end + end - expect(commit.create_next_builds(nil)).to be_truthy - commit.builds.reload - expect(commit.builds.size).to eq(4) + describe :retried do + subject { commit.retried } - expect(commit.create_next_builds(nil)).to be_truthy - commit.builds.reload - expect(commit.builds.size).to eq(5) + before do + @commit1 = FactoryGirl.create :ci_build, commit: commit, name: 'deploy' + @commit2 = FactoryGirl.create :ci_build, commit: commit, name: 'deploy' + end - expect(commit.create_next_builds(nil)).to be_falsey + it 'returns old builds' do + is_expected.to contain_exactly(@commit1) end end describe :create_builds do - before do - allow(commit).to receive(:config_processor).and_return(config_processor) + let!(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } + + def create_builds(trigger_request = nil) + commit.create_builds('master', false, nil, trigger_request) + end + + def create_next_builds + commit.create_next_builds(commit.builds.order(:id).last) end it 'creates builds' do - expect(commit.create_builds).to be_truthy - commit.builds.reload - expect(commit.builds.size).to eq(2) + expect(create_builds).to be_truthy + commit.builds.update_all(status: "success") + expect(commit.builds.count(:all)).to eq(2) + + expect(create_next_builds).to be_truthy + commit.builds.update_all(status: "success") + expect(commit.builds.count(:all)).to eq(4) + + expect(create_next_builds).to be_truthy + commit.builds.update_all(status: "success") + expect(commit.builds.count(:all)).to eq(5) + + expect(create_next_builds).to be_falsey + end + + context 'for different ref' do + def create_develop_builds + commit.create_builds('develop', false, nil, nil) + end + + it 'creates builds' do + expect(create_builds).to be_truthy + commit.builds.update_all(status: "success") + expect(commit.builds.count(:all)).to eq(2) + + expect(create_develop_builds).to be_truthy + commit.builds.update_all(status: "success") + expect(commit.builds.count(:all)).to eq(4) + expect(commit.refs.size).to eq(2) + expect(commit.builds.pluck(:name).uniq.size).to eq(2) + end end context 'for build triggers' do @@ -181,46 +210,165 @@ describe Ci::Commit do let(:trigger_request) { FactoryGirl.create :ci_trigger_request, commit: commit, trigger: trigger } it 'creates builds' do - expect(commit.create_builds(trigger_request)).to be_truthy - commit.builds.reload - expect(commit.builds.size).to eq(2) + expect(create_builds(trigger_request)).to be_truthy + expect(commit.builds.count(:all)).to eq(2) end it 'rebuilds commit' do - expect(commit.create_builds).to be_truthy - commit.builds.reload - expect(commit.builds.size).to eq(2) + expect(create_builds).to be_truthy + expect(commit.builds.count(:all)).to eq(2) - expect(commit.create_builds(trigger_request)).to be_truthy - commit.builds.reload - expect(commit.builds.size).to eq(4) + expect(create_builds(trigger_request)).to be_truthy + expect(commit.builds.count(:all)).to eq(4) end it 'creates next builds' do - expect(commit.create_builds(trigger_request)).to be_truthy - commit.builds.reload - expect(commit.builds.size).to eq(2) + expect(create_builds(trigger_request)).to be_truthy + expect(commit.builds.count(:all)).to eq(2) + commit.builds.update_all(status: "success") - expect(commit.create_next_builds(trigger_request)).to be_truthy - commit.builds.reload - expect(commit.builds.size).to eq(4) + expect(create_next_builds).to be_truthy + expect(commit.builds.count(:all)).to eq(4) end context 'for [ci skip]' do before do - commit.push_data[:commits][0][:message] = 'skip this commit [ci skip]' - commit.save + allow(commit).to receive(:git_commit_message) { 'message [ci skip]' } end it 'rebuilds commit' do expect(commit.status).to eq('skipped') - expect(commit.create_builds(trigger_request)).to be_truthy - commit.builds.reload - expect(commit.builds.size).to eq(2) - expect(commit.status).to eq('pending') + expect(create_builds).to be_truthy + + # since everything in Ci::Commit is cached we need to fetch a new object + new_commit = Ci::Commit.find_by_id(commit.id) + expect(new_commit.status).to eq('pending') end end end + + context 'properly creates builds when "when" is defined' do + let(:yaml) do + { + stages: ["build", "test", "test_failure", "deploy", "cleanup"], + build: { + stage: "build", + script: "BUILD", + }, + test: { + stage: "test", + script: "TEST", + }, + test_failure: { + stage: "test_failure", + script: "ON test failure", + when: "on_failure", + }, + deploy: { + stage: "deploy", + script: "PUBLISH", + }, + cleanup: { + stage: "cleanup", + script: "TIDY UP", + when: "always", + } + } + end + + before do + stub_ci_commit_yaml_file(YAML.dump(yaml)) + end + + it 'properly creates builds' do + expect(create_builds).to be_truthy + expect(commit.builds.pluck(:name)).to contain_exactly('build') + expect(commit.builds.pluck(:status)).to contain_exactly('pending') + commit.builds.running_or_pending.each(&:success) + + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending') + commit.builds.running_or_pending.each(&:success) + + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'pending') + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy') + commit.builds.running_or_pending.each(&:success) + + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy', 'cleanup') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'pending') + commit.builds.running_or_pending.each(&:success) + + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'success') + expect(commit.status).to eq('success') + end + + it 'properly creates builds when test fails' do + expect(create_builds).to be_truthy + expect(commit.builds.pluck(:name)).to contain_exactly('build') + expect(commit.builds.pluck(:status)).to contain_exactly('pending') + commit.builds.running_or_pending.each(&:success) + + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending') + commit.builds.running_or_pending.each(&:drop) + + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'pending') + commit.builds.running_or_pending.each(&:success) + + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'pending') + commit.builds.running_or_pending.each(&:success) + + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'success') + expect(commit.status).to eq('failed') + end + + it 'properly creates builds when test and test_failure fails' do + expect(create_builds).to be_truthy + expect(commit.builds.pluck(:name)).to contain_exactly('build') + expect(commit.builds.pluck(:status)).to contain_exactly('pending') + commit.builds.running_or_pending.each(&:success) + + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending') + commit.builds.running_or_pending.each(&:drop) + + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'pending') + commit.builds.running_or_pending.each(&:drop) + + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'pending') + commit.builds.running_or_pending.each(&:success) + + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'success') + expect(commit.status).to eq('failed') + end + + it 'properly creates builds when deploy fails' do + expect(create_builds).to be_truthy + expect(commit.builds.pluck(:name)).to contain_exactly('build') + expect(commit.builds.pluck(:status)).to contain_exactly('pending') + commit.builds.running_or_pending.each(&:success) + + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending') + commit.builds.running_or_pending.each(&:success) + + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'pending') + commit.builds.running_or_pending.each(&:drop) + + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy', 'cleanup') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'pending') + commit.builds.running_or_pending.each(&:success) + + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'success') + expect(commit.status).to eq('failed') + end + end end describe "#finished_at" do diff --git a/spec/models/ci/project_services/hip_chat_message_spec.rb b/spec/models/ci/project_services/hip_chat_message_spec.rb index 1903c036924..e23d6ae2c28 100644 --- a/spec/models/ci/project_services/hip_chat_message_spec.rb +++ b/spec/models/ci/project_services/hip_chat_message_spec.rb @@ -3,70 +3,37 @@ require 'spec_helper' describe Ci::HipChatMessage do subject { Ci::HipChatMessage.new(build) } - context "One build" do - let(:commit) { FactoryGirl.create(:ci_commit_with_one_job) } + let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) } - let(:build) do - commit.create_builds - commit.builds.first - end - - context 'when build succeeds' do - it 'returns a successful message' do - build.update(status: "success") - - expect( subject.status_color ).to eq 'green' - expect( subject.notify? ).to be_falsey - expect( subject.to_s ).to match(/Build '[^']+' #\d+/) - expect( subject.to_s ).to match(/Successful in \d+ second\(s\)\./) - end - end - - context 'when build fails' do - it 'returns a failure message' do - build.update(status: "failed") - - expect( subject.status_color ).to eq 'red' - expect( subject.notify? ).to be_truthy - expect( subject.to_s ).to match(/Build '[^']+' #\d+/) - expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./) - end - end + let(:build) do + commit.builds.first end - context "Several builds" do - let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) } - - let(:build) do - commit.builds.first - end - - context 'when all matrix builds succeed' do - it 'returns a successful message' do - commit.create_builds - commit.builds.update_all(status: "success") - commit.reload + context 'when all matrix builds succeed' do + it 'returns a successful message' do + commit.create_builds('master', false, nil) + commit.builds.update_all(status: "success") + commit.reload - expect( subject.status_color ).to eq 'green' - expect( subject.notify? ).to be_falsey - expect( subject.to_s ).to match(/Commit #\d+/) - expect( subject.to_s ).to match(/Successful in \d+ second\(s\)\./) - end + expect(subject.status_color).to eq 'green' + expect(subject.notify?).to be_falsey + expect(subject.to_s).to match(/Commit #\d+/) + expect(subject.to_s).to match(/Successful in \d+ second\(s\)\./) end + end - context 'when at least one matrix build fails' do - it 'returns a failure message' do - commit.create_builds - first_build = commit.builds.first - second_build = commit.builds.last - first_build.update(status: "success") - second_build.update(status: "failed") - - expect( subject.status_color ).to eq 'red' - expect( subject.notify? ).to be_truthy - expect( subject.to_s ).to match(/Commit #\d+/) - expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./) - end + context 'when at least one matrix build fails' do + it 'returns a failure message' do + commit.create_builds('master', false, nil) + first_build = commit.builds.first + second_build = commit.builds.last + first_build.update(status: "success") + second_build.update(status: "failed") + + expect(subject.status_color).to eq 'red' + expect(subject.notify?).to be_truthy + expect(subject.to_s).to match(/Commit #\d+/) + expect(subject.to_s).to match(/Failed in \d+ second\(s\)\./) end end end diff --git a/spec/models/ci/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb index 0d9f85959ba..d9b3d34ff15 100644 --- a/spec/models/ci/mail_service_spec.rb +++ b/spec/models/ci/project_services/mail_service_spec.rb @@ -29,12 +29,13 @@ describe Ci::MailService do describe 'Sends email for' do let(:mail) { Ci::MailService.new } + let(:user) { User.new(notification_email: 'git@example.com')} describe 'failed build' do let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true) } let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit) } + let(:build) { FactoryGirl.create(:ci_build, status: 'failed', commit: commit, user: user) } before do allow(mail).to receive_messages( @@ -57,7 +58,7 @@ describe Ci::MailService do let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true, email_only_broken_builds: false) } let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } + let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) } before do allow(mail).to receive_messages( @@ -85,7 +86,7 @@ describe Ci::MailService do end let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } + let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) } before do allow(mail).to receive_messages( @@ -114,7 +115,7 @@ describe Ci::MailService do end let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } + let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) } before do allow(mail).to receive_messages( @@ -143,7 +144,7 @@ describe Ci::MailService do end let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } + let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) } before do allow(mail).to receive_messages( @@ -166,7 +167,7 @@ describe Ci::MailService do end let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit) } + let(:build) { FactoryGirl.create(:ci_build, status: 'failed', commit: commit, user: user) } before do allow(mail).to receive_messages( diff --git a/spec/models/ci/project_services/slack_message_spec.rb b/spec/models/ci/project_services/slack_message_spec.rb index 7b541802d7d..8adda6c86cc 100644 --- a/spec/models/ci/project_services/slack_message_spec.rb +++ b/spec/models/ci/project_services/slack_message_spec.rb @@ -3,80 +3,41 @@ require 'spec_helper' describe Ci::SlackMessage do subject { Ci::SlackMessage.new(commit) } - context "One build" do - let(:commit) { FactoryGirl.create(:ci_commit_with_one_job) } + let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) } - let(:build) do - commit.create_builds - commit.builds.first - end - - context 'when build succeeded' do - let(:color) { 'good' } + context 'when all matrix builds succeeded' do + let(:color) { 'good' } - it 'returns a message with succeeded build' do - build.update(status: "success") + it 'returns a message with success' do + commit.create_builds('master', false, nil) + commit.builds.update_all(status: "success") + commit.reload - expect(subject.color).to eq(color) - expect(subject.fallback).to include('Build') - expect(subject.fallback).to include("\##{build.id}") - expect(subject.fallback).to include('succeeded') - expect(subject.attachments.first[:fields]).to be_empty - end - end - - context 'when build failed' do - let(:color) { 'danger' } - - it 'returns a message with failed build' do - build.update(status: "failed") - - expect(subject.color).to eq(color) - expect(subject.fallback).to include('Build') - expect(subject.fallback).to include("\##{build.id}") - expect(subject.fallback).to include('failed') - expect(subject.attachments.first[:fields]).to be_empty - end + expect(subject.color).to eq(color) + expect(subject.fallback).to include('Commit') + expect(subject.fallback).to include("\##{commit.id}") + expect(subject.fallback).to include('succeeded') + expect(subject.attachments.first[:fields]).to be_empty end end - context "Several builds" do - let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) } - - context 'when all matrix builds succeeded' do - let(:color) { 'good' } - - it 'returns a message with success' do - commit.create_builds - commit.builds.update_all(status: "success") - commit.reload - - expect(subject.color).to eq(color) - expect(subject.fallback).to include('Commit') - expect(subject.fallback).to include("\##{commit.id}") - expect(subject.fallback).to include('succeeded') - expect(subject.attachments.first[:fields]).to be_empty - end - end - - context 'when one of matrix builds failed' do - let(:color) { 'danger' } - - it 'returns a message with information about failed build' do - commit.create_builds - first_build = commit.builds.first - second_build = commit.builds.last - first_build.update(status: "success") - second_build.update(status: "failed") - - expect(subject.color).to eq(color) - expect(subject.fallback).to include('Commit') - expect(subject.fallback).to include("\##{commit.id}") - expect(subject.fallback).to include('failed') - expect(subject.attachments.first[:fields].size).to eq(1) - expect(subject.attachments.first[:fields].first[:title]).to eq(second_build.name) - expect(subject.attachments.first[:fields].first[:value]).to include("\##{second_build.id}") - end + context 'when one of matrix builds failed' do + let(:color) { 'danger' } + + it 'returns a message with information about failed build' do + commit.create_builds('master', false, nil) + first_build = commit.builds.first + second_build = commit.builds.last + first_build.update(status: "success") + second_build.update(status: "failed") + + expect(subject.color).to eq(color) + expect(subject.fallback).to include('Commit') + expect(subject.fallback).to include("\##{commit.id}") + expect(subject.fallback).to include('failed') + expect(subject.attachments.first[:fields].size).to eq(1) + expect(subject.attachments.first[:fields].first[:title]).to eq(second_build.name) + expect(subject.attachments.first[:fields].first[:value]).to include("\##{second_build.id}") end end end diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index dec4720a711..490c6a67982 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -131,24 +131,6 @@ describe Ci::Project do end end - describe 'ordered commits' do - let(:project) { FactoryGirl.create :empty_project } - - it 'returns ordered list of commits' do - commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project - commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project - expect(project.ci_commits).to eq([commit2, commit1]) - end - - it 'returns commits ordered by committed_at and id, with nulls last' do - commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project - commit2 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project - commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project - commit4 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project - expect(project.ci_commits).to eq([commit2, commit4, commit3, commit1]) - end - end - context :valid_project do let(:commit) { FactoryGirl.create(:ci_commit) } @@ -259,5 +241,18 @@ describe Ci::Project do FactoryGirl.create(:ci_shared_runner) expect(project.any_runners?).to be_falsey end + + it "checks the presence of specific runner" do + project = FactoryGirl.create(:ci_project) + specific_runner = FactoryGirl.create(:ci_specific_runner) + project.runners << specific_runner + expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy + end + + it "checks the presence of shared runner" do + project = FactoryGirl.create(:ci_project, shared_runners_enabled: true) + shared_runner = FactoryGirl.create(:ci_shared_runner) + expect(project.any_runners? { |runner| runner == shared_runner }).to be_truthy + end end end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 757593a7ab8..f8a51c29dc2 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -32,7 +32,7 @@ describe Ci::Runner do end it 'should return the token if the description is an empty string' do - runner = FactoryGirl.build(:ci_runner, description: '') + runner = FactoryGirl.build(:ci_runner, description: '', token: 'token') expect(runner.display_name).to eq runner.token end end @@ -48,6 +48,71 @@ describe Ci::Runner do it { expect(shared_runner.only_for?(project)).to be_truthy } end + describe :online do + subject { Ci::Runner.online } + + before do + @runner1 = FactoryGirl.create(:ci_shared_runner, contacted_at: 1.year.ago) + @runner2 = FactoryGirl.create(:ci_shared_runner, contacted_at: 1.second.ago) + end + + it { is_expected.to eq([@runner2])} + end + + describe :online? do + let(:runner) { FactoryGirl.create(:ci_shared_runner) } + + subject { runner.online? } + + context 'never contacted' do + before { runner.contacted_at = nil } + + it { is_expected.to be_falsey } + end + + context 'contacted long time ago time' do + before { runner.contacted_at = 1.year.ago } + + it { is_expected.to be_falsey } + end + + context 'contacted 1s ago' do + before { runner.contacted_at = 1.second.ago } + + it { is_expected.to be_truthy } + end + end + + describe :status do + let(:runner) { FactoryGirl.create(:ci_shared_runner, contacted_at: 1.second.ago) } + + subject { runner.status } + + context 'never connected' do + before { runner.contacted_at = nil } + + it { is_expected.to eq(:not_connected) } + end + + context 'contacted 1s ago' do + before { runner.contacted_at = 1.second.ago } + + it { is_expected.to eq(:online) } + end + + context 'contacted long time ago' do + before { runner.contacted_at = 1.year.ago } + + it { is_expected.to eq(:offline) } + end + + context 'inactive' do + before { runner.active = false } + + it { is_expected.to eq(:paused) } + end + end + describe "belongs_to_one_project?" do it "returns false if there are two projects runner assigned to" do runner = FactoryGirl.create(:ci_specific_runner) diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index e303a97e6b5..90be9324951 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -89,9 +89,9 @@ eos end it_behaves_like 'a mentionable' do - subject { commit } + subject { create(:project).commit } - let(:author) { create(:user, email: commit.author_email) } + let(:author) { create(:user, email: subject.author_email) } let(:backref_text) { "commit #{subject.id}" } let(:set_mentionable_text) do ->(txt) { allow(subject).to receive(:safe_message).and_return(txt) } diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb new file mode 100644 index 00000000000..c96a606fdaa --- /dev/null +++ b/spec/models/commit_status_spec.rb @@ -0,0 +1,164 @@ +require 'spec_helper' + +describe CommitStatus do + let(:commit) { FactoryGirl.create :ci_commit } + let(:commit_status) { FactoryGirl.create :commit_status, commit: commit } + + it { is_expected.to belong_to(:commit) } + it { is_expected.to belong_to(:user) } + it { is_expected.to validate_presence_of(:name) } + it { is_expected.to validate_inclusion_of(:status).in_array(%w(pending running failed success canceled)) } + + it { is_expected.to delegate_method(:sha).to(:commit) } + it { is_expected.to delegate_method(:short_sha).to(:commit) } + it { is_expected.to delegate_method(:gl_project).to(:commit) } + + it { is_expected.to respond_to :success? } + it { is_expected.to respond_to :failed? } + it { is_expected.to respond_to :running? } + it { is_expected.to respond_to :pending? } + + describe :author do + subject { commit_status.author } + before { commit_status.author = User.new } + + it { is_expected.to eq(commit_status.user) } + end + + describe :started? do + subject { commit_status.started? } + + context 'without started_at' do + before { commit_status.started_at = nil } + + it { is_expected.to be_falsey } + end + + %w(running success failed).each do |status| + context "if commit status is #{status}" do + before { commit_status.status = status } + + it { is_expected.to be_truthy } + end + end + + %w(pending canceled).each do |status| + context "if commit status is #{status}" do + before { commit_status.status = status } + + it { is_expected.to be_falsey } + end + end + end + + describe :active? do + subject { commit_status.active? } + + %w(pending running).each do |state| + context "if commit_status.status is #{state}" do + before { commit_status.status = state } + + it { is_expected.to be_truthy } + end + end + + %w(success failed canceled).each do |state| + context "if commit_status.status is #{state}" do + before { commit_status.status = state } + + it { is_expected.to be_falsey } + end + end + end + + describe :complete? do + subject { commit_status.complete? } + + %w(success failed canceled).each do |state| + context "if commit_status.status is #{state}" do + before { commit_status.status = state } + + it { is_expected.to be_truthy } + end + end + + %w(pending running).each do |state| + context "if commit_status.status is #{state}" do + before { commit_status.status = state } + + it { is_expected.to be_falsey } + end + end + end + + describe :duration do + subject { commit_status.duration } + + it { is_expected.to eq(120.0) } + + context 'if the building process has not started yet' do + before do + commit_status.started_at = nil + commit_status.finished_at = nil + end + + it { is_expected.to be_nil } + end + + context 'if the building process has started' do + before do + commit_status.started_at = Time.now - 1.minute + commit_status.finished_at = nil + end + + it { is_expected.to be_a(Float) } + it { is_expected.to be > 0.0 } + end + end + + describe :latest do + subject { CommitStatus.latest.order(:id) } + + before do + @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running' + @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending' + @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'cc', status: 'success' + @commit4 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'bb', status: 'success' + @commit5 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'success' + end + + it 'return unique statuses' do + is_expected.to eq([@commit2, @commit3, @commit4, @commit5]) + end + end + + describe :for_ref do + subject { CommitStatus.for_ref('bb').order(:id) } + + before do + @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running' + @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending' + @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: nil, status: 'success' + end + + it 'return statuses with equal and nil ref set' do + is_expected.to eq([@commit1]) + end + end + + describe :running_or_pending do + subject { CommitStatus.running_or_pending.order(:id) } + + before do + @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running' + @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending' + @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: nil, status: 'success' + @commit4 = FactoryGirl.create :commit_status, commit: commit, name: 'dd', ref: nil, status: 'failed' + @commit5 = FactoryGirl.create :commit_status, commit: commit, name: 'ee', ref: nil, status: 'canceled' + end + + it 'return statuses that are running or pending' do + is_expected.to eq([@commit1, @commit2]) + end + end +end diff --git a/spec/models/concerns/case_sensitivity_spec.rb b/spec/models/concerns/case_sensitivity_spec.rb new file mode 100644 index 00000000000..f7ed30f8198 --- /dev/null +++ b/spec/models/concerns/case_sensitivity_spec.rb @@ -0,0 +1,189 @@ +require 'spec_helper' + +describe CaseSensitivity do + describe '.iwhere' do + let(:connection) { ActiveRecord::Base.connection } + let(:model) { Class.new { include CaseSensitivity } } + + describe 'using PostgreSQL' do + before do + allow(Gitlab::Database).to receive(:postgresql?).and_return(true) + allow(Gitlab::Database).to receive(:mysql?).and_return(false) + end + + describe 'with a single column/value pair' do + it 'returns the criteria for a column and a value' do + criteria = double(:criteria) + + expect(connection).to receive(:quote_table_name). + with(:foo). + and_return('"foo"') + + expect(model).to receive(:where). + with(%q{LOWER("foo") = LOWER(:value)}, value: 'bar'). + and_return(criteria) + + expect(model.iwhere(foo: 'bar')).to eq(criteria) + end + + it 'returns the criteria for a column with a table, and a value' do + criteria = double(:criteria) + + expect(connection).to receive(:quote_table_name). + with(:'foo.bar'). + and_return('"foo"."bar"') + + expect(model).to receive(:where). + with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar'). + and_return(criteria) + + expect(model.iwhere(:'foo.bar' => 'bar')).to eq(criteria) + end + end + + describe 'with multiple column/value pairs' do + it 'returns the criteria for a column and a value' do + initial = double(:criteria) + final = double(:criteria) + + expect(connection).to receive(:quote_table_name). + with(:foo). + and_return('"foo"') + + expect(connection).to receive(:quote_table_name). + with(:bar). + and_return('"bar"') + + expect(model).to receive(:where). + with(%q{LOWER("foo") = LOWER(:value)}, value: 'bar'). + and_return(initial) + + expect(initial).to receive(:where). + with(%q{LOWER("bar") = LOWER(:value)}, value: 'baz'). + and_return(final) + + got = model.iwhere(foo: 'bar', bar: 'baz') + + expect(got).to eq(final) + end + + it 'returns the criteria for a column with a table, and a value' do + initial = double(:criteria) + final = double(:criteria) + + expect(connection).to receive(:quote_table_name). + with(:'foo.bar'). + and_return('"foo"."bar"') + + expect(connection).to receive(:quote_table_name). + with(:'foo.baz'). + and_return('"foo"."baz"') + + expect(model).to receive(:where). + with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar'). + and_return(initial) + + expect(initial).to receive(:where). + with(%q{LOWER("foo"."baz") = LOWER(:value)}, value: 'baz'). + and_return(final) + + got = model.iwhere(:'foo.bar' => 'bar', + :'foo.baz' => 'baz') + + expect(got).to eq(final) + end + end + end + + describe 'using MySQL' do + before do + allow(Gitlab::Database).to receive(:postgresql?).and_return(false) + allow(Gitlab::Database).to receive(:mysql?).and_return(true) + end + + describe 'with a single column/value pair' do + it 'returns the criteria for a column and a value' do + criteria = double(:criteria) + + expect(connection).to receive(:quote_table_name). + with(:foo). + and_return('`foo`') + + expect(model).to receive(:where). + with(%q{`foo` = :value}, value: 'bar'). + and_return(criteria) + + expect(model.iwhere(foo: 'bar')).to eq(criteria) + end + + it 'returns the criteria for a column with a table, and a value' do + criteria = double(:criteria) + + expect(connection).to receive(:quote_table_name). + with(:'foo.bar'). + and_return('`foo`.`bar`') + + expect(model).to receive(:where). + with(%q{`foo`.`bar` = :value}, value: 'bar'). + and_return(criteria) + + expect(model.iwhere(:'foo.bar' => 'bar')). + to eq(criteria) + end + end + + describe 'with multiple column/value pairs' do + it 'returns the criteria for a column and a value' do + initial = double(:criteria) + final = double(:criteria) + + expect(connection).to receive(:quote_table_name). + with(:foo). + and_return('`foo`') + + expect(connection).to receive(:quote_table_name). + with(:bar). + and_return('`bar`') + + expect(model).to receive(:where). + with(%q{`foo` = :value}, value: 'bar'). + and_return(initial) + + expect(initial).to receive(:where). + with(%q{`bar` = :value}, value: 'baz'). + and_return(final) + + got = model.iwhere(foo: 'bar', bar: 'baz') + + expect(got).to eq(final) + end + + it 'returns the criteria for a column with a table, and a value' do + initial = double(:criteria) + final = double(:criteria) + + expect(connection).to receive(:quote_table_name). + with(:'foo.bar'). + and_return('`foo`.`bar`') + + expect(connection).to receive(:quote_table_name). + with(:'foo.baz'). + and_return('`foo`.`baz`') + + expect(model).to receive(:where). + with(%q{`foo`.`bar` = :value}, value: 'bar'). + and_return(initial) + + expect(initial).to receive(:where). + with(%q{`foo`.`baz` = :value}, value: 'baz'). + and_return(final) + + got = model.iwhere(:'foo.bar' => 'bar', + :'foo.baz' => 'baz') + + expect(got).to eq(final) + end + end + end + end +end diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb index 2d6fe003215..6179882e935 100644 --- a/spec/models/concerns/mentionable_spec.rb +++ b/spec/models/concerns/mentionable_spec.rb @@ -25,7 +25,7 @@ describe Issue, "Mentionable" do it 'correctly removes already-mentioned Commits' do expect(SystemNoteService).not_to receive(:cross_reference) - issue.create_cross_references!(project, author, [commit2]) + issue.create_cross_references!(author, [commit2]) end end diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb new file mode 100644 index 00000000000..f442fa5fbe5 --- /dev/null +++ b/spec/models/generic_commit_status_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe GenericCommitStatus do + let(:commit) { FactoryGirl.create :ci_commit } + let(:generic_commit_status) { FactoryGirl.create :generic_commit_status, commit: commit } + + describe :context do + subject { generic_commit_status.context } + before { generic_commit_status.context = 'my_context' } + + it { is_expected.to eq(generic_commit_status.name) } + end + + describe :tags do + subject { generic_commit_status.tags } + + it { is_expected.to eq([:external]) } + end + + describe :set_default_values do + before do + generic_commit_status.context = nil + generic_commit_status.stage = nil + generic_commit_status.save + end + + describe :context do + subject { generic_commit_status.context } + + it { is_expected.to_not be_nil } + end + + describe :stage do + subject { generic_commit_status.stage } + + it { is_expected.to_not be_nil } + end + end +end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index cf336d82957..623332cd2f9 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -69,7 +69,7 @@ describe Issue do end it_behaves_like 'an editable mentionable' do - subject { create(:issue, project: project) } + subject { create(:issue) } let(:backref_text) { "issue #{subject.to_reference}" } let(:set_mentionable_text) { ->(txt){ subject.description = txt } } diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 17a49013d25..6aaf1c036b0 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -165,6 +165,17 @@ describe MergeRequest do end end + describe "#hook_attrs" do + it "has all the required keys" do + attrs = subject.hook_attrs + attrs = attrs.to_h + expect(attrs).to include(:source) + expect(attrs).to include(:target) + expect(attrs).to include(:last_commit) + expect(attrs).to include(:work_in_progress) + end + end + it_behaves_like 'an editable mentionable' do subject { create(:merge_request) } diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 3a0b194ba1e..75564839dcf 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -192,10 +192,9 @@ describe Note do end it_behaves_like 'an editable mentionable' do - subject { create :note, noteable: issue, project: project } + subject { create :note, noteable: issue, project: issue.project } - let(:project) { create(:project) } - let(:issue) { create :issue, project: project } + let(:issue) { create :issue } let(:backref_text) { issue.gfm_reference } let(:set_mentionable_text) { ->(txt) { subject.note = txt } } end diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb new file mode 100644 index 00000000000..c34b2487ecf --- /dev/null +++ b/spec/models/project_services/bamboo_service_spec.rb @@ -0,0 +1,94 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null +# + +require 'spec_helper' + +describe BambooService, models: true do + describe "Associations" do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe "Execute" do + let(:user) { create(:user) } + let(:project) { create(:project) } + + context "when a password was previously set" do + before do + @bamboo_service = BambooService.create( + project: create(:project), + properties: { + bamboo_url: 'http://gitlab.com', + username: 'mic', + password: "password" + } + ) + end + + it "reset password if url changed" do + @bamboo_service.bamboo_url = 'http://gitlab1.com' + @bamboo_service.save + expect(@bamboo_service.password).to be_nil + end + + it "does not reset password if username changed" do + @bamboo_service.username = "some_name" + @bamboo_service.save + expect(@bamboo_service.password).to eq("password") + end + + it "does not reset password if new url is set together with password, even if it's the same password" do + @bamboo_service.bamboo_url = 'http://gitlab_edited.com' + @bamboo_service.password = 'password' + @bamboo_service.save + expect(@bamboo_service.password).to eq("password") + expect(@bamboo_service.bamboo_url).to eq("http://gitlab_edited.com") + end + + it "should reset password if url changed, even if setter called multiple times" do + @bamboo_service.bamboo_url = 'http://gitlab1.com' + @bamboo_service.bamboo_url = 'http://gitlab1.com' + @bamboo_service.save + expect(@bamboo_service.password).to be_nil + end + end + + context "when no password was previously set" do + before do + @bamboo_service = BambooService.create( + project: create(:project), + properties: { + bamboo_url: 'http://gitlab.com', + username: 'mic' + } + ) + end + + it "saves password if new url is set together with password" do + @bamboo_service.bamboo_url = 'http://gitlab_edited.com' + @bamboo_service.password = 'password' + @bamboo_service.save + expect(@bamboo_service.password).to eq("password") + expect(@bamboo_service.bamboo_url).to eq("http://gitlab_edited.com") + end + + end + end +end diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb index 683a403a907..842089ebe0d 100644 --- a/spec/models/project_services/gitlab_ci_service_spec.rb +++ b/spec/models/project_services/gitlab_ci_service_spec.rb @@ -39,8 +39,7 @@ describe GitlabCiService do end describe :build_page do - it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://localhost/ci/projects/#{@ci_project.id}/refs/master/commits/2ab7834c")} - it { expect(@service.build_page("issue#2", 'master')).to eq("http://localhost/ci/projects/#{@ci_project.id}/refs/master/commits/issue%232")} + it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://localhost/#{@ci_project.gl_project.path_with_namespace}/commit/2ab7834c/ci")} end describe "execute" do @@ -48,8 +47,8 @@ describe GitlabCiService do let(:project) { create(:project, name: 'project') } let(:push_sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) } - it "calls ci_yaml_file" do - expect(@service).to receive(:ci_yaml_file).with(push_sample_data[:checkout_sha]) + it "calls CreateCommitService" do + expect_any_instance_of(Ci::CreateCommitService).to receive(:execute).with(@ci_project, user, push_sample_data) @service.execute(push_sample_data) end diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb new file mode 100644 index 00000000000..f26b47a856c --- /dev/null +++ b/spec/models/project_services/teamcity_service_spec.rb @@ -0,0 +1,93 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null +# + +require 'spec_helper' + +describe TeamcityService, models: true do + describe "Associations" do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe "Execute" do + let(:user) { create(:user) } + let(:project) { create(:project) } + + context "when a password was previously set" do + before do + @teamcity_service = TeamcityService.create( + project: create(:project), + properties: { + teamcity_url: 'http://gitlab.com', + username: 'mic', + password: "password" + } + ) + end + + it "reset password if url changed" do + @teamcity_service.teamcity_url = 'http://gitlab1.com' + @teamcity_service.save + expect(@teamcity_service.password).to be_nil + end + + it "does not reset password if username changed" do + @teamcity_service.username = "some_name" + @teamcity_service.save + expect(@teamcity_service.password).to eq("password") + end + + it "does not reset password if new url is set together with password, even if it's the same password" do + @teamcity_service.teamcity_url = 'http://gitlab_edited.com' + @teamcity_service.password = 'password' + @teamcity_service.save + expect(@teamcity_service.password).to eq("password") + expect(@teamcity_service.teamcity_url).to eq("http://gitlab_edited.com") + end + + it "should reset password if url changed, even if setter called multiple times" do + @teamcity_service.teamcity_url = 'http://gitlab1.com' + @teamcity_service.teamcity_url = 'http://gitlab1.com' + @teamcity_service.save + expect(@teamcity_service.password).to be_nil + end + end + + context "when no password was previously set" do + before do + @teamcity_service = TeamcityService.create( + project: create(:project), + properties: { + teamcity_url: 'http://gitlab.com', + username: 'mic' + } + ) + end + + it "saves password if new url is set together with password" do + @teamcity_service.teamcity_url = 'http://gitlab_edited.com' + @teamcity_service.password = 'password' + @teamcity_service.save + expect(@teamcity_service.password).to eq("password") + expect(@teamcity_service.teamcity_url).to eq("http://gitlab_edited.com") + end + end + end +end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 8b5d2c3a1c1..f93935ebe3b 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -423,4 +423,42 @@ describe Project do it { expect(project.gitlab_ci?).to be_truthy } it { expect(project.gitlab_ci_project).to be_a(Ci::Project) } end + + describe '.trending' do + let(:group) { create(:group) } + let(:project1) { create(:empty_project, :public, group: group) } + let(:project2) { create(:empty_project, :public, group: group) } + + before do + 2.times do + create(:note_on_commit, project: project1) + end + + create(:note_on_commit, project: project2) + end + + describe 'without an explicit start date' do + subject { described_class.trending.to_a } + + it 'sorts Projects by the amount of notes in descending order' do + expect(subject).to eq([project1, project2]) + end + end + + describe 'with an explicit start date' do + let(:date) { 2.months.ago } + + subject { described_class.trending(date).to_a } + + before do + 2.times do + create(:note_on_commit, project: project2, created_at: date) + end + end + + it 'sorts Projects by the amount of notes in descending order' do + expect(subject).to eq([project2, project1]) + end + end + end end diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index a213ffe6c4b..692e5fda3ba 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -103,4 +103,125 @@ describe Service do end end end + + describe "{property}_changed?" do + let(:service) do + BambooService.create( + project: create(:project), + properties: { + bamboo_url: 'http://gitlab.com', + username: 'mic', + password: "password" + } + ) + end + + it "returns false when the property has not been assigned a new value" do + service.username = "key_changed" + expect(service.bamboo_url_changed?).to be_falsy + end + + it "returns true when the property has been assigned a different value" do + service.bamboo_url = "http://example.com" + expect(service.bamboo_url_changed?).to be_truthy + end + + it "returns true when the property has been assigned a different value twice" do + service.bamboo_url = "http://example.com" + service.bamboo_url = "http://example.com" + expect(service.bamboo_url_changed?).to be_truthy + end + + it "returns false when the property has been re-assigned the same value" do + service.bamboo_url = 'http://gitlab.com' + expect(service.bamboo_url_changed?).to be_falsy + end + + it "returns false when the property has been assigned a new value then saved" do + service.bamboo_url = 'http://example.com' + service.save + expect(service.bamboo_url_changed?).to be_falsy + end + end + + describe "{property}_touched?" do + let(:service) do + BambooService.create( + project: create(:project), + properties: { + bamboo_url: 'http://gitlab.com', + username: 'mic', + password: "password" + } + ) + end + + it "returns false when the property has not been assigned a new value" do + service.username = "key_changed" + expect(service.bamboo_url_touched?).to be_falsy + end + + it "returns true when the property has been assigned a different value" do + service.bamboo_url = "http://example.com" + expect(service.bamboo_url_touched?).to be_truthy + end + + it "returns true when the property has been assigned a different value twice" do + service.bamboo_url = "http://example.com" + service.bamboo_url = "http://example.com" + expect(service.bamboo_url_touched?).to be_truthy + end + + it "returns true when the property has been re-assigned the same value" do + service.bamboo_url = 'http://gitlab.com' + expect(service.bamboo_url_touched?).to be_truthy + end + + it "returns false when the property has been assigned a new value then saved" do + service.bamboo_url = 'http://example.com' + service.save + expect(service.bamboo_url_changed?).to be_falsy + end + end + + describe "{property}_was" do + let(:service) do + BambooService.create( + project: create(:project), + properties: { + bamboo_url: 'http://gitlab.com', + username: 'mic', + password: "password" + } + ) + end + + + it "returns nil when the property has not been assigned a new value" do + service.username = "key_changed" + expect(service.bamboo_url_was).to be_nil + end + + it "returns the previous value when the property has been assigned a different value" do + service.bamboo_url = "http://example.com" + expect(service.bamboo_url_was).to eq('http://gitlab.com') + end + + it "returns initial value when the property has been re-assigned the same value" do + service.bamboo_url = 'http://gitlab.com' + expect(service.bamboo_url_was).to eq('http://gitlab.com') + end + + it "returns initial value when the property has been assigned multiple values" do + service.bamboo_url = "http://example.com" + service.bamboo_url = "http://example2.com" + expect(service.bamboo_url_was).to eq('http://gitlab.com') + end + + it "returns nil when the property has been assigned a new value then saved" do + service.bamboo_url = 'http://example.com' + service.save + expect(service.bamboo_url_was).to be_nil + end + end end diff --git a/spec/requests/api/commit_status_spec.rb b/spec/requests/api/commit_status_spec.rb new file mode 100644 index 00000000000..b9e6dfc15a7 --- /dev/null +++ b/spec/requests/api/commit_status_spec.rb @@ -0,0 +1,135 @@ +require 'spec_helper' + +describe API::API, api: true do + include ApiHelpers + let(:user) { create(:user) } + let(:user2) { create(:user) } + let!(:project) { create(:project, creator_id: user.id) } + let!(:reporter) { create(:project_member, user: user, project: project, access_level: ProjectMember::REPORTER) } + let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) } + let(:commit) { project.repository.commit } + let!(:ci_commit) { project.ensure_ci_commit(commit.id) } + let(:commit_status) { create(:commit_status, commit: ci_commit) } + + describe "GET /projects/:id/repository/commits/:sha/statuses" do + context "reporter user" do + let(:statuses_id) { json_response.map { |status| status['id'] } } + + before do + @status1 = create(:commit_status, commit: ci_commit, status: 'running') + @status2 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'pending') + @status3 = create(:commit_status, commit: ci_commit, name: 'coverage', ref: 'develop', status: 'running') + @status4 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'success') + @status5 = create(:commit_status, commit: ci_commit, ref: 'develop', status: 'success') + @status6 = create(:commit_status, commit: ci_commit, status: 'success') + end + + it "should return latest commit statuses" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user) + expect(response.status).to eq(200) + + expect(json_response).to be_an Array + expect(statuses_id).to contain_exactly(@status3.id, @status4.id, @status5.id, @status6.id) + end + + it "should return all commit statuses" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?all=1", user) + expect(response.status).to eq(200) + + expect(json_response).to be_an Array + expect(statuses_id).to contain_exactly(@status1.id, @status2.id, @status3.id, @status4.id, @status5.id, @status6.id) + end + + it "should return latest commit statuses for specific ref" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?ref=develop", user) + expect(response.status).to eq(200) + + expect(json_response).to be_an Array + expect(statuses_id).to contain_exactly(@status3.id, @status5.id) + end + + it "should return latest commit statuses for specific name" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?name=coverage", user) + expect(response.status).to eq(200) + + expect(json_response).to be_an Array + expect(statuses_id).to contain_exactly(@status3.id, @status4.id) + end + end + + context "guest user" do + it "should not return project commits" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user2) + expect(response.status).to eq(403) + end + end + + context "unauthorized user" do + it "should not return project commits" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses") + expect(response.status).to eq(401) + end + end + end + + describe 'POST /projects/:id/statuses/:sha' do + let(:post_url) { "/projects/#{project.id}/statuses/#{commit.id}" } + + context 'reporter user' do + context 'should create commit status' do + it 'with only required parameters' do + post api(post_url, user), state: 'success' + expect(response.status).to eq(201) + expect(json_response['sha']).to eq(commit.id) + expect(json_response['status']).to eq('success') + expect(json_response['name']).to eq('default') + expect(json_response['ref']).to be_nil + expect(json_response['target_url']).to be_nil + expect(json_response['description']).to be_nil + end + + it 'with all optional parameters' do + post api(post_url, user), state: 'success', context: 'coverage', ref: 'develop', target_url: 'url', description: 'test' + expect(response.status).to eq(201) + expect(json_response['sha']).to eq(commit.id) + expect(json_response['status']).to eq('success') + expect(json_response['name']).to eq('coverage') + expect(json_response['ref']).to eq('develop') + expect(json_response['target_url']).to eq('url') + expect(json_response['description']).to eq('test') + end + end + + context 'should not create commit status' do + it 'with invalid state' do + post api(post_url, user), state: 'invalid' + expect(response.status).to eq(400) + end + + it 'without state' do + post api(post_url, user) + expect(response.status).to eq(400) + end + + it 'invalid commit' do + post api("/projects/#{project.id}/statuses/invalid_sha", user), state: 'running' + expect(response.status).to eq(404) + end + end + end + + context 'guest user' do + it 'should not create commit status' do + post api(post_url, user2) + expect(response.status).to eq(403) + end + end + + context 'unauthorized user' do + it 'should not create commit status' do + post api(post_url) + expect(response.status).to eq(401) + end + end + end +end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index a1c248c636e..49acc3368f4 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -47,6 +47,19 @@ describe API::API, api: true do get api("/projects/#{project.id}/repository/commits/invalid_sha", user) expect(response.status).to eq(404) end + + it "should return not_found for CI status" do + get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) + expect(response.status).to eq(200) + expect(json_response['status']).to eq('not_found') + end + + it "should return status for CI" do + ci_commit = project.ensure_ci_commit(project.repository.commit.sha) + get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) + expect(response.status).to eq(200) + expect(json_response['status']).to eq(ci_commit.status) + end end context "unauthorized user" do diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 35b3d3e296a..a68c7b1e461 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -379,9 +379,14 @@ describe API::API, api: true do describe "POST /projects/:id/merge_request/:merge_request_id/comments" do it "should return comment" do + original_count = merge_request.notes.size + post api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user), note: "My comment" expect(response.status).to eq(201) expect(json_response['note']).to eq('My comment') + expect(json_response['author']['name']).to eq(user.name) + expect(json_response['author']['username']).to eq(user.username) + expect(merge_request.notes.size).to eq(original_count + 1) end it "should return 400 if note is missing" do diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index 09a79553f72..1149f7e7989 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -166,24 +166,21 @@ describe API::API, api: true do get api("/projects/#{project.id}/repository/archive", user) repo_name = project.repository.name.gsub("\.git", "") expect(response.status).to eq(200) - expect(response.headers['Content-Disposition']).to match(/filename\=\"#{repo_name}\-[^\.]+\.tar.gz\"/) - expect(response.content_type).to eq(MIME::Types.type_for('file.tar.gz').first.content_type) + expect(json_response['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.tar.gz/) end it "should get the archive.zip" do get api("/projects/#{project.id}/repository/archive.zip", user) repo_name = project.repository.name.gsub("\.git", "") expect(response.status).to eq(200) - expect(response.headers['Content-Disposition']).to match(/filename\=\"#{repo_name}\-[^\.]+\.zip\"/) - expect(response.content_type).to eq(MIME::Types.type_for('file.zip').first.content_type) + expect(json_response['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.zip/) end it "should get the archive.tar.bz2" do get api("/projects/#{project.id}/repository/archive.tar.bz2", user) repo_name = project.repository.name.gsub("\.git", "") expect(response.status).to eq(200) - expect(response.headers['Content-Disposition']).to match(/filename\=\"#{repo_name}\-[^\.]+\.tar.bz2\"/) - expect(response.content_type).to eq(MIME::Types.type_for('file.tar.bz2').first.content_type) + expect(json_response['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.tar.bz2/) end it "should return 404 for invalid sha" do diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb index 9aa60826f21..c0226605a23 100644 --- a/spec/requests/api/services_spec.rb +++ b/spec/requests/api/services_spec.rb @@ -3,6 +3,8 @@ require "spec_helper" describe API::API, api: true do include ApiHelpers let(:user) { create(:user) } + let(:admin) { create(:admin) } + let(:user2) { create(:user) } let(:project) {create(:project, creator_id: user.id, namespace: user.namespace) } Service.available_services_names.each do |service| @@ -51,11 +53,40 @@ describe API::API, api: true do describe "GET /projects/:id/services/#{service.dasherize}" do include_context service - it "should get #{service} settings" do + # inject some properties into the service + before do + project.build_missing_services + service_object = project.send(service_method) + service_object.properties = service_attrs + service_object.save + end + + it 'should return authentication error when unauthenticated' do + get api("/projects/#{project.id}/services/#{dashed_service}") + expect(response.status).to eq(401) + end + + it "should return all properties of service #{service} when authenticated as admin" do + get api("/projects/#{project.id}/services/#{dashed_service}", admin) + + expect(response.status).to eq(200) + expect(json_response['properties'].keys.map(&:to_sym)).to match_array(service_attrs_list.map) + end + + it "should return properties of service #{service} other than passwords when authenticated as project owner" do get api("/projects/#{project.id}/services/#{dashed_service}", user) expect(response.status).to eq(200) + expect(json_response['properties'].keys.map(&:to_sym)).to match_array(service_attrs_list_without_passwords) end + + it "should return error when authenticated but not a project owner" do + project.team << [user2, :developer] + get api("/projects/#{project.id}/services/#{dashed_service}", user2) + + expect(response.status).to eq(403) + end + end end end diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index bad250fbf48..88218a93e1f 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -7,6 +7,10 @@ describe Ci::API::API do let(:project) { FactoryGirl.create(:ci_project) } let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } + before do + stub_ci_commit_to_return_yaml_file + end + describe "Builds API for runners" do let(:shared_runner) { FactoryGirl.create(:ci_runner, token: "SharedRunner") } let(:shared_project) { FactoryGirl.create(:ci_project, name: "SharedProject") } @@ -19,7 +23,7 @@ describe Ci::API::API do describe "POST /builds/register" do it "should start a build" do commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) - commit.create_builds + commit.create_builds('master', false, nil) build = commit.builds.first post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } @@ -55,7 +59,7 @@ describe Ci::API::API do it "returns options" do commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) - commit.create_builds + commit.create_builds('master', false, nil) post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } @@ -65,13 +69,15 @@ describe Ci::API::API do it "returns variables" do commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) - commit.create_builds + commit.create_builds('master', false, nil) project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } expect(response.status).to eq(201) expect(json_response["variables"]).to eq([ + { "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true }, + { "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true }, { "key" => "DB_NAME", "value" => "postgres", "public" => true }, { "key" => "SECRET_KEY", "value" => "secret_value", "public" => false }, ]) @@ -82,13 +88,16 @@ describe Ci::API::API do commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, commit: commit, trigger: trigger) - commit.create_builds(trigger_request) + commit.create_builds('master', false, nil, trigger_request) project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } expect(response.status).to eq(201) expect(json_response["variables"]).to eq([ + { "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true }, + { "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true }, + { "key" => "CI_BUILD_TRIGGERED", "value" => "true", "public" => true }, { "key" => "DB_NAME", "value" => "postgres", "public" => true }, { "key" => "SECRET_KEY", "value" => "secret_value", "public" => false }, { "key" => "TRIGGER_KEY", "value" => "TRIGGER_VALUE", "public" => false }, diff --git a/spec/requests/ci/api/commits_spec.rb b/spec/requests/ci/api/commits_spec.rb index a41c321a300..6049135fd10 100644 --- a/spec/requests/ci/api/commits_spec.rb +++ b/spec/requests/ci/api/commits_spec.rb @@ -44,8 +44,7 @@ describe Ci::API::API, 'Commits' do "email" => "jordi@softcatala.org", } } - ], - ci_yaml_file: gitlab_ci_yaml + ] } end diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb index bbe98e7dacd..93617fc4b3f 100644 --- a/spec/requests/ci/api/triggers_spec.rb +++ b/spec/requests/ci/api/triggers_spec.rb @@ -5,8 +5,8 @@ describe Ci::API::API do describe 'POST /projects/:project_id/refs/:ref/trigger' do let!(:trigger_token) { 'secure token' } - let!(:project) { FactoryGirl.create(:ci_project) } - let!(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } + let!(:gl_project) { FactoryGirl.create(:project) } + let!(:project) { FactoryGirl.create(:ci_project, gl_project: gl_project) } let!(:project2) { FactoryGirl.create(:ci_project) } let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) } let(:options) do @@ -15,6 +15,10 @@ describe Ci::API::API do } end + before do + stub_ci_commit_to_return_yaml_file + end + context 'Handles errors' do it 'should return bad request if token is missing' do post ci_api("/projects/#{project.id}/refs/master/trigger") @@ -33,15 +37,13 @@ describe Ci::API::API do end context 'Have a commit' do - before do - @commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) - end + let(:commit) { project.commits.last } it 'should create builds' do post ci_api("/projects/#{project.id}/refs/master/trigger"), options expect(response.status).to eq(201) - @commit.builds.reload - expect(@commit.builds.size).to eq(2) + commit.builds.reload + expect(commit.builds.size).to eq(2) end it 'should return bad request with no builds created if there\'s no commit for that ref' do @@ -70,8 +72,8 @@ describe Ci::API::API do it 'create trigger request with variables' do post ci_api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: variables) expect(response.status).to eq(201) - @commit.builds.reload - expect(@commit.builds.first.trigger_request.variables).to eq(variables) + commit.builds.reload + expect(commit.builds.first.trigger_request.variables).to eq(variables) end end end diff --git a/spec/requests/ci/builds_spec.rb b/spec/requests/ci/builds_spec.rb deleted file mode 100644 index f68116c52aa..00000000000 --- a/spec/requests/ci/builds_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'spec_helper' - -describe "Builds" do - before do - @commit = FactoryGirl.create :ci_commit - @build = FactoryGirl.create :ci_build, commit: @commit - end - - describe "GET /:project/builds/:id/status.json" do - before do - get status_ci_project_build_path(@commit.project, @build), format: :json - end - - it { expect(response.status).to eq(200) } - it { expect(response.body).to include(@build.sha) } - end -end diff --git a/spec/requests/ci/commits_spec.rb b/spec/requests/ci/commits_spec.rb deleted file mode 100644 index 3ab8c915dfd..00000000000 --- a/spec/requests/ci/commits_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'spec_helper' - -describe "Commits" do - before do - @commit = FactoryGirl.create :ci_commit - end - - describe "GET /:project/refs/:ref_name/commits/:id/status.json" do - before do - get status_ci_project_ref_commits_path(@commit.project, @commit.ref, @commit.sha), format: :json - end - - it { expect(response.status).to eq(200) } - it { expect(response.body).to include(@commit.sha) } - end -end diff --git a/spec/services/archive_repository_service_spec.rb b/spec/services/archive_repository_service_spec.rb index 0ec70c51b3a..1cc7b240216 100644 --- a/spec/services/archive_repository_service_spec.rb +++ b/spec/services/archive_repository_service_spec.rb @@ -13,7 +13,7 @@ describe ArchiveRepositoryService do context "when the repository doesn't have an archive file path" do before do - allow(project.repository).to receive(:archive_file_path).and_return(nil) + allow(project.repository).to receive(:archive_metadata).and_return(Hash.new) end it "raises an error" do @@ -21,70 +21,5 @@ describe ArchiveRepositoryService do end end - context "when the repository has an archive file path" do - let(:file_path) { "/archive.zip" } - let(:pid_file_path) { "/archive.zip.pid" } - - before do - allow(project.repository).to receive(:archive_file_path).and_return(file_path) - allow(project.repository).to receive(:archive_pid_file_path).and_return(pid_file_path) - end - - context "when the archive file already exists" do - before do - allow(File).to receive(:exist?).with(file_path).and_return(true) - end - - it "returns the file path" do - expect(subject.execute(timeout: 0.0)).to eq(file_path) - end - end - - context "when the archive file doesn't exist yet" do - before do - allow(File).to receive(:exist?).with(file_path).and_return(false) - allow(File).to receive(:exist?).with(pid_file_path).and_return(true) - end - - context "when the archive pid file doesn't exist yet" do - before do - allow(File).to receive(:exist?).with(pid_file_path).and_return(false) - end - - it "queues the RepositoryArchiveWorker" do - expect(RepositoryArchiveWorker).to receive(:perform_async) - - subject.execute(timeout: 0.0) - end - end - - context "when the archive pid file already exists" do - it "doesn't queue the RepositoryArchiveWorker" do - expect(RepositoryArchiveWorker).not_to receive(:perform_async) - - subject.execute(timeout: 0.0) - end - end - - context "when the archive file exists after a little while" do - before do - Thread.new do - sleep 0.1 - allow(File).to receive(:exist?).with(file_path).and_return(true) - end - end - - it "returns the file path" do - expect(subject.execute(timeout: 0.2)).to eq(file_path) - end - end - - context "when the archive file doesn't exist after the timeout" do - it "returns nil" do - expect(subject.execute(timeout: 0.0)).to eq(nil) - end - end - end - end end end diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb index 84ab0a615dd..e3a8fe9681b 100644 --- a/spec/services/ci/create_commit_service_spec.rb +++ b/spec/services/ci/create_commit_service_spec.rb @@ -4,15 +4,19 @@ module Ci describe CreateCommitService do let(:service) { CreateCommitService.new } let(:project) { FactoryGirl.create(:ci_project) } + let(:user) { nil } + + before do + stub_ci_commit_to_return_yaml_file + end describe :execute do context 'valid params' do let(:commit) do - service.execute(project, + service.execute(project, user, ref: 'refs/heads/master', before: '00000000', after: '31das312', - ci_yaml_file: gitlab_ci_yaml, commits: [ { message: "Message" } ] ) end @@ -26,11 +30,10 @@ module Ci context "skip tag if there is no build for it" do it "creates commit if there is appropriate job" do - result = service.execute(project, + result = service.execute(project, user, ref: 'refs/tags/0_1', before: '00000000', after: '31das312', - ci_yaml_file: gitlab_ci_yaml, commits: [ { message: "Message" } ] ) expect(result).to be_persisted @@ -38,12 +41,12 @@ module Ci it "creates commit if there is no appropriate job but deploy job has right ref setting" do config = YAML.dump({ deploy: { deploy: "ls", only: ["0_1"] } }) + stub_ci_commit_yaml_file(config) - result = service.execute(project, + result = service.execute(project, user, ref: 'refs/heads/0_1', before: '00000000', after: '31das312', - ci_yaml_file: config, commits: [ { message: "Message" } ] ) expect(result).to be_persisted @@ -51,11 +54,11 @@ module Ci end it 'fails commits without .gitlab-ci.yml' do - result = service.execute(project, + stub_ci_commit_yaml_file(nil) + result = service.execute(project, user, ref: 'refs/heads/0_1', before: '00000000', after: '31das312', - ci_yaml_file: config, commits: [ { message: 'Message' } ] ) expect(result).to be_persisted @@ -64,41 +67,46 @@ module Ci end describe :ci_skip? do + let(:message) { "some message[ci skip]" } + + before do + allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message } + end + it "skips builds creation if there is [ci skip] tag in commit message" do - commits = [{ message: "some message[ci skip]" }] - commit = service.execute(project, + commits = [{ message: message }] + commit = service.execute(project, user, ref: 'refs/tags/0_1', before: '00000000', after: '31das312', - commits: commits, - ci_yaml_file: gitlab_ci_yaml + commits: commits ) expect(commit.builds.any?).to be false expect(commit.status).to eq("skipped") end it "does not skips builds creation if there is no [ci skip] tag in commit message" do - commits = [{ message: "some message" }] + allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { "some message" } - commit = service.execute(project, + commits = [{ message: "some message" }] + commit = service.execute(project, user, ref: 'refs/tags/0_1', before: '00000000', after: '31das312', - commits: commits, - ci_yaml_file: gitlab_ci_yaml + commits: commits ) expect(commit.builds.first.name).to eq("staging") end it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do - commits = [{ message: "some message[ci skip]" }] - commit = service.execute(project, + stub_ci_commit_yaml_file('invalid: file') + commits = [{ message: message }] + commit = service.execute(project, user, ref: 'refs/tags/0_1', before: '00000000', after: '31das312', - commits: commits, - ci_yaml_file: "invalid: file" + commits: commits ) expect(commit.builds.any?).to be false expect(commit.status).to eq("skipped") @@ -106,35 +114,36 @@ module Ci end it "skips build creation if there are already builds" do + allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file) { gitlab_ci_yaml } + commits = [{ message: "message" }] - commit = service.execute(project, + commit = service.execute(project, user, ref: 'refs/heads/master', before: '00000000', after: '31das312', - commits: commits, - ci_yaml_file: gitlab_ci_yaml + commits: commits ) expect(commit.builds.count(:all)).to eq(2) - commit = service.execute(project, + commit = service.execute(project, user, ref: 'refs/heads/master', before: '00000000', after: '31das312', - commits: commits, - ci_yaml_file: gitlab_ci_yaml + commits: commits ) expect(commit.builds.count(:all)).to eq(2) end it "creates commit with failed status if yaml is invalid" do + stub_ci_commit_yaml_file('invalid: file') + commits = [{ message: "some message" }] - commit = service.execute(project, + commit = service.execute(project, user, ref: 'refs/tags/0_1', before: '00000000', after: '31das312', - commits: commits, - ci_yaml_file: "invalid: file" + commits: commits ) expect(commit.status).to eq("failed") diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb index 525a24cc200..fcafae38644 100644 --- a/spec/services/ci/create_trigger_request_service_spec.rb +++ b/spec/services/ci/create_trigger_request_service_spec.rb @@ -2,20 +2,20 @@ require 'spec_helper' describe Ci::CreateTriggerRequestService do let(:service) { Ci::CreateTriggerRequestService.new } - let(:project) { FactoryGirl.create :ci_project } - let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } - let(:trigger) { FactoryGirl.create :ci_trigger, project: project } + let(:gl_project) { create(:project) } + let(:project) { create(:ci_project, gl_project: gl_project) } + let(:trigger) { create(:ci_trigger, project: project) } + + before do + stub_ci_commit_to_return_yaml_file + end describe :execute do context 'valid params' do subject { service.execute(project, trigger, 'master') } - before do - @commit = FactoryGirl.create :ci_commit, gl_project: gl_project - end - it { expect(subject).to be_kind_of(Ci::TriggerRequest) } - it { expect(subject.commit).to eq(@commit) } + it { expect(subject.builds.first).to be_kind_of(Ci::Build) } end context 'no commit for ref' do @@ -28,26 +28,11 @@ describe Ci::CreateTriggerRequestService do subject { service.execute(project, trigger, 'master') } before do - FactoryGirl.create :ci_commit_without_jobs, gl_project: gl_project + stub_ci_commit_yaml_file('{}') + FactoryGirl.create :ci_commit, gl_project: gl_project end it { expect(subject).to be_nil } end - - context 'for multiple commits' do - subject { service.execute(project, trigger, 'master') } - - before do - @commit1 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: gl_project - @commit2 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: gl_project - @commit3 = FactoryGirl.create :ci_commit, committed_at: 3.hour.ago, gl_project: gl_project - end - - context 'retries latest one' do - it { expect(subject).to be_kind_of(Ci::TriggerRequest) } - it { expect(subject).to be_persisted } - it { expect(subject.commit).to eq(@commit2) } - end - end end end diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index c483060fd73..17015d29e51 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -112,6 +112,14 @@ describe GitPushService do it { expect(@event.project).to eq(project) } it { expect(@event.action).to eq(Event::PUSHED) } it { expect(@event.data).to eq(service.push_data) } + + context "Updates merge requests" do + it "when pushing a new branch for the first time" do + expect(project).to receive(:update_merge_requests). + with(@blankrev, 'newrev', 'refs/heads/master', user) + service.execute(project, user, @blankrev, 'newrev', 'refs/heads/master') + end + end end describe "Web Hooks" do @@ -155,7 +163,7 @@ describe GitPushService do before do allow(commit).to receive_messages( - safe_message: "this commit \n mentions ##{issue.id}", + safe_message: "this commit \n mentions #{issue.to_reference}", references: [issue], author_name: commit_author.name, author_email: commit_author.email diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb index 9516e7936d8..227ac995ec2 100644 --- a/spec/services/merge_requests/refresh_service_spec.rb +++ b/spec/services/merge_requests/refresh_service_spec.rb @@ -106,6 +106,27 @@ describe MergeRequests::RefreshService do it { expect(@fork_merge_request.notes).to be_empty } end + context 'push new branch that exists in a merge request' do + let(:refresh_service) { service.new(@fork_project, @user) } + + it 'refreshes the merge request' do + expect(refresh_service).to receive(:execute_hooks). + with(@fork_merge_request, 'update') + allow_any_instance_of(Repository).to receive(:merge_base).and_return(@oldrev) + + refresh_service.execute(Gitlab::Git::BLANK_SHA, @newrev, 'refs/heads/master') + reload_mrs + + expect(@merge_request.notes).to be_empty + expect(@merge_request).to be_open + + notes = @fork_merge_request.notes.reorder(:created_at).map(&:note) + expect(notes[0]).to include('Restored source branch `master`') + expect(notes[1]).to include('Added 4 commits') + expect(@fork_merge_request).to be_open + end + end + def reload_mrs @merge_request.reload @fork_merge_request.reload diff --git a/spec/services/projects/download_service_spec.rb b/spec/services/projects/download_service_spec.rb index f12e09c58c3..ddee2e62dfc 100644 --- a/spec/services/projects/download_service_spec.rb +++ b/spec/services/projects/download_service_spec.rb @@ -37,7 +37,6 @@ describe Projects::DownloadService do it { expect(@link_to_file).to have_key('url') } it { expect(@link_to_file).to have_key('is_image') } it { expect(@link_to_file['is_image']).to be true } - it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") } it { expect(@link_to_file['url']).to match('rails_sample.jpg') } it { expect(@link_to_file['alt']).to eq('rails_sample') } end @@ -52,7 +51,6 @@ describe Projects::DownloadService do it { expect(@link_to_file).to have_key('url') } it { expect(@link_to_file).to have_key('is_image') } it { expect(@link_to_file['is_image']).to be false } - it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") } it { expect(@link_to_file['url']).to match('doc_sample.txt') } it { expect(@link_to_file['alt']).to eq('doc_sample.txt') } end diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index bb7da33b12e..47755bfc990 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -7,6 +7,8 @@ describe Projects::TransferService do context 'namespace -> namespace' do before do + allow_any_instance_of(Gitlab::UploadsTransfer). + to receive(:move_project).and_return(true) group.add_owner(user) @result = transfer_project(project, user, group) end diff --git a/spec/services/projects/upload_service_spec.rb b/spec/services/projects/upload_service_spec.rb index fa4ff6b01ad..1b1a80d1fe7 100644 --- a/spec/services/projects/upload_service_spec.rb +++ b/spec/services/projects/upload_service_spec.rb @@ -18,7 +18,6 @@ describe Projects::UploadService do it { expect(@link_to_file).to have_key(:is_image) } it { expect(@link_to_file).to have_value('banana_sample') } it { expect(@link_to_file[:is_image]).to equal(true) } - it { expect(@link_to_file[:url]).to match("/#{@project.path_with_namespace}") } it { expect(@link_to_file[:url]).to match('banana_sample.gif') } end @@ -34,7 +33,6 @@ describe Projects::UploadService do it { expect(@link_to_file).to have_value('dk') } it { expect(@link_to_file).to have_key(:is_image) } it { expect(@link_to_file[:is_image]).to equal(true) } - it { expect(@link_to_file[:url]).to match("/#{@project.path_with_namespace}") } it { expect(@link_to_file[:url]).to match('dk.png') } end @@ -49,7 +47,6 @@ describe Projects::UploadService do it { expect(@link_to_file).to have_key(:is_image) } it { expect(@link_to_file).to have_value('rails_sample') } it { expect(@link_to_file[:is_image]).to equal(true) } - it { expect(@link_to_file[:url]).to match("/#{@project.path_with_namespace}") } it { expect(@link_to_file[:url]).to match('rails_sample.jpg') } end @@ -64,7 +61,6 @@ describe Projects::UploadService do it { expect(@link_to_file).to have_key(:is_image) } it { expect(@link_to_file).to have_value('doc_sample.txt') } it { expect(@link_to_file[:is_image]).to equal(false) } - it { expect(@link_to_file[:url]).to match("/#{@project.path_with_namespace}") } it { expect(@link_to_file[:url]).to match('doc_sample.txt') } end diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb index 48c49e2f717..a31fc1e4b07 100644 --- a/spec/services/system_hooks_service_spec.rb +++ b/spec/services/system_hooks_service_spec.rb @@ -13,8 +13,8 @@ describe SystemHooksService do it { expect(event_data(user, :destroy)).to include(:event_name, :name, :created_at, :email, :user_id) } it { expect(event_data(project, :create)).to include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } it { expect(event_data(project, :destroy)).to include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } - it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :access_level, :project_visibility) } - it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :access_level, :project_visibility) } + it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :access_level, :project_visibility) } + it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :access_level, :project_visibility) } it { expect(event_data(key, :create)).to include(:username, :key, :id) } it { expect(event_data(key, :destroy)).to include(:username, :key, :id) } diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 2658576640c..a45130bd473 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -242,6 +242,18 @@ describe SystemNoteService do end end + describe '.change_branch_presence' do + subject { described_class.change_branch_presence(noteable, project, author, :source, 'feature', :delete) } + + it_behaves_like 'a system note' + + context 'when source branch deleted' do + it 'sets the note text' do + expect(subject.note).to eq "Deleted source branch `feature`" + end + end + end + describe '.cross_reference' do subject { described_class.cross_reference(noteable, mentioner, author) } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index dfe855926c6..2be13bb3e6a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -14,6 +14,7 @@ require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' require 'shoulda/matchers' require 'sidekiq/testing/inline' +require 'benchmark/ips' # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. @@ -32,7 +33,7 @@ RSpec.configure do |config| config.include TestEnv config.include StubGitlabCalls config.include StubGitlabData - + config.include BenchmarkMatchers, benchmark: true config.infer_spec_type_from_file_location! config.raise_errors_for_deprecations! @@ -42,4 +43,8 @@ RSpec.configure do |config| end end +FactoryGirl::SyntaxRunner.class_eval do + include RSpec::Mocks::ExampleMethods +end + ActiveRecord::Migration.maintain_test_schema! diff --git a/spec/support/filter_spec_helper.rb b/spec/support/filter_spec_helper.rb index 203117aee70..97e5c270a59 100644 --- a/spec/support/filter_spec_helper.rb +++ b/spec/support/filter_spec_helper.rb @@ -29,12 +29,19 @@ module FilterSpecHelper # # Returns the Hash def pipeline_result(body, contexts = {}) - contexts.reverse_merge!(project: project) + contexts.reverse_merge!(project: project) if defined?(project) pipeline = HTML::Pipeline.new([described_class], contexts) pipeline.call(body) end + def reference_pipeline_result(body, contexts = {}) + contexts.reverse_merge!(project: project) if defined?(project) + + pipeline = HTML::Pipeline.new([described_class, Gitlab::Markdown::ReferenceGathererFilter], contexts) + pipeline.call(body) + end + # Modify a String reference to make it invalid # # Commit SHAs get reversed, IDs get incremented by 1, all other Strings get @@ -55,20 +62,6 @@ module FilterSpecHelper end end - # Stub CrossProjectReference#user_can_reference_project? to return true for - # the current test - def allow_cross_reference! - allow_any_instance_of(described_class). - to receive(:user_can_reference_project?).and_return(true) - end - - # Stub CrossProjectReference#user_can_reference_project? to return false for - # the current test - def disallow_cross_reference! - allow_any_instance_of(described_class). - to receive(:user_can_reference_project?).and_return(false) - end - # Shortcut to Rails' auto-generated routes helpers, to avoid including the # module def urls diff --git a/spec/support/markdown_feature.rb b/spec/support/markdown_feature.rb index 39a64391460..bedc1a7f1db 100644 --- a/spec/support/markdown_feature.rb +++ b/spec/support/markdown_feature.rb @@ -15,18 +15,17 @@ class MarkdownFeature end def group - unless @group - @group = create(:group) - @group.add_developer(user) + @group ||= create(:group).tap do |group| + group.add_developer(user) end - - @group end # Direct references ---------------------------------------------------------- def project - @project ||= create(:project) + @project ||= create(:project).tap do |project| + project.team << [user, :master] + end end def issue @@ -46,12 +45,10 @@ class MarkdownFeature end def commit_range - unless @commit_range + @commit_range ||= begin commit2 = project.commit('HEAD~3') - @commit_range = CommitRange.new("#{commit.id}...#{commit2.id}", project) + CommitRange.new("#{commit.id}...#{commit2.id}", project) end - - @commit_range end def simple_label @@ -65,13 +62,12 @@ class MarkdownFeature # Cross-references ----------------------------------------------------------- def xproject - unless @xproject + @xproject ||= begin namespace = create(:namespace, name: 'cross-reference') - @xproject = create(:project, namespace: namespace) - @xproject.team << [user, :developer] + create(:project, namespace: namespace) do |project| + project.team << [user, :developer] + end end - - @xproject end def xissue @@ -91,12 +87,10 @@ class MarkdownFeature end def xcommit_range - unless @xcommit_range + @xcommit_range ||= begin xcommit2 = xproject.commit('HEAD~2') - @xcommit_range = CommitRange.new("#{xcommit.id}...#{xcommit2.id}", xproject) + CommitRange.new("#{xcommit.id}...#{xcommit2.id}", xproject) end - - @xcommit_range end def raw_markdown diff --git a/spec/support/matchers/benchmark_matchers.rb b/spec/support/matchers/benchmark_matchers.rb new file mode 100644 index 00000000000..84f655c2119 --- /dev/null +++ b/spec/support/matchers/benchmark_matchers.rb @@ -0,0 +1,61 @@ +module BenchmarkMatchers + extend RSpec::Matchers::DSL + + def self.included(into) + into.extend(ClassMethods) + end + + matcher :iterate_per_second do |min_iterations| + supports_block_expectations + + match do |block| + @max_stddev ||= 30 + + @entry = benchmark(&block) + + expect(@entry.ips).to be >= min_iterations + expect(@entry.stddev_percentage).to be <= @max_stddev + end + + chain :with_maximum_stddev do |value| + @max_stddev = value + end + + description do + "run at least #{min_iterations} iterations per second" + end + + failure_message do + ips = @entry.ips.round(2) + stddev = @entry.stddev_percentage.round(2) + + "expected at least #{min_iterations} iterations per second " \ + "with a maximum stddev of #{@max_stddev}%, instead of " \ + "#{ips} iterations per second with a stddev of #{stddev}%" + end + end + + # Benchmarks the given block and returns a Benchmark::IPS::Report::Entry. + def benchmark(&block) + report = Benchmark.ips(quiet: true) do |bench| + bench.report do + instance_eval(&block) + end + end + + report.entries[0] + end + + module ClassMethods + # Wraps around rspec's subject method so you can write: + # + # benchmark_subject { SomeClass.some_method } + # + # instead of: + # + # subject { -> { SomeClass.some_method } } + def benchmark_subject(&block) + subject { block } + end + end +end diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb index e3de0afb448..3bb568f4d49 100644 --- a/spec/support/mentionable_shared_examples.rb +++ b/spec/support/mentionable_shared_examples.rb @@ -5,7 +5,7 @@ # - let(:set_mentionable_text) { lambda { |txt| "block that assigns txt to the subject's mentionable_text" } } def common_mentionable_setup - let(:project) { create :project } + let(:project) { subject.project } let(:author) { subject.author } let(:mentioned_issue) { create(:issue, project: project) } @@ -50,6 +50,8 @@ def common_mentionable_setup } extra_commits.each { |c| commitmap[c.short_id] = c } + allow(Project).to receive(:find).and_call_original + allow(Project).to receive(:find).with(project.id.to_s).and_return(project) allow(project.repository).to receive(:commit) { |sha| commitmap[sha] } set_mentionable_text.call(ref_string) @@ -65,7 +67,7 @@ shared_examples 'a mentionable' do it "extracts references from its reference property" do # De-duplicate and omit itself - refs = subject.references(project) + refs = subject.referenced_mentionables expect(refs.size).to eq(6) expect(refs).to include(mentioned_issue) expect(refs).to include(mentioned_mr) @@ -84,14 +86,7 @@ shared_examples 'a mentionable' do with(referenced, subject.local_reference, author) end - subject.create_cross_references!(project, author) - end - - it 'detects existing cross-references' do - SystemNoteService.cross_reference(mentioned_issue, subject.local_reference, author) - - expect(subject).to have_mentioned(mentioned_issue) - expect(subject).not_to have_mentioned(mentioned_mr) + subject.create_cross_references! end end @@ -143,6 +138,6 @@ shared_examples 'an editable mentionable' do end set_mentionable_text.call(new_text) - subject.create_new_cross_references!(project, author) + subject.create_new_cross_references!(author) end end diff --git a/spec/support/services_shared_context.rb b/spec/support/services_shared_context.rb index 4d007ae55ee..d1c999cad4d 100644 --- a/spec/support/services_shared_context.rb +++ b/spec/support/services_shared_context.rb @@ -3,7 +3,13 @@ Service.available_services_names.each do |service| let(:dashed_service) { service.dasherize } let(:service_method) { "#{service}_service".to_sym } let(:service_klass) { "#{service}_service".classify.constantize } - let(:service_attrs_list) { service_klass.new.fields.inject([]) {|arr, hash| arr << hash[:name].to_sym } } + let(:service_fields) { service_klass.new.fields } + let(:service_attrs_list) { service_fields.inject([]) {|arr, hash| arr << hash[:name].to_sym } } + let(:service_attrs_list_without_passwords) do + service_fields. + select { |field| field[:type] != 'password' }. + map { |field| field[:name].to_sym} + end let(:service_attrs) do service_attrs_list.inject({}) do |hash, k| if k =~ /^(token*|.*_token|.*_key)/ diff --git a/spec/support/setup_builds_storage.rb b/spec/support/setup_builds_storage.rb index a3e59646187..a4f21e95338 100644 --- a/spec/support/setup_builds_storage.rb +++ b/spec/support/setup_builds_storage.rb @@ -10,8 +10,10 @@ RSpec.configure do |config| end config.after(:suite) do - Dir.chdir(builds_path) do - `ls | grep -v .gitkeep | xargs rm -r` + Dir[File.join(builds_path, '*')].each do |path| + next if File.basename(path) == '.gitkeep' + + FileUtils.rm_rf(path) end end end diff --git a/spec/support/stub_gitlab_calls.rb b/spec/support/stub_gitlab_calls.rb index 5e6744afda1..5b3eb1bfc5f 100644 --- a/spec/support/stub_gitlab_calls.rb +++ b/spec/support/stub_gitlab_calls.rb @@ -13,6 +13,14 @@ module StubGitlabCalls allow_any_instance_of(Network).to receive(:projects) { project_hash_array } end + def stub_ci_commit_to_return_yaml_file + stub_ci_commit_yaml_file(gitlab_ci_yaml) + end + + def stub_ci_commit_yaml_file(ci_yaml) + allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file) { ci_yaml } + end + private def gitlab_url diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 3eab74ba986..d12ba25b71b 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -9,7 +9,7 @@ module TestEnv 'flatten-dir' => 'e56497b', 'feature' => '0b4bc9a', 'feature_conflict' => 'bb5206f', - 'fix' => '12d65c8', + 'fix' => '48f0be4', 'improve/awesome' => '5937ac0', 'markdown' => '0ed8c6c', 'master' => '5937ac0', diff --git a/spec/workers/repository_archive_worker_spec.rb b/spec/workers/repository_archive_worker_spec.rb deleted file mode 100644 index a914d0ac8dc..00000000000 --- a/spec/workers/repository_archive_worker_spec.rb +++ /dev/null @@ -1,79 +0,0 @@ -require 'spec_helper' - -describe RepositoryArchiveWorker do - let(:project) { create(:project) } - subject { RepositoryArchiveWorker.new } - - before do - allow(Project).to receive(:find).and_return(project) - end - - describe "#perform" do - it "cleans old archives" do - expect(project.repository).to receive(:clean_old_archives) - - subject.perform(project.id, "master", "zip") - end - - context "when the repository doesn't have an archive file path" do - before do - allow(project.repository).to receive(:archive_file_path).and_return(nil) - end - - it "doesn't archive the repo" do - expect(project.repository).not_to receive(:archive_repo) - - subject.perform(project.id, "master", "zip") - end - end - - context "when the repository has an archive file path" do - let(:file_path) { "/archive.zip" } - let(:pid_file_path) { "/archive.zip.pid" } - - before do - allow(project.repository).to receive(:archive_file_path).and_return(file_path) - allow(project.repository).to receive(:archive_pid_file_path).and_return(pid_file_path) - end - - context "when the archive file already exists" do - before do - allow(File).to receive(:exist?).with(file_path).and_return(true) - end - - it "doesn't archive the repo" do - expect(project.repository).not_to receive(:archive_repo) - - subject.perform(project.id, "master", "zip") - end - end - - context "when the archive file doesn't exist yet" do - before do - allow(File).to receive(:exist?).with(file_path).and_return(false) - allow(File).to receive(:exist?).with(pid_file_path).and_return(true) - end - - context "when the archive pid file doesn't exist yet" do - before do - allow(File).to receive(:exist?).with(pid_file_path).and_return(false) - end - - it "archives the repo" do - expect(project.repository).to receive(:archive_repo) - - subject.perform(project.id, "master", "zip") - end - end - - context "when the archive pid file already exists" do - it "doesn't archive the repo" do - expect(project.repository).not_to receive(:archive_repo) - - subject.perform(project.id, "master", "zip") - end - end - end - end - end -end |