diff options
Diffstat (limited to 'spec/models')
50 files changed, 2311 insertions, 724 deletions
diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb index 635a6e2518c..d45319b25d4 100644 --- a/spec/models/abuse_report_spec.rb +++ b/spec/models/abuse_report_spec.rb @@ -16,4 +16,16 @@ RSpec.describe AbuseReport, type: :model do subject { create(:abuse_report) } it { expect(subject).to be_valid } + + describe 'associations' do + it { is_expected.to belong_to(:reporter).class_name('User') } + it { is_expected.to belong_to(:user) } + end + + describe 'validations' do + it { is_expected.to validate_presence_of(:reporter) } + it { is_expected.to validate_presence_of(:user) } + it { is_expected.to validate_presence_of(:message) } + it { is_expected.to validate_uniqueness_of(:user_id) } + end end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index de0b2ef4cda..b67b84959d9 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -23,16 +23,36 @@ # after_sign_out_path :string(255) # session_expire_delay :integer default(10080), not null # import_sources :text +# help_page_text :text +# admin_notification_email :string(255) +# shared_runners_enabled :boolean default(TRUE), not null +# max_artifacts_size :integer default(100), not null # require 'spec_helper' describe ApplicationSetting, models: true do - it { expect(ApplicationSetting.create_from_defaults).to be_valid } + let(:setting) { ApplicationSetting.create_from_defaults } - context 'restricted signup domains' do - let(:setting) { ApplicationSetting.create_from_defaults } + it { expect(setting).to be_valid } + + describe 'validations' do + let(:http) { 'http://example.com' } + let(:https) { 'https://example.com' } + let(:ftp) { 'ftp://example.com' } + + it { is_expected.to allow_value(nil).for(:home_page_url) } + it { is_expected.to allow_value(http).for(:home_page_url) } + it { is_expected.to allow_value(https).for(:home_page_url) } + it { is_expected.not_to allow_value(ftp).for(:home_page_url) } + + it { is_expected.to allow_value(nil).for(:after_sign_out_path) } + it { is_expected.to allow_value(http).for(:after_sign_out_path) } + it { is_expected.to allow_value(https).for(:after_sign_out_path) } + it { is_expected.not_to allow_value(ftp).for(:after_sign_out_path) } + end + context 'restricted signup domains' do it 'set single domain' do setting.restricted_signup_domains_raw = 'example.com' expect(setting.restricted_signup_domains).to eq(['example.com']) @@ -53,4 +73,26 @@ describe ApplicationSetting, models: true do expect(setting.restricted_signup_domains).to eq(['example.com', '*.example.com']) end end + + context 'shared runners' do + let(:gl_project) { create(:empty_project) } + + before do + allow_any_instance_of(Project).to receive(:current_application_settings).and_return(setting) + end + + subject { gl_project.ensure_gitlab_ci_project.shared_runners_enabled } + + context 'enabled' do + before { setting.update_attributes(shared_runners_enabled: true) } + + it { is_expected.to be_truthy } + end + + context 'disabled' do + before { setting.update_attributes(shared_runners_enabled: false) } + + it { is_expected.to be_falsey } + end + end end diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb index 8ab72151a69..2b325f44f64 100644 --- a/spec/models/broadcast_message_spec.rb +++ b/spec/models/broadcast_message_spec.rb @@ -20,6 +20,21 @@ describe BroadcastMessage do it { is_expected.to be_valid } + describe 'validations' do + let(:triplet) { '#000' } + let(:hex) { '#AABBCC' } + + it { is_expected.to allow_value(nil).for(:color) } + it { is_expected.to allow_value(triplet).for(:color) } + it { is_expected.to allow_value(hex).for(:color) } + it { is_expected.not_to allow_value('000').for(:color) } + + it { is_expected.to allow_value(nil).for(:font) } + it { is_expected.to allow_value(triplet).for(:font) } + it { is_expected.to allow_value(hex).for(:font) } + it { is_expected.not_to allow_value('000').for(:font) } + end + describe :current do it "should return last message if time match" do broadcast_message = create(:broadcast_message, starts_at: Time.now.yesterday, ends_at: Time.now.tomorrow) @@ -27,12 +42,12 @@ describe BroadcastMessage do end it "should return nil if time not come" do - broadcast_message = create(:broadcast_message, starts_at: Time.now.tomorrow, ends_at: Time.now + 2.days) + create(:broadcast_message, starts_at: Time.now.tomorrow, ends_at: Time.now + 2.days) expect(BroadcastMessage.current).to be_nil end it "should return nil if time has passed" do - broadcast_message = create(:broadcast_message, starts_at: Time.now - 2.days, ends_at: Time.now.yesterday) + create(:broadcast_message, starts_at: Time.now - 2.days, ends_at: Time.now.yesterday) expect(BroadcastMessage.current).to be_nil end end diff --git a/spec/models/ci/build_spec.rb b/spec/models/build_spec.rb index ce801152042..839b4c6b16e 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/build_spec.rb @@ -27,16 +27,12 @@ require 'spec_helper' describe Ci::Build do let(:project) { FactoryGirl.create :ci_project } - let(:commit) { FactoryGirl.create :ci_commit, project: project } + 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, 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 @@ -63,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? } @@ -177,37 +107,23 @@ describe Ci::Build do it { is_expected.to include(text) } it { expect(subject.length).to be >= text.length } end - end - - describe :timeout do - subject { build.timeout } - - it { is_expected.to eq(commit.project.timeout) } - end - - describe :duration do - subject { build.duration } - it { is_expected.to eq(120.0) } + context 'if build.trace hides token' do + let(:token) { 'my_secret_token' } - context 'if the building process has not started yet' do before do - build.started_at = nil - build.finished_at = nil + build.project.update_attributes(token: token) + build.update_attributes(trace: token) end - it { is_expected.to be_nil } + it { is_expected.to_not include(token) } end + end - context 'if the building process has started' do - before do - build.started_at = Time.now - 1.minute - build.finished_at = nil - end + describe :timeout do + subject { build.timeout } - it { is_expected.to be_a(Float) } - it { is_expected.to be > 0.0 } - end + it { is_expected.to eq(commit.project.timeout) } end describe :options do @@ -224,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 } @@ -308,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 @@ -327,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 } @@ -337,14 +250,169 @@ 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 + + describe :download_url do + subject { build.download_url } + + it "should be nil if artifact doesn't exist" do + build.update_attributes(artifacts_file: nil) + is_expected.to be_nil + end + + it 'should be nil if artifact exist' do + gif = fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') + build.update_attributes(artifacts_file: gif) + is_expected.to_not be_nil + end end end diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index 586c9dc23a7..a13f6458cac 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -1,39 +1,56 @@ # == Schema Information # -# Table name: commits +# Table name: ci_commits # -# id :integer not null, primary key -# project_id :integer -# ref :string(255) -# sha :string(255) -# before_sha :string(255) -# push_data :text -# created_at :datetime -# updated_at :datetime -# tag :boolean default(FALSE) -# yaml_errors :text -# committed_at :datetime +# id :integer not null, primary key +# project_id :integer +# ref :string(255) +# sha :string(255) +# before_sha :string(255) +# push_data :text +# created_at :datetime +# updated_at :datetime +# tag :boolean default(FALSE) +# yaml_errors :text +# committed_at :datetime +# gl_project_id :integer # require 'spec_helper' describe Ci::Commit do let(:project) { FactoryGirl.create :ci_project } - let(:commit) { FactoryGirl.create :ci_commit, project: project } - let(:commit_with_project) { FactoryGirl.create :ci_commit, project: project } - let(:config_processor) { Ci::GitlabCiYamlProcessor.new(gitlab_ci_yaml) } + let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } + let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } - it { is_expected.to belong_to(:project) } + 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 @@ -51,53 +68,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: '' - commit = FactoryGirl.create :ci_commit, project: 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' - commit = FactoryGirl.create :ci_commit, project: 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' - commit = FactoryGirl.create :ci_commit, project: 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' - commit = FactoryGirl.create :ci_commit, project: 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 @@ -112,63 +88,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 @@ -176,61 +211,179 @@ 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 - let(:project) { FactoryGirl.create :ci_project } - let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:commit) { FactoryGirl.create :ci_commit } it "returns finished_at of latest build" do build = FactoryGirl.create :ci_build, commit: commit, finished_at: Time.now - 60 - build1 = FactoryGirl.create :ci_build, commit: commit, finished_at: Time.now - 120 + FactoryGirl.create :ci_build, commit: commit, finished_at: Time.now - 120 expect(commit.finished_at.to_i).to eq(build.finished_at.to_i) end it "returns nil if there is no finished build" do - build = FactoryGirl.create :ci_not_started_build, commit: commit + FactoryGirl.create :ci_not_started_build, commit: commit expect(commit.finished_at).to be_nil end @@ -238,7 +391,8 @@ describe Ci::Commit do describe "coverage" do let(:project) { FactoryGirl.create :ci_project, coverage_regex: "/.*/" } - let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } + let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } it "calculates average when there are two builds with coverage" do FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit 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 49ac0860259..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,72 +3,37 @@ require 'spec_helper' describe Ci::HipChatMessage do subject { Ci::HipChatMessage.new(build) } - let(:project) { FactoryGirl.create(:ci_project) } + let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) } - context "One build" do - let(:commit) { FactoryGirl.create(:ci_commit_with_one_job, project: project) } - - 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, project: project) } - - 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/project_services/hip_chat_service_spec.rb b/spec/models/ci/project_services/hip_chat_service_spec.rb index 063d46b84d4..d9ccc855edf 100644 --- a/spec/models/ci/project_services/hip_chat_service_spec.rb +++ b/spec/models/ci/project_services/hip_chat_service_spec.rb @@ -33,15 +33,14 @@ describe Ci::HipChatService do describe "Execute" do let(:service) { Ci::HipChatService.new } - let(:project) { FactoryGirl.create :ci_project } - let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:commit) { FactoryGirl.create :ci_commit } let(:build) { FactoryGirl.create :ci_build, commit: commit, status: 'failed' } let(:api_url) { 'https://api.hipchat.com/v2/room/123/notification?auth_token=a1b2c3d4e5f6' } before do allow(service).to receive_messages( - project: project, - project_id: project.id, + project: commit.project, + project_id: commit.project_id, notify_only_broken_builds: false, hipchat_room: 123, hipchat_token: 'a1b2c3d4e5f6' diff --git a/spec/models/ci/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb index b5f37b349db..c03be3ef75f 100644 --- a/spec/models/ci/mail_service_spec.rb +++ b/spec/models/ci/project_services/mail_service_spec.rb @@ -29,11 +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(:commit) { FactoryGirl.create(:ci_commit, project: project) } - let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit) } + 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, user: user) } before do allow(mail).to receive_messages( @@ -42,20 +44,18 @@ describe Ci::MailService do end it do - should_email("git@example.com") - mail.execute(build) - end - - def should_email(email) - expect(Ci::Notify).to receive(:build_fail_email).with(build.id, email) - expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email) + perform_enqueued_jobs do + expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(1) + expect(ActionMailer::Base.deliveries.last.to).to eq(["git@example.com"]) + end end end describe 'successfull build' do let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true, email_only_broken_builds: false) } - let(:commit) { FactoryGirl.create(:ci_commit, project: project) } - let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } + 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, user: user) } before do allow(mail).to receive_messages( @@ -64,13 +64,10 @@ describe Ci::MailService do end it do - should_email("git@example.com") - mail.execute(build) - end - - def should_email(email) - expect(Ci::Notify).to receive(:build_success_email).with(build.id, email) - expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email) + perform_enqueued_jobs do + expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(1) + expect(ActionMailer::Base.deliveries.last.to).to eq(["git@example.com"]) + end end end @@ -81,8 +78,9 @@ describe Ci::MailService do email_only_broken_builds: false, email_recipients: "jeroen@example.com") end - let(:commit) { FactoryGirl.create(:ci_commit, project: project) } - let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } + 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, user: user) } before do allow(mail).to receive_messages( @@ -91,14 +89,12 @@ describe Ci::MailService do end it do - should_email("git@example.com") - should_email("jeroen@example.com") - mail.execute(build) - end - - def should_email(email) - expect(Ci::Notify).to receive(:build_success_email).with(build.id, email) - expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email) + perform_enqueued_jobs do + expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(2) + expect( + ActionMailer::Base.deliveries.map(&:to).flatten + ).to include("git@example.com", "jeroen@example.com") + end end end @@ -109,8 +105,9 @@ describe Ci::MailService do email_only_broken_builds: true, email_recipients: "jeroen@example.com") end - let(:commit) { FactoryGirl.create(:ci_commit, project: project) } - let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } + 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, user: user) } before do allow(mail).to receive_messages( @@ -119,14 +116,11 @@ describe Ci::MailService do end it do - should_email(commit.git_author_email) - should_email("jeroen@example.com") - mail.execute(build) if mail.can_execute?(build) - end - - def should_email(email) - expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email) - expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email) + perform_enqueued_jobs do + expect do + mail.execute(build) if mail.can_execute?(build) + end.to_not change{ ActionMailer::Base.deliveries.size } + end end end @@ -137,8 +131,9 @@ describe Ci::MailService do email_only_broken_builds: false, email_recipients: "jeroen@example.com") end - let(:commit) { FactoryGirl.create(:ci_commit, project: project) } - let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) } + 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, user: user) } before do allow(mail).to receive_messages( @@ -159,8 +154,9 @@ describe Ci::MailService do email_only_broken_builds: true, email_recipients: "jeroen@example.com") end - let(:commit) { FactoryGirl.create(:ci_commit, project: project) } - let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit) } + 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, user: user) } before do allow(mail).to receive_messages( @@ -170,14 +166,11 @@ describe Ci::MailService do it do Ci::Build.retry(build) - should_email(commit.git_author_email) - should_email("jeroen@example.com") - mail.execute(build) if mail.can_execute?(build) - end - - def should_email(email) - expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email) - expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email) + perform_enqueued_jobs do + expect do + mail.execute(build) if mail.can_execute?(build) + end.to_not change{ ActionMailer::Base.deliveries.size } + end end end end diff --git a/spec/models/ci/project_services/slack_message_spec.rb b/spec/models/ci/project_services/slack_message_spec.rb index f5335903728..8adda6c86cc 100644 --- a/spec/models/ci/project_services/slack_message_spec.rb +++ b/spec/models/ci/project_services/slack_message_spec.rb @@ -3,82 +3,41 @@ require 'spec_helper' describe Ci::SlackMessage do subject { Ci::SlackMessage.new(commit) } - let(:project) { FactoryGirl.create :ci_project } + let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) } - context "One build" do - let(:commit) { FactoryGirl.create(:ci_commit_with_one_job, project: project) } + context 'when all matrix builds succeeded' do + let(:color) { 'good' } - let(:build) do - commit.create_builds - commit.builds.first - end - - context 'when build succeeded' do - let(:color) { 'good' } - - it 'returns a message with succeeded build' do - build.update(status: "success") - - 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") + 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('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, project: project) } - - 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_services/slack_service_spec.rb b/spec/models/ci/project_services/slack_service_spec.rb index 0524f472432..1ac7dfe568d 100644 --- a/spec/models/ci/project_services/slack_service_spec.rb +++ b/spec/models/ci/project_services/slack_service_spec.rb @@ -31,16 +31,15 @@ describe Ci::SlackService do describe "Execute" do let(:slack) { Ci::SlackService.new } - let(:project) { FactoryGirl.create :ci_project } - let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:commit) { FactoryGirl.create :ci_commit } let(:build) { FactoryGirl.create :ci_build, commit: commit, status: 'failed' } let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' } let(:notify_only_broken_builds) { false } before do allow(slack).to receive_messages( - project: project, - project_id: project.id, + project: commit.project, + project_id: commit.project_id, webhook: webhook_url, notify_only_broken_builds: notify_only_broken_builds ) diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index 261ea69f5b4..ac7e38bbcb0 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -1,9 +1,9 @@ # == Schema Information # -# Table name: projects +# Table name: ci_projects # # id :integer not null, primary key -# name :string(255) not null +# name :string(255) # timeout :integer default(3600), not null # created_at :datetime # updated_at :datetime @@ -28,13 +28,20 @@ require 'spec_helper' describe Ci::Project do - subject { FactoryGirl.build :ci_project } + let(:project) { FactoryGirl.create :ci_project } + let(:gl_project) { project.gl_project } + subject { project } + + it { is_expected.to have_many(:runner_projects) } + it { is_expected.to have_many(:runners) } + it { is_expected.to have_many(:web_hooks) } + it { is_expected.to have_many(:events) } + it { is_expected.to have_many(:variables) } + it { is_expected.to have_many(:triggers) } + it { is_expected.to have_many(:services) } - it { is_expected.to have_many(:commits) } - - it { is_expected.to validate_presence_of :name } it { is_expected.to validate_presence_of :timeout } - it { is_expected.to validate_presence_of :default_ref } + it { is_expected.to validate_presence_of :gitlab_id } describe 'before_validation' do it 'should set an random token if none provided' do @@ -48,43 +55,89 @@ describe Ci::Project do end end - describe "ordered_by_last_commit_date" do - it "returns ordered projects" do - newest_project = FactoryGirl.create :ci_project - oldest_project = FactoryGirl.create :ci_project - project_without_commits = FactoryGirl.create :ci_project + describe :name_with_namespace do + subject { project.name_with_namespace } + + it { is_expected.to eq(project.name) } + it { is_expected.to eq(gl_project.name_with_namespace) } + end + + describe :path_with_namespace do + subject { project.path_with_namespace } + + it { is_expected.to eq(project.path) } + it { is_expected.to eq(gl_project.path_with_namespace) } + end + + describe :path_with_namespace do + subject { project.web_url } + + it { is_expected.to eq(gl_project.web_url) } + end - FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: newest_project - FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: oldest_project + describe :web_url do + subject { project.web_url } - expect(Ci::Project.ordered_by_last_commit_date).to eq([newest_project, oldest_project, project_without_commits]) + it { is_expected.to eq(project.gitlab_url) } + it { is_expected.to eq(gl_project.web_url) } + end + + describe :http_url_to_repo do + subject { project.http_url_to_repo } + + it { is_expected.to eq(gl_project.http_url_to_repo) } + end + + describe :ssh_url_to_repo do + subject { project.ssh_url_to_repo } + + it { is_expected.to eq(gl_project.ssh_url_to_repo) } + end + + describe :commits do + subject { project.commits } + + before do + FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: gl_project end + + it { is_expected.to eq(gl_project.ci_commits) } end - describe 'ordered commits' do - let(:project) { FactoryGirl.create :ci_project } + describe :builds do + subject { project.builds } - it 'returns ordered list of commits' do - commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: project - commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: project - expect(project.commits).to eq([commit2, commit1]) + before do + commit = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: gl_project + FactoryGirl.create :ci_build, commit: commit end - it 'returns commits ordered by committed_at and id, with nulls last' do - commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: project - commit2 = FactoryGirl.create :ci_commit, committed_at: nil, project: project - commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: project - commit4 = FactoryGirl.create :ci_commit, committed_at: nil, project: project - expect(project.commits).to eq([commit2, commit4, commit3, commit1]) + it { is_expected.to eq(gl_project.ci_builds) } + end + + describe "ordered_by_last_commit_date" do + it "returns ordered projects" do + newest_project = FactoryGirl.create :empty_project + newest_ci_project = newest_project.ensure_gitlab_ci_project + oldest_project = FactoryGirl.create :empty_project + oldest_ci_project = oldest_project.ensure_gitlab_ci_project + project_without_commits = FactoryGirl.create :empty_project + ci_project_without_commits = project_without_commits.ensure_gitlab_ci_project + + FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: newest_project + FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: oldest_project + + expect(Ci::Project.ordered_by_last_commit_date).to eq([newest_ci_project, oldest_ci_project, ci_project_without_commits]) end end context :valid_project do - let(:project) { FactoryGirl.create :ci_project } + let(:commit) { FactoryGirl.create(:ci_commit) } context :project_with_commit_and_builds do + let(:project) { commit.project } + before do - commit = FactoryGirl.create(:ci_commit, project: project) FactoryGirl.create(:ci_build, commit: commit) end @@ -141,18 +194,6 @@ describe Ci::Project do end end - describe 'Project.parse' do - let(:project) { FactoryGirl.create :project } - - subject { Ci::Project.parse(project) } - - it { is_expected.to be_valid } - it { is_expected.to be_kind_of(Ci::Project) } - it { expect(subject.name).to eq(project.name_with_namespace) } - it { expect(subject.gitlab_id).to eq(project.id) } - it { expect(subject.gitlab_url).to eq(project.web_url) } - end - describe :repo_url_with_auth do let(:project) { FactoryGirl.create :ci_project } subject { project.repo_url_with_auth } @@ -165,13 +206,6 @@ describe Ci::Project do it { is_expected.to include(project.gitlab_url[7..-1]) } end - describe :search do - let!(:project) { FactoryGirl.create(:ci_project, name: "foo") } - - it { expect(Ci::Project.search('fo')).to include(project) } - it { expect(Ci::Project.search('bar')).to be_empty } - end - describe :any_runners do it "there are no runners available" do project = FactoryGirl.create(:ci_project) @@ -195,5 +229,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_project_spec.rb b/spec/models/ci/runner_project_spec.rb index 0218d484130..37682c6ea0c 100644 --- a/spec/models/ci/runner_project_spec.rb +++ b/spec/models/ci/runner_project_spec.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: runner_projects +# Table name: ci_runner_projects # # id :integer not null, primary key # runner_id :integer not null diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 757593a7ab8..9a1233b9095 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: runners +# Table name: ci_runners # # id :integer not null, primary key # token :string(255) @@ -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/ci/service_spec.rb b/spec/models/ci/service_spec.rb index 2c575056b08..36cda988eb4 100644 --- a/spec/models/ci/service_spec.rb +++ b/spec/models/ci/service_spec.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: services +# Table name: ci_services # # id :integer not null, primary key # type :string(255) @@ -29,13 +29,12 @@ describe Ci::Service do end describe "Testable" do - let(:project) { FactoryGirl.create :ci_project } - let(:commit) { FactoryGirl.create :ci_commit, project: project } + let(:commit) { FactoryGirl.create :ci_commit } let(:build) { FactoryGirl.create :ci_build, commit: commit } before do allow(@service).to receive_messages( - project: project + project: commit.project ) build @testable = @service.can_test? diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb index 19c14ef2da2..b8aa3c1e777 100644 --- a/spec/models/ci/trigger_spec.rb +++ b/spec/models/ci/trigger_spec.rb @@ -1,3 +1,15 @@ +# == Schema Information +# +# Table name: ci_triggers +# +# id :integer not null, primary key +# token :string(255) +# project_id :integer not null +# deleted_at :datetime +# created_at :datetime +# updated_at :datetime +# + require 'spec_helper' describe Ci::Trigger do diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb index d034a6c7b9f..a515f5881ff 100644 --- a/spec/models/ci/variable_spec.rb +++ b/spec/models/ci/variable_spec.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: variables +# Table name: ci_variables # # id :integer not null, primary key # project_id :integer not null diff --git a/spec/models/ci/web_hook_spec.rb b/spec/models/ci/web_hook_spec.rb index bf9481ab81d..2865482a212 100644 --- a/spec/models/ci/web_hook_spec.rb +++ b/spec/models/ci/web_hook_spec.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: web_hooks +# Table name: ci_web_hooks # # id :integer not null, primary key # url :string(255) not null diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb index 1031af097bd..3c1009a2eb0 100644 --- a/spec/models/commit_range_spec.rb +++ b/spec/models/commit_range_spec.rb @@ -7,50 +7,72 @@ describe CommitRange do it { is_expected.to include_module(Referable) } end - let(:sha_from) { 'f3f85602' } - let(:sha_to) { 'e86e1013' } + let!(:project) { create(:project, :public) } + let!(:commit1) { project.commit("HEAD~2") } + let!(:commit2) { project.commit } - let(:range) { described_class.new("#{sha_from}...#{sha_to}") } - let(:range2) { described_class.new("#{sha_from}..#{sha_to}") } + let(:sha_from) { commit1.short_id } + let(:sha_to) { commit2.short_id } + + let(:full_sha_from) { commit1.id } + let(:full_sha_to) { commit2.id } + + let(:range) { described_class.new("#{sha_from}...#{sha_to}", project) } + let(:range2) { described_class.new("#{sha_from}..#{sha_to}", project) } it 'raises ArgumentError when given an invalid range string' do - expect { described_class.new("Foo") }.to raise_error(ArgumentError) + expect { described_class.new("Foo", project) }.to raise_error(ArgumentError) end describe '#to_s' do it 'is correct for three-dot syntax' do - expect(range.to_s).to eq "#{sha_from[0..7]}...#{sha_to[0..7]}" + expect(range.to_s).to eq "#{full_sha_from}...#{full_sha_to}" end it 'is correct for two-dot syntax' do - expect(range2.to_s).to eq "#{sha_from[0..7]}..#{sha_to[0..7]}" + expect(range2.to_s).to eq "#{full_sha_from}..#{full_sha_to}" end end describe '#to_reference' do - let(:project) { double('project', to_reference: 'namespace1/project') } + let(:cross) { create(:project) } + + it 'returns a String reference to the object' do + expect(range.to_reference).to eq "#{full_sha_from}...#{full_sha_to}" + end + + it 'returns a String reference to the object' do + expect(range2.to_reference).to eq "#{full_sha_from}..#{full_sha_to}" + end + + it 'supports a cross-project reference' do + expect(range.to_reference(cross)).to eq "#{project.to_reference}@#{full_sha_from}...#{full_sha_to}" + end + end - before do - range.project = project + describe '#reference_link_text' do + let(:cross) { create(:project) } + + it 'returns a String reference to the object' do + expect(range.reference_link_text).to eq "#{sha_from}...#{sha_to}" end it 'returns a String reference to the object' do - expect(range.to_reference).to eq range.to_s + expect(range2.reference_link_text).to eq "#{sha_from}..#{sha_to}" end it 'supports a cross-project reference' do - cross = double('project') - expect(range.to_reference(cross)).to eq "#{project.to_reference}@#{range.to_s}" + expect(range.reference_link_text(cross)).to eq "#{project.to_reference}@#{sha_from}...#{sha_to}" end end describe '#reference_title' do it 'returns the correct String for three-dot ranges' do - expect(range.reference_title).to eq "Commits #{sha_from} through #{sha_to}" + expect(range.reference_title).to eq "Commits #{full_sha_from} through #{full_sha_to}" end it 'returns the correct String for two-dot ranges' do - expect(range2.reference_title).to eq "Commits #{sha_from}^ through #{sha_to}" + expect(range2.reference_title).to eq "Commits #{full_sha_from}^ through #{full_sha_to}" end end @@ -60,11 +82,11 @@ describe CommitRange do end it 'includes the correct values for a three-dot range' do - expect(range.to_param).to eq({ from: sha_from, to: sha_to }) + expect(range.to_param).to eq({ from: full_sha_from, to: full_sha_to }) end it 'includes the correct values for a two-dot range' do - expect(range2.to_param).to eq({ from: sha_from + '^', to: sha_to }) + expect(range2.to_param).to eq({ from: full_sha_from + '^', to: full_sha_to }) end end @@ -79,64 +101,37 @@ describe CommitRange do end describe '#valid_commits?' do - context 'without a project' do - it 'returns nil' do - expect(range.valid_commits?).to be_nil + context 'with a valid repo' do + before do + expect(project).to receive(:valid_repo?).and_return(true) end - end - - it 'accepts an optional project argument' do - project1 = double('project1').as_null_object - project2 = double('project2').as_null_object - - # project1 gets assigned through the accessor, but ignored when not given - # as an argument to `valid_commits?` - expect(project1).not_to receive(:present?) - range.project = project1 - - # project2 gets passed to `valid_commits?` - expect(project2).to receive(:present?).and_return(false) - range.valid_commits?(project2) - end - - context 'with a project' do - let(:project) { double('project', repository: double('repository')) } + it 'is false when `sha_from` is invalid' do + expect(project).to receive(:commit).with(sha_from).and_return(nil) + expect(project).to receive(:commit).with(sha_to).and_call_original - context 'with a valid repo' do - before do - expect(project).to receive(:valid_repo?).and_return(true) - range.project = project - end + expect(range).not_to be_valid_commits + end - it 'is false when `sha_from` is invalid' do - expect(project.repository).to receive(:commit).with(sha_from).and_return(false) - expect(project.repository).not_to receive(:commit).with(sha_to) - expect(range).not_to be_valid_commits - end + it 'is false when `sha_to` is invalid' do + expect(project).to receive(:commit).with(sha_from).and_call_original + expect(project).to receive(:commit).with(sha_to).and_return(nil) - it 'is false when `sha_to` is invalid' do - expect(project.repository).to receive(:commit).with(sha_from).and_return(true) - expect(project.repository).to receive(:commit).with(sha_to).and_return(false) - expect(range).not_to be_valid_commits - end + expect(range).not_to be_valid_commits + end - it 'is true when both `sha_from` and `sha_to` are valid' do - expect(project.repository).to receive(:commit).with(sha_from).and_return(true) - expect(project.repository).to receive(:commit).with(sha_to).and_return(true) - expect(range).to be_valid_commits - end + it 'is true when both `sha_from` and `sha_to` are valid' do + expect(range).to be_valid_commits end + end - context 'without a valid repo' do - before do - expect(project).to receive(:valid_repo?).and_return(false) - range.project = project - end + context 'without a valid repo' do + before do + expect(project).to receive(:valid_repo?).and_return(false) + end - it 'returns false' do - expect(range).not_to be_valid_commits - end + it 'returns false' do + expect(range).not_to be_valid_commits end end end diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index e303a97e6b5..38a3dc1f4a6 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -24,6 +24,17 @@ describe Commit do end end + describe '#reference_link_text' do + it 'returns a String reference to the object' do + expect(commit.reference_link_text).to eq commit.short_id + end + + it 'supports a cross-project reference' do + cross = double('project') + expect(commit.reference_link_text(cross)).to eq "#{project.to_reference}@#{commit.short_id}" + end + end + describe '#title' do it "returns no_commit_message when safe_message is blank" do allow(commit).to receive(:safe_message).and_return('') @@ -77,21 +88,17 @@ eos let(:other_issue) { create :issue, project: other_project } it 'detects issues that this commit is marked as closing' do - allow(commit).to receive(:safe_message).and_return("Fixes ##{issue.iid}") - expect(commit.closes_issues).to eq([issue]) - end - - it 'does not detect issues from other projects' do ext_ref = "#{other_project.path_with_namespace}##{other_issue.iid}" - allow(commit).to receive(:safe_message).and_return("Fixes #{ext_ref}") - expect(commit.closes_issues).to be_empty + allow(commit).to receive(:safe_message).and_return("Fixes ##{issue.iid} and #{ext_ref}") + expect(commit.closes_issues).to include(issue) + expect(commit.closes_issues).to include(other_issue) end 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) } @@ -100,4 +107,15 @@ eos # Include the subject in the repository stub. let(:extra_commits) { [subject] } end + + describe '#hook_attrs' do + let(:data) { commit.hook_attrs(with_changed_files: true) } + + it { expect(data).to be_a(Hash) } + it { expect(data[:message]).to include('Add submodule from gitlab.com') } + it { expect(data[:timestamp]).to eq('2014-02-27T11:01:38+02:00') } + it { expect(data[:added]).to eq(["gitlab-grack"]) } + it { expect(data[:modified]).to eq([".gitmodules"]) } + it { expect(data[:removed]).to eq([]) } + end end diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb new file mode 100644 index 00000000000..dca0715eed8 --- /dev/null +++ b/spec/models/commit_status_spec.rb @@ -0,0 +1,197 @@ +# == Schema Information +# +# Table name: ci_builds +# +# id :integer not null, primary key +# project_id :integer +# status :string(255) +# finished_at :datetime +# trace :text +# created_at :datetime +# updated_at :datetime +# started_at :datetime +# runner_id :integer +# coverage :float +# commit_id :integer +# commands :text +# job_id :integer +# name :string(255) +# deploy :boolean default(FALSE) +# options :text +# allow_failure :boolean default(FALSE), not null +# stage :string(255) +# trigger_request_id :integer +# stage_idx :integer +# tag :boolean +# ref :string(255) +# user_id :integer +# type :string(255) +# target_url :string(255) +# description :string(255) +# artifacts_file :text +# + +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/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 8f706f8934b..0f13c4410cd 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -68,7 +68,6 @@ describe Issue, "Issuable" do end end - describe "#to_hook_data" do let(:hook_data) { issue.to_hook_data(user) } 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/concerns/strip_attribute_spec.rb b/spec/models/concerns/strip_attribute_spec.rb new file mode 100644 index 00000000000..6445e29c3ef --- /dev/null +++ b/spec/models/concerns/strip_attribute_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Milestone, "StripAttribute" do + let(:milestone) { create(:milestone) } + + describe ".strip_attributes" do + it { expect(Milestone).to respond_to(:strip_attributes) } + it { expect(Milestone.strip_attrs).to include(:title) } + end + + describe "#strip_attributes" do + before do + milestone.title = ' 8.3 ' + milestone.valid? + end + + it { expect(milestone.title).to eq('8.3') } + end + +end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 0f32f162a10..ae53f7a536b 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -64,4 +64,42 @@ describe Event do it { expect(@event.branch_name).to eq("master") } it { expect(@event.author).to eq(@user) } end + + describe '.latest_update_time' do + describe 'when events are present' do + let(:time) { Time.utc(2015, 1, 1) } + + before do + create(:closed_issue_event, updated_at: time) + create(:closed_issue_event, updated_at: time + 5) + end + + it 'returns the latest update time' do + expect(Event.latest_update_time).to eq(time + 5) + end + end + + describe 'when no events exist' do + it 'returns nil' do + expect(Event.latest_update_time).to be_nil + end + end + end + + describe '.limit_recent' do + let!(:event1) { create(:closed_issue_event) } + let!(:event2) { create(:closed_issue_event) } + + describe 'without an explicit limit' do + subject { Event.limit_recent } + + it { is_expected.to eq([event2, event1]) } + end + + describe 'with an explicit limit' do + subject { Event.limit_recent(1) } + + it { is_expected.to eq([event2]) } + end + 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..c86314c454c --- /dev/null +++ b/spec/models/generic_commit_status_spec.rb @@ -0,0 +1,72 @@ +# == Schema Information +# +# Table name: ci_builds +# +# id :integer not null, primary key +# project_id :integer +# status :string(255) +# finished_at :datetime +# trace :text +# created_at :datetime +# updated_at :datetime +# started_at :datetime +# runner_id :integer +# coverage :float +# commit_id :integer +# commands :text +# job_id :integer +# name :string(255) +# deploy :boolean default(FALSE) +# options :text +# allow_failure :boolean default(FALSE), not null +# stage :string(255) +# trigger_request_id :integer +# stage_idx :integer +# tag :boolean +# ref :string(255) +# user_id :integer +# type :string(255) +# target_url :string(255) +# description :string(255) +# artifacts_file :text +# + +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/global_milestone_spec.rb b/spec/models/global_milestone_spec.rb new file mode 100644 index 00000000000..6eeff30b20e --- /dev/null +++ b/spec/models/global_milestone_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +describe GlobalMilestone do + let(:user) { create(:user) } + let(:user2) { create(:user) } + let(:group) { create(:group) } + let(:project1) { create(:project, group: group) } + let(:project2) { create(:project, path: 'gitlab-ci', group: group) } + let(:project3) { create(:project, path: 'cookbook-gitlab', group: group) } + let(:milestone1_project1) { create(:milestone, title: "Milestone v1.2", project: project1) } + let(:milestone1_project2) { create(:milestone, title: "Milestone v1.2", project: project2) } + let(:milestone1_project3) { create(:milestone, title: "Milestone v1.2", project: project3) } + let(:milestone2_project1) { create(:milestone, title: "VD-123", project: project1) } + let(:milestone2_project2) { create(:milestone, title: "VD-123", project: project2) } + let(:milestone2_project3) { create(:milestone, title: "VD-123", project: project3) } + + describe :build_collection do + before do + milestones = + [ + milestone1_project1, + milestone1_project2, + milestone1_project3, + milestone2_project1, + milestone2_project2, + milestone2_project3 + ] + + @global_milestones = GlobalMilestone.build_collection(milestones) + end + + it 'should have all project milestones' do + expect(@global_milestones.count).to eq(2) + end + + it 'should have all project milestones titles' do + expect(@global_milestones.map(&:title)).to match_array(['Milestone v1.2', 'VD-123']) + end + + it 'should have all project milestones' do + expect(@global_milestones.map { |group_milestone| group_milestone.milestones.count }.sum).to eq(6) + end + end + + describe :initialize do + before do + milestones = + [ + milestone1_project1, + milestone1_project2, + milestone1_project3, + ] + + @global_milestone = GlobalMilestone.new(milestone1_project1.title, milestones) + end + + it 'should have exactly one group milestone' do + expect(@global_milestone.title).to eq('Milestone v1.2') + end + + it 'should have all project milestones with the same title' do + expect(@global_milestone.milestones.count).to eq(3) + end + end +end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 80638fc8db2..6f166b5ab75 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -11,6 +11,7 @@ # type :string(255) # description :string(255) default(""), not null # avatar :string(255) +# public :boolean default(FALSE) # require 'spec_helper' @@ -37,6 +38,33 @@ describe Group do it { is_expected.not_to validate_presence_of :owner } end + describe '.public_and_given_groups' do + let!(:public_group) { create(:group, public: true) } + + subject { described_class.public_and_given_groups([group.id]) } + + it { is_expected.to eq([public_group, group]) } + end + + describe '.visible_to_user' do + let!(:group) { create(:group) } + let!(:user) { create(:user) } + + subject { described_class.visible_to_user(user) } + + describe 'when the user has access to a group' do + before do + group.add_user(user, Gitlab::Access::MASTER) + end + + it { is_expected.to eq([group]) } + end + + describe 'when the user does not have access to any groups' do + it { is_expected.to eq([]) } + end + end + describe '#to_reference' do it 'returns a String reference to the object' do expect(group.to_reference).to eq "@#{group.name}" @@ -84,4 +112,23 @@ describe Group do expect(group.avatar_type).to eq(["only images allowed"]) end end + + describe "public_profile?" do + it "returns true for public group" do + group = create(:group, public: true) + expect(group.public_profile?).to be_truthy + end + + it "returns true for non-public group with public project" do + group = create(:group) + create(:project, :public, group: group) + expect(group.public_profile?).to be_truthy + end + + it "returns false for non-public group with no public projects" do + group = create(:group) + create(:project, group: group) + expect(group.public_profile?).to be_falsy + end + end end diff --git a/spec/models/hooks/project_hook_spec.rb b/spec/models/hooks/project_hook_spec.rb index dae7e399cfb..a2dc66fce3e 100644 --- a/spec/models/hooks/project_hook_spec.rb +++ b/spec/models/hooks/project_hook_spec.rb @@ -22,7 +22,7 @@ describe ProjectHook do describe '.push_hooks' do it 'should return hooks for push events only' do hook = create(:project_hook, push_events: true) - hook2 = create(:project_hook, push_events: false) + create(:project_hook, push_events: false) expect(ProjectHook.push_hooks).to eq([hook]) end end @@ -30,7 +30,7 @@ describe ProjectHook do describe '.tag_push_hooks' do it 'should return hooks for tag push events only' do hook = create(:project_hook, tag_push_events: true) - hook2 = create(:project_hook, tag_push_events: false) + create(:project_hook, tag_push_events: false) expect(ProjectHook.tag_push_hooks).to eq([hook]) end end diff --git a/spec/models/hooks/service_hook_spec.rb b/spec/models/hooks/service_hook_spec.rb index 4c8b8910ae7..16641c12124 100644 --- a/spec/models/hooks/service_hook_spec.rb +++ b/spec/models/hooks/service_hook_spec.rb @@ -39,8 +39,6 @@ describe ServiceHook do end it "POSTs the data as JSON" do - json = @data.to_json - @service_hook.execute(@data) expect(WebMock).to have_requested(:post, @service_hook.url).with( headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Service Hook' } diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb index 23f30881d99..35042788c65 100644 --- a/spec/models/hooks/web_hook_spec.rb +++ b/spec/models/hooks/web_hook_spec.rb @@ -60,8 +60,6 @@ describe ProjectHook do end it "POSTs the data as JSON" do - json = @data.to_json - @project_hook.execute(@data, 'push_hooks') expect(WebMock).to have_requested(:post, @project_hook.url).with( headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Push Hook' } @@ -73,5 +71,11 @@ describe ProjectHook do expect { @project_hook.execute(@data, 'push_hooks') }.to raise_error(RuntimeError) end + + it "handles SSL exceptions" do + expect(WebHook).to receive(:post).and_raise(OpenSSL::SSL::SSLError.new('SSL error')) + + expect(@project_hook.execute(@data, 'push_hooks')).to eq([false, 'SSL error']) + end end end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index cf336d82957..c9aa1b063c6 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -68,8 +68,45 @@ describe Issue do end end + describe '#closed_by_merge_requests' do + let(:project) { create(:project) } + let(:issue) { create(:issue, project: project, state: "opened")} + let(:closed_issue) { build(:issue, project: project, state: "closed")} + + let(:mr) do + opts = { + title: 'Awesome merge_request', + description: "Fixes #{issue.to_reference}", + source_branch: 'feature', + target_branch: 'master' + } + MergeRequests::CreateService.new(project, project.owner, opts).execute + end + + let(:closed_mr) do + opts = { + title: 'Awesome merge_request 2', + description: "Fixes #{issue.to_reference}", + source_branch: 'feature', + target_branch: 'master', + state: 'closed' + } + MergeRequests::CreateService.new(project, project.owner, opts).execute + end + + it 'returns the merge request to close this issue' do + allow(mr).to receive(:closes_issue?).with(issue).and_return(true) + + expect(issue.closed_by_merge_requests).to eq([mr]) + end + + it "returns an empty array when the current issue is closed already" do + expect(closed_issue.closed_by_merge_requests).to eq([]) + end + 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/label_spec.rb b/spec/models/label_spec.rb index 6518213d71c..511ee8cbd96 100644 --- a/spec/models/label_spec.rb +++ b/spec/models/label_spec.rb @@ -8,6 +8,7 @@ # project_id :integer # created_at :datetime # updated_at :datetime +# template :boolean default(FALSE) # require 'spec_helper' diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 17a49013d25..567c911425c 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -20,6 +20,7 @@ # position :integer default(0) # locked_at :datetime # updated_by_id :integer +# merge_error :string(255) # require 'spec_helper' @@ -79,6 +80,12 @@ describe MergeRequest do expect(merge_request.commits).not_to be_empty expect(merge_request.mr_and_commit_notes.count).to eq(2) end + + it "should include notes for commits from target project as well" do + create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit', project: merge_request.target_project) + expect(merge_request.commits).not_to be_empty + expect(merge_request.mr_and_commit_notes.count).to eq(3) + end end describe '#is_being_reassigned?' do @@ -165,6 +172,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) } @@ -175,4 +193,29 @@ describe MergeRequest do it_behaves_like 'a Taskable' do subject { create :merge_request, :simple } end + + describe '#ci_commit' do + describe 'when the source project exists' do + it 'returns the latest commit' do + commit = double(:commit, id: '123abc') + ci_commit = double(:ci_commit) + + allow(subject).to receive(:last_commit).and_return(commit) + + expect(subject.source_project).to receive(:ci_commit). + with('123abc'). + and_return(ci_commit) + + expect(subject.ci_commit).to eq(ci_commit) + end + end + + describe 'when the source project does not exist' do + it 'returns nil' do + allow(subject).to receive(:source_project).and_return(nil) + + expect(subject.ci_commit).to be_nil + end + end + end end diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index 36352e1ecce..77c58627322 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -111,8 +111,8 @@ describe Milestone do describe :is_empty? do before do - issue = create :closed_issue, milestone: milestone - merge_request = create :merge_request, milestone: milestone + create :closed_issue, milestone: milestone + create :merge_request, milestone: milestone end it 'Should return total count of issues and merge requests assigned to milestone' do @@ -125,7 +125,7 @@ describe Milestone do milestone = create :milestone create :closed_issue, milestone: milestone - issue = create :issue + create :issue end it 'should be true if milestone active and all nested issues closed' do @@ -140,4 +140,32 @@ describe Milestone do end end + describe '#sort_issues' do + let(:milestone) { create(:milestone) } + + let(:issue1) { create(:issue, milestone: milestone, position: 1) } + let(:issue2) { create(:issue, milestone: milestone, position: 2) } + let(:issue3) { create(:issue, milestone: milestone, position: 3) } + let(:issue4) { create(:issue, position: 42) } + + it 'sorts the given issues' do + milestone.sort_issues([issue3.id, issue2.id, issue1.id]) + + issue1.reload + issue2.reload + issue3.reload + + expect(issue1.position).to eq(3) + expect(issue2.position).to eq(2) + expect(issue3.position).to eq(1) + end + + it 'ignores issues not part of the milestone' do + milestone.sort_issues([issue3.id, issue2.id, issue1.id, issue4.id]) + + issue4.reload + + expect(issue4.position).to eq(42) + end + end end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 1d72a9503ae..a98b9cb7321 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -11,6 +11,7 @@ # type :string(255) # description :string(255) default(""), not null # avatar :string(255) +# public :boolean default(FALSE) # require 'spec_helper' diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 3a0b194ba1e..f347f537550 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -32,77 +32,6 @@ describe Note do it { is_expected.to validate_presence_of(:project) } end - describe '#votable?' do - it 'is true for issue notes' do - note = build(:note_on_issue) - expect(note).to be_votable - end - - it 'is true for merge request notes' do - note = build(:note_on_merge_request) - expect(note).to be_votable - end - - it 'is false for merge request diff notes' do - note = build(:note_on_merge_request_diff) - expect(note).not_to be_votable - end - - it 'is false for commit notes' do - note = build(:note_on_commit) - expect(note).not_to be_votable - end - - it 'is false for commit diff notes' do - note = build(:note_on_commit_diff) - expect(note).not_to be_votable - end - end - - describe 'voting score' do - it 'recognizes a neutral note' do - note = build(:votable_note, note: 'This is not a +1 note') - expect(note).not_to be_upvote - expect(note).not_to be_downvote - end - - it 'recognizes a neutral emoji note' do - note = build(:votable_note, note: "I would :+1: this, but I don't want to") - expect(note).not_to be_upvote - expect(note).not_to be_downvote - end - - it 'recognizes a +1 note' do - note = build(:votable_note, note: '+1 for this') - expect(note).to be_upvote - end - - it 'recognizes a +1 emoji as a vote' do - note = build(:votable_note, note: ':+1: for this') - expect(note).to be_upvote - end - - it 'recognizes a thumbsup emoji as a vote' do - note = build(:votable_note, note: ':thumbsup: for this') - expect(note).to be_upvote - end - - it 'recognizes a -1 note' do - note = build(:votable_note, note: '-1 for this') - expect(note).to be_downvote - end - - it 'recognizes a -1 emoji as a vote' do - note = build(:votable_note, note: ':-1: for this') - expect(note).to be_downvote - end - - it 'recognizes a thumbsdown emoji as a vote' do - note = build(:votable_note, note: ':thumbsdown: for this') - expect(note).to be_downvote - end - end - describe "Commit notes" do let!(:note) { create(:note_on_commit, note: "+1 from me") } let!(:commit) { note.noteable } @@ -139,10 +68,6 @@ describe Note do it "should be recognized by #for_commit_diff_line?" do expect(note).to be_for_commit_diff_line end - - it "should not be votable" do - expect(note).not_to be_votable - end end describe 'authorization' do @@ -192,10 +117,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 @@ -205,4 +129,16 @@ describe Note do it { expect(Note.search('wow')).to include(note) } end + + describe :grouped_awards do + before do + create :note, note: "smile", is_award: true + create :note, note: "smile", is_award: true + end + + it "returns grouped array of notes" do + expect(Note.grouped_awards.first.first).to eq("smile") + expect(Note.grouped_awards.first.last).to match_array(Note.all) + end + end 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 8cdd551a0ca..b9006b693b2 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/builds")} end describe "execute" do @@ -48,33 +47,11 @@ 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 - service_hook = double - 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 end end - - describe "Fork registration" do - before do - @old_project = create(:ci_project).gl_project - @project = create(:empty_project) - @user = create(:user) - - @service = GitlabCiService.new - allow(@service).to receive_messages( - service_hook: true, - project_url: 'http://ci.gitlab.org/projects/2', - token: 'verySecret', - project: @old_project - ) - end - - it "creates fork on CI" do - expect_any_instance_of(Ci::CreateProjectService).to receive(:execute) - @service.fork_registration(@project, @user) - end - end end diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index 65d16beef91..f67d7b30980 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -87,7 +87,7 @@ describe HipchatService do it "should create a push message" do message = hipchat.send(:create_push_message, push_sample_data) - obj_attr = push_sample_data[:object_attributes] + push_sample_data[:object_attributes] branch = push_sample_data[:ref].gsub('refs/heads/', '') expect(message).to include("#{user.name} pushed to branch " \ "<a href=\"#{project.web_url}/commits/#{branch}\">#{branch}</a> of " \ @@ -107,7 +107,7 @@ describe HipchatService do it "should create a tag push message" do message = hipchat.send(:create_push_message, push_sample_data) - obj_attr = push_sample_data[:object_attributes] + push_sample_data[:object_attributes] expect(message).to eq("#{user.name} pushed new tag " \ "<a href=\"#{project.web_url}/commits/test\">test</a> to " \ "<a href=\"#{project.web_url}\">#{project_name}</a>\n") diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index ddd2cce212c..576f5fc79eb 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -94,9 +94,9 @@ describe JiraService do end it 'should be prepopulated with the settings' do - expect(@service.properties[:project_url]).to eq('http://jira.sample/projects/project_a') - expect(@service.properties[:issues_url]).to eq("http://jira.sample/issues/:id") - expect(@service.properties[:new_issue_url]).to eq("http://jira.sample/projects/project_a/issues/new") + expect(@service.properties["project_url"]).to eq('http://jira.sample/projects/project_a') + expect(@service.properties["issues_url"]).to eq("http://jira.sample/issues/:id") + expect(@service.properties["new_issue_url"]).to eq("http://jira.sample/projects/project_a/issues/new") end end 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 9e7b6f5cb30..06a02c13bf1 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -140,7 +140,7 @@ describe Project do describe 'last_activity_date' do it 'returns the creation date of the project\'s last event if present' do - last_activity_event = create(:event, project: project) + create(:event, project: project) expect(project.last_activity_at.to_i).to eq(last_event.created_at.to_i) end @@ -220,6 +220,7 @@ describe Project do end it { expect(Project.find_with_namespace('gitlab/gitlabhq')).to eq(@project) } + it { expect(Project.find_with_namespace('GitLab/GitlabHQ')).to eq(@project) } it { expect(Project.find_with_namespace('gitlab-ci')).to be_nil } end end @@ -344,17 +345,6 @@ describe Project do expect(project1.star_count).to eq(0) expect(project2.star_count).to eq(0) end - - it 'is decremented when an upvoter account is deleted' do - user = create :user - project = create :project, :public - user.toggle_star(project) - project.reload - expect(project.star_count).to eq(1) - user.destroy - project.reload - expect(project.star_count).to eq(0) - end end describe :avatar_type do @@ -404,21 +394,84 @@ describe Project do describe :ci_commit do let(:project) { create :project } - let(:ci_project) { create :ci_project, gl_project: project } - let(:commit) { create :ci_commit, project: ci_project } + let(:commit) { create :ci_commit, gl_project: project } - before { project.create_gitlab_ci_service(active: true) } + before do + project.ensure_gitlab_ci_project + project.create_gitlab_ci_service(active: true) + end it { expect(project.ci_commit(commit.sha)).to eq(commit) } end - describe :enable_ci do + describe :builds_enabled do let(:project) { create :project } - let(:user) { create :user } - before { project.enable_ci(user) } + before { project.builds_enabled = true } + + subject { project.builds_enabled } - it { expect(project.gitlab_ci?).to be_truthy } + it { is_expected.to eq(project.gitlab_ci_service.active) } + it { expect(project.builds_enabled?).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 + # Little fix for special issue related to Fractional Seconds support for MySQL. + # See: https://github.com/rails/rails/pull/14359/files + create(:note_on_commit, project: project2, created_at: date + 1) + end + end + + it 'sorts Projects by the amount of notes in descending order' do + expect(subject).to eq([project2, project1]) + end + end + end + + describe '.visible_to_user' do + let!(:project) { create(:project, :private) } + let!(:user) { create(:user) } + + subject { described_class.visible_to_user(user) } + + describe 'when a user has access to a project' do + before do + project.team.add_user(user, Gitlab::Access::MASTER) + end + + it { is_expected.to eq([project]) } + end + + describe 'when a user does not have access to any projects' do + it { is_expected.to eq([]) } + end + end end diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb index cc1138490a0..26e8fdae472 100644 --- a/spec/models/project_team_spec.rb +++ b/spec/models/project_team_spec.rb @@ -66,4 +66,16 @@ describe ProjectTeam do it { expect(project.team.member?(guest)).to be_truthy } end end + + describe "#human_max_access" do + it "return master role" do + user = create :user + group = create :group + group.add_users([user.id], GroupMember::MASTER) + project = create(:project, namespace: group) + project.team << [user, :guest] + + expect(project.team.human_max_access(user.id)).to eq("Master") + end + end end diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb index f785203af7d..3b889144447 100644 --- a/spec/models/project_wiki_spec.rb +++ b/spec/models/project_wiki_spec.rb @@ -184,6 +184,12 @@ describe ProjectWiki do subject.create_page("test page", "some content", :markdown, "commit message") expect(subject.pages.first.page.version.message).to eq("commit message") end + + it 'updates project activity' do + expect(subject).to receive(:update_project_activity) + + subject.create_page('Test Page', 'This is content') + end end describe "#update_page" do @@ -205,6 +211,12 @@ describe ProjectWiki do it "sets the correct commit message" do expect(@page.version.message).to eq("updated page") end + + it 'updates project activity' do + expect(subject).to receive(:update_project_activity) + + subject.update_page(@gollum_page, 'Yet more content', :markdown, 'Updated page again') + end end describe "#delete_page" do @@ -217,13 +229,19 @@ describe ProjectWiki do subject.delete_page(@page) expect(subject.pages.count).to eq(0) end + + it 'updates project activity' do + expect(subject).to receive(:update_project_activity) + + subject.delete_page(@page) + end end private def create_temp_repo(path) FileUtils.mkdir_p path - system(*%W(git init --quiet --bare -- #{path})) + system(*%W(#{Gitlab.config.git.bin_path} init --quiet --bare -- #{path})) end def remove_temp_repo(path) @@ -231,7 +249,7 @@ describe ProjectWiki do end def commit_details - commit = { name: user.name, email: user.email, message: "test commit" } + { name: user.name, email: user.email, message: "test commit" } end def create_page(name, content) diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb new file mode 100644 index 00000000000..72ecb442a36 --- /dev/null +++ b/spec/models/release_spec.rb @@ -0,0 +1,28 @@ +# == Schema Information +# +# Table name: releases +# +# id :integer not null, primary key +# tag :string(255) +# description :text +# project_id :integer +# created_at :datetime +# updated_at :datetime +# + +require 'rails_helper' + +RSpec.describe Release, type: :model do + let(:release) { create(:release) } + + it { expect(release).to be_valid } + + describe 'associations' do + it { is_expected.to belong_to(:project) } + end + + describe 'validation' do + it { is_expected.to validate_presence_of(:project) } + it { is_expected.to validate_presence_of(:description) } + end +end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 05e51532eb8..fa261e64c35 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -4,6 +4,7 @@ describe Repository do include RepoHelpers let(:repository) { create(:project).repository } + let(:user) { create(:user) } describe :branch_names_contains do subject { repository.branch_names_contains(sample_commit.id) } @@ -26,6 +27,15 @@ describe Repository do it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') } end + describe :find_commits_by_message do + subject { repository.find_commits_by_message('submodule').map{ |k| k.id } } + + it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } + it { is_expected.to include('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } + it { is_expected.to include('cfe32cf61b73a0d5e9f13e774abde7ff789b1660') } + it { is_expected.not_to include('913c66a37b4a45b9769037c55c2d238bd0942d2e') } + end + describe :blob_at do context 'blank sha' do subject { repository.blob_at(Gitlab::Git::BLANK_SHA, '.gitignore') } @@ -90,5 +100,104 @@ describe Repository do it { expect(subject.startline).to eq(186) } it { expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n") } end + + end + + describe :add_branch do + context 'when pre hooks were successful' do + it 'should run without errors' do + hook = double(trigger: true) + expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook) + + expect { repository.add_branch(user, 'new_feature', 'master') }.not_to raise_error + end + + it 'should create the branch' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return(true) + + branch = repository.add_branch(user, 'new_feature', 'master') + + expect(branch.name).to eq('new_feature') + end + end + + context 'when pre hooks failed' do + it 'should get an error' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return(false) + + expect do + repository.add_branch(user, 'new_feature', 'master') + end.to raise_error(GitHooksService::PreReceiveError) + end + + it 'should not create the branch' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return(false) + + expect do + repository.add_branch(user, 'new_feature', 'master') + end.to raise_error(GitHooksService::PreReceiveError) + expect(repository.find_branch('new_feature')).to be_nil + end + end + end + + describe :rm_branch do + context 'when pre hooks were successful' do + it 'should run without errors' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return(true) + + expect { repository.rm_branch(user, 'feature') }.not_to raise_error + end + + it 'should delete the branch' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return(true) + + expect { repository.rm_branch(user, 'feature') }.not_to raise_error + + expect(repository.find_branch('feature')).to be_nil + end + end + + context 'when pre hooks failed' do + it 'should get an error' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return(false) + + expect do + repository.rm_branch(user, 'new_feature') + end.to raise_error(GitHooksService::PreReceiveError) + end + + it 'should not delete the branch' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return(false) + + expect do + repository.rm_branch(user, 'feature') + end.to raise_error(GitHooksService::PreReceiveError) + expect(repository.find_branch('feature')).not_to be_nil + end + end end + + describe :commit_with_hooks do + context 'when pre hooks were successful' do + it 'should run without errors' do + expect_any_instance_of(GitHooksService).to receive(:execute).and_return(true) + + expect do + repository.commit_with_hooks(user, 'feature') { sample_commit.id } + end.not_to raise_error + end + end + + context 'when pre hooks failed' do + it 'should get an error' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return(false) + + expect do + repository.commit_with_hooks(user, 'feature') { sample_commit.id } + end.to raise_error(GitHooksService::PreReceiveError) + 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/models/user_spec.rb b/spec/models/user_spec.rb index 480950859a2..a0f78d3b336 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -54,6 +54,8 @@ # public_email :string(255) default(""), not null # dashboard :integer default(0) # project_view :integer default(0) +# consumed_timestep :integer +# layout :integer default(0) # require 'spec_helper' @@ -85,10 +87,27 @@ describe User do it { is_expected.to have_many(:merge_requests).dependent(:destroy) } it { is_expected.to have_many(:assigned_merge_requests).dependent(:destroy) } it { is_expected.to have_many(:identities).dependent(:destroy) } + it { is_expected.to have_one(:abuse_report) } end describe 'validations' do - it { is_expected.to validate_presence_of(:username) } + describe 'username' do + it 'validates presence' do + expect(subject).to validate_presence_of(:username) + end + + it 'rejects blacklisted names' do + user = build(:user, username: 'dashboard') + + expect(user).not_to be_valid + expect(user.errors.values).to eq [['dashboard is a reserved name']] + end + + it 'validates uniqueness' do + expect(subject).to validate_uniqueness_of(:username) + end + end + it { is_expected.to validate_presence_of(:projects_limit) } it { is_expected.to validate_numericality_of(:projects_limit) } it { is_expected.to allow_value(0).for(:projects_limit) } @@ -227,6 +246,26 @@ describe User do end end + describe '#recently_sent_password_reset?' do + it 'is false when reset_password_sent_at is nil' do + user = build_stubbed(:user, reset_password_sent_at: nil) + + expect(user.recently_sent_password_reset?).to eq false + end + + it 'is false when sent more than one minute ago' do + user = build_stubbed(:user, reset_password_sent_at: 5.minutes.ago) + + expect(user.recently_sent_password_reset?).to eq false + end + + it 'is true when sent less than one minute ago' do + user = build_stubbed(:user, reset_password_sent_at: Time.now) + + expect(user.recently_sent_password_reset?).to eq true + end + end + describe '#disable_two_factor!' do it 'clears all 2FA-related fields' do user = create(:user, :two_factor) @@ -642,28 +681,28 @@ describe User do @user1 = create :user, created_at: Date.today - 1, last_sign_in_at: Date.today - 1, name: 'Omega' end - it "sorts users as recently_signed_in" do + it "sorts users by the recent sign-in time" do expect(User.sort('recent_sign_in').first).to eq(@user) end - it "sorts users as late_signed_in" do + it "sorts users by the oldest sign-in time" do expect(User.sort('oldest_sign_in').first).to eq(@user1) end - it "sorts users as recently_created" do + it "sorts users in descending order by their creation time" do expect(User.sort('created_desc').first).to eq(@user) end - it "sorts users as late_created" do + it "sorts users in ascending order by their creation time" do expect(User.sort('created_asc').first).to eq(@user1) end - it "sorts users by name when nil is passed" do - expect(User.sort(nil).first).to eq(@user) + it "sorts users by id in descending order when nil is passed" do + expect(User.sort(nil).first).to eq(@user1) end end - describe "#contributed_projects_ids" do + describe "#contributed_projects" do subject { create(:user) } let!(:project1) { create(:project) } let!(:project2) { create(:project, forked_from_project: project3) } @@ -678,15 +717,15 @@ describe User do end it "includes IDs for projects the user has pushed to" do - expect(subject.contributed_projects_ids).to include(project1.id) + expect(subject.contributed_projects).to include(project1) end it "includes IDs for projects the user has had merge requests merged into" do - expect(subject.contributed_projects_ids).to include(project3.id) + expect(subject.contributed_projects).to include(project3) end it "doesn't include IDs for unrelated projects" do - expect(subject.contributed_projects_ids).not_to include(project2.id) + expect(subject.contributed_projects).not_to include(project2) end end @@ -735,4 +774,30 @@ describe User do expect(subject.recent_push).to eq(nil) end end + + describe '#authorized_groups' do + let!(:user) { create(:user) } + let!(:private_group) { create(:group) } + + before do + private_group.add_user(user, Gitlab::Access::MASTER) + end + + subject { user.authorized_groups } + + it { is_expected.to eq([private_group]) } + end + + describe '#authorized_projects' do + let!(:user) { create(:user) } + let!(:private_project) { create(:project, :private) } + + before do + private_project.team << [user, Gitlab::Access::MASTER] + end + + subject { user.authorized_projects } + + it { is_expected.to eq([private_project]) } + end end diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index dc84a14bb40..d7802d1734f 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -196,7 +196,7 @@ describe WikiPage do end def commit_details - commit = { name: user.name, email: user.email, message: "test commit" } + { name: user.name, email: user.email, message: "test commit" } end def create_page(name, content) |