diff options
Diffstat (limited to 'spec/models/project_spec.rb')
-rw-r--r-- | spec/models/project_spec.rb | 381 |
1 files changed, 309 insertions, 72 deletions
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index b8b9a455b83..53c8408633c 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1,43 +1,3 @@ -# == Schema Information -# -# Table name: projects -# -# id :integer not null, primary key -# name :string(255) -# path :string(255) -# description :text -# created_at :datetime -# updated_at :datetime -# creator_id :integer -# issues_enabled :boolean default(TRUE), not null -# wall_enabled :boolean default(TRUE), not null -# merge_requests_enabled :boolean default(TRUE), not null -# wiki_enabled :boolean default(TRUE), not null -# namespace_id :integer -# issues_tracker :string(255) default("gitlab"), not null -# issues_tracker_id :string(255) -# snippets_enabled :boolean default(TRUE), not null -# last_activity_at :datetime -# import_url :string(255) -# visibility_level :integer default(0), not null -# archived :boolean default(FALSE), not null -# avatar :string(255) -# import_status :string(255) -# repository_size :float default(0.0) -# star_count :integer default(0), not null -# import_type :string(255) -# import_source :string(255) -# commit_count :integer default(0) -# import_error :text -# ci_id :integer -# builds_enabled :boolean default(TRUE), not null -# shared_runners_enabled :boolean default(TRUE), not null -# runners_token :string -# build_coverage_regex :string -# build_allow_git_fetch :boolean default(TRUE), not null -# build_timeout :integer default(3600), not null -# - require 'spec_helper' describe Project, models: true do @@ -62,12 +22,14 @@ describe Project, models: true do it { is_expected.to have_one(:pushover_service).dependent(:destroy) } it { is_expected.to have_one(:asana_service).dependent(:destroy) } it { is_expected.to have_many(:commit_statuses) } - it { is_expected.to have_many(:ci_commits) } + it { is_expected.to have_many(:pipelines) } it { is_expected.to have_many(:builds) } it { is_expected.to have_many(:runner_projects) } 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 @@ -93,14 +55,22 @@ 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 project2 = build(:project) allow(project2).to receive(:creator).and_return(double(can_create_project?: false, projects_limit: 0).as_null_object) expect(project2).not_to be_valid - expect(project2.errors[:limit_reached].first).to match(/Your project limit is 0/) + expect(project2.errors[:limit_reached].first).to match(/Personal project creation is not allowed/) + end + end + + describe 'default_scope' do + it 'excludes projects pending deletion from the results' do + project = create(:empty_project) + create(:empty_project, pending_delete: true) + + expect(Project.all).to eq [project] end end @@ -121,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) } @@ -244,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' @@ -255,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 @@ -289,24 +281,66 @@ describe Project, models: true do end end - describe :can_have_issues_tracker_id? do + describe :external_issue_tracker 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 + context 'on existing projects with no value for has_external_issue_tracker' do + before(:each) do + project.update_column(:has_external_issue_tracker, nil) + ext_project.update_column(:has_external_issue_tracker, nil) + end + + it 'updates the has_external_issue_tracker boolean' do + expect do + project.external_issue_tracker + end.to change { project.reload.has_external_issue_tracker }.to(false) + + expect do + ext_project.external_issue_tracker + end.to change { ext_project.reload.has_external_issue_tracker }.to(true) + end + end + + it 'returns nil and does not query services when there is no external issue tracker' do + project.build_missing_services + project.reload + + expect(project).not_to receive(:services) + + expect(project.external_issue_tracker).to eq(nil) + end + + it 'retrieves external_issue_tracker querying services and cache it when there is external issue tracker' do + ext_project.reload # Factory returns a project with changed attributes + ext_project.build_missing_services + ext_project.reload + + expect(ext_project).to receive(:services).once.and_call_original + + 2.times { expect(ext_project.external_issue_tracker).to be_a_kind_of(RedmineService) } end + end + + describe :cache_has_external_issue_tracker do + let(:project) { create(:project) } + + it 'stores true if there is any external_issue_tracker' do + services = double(:service, external_issue_trackers: [RedmineService.new]) + expect(project).to receive(:services).and_return(services) - it 'should be false for projects with internal issue tracker if issues enabled' do - expect(project.can_have_issues_tracker_id?).to be_falsey + expect do + project.cache_has_external_issue_tracker + end.to change { project.has_external_issue_tracker}.to(true) end - it 'should be always false if issues disabled' do - project.issues_enabled = false - ext_project.issues_enabled = false + it 'stores false if there is no external_issue_tracker' do + services = double(:service, external_issue_trackers: []) + expect(project).to receive(:services).and_return(services) - expect(project.can_have_issues_tracker_id?).to be_falsey - expect(ext_project.can_have_issues_tracker_id?).to be_falsey + expect do + project.cache_has_external_issue_tracker + end.to change { project.has_external_issue_tracker}.to(false) end end @@ -422,13 +456,32 @@ describe Project, models: true do it { should eq "http://localhost#{avatar_path}" } end + + context 'when git repo is empty' do + let(:project) { create(:empty_project) } + + it { should eq nil } + end end - describe :ci_commit do + describe :pipeline do let(:project) { create :project } - let(:commit) { create :ci_commit, project: project } + let(:pipeline) { create :ci_pipeline, project: project, ref: 'master' } + + subject { project.pipeline(pipeline.sha, 'master') } - it { expect(project.ci_commit(commit.sha)).to eq(commit) } + it { is_expected.to eq(pipeline) } + + context 'return latest' do + let(:pipeline2) { create :ci_pipeline, project: project, ref: 'master' } + + before do + pipeline + pipeline2 + end + + it { is_expected.to eq(pipeline2) } + end end describe :builds_enabled do @@ -442,7 +495,7 @@ describe Project, models: true do end describe '.trending' do - let(:group) { create(:group) } + let(:group) { create(:group, :public) } let(:project1) { create(:empty_project, :public, group: group) } let(:project2) { create(:empty_project, :public, group: group) } @@ -571,12 +624,8 @@ describe Project, models: true do end context 'when checking on forked project' do - let(:forked_project) { create :forked_project_with_submodules } - - before do - forked_project.build_forked_project_link(forked_to_project_id: forked_project.id, forked_from_project_id: project.id) - forked_project.save - end + let(:project) { create(:project, :internal) } + let(:forked_project) { create(:project, forked_from_project: project) } it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy } it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_truthy } @@ -650,11 +699,11 @@ describe Project, models: true do # Project#gitlab_shell returns a new instance of Gitlab::Shell on every # call. This makes testing a bit easier. allow(project).to receive(:gitlab_shell).and_return(gitlab_shell) - end - it 'renames a repository' do allow(project).to receive(:previous_changes).and_return('path' => ['foo']) + end + it 'renames a repository' do ns = project.namespace_dir expect(gitlab_shell).to receive(:mv_repository). @@ -679,6 +728,17 @@ describe Project, models: true do project.rename_repo end + + context 'container registry with tags' do + before do + stub_container_registry_config(enabled: true) + stub_container_registry_tags('tag') + end + + subject { project.rename_repo } + + it { expect{subject}.to raise_error(Exception) } + end end describe '#expire_caches_before_rename' do @@ -695,11 +755,8 @@ describe Project, models: true do with('foo.wiki', project). and_return(wiki) - expect(repo).to receive(:expire_cache) - expect(repo).to receive(:expire_emptiness_caches) - - expect(wiki).to receive(:expire_cache) - expect(wiki).to receive(:expire_emptiness_caches) + expect(repo).to receive(:before_delete) + expect(wiki).to receive(:before_delete) project.expire_caches_before_rename('foo') end @@ -720,4 +777,184 @@ describe Project, models: true do expect(described_class.search_by_title('KITTENS')).to eq([project]) end end + + context 'when checking projects from groups' do + let(:private_group) { create(:group, visibility_level: 0) } + let(:internal_group) { create(:group, visibility_level: 10) } + + let(:private_project) { create :project, :private, group: private_group } + let(:internal_project) { create :project, :internal, group: internal_group } + + context 'when group is private project can not be internal' do + it { expect(private_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_falsey } + end + + context 'when group is internal project can not be public' do + it { expect(internal_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_falsey } + end + end + + describe '#create_repository' do + let(:project) { create(:project) } + let(:shell) { Gitlab::Shell.new } + + before do + allow(project).to receive(:gitlab_shell).and_return(shell) + end + + context 'using a regular repository' do + it 'creates the repository' do + expect(shell).to receive(:add_repository). + with(project.path_with_namespace). + and_return(true) + + expect(project.repository).to receive(:after_create) + + expect(project.create_repository).to eq(true) + end + + it 'adds an error if the repository could not be created' do + expect(shell).to receive(:add_repository). + with(project.path_with_namespace). + and_return(false) + + expect(project.repository).not_to receive(:after_create) + + expect(project.create_repository).to eq(false) + expect(project.errors).not_to be_empty + end + end + + context 'using a forked repository' do + it 'does nothing' do + expect(project).to receive(:forked?).and_return(true) + expect(shell).not_to receive(:add_repository) + + project.create_repository + end + end + end + + describe '#protected_branch?' do + let(:project) { create(:empty_project) } + + it 'returns true when a branch is a protected branch' do + project.protected_branches.create!(name: 'foo') + + expect(project.protected_branch?('foo')).to eq(true) + end + + it 'returns false when a branch is not a protected branch' do + expect(project.protected_branch?('foo')).to eq(false) + end + end + + describe '#container_registry_path_with_namespace' do + let(:project) { create(:empty_project, path: 'PROJECT') } + + subject { project.container_registry_path_with_namespace } + + it { is_expected.not_to eq(project.path_with_namespace) } + it { is_expected.to eq(project.path_with_namespace.downcase) } + end + + describe '#container_registry_repository' do + let(:project) { create(:empty_project) } + + before { stub_container_registry_config(enabled: true) } + + subject { project.container_registry_repository } + + it { is_expected.not_to be_nil } + end + + describe '#container_registry_repository_url' do + let(:project) { create(:empty_project) } + + subject { project.container_registry_repository_url } + + before { stub_container_registry_config(**registry_settings) } + + context 'for enabled registry' do + let(:registry_settings) do + { + enabled: true, + host_port: 'example.com', + } + end + + it { is_expected.not_to be_nil } + end + + context 'for disabled registry' do + let(:registry_settings) do + { + enabled: false + } + end + + it { is_expected.to be_nil } + end + end + + describe '#has_container_registry_tags?' do + let(:project) { create(:empty_project) } + + subject { project.has_container_registry_tags? } + + context 'for enabled registry' do + before { stub_container_registry_config(enabled: true) } + + context 'with tags' do + before { stub_container_registry_tags('test', 'test2') } + + it { is_expected.to be_truthy } + end + + context 'when no tags' do + before { stub_container_registry_tags } + + it { is_expected.to be_falsey } + end + end + + context 'for disabled registry' do + before { stub_container_registry_config(enabled: false) } + + it { is_expected.to be_falsey } + end + end + + describe '.where_paths_in' do + context 'without any paths' do + it 'returns an empty relation' do + expect(Project.where_paths_in([])).to eq([]) + end + end + + context 'without any valid paths' do + it 'returns an empty relation' do + expect(Project.where_paths_in(%w[foo])).to eq([]) + end + end + + context 'with valid paths' do + let!(:project1) { create(:project) } + let!(:project2) { create(:project) } + + it 'returns the projects matching the paths' do + projects = Project.where_paths_in([project1.path_with_namespace, + project2.path_with_namespace]) + + expect(projects).to contain_exactly(project1, project2) + end + + it 'returns projects regardless of the casing of paths' do + projects = Project.where_paths_in([project1.path_with_namespace.upcase, + project2.path_with_namespace.upcase]) + + expect(projects).to contain_exactly(project1, project2) + end + end + end end |