summaryrefslogtreecommitdiff
path: root/spec/models
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models')
-rw-r--r--spec/models/build_spec.rb197
-rw-r--r--spec/models/ci/pipeline_spec.rb13
-rw-r--r--spec/models/ci/runner_spec.rb241
-rw-r--r--spec/models/commit_spec.rb12
-rw-r--r--spec/models/commit_status_spec.rb19
-rw-r--r--spec/models/concerns/access_requestable_spec.rb40
-rw-r--r--spec/models/concerns/milestoneish_spec.rb14
-rw-r--r--spec/models/concerns/participable_spec.rb10
-rw-r--r--spec/models/deployment_spec.rb17
-rw-r--r--spec/models/environment_spec.rb14
-rw-r--r--spec/models/event_spec.rb6
-rw-r--r--spec/models/group_spec.rb60
-rw-r--r--spec/models/jira_issue_spec.rb30
-rw-r--r--spec/models/key_spec.rb2
-rw-r--r--spec/models/member_spec.rb112
-rw-r--r--spec/models/members/group_member_spec.rb18
-rw-r--r--spec/models/members/project_member_spec.rb18
-rw-r--r--spec/models/note_spec.rb15
-rw-r--r--spec/models/personal_access_token_spec.rb15
-rw-r--r--spec/models/project_services/bamboo_service_spec.rb12
-rw-r--r--spec/models/project_services/jira_service_spec.rb6
-rw-r--r--spec/models/project_services/teamcity_service_spec.rb10
-rw-r--r--spec/models/project_spec.rb50
-rw-r--r--spec/models/project_team_spec.rb144
-rw-r--r--spec/models/repository_spec.rb41
25 files changed, 915 insertions, 201 deletions
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index 2beb6cc598d..8154001cf46 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -2,7 +2,12 @@ require 'spec_helper'
describe Ci::Build, models: true do
let(:project) { create(:project) }
- let(:pipeline) { create(:ci_pipeline, project: project) }
+
+ let(:pipeline) do
+ create(:ci_pipeline, project: project,
+ sha: project.commit.id)
+ end
+
let(:build) { create(:ci_build, pipeline: pipeline) }
it { is_expected.to validate_presence_of :ref }
@@ -36,32 +41,44 @@ describe Ci::Build, models: true do
subject { build.ignored? }
context 'if build is not allowed to fail' do
- before { build.allow_failure = false }
+ before do
+ build.allow_failure = false
+ end
context 'and build.status is success' do
- before { build.status = 'success' }
+ before do
+ build.status = 'success'
+ end
it { is_expected.to be_falsey }
end
context 'and build.status is failed' do
- before { build.status = 'failed' }
+ before do
+ build.status = 'failed'
+ end
it { is_expected.to be_falsey }
end
end
context 'if build is allowed to fail' do
- before { build.allow_failure = true }
+ before do
+ build.allow_failure = true
+ end
context 'and build.status is success' do
- before { build.status = 'success' }
+ before do
+ build.status = 'success'
+ end
it { is_expected.to be_falsey }
end
context 'and build.status is failed' do
- before { build.status = 'failed' }
+ before do
+ build.status = 'failed'
+ end
it { is_expected.to be_truthy }
end
@@ -75,7 +92,9 @@ describe Ci::Build, models: true do
context 'if build.trace contains text' do
let(:text) { 'example output' }
- before { build.trace = text }
+ before do
+ build.trace = text
+ end
it { is_expected.to include(text) }
it { expect(subject.length).to be >= text.length }
@@ -188,7 +207,9 @@ describe Ci::Build, models: true do
]
end
- before { build.update_attributes(stage: 'stage') }
+ before do
+ build.update_attributes(stage: 'stage')
+ end
it { is_expected.to eq(predefined_variables + yaml_variables) }
@@ -199,7 +220,9 @@ describe Ci::Build, models: true do
]
end
- before { build.update_attributes(tag: true) }
+ before do
+ build.update_attributes(tag: true)
+ end
it { is_expected.to eq(tag_variable + predefined_variables + yaml_variables) }
end
@@ -257,57 +280,6 @@ describe Ci::Build, models: true do
end
end
- describe '#can_be_served?' do
- let(:runner) { create(:ci_runner) }
-
- before { build.project.runners << runner }
-
- context 'when runner does not have 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 'when runner has tags' do
- before { runner.tag_list = ['bb', 'cc'] }
-
- shared_examples 'tagged build picker' do
- 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 without matching tags' do
- build.tag_list = ['aa']
- expect(build.can_be_served?(runner)).to be_falsey
- end
- end
-
- context 'when runner can pick untagged jobs' do
- it 'can handle builds without tags' do
- expect(build.can_be_served?(runner)).to be_truthy
- end
-
- it_behaves_like 'tagged build picker'
- end
-
- context 'when runner can not pick untagged jobs' do
- before { runner.run_untagged = false }
-
- it 'can not handle builds without tags' do
- expect(build.can_be_served?(runner)).to be_falsey
- end
-
- it_behaves_like 'tagged build picker'
- end
- end
- end
-
describe '#has_tags?' do
context 'when build has tags' do
subject { create(:ci_build, tag_list: ['tag']) }
@@ -348,7 +320,7 @@ describe Ci::Build, models: true do
end
it 'that cannot handle build' do
- expect_any_instance_of(Ci::Build).to receive(:can_be_served?).and_return(false)
+ expect_any_instance_of(Ci::Runner).to receive(:can_pick?).and_return(false)
is_expected.to be_falsey
end
@@ -360,7 +332,9 @@ describe Ci::Build, models: true do
%w(pending).each do |state|
context "if commit_status.status is #{state}" do
- before { build.status = state }
+ before do
+ build.status = state
+ end
it { is_expected.to be_truthy }
@@ -379,7 +353,9 @@ describe Ci::Build, models: true do
%w(success failed canceled running).each do |state|
context "if commit_status.status is #{state}" do
- before { build.status = state }
+ before do
+ build.status = state
+ end
it { is_expected.to be_falsey }
end
@@ -390,16 +366,44 @@ describe Ci::Build, models: true do
subject { build.artifacts? }
context 'artifacts archive does not exist' do
- before { build.update_attributes(artifacts_file: nil) }
+ before do
+ build.update_attributes(artifacts_file: nil)
+ end
+
it { is_expected.to be_falsy }
end
context 'artifacts archive exists' do
let(:build) { create(:ci_build, :artifacts) }
it { is_expected.to be_truthy }
+
+ context 'is expired' do
+ before { build.update(artifacts_expire_at: Time.now - 7.days) }
+ it { is_expected.to be_falsy }
+ end
+
+ context 'is not expired' do
+ before { build.update(artifacts_expire_at: Time.now + 7.days) }
+ it { is_expected.to be_truthy }
+ end
end
end
+ describe '#artifacts_expired?' do
+ subject { build.artifacts_expired? }
+
+ context 'is expired' do
+ before { build.update(artifacts_expire_at: Time.now - 7.days) }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'is not expired' do
+ before { build.update(artifacts_expire_at: Time.now + 7.days) }
+
+ it { is_expected.to be_falsey }
+ end
+ end
describe '#artifacts_metadata?' do
subject { build.artifacts_metadata? }
@@ -412,7 +416,6 @@ describe Ci::Build, models: true do
it { is_expected.to be_truthy }
end
end
-
describe '#repo_url' do
let(:build) { create(:ci_build) }
let(:project) { build.project }
@@ -427,6 +430,50 @@ describe Ci::Build, models: true do
it { is_expected.to include(project.web_url[7..-1]) }
end
+ describe '#artifacts_expire_in' do
+ subject { build.artifacts_expire_in }
+ it { is_expected.to be_nil }
+
+ context 'when artifacts_expire_at is specified' do
+ let(:expire_at) { Time.now + 7.days }
+
+ before { build.artifacts_expire_at = expire_at }
+
+ it { is_expected.to be_within(5).of(expire_at - Time.now) }
+ end
+ end
+
+ describe '#artifacts_expire_in=' do
+ subject { build.artifacts_expire_in }
+
+ it 'when assigning valid duration' do
+ build.artifacts_expire_in = '7 days'
+
+ is_expected.to be_within(10).of(7.days.to_i)
+ end
+
+ it 'when assigning invalid duration' do
+ expect { build.artifacts_expire_in = '7 elephants' }.to raise_error(ChronicDuration::DurationParseError)
+ is_expected.to be_nil
+ end
+
+ it 'when resseting value' do
+ build.artifacts_expire_in = nil
+
+ is_expected.to be_nil
+ end
+ end
+
+ describe '#keep_artifacts!' do
+ let(:build) { create(:ci_build, artifacts_expire_at: Time.now + 7.days) }
+
+ it 'to reset expire_at' do
+ build.keep_artifacts!
+
+ expect(build.artifacts_expire_at).to be_nil
+ end
+ end
+
describe '#depends_on_builds' do
let!(:build) { create(:ci_build, pipeline: pipeline, name: 'build', stage_idx: 0, stage: 'build') }
let!(:rspec_test) { create(:ci_build, pipeline: pipeline, name: 'rspec', stage_idx: 1, stage: 'test') }
@@ -555,7 +602,9 @@ describe Ci::Build, models: true do
let!(:build) { create(:ci_build, :trace, :success, :artifacts) }
describe '#erase' do
- before { build.erase(erased_by: user) }
+ before do
+ build.erase(erased_by: user)
+ end
context 'erased by user' do
let!(:user) { create(:user, username: 'eraser') }
@@ -592,7 +641,9 @@ describe Ci::Build, models: true do
end
context 'build has been erased' do
- before { build.erase }
+ before do
+ build.erase
+ end
it { is_expected.to be true }
end
@@ -600,7 +651,9 @@ describe Ci::Build, models: true do
context 'metadata and build trace are not available' do
let!(:build) { create(:ci_build, :success, :artifacts) }
- before { build.remove_artifacts_metadata! }
+ before do
+ build.remove_artifacts_metadata!
+ end
describe '#erase' do
it 'should not raise error' do
@@ -610,4 +663,10 @@ describe Ci::Build, models: true do
end
end
end
+
+ describe '#commit' do
+ it 'returns commit pipeline has been created for' do
+ expect(build.commit).to eq project.commit
+ end
+ end
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 0d769ed7324..34507cf5083 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -258,6 +258,19 @@ describe Ci::Pipeline, models: true do
end
end
end
+
+ context 'when no builds created' do
+ let(:pipeline) { build(:ci_pipeline) }
+
+ before do
+ stub_ci_pipeline_yaml_file(YAML.dump(before_script: ['ls']))
+ end
+
+ it 'returns false' do
+ expect(pipeline.create_builds(nil)).to be_falsey
+ expect(pipeline).not_to be_persisted
+ end
+ end
end
describe "#finished_at" do
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 5d04d8ffcff..ef65eb99328 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -20,34 +20,36 @@ describe Ci::Runner, models: true do
end
describe '#display_name' do
- it 'should return the description if it has a value' do
+ it 'returns the description if it has a value' do
runner = FactoryGirl.build(:ci_runner, description: 'Linux/Ruby-1.9.3-p448')
expect(runner.display_name).to eq 'Linux/Ruby-1.9.3-p448'
end
- it 'should return the token if it does not have a description' do
+ it 'returns the token if it does not have a description' do
runner = FactoryGirl.create(:ci_runner)
expect(runner.display_name).to eq runner.description
end
- it 'should return the token if the description is an empty string' do
+ it 'returns the token if the description is an empty string' do
runner = FactoryGirl.build(:ci_runner, description: '', token: 'token')
expect(runner.display_name).to eq runner.token
end
end
- describe :assign_to do
+ describe '#assign_to' do
let!(:project) { FactoryGirl.create :empty_project }
let!(:shared_runner) { FactoryGirl.create(:ci_runner, :shared) }
- before { shared_runner.assign_to(project) }
+ before do
+ shared_runner.assign_to(project)
+ end
it { expect(shared_runner).to be_specific }
it { expect(shared_runner.projects).to eq([project]) }
it { expect(shared_runner.only_for?(project)).to be_truthy }
end
- describe :online do
+ describe '.online' do
subject { Ci::Runner.online }
before do
@@ -58,60 +60,269 @@ describe Ci::Runner, models: true do
it { is_expected.to eq([@runner2])}
end
- describe :online? do
+ describe '#online?' do
let(:runner) { FactoryGirl.create(:ci_runner, :shared) }
subject { runner.online? }
context 'never contacted' do
- before { runner.contacted_at = nil }
+ before do
+ runner.contacted_at = nil
+ end
it { is_expected.to be_falsey }
end
context 'contacted long time ago time' do
- before { runner.contacted_at = 1.year.ago }
+ before do
+ runner.contacted_at = 1.year.ago
+ end
it { is_expected.to be_falsey }
end
context 'contacted 1s ago' do
- before { runner.contacted_at = 1.second.ago }
+ before do
+ runner.contacted_at = 1.second.ago
+ end
it { is_expected.to be_truthy }
end
end
- describe :status do
+ describe '#can_pick?' do
+ let(:project) { create(:project) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+ let(:runner) { create(:ci_runner) }
+
+ before do
+ build.project.runners << runner
+ end
+
+ context 'when runner does not have tags' do
+ it 'can handle builds without tags' do
+ expect(runner.can_pick?(build)).to be_truthy
+ end
+
+ it 'cannot handle build with tags' do
+ build.tag_list = ['aa']
+
+ expect(runner.can_pick?(build)).to be_falsey
+ end
+ end
+
+ context 'when runner has tags' do
+ before do
+ runner.tag_list = ['bb', 'cc']
+ end
+
+ shared_examples 'tagged build picker' do
+ it 'can handle build with matching tags' do
+ build.tag_list = ['bb']
+
+ expect(runner.can_pick?(build)).to be_truthy
+ end
+
+ it 'cannot handle build without matching tags' do
+ build.tag_list = ['aa']
+
+ expect(runner.can_pick?(build)).to be_falsey
+ end
+ end
+
+ context 'when runner can pick untagged jobs' do
+ it 'can handle builds without tags' do
+ expect(runner.can_pick?(build)).to be_truthy
+ end
+
+ it_behaves_like 'tagged build picker'
+ end
+
+ context 'when runner cannot pick untagged jobs' do
+ before do
+ runner.run_untagged = false
+ end
+
+ it 'cannot handle builds without tags' do
+ expect(runner.can_pick?(build)).to be_falsey
+ end
+
+ it_behaves_like 'tagged build picker'
+ end
+ end
+
+ context 'when runner is locked' do
+ before do
+ runner.locked = true
+ end
+
+ shared_examples 'locked build picker' do
+ context 'when runner cannot pick untagged jobs' do
+ before do
+ runner.run_untagged = false
+ end
+
+ it 'cannot handle builds without tags' do
+ expect(runner.can_pick?(build)).to be_falsey
+ end
+ end
+
+ context 'when having runner tags' do
+ before do
+ runner.tag_list = ['bb', 'cc']
+ end
+
+ it 'cannot handle it for builds without matching tags' do
+ build.tag_list = ['aa']
+
+ expect(runner.can_pick?(build)).to be_falsey
+ end
+ end
+ end
+
+ context 'when serving the same project' do
+ it 'can handle it' do
+ expect(runner.can_pick?(build)).to be_truthy
+ end
+
+ it_behaves_like 'locked build picker'
+
+ context 'when having runner tags' do
+ before do
+ runner.tag_list = ['bb', 'cc']
+ build.tag_list = ['bb']
+ end
+
+ it 'can handle it for matching tags' do
+ expect(runner.can_pick?(build)).to be_truthy
+ end
+ end
+ end
+
+ context 'serving a different project' do
+ before do
+ runner.runner_projects.destroy_all
+ end
+
+ it 'cannot handle it' do
+ expect(runner.can_pick?(build)).to be_falsey
+ end
+
+ it_behaves_like 'locked build picker'
+
+ context 'when having runner tags' do
+ before do
+ runner.tag_list = ['bb', 'cc']
+ build.tag_list = ['bb']
+ end
+
+ it 'cannot handle it for matching tags' do
+ expect(runner.can_pick?(build)).to be_falsey
+ end
+ end
+ end
+ end
+ end
+
+ describe '#status' do
let(:runner) { FactoryGirl.create(:ci_runner, :shared, contacted_at: 1.second.ago) }
subject { runner.status }
context 'never connected' do
- before { runner.contacted_at = nil }
+ before do
+ runner.contacted_at = nil
+ end
it { is_expected.to eq(:not_connected) }
end
context 'contacted 1s ago' do
- before { runner.contacted_at = 1.second.ago }
+ before do
+ runner.contacted_at = 1.second.ago
+ end
it { is_expected.to eq(:online) }
end
context 'contacted long time ago' do
- before { runner.contacted_at = 1.year.ago }
+ before do
+ runner.contacted_at = 1.year.ago
+ end
it { is_expected.to eq(:offline) }
end
context 'inactive' do
- before { runner.active = false }
+ before do
+ runner.active = false
+ end
it { is_expected.to eq(:paused) }
end
end
+ describe '.assignable_for' do
+ let(:runner) { create(:ci_runner) }
+ let(:project) { create(:project) }
+ let(:another_project) { create(:project) }
+
+ before do
+ project.runners << runner
+ end
+
+ context 'with shared runners' do
+ before do
+ runner.update(is_shared: true)
+ end
+
+ context 'does not give owned runner' do
+ subject { Ci::Runner.assignable_for(project) }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'does not give shared runner' do
+ subject { Ci::Runner.assignable_for(another_project) }
+
+ it { is_expected.to be_empty }
+ end
+ end
+
+ context 'with unlocked runner' do
+ context 'does not give owned runner' do
+ subject { Ci::Runner.assignable_for(project) }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'does give a specific runner' do
+ subject { Ci::Runner.assignable_for(another_project) }
+
+ it { is_expected.to contain_exactly(runner) }
+ end
+ end
+
+ context 'with locked runner' do
+ before do
+ runner.update(locked: true)
+ end
+
+ context 'does not give owned runner' do
+ subject { Ci::Runner.assignable_for(project) }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'does not give a locked runner' do
+ subject { Ci::Runner.assignable_for(another_project) }
+
+ it { is_expected.to be_empty }
+ end
+ end
+ end
+
describe "belongs_to_one_project?" do
it "returns false if there are two projects runner assigned to" do
runner = FactoryGirl.create(:ci_runner)
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index beca8708c9d..ba02d5fe977 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -207,4 +207,16 @@ eos
expect(commit.participants).to include(note1.author, note2.author)
end
end
+
+ describe '#uri_type' do
+ it 'returns the URI type at the given path' do
+ expect(commit.uri_type('files/html')).to be(:tree)
+ expect(commit.uri_type('files/images/logo-black.png')).to be(:raw)
+ expect(commit.uri_type('files/js/application.js')).to be(:blob)
+ end
+
+ it "returns nil if the path doesn't exists" do
+ expect(commit.uri_type('this/path/doesnt/exist')).to be_nil
+ end
+ end
end
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index 8fb605fff8a..96397d7c8a9 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -1,8 +1,13 @@
require 'spec_helper'
describe CommitStatus, models: true do
- let(:pipeline) { FactoryGirl.create :ci_pipeline }
- let(:commit_status) { FactoryGirl.create :commit_status, pipeline: pipeline }
+ let(:project) { create(:project) }
+
+ let(:pipeline) do
+ create(:ci_pipeline, project: project, sha: project.commit.id)
+ end
+
+ let(:commit_status) { create(:commit_status, pipeline: pipeline) }
it { is_expected.to belong_to(:pipeline) }
it { is_expected.to belong_to(:user) }
@@ -13,7 +18,7 @@ describe CommitStatus, models: true do
it { is_expected.to delegate_method(:sha).to(:pipeline) }
it { is_expected.to delegate_method(:short_sha).to(:pipeline) }
-
+
it { is_expected.to respond_to :success? }
it { is_expected.to respond_to :failed? }
it { is_expected.to respond_to :running? }
@@ -116,7 +121,7 @@ describe CommitStatus, models: true do
it { is_expected.to be > 0.0 }
end
end
-
+
describe :latest do
subject { CommitStatus.latest.order(:id) }
@@ -198,4 +203,10 @@ describe CommitStatus, models: true do
end
end
end
+
+ describe '#commit' do
+ it 'returns commit pipeline has been created for' do
+ expect(commit_status.commit).to eq project.commit
+ end
+ end
end
diff --git a/spec/models/concerns/access_requestable_spec.rb b/spec/models/concerns/access_requestable_spec.rb
new file mode 100644
index 00000000000..98307876962
--- /dev/null
+++ b/spec/models/concerns/access_requestable_spec.rb
@@ -0,0 +1,40 @@
+require 'spec_helper'
+
+describe AccessRequestable do
+ describe 'Group' do
+ describe '#request_access' do
+ let(:group) { create(:group, :public) }
+ let(:user) { create(:user) }
+
+ it { expect(group.request_access(user)).to be_a(GroupMember) }
+ it { expect(group.request_access(user).user).to eq(user) }
+ end
+
+ describe '#access_requested?' do
+ let(:group) { create(:group, :public) }
+ let(:user) { create(:user) }
+
+ before { group.request_access(user) }
+
+ it { expect(group.members.request.exists?(user_id: user)).to be_truthy }
+ end
+ end
+
+ describe 'Project' do
+ describe '#request_access' do
+ let(:project) { create(:empty_project, :public) }
+ let(:user) { create(:user) }
+
+ it { expect(project.request_access(user)).to be_a(ProjectMember) }
+ end
+
+ describe '#access_requested?' do
+ let(:project) { create(:empty_project, :public) }
+ let(:user) { create(:user) }
+
+ before { project.request_access(user) }
+
+ it { expect(project.members.request.exists?(user_id: user)).to be_truthy }
+ end
+ end
+end
diff --git a/spec/models/concerns/milestoneish_spec.rb b/spec/models/concerns/milestoneish_spec.rb
index 47c3be673c5..7e9ab8940cf 100644
--- a/spec/models/concerns/milestoneish_spec.rb
+++ b/spec/models/concerns/milestoneish_spec.rb
@@ -5,6 +5,7 @@ describe Milestone, 'Milestoneish' do
let(:assignee) { create(:user) }
let(:non_member) { create(:user) }
let(:member) { create(:user) }
+ let(:guest) { create(:user) }
let(:admin) { create(:admin) }
let(:project) { create(:project, :public) }
let(:milestone) { create(:milestone, project: project) }
@@ -21,6 +22,7 @@ describe Milestone, 'Milestoneish' do
before do
project.team << [member, :developer]
+ project.team << [guest, :guest]
end
describe '#closed_items_count' do
@@ -28,6 +30,10 @@ describe Milestone, 'Milestoneish' do
expect(milestone.closed_items_count(non_member)).to eq 2
end
+ it 'should not count confidential issues for project members with guest role' do
+ expect(milestone.closed_items_count(guest)).to eq 2
+ end
+
it 'should count confidential issues for author' do
expect(milestone.closed_items_count(author)).to eq 4
end
@@ -50,6 +56,10 @@ describe Milestone, 'Milestoneish' do
expect(milestone.total_items_count(non_member)).to eq 4
end
+ it 'should not count confidential issues for project members with guest role' do
+ expect(milestone.total_items_count(guest)).to eq 4
+ end
+
it 'should count confidential issues for author' do
expect(milestone.total_items_count(author)).to eq 7
end
@@ -85,6 +95,10 @@ describe Milestone, 'Milestoneish' do
expect(milestone.percent_complete(non_member)).to eq 50
end
+ it 'should not count confidential issues for project members with guest role' do
+ expect(milestone.percent_complete(guest)).to eq 50
+ end
+
it 'should count confidential issues for author' do
expect(milestone.percent_complete(author)).to eq 57
end
diff --git a/spec/models/concerns/participable_spec.rb b/spec/models/concerns/participable_spec.rb
index 7e4ea0f2d66..a9f4ef9ee5e 100644
--- a/spec/models/concerns/participable_spec.rb
+++ b/spec/models/concerns/participable_spec.rb
@@ -37,6 +37,16 @@ describe Participable, models: true do
expect(participants).to include(user3)
end
+ it 'caches the raw list of participants' do
+ instance = model.new
+ user1 = build(:user)
+
+ expect(instance).to receive(:raw_participants).once
+
+ instance.participants(user1)
+ instance.participants(user1)
+ end
+
it 'supports attributes returning another Participable' do
other_model = Class.new { include Participable }
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
new file mode 100644
index 00000000000..b273018707f
--- /dev/null
+++ b/spec/models/deployment_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+
+describe Deployment, models: true do
+ subject { build(:deployment) }
+
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:environment) }
+ it { is_expected.to belong_to(:user) }
+ it { is_expected.to belong_to(:deployable) }
+
+ it { is_expected.to delegate_method(:name).to(:environment).with_prefix }
+ it { is_expected.to delegate_method(:commit).to(:project) }
+ it { is_expected.to delegate_method(:commit_title).to(:commit).as(:try) }
+
+ it { is_expected.to validate_presence_of(:ref) }
+ it { is_expected.to validate_presence_of(:sha) }
+end
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
new file mode 100644
index 00000000000..7629af6a570
--- /dev/null
+++ b/spec/models/environment_spec.rb
@@ -0,0 +1,14 @@
+require 'spec_helper'
+
+describe Environment, models: true do
+ let(:environment) { create(:environment) }
+
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to have_many(:deployments) }
+
+ it { is_expected.to delegate_method(:last_deployment).to(:deployments).as(:last) }
+
+ it { is_expected.to validate_presence_of(:name) }
+ it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id) }
+ it { is_expected.to validate_length_of(:name).is_within(0..255) }
+end
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index b0e76fec693..166a1dc4ddb 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -50,6 +50,7 @@ describe Event, models: true do
let(:project) { create(:empty_project, :public) }
let(:non_member) { create(:user) }
let(:member) { create(:user) }
+ let(:guest) { create(:user) }
let(:author) { create(:author) }
let(:assignee) { create(:user) }
let(:admin) { create(:admin) }
@@ -61,6 +62,7 @@ describe Event, models: true do
before do
project.team << [member, :developer]
+ project.team << [guest, :guest]
end
context 'issue event' do
@@ -71,6 +73,7 @@ describe Event, models: true do
it { expect(event.visible_to_user?(author)).to eq true }
it { expect(event.visible_to_user?(assignee)).to eq true }
it { expect(event.visible_to_user?(member)).to eq true }
+ it { expect(event.visible_to_user?(guest)).to eq true }
it { expect(event.visible_to_user?(admin)).to eq true }
end
@@ -81,6 +84,7 @@ describe Event, models: true do
it { expect(event.visible_to_user?(author)).to eq true }
it { expect(event.visible_to_user?(assignee)).to eq true }
it { expect(event.visible_to_user?(member)).to eq true }
+ it { expect(event.visible_to_user?(guest)).to eq false }
it { expect(event.visible_to_user?(admin)).to eq true }
end
end
@@ -93,6 +97,7 @@ describe Event, models: true do
it { expect(event.visible_to_user?(author)).to eq true }
it { expect(event.visible_to_user?(assignee)).to eq true }
it { expect(event.visible_to_user?(member)).to eq true }
+ it { expect(event.visible_to_user?(guest)).to eq true }
it { expect(event.visible_to_user?(admin)).to eq true }
end
@@ -103,6 +108,7 @@ describe Event, models: true do
it { expect(event.visible_to_user?(author)).to eq true }
it { expect(event.visible_to_user?(assignee)).to eq true }
it { expect(event.visible_to_user?(member)).to eq true }
+ it { expect(event.visible_to_user?(guest)).to eq false }
it { expect(event.visible_to_user?(admin)).to eq true }
end
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 6fa16be7f04..2c19aa3f67f 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -5,7 +5,11 @@ describe Group, models: true do
describe 'associations' do
it { is_expected.to have_many :projects }
- it { is_expected.to have_many :group_members }
+ it { is_expected.to have_many(:group_members).dependent(:destroy) }
+ it { is_expected.to have_many(:users).through(:group_members) }
+ it { is_expected.to have_many(:project_group_links).dependent(:destroy) }
+ it { is_expected.to have_many(:shared_projects).through(:project_group_links) }
+ it { is_expected.to have_many(:notification_settings).dependent(:destroy) }
end
describe 'modules' do
@@ -131,4 +135,58 @@ describe Group, models: true do
expect(described_class.search(group.path.upcase)).to eq([group])
end
end
+
+ describe '#has_owner?' do
+ before { @members = setup_group_members(group) }
+
+ it { expect(group.has_owner?(@members[:owner])).to be_truthy }
+ it { expect(group.has_owner?(@members[:master])).to be_falsey }
+ it { expect(group.has_owner?(@members[:developer])).to be_falsey }
+ it { expect(group.has_owner?(@members[:reporter])).to be_falsey }
+ it { expect(group.has_owner?(@members[:guest])).to be_falsey }
+ it { expect(group.has_owner?(@members[:requester])).to be_falsey }
+ end
+
+ describe '#has_master?' do
+ before { @members = setup_group_members(group) }
+
+ it { expect(group.has_master?(@members[:owner])).to be_falsey }
+ it { expect(group.has_master?(@members[:master])).to be_truthy }
+ it { expect(group.has_master?(@members[:developer])).to be_falsey }
+ it { expect(group.has_master?(@members[:reporter])).to be_falsey }
+ it { expect(group.has_master?(@members[:guest])).to be_falsey }
+ it { expect(group.has_master?(@members[:requester])).to be_falsey }
+ end
+
+ describe '#owners' do
+ let(:owner) { create(:user) }
+ let(:developer) { create(:user) }
+
+ it 'returns the owners of a Group' do
+ group.add_owner(owner)
+ group.add_developer(developer)
+
+ expect(group.owners).to eq([owner])
+ end
+ end
+
+ def setup_group_members(group)
+ members = {
+ owner: create(:user),
+ master: create(:user),
+ developer: create(:user),
+ reporter: create(:user),
+ guest: create(:user),
+ requester: create(:user)
+ }
+
+ group.add_user(members[:owner], GroupMember::OWNER)
+ group.add_user(members[:master], GroupMember::MASTER)
+ group.add_user(members[:developer], GroupMember::DEVELOPER)
+ group.add_user(members[:reporter], GroupMember::REPORTER)
+ group.add_user(members[:guest], GroupMember::GUEST)
+ group.request_access(members[:requester])
+
+ members
+ end
end
diff --git a/spec/models/jira_issue_spec.rb b/spec/models/jira_issue_spec.rb
deleted file mode 100644
index 1634265b439..00000000000
--- a/spec/models/jira_issue_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require 'spec_helper'
-
-describe JiraIssue do
- let(:project) { create(:project) }
- subject { JiraIssue.new('JIRA-123', project) }
-
- describe 'id' do
- subject { super().id }
- it { is_expected.to eq('JIRA-123') }
- end
-
- describe 'iid' do
- subject { super().iid }
- it { is_expected.to eq('JIRA-123') }
- end
-
- describe 'to_s' do
- subject { super().to_s }
- it { is_expected.to eq('JIRA-123') }
- end
-
- describe :== do
- specify { expect(subject).to eq(JiraIssue.new('JIRA-123', project)) }
- specify { expect(subject).not_to eq(JiraIssue.new('JIRA-124', project)) }
-
- it 'only compares with JiraIssues' do
- expect(subject).not_to eq('JIRA-123')
- end
- end
-end
diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb
index 26fbedbef2f..49cf3d8633a 100644
--- a/spec/models/key_spec.rb
+++ b/spec/models/key_spec.rb
@@ -26,7 +26,7 @@ describe Key, models: true do
end
end
- context "validation of uniqueness" do
+ context "validation of uniqueness (based on fingerprint uniqueness)" do
let(:user) { create(:user) }
it "accepts the key once" do
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index 6e51730eecd..e9134a3d283 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -55,6 +55,80 @@ describe Member, models: true do
end
end
+ describe 'Scopes & finders' do
+ before do
+ project = create(:project)
+ group = create(:group)
+ @owner_user = create(:user).tap { |u| group.add_owner(u) }
+ @owner = group.members.find_by(user_id: @owner_user.id)
+
+ @master_user = create(:user).tap { |u| project.team << [u, :master] }
+ @master = project.members.find_by(user_id: @master_user.id)
+
+ ProjectMember.add_user(project.members, 'toto1@example.com', Gitlab::Access::DEVELOPER, @master_user)
+ @invited_member = project.members.invite.find_by_invite_email('toto1@example.com')
+
+ accepted_invite_user = build(:user)
+ ProjectMember.add_user(project.members, 'toto2@example.com', Gitlab::Access::DEVELOPER, @master_user)
+ @accepted_invite_member = project.members.invite.find_by_invite_email('toto2@example.com').tap { |u| u.accept_invite!(accepted_invite_user) }
+
+ requested_user = create(:user).tap { |u| project.request_access(u) }
+ @requested_member = project.members.request.find_by(user_id: requested_user.id)
+
+ accepted_request_user = create(:user).tap { |u| project.request_access(u) }
+ @accepted_request_member = project.members.request.find_by(user_id: accepted_request_user.id).tap { |m| m.accept_request }
+ end
+
+ describe '.invite' do
+ it { expect(described_class.invite).not_to include @master }
+ it { expect(described_class.invite).to include @invited_member }
+ it { expect(described_class.invite).not_to include @accepted_invite_member }
+ it { expect(described_class.invite).not_to include @requested_member }
+ it { expect(described_class.invite).not_to include @accepted_request_member }
+ end
+
+ describe '.non_invite' do
+ it { expect(described_class.non_invite).to include @master }
+ it { expect(described_class.non_invite).not_to include @invited_member }
+ it { expect(described_class.non_invite).to include @accepted_invite_member }
+ it { expect(described_class.non_invite).to include @requested_member }
+ it { expect(described_class.non_invite).to include @accepted_request_member }
+ end
+
+ describe '.request' do
+ it { expect(described_class.request).not_to include @master }
+ it { expect(described_class.request).not_to include @invited_member }
+ it { expect(described_class.request).not_to include @accepted_invite_member }
+ it { expect(described_class.request).to include @requested_member }
+ it { expect(described_class.request).not_to include @accepted_request_member }
+ end
+
+ describe '.non_request' do
+ it { expect(described_class.non_request).to include @master }
+ it { expect(described_class.non_request).to include @invited_member }
+ it { expect(described_class.non_request).to include @accepted_invite_member }
+ it { expect(described_class.non_request).not_to include @requested_member }
+ it { expect(described_class.non_request).to include @accepted_request_member }
+ end
+
+ describe '.non_pending' do
+ it { expect(described_class.non_pending).to include @master }
+ it { expect(described_class.non_pending).not_to include @invited_member }
+ it { expect(described_class.non_pending).to include @accepted_invite_member }
+ it { expect(described_class.non_pending).not_to include @requested_member }
+ it { expect(described_class.non_pending).to include @accepted_request_member }
+ end
+
+ describe '.owners_and_masters' do
+ it { expect(described_class.owners_and_masters).to include @owner }
+ it { expect(described_class.owners_and_masters).to include @master }
+ it { expect(described_class.owners_and_masters).not_to include @invited_member }
+ it { expect(described_class.owners_and_masters).not_to include @accepted_invite_member }
+ it { expect(described_class.owners_and_masters).not_to include @requested_member }
+ it { expect(described_class.owners_and_masters).not_to include @accepted_request_member }
+ end
+ end
+
describe "Delegate methods" do
it { is_expected.to respond_to(:user_name) }
it { is_expected.to respond_to(:user_email) }
@@ -97,6 +171,44 @@ describe Member, models: true do
end
end
+ describe '#accept_request' do
+ let(:member) { create(:project_member, requested_at: Time.now.utc) }
+
+ it { expect(member.accept_request).to be_truthy }
+
+ it 'clears requested_at' do
+ member.accept_request
+
+ expect(member.requested_at).to be_nil
+ end
+
+ it 'calls #after_accept_request' do
+ expect(member).to receive(:after_accept_request)
+
+ member.accept_request
+ end
+ end
+
+ describe '#invite?' do
+ subject { create(:project_member, invite_email: "user@example.com", user: nil) }
+
+ it { is_expected.to be_invite }
+ end
+
+ describe '#request?' do
+ subject { create(:project_member, requested_at: Time.now.utc) }
+
+ it { is_expected.to be_request }
+ end
+
+ describe '#pending?' do
+ let(:invited_member) { create(:project_member, invite_email: "user@example.com", user: nil) }
+ let(:requester) { create(:project_member, requested_at: Time.now.utc) }
+
+ it { expect(invited_member).to be_invite }
+ it { expect(requester).to be_pending }
+ end
+
describe "#accept_invite!" do
let!(:member) { create(:project_member, invite_email: "user@example.com", user: nil) }
let(:user) { create(:user) }
diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index 5424c9b9cba..18439cac2a4 100644
--- a/spec/models/members/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -20,7 +20,7 @@
require 'spec_helper'
describe GroupMember, models: true do
- context 'notification' do
+ describe 'notifications' do
describe "#after_create" do
it "should send email to user" do
membership = build(:group_member)
@@ -50,5 +50,21 @@ describe GroupMember, models: true do
@group_member.update_attribute(:access_level, GroupMember::OWNER)
end
end
+
+ describe '#after_accept_request' do
+ it 'calls NotificationService.accept_group_access_request' do
+ member = create(:group_member, user: build_stubbed(:user), requested_at: Time.now)
+
+ expect_any_instance_of(NotificationService).to receive(:new_group_member)
+
+ member.__send__(:after_accept_request)
+ end
+ end
+
+ describe '#real_source_type' do
+ subject { create(:group_member).real_source_type }
+
+ it { is_expected.to eq 'Group' }
+ end
end
end
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index 9f13874b532..bbf65edb27c 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -33,6 +33,12 @@ describe ProjectMember, models: true do
it { is_expected.to include_module(Gitlab::ShellAdapter) }
end
+ describe '#real_source_type' do
+ subject { create(:project_member).real_source_type }
+
+ it { is_expected.to eq 'Project' }
+ end
+
describe "#destroy" do
let(:owner) { create(:project_member, access_level: ProjectMember::OWNER) }
let(:project) { owner.project }
@@ -135,4 +141,16 @@ describe ProjectMember, models: true do
it { expect(@project_1.users).to be_empty }
it { expect(@project_2.users).to be_empty }
end
+
+ describe 'notifications' do
+ describe '#after_accept_request' do
+ it 'calls NotificationService.new_project_member' do
+ member = create(:project_member, user: build_stubbed(:user), requested_at: Time.now)
+
+ expect_any_instance_of(NotificationService).to receive(:new_project_member)
+
+ member.__send__(:after_accept_request)
+ end
+ end
+ end
end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index f15e96714b2..285ab19cfaf 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -162,16 +162,23 @@ describe Note, models: true do
end
context "confidential issues" do
- let(:user) { create :user }
- let(:confidential_issue) { create(:issue, :confidential, author: user) }
- let(:confidential_note) { create :note, note: "Random", noteable: confidential_issue, project: confidential_issue.project }
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:confidential_issue) { create(:issue, :confidential, project: project, author: user) }
+ let(:confidential_note) { create(:note, note: "Random", noteable: confidential_issue, project: confidential_issue.project) }
it "returns notes with matching content if user can see the issue" do
expect(described_class.search(confidential_note.note, as_user: user)).to eq([confidential_note])
end
it "does not return notes with matching content if user can not see the issue" do
- user = create :user
+ user = create(:user)
+ expect(described_class.search(confidential_note.note, as_user: user)).to be_empty
+ end
+
+ it "does not return notes with matching content for project members with guest role" do
+ user = create(:user)
+ project.team << [user, :guest]
expect(described_class.search(confidential_note.note, as_user: user)).to be_empty
end
diff --git a/spec/models/personal_access_token_spec.rb b/spec/models/personal_access_token_spec.rb
new file mode 100644
index 00000000000..46eb71cef14
--- /dev/null
+++ b/spec/models/personal_access_token_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe PersonalAccessToken, models: true do
+ describe ".generate" do
+ it "generates a random token" do
+ personal_access_token = PersonalAccessToken.generate({})
+ expect(personal_access_token.token).to be_present
+ end
+
+ it "doesn't save the record" do
+ personal_access_token = PersonalAccessToken.generate({})
+ expect(personal_access_token).not_to be_persisted
+ end
+ end
+end
diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb
index ec81f05fc7a..9ae461f8c2d 100644
--- a/spec/models/project_services/bamboo_service_spec.rb
+++ b/spec/models/project_services/bamboo_service_spec.rb
@@ -126,25 +126,25 @@ describe BambooService, models: true do
it 'returns a specific URL when status is 500' do
stub_request(status: 500)
- expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/browse/foo')
+ expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/bamboo/browse/foo')
end
it 'returns a specific URL when response has no results' do
stub_request(body: %Q({"results":{"results":{"size":"0"}}}))
- expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/browse/foo')
+ expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/bamboo/browse/foo')
end
it 'returns a build URL when bamboo_url has no trailing slash' do
stub_request(body: %Q({"results":{"results":{"result":{"planResultKey":{"key":"42"}}}}}))
- expect(service(bamboo_url: 'http://gitlab.com').build_page('123', 'unused')).to eq('http://gitlab.com/browse/42')
+ expect(service(bamboo_url: 'http://gitlab.com/bamboo').build_page('123', 'unused')).to eq('http://gitlab.com/bamboo/browse/42')
end
it 'returns a build URL when bamboo_url has a trailing slash' do
stub_request(body: %Q({"results":{"results":{"result":{"planResultKey":{"key":"42"}}}}}))
- expect(service(bamboo_url: 'http://gitlab.com/').build_page('123', 'unused')).to eq('http://gitlab.com/browse/42')
+ expect(service(bamboo_url: 'http://gitlab.com/bamboo/').build_page('123', 'unused')).to eq('http://gitlab.com/bamboo/browse/42')
end
end
@@ -192,7 +192,7 @@ describe BambooService, models: true do
end
end
- def service(bamboo_url: 'http://gitlab.com')
+ def service(bamboo_url: 'http://gitlab.com/bamboo')
described_class.create(
project: create(:empty_project),
properties: {
@@ -205,7 +205,7 @@ describe BambooService, models: true do
end
def stub_request(status: 200, body: nil, build_state: 'success')
- bamboo_full_url = 'http://mic:password@gitlab.com/rest/api/latest/result?label=123&os_authType=basic'
+ bamboo_full_url = 'http://mic:password@gitlab.com/bamboo/rest/api/latest/result?label=123&os_authType=basic'
body ||= %Q({"results":{"results":{"result":{"buildState":"#{build_state}"}}}})
WebMock.stub_request(:get, bamboo_full_url).to_return(
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index 5309cfb99ff..c9517324541 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -76,7 +76,8 @@ describe JiraService, models: true do
end
it "should call JIRA API" do
- @jira_service.execute(merge_request, JiraIssue.new("JIRA-123", project))
+ @jira_service.execute(merge_request,
+ ExternalIssue.new("JIRA-123", project))
expect(WebMock).to have_requested(:post, @comment_url).with(
body: /Issue solved with/
).once
@@ -84,7 +85,8 @@ describe JiraService, models: true do
it "calls the api with jira_issue_transition_id" do
@jira_service.jira_issue_transition_id = 'this-is-a-custom-id'
- @jira_service.execute(merge_request, JiraIssue.new("JIRA-123", project))
+ @jira_service.execute(merge_request,
+ ExternalIssue.new("JIRA-123", project))
expect(WebMock).to have_requested(:post, @api_url).with(
body: /this-is-a-custom-id/
).once
diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb
index 24a708ca849..474715d24c3 100644
--- a/spec/models/project_services/teamcity_service_spec.rb
+++ b/spec/models/project_services/teamcity_service_spec.rb
@@ -126,19 +126,19 @@ describe TeamcityService, models: true do
it 'returns a specific URL when status is 500' do
stub_request(status: 500)
- expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildTypeId=foo')
+ expect(service.build_page('123', 'unused')).to eq('http://gitlab.com/teamcity/viewLog.html?buildTypeId=foo')
end
it 'returns a build URL when teamcity_url has no trailing slash' do
stub_request(body: %Q({"build":{"id":"666"}}))
- expect(service(teamcity_url: 'http://gitlab.com').build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildId=666&buildTypeId=foo')
+ expect(service(teamcity_url: 'http://gitlab.com/teamcity').build_page('123', 'unused')).to eq('http://gitlab.com/teamcity/viewLog.html?buildId=666&buildTypeId=foo')
end
it 'returns a build URL when teamcity_url has a trailing slash' do
stub_request(body: %Q({"build":{"id":"666"}}))
- expect(service(teamcity_url: 'http://gitlab.com/').build_page('123', 'unused')).to eq('http://gitlab.com/viewLog.html?buildId=666&buildTypeId=foo')
+ expect(service(teamcity_url: 'http://gitlab.com/teamcity/').build_page('123', 'unused')).to eq('http://gitlab.com/teamcity/viewLog.html?buildId=666&buildTypeId=foo')
end
end
@@ -180,7 +180,7 @@ describe TeamcityService, models: true do
end
end
- def service(teamcity_url: 'http://gitlab.com')
+ def service(teamcity_url: 'http://gitlab.com/teamcity')
described_class.create(
project: create(:empty_project),
properties: {
@@ -193,7 +193,7 @@ describe TeamcityService, models: true do
end
def stub_request(status: 200, body: nil, build_status: 'success')
- teamcity_full_url = 'http://mic:password@gitlab.com/httpAuth/app/rest/builds/branch:unspecified:any,number:123'
+ teamcity_full_url = 'http://mic:password@gitlab.com/teamcity/httpAuth/app/rest/builds/branch:unspecified:any,number:123'
body ||= %Q({"build":{"status":"#{build_status}","id":"666"}})
WebMock.stub_request(:get, teamcity_full_url).to_return(
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index f3590f72cfe..53c8408633c 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -28,6 +28,8 @@ describe Project, models: true do
it { is_expected.to have_many(:runners) }
it { is_expected.to have_many(:variables) }
it { is_expected.to have_many(:triggers) }
+ it { is_expected.to have_many(:environments).dependent(:destroy) }
+ it { is_expected.to have_many(:deployments).dependent(:destroy) }
it { is_expected.to have_many(:todos).dependent(:destroy) }
end
@@ -53,7 +55,6 @@ describe Project, models: true do
it { is_expected.to validate_length_of(:path).is_within(0..255) }
it { is_expected.to validate_length_of(:description).is_within(0..2000) }
it { is_expected.to validate_presence_of(:creator) }
- it { is_expected.to validate_length_of(:issues_tracker_id).is_within(0..255) }
it { is_expected.to validate_presence_of(:namespace) }
it 'should not allow new projects beyond user limits' do
@@ -90,11 +91,17 @@ describe Project, models: true do
it { is_expected.to respond_to(:repo_exists?) }
it { is_expected.to respond_to(:update_merge_requests) }
it { is_expected.to respond_to(:execute_hooks) }
- it { is_expected.to respond_to(:name_with_namespace) }
it { is_expected.to respond_to(:owner) }
it { is_expected.to respond_to(:path_with_namespace) }
end
+ describe '#name_with_namespace' do
+ let(:project) { build_stubbed(:empty_project) }
+
+ it { expect(project.name_with_namespace).to eq "#{project.namespace.human_name} / #{project.name}" }
+ it { expect(project.human_name).to eq project.name_with_namespace }
+ end
+
describe '#to_reference' do
let(:project) { create(:empty_project) }
@@ -213,7 +220,7 @@ describe Project, models: true do
end
end
- describe :find_with_namespace do
+ describe '.find_with_namespace' do
context 'with namespace' do
before do
@group = create :group, name: 'gitlab'
@@ -224,6 +231,22 @@ describe Project, models: true do
it { expect(Project.find_with_namespace('GitLab/GitlabHQ')).to eq(@project) }
it { expect(Project.find_with_namespace('gitlab-ci')).to be_nil }
end
+
+ context 'when multiple projects using a similar name exist' do
+ let(:group) { create(:group, name: 'gitlab') }
+
+ let!(:project1) do
+ create(:empty_project, name: 'gitlab1', path: 'gitlab', namespace: group)
+ end
+
+ let!(:project2) do
+ create(:empty_project, name: 'gitlab2', path: 'GITLAB', namespace: group)
+ end
+
+ it 'returns the row where the path matches literally' do
+ expect(Project.find_with_namespace('gitlab/GITLAB')).to eq(project2)
+ end
+ end
end
describe :to_param do
@@ -321,27 +344,6 @@ describe Project, models: true do
end
end
- describe :can_have_issues_tracker_id? do
- let(:project) { create(:project) }
- let(:ext_project) { create(:redmine_project) }
-
- it 'should be true for projects with external issues tracker if issues enabled' do
- expect(ext_project.can_have_issues_tracker_id?).to be_truthy
- end
-
- it 'should be false for projects with internal issue tracker if issues enabled' do
- expect(project.can_have_issues_tracker_id?).to be_falsey
- end
-
- it 'should be always false if issues disabled' do
- project.issues_enabled = false
- ext_project.issues_enabled = false
-
- expect(project.can_have_issues_tracker_id?).to be_falsey
- expect(ext_project.can_have_issues_tracker_id?).to be_falsey
- end
- end
-
describe :open_branches do
let(:project) { create(:project) }
diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb
index bacb17a8883..9262aeb6ed8 100644
--- a/spec/models/project_team_spec.rb
+++ b/spec/models/project_team_spec.rb
@@ -29,6 +29,9 @@ describe ProjectTeam, models: true do
it { expect(project.team.master?(nonmember)).to be_falsey }
it { expect(project.team.member?(nonmember)).to be_falsey }
it { expect(project.team.member?(guest)).to be_truthy }
+ it { expect(project.team.member?(reporter, Gitlab::Access::REPORTER)).to be_truthy }
+ it { expect(project.team.member?(guest, Gitlab::Access::REPORTER)).to be_falsey }
+ it { expect(project.team.member?(nonmember, Gitlab::Access::GUEST)).to be_falsey }
end
end
@@ -64,50 +67,48 @@ describe ProjectTeam, models: true do
it { expect(project.team.master?(nonmember)).to be_falsey }
it { expect(project.team.member?(nonmember)).to be_falsey }
it { expect(project.team.member?(guest)).to be_truthy }
+ it { expect(project.team.member?(guest, Gitlab::Access::MASTER)).to be_truthy }
+ it { expect(project.team.member?(reporter, Gitlab::Access::MASTER)).to be_falsey }
+ it { expect(project.team.member?(nonmember, Gitlab::Access::GUEST)).to be_falsey }
end
end
- describe :max_invited_level do
- let(:group) { create(:group) }
- let(:project) { create(:empty_project) }
-
- before do
- project.project_group_links.create(
- group: group,
- group_access: Gitlab::Access::DEVELOPER
- )
-
- group.add_user(master, Gitlab::Access::MASTER)
- group.add_user(reporter, Gitlab::Access::REPORTER)
- end
-
- it { expect(project.team.max_invited_level(master.id)).to eq(Gitlab::Access::DEVELOPER) }
- it { expect(project.team.max_invited_level(reporter.id)).to eq(Gitlab::Access::REPORTER) }
- it { expect(project.team.max_invited_level(nonmember.id)).to be_nil }
- end
-
- describe :max_member_access do
- let(:group) { create(:group) }
- let(:project) { create(:empty_project) }
-
- before do
- project.project_group_links.create(
- group: group,
- group_access: Gitlab::Access::DEVELOPER
- )
-
- group.add_user(master, Gitlab::Access::MASTER)
- group.add_user(reporter, Gitlab::Access::REPORTER)
+ describe '#find_member' do
+ context 'personal project' do
+ let(:project) { create(:empty_project) }
+ let(:requester) { create(:user) }
+
+ before do
+ project.team << [master, :master]
+ project.team << [reporter, :reporter]
+ project.team << [guest, :guest]
+ project.request_access(requester)
+ end
+
+ it { expect(project.team.find_member(master.id)).to be_a(ProjectMember) }
+ it { expect(project.team.find_member(reporter.id)).to be_a(ProjectMember) }
+ it { expect(project.team.find_member(guest.id)).to be_a(ProjectMember) }
+ it { expect(project.team.find_member(nonmember.id)).to be_nil }
+ it { expect(project.team.find_member(requester.id)).to be_nil }
end
- it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::DEVELOPER) }
- it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::REPORTER) }
- it { expect(project.team.max_member_access(nonmember.id)).to be_nil }
-
- it "does not have an access" do
- project.namespace.update(share_with_group_lock: true)
- expect(project.team.max_member_access(master.id)).to be_nil
- expect(project.team.max_member_access(reporter.id)).to be_nil
+ context 'group project' do
+ let(:group) { create(:group) }
+ let(:project) { create(:empty_project, group: group) }
+ let(:requester) { create(:user) }
+
+ before do
+ group.add_master(master)
+ group.add_reporter(reporter)
+ group.add_guest(guest)
+ group.request_access(requester)
+ end
+
+ it { expect(project.team.find_member(master.id)).to be_a(GroupMember) }
+ it { expect(project.team.find_member(reporter.id)).to be_a(GroupMember) }
+ it { expect(project.team.find_member(guest.id)).to be_a(GroupMember) }
+ it { expect(project.team.find_member(nonmember.id)).to be_nil }
+ it { expect(project.team.find_member(requester.id)).to be_nil }
end
end
@@ -132,4 +133,69 @@ describe ProjectTeam, models: true do
expect(project.team.human_max_access(user.id)).to eq 'Owner'
end
end
+
+ describe '#max_member_access' do
+ let(:requester) { create(:user) }
+
+ context 'personal project' do
+ let(:project) { create(:empty_project) }
+
+ context 'when project is not shared with group' do
+ before do
+ project.team << [master, :master]
+ project.team << [reporter, :reporter]
+ project.team << [guest, :guest]
+ project.request_access(requester)
+ end
+
+ it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::MASTER) }
+ it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::REPORTER) }
+ it { expect(project.team.max_member_access(guest.id)).to eq(Gitlab::Access::GUEST) }
+ it { expect(project.team.max_member_access(nonmember.id)).to be_nil }
+ it { expect(project.team.max_member_access(requester.id)).to be_nil }
+ end
+
+ context 'when project is shared with group' do
+ before do
+ group = create(:group)
+ project.project_group_links.create(
+ group: group,
+ group_access: Gitlab::Access::DEVELOPER)
+
+ group.add_master(master)
+ group.add_reporter(reporter)
+ end
+
+ it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::DEVELOPER) }
+ it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::REPORTER) }
+ it { expect(project.team.max_member_access(nonmember.id)).to be_nil }
+ it { expect(project.team.max_member_access(requester.id)).to be_nil }
+
+ context 'but share_with_group_lock is true' do
+ before { project.namespace.update(share_with_group_lock: true) }
+
+ it { expect(project.team.max_member_access(master.id)).to be_nil }
+ it { expect(project.team.max_member_access(reporter.id)).to be_nil }
+ end
+ end
+ end
+
+ context 'group project' do
+ let(:group) { create(:group) }
+ let(:project) { create(:empty_project, group: group) }
+
+ before do
+ group.add_master(master)
+ group.add_reporter(reporter)
+ group.add_guest(guest)
+ group.request_access(requester)
+ end
+
+ it { expect(project.team.max_member_access(master.id)).to eq(Gitlab::Access::MASTER) }
+ it { expect(project.team.max_member_access(reporter.id)).to eq(Gitlab::Access::REPORTER) }
+ it { expect(project.team.max_member_access(guest.id)).to eq(Gitlab::Access::GUEST) }
+ it { expect(project.team.max_member_access(nonmember.id)).to be_nil }
+ it { expect(project.team.max_member_access(requester.id)).to be_nil }
+ end
+ end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 8c2347992f1..d8350000bf6 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -31,6 +31,47 @@ describe Repository, models: true do
it { is_expected.not_to include('v1.0.0') }
end
+ describe 'tags_sorted_by' do
+ context 'name' do
+ subject { repository.tags_sorted_by('name').map(&:name) }
+
+ it { is_expected.to eq(['v1.1.0', 'v1.0.0']) }
+ end
+
+ context 'updated' do
+ let(:tag_a) { repository.find_tag('v1.0.0') }
+ let(:tag_b) { repository.find_tag('v1.1.0') }
+
+ context 'desc' do
+ subject { repository.tags_sorted_by('updated_desc').map(&:name) }
+
+ before do
+ double_first = double(committed_date: Time.now)
+ double_last = double(committed_date: Time.now - 1.second)
+
+ allow(repository).to receive(:commit).with(tag_a.target).and_return(double_first)
+ allow(repository).to receive(:commit).with(tag_b.target).and_return(double_last)
+ end
+
+ it { is_expected.to eq(['v1.0.0', 'v1.1.0']) }
+ end
+
+ context 'asc' do
+ subject { repository.tags_sorted_by('updated_asc').map(&:name) }
+
+ before do
+ double_first = double(committed_date: Time.now - 1.second)
+ double_last = double(committed_date: Time.now)
+
+ allow(repository).to receive(:commit).with(tag_a.target).and_return(double_last)
+ allow(repository).to receive(:commit).with(tag_b.target).and_return(double_first)
+ end
+
+ it { is_expected.to eq(['v1.1.0', 'v1.0.0']) }
+ end
+ end
+ end
+
describe :last_commit_for_path do
subject { repository.last_commit_for_path(sample_commit.id, '.gitignore').id }