summaryrefslogtreecommitdiff
path: root/spec/lib
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib')
-rw-r--r--spec/lib/banzai/filter/abstract_link_filter_spec.rb4
-rw-r--r--spec/lib/banzai/filter/autolink_filter_spec.rb22
-rw-r--r--spec/lib/banzai/filter/commit_range_reference_filter_spec.rb99
-rw-r--r--spec/lib/banzai/filter/commit_reference_filter_spec.rb78
-rw-r--r--spec/lib/banzai/filter/issue_reference_filter_spec.rb108
-rw-r--r--spec/lib/banzai/filter/label_reference_filter_spec.rb291
-rw-r--r--spec/lib/banzai/filter/math_filter_spec.rb120
-rw-r--r--spec/lib/banzai/filter/merge_request_reference_filter_spec.rb92
-rw-r--r--spec/lib/banzai/filter/milestone_reference_filter_spec.rb108
-rw-r--r--spec/lib/banzai/filter/redactor_filter_spec.rb42
-rw-r--r--spec/lib/banzai/filter/relative_link_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/snippet_reference_filter_spec.rb85
-rw-r--r--spec/lib/banzai/filter/table_of_contents_filter_spec.rb21
-rw-r--r--spec/lib/banzai/reference_parser/base_parser_spec.rb35
-rw-r--r--spec/lib/banzai/reference_parser/commit_parser_spec.rb8
-rw-r--r--spec/lib/banzai/reference_parser/commit_range_parser_spec.rb8
-rw-r--r--spec/lib/banzai/reference_parser/external_issue_parser_spec.rb8
-rw-r--r--spec/lib/banzai/reference_parser/issue_parser_spec.rb10
-rw-r--r--spec/lib/banzai/reference_parser/label_parser_spec.rb8
-rw-r--r--spec/lib/banzai/reference_parser/merge_request_parser_spec.rb13
-rw-r--r--spec/lib/banzai/reference_parser/milestone_parser_spec.rb8
-rw-r--r--spec/lib/banzai/reference_parser/snippet_parser_spec.rb8
-rw-r--r--spec/lib/banzai/reference_parser/user_parser_spec.rb2
-rw-r--r--spec/lib/bitbucket/collection_spec.rb24
-rw-r--r--spec/lib/bitbucket/connection_spec.rb35
-rw-r--r--spec/lib/bitbucket/page_spec.rb50
-rw-r--r--spec/lib/bitbucket/paginator_spec.rb21
-rw-r--r--spec/lib/bitbucket/representation/comment_spec.rb22
-rw-r--r--spec/lib/bitbucket/representation/issue_spec.rb47
-rw-r--r--spec/lib/bitbucket/representation/pull_request_comment_spec.rb34
-rw-r--r--spec/lib/bitbucket/representation/pull_request_spec.rb47
-rw-r--r--spec/lib/bitbucket/representation/user_spec.rb11
-rw-r--r--spec/lib/ci/gitlab_ci_yaml_processor_spec.rb4
-rw-r--r--spec/lib/constraints/group_url_constrainer_spec.rb28
-rw-r--r--spec/lib/constraints/namespace_url_constrainer_spec.rb35
-rw-r--r--spec/lib/constraints/project_url_constrainer_spec.rb32
-rw-r--r--spec/lib/constraints/user_url_constrainer_spec.rb21
-rw-r--r--spec/lib/event_filter_spec.rb15
-rw-r--r--spec/lib/gitlab/allowable_spec.rb27
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb4
-rw-r--r--spec/lib/gitlab/auth_spec.rb68
-rw-r--r--spec/lib/gitlab/backup/manager_spec.rb127
-rw-r--r--spec/lib/gitlab/badge/build/status_spec.rb4
-rw-r--r--spec/lib/gitlab/bitbucket_import/client_spec.rb67
-rw-r--r--spec/lib/gitlab/bitbucket_import/importer_spec.rb54
-rw-r--r--spec/lib/gitlab/bitbucket_import/project_creator_spec.rb18
-rw-r--r--spec/lib/gitlab/chat_commands/command_spec.rb94
-rw-r--r--spec/lib/gitlab/chat_commands/deploy_spec.rb79
-rw-r--r--spec/lib/gitlab/chat_commands/issue_create_spec.rb68
-rw-r--r--spec/lib/gitlab/chat_commands/issue_search_spec.rb46
-rw-r--r--spec/lib/gitlab/chat_commands/issue_show_spec.rb48
-rw-r--r--spec/lib/gitlab/chat_name_token_spec.rb37
-rw-r--r--spec/lib/gitlab/ci/build/credentials/factory_spec.rb38
-rw-r--r--spec/lib/gitlab/ci/build/credentials/registry_spec.rb41
-rw-r--r--spec/lib/gitlab/ci/config/entry/artifacts_spec.rb (renamed from spec/lib/gitlab/ci/config/node/artifacts_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/attributable_spec.rb (renamed from spec/lib/gitlab/ci/config/node/attributable_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/boolean_spec.rb (renamed from spec/lib/gitlab/ci/config/node/boolean_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/cache_spec.rb (renamed from spec/lib/gitlab/ci/config/node/cache_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/commands_spec.rb (renamed from spec/lib/gitlab/ci/config/node/commands_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/configurable_spec.rb67
-rw-r--r--spec/lib/gitlab/ci/config/entry/environment_spec.rb (renamed from spec/lib/gitlab/ci/config/node/environment_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/factory_spec.rb (renamed from spec/lib/gitlab/ci/config/node/factory_spec.rb)14
-rw-r--r--spec/lib/gitlab/ci/config/entry/global_spec.rb (renamed from spec/lib/gitlab/ci/config/node/global_spec.rb)115
-rw-r--r--spec/lib/gitlab/ci/config/entry/hidden_spec.rb (renamed from spec/lib/gitlab/ci/config/node/hidden_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/image_spec.rb (renamed from spec/lib/gitlab/ci/config/node/image_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb (renamed from spec/lib/gitlab/ci/config/node/job_spec.rb)16
-rw-r--r--spec/lib/gitlab/ci/config/entry/jobs_spec.rb (renamed from spec/lib/gitlab/ci/config/node/jobs_spec.rb)6
-rw-r--r--spec/lib/gitlab/ci/config/entry/key_spec.rb (renamed from spec/lib/gitlab/ci/config/node/key_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/paths_spec.rb (renamed from spec/lib/gitlab/ci/config/node/paths_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/script_spec.rb (renamed from spec/lib/gitlab/ci/config/node/script_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/services_spec.rb (renamed from spec/lib/gitlab/ci/config/node/services_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/stage_spec.rb (renamed from spec/lib/gitlab/ci/config/node/stage_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/stages_spec.rb (renamed from spec/lib/gitlab/ci/config/node/stages_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/trigger_spec.rb (renamed from spec/lib/gitlab/ci/config/node/trigger_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/undefined_spec.rb (renamed from spec/lib/gitlab/ci/config/node/undefined_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/unspecified_spec.rb (renamed from spec/lib/gitlab/ci/config/node/unspecified_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/validatable_spec.rb (renamed from spec/lib/gitlab/ci/config/node/validatable_spec.rb)26
-rw-r--r--spec/lib/gitlab/ci/config/entry/validator_spec.rb (renamed from spec/lib/gitlab/ci/config/node/validator_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/entry/variables_spec.rb (renamed from spec/lib/gitlab/ci/config/node/variables_spec.rb)2
-rw-r--r--spec/lib/gitlab/ci/config/node/configurable_spec.rb67
-rw-r--r--spec/lib/gitlab/ci/status/build/cancelable_spec.rb86
-rw-r--r--spec/lib/gitlab/ci/status/build/common_spec.rb37
-rw-r--r--spec/lib/gitlab/ci/status/build/factory_spec.rb141
-rw-r--r--spec/lib/gitlab/ci/status/build/play_spec.rb82
-rw-r--r--spec/lib/gitlab/ci/status/build/retryable_spec.rb86
-rw-r--r--spec/lib/gitlab/ci/status/build/stop_spec.rb84
-rw-r--r--spec/lib/gitlab/ci/status/canceled_spec.rb19
-rw-r--r--spec/lib/gitlab/ci/status/created_spec.rb19
-rw-r--r--spec/lib/gitlab/ci/status/extended_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/status/factory_spec.rb24
-rw-r--r--spec/lib/gitlab/ci/status/failed_spec.rb19
-rw-r--r--spec/lib/gitlab/ci/status/pending_spec.rb19
-rw-r--r--spec/lib/gitlab/ci/status/pipeline/common_spec.rb36
-rw-r--r--spec/lib/gitlab/ci/status/pipeline/factory_spec.rb59
-rw-r--r--spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb65
-rw-r--r--spec/lib/gitlab/ci/status/running_spec.rb19
-rw-r--r--spec/lib/gitlab/ci/status/skipped_spec.rb19
-rw-r--r--spec/lib/gitlab/ci/status/stage/common_spec.rb43
-rw-r--r--spec/lib/gitlab/ci/status/stage/factory_spec.rb46
-rw-r--r--spec/lib/gitlab/ci/status/success_spec.rb19
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb3
-rw-r--r--spec/lib/gitlab/contributions_calendar_spec.rb104
-rw-r--r--spec/lib/gitlab/cycle_analytics/code_event_spec.rb10
-rw-r--r--spec/lib/gitlab/cycle_analytics/events_spec.rb326
-rw-r--r--spec/lib/gitlab/cycle_analytics/issue_event_spec.rb10
-rw-r--r--spec/lib/gitlab/cycle_analytics/permissions_spec.rb127
-rw-r--r--spec/lib/gitlab/cycle_analytics/plan_event_spec.rb18
-rw-r--r--spec/lib/gitlab/cycle_analytics/production_event_spec.rb10
-rw-r--r--spec/lib/gitlab/cycle_analytics/review_event_spec.rb10
-rw-r--r--spec/lib/gitlab/cycle_analytics/shared_event_spec.rb21
-rw-r--r--spec/lib/gitlab/cycle_analytics/staging_event_spec.rb10
-rw-r--r--spec/lib/gitlab/cycle_analytics/test_event_spec.rb10
-rw-r--r--spec/lib/gitlab/cycle_analytics/updater_spec.rb25
-rw-r--r--spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb21
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb24
-rw-r--r--spec/lib/gitlab/email/reply_parser_spec.rb4
-rw-r--r--spec/lib/gitlab/file_detector_spec.rb59
-rw-r--r--spec/lib/gitlab/gfm/reference_rewriter_spec.rb8
-rw-r--r--spec/lib/gitlab/git_access_spec.rb25
-rw-r--r--spec/lib/gitlab/git_access_wiki_spec.rb27
-rw-r--r--spec/lib/gitlab/github_import/branch_formatter_spec.rb12
-rw-r--r--spec/lib/gitlab/github_import/importer_spec.rb5
-rw-r--r--spec/lib/gitlab/github_import/issue_formatter_spec.rb10
-rw-r--r--spec/lib/gitlab/identifier_spec.rb5
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml11
-rw-r--r--spec/lib/gitlab/import_export/avatar_restorer_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/members_mapper_spec.rb11
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml4
-rw-r--r--spec/lib/gitlab/ldap/config_spec.rb120
-rw-r--r--spec/lib/gitlab/middleware/go_spec.rb2
-rw-r--r--spec/lib/gitlab/middleware/multipart_spec.rb74
-rw-r--r--spec/lib/gitlab/o_auth/user_spec.rb23
-rw-r--r--spec/lib/gitlab/project_search_results_spec.rb89
-rw-r--r--spec/lib/gitlab/reference_extractor_spec.rb3
-rw-r--r--spec/lib/gitlab/regex_spec.rb16
-rw-r--r--spec/lib/gitlab/routing_spec.rb23
-rw-r--r--spec/lib/gitlab/search_results_spec.rb67
-rw-r--r--spec/lib/gitlab/sidekiq_throttler_spec.rb28
-rw-r--r--spec/lib/gitlab/sql/union_spec.rb22
-rw-r--r--spec/lib/light_url_builder_spec.rb119
-rw-r--r--spec/lib/mattermost/session_spec.rb99
141 files changed, 4742 insertions, 566 deletions
diff --git a/spec/lib/banzai/filter/abstract_link_filter_spec.rb b/spec/lib/banzai/filter/abstract_link_filter_spec.rb
index 1ee31a603e4..70a87fbc01e 100644
--- a/spec/lib/banzai/filter/abstract_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/abstract_link_filter_spec.rb
@@ -5,7 +5,7 @@ describe Banzai::Filter::AbstractReferenceFilter do
describe '#references_per_project' do
it 'returns a Hash containing references grouped per project paths' do
- doc = Nokogiri::HTML.fragment("#1 #{project.to_reference}#2")
+ doc = Nokogiri::HTML.fragment("#1 #{project.path_with_namespace}#2")
filter = described_class.new(doc, project: project)
expect(filter).to receive(:object_class).exactly(4).times.and_return(Issue)
@@ -14,7 +14,7 @@ describe Banzai::Filter::AbstractReferenceFilter do
refs = filter.references_per_project
expect(refs).to be_an_instance_of(Hash)
- expect(refs[project.to_reference]).to eq(Set.new(%w[1 2]))
+ expect(refs[project.path_with_namespace]).to eq(Set.new(%w[1 2]))
end
end
diff --git a/spec/lib/banzai/filter/autolink_filter_spec.rb b/spec/lib/banzai/filter/autolink_filter_spec.rb
index dca7f997570..a6d2ea11fcc 100644
--- a/spec/lib/banzai/filter/autolink_filter_spec.rb
+++ b/spec/lib/banzai/filter/autolink_filter_spec.rb
@@ -99,6 +99,28 @@ describe Banzai::Filter::AutolinkFilter, lib: true do
expect(doc.at_css('a')['href']).to eq link
end
+ it 'autolinks rdar' do
+ link = 'rdar://localhost.com/blah'
+ doc = filter("See #{link}")
+
+ expect(doc.at_css('a').text).to eq link
+ expect(doc.at_css('a')['href']).to eq link
+ end
+
+ it 'does not autolink javascript' do
+ link = 'javascript://alert(document.cookie);'
+ doc = filter("See #{link}")
+
+ expect(doc.at_css('a')).to be_nil
+ end
+
+ it 'does not autolink bad URLs' do
+ link = 'foo://23423:::asdf'
+ doc = filter("See #{link}")
+
+ expect(doc.to_s).to eq("See #{link}")
+ end
+
it 'does not include trailing punctuation' do
doc = filter("See #{link}.")
expect(doc.at_css('a').text).to eq link
diff --git a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
index e6c90ad87ee..9703e2315b8 100644
--- a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
@@ -59,9 +59,7 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
it 'ignores invalid commit IDs' do
exp = act = "See #{commit1.id.reverse}...#{commit2.id}"
- expect(project).to receive(:valid_repo?).and_return(true)
- expect(project.repository).to receive(:commit).with(commit1.id.reverse)
- expect(project.repository).to receive(:commit).with(commit2.id)
+ allow(project.repository).to receive(:commit).with(commit1.id.reverse)
expect(reference_filter(act).to_html).to eq exp
end
@@ -100,14 +98,44 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
end
end
- context 'cross-project reference' do
- let(:namespace) { create(:namespace, name: 'cross-reference') }
- let(:project2) { create(:project, :public, namespace: namespace) }
- let(:reference) { range.to_reference(project) }
+ context 'cross-project / cross-namespace complete reference' do
+ let(:project2) { create(:project, :public) }
+ let(:reference) { "#{project2.path_with_namespace}@#{commit1.id}...#{commit2.id}" }
- before do
- range.project = project2
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.css('a').first.text).
+ to eql("#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.text).to eql("Fixed (#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}.)")
+ end
+
+ it 'ignores invalid commit IDs on the referenced project' do
+ exp = act = "Fixed #{project2.path_with_namespace}@#{commit1.id.reverse}...#{commit2.id}"
+ expect(reference_filter(act).to_html).to eq exp
+
+ exp = act = "Fixed #{project2.path_with_namespace}@#{commit1.id}...#{commit2.id.reverse}"
+ expect(reference_filter(act).to_html).to eq exp
end
+ end
+
+ context 'cross-project / same-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:project, :public, namespace: namespace) }
+ let(:project2) { create(:project, :public, path: "same-namespace", namespace: namespace) }
+ let(:reference) { "#{project2.path}@#{commit1.id}...#{commit2.id}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
@@ -116,24 +144,65 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
end
- it 'links with adjacent text' do
+ it 'link has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
- exp = Regexp.escape("#{project2.to_reference}@#{range.reference_link_text}")
- expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/)
+ expect(doc.css('a').first.text).
+ to eql("#{project2.path}@#{commit1.short_id}...#{commit2.short_id}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.text).to eql("Fixed (#{project2.path}@#{commit1.short_id}...#{commit2.short_id}.)")
end
it 'ignores invalid commit IDs on the referenced project' do
- exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}"
+ exp = act = "Fixed #{project2.path}@#{commit1.id.reverse}...#{commit2.id}"
expect(reference_filter(act).to_html).to eq exp
- exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}"
+ exp = act = "Fixed #{project2.path}@#{commit1.id}...#{commit2.id.reverse}"
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'cross-project shorthand reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:project, :public, namespace: namespace) }
+ let(:project2) { create(:project, :public, path: "same-namespace", namespace: namespace) }
+ let(:reference) { "#{project2.path}@#{commit1.id}...#{commit2.id}" }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.css('a').first.text).
+ to eql("#{project2.path}@#{commit1.short_id}...#{commit2.short_id}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.text).to eql("Fixed (#{project2.path}@#{commit1.short_id}...#{commit2.short_id}.)")
+ end
+
+ it 'ignores invalid commit IDs on the referenced project' do
+ exp = act = "Fixed #{project2.path}@#{commit1.id.reverse}...#{commit2.id}"
+ expect(reference_filter(act).to_html).to eq exp
+
+ exp = act = "Fixed #{project2.path}@#{commit1.id}...#{commit2.id.reverse}"
expect(reference_filter(act).to_html).to eq exp
end
end
context 'cross-project URL reference' do
- let(:namespace) { create(:namespace, name: 'cross-reference') }
+ let(:namespace) { create(:namespace) }
let(:project2) { create(:project, :public, namespace: namespace) }
let(:range) { CommitRange.new("#{commit1.id}...master", project) }
let(:reference) { urls.namespace_project_compare_url(project2.namespace, project2, from: commit1.id, to: 'master') }
diff --git a/spec/lib/banzai/filter/commit_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
index e0f08282551..2e6dcc3a434 100644
--- a/spec/lib/banzai/filter/commit_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
@@ -41,6 +41,7 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do
it 'links with adjacent text' do
doc = reference_filter("See (#{reference}.)")
+
expect(doc.to_html).to match(/\(<a.+>#{commit.short_id}<\/a>\.\)/)
end
@@ -48,8 +49,6 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do
invalid = invalidate_reference(reference)
exp = act = "See #{invalid}"
- expect(project).to receive(:valid_repo?).and_return(true)
- expect(project.repository).to receive(:commit).with(invalid)
expect(reference_filter(act).to_html).to eq exp
end
@@ -95,34 +94,85 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do
end
end
- context 'cross-project reference' do
- let(:namespace) { create(:namespace, name: 'cross-reference') }
+ context 'cross-project / cross-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
let(:project2) { create(:project, :public, namespace: namespace) }
let(:commit) { project2.commit }
- let(:reference) { commit.to_reference(project) }
+ let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" }
- it 'links to a valid reference' do
- doc = reference_filter("See #{reference}")
+ it 'link has valid text' do
+ doc = reference_filter("See (#{reference}.)")
- expect(doc.css('a').first.attr('href')).
- to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id)
+ expect(doc.css('a').first.text).to eql("#{project2.path_with_namespace}@#{commit.short_id}")
end
- it 'links with adjacent text' do
- doc = reference_filter("Fixed (#{reference}.)")
+ it 'has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.text).to eql("See (#{project2.path_with_namespace}@#{commit.short_id}.)")
+ end
+
+ it 'ignores invalid commit IDs on the referenced project' do
+ exp = act = "Committed #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'cross-project / same-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, namespace: namespace) }
+ let(:project2) { create(:project, :public, namespace: namespace) }
+ let(:commit) { project2.commit }
+ let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" }
+
+ it 'link has valid text' do
+ doc = reference_filter("See (#{reference}.)")
- exp = Regexp.escape(project2.to_reference)
- expect(doc.to_html).to match(/\(<a.+>#{exp}@#{commit.short_id}<\/a>\.\)/)
+ expect(doc.css('a').first.text).to eql("#{project2.path}@#{commit.short_id}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.text).to eql("See (#{project2.path}@#{commit.short_id}.)")
end
it 'ignores invalid commit IDs on the referenced project' do
exp = act = "Committed #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'cross-project shorthand reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, namespace: namespace) }
+ let(:project2) { create(:project, :public, namespace: namespace) }
+ let(:commit) { project2.commit }
+ let(:reference) { "#{project2.path_with_namespace}@#{commit.short_id}" }
+
+ it 'link has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.css('a').first.text).to eql("#{project2.path}@#{commit.short_id}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.text).to eql("See (#{project2.path}@#{commit.short_id}.)")
+ end
+
+ it 'ignores invalid commit IDs on the referenced project' do
+ exp = act = "Committed #{invalidate_reference(reference)}"
+
expect(reference_filter(act).to_html).to eq exp
end
end
context 'cross-project URL reference' do
- let(:namespace) { create(:namespace, name: 'cross-reference') }
+ let(:namespace) { create(:namespace) }
let(:project2) { create(:project, :public, namespace: namespace) }
let(:commit) { project2.commit }
let(:reference) { urls.namespace_project_commit_url(project2.namespace, project2, commit.id) }
diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
index 8f0b2db3e8e..456dbac0698 100644
--- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
@@ -8,7 +8,7 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
end
let(:project) { create(:empty_project, :public) }
- let(:issue) { create(:issue, project: project) }
+ let(:issue) { create(:issue, project: project) }
it 'requires project context' do
expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
@@ -24,7 +24,7 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
context 'internal reference' do
it_behaves_like 'a reference containing an element node'
- let(:reference) { issue.to_reference }
+ let(:reference) { "##{issue.iid}" }
it 'ignores valid references when using non-default tracker' do
allow(project).to receive(:default_issues_tracker?).and_return(false)
@@ -42,7 +42,7 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
it 'links with adjacent text' do
doc = reference_filter("Fixed (#{reference}.)")
- expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
+ expect(doc.text).to eql("Fixed (#{reference}.)")
end
it 'ignores invalid issue IDs' do
@@ -116,13 +116,56 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
end
end
- context 'cross-project reference' do
+ context 'cross-project / cross-namespace complete reference' do
it_behaves_like 'a reference containing an element node'
- let(:namespace) { create(:namespace, name: 'cross-reference') }
+ let(:project2) { create(:empty_project, :public) }
+ let(:issue) { create(:issue, project: project2) }
+ let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" }
+
+ it 'ignores valid references when cross-reference project uses external tracker' do
+ expect_any_instance_of(described_class).to receive(:find_object).
+ with(project2, issue.iid).
+ and_return(nil)
+
+ exp = act = "Issue #{reference}"
+ expect(reference_filter(act).to_html).to eq exp
+ end
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq helper.url_for_issue(issue.iid, project2)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.css('a').first.text).to eql("#{project2.path_with_namespace}##{issue.iid}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.text).to eq("Fixed (#{project2.path_with_namespace}##{issue.iid}.)")
+ end
+
+ it 'ignores invalid issue IDs on the referenced project' do
+ exp = act = "Fixed #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'cross-project / same-namespace complete reference' do
+ it_behaves_like 'a reference containing an element node'
+
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, :public, namespace: namespace) }
let(:project2) { create(:empty_project, :public, namespace: namespace) }
let(:issue) { create(:issue, project: project2) }
- let(:reference) { issue.to_reference(project) }
+ let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" }
it 'ignores valid references when cross-reference project uses external tracker' do
expect_any_instance_of(described_class).to receive(:find_object).
@@ -140,9 +183,16 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
to eq helper.url_for_issue(issue.iid, project2)
end
- it 'links with adjacent text' do
+ it 'link has valid text' do
doc = reference_filter("Fixed (#{reference}.)")
- expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
+
+ expect(doc.css('a').first.text).to eql("#{project2.path}##{issue.iid}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.text).to eq("Fixed (#{project2.path}##{issue.iid}.)")
end
it 'ignores invalid issue IDs on the referenced project' do
@@ -150,9 +200,47 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
expect(reference_filter(act).to_html).to eq exp
end
+ end
+
+ context 'cross-project shorthand reference' do
+ it_behaves_like 'a reference containing an element node'
- it 'ignores out-of-bounds issue IDs on the referenced project' do
- exp = act = "Fixed ##{Gitlab::Database::MAX_INT_VALUE + 1}"
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, :public, namespace: namespace) }
+ let(:project2) { create(:empty_project, :public, namespace: namespace) }
+ let(:issue) { create(:issue, project: project2) }
+ let(:reference) { "#{project2.path}##{issue.iid}" }
+
+ it 'ignores valid references when cross-reference project uses external tracker' do
+ expect_any_instance_of(described_class).to receive(:find_object).
+ with(project2, issue.iid).
+ and_return(nil)
+
+ exp = act = "Issue #{reference}"
+ expect(reference_filter(act).to_html).to eq exp
+ end
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq helper.url_for_issue(issue.iid, project2)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.css('a').first.text).to eql("#{project2.path}##{issue.iid}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Fixed (#{reference}.)")
+
+ expect(doc.text).to eq("Fixed (#{project2.path}##{issue.iid}.)")
+ end
+
+ it 'ignores invalid issue IDs on the referenced project' do
+ exp = act = "Fixed #{invalidate_reference(reference)}"
expect(reference_filter(act).to_html).to eq exp
end
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index 9c09f00ae8a..284641fb20a 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -4,7 +4,7 @@ require 'html/pipeline'
describe Banzai::Filter::LabelReferenceFilter, lib: true do
include FilterSpecHelper
- let(:project) { create(:empty_project, :public) }
+ let(:project) { create(:empty_project, :public, name: 'sample-project') }
let(:label) { create(:label, project: project) }
let(:reference) { label.to_reference }
@@ -48,6 +48,14 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
expect(link).to eq urls.namespace_project_issues_path(project.namespace, project, label_name: label.name)
end
+ context 'project that does not exist referenced' do
+ let(:result) { reference_filter('aaa/bbb~ccc') }
+
+ it 'does not link reference' do
+ expect(result.to_html).to eq 'aaa/bbb~ccc'
+ end
+ end
+
describe 'label span element' do
it 'includes default classes' do
doc = reference_filter("Label #{reference}")
@@ -334,14 +342,14 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
end
context 'with project reference' do
- let(:reference) { project.to_reference + group_label.to_reference(format: :name) }
+ let(:reference) { "#{project.to_reference}#{group_label.to_reference(format: :name)}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}", project: project)
expect(doc.css('a').first.attr('href')).to eq urls.
namespace_project_issues_url(project.namespace, project, label_name: group_label.name)
- expect(doc.text).to eq 'See gfm references'
+ expect(doc.text).to eq "See gfm references"
end
it 'links with adjacent text' do
@@ -357,68 +365,247 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
end
end
- describe 'cross project label references' do
- context 'valid project referenced' do
- let(:another_project) { create(:empty_project, :public) }
- let(:project_name) { another_project.name_with_namespace }
- let(:label) { create(:label, project: another_project, color: '#00ff00') }
- let(:reference) { label.to_reference(project) }
+ describe 'cross-project / cross-namespace complete reference' do
+ let(:project2) { create(:empty_project) }
+ let(:label) { create(:label, project: project2, color: '#00ff00') }
+ let(:reference) { "#{project2.path_with_namespace}~#{label.name}" }
+ let!(:result) { reference_filter("See #{reference}") }
- let!(:result) { reference_filter("See #{reference}") }
+ it 'links to a valid reference' do
+ expect(result.css('a').first.attr('href'))
+ .to eq urls.namespace_project_issues_url(project2.namespace,
+ project2,
+ label_name: label.name)
+ end
- it 'points to referenced project issues page' do
- expect(result.css('a').first.attr('href'))
- .to eq urls.namespace_project_issues_url(another_project.namespace,
- another_project,
- label_name: label.name)
- end
+ it 'has valid color' do
+ expect(result.css('a span').first.attr('style')).to match /background-color: #00ff00/
+ end
- it 'has valid color' do
- expect(result.css('a span').first.attr('style'))
- .to match /background-color: #00ff00/
- end
+ it 'has valid link text' do
+ expect(result.css('a').first.text).to eq "#{label.name} in #{project2.name_with_namespace}"
+ end
- it 'contains cross project content' do
- expect(result.css('a').first.text).to eq "#{label.name} in #{project_name}"
- end
+ it 'has valid text' do
+ expect(result.text).to eq "See #{label.name} in #{project2.name_with_namespace}"
end
- context 'project that does not exist referenced' do
- let(:result) { reference_filter('aaa/bbb~ccc') }
+ it 'ignores invalid IDs on the referenced label' do
+ exp = act = "See #{invalidate_reference(reference)}"
- it 'does not link reference' do
- expect(result.to_html).to eq 'aaa/bbb~ccc'
- end
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ describe 'cross-project / same-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, namespace: namespace) }
+ let(:project2) { create(:empty_project, namespace: namespace) }
+ let(:label) { create(:label, project: project2, color: '#00ff00') }
+ let(:reference) { "#{project2.path_with_namespace}~#{label.name}" }
+ let!(:result) { reference_filter("See #{reference}") }
+
+ it 'links to a valid reference' do
+ expect(result.css('a').first.attr('href'))
+ .to eq urls.namespace_project_issues_url(project2.namespace,
+ project2,
+ label_name: label.name)
+ end
+
+ it 'has valid color' do
+ expect(result.css('a span').first.attr('style')).to match /background-color: #00ff00/
+ end
+
+ it 'has valid link text' do
+ expect(result.css('a').first.text).to eq "#{label.name} in #{project2.name}"
+ end
+
+ it 'has valid text' do
+ expect(result.text).to eq "See #{label.name} in #{project2.name}"
+ end
+
+ it 'ignores invalid IDs on the referenced label' do
+ exp = act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ describe 'cross-project shorthand reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, namespace: namespace) }
+ let(:project2) { create(:empty_project, namespace: namespace) }
+ let(:label) { create(:label, project: project2, color: '#00ff00') }
+ let(:reference) { "#{project2.path}~#{label.name}" }
+ let!(:result) { reference_filter("See #{reference}") }
+
+ it 'links to a valid reference' do
+ expect(result.css('a').first.attr('href'))
+ .to eq urls.namespace_project_issues_url(project2.namespace,
+ project2,
+ label_name: label.name)
+ end
+
+ it 'has valid color' do
+ expect(result.css('a span').first.attr('style')).
+ to match /background-color: #00ff00/
+ end
+
+ it 'has valid link text' do
+ expect(result.css('a').first.text).to eq "#{label.name} in #{project2.name}"
+ end
+
+ it 'has valid text' do
+ expect(result.text).to eq "See #{label.name} in #{project2.name}"
+ end
+
+ it 'ignores invalid IDs on the referenced label' do
+ exp = act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
end
end
describe 'cross group label references' do
- context 'valid project referenced' do
- let(:group) { create(:group) }
- let(:project) { create(:empty_project, :public, namespace: group) }
- let(:another_group) { create(:group) }
- let(:another_project) { create(:empty_project, :public, namespace: another_group) }
- let(:project_name) { another_project.name_with_namespace }
- let(:group_label) { create(:group_label, group: another_group, color: '#00ff00') }
- let(:reference) { another_project.to_reference + group_label.to_reference }
-
- let!(:result) { reference_filter("See #{reference}", project: project) }
-
- it 'points to referenced project issues page' do
- expect(result.css('a').first.attr('href'))
- .to eq urls.namespace_project_issues_url(another_project.namespace,
- another_project,
- label_name: group_label.name)
- end
+ let(:group) { create(:group) }
+ let(:project) { create(:empty_project, :public, namespace: group) }
+ let(:another_group) { create(:group) }
+ let(:another_project) { create(:empty_project, :public, namespace: another_group) }
+ let(:group_label) { create(:group_label, group: another_group, color: '#00ff00') }
+ let(:reference) { "#{another_project.path_with_namespace}~#{group_label.name}" }
+ let!(:result) { reference_filter("See #{reference}", project: project) }
- it 'has valid color' do
- expect(result.css('a span').first.attr('style'))
- .to match /background-color: #00ff00/
- end
+ it 'points to referenced project issues page' do
+ expect(result.css('a').first.attr('href'))
+ .to eq urls.namespace_project_issues_url(another_project.namespace,
+ another_project,
+ label_name: group_label.name)
+ end
- it 'contains cross project content' do
- expect(result.css('a').first.text).to eq "#{group_label.name} in #{project_name}"
- end
+ it 'has valid color' do
+ expect(result.css('a span').first.attr('style')).
+ to match /background-color: #00ff00/
+ end
+
+ it 'has valid link text' do
+ expect(result.css('a').first.text).
+ to eq "#{group_label.name} in #{another_project.name_with_namespace}"
+ end
+
+ it 'has valid text' do
+ expect(result.text).
+ to eq "See #{group_label.name} in #{another_project.name_with_namespace}"
+ end
+
+ it 'ignores invalid IDs on the referenced label' do
+ exp = act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ describe 'cross-project / same-group_label complete reference' do
+ let(:group) { create(:group) }
+ let(:project) { create(:empty_project, :public, namespace: group) }
+ let(:another_project) { create(:empty_project, :public, namespace: group) }
+ let(:group_label) { create(:group_label, group: group, color: '#00ff00') }
+ let(:reference) { "#{another_project.path_with_namespace}~#{group_label.name}" }
+ let!(:result) { reference_filter("See #{reference}", project: project) }
+
+ it 'points to referenced project issues page' do
+ expect(result.css('a').first.attr('href')).
+ to eq urls.namespace_project_issues_url(another_project.namespace,
+ another_project,
+ label_name: group_label.name)
+ end
+
+ it 'has valid color' do
+ expect(result.css('a span').first.attr('style')).
+ to match /background-color: #00ff00/
+ end
+
+ it 'has valid link text' do
+ expect(result.css('a').first.text).
+ to eq "#{group_label.name} in #{another_project.name}"
+ end
+
+ it 'has valid text' do
+ expect(result.text).
+ to eq "See #{group_label.name} in #{another_project.name}"
+ end
+
+ it 'ignores invalid IDs on the referenced label' do
+ exp = act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ describe 'same project / same group_label complete reference' do
+ let(:group) { create(:group) }
+ let(:project) { create(:empty_project, :public, namespace: group) }
+ let(:group_label) { create(:group_label, group: group, color: '#00ff00') }
+ let(:reference) { "#{project.path_with_namespace}~#{group_label.name}" }
+ let!(:result) { reference_filter("See #{reference}", project: project) }
+
+ it 'points to referenced project issues page' do
+ expect(result.css('a').first.attr('href'))
+ .to eq urls.namespace_project_issues_url(project.namespace,
+ project,
+ label_name: group_label.name)
+ end
+
+ it 'has valid color' do
+ expect(result.css('a span').first.attr('style'))
+ .to match /background-color: #00ff00/
+ end
+
+ it 'has valid link text' do
+ expect(result.css('a').first.text).to eq group_label.name
+ end
+
+ it 'has valid text' do
+ expect(result.text).to eq "See #{group_label.name}"
+ end
+
+ it 'ignores invalid IDs on the referenced label' do
+ exp = act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ describe 'same project / same group_label shorthand reference' do
+ let(:group) { create(:group) }
+ let(:project) { create(:empty_project, :public, namespace: group) }
+ let(:group_label) { create(:group_label, group: group, color: '#00ff00') }
+ let(:reference) { "#{project.path}~#{group_label.name}" }
+ let!(:result) { reference_filter("See #{reference}", project: project) }
+
+ it 'points to referenced project issues page' do
+ expect(result.css('a').first.attr('href'))
+ .to eq urls.namespace_project_issues_url(project.namespace,
+ project,
+ label_name: group_label.name)
+ end
+
+ it 'has valid color' do
+ expect(result.css('a span').first.attr('style')).
+ to match /background-color: #00ff00/
+ end
+
+ it 'has valid link text' do
+ expect(result.css('a').first.text).to eq group_label.name
+ end
+
+ it 'has valid text' do
+ expect(result.text).to eq "See #{group_label.name}"
+ end
+
+ it 'ignores invalid IDs on the referenced label' do
+ exp = act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
end
end
end
diff --git a/spec/lib/banzai/filter/math_filter_spec.rb b/spec/lib/banzai/filter/math_filter_spec.rb
new file mode 100644
index 00000000000..3fe2c7f5d5d
--- /dev/null
+++ b/spec/lib/banzai/filter/math_filter_spec.rb
@@ -0,0 +1,120 @@
+require 'spec_helper'
+
+describe Banzai::Filter::MathFilter, lib: true do
+ include FilterSpecHelper
+
+ it 'leaves regular inline code unchanged' do
+ input = "<code>2+2</code>"
+ doc = filter(input)
+
+ expect(doc.to_s).to eq input
+ end
+
+ it 'removes surrounding dollar signs and adds class code, math and js-render-math' do
+ doc = filter("$<code>2+2</code>$")
+
+ expect(doc.to_s).to eq '<code class="code math js-render-math" data-math-style="inline">2+2</code>'
+ end
+
+ it 'only removes surrounding dollar signs' do
+ doc = filter("test $<code>2+2</code>$ test")
+ before = doc.xpath('descendant-or-self::text()[1]').first
+ after = doc.xpath('descendant-or-self::text()[3]').first
+
+ expect(before.to_s).to eq 'test '
+ expect(after.to_s).to eq ' test'
+ end
+
+ it 'only removes surrounding single dollar sign' do
+ doc = filter("test $$<code>2+2</code>$$ test")
+ before = doc.xpath('descendant-or-self::text()[1]').first
+ after = doc.xpath('descendant-or-self::text()[3]').first
+
+ expect(before.to_s).to eq 'test $'
+ expect(after.to_s).to eq '$ test'
+ end
+
+ it 'adds data-math-style inline attribute to inline math' do
+ doc = filter('$<code>2+2</code>$')
+ code = doc.xpath('descendant-or-self::code').first
+
+ expect(code['data-math-style']).to eq 'inline'
+ end
+
+ it 'adds class code and math to inline math' do
+ doc = filter('$<code>2+2</code>$')
+ code = doc.xpath('descendant-or-self::code').first
+
+ expect(code[:class]).to include("code")
+ expect(code[:class]).to include("math")
+ end
+
+ it 'adds js-render-math class to inline math' do
+ doc = filter('$<code>2+2</code>$')
+ code = doc.xpath('descendant-or-self::code').first
+
+ expect(code[:class]).to include("js-render-math")
+ end
+
+ # Cases with faulty syntax. Should be a no-op
+
+ it 'ignores cases with missing dolar sign at the end' do
+ input = "test $<code>2+2</code> test"
+ doc = filter(input)
+
+ expect(doc.to_s).to eq input
+ end
+
+ it 'ignores cases with missing dolar sign at the beginning' do
+ input = "test <code>2+2</code>$ test"
+ doc = filter(input)
+
+ expect(doc.to_s).to eq input
+ end
+
+ it 'ignores dollar signs if it is not adjacent' do
+ input = '<p>We check strictly $<code>2+2</code> and <code>2+2</code>$ </p>'
+ doc = filter(input)
+
+ expect(doc.to_s).to eq input
+ end
+
+ # Display math
+
+ it 'adds data-math-style display attribute to display math' do
+ doc = filter('<pre class="code highlight js-syntax-highlight math" v-pre="true"><code>2+2</code></pre>')
+ pre = doc.xpath('descendant-or-self::pre').first
+
+ expect(pre['data-math-style']).to eq 'display'
+ end
+
+ it 'adds js-render-math class to display math' do
+ doc = filter('<pre class="code highlight js-syntax-highlight math" v-pre="true"><code>2+2</code></pre>')
+ pre = doc.xpath('descendant-or-self::pre').first
+
+ expect(pre[:class]).to include("js-render-math")
+ end
+
+ it 'ignores code blocks that are not math' do
+ input = '<pre class="code highlight js-syntax-highlight plaintext" v-pre="true"><code>2+2</code></pre>'
+ doc = filter(input)
+
+ expect(doc.to_s).to eq input
+ end
+
+ it 'requires the pre to contain both code and math' do
+ input = '<pre class="highlight js-syntax-highlight plaintext math" v-pre="true"><code>2+2</code></pre>'
+ doc = filter(input)
+
+ expect(doc.to_s).to eq input
+ end
+
+ it 'dollar signs around to display math' do
+ doc = filter('$<pre class="code highlight js-syntax-highlight math" v-pre="true"><code>2+2</code></pre>$')
+ before = doc.xpath('descendant-or-self::text()[1]').first
+ after = doc.xpath('descendant-or-self::text()[3]').first
+
+ expect(before.to_s).to eq '$'
+ expect(after.to_s).to eq '$'
+ end
+end
diff --git a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
index 274258a045c..275010c1a2c 100644
--- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
include FilterSpecHelper
- let(:project) { create(:project, :public) }
+ let(:project) { create(:empty_project, :public) }
let(:merge) { create(:merge_request, source_project: project) }
it 'requires project context' do
@@ -86,23 +86,97 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
end
end
- context 'cross-project reference' do
- let(:namespace) { create(:namespace, name: 'cross-reference') }
- let(:project2) { create(:project, :public, namespace: namespace) }
- let(:merge) { create(:merge_request, source_project: project2) }
- let(:reference) { merge.to_reference(project) }
+ context 'cross-project / cross-namespace complete reference' do
+ let(:project2) { create(:empty_project, :public) }
+ let(:merge) { create(:merge_request, source_project: project2) }
+ let(:reference) { "#{project2.path_with_namespace}!#{merge.iid}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
expect(doc.css('a').first.attr('href')).
to eq urls.namespace_project_merge_request_url(project2.namespace,
- project, merge)
+ project2, merge)
end
- it 'links with adjacent text' do
+ it 'link has valid text' do
doc = reference_filter("Merge (#{reference}.)")
- expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
+
+ expect(doc.css('a').first.text).to eq(reference)
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Merge (#{reference}.)")
+
+ expect(doc.text).to eq("Merge (#{reference}.)")
+ end
+
+ it 'ignores invalid merge IDs on the referenced project' do
+ exp = act = "Merge #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'cross-project / same-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, :public, namespace: namespace) }
+ let(:project2) { create(:empty_project, :public, namespace: namespace) }
+ let!(:merge) { create(:merge_request, source_project: project2) }
+ let(:reference) { "#{project2.path_with_namespace}!#{merge.iid}" }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq urls.namespace_project_merge_request_url(project2.namespace,
+ project2, merge)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("Merge (#{reference}.)")
+
+ expect(doc.css('a').first.text).to eq("#{project2.path}!#{merge.iid}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Merge (#{reference}.)")
+
+ expect(doc.text).to eq("Merge (#{project2.path}!#{merge.iid}.)")
+ end
+
+ it 'ignores invalid merge IDs on the referenced project' do
+ exp = act = "Merge #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'cross-project shorthand reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, :public, namespace: namespace) }
+ let(:project2) { create(:empty_project, :public, namespace: namespace) }
+ let!(:merge) { create(:merge_request, source_project: project2) }
+ let(:reference) { "#{project2.path}!#{merge.iid}" }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq urls.namespace_project_merge_request_url(project2.namespace,
+ project2, merge)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("Merge (#{reference}.)")
+
+ expect(doc.css('a').first.text).to eq("#{project2.path}!#{merge.iid}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("Merge (#{reference}.)")
+
+ expect(doc.text).to eq("Merge (#{project2.path}!#{merge.iid}.)")
end
it 'ignores invalid merge IDs on the referenced project' do
diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
index 7419863d848..73b5edb99b3 100644
--- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
@@ -148,13 +148,51 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
end
end
- describe 'cross project milestone references' do
- let(:another_project) { create(:empty_project, :public) }
- let(:project_path) { another_project.path_with_namespace }
- let(:milestone) { create(:milestone, project: another_project) }
- let(:reference) { milestone.to_reference(project) }
+ describe 'cross-project / cross-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
+ let(:another_project) { create(:empty_project, :public, namespace: namespace) }
+ let(:milestone) { create(:milestone, project: another_project) }
+ let(:reference) { "#{another_project.path_with_namespace}%#{milestone.iid}" }
+ let!(:result) { reference_filter("See #{reference}") }
- let!(:result) { reference_filter("See #{reference}") }
+ it 'points to referenced project milestone page' do
+ expect(result.css('a').first.attr('href')).to eq urls.
+ namespace_project_milestone_url(another_project.namespace,
+ another_project,
+ milestone)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.css('a').first.text).
+ to eq("#{milestone.name} in #{another_project.path_with_namespace}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.text).
+ to eq("See (#{milestone.name} in #{another_project.path_with_namespace}.)")
+ end
+
+ it 'escapes the name attribute' do
+ allow_any_instance_of(Milestone).to receive(:title).and_return(%{"></a>whatever<a title="})
+
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.text).
+ to eq "#{milestone.name} in #{another_project.path_with_namespace}"
+ end
+ end
+
+ describe 'cross-project / same-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, :public, namespace: namespace) }
+ let(:another_project) { create(:empty_project, :public, namespace: namespace) }
+ let(:milestone) { create(:milestone, project: another_project) }
+ let(:reference) { "#{another_project.path_with_namespace}%#{milestone.iid}" }
+ let!(:result) { reference_filter("See #{reference}") }
it 'points to referenced project milestone page' do
expect(result.css('a').first.attr('href')).to eq urls.
@@ -163,14 +201,66 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
milestone)
end
- it 'contains cross project content' do
- expect(result.css('a').first.text).to eq "#{milestone.name} in #{project_path}"
+ it 'link has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.css('a').first.text).
+ to eq("#{milestone.name} in #{another_project.path}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.text).
+ to eq("See (#{milestone.name} in #{another_project.path}.)")
end
it 'escapes the name attribute' do
allow_any_instance_of(Milestone).to receive(:title).and_return(%{"></a>whatever<a title="})
+
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.text).
+ to eq "#{milestone.name} in #{another_project.path}"
+ end
+ end
+
+ describe 'cross project shorthand reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, :public, namespace: namespace) }
+ let(:another_project) { create(:empty_project, :public, namespace: namespace) }
+ let(:milestone) { create(:milestone, project: another_project) }
+ let(:reference) { "#{another_project.path}%#{milestone.iid}" }
+ let!(:result) { reference_filter("See #{reference}") }
+
+ it 'points to referenced project milestone page' do
+ expect(result.css('a').first.attr('href')).to eq urls.
+ namespace_project_milestone_url(another_project.namespace,
+ another_project,
+ milestone)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.css('a').first.text).
+ to eq("#{milestone.name} in #{another_project.path}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.text).
+ to eq("See (#{milestone.name} in #{another_project.path}.)")
+ end
+
+ it 'escapes the name attribute' do
+ allow_any_instance_of(Milestone).to receive(:title).and_return(%{"></a>whatever<a title="})
+
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.text).to eq "#{milestone.name} in #{project_path}"
+
+ expect(doc.css('a').first.text).
+ to eq "#{milestone.name} in #{another_project.path}"
end
end
end
diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb
index f181125156b..0140a91c7ba 100644
--- a/spec/lib/banzai/filter/redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/redactor_filter_spec.rb
@@ -28,31 +28,39 @@ describe Banzai::Filter::RedactorFilter, lib: true do
and_return(parser_class)
end
- it 'removes unpermitted Project references' do
- user = create(:user)
- project = create(:empty_project)
+ context 'valid projects' do
+ before { allow_any_instance_of(Banzai::ReferenceParser::BaseParser).to receive(:can_read_reference?).and_return(true) }
- link = reference_link(project: project.id, reference_type: 'test')
- doc = filter(link, current_user: user)
+ it 'allows permitted Project references' do
+ user = create(:user)
+ project = create(:empty_project)
+ project.team << [user, :master]
+
+ link = reference_link(project: project.id, reference_type: 'test')
+ doc = filter(link, current_user: user)
- expect(doc.css('a').length).to eq 0
+ expect(doc.css('a').length).to eq 1
+ end
end
- it 'allows permitted Project references' do
- user = create(:user)
- project = create(:empty_project)
- project.team << [user, :master]
+ context 'invalid projects' do
+ before { allow_any_instance_of(Banzai::ReferenceParser::BaseParser).to receive(:can_read_reference?).and_return(false) }
- link = reference_link(project: project.id, reference_type: 'test')
- doc = filter(link, current_user: user)
+ it 'removes unpermitted references' do
+ user = create(:user)
+ project = create(:empty_project)
- expect(doc.css('a').length).to eq 1
- end
+ link = reference_link(project: project.id, reference_type: 'test')
+ doc = filter(link, current_user: user)
- it 'handles invalid Project references' do
- link = reference_link(project: 12345, reference_type: 'test')
+ expect(doc.css('a').length).to eq 0
+ end
+
+ it 'handles invalid references' do
+ link = reference_link(project: 12345, reference_type: 'test')
- expect { filter(link) }.not_to raise_error
+ expect { filter(link) }.not_to raise_error
+ end
end
end
diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb
index 2bfa51deb20..df2dd173b57 100644
--- a/spec/lib/banzai/filter/relative_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb
@@ -175,7 +175,7 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do
allow_any_instance_of(described_class).to receive(:uri_type).and_return(:raw)
doc = filter(image(escaped))
- expect(doc.at_css('img')['src']).to match '/raw/'
+ expect(doc.at_css('img')['src']).to eq "/#{project_path}/raw/#{Addressable::URI.escape(ref)}/#{escaped}"
end
context 'when requested path is a file in the repo' do
diff --git a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
index 9b92d1a3926..e036514d283 100644
--- a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
@@ -79,11 +79,11 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do
end
end
- context 'cross-project reference' do
- let(:namespace) { create(:namespace, name: 'cross-reference') }
+ context 'cross-project / cross-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
let(:project2) { create(:empty_project, :public, namespace: namespace) }
- let(:snippet) { create(:project_snippet, project: project2) }
- let(:reference) { snippet.to_reference(project) }
+ let!(:snippet) { create(:project_snippet, project: project2) }
+ let(:reference) { "#{project2.path_with_namespace}$#{snippet.id}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
@@ -92,9 +92,82 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do
to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
end
- it 'links with adjacent text' do
+ it 'link has valid text' do
doc = reference_filter("See (#{reference}.)")
- expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
+
+ expect(doc.css('a').first.text).to eql(reference)
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("See (#{reference}.)")
+
+ expect(doc.text).to eql("See (#{reference}.)")
+ end
+
+ it 'ignores invalid snippet IDs on the referenced project' do
+ exp = act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'cross-project / same-namespace complete reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, :public, namespace: namespace) }
+ let(:project2) { create(:empty_project, :public, namespace: namespace) }
+ let!(:snippet) { create(:project_snippet, project: project2) }
+ let(:reference) { "#{project2.path_with_namespace}$#{snippet.id}" }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("See (#{project2.path}$#{snippet.id}.)")
+
+ expect(doc.css('a').first.text).to eql("#{project2.path}$#{snippet.id}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("See (#{project2.path}$#{snippet.id}.)")
+
+ expect(doc.text).to eql("See (#{project2.path}$#{snippet.id}.)")
+ end
+
+ it 'ignores invalid snippet IDs on the referenced project' do
+ exp = act = "See #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'cross-project shorthand reference' do
+ let(:namespace) { create(:namespace) }
+ let(:project) { create(:empty_project, :public, namespace: namespace) }
+ let(:project2) { create(:empty_project, :public, namespace: namespace) }
+ let!(:snippet) { create(:project_snippet, project: project2) }
+ let(:reference) { "#{project2.path}$#{snippet.id}" }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).
+ to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
+ end
+
+ it 'link has valid text' do
+ doc = reference_filter("See (#{project2.path}$#{snippet.id}.)")
+
+ expect(doc.css('a').first.text).to eql("#{project2.path}$#{snippet.id}")
+ end
+
+ it 'has valid text' do
+ doc = reference_filter("See (#{project2.path}$#{snippet.id}.)")
+
+ expect(doc.text).to eql("See (#{project2.path}$#{snippet.id}.)")
end
it 'ignores invalid snippet IDs on the referenced project' do
diff --git a/spec/lib/banzai/filter/table_of_contents_filter_spec.rb b/spec/lib/banzai/filter/table_of_contents_filter_spec.rb
index 356dd01a03a..70b31f3a880 100644
--- a/spec/lib/banzai/filter/table_of_contents_filter_spec.rb
+++ b/spec/lib/banzai/filter/table_of_contents_filter_spec.rb
@@ -22,7 +22,7 @@ describe Banzai::Filter::TableOfContentsFilter, lib: true do
html = header(i, "Header #{i}")
doc = filter(html)
- expect(doc.css("h#{i} a").first.attr('id')).to eq "header-#{i}"
+ expect(doc.css("h#{i} a").first.attr('id')).to eq "user-content-header-#{i}"
end
end
@@ -32,7 +32,12 @@ describe Banzai::Filter::TableOfContentsFilter, lib: true do
expect(doc.css('h1 a').first.attr('class')).to eq 'anchor'
end
- it 'links to the id' do
+ it 'has a namespaced id' do
+ doc = filter(header(1, 'Header'))
+ expect(doc.css('h1 a').first.attr('id')).to eq 'user-content-header'
+ end
+
+ it 'links to the non-namespaced id' do
doc = filter(header(1, 'Header'))
expect(doc.css('h1 a').first.attr('href')).to eq '#header'
end
@@ -40,29 +45,29 @@ describe Banzai::Filter::TableOfContentsFilter, lib: true do
describe 'generated IDs' do
it 'translates spaces to dashes' do
doc = filter(header(1, 'This header has spaces in it'))
- expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-has-spaces-in-it'
+ expect(doc.css('h1 a').first.attr('href')).to eq '#this-header-has-spaces-in-it'
end
it 'squeezes multiple spaces and dashes' do
doc = filter(header(1, 'This---header is poorly-formatted'))
- expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-is-poorly-formatted'
+ expect(doc.css('h1 a').first.attr('href')).to eq '#this-header-is-poorly-formatted'
end
it 'removes punctuation' do
doc = filter(header(1, "This, header! is, filled. with @ punctuation?"))
- expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-is-filled-with-punctuation'
+ expect(doc.css('h1 a').first.attr('href')).to eq '#this-header-is-filled-with-punctuation'
end
it 'appends a unique number to duplicates' do
doc = filter(header(1, 'One') + header(2, 'One'))
- expect(doc.css('h1 a').first.attr('id')).to eq 'one'
- expect(doc.css('h2 a').first.attr('id')).to eq 'one-1'
+ expect(doc.css('h1 a').first.attr('href')).to eq '#one'
+ expect(doc.css('h2 a').first.attr('href')).to eq '#one-1'
end
it 'supports Unicode' do
doc = filter(header(1, '한글'))
- expect(doc.css('h1 a').first.attr('id')).to eq '한글'
+ expect(doc.css('h1 a').first.attr('id')).to eq 'user-content-한글'
expect(doc.css('h1 a').first.attr('href')).to eq '#한글'
end
end
diff --git a/spec/lib/banzai/reference_parser/base_parser_spec.rb b/spec/lib/banzai/reference_parser/base_parser_spec.rb
index 9095d2b1345..aa127f0179d 100644
--- a/spec/lib/banzai/reference_parser/base_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/base_parser_spec.rb
@@ -27,41 +27,12 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
let(:link) { empty_html_link }
context 'when the link has a data-project attribute' do
- it 'returns the nodes if the attribute value equals the current project ID' do
+ it 'checks if user can read the resource' do
link['data-project'] = project.id.to_s
- expect(Ability).not_to receive(:allowed?)
- expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
- end
-
- it 'returns the nodes if the user can read the project' do
- other_project = create(:empty_project, :public)
-
- link['data-project'] = other_project.id.to_s
-
- expect(Ability).to receive(:allowed?).
- with(user, :read_project, other_project).
- and_return(true)
-
- expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
- end
-
- it 'returns an empty Array when the attribute value is empty' do
- link['data-project'] = ''
-
- expect(subject.nodes_visible_to_user(user, [link])).to eq([])
- end
-
- it 'returns an empty Array when the user can not read the project' do
- other_project = create(:empty_project, :public)
-
- link['data-project'] = other_project.id.to_s
-
- expect(Ability).to receive(:allowed?).
- with(user, :read_project, other_project).
- and_return(false)
+ expect(subject).to receive(:can_read_reference?).with(user, project)
- expect(subject.nodes_visible_to_user(user, [link])).to eq([])
+ subject.nodes_visible_to_user(user, [link])
end
end
diff --git a/spec/lib/banzai/reference_parser/commit_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_parser_spec.rb
index 0b76d29fce0..412ffa77c36 100644
--- a/spec/lib/banzai/reference_parser/commit_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/commit_parser_spec.rb
@@ -8,6 +8,14 @@ describe Banzai::ReferenceParser::CommitParser, lib: true do
subject { described_class.new(project, user) }
let(:link) { empty_html_link }
+ describe '#nodes_visible_to_user' do
+ context 'when the link has a data-issue attribute' do
+ before { link['data-commit'] = 123 }
+
+ it_behaves_like "referenced feature visibility", "repository"
+ end
+ end
+
describe '#referenced_by' do
context 'when the link has a data-project attribute' do
before do
diff --git a/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb
index ba982f38542..96e55b0997a 100644
--- a/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb
@@ -8,6 +8,14 @@ describe Banzai::ReferenceParser::CommitRangeParser, lib: true do
subject { described_class.new(project, user) }
let(:link) { empty_html_link }
+ describe '#nodes_visible_to_user' do
+ context 'when the link has a data-issue attribute' do
+ before { link['data-commit-range'] = '123..456' }
+
+ it_behaves_like "referenced feature visibility", "repository"
+ end
+ end
+
describe '#referenced_by' do
context 'when the link has a data-project attribute' do
before do
diff --git a/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb
index a6ef8394fe7..50a5d1a19ba 100644
--- a/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/external_issue_parser_spec.rb
@@ -8,6 +8,14 @@ describe Banzai::ReferenceParser::ExternalIssueParser, lib: true do
subject { described_class.new(project, user) }
let(:link) { empty_html_link }
+ describe '#nodes_visible_to_user' do
+ context 'when the link has a data-issue attribute' do
+ before { link['data-external-issue'] = 123 }
+
+ it_behaves_like "referenced feature visibility", "issues"
+ end
+ end
+
describe '#referenced_by' do
context 'when the link has a data-project attribute' do
before do
diff --git a/spec/lib/banzai/reference_parser/issue_parser_spec.rb b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
index 85cfe728b6a..6873b7b85f9 100644
--- a/spec/lib/banzai/reference_parser/issue_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
@@ -4,10 +4,10 @@ describe Banzai::ReferenceParser::IssueParser, lib: true do
include ReferenceParserHelpers
let(:project) { create(:empty_project, :public) }
- let(:user) { create(:user) }
- let(:issue) { create(:issue, project: project) }
- subject { described_class.new(project, user) }
- let(:link) { empty_html_link }
+ let(:user) { create(:user) }
+ let(:issue) { create(:issue, project: project) }
+ let(:link) { empty_html_link }
+ subject { described_class.new(project, user) }
describe '#nodes_visible_to_user' do
context 'when the link has a data-issue attribute' do
@@ -15,6 +15,8 @@ describe Banzai::ReferenceParser::IssueParser, lib: true do
link['data-issue'] = issue.id.to_s
end
+ it_behaves_like "referenced feature visibility", "issues"
+
it 'returns the nodes when the user can read the issue' do
expect(Ability).to receive(:issues_readable_by_user).
with([issue], user).
diff --git a/spec/lib/banzai/reference_parser/label_parser_spec.rb b/spec/lib/banzai/reference_parser/label_parser_spec.rb
index 77fda47f0e7..8c540d35ddd 100644
--- a/spec/lib/banzai/reference_parser/label_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/label_parser_spec.rb
@@ -9,6 +9,14 @@ describe Banzai::ReferenceParser::LabelParser, lib: true do
subject { described_class.new(project, user) }
let(:link) { empty_html_link }
+ describe '#nodes_visible_to_user' do
+ context 'when the link has a data-issue attribute' do
+ before { link['data-label'] = label.id.to_s }
+
+ it_behaves_like "referenced feature visibility", "issues", "merge_requests"
+ end
+ end
+
describe '#referenced_by' do
describe 'when the link has a data-label attribute' do
context 'using an existing label ID' do
diff --git a/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb b/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb
index cf89ad598ea..cb69ca16800 100644
--- a/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/merge_request_parser_spec.rb
@@ -8,6 +8,19 @@ describe Banzai::ReferenceParser::MergeRequestParser, lib: true do
subject { described_class.new(merge_request.target_project, user) }
let(:link) { empty_html_link }
+ describe '#nodes_visible_to_user' do
+ context 'when the link has a data-issue attribute' do
+ let(:project) { merge_request.target_project }
+
+ before do
+ project.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PUBLIC)
+ link['data-merge-request'] = merge_request.id.to_s
+ end
+
+ it_behaves_like "referenced feature visibility", "merge_requests"
+ end
+ end
+
describe '#referenced_by' do
describe 'when the link has a data-merge-request attribute' do
context 'using an existing merge request ID' do
diff --git a/spec/lib/banzai/reference_parser/milestone_parser_spec.rb b/spec/lib/banzai/reference_parser/milestone_parser_spec.rb
index 6aa45a22cc4..2d4d589ae34 100644
--- a/spec/lib/banzai/reference_parser/milestone_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/milestone_parser_spec.rb
@@ -9,6 +9,14 @@ describe Banzai::ReferenceParser::MilestoneParser, lib: true do
subject { described_class.new(project, user) }
let(:link) { empty_html_link }
+ describe '#nodes_visible_to_user' do
+ context 'when the link has a data-issue attribute' do
+ before { link['data-milestone'] = milestone.id.to_s }
+
+ it_behaves_like "referenced feature visibility", "issues", "merge_requests"
+ end
+ end
+
describe '#referenced_by' do
describe 'when the link has a data-milestone attribute' do
context 'using an existing milestone ID' do
diff --git a/spec/lib/banzai/reference_parser/snippet_parser_spec.rb b/spec/lib/banzai/reference_parser/snippet_parser_spec.rb
index 59127b7c5d1..d217a775802 100644
--- a/spec/lib/banzai/reference_parser/snippet_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/snippet_parser_spec.rb
@@ -9,6 +9,14 @@ describe Banzai::ReferenceParser::SnippetParser, lib: true do
subject { described_class.new(project, user) }
let(:link) { empty_html_link }
+ describe '#nodes_visible_to_user' do
+ context 'when the link has a data-issue attribute' do
+ before { link['data-snippet'] = snippet.id.to_s }
+
+ it_behaves_like "referenced feature visibility", "snippets"
+ end
+ end
+
describe '#referenced_by' do
describe 'when the link has a data-snippet attribute' do
context 'using an existing snippet ID' do
diff --git a/spec/lib/banzai/reference_parser/user_parser_spec.rb b/spec/lib/banzai/reference_parser/user_parser_spec.rb
index 4e7f82a6e09..fafc2cec546 100644
--- a/spec/lib/banzai/reference_parser/user_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/user_parser_spec.rb
@@ -103,6 +103,8 @@ describe Banzai::ReferenceParser::UserParser, lib: true do
it 'returns the nodes if the attribute value equals the current project ID' do
link['data-project'] = project.id.to_s
+ # Ensure that we dont call for Ability.allowed?
+ # When project_id in the node is equal to current project ID
expect(Ability).not_to receive(:allowed?)
expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
diff --git a/spec/lib/bitbucket/collection_spec.rb b/spec/lib/bitbucket/collection_spec.rb
new file mode 100644
index 00000000000..015a7f80e03
--- /dev/null
+++ b/spec/lib/bitbucket/collection_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+# Emulates paginator. It returns 2 pages with results
+class TestPaginator
+ def initialize
+ @current_page = 0
+ end
+
+ def items
+ @current_page += 1
+
+ raise StopIteration if @current_page > 2
+
+ ["result_1_page_#{@current_page}", "result_2_page_#{@current_page}"]
+ end
+end
+
+describe Bitbucket::Collection do
+ it "iterates paginator" do
+ collection = described_class.new(TestPaginator.new)
+
+ expect(collection.to_a).to match(["result_1_page_1", "result_2_page_1", "result_1_page_2", "result_2_page_2"])
+ end
+end
diff --git a/spec/lib/bitbucket/connection_spec.rb b/spec/lib/bitbucket/connection_spec.rb
new file mode 100644
index 00000000000..14faeb231a9
--- /dev/null
+++ b/spec/lib/bitbucket/connection_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe Bitbucket::Connection do
+ before do
+ allow_any_instance_of(described_class).to receive(:provider).and_return(double(app_id: '', app_secret: ''))
+ end
+
+ describe '#get' do
+ it 'calls OAuth2::AccessToken::get' do
+ expect_any_instance_of(OAuth2::AccessToken).to receive(:get).and_return(double(parsed: true))
+
+ connection = described_class.new({})
+
+ connection.get('/users')
+ end
+ end
+
+ describe '#expired?' do
+ it 'calls connection.expired?' do
+ expect_any_instance_of(OAuth2::AccessToken).to receive(:expired?).and_return(true)
+
+ expect(described_class.new({}).expired?).to be_truthy
+ end
+ end
+
+ describe '#refresh!' do
+ it 'calls connection.refresh!' do
+ response = double(token: nil, expires_at: nil, expires_in: nil, refresh_token: nil)
+
+ expect_any_instance_of(OAuth2::AccessToken).to receive(:refresh!).and_return(response)
+
+ described_class.new({}).refresh!
+ end
+ end
+end
diff --git a/spec/lib/bitbucket/page_spec.rb b/spec/lib/bitbucket/page_spec.rb
new file mode 100644
index 00000000000..04d5a0470b1
--- /dev/null
+++ b/spec/lib/bitbucket/page_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+
+describe Bitbucket::Page do
+ let(:response) { { 'values' => [{ 'username' => 'Ben' }], 'pagelen' => 2, 'next' => '' } }
+
+ before do
+ # Autoloading hack
+ Bitbucket::Representation::User.new({})
+ end
+
+ describe '#items' do
+ it 'returns collection of needed objects' do
+ page = described_class.new(response, :user)
+
+ expect(page.items.first).to be_a(Bitbucket::Representation::User)
+ expect(page.items.count).to eq(1)
+ end
+ end
+
+ describe '#attrs' do
+ it 'returns attributes' do
+ page = described_class.new(response, :user)
+
+ expect(page.attrs.keys).to include(:pagelen, :next)
+ end
+ end
+
+ describe '#next?' do
+ it 'returns true' do
+ page = described_class.new(response, :user)
+
+ expect(page.next?).to be_truthy
+ end
+
+ it 'returns false' do
+ response['next'] = nil
+ page = described_class.new(response, :user)
+
+ expect(page.next?).to be_falsey
+ end
+ end
+
+ describe '#next' do
+ it 'returns next attribute' do
+ page = described_class.new(response, :user)
+
+ expect(page.next).to eq('')
+ end
+ end
+end
diff --git a/spec/lib/bitbucket/paginator_spec.rb b/spec/lib/bitbucket/paginator_spec.rb
new file mode 100644
index 00000000000..2c972da682e
--- /dev/null
+++ b/spec/lib/bitbucket/paginator_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe Bitbucket::Paginator do
+ let(:last_page) { double(:page, next?: false, items: ['item_2']) }
+ let(:first_page) { double(:page, next?: true, next: last_page, items: ['item_1']) }
+
+ describe 'items' do
+ it 'return items and raises StopIteration in the end' do
+ paginator = described_class.new(nil, nil, nil)
+
+ allow(paginator).to receive(:fetch_next_page).and_return(first_page)
+ expect(paginator.items).to match(['item_1'])
+
+ allow(paginator).to receive(:fetch_next_page).and_return(last_page)
+ expect(paginator.items).to match(['item_2'])
+
+ allow(paginator).to receive(:fetch_next_page).and_return(nil)
+ expect{ paginator.items }.to raise_error(StopIteration)
+ end
+ end
+end
diff --git a/spec/lib/bitbucket/representation/comment_spec.rb b/spec/lib/bitbucket/representation/comment_spec.rb
new file mode 100644
index 00000000000..fec243a9f96
--- /dev/null
+++ b/spec/lib/bitbucket/representation/comment_spec.rb
@@ -0,0 +1,22 @@
+require 'spec_helper'
+
+describe Bitbucket::Representation::Comment do
+ describe '#author' do
+ it { expect(described_class.new('user' => { 'username' => 'Ben' }).author).to eq('Ben') }
+ it { expect(described_class.new({}).author).to be_nil }
+ end
+
+ describe '#note' do
+ it { expect(described_class.new('content' => { 'raw' => 'Text' }).note).to eq('Text') }
+ it { expect(described_class.new({}).note).to be_nil }
+ end
+
+ describe '#created_at' do
+ it { expect(described_class.new('created_on' => Date.today).created_at).to eq(Date.today) }
+ end
+
+ describe '#updated_at' do
+ it { expect(described_class.new('updated_on' => Date.today).updated_at).to eq(Date.today) }
+ it { expect(described_class.new('created_on' => Date.today).updated_at).to eq(Date.today) }
+ end
+end
diff --git a/spec/lib/bitbucket/representation/issue_spec.rb b/spec/lib/bitbucket/representation/issue_spec.rb
new file mode 100644
index 00000000000..20f47224aa8
--- /dev/null
+++ b/spec/lib/bitbucket/representation/issue_spec.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+describe Bitbucket::Representation::Issue do
+ describe '#iid' do
+ it { expect(described_class.new('id' => 1).iid).to eq(1) }
+ end
+
+ describe '#kind' do
+ it { expect(described_class.new('kind' => 'bug').kind).to eq('bug') }
+ end
+
+ describe '#milestone' do
+ it { expect(described_class.new({ 'milestone' => { 'name' => '1.0' } }).milestone).to eq('1.0') }
+ it { expect(described_class.new({}).milestone).to be_nil }
+ end
+
+ describe '#author' do
+ it { expect(described_class.new({ 'reporter' => { 'username' => 'Ben' } }).author).to eq('Ben') }
+ it { expect(described_class.new({}).author).to be_nil }
+ end
+
+ describe '#description' do
+ it { expect(described_class.new({ 'content' => { 'raw' => 'Text' } }).description).to eq('Text') }
+ it { expect(described_class.new({}).description).to be_nil }
+ end
+
+ describe '#state' do
+ it { expect(described_class.new({ 'state' => 'invalid' }).state).to eq('closed') }
+ it { expect(described_class.new({ 'state' => 'wontfix' }).state).to eq('closed') }
+ it { expect(described_class.new({ 'state' => 'resolved' }).state).to eq('closed') }
+ it { expect(described_class.new({ 'state' => 'duplicate' }).state).to eq('closed') }
+ it { expect(described_class.new({ 'state' => 'closed' }).state).to eq('closed') }
+ it { expect(described_class.new({ 'state' => 'opened' }).state).to eq('opened') }
+ end
+
+ describe '#title' do
+ it { expect(described_class.new('title' => 'Issue').title).to eq('Issue') }
+ end
+
+ describe '#created_at' do
+ it { expect(described_class.new('created_on' => Date.today).created_at).to eq(Date.today) }
+ end
+
+ describe '#updated_at' do
+ it { expect(described_class.new('edited_on' => Date.today).updated_at).to eq(Date.today) }
+ end
+end
diff --git a/spec/lib/bitbucket/representation/pull_request_comment_spec.rb b/spec/lib/bitbucket/representation/pull_request_comment_spec.rb
new file mode 100644
index 00000000000..673dcf22ce8
--- /dev/null
+++ b/spec/lib/bitbucket/representation/pull_request_comment_spec.rb
@@ -0,0 +1,34 @@
+require 'spec_helper'
+
+describe Bitbucket::Representation::PullRequestComment do
+ describe '#iid' do
+ it { expect(described_class.new('id' => 1).iid).to eq(1) }
+ end
+
+ describe '#file_path' do
+ it { expect(described_class.new('inline' => { 'path' => '/path' }).file_path).to eq('/path') }
+ end
+
+ describe '#old_pos' do
+ it { expect(described_class.new('inline' => { 'from' => 3 }).old_pos).to eq(3) }
+ end
+
+ describe '#new_pos' do
+ it { expect(described_class.new('inline' => { 'to' => 3 }).new_pos).to eq(3) }
+ end
+
+ describe '#parent_id' do
+ it { expect(described_class.new({ 'parent' => { 'id' => 2 } }).parent_id).to eq(2) }
+ it { expect(described_class.new({}).parent_id).to be_nil }
+ end
+
+ describe '#inline?' do
+ it { expect(described_class.new('inline' => {}).inline?).to be_truthy }
+ it { expect(described_class.new({}).inline?).to be_falsey }
+ end
+
+ describe '#has_parent?' do
+ it { expect(described_class.new('parent' => {}).has_parent?).to be_truthy }
+ it { expect(described_class.new({}).has_parent?).to be_falsey }
+ end
+end
diff --git a/spec/lib/bitbucket/representation/pull_request_spec.rb b/spec/lib/bitbucket/representation/pull_request_spec.rb
new file mode 100644
index 00000000000..30453528be4
--- /dev/null
+++ b/spec/lib/bitbucket/representation/pull_request_spec.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+describe Bitbucket::Representation::PullRequest do
+ describe '#iid' do
+ it { expect(described_class.new('id' => 1).iid).to eq(1) }
+ end
+
+ describe '#author' do
+ it { expect(described_class.new({ 'author' => { 'username' => 'Ben' } }).author).to eq('Ben') }
+ it { expect(described_class.new({}).author).to be_nil }
+ end
+
+ describe '#description' do
+ it { expect(described_class.new({ 'description' => 'Text' }).description).to eq('Text') }
+ it { expect(described_class.new({}).description).to be_nil }
+ end
+
+ describe '#state' do
+ it { expect(described_class.new({ 'state' => 'MERGED' }).state).to eq('merged') }
+ it { expect(described_class.new({ 'state' => 'DECLINED' }).state).to eq('closed') }
+ it { expect(described_class.new({}).state).to eq('opened') }
+ end
+
+ describe '#title' do
+ it { expect(described_class.new('title' => 'Issue').title).to eq('Issue') }
+ end
+
+ describe '#source_branch_name' do
+ it { expect(described_class.new({ source: { branch: { name: 'feature' } } }.with_indifferent_access).source_branch_name).to eq('feature') }
+ it { expect(described_class.new({ source: {} }.with_indifferent_access).source_branch_name).to be_nil }
+ end
+
+ describe '#source_branch_sha' do
+ it { expect(described_class.new({ source: { commit: { hash: 'abcd123' } } }.with_indifferent_access).source_branch_sha).to eq('abcd123') }
+ it { expect(described_class.new({ source: {} }.with_indifferent_access).source_branch_sha).to be_nil }
+ end
+
+ describe '#target_branch_name' do
+ it { expect(described_class.new({ destination: { branch: { name: 'master' } } }.with_indifferent_access).target_branch_name).to eq('master') }
+ it { expect(described_class.new({ destination: {} }.with_indifferent_access).target_branch_name).to be_nil }
+ end
+
+ describe '#target_branch_sha' do
+ it { expect(described_class.new({ destination: { commit: { hash: 'abcd123' } } }.with_indifferent_access).target_branch_sha).to eq('abcd123') }
+ it { expect(described_class.new({ destination: {} }.with_indifferent_access).target_branch_sha).to be_nil }
+ end
+end
diff --git a/spec/lib/bitbucket/representation/user_spec.rb b/spec/lib/bitbucket/representation/user_spec.rb
new file mode 100644
index 00000000000..f79ff4edb7b
--- /dev/null
+++ b/spec/lib/bitbucket/representation/user_spec.rb
@@ -0,0 +1,11 @@
+require 'spec_helper'
+
+describe Bitbucket::Representation::User do
+ describe '#username' do
+ it 'returns correct value' do
+ user = described_class.new('username' => 'Ben')
+
+ expect(user.username).to eq('Ben')
+ end
+ end
+end
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index 84f21631719..ff5dcc06ab3 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -1124,8 +1124,8 @@ EOT
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:extra config should be a hash")
end
- it "returns errors if there are unknown parameters that are hashes, but doesn't have a script" do
- config = YAML.dump({ extra: { services: "test" } })
+ it "returns errors if services configuration is not correct" do
+ config = YAML.dump({ extra: { script: 'rspec', services: "test" } })
expect do
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:extra:services config should be an array of strings")
diff --git a/spec/lib/constraints/group_url_constrainer_spec.rb b/spec/lib/constraints/group_url_constrainer_spec.rb
index f0b75a664f2..96dacdc5cd2 100644
--- a/spec/lib/constraints/group_url_constrainer_spec.rb
+++ b/spec/lib/constraints/group_url_constrainer_spec.rb
@@ -1,10 +1,30 @@
require 'spec_helper'
describe GroupUrlConstrainer, lib: true do
- let!(:username) { create(:group, path: 'gitlab-org') }
+ let!(:group) { create(:group, path: 'gitlab') }
- describe '#find_resource' do
- it { expect(!!subject.find_resource('gitlab-org')).to be_truthy }
- it { expect(!!subject.find_resource('gitlab-com')).to be_falsey }
+ describe '#matches?' do
+ context 'valid request' do
+ let(:request) { build_request(group.path) }
+
+ it { expect(subject.matches?(request)).to be_truthy }
+ end
+
+ context 'valid request for nested group' do
+ let!(:nested_group) { create(:group, path: 'nested', parent: group) }
+ let!(:request) { build_request('gitlab/nested') }
+
+ it { expect(subject.matches?(request)).to be_truthy }
+ end
+
+ context 'invalid request' do
+ let(:request) { build_request('foo') }
+
+ it { expect(subject.matches?(request)).to be_falsey }
+ end
+ end
+
+ def build_request(path)
+ double(:request, params: { id: path })
end
end
diff --git a/spec/lib/constraints/namespace_url_constrainer_spec.rb b/spec/lib/constraints/namespace_url_constrainer_spec.rb
deleted file mode 100644
index 7814711fe27..00000000000
--- a/spec/lib/constraints/namespace_url_constrainer_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require 'spec_helper'
-
-describe NamespaceUrlConstrainer, lib: true do
- let!(:group) { create(:group, path: 'gitlab') }
-
- describe '#matches?' do
- context 'existing namespace' do
- it { expect(subject.matches?(request '/gitlab')).to be_truthy }
- it { expect(subject.matches?(request '/gitlab.atom')).to be_truthy }
- it { expect(subject.matches?(request '/gitlab/')).to be_truthy }
- it { expect(subject.matches?(request '//gitlab/')).to be_truthy }
- end
-
- context 'non-existing namespace' do
- it { expect(subject.matches?(request '/gitlab-ce')).to be_falsey }
- it { expect(subject.matches?(request '/gitlab.ce')).to be_falsey }
- it { expect(subject.matches?(request '/g/gitlab')).to be_falsey }
- it { expect(subject.matches?(request '/.gitlab')).to be_falsey }
- end
-
- context 'relative url' do
- before do
- allow(Gitlab::Application.config).to receive(:relative_url_root) { '/gitlab' }
- end
-
- it { expect(subject.matches?(request '/gitlab/gitlab')).to be_truthy }
- it { expect(subject.matches?(request '/gitlab/gitlab-ce')).to be_falsey }
- it { expect(subject.matches?(request '/gitlab/')).to be_falsey }
- end
- end
-
- def request(path)
- OpenStruct.new(path: path)
- end
-end
diff --git a/spec/lib/constraints/project_url_constrainer_spec.rb b/spec/lib/constraints/project_url_constrainer_spec.rb
new file mode 100644
index 00000000000..94266f6653b
--- /dev/null
+++ b/spec/lib/constraints/project_url_constrainer_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+
+describe ProjectUrlConstrainer, lib: true do
+ let!(:project) { create(:project) }
+ let!(:namespace) { project.namespace }
+
+ describe '#matches?' do
+ context 'valid request' do
+ let(:request) { build_request(namespace.path, project.path) }
+
+ it { expect(subject.matches?(request)).to be_truthy }
+ end
+
+ context 'invalid request' do
+ context "non-existing project" do
+ let(:request) { build_request('foo', 'bar') }
+
+ it { expect(subject.matches?(request)).to be_falsey }
+ end
+
+ context "project id ending with .git" do
+ let(:request) { build_request(namespace.path, project.path + '.git') }
+
+ it { expect(subject.matches?(request)).to be_falsey }
+ end
+ end
+ end
+
+ def build_request(namespace, project)
+ double(:request, params: { namespace_id: namespace, id: project })
+ end
+end
diff --git a/spec/lib/constraints/user_url_constrainer_spec.rb b/spec/lib/constraints/user_url_constrainer_spec.rb
index 4b26692672f..207b6fe6c9e 100644
--- a/spec/lib/constraints/user_url_constrainer_spec.rb
+++ b/spec/lib/constraints/user_url_constrainer_spec.rb
@@ -1,10 +1,23 @@
require 'spec_helper'
describe UserUrlConstrainer, lib: true do
- let!(:username) { create(:user, username: 'dz') }
+ let!(:user) { create(:user, username: 'dz') }
- describe '#find_resource' do
- it { expect(!!subject.find_resource('dz')).to be_truthy }
- it { expect(!!subject.find_resource('john')).to be_falsey }
+ describe '#matches?' do
+ context 'valid request' do
+ let(:request) { build_request(user.username) }
+
+ it { expect(subject.matches?(request)).to be_truthy }
+ end
+
+ context 'invalid request' do
+ let(:request) { build_request('foo') }
+
+ it { expect(subject.matches?(request)).to be_falsey }
+ end
+ end
+
+ def build_request(username)
+ double(:request, params: { username: username })
end
end
diff --git a/spec/lib/event_filter_spec.rb b/spec/lib/event_filter_spec.rb
index a6d8e6927e0..ec2f66b1136 100644
--- a/spec/lib/event_filter_spec.rb
+++ b/spec/lib/event_filter_spec.rb
@@ -7,6 +7,10 @@ describe EventFilter, lib: true do
let!(:push_event) { create(:event, action: Event::PUSHED, project: public_project, target: public_project, author: source_user) }
let!(:merged_event) { create(:event, action: Event::MERGED, project: public_project, target: public_project, author: source_user) }
+ let!(:created_event) { create(:event, action: Event::CREATED, project: public_project, target: public_project, author: source_user) }
+ let!(:updated_event) { create(:event, action: Event::UPDATED, project: public_project, target: public_project, author: source_user) }
+ let!(:closed_event) { create(:event, action: Event::CLOSED, project: public_project, target: public_project, author: source_user) }
+ let!(:reopened_event) { create(:event, action: Event::REOPENED, project: public_project, target: public_project, author: source_user) }
let!(:comments_event) { create(:event, action: Event::COMMENTED, project: public_project, target: public_project, author: source_user) }
let!(:joined_event) { create(:event, action: Event::JOINED, project: public_project, target: public_project, author: source_user) }
let!(:left_event) { create(:event, action: Event::LEFT, project: public_project, target: public_project, author: source_user) }
@@ -21,6 +25,11 @@ describe EventFilter, lib: true do
expect(events).to contain_exactly(merged_event)
end
+ it 'applies issue filter' do
+ events = EventFilter.new(EventFilter.issue).apply_filter(Event.all)
+ expect(events).to contain_exactly(created_event, updated_event, closed_event, reopened_event)
+ end
+
it 'applies comments filter' do
events = EventFilter.new(EventFilter.comments).apply_filter(Event.all)
expect(events).to contain_exactly(comments_event)
@@ -33,17 +42,17 @@ describe EventFilter, lib: true do
it 'applies all filter' do
events = EventFilter.new(EventFilter.all).apply_filter(Event.all)
- expect(events).to contain_exactly(push_event, merged_event, comments_event, joined_event, left_event)
+ expect(events).to contain_exactly(push_event, merged_event, created_event, updated_event, closed_event, reopened_event, comments_event, joined_event, left_event)
end
it 'applies no filter' do
events = EventFilter.new(nil).apply_filter(Event.all)
- expect(events).to contain_exactly(push_event, merged_event, comments_event, joined_event, left_event)
+ expect(events).to contain_exactly(push_event, merged_event, created_event, updated_event, closed_event, reopened_event, comments_event, joined_event, left_event)
end
it 'applies unknown filter' do
events = EventFilter.new('').apply_filter(Event.all)
- expect(events).to contain_exactly(push_event, merged_event, comments_event, joined_event, left_event)
+ expect(events).to contain_exactly(push_event, merged_event, created_event, updated_event, closed_event, reopened_event, comments_event, joined_event, left_event)
end
end
end
diff --git a/spec/lib/gitlab/allowable_spec.rb b/spec/lib/gitlab/allowable_spec.rb
new file mode 100644
index 00000000000..87733d53e92
--- /dev/null
+++ b/spec/lib/gitlab/allowable_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe Gitlab::Allowable do
+ subject do
+ Class.new.include(described_class).new
+ end
+
+ describe '#can?' do
+ let(:user) { create(:user) }
+
+ context 'when user is allowed to do something' do
+ let(:project) { create(:empty_project, :public) }
+
+ it 'reports correct ability to perform action' do
+ expect(subject.can?(user, :read_project, project)).to be true
+ end
+ end
+
+ context 'when user is not allowed to do something' do
+ let(:project) { create(:empty_project, :private) }
+
+ it 'reports correct ability to perform action' do
+ expect(subject.can?(user, :read_project, project)).to be false
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index 4aba783dc33..f3843ca64ff 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -11,7 +11,7 @@ module Gitlab
it "converts the input using Asciidoctor and default options" do
expected_asciidoc_opts = {
safe: :secure,
- backend: :html5,
+ backend: :gitlab_html5,
attributes: described_class::DEFAULT_ADOC_ATTRS
}
@@ -27,7 +27,7 @@ module Gitlab
it "merges the options with default ones" do
expected_asciidoc_opts = {
safe: :safe,
- backend: :html5,
+ backend: :gitlab_html5,
attributes: described_class::DEFAULT_ADOC_ATTRS + ['foo']
}
diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb
index c9d64e99f88..f251c0dd25a 100644
--- a/spec/lib/gitlab/auth_spec.rb
+++ b/spec/lib/gitlab/auth_spec.rb
@@ -47,54 +47,76 @@ describe Gitlab::Auth, lib: true do
project.create_drone_ci_service(active: true)
project.drone_ci_service.update(token: 'token')
- ip = 'ip'
-
- expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'drone-ci-token')
- expect(gl_auth.find_for_git_client('drone-ci-token', 'token', project: project, ip: ip)).to eq(Gitlab::Auth::Result.new(nil, project, :ci, build_authentication_abilities))
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'drone-ci-token')
+ expect(gl_auth.find_for_git_client('drone-ci-token', 'token', project: project, ip: 'ip')).to eq(Gitlab::Auth::Result.new(nil, project, :ci, build_authentication_abilities))
end
it 'recognizes master passwords' do
user = create(:user, password: 'password')
- ip = 'ip'
- expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: user.username)
- expect(gl_auth.find_for_git_client(user.username, 'password', project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, full_authentication_abilities))
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: user.username)
+ expect(gl_auth.find_for_git_client(user.username, 'password', project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, full_authentication_abilities))
end
it 'recognizes user lfs tokens' do
user = create(:user)
- ip = 'ip'
token = Gitlab::LfsToken.new(user).token
- expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: user.username)
- expect(gl_auth.find_for_git_client(user.username, token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, nil, :lfs_token, full_authentication_abilities))
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: user.username)
+ expect(gl_auth.find_for_git_client(user.username, token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(user, nil, :lfs_token, full_authentication_abilities))
end
it 'recognizes deploy key lfs tokens' do
key = create(:deploy_key)
- ip = 'ip'
token = Gitlab::LfsToken.new(key).token
- expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: "lfs+deploy-key-#{key.id}")
- expect(gl_auth.find_for_git_client("lfs+deploy-key-#{key.id}", token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(key, nil, :lfs_deploy_token, read_authentication_abilities))
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: "lfs+deploy-key-#{key.id}")
+ expect(gl_auth.find_for_git_client("lfs+deploy-key-#{key.id}", token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(key, nil, :lfs_deploy_token, read_authentication_abilities))
end
- it 'recognizes OAuth tokens' do
- user = create(:user)
- application = Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user)
- token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id)
- ip = 'ip'
+ context "while using OAuth tokens as passwords" do
+ it 'succeeds for OAuth tokens with the `api` scope' do
+ user = create(:user)
+ application = Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user)
+ token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: "api")
+
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'oauth2')
+ expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(user, nil, :oauth, read_authentication_abilities))
+ end
- expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'oauth2')
- expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, nil, :oauth, read_authentication_abilities))
+ it 'fails for OAuth tokens with other scopes' do
+ user = create(:user)
+ application = Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user)
+ token = Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: "read_user")
+
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'oauth2')
+ expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(nil, nil))
+ end
+ end
+
+ context "while using personal access tokens as passwords" do
+ it 'succeeds for personal access tokens with the `api` scope' do
+ user = create(:user)
+ personal_access_token = create(:personal_access_token, user: user, scopes: ['api'])
+
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: user.email)
+ expect(gl_auth.find_for_git_client(user.email, personal_access_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(user, nil, :personal_token, full_authentication_abilities))
+ end
+
+ it 'fails for personal access tokens with other scopes' do
+ user = create(:user)
+ personal_access_token = create(:personal_access_token, user: user, scopes: ['read_user'])
+
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: user.email)
+ expect(gl_auth.find_for_git_client(user.email, personal_access_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(nil, nil))
+ end
end
it 'returns double nil for invalid credentials' do
login = 'foo'
- ip = 'ip'
- expect(gl_auth).to receive(:rate_limit!).with(ip, success: false, login: login)
- expect(gl_auth.find_for_git_client(login, 'bar', project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new)
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
+ expect(gl_auth.find_for_git_client(login, 'bar', project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new)
end
end
diff --git a/spec/lib/gitlab/backup/manager_spec.rb b/spec/lib/gitlab/backup/manager_spec.rb
new file mode 100644
index 00000000000..1b749d1bd39
--- /dev/null
+++ b/spec/lib/gitlab/backup/manager_spec.rb
@@ -0,0 +1,127 @@
+require 'spec_helper'
+
+describe Backup::Manager, lib: true do
+ describe '#remove_old' do
+ let(:progress) { StringIO.new }
+
+ let(:files) do
+ [
+ '1451606400_2016_01_01_gitlab_backup.tar',
+ '1451520000_2015_12_31_gitlab_backup.tar',
+ '1450742400_2015_12_22_gitlab_backup.tar',
+ '1449878400_gitlab_backup.tar',
+ '1449014400_gitlab_backup.tar',
+ 'manual_gitlab_backup.tar'
+ ]
+ end
+
+ before do
+ allow(Dir).to receive(:chdir).and_yield
+ allow(Dir).to receive(:glob).and_return(files)
+ allow(FileUtils).to receive(:rm)
+ allow(Time).to receive(:now).and_return(Time.utc(2016))
+
+ allow(progress).to receive(:puts)
+ allow(progress).to receive(:print)
+
+ allow_any_instance_of(String).to receive(:color) do |string, _color|
+ string
+ end
+
+ @old_progress = $progress # rubocop:disable Style/GlobalVars
+ $progress = progress # rubocop:disable Style/GlobalVars
+ end
+
+ after do
+ $progress = @old_progress # rubocop:disable Style/GlobalVars
+ end
+
+ context 'when keep_time is zero' do
+ before do
+ allow(Gitlab.config.backup).to receive(:keep_time).and_return(0)
+
+ subject.remove_old
+ end
+
+ it 'removes no files' do
+ expect(FileUtils).not_to have_received(:rm)
+ end
+
+ it 'prints a skipped message' do
+ expect(progress).to have_received(:puts).with('skipping')
+ end
+ end
+
+ context 'when there are no files older than keep_time' do
+ before do
+ allow(Gitlab.config.backup).to receive(:keep_time).and_return(2592000)
+
+ subject.remove_old
+ end
+
+ it 'removes no files' do
+ expect(FileUtils).not_to have_received(:rm)
+ end
+
+ it 'prints a done message' do
+ expect(progress).to have_received(:puts).with('done. (0 removed)')
+ end
+ end
+
+ context 'when keep_time is set to remove files' do
+ before do
+ allow(Gitlab.config.backup).to receive(:keep_time).and_return(1)
+
+ subject.remove_old
+ end
+
+ it 'removes matching files with a human-readable timestamp' do
+ expect(FileUtils).to have_received(:rm).with(files[1])
+ expect(FileUtils).to have_received(:rm).with(files[2])
+ end
+
+ it 'removes matching files without a human-readable timestamp' do
+ expect(FileUtils).to have_received(:rm).with(files[3])
+ expect(FileUtils).to have_received(:rm).with(files[4])
+ end
+
+ it 'does not remove files that are not old enough' do
+ expect(FileUtils).not_to have_received(:rm).with(files[0])
+ end
+
+ it 'does not remove non-matching files' do
+ expect(FileUtils).not_to have_received(:rm).with(files[5])
+ end
+
+ it 'prints a done message' do
+ expect(progress).to have_received(:puts).with('done. (4 removed)')
+ end
+ end
+
+ context 'when removing a file fails' do
+ let(:file) { files[1] }
+ let(:message) { "Permission denied @ unlink_internal - #{file}" }
+
+ before do
+ allow(Gitlab.config.backup).to receive(:keep_time).and_return(1)
+ allow(FileUtils).to receive(:rm).with(file).and_raise(Errno::EACCES, message)
+
+ subject.remove_old
+ end
+
+ it 'removes the remaining expected files' do
+ expect(FileUtils).to have_received(:rm).with(files[2])
+ expect(FileUtils).to have_received(:rm).with(files[3])
+ expect(FileUtils).to have_received(:rm).with(files[4])
+ end
+
+ it 'sets the correct removed count' do
+ expect(progress).to have_received(:puts).with('done. (3 removed)')
+ end
+
+ it 'prints the error from file that could not be removed' do
+ expect(progress).to have_received(:puts).with(a_string_matching(message))
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/badge/build/status_spec.rb b/spec/lib/gitlab/badge/build/status_spec.rb
index 38eebb2a176..70f03021d36 100644
--- a/spec/lib/gitlab/badge/build/status_spec.rb
+++ b/spec/lib/gitlab/badge/build/status_spec.rb
@@ -69,8 +69,8 @@ describe Gitlab::Badge::Build::Status do
new_build.success!
end
- it 'reports the compound status' do
- expect(badge.status).to eq 'failed'
+ it 'does not take outdated pipeline into account' do
+ expect(badge.status).to eq 'success'
end
end
end
diff --git a/spec/lib/gitlab/bitbucket_import/client_spec.rb b/spec/lib/gitlab/bitbucket_import/client_spec.rb
deleted file mode 100644
index 7543c29bcc4..00000000000
--- a/spec/lib/gitlab/bitbucket_import/client_spec.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::BitbucketImport::Client, lib: true do
- include ImportSpecHelper
-
- let(:token) { '123456' }
- let(:secret) { 'secret' }
- let(:client) { Gitlab::BitbucketImport::Client.new(token, secret) }
-
- before do
- stub_omniauth_provider('bitbucket')
- end
-
- it 'all OAuth client options are symbols' do
- client.consumer.options.keys.each do |key|
- expect(key).to be_kind_of(Symbol)
- end
- end
-
- context 'issues' do
- let(:per_page) { 50 }
- let(:count) { 95 }
- let(:sample_issues) do
- issues = []
-
- count.times do |i|
- issues << { local_id: i }
- end
-
- issues
- end
- let(:first_sample_data) { { count: count, issues: sample_issues[0..per_page - 1] } }
- let(:second_sample_data) { { count: count, issues: sample_issues[per_page..count] } }
- let(:project_id) { 'namespace/repo' }
-
- it 'retrieves issues over a number of pages' do
- stub_request(:get,
- "https://bitbucket.org/api/1.0/repositories/#{project_id}/issues?limit=50&sort=utc_created_on&start=0").
- to_return(status: 200,
- body: first_sample_data.to_json,
- headers: {})
-
- stub_request(:get,
- "https://bitbucket.org/api/1.0/repositories/#{project_id}/issues?limit=50&sort=utc_created_on&start=50").
- to_return(status: 200,
- body: second_sample_data.to_json,
- headers: {})
-
- issues = client.issues(project_id)
- expect(issues.count).to eq(95)
- end
- end
-
- context 'project import' do
- it 'calls .from_project with no errors' do
- project = create(:empty_project)
- project.import_url = "ssh://git@bitbucket.org/test/test.git"
- project.create_or_update_import_data(credentials:
- { user: "git",
- password: nil,
- bb_session: { bitbucket_access_token: "test",
- bitbucket_access_token_secret: "test" } })
-
- expect { described_class.from_project(project) }.not_to raise_error
- end
- end
-end
diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
index aa00f32becb..53f3c73ade4 100644
--- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
@@ -18,15 +18,21 @@ describe Gitlab::BitbucketImport::Importer, lib: true do
"closed" # undocumented status
]
end
+
let(:sample_issues_statuses) do
issues = []
statuses.map.with_index do |status, index|
issues << {
- local_id: index,
- status: status,
+ id: index,
+ state: status,
title: "Issue #{index}",
- content: "Some content to issue #{index}"
+ kind: 'bug',
+ content: {
+ raw: "Some content to issue #{index}",
+ markup: "markdown",
+ html: "Some content to issue #{index}"
+ }
}
end
@@ -34,14 +40,16 @@ describe Gitlab::BitbucketImport::Importer, lib: true do
end
let(:project_identifier) { 'namespace/repo' }
+
let(:data) do
{
'bb_session' => {
- 'bitbucket_access_token' => "123456",
- 'bitbucket_access_token_secret' => "secret"
+ 'bitbucket_token' => "123456",
+ 'bitbucket_refresh_token' => "secret"
}
}
end
+
let(:project) do
create(
:project,
@@ -49,11 +57,13 @@ describe Gitlab::BitbucketImport::Importer, lib: true do
import_data: ProjectImportData.new(credentials: data)
)
end
+
let(:importer) { Gitlab::BitbucketImport::Importer.new(project) }
+
let(:issues_statuses_sample_data) do
{
count: sample_issues_statuses.count,
- issues: sample_issues_statuses
+ values: sample_issues_statuses
}
end
@@ -61,26 +71,46 @@ describe Gitlab::BitbucketImport::Importer, lib: true do
before do
stub_request(
:get,
- "https://bitbucket.org/api/1.0/repositories/#{project_identifier}"
- ).to_return(status: 200, body: { has_issues: true }.to_json)
+ "https://api.bitbucket.org/2.0/repositories/#{project_identifier}"
+ ).to_return(status: 200,
+ headers: { "Content-Type" => "application/json" },
+ body: { has_issues: true, full_name: project_identifier }.to_json)
stub_request(
:get,
- "https://bitbucket.org/api/1.0/repositories/#{project_identifier}/issues?limit=50&sort=utc_created_on&start=0"
- ).to_return(status: 200, body: issues_statuses_sample_data.to_json)
+ "https://api.bitbucket.org/2.0/repositories/#{project_identifier}/issues?pagelen=50&sort=created_on"
+ ).to_return(status: 200,
+ headers: { "Content-Type" => "application/json" },
+ body: issues_statuses_sample_data.to_json)
+
+ stub_request(:get, "https://api.bitbucket.org/2.0/repositories/namespace/repo?pagelen=50&sort=created_on").
+ with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer', 'User-Agent' => 'Faraday v0.9.2' }).
+ to_return(status: 200,
+ body: "",
+ headers: {})
sample_issues_statuses.each_with_index do |issue, index|
stub_request(
:get,
- "https://bitbucket.org/api/1.0/repositories/#{project_identifier}/issues/#{issue[:local_id]}/comments"
+ "https://api.bitbucket.org/2.0/repositories/#{project_identifier}/issues/#{issue[:id]}/comments?pagelen=50&sort=created_on"
).to_return(
status: 200,
- body: [{ author_info: { username: "username" }, utc_created_on: index }].to_json
+ headers: { "Content-Type" => "application/json" },
+ body: { author_info: { username: "username" }, utc_created_on: index }.to_json
)
end
+
+ stub_request(
+ :get,
+ "https://api.bitbucket.org/2.0/repositories/#{project_identifier}/pullrequests?pagelen=50&sort=created_on&state=ALL"
+ ).to_return(status: 200,
+ headers: { "Content-Type" => "application/json" },
+ body: {}.to_json)
end
it 'map statuses to open or closed' do
+ # HACK: Bitbucket::Representation.const_get('Issue') seems to return ::Issue without this
+ Bitbucket::Representation::Issue.new({})
importer.execute
expect(project.issues.where(state: "closed").size).to eq(5)
diff --git a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb
index e1c60e07b4d..b6d052a4612 100644
--- a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb
@@ -2,14 +2,18 @@ require 'spec_helper'
describe Gitlab::BitbucketImport::ProjectCreator, lib: true do
let(:user) { create(:user) }
+
let(:repo) do
- {
- name: 'Vim',
- slug: 'vim',
- is_private: true,
- owner: "asd"
- }.with_indifferent_access
+ double(name: 'Vim',
+ slug: 'vim',
+ description: 'Test repo',
+ is_private: true,
+ owner: "asd",
+ full_name: 'Vim repo',
+ visibility_level: Gitlab::VisibilityLevel::PRIVATE,
+ clone_url: 'ssh://git@bitbucket.org/asd/vim.git')
end
+
let(:namespace){ create(:group, owner: user) }
let(:token) { "asdasd12345" }
let(:secret) { "sekrettt" }
@@ -22,7 +26,7 @@ describe Gitlab::BitbucketImport::ProjectCreator, lib: true do
it 'creates project' do
allow_any_instance_of(Project).to receive(:add_import_job)
- project_creator = Gitlab::BitbucketImport::ProjectCreator.new(repo, namespace, user, access_params)
+ project_creator = Gitlab::BitbucketImport::ProjectCreator.new(repo, 'vim', namespace, user, access_params)
project = project_creator.execute
expect(project.import_url).to eq("ssh://git@bitbucket.org/asd/vim.git")
diff --git a/spec/lib/gitlab/chat_commands/command_spec.rb b/spec/lib/gitlab/chat_commands/command_spec.rb
new file mode 100644
index 00000000000..bfc6818ac08
--- /dev/null
+++ b/spec/lib/gitlab/chat_commands/command_spec.rb
@@ -0,0 +1,94 @@
+require 'spec_helper'
+
+describe Gitlab::ChatCommands::Command, service: true do
+ let(:project) { create(:empty_project) }
+ let(:user) { create(:user) }
+
+ describe '#execute' do
+ subject { described_class.new(project, user, params).execute }
+
+ context 'when no command is available' do
+ let(:params) { { text: 'issue show 1' } }
+ let(:project) { create(:project, has_external_issue_tracker: true) }
+
+ it 'displays 404 messages' do
+ expect(subject[:response_type]).to be(:ephemeral)
+ expect(subject[:text]).to start_with('404 not found')
+ end
+ end
+
+ context 'when an unknown command is triggered' do
+ let(:params) { { command: '/gitlab', text: "unknown command 123" } }
+
+ it 'displays the help message' do
+ expect(subject[:response_type]).to be(:ephemeral)
+ expect(subject[:text]).to start_with('Available commands')
+ expect(subject[:text]).to match('/gitlab issue show')
+ end
+ end
+
+ context 'the user can not create an issue' do
+ let(:params) { { text: "issue create my new issue" } }
+
+ it 'rejects the actions' do
+ expect(subject[:response_type]).to be(:ephemeral)
+ expect(subject[:text]).to start_with('Whoops! That action is not allowed')
+ end
+ end
+
+ context 'issue is successfully created' do
+ let(:params) { { text: "issue create my new issue" } }
+
+ before do
+ project.team << [user, :master]
+ end
+
+ it 'presents the issue' do
+ expect(subject[:text]).to match("my new issue")
+ end
+
+ it 'shows a link to the new issue' do
+ expect(subject[:text]).to match(/\/issues\/\d+/)
+ end
+ end
+
+ context 'when trying to do deployment' do
+ let(:params) { { text: 'deploy staging to production' } }
+ let!(:build) { create(:ci_build, project: project) }
+ let!(:staging) { create(:environment, name: 'staging', project: project) }
+ let!(:deployment) { create(:deployment, environment: staging, deployable: build) }
+ let!(:manual) do
+ create(:ci_build, :manual, project: project, pipeline: build.pipeline, name: 'first', environment: 'production')
+ end
+
+ context 'and user can not create deployment' do
+ it 'returns action' do
+ expect(subject[:response_type]).to be(:ephemeral)
+ expect(subject[:text]).to start_with('Whoops! That action is not allowed')
+ end
+ end
+
+ context 'and user does have deployment permission' do
+ before do
+ project.team << [user, :developer]
+ end
+
+ it 'returns action' do
+ expect(subject[:text]).to include('Deployment from staging to production started')
+ expect(subject[:response_type]).to be(:in_channel)
+ end
+
+ context 'when duplicate action exists' do
+ let!(:manual2) do
+ create(:ci_build, :manual, project: project, pipeline: build.pipeline, name: 'second', environment: 'production')
+ end
+
+ it 'returns error' do
+ expect(subject[:response_type]).to be(:ephemeral)
+ expect(subject[:text]).to include('Too many actions defined')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/chat_commands/deploy_spec.rb b/spec/lib/gitlab/chat_commands/deploy_spec.rb
new file mode 100644
index 00000000000..bd8099c92da
--- /dev/null
+++ b/spec/lib/gitlab/chat_commands/deploy_spec.rb
@@ -0,0 +1,79 @@
+require 'spec_helper'
+
+describe Gitlab::ChatCommands::Deploy, service: true do
+ describe '#execute' do
+ let(:project) { create(:empty_project) }
+ let(:user) { create(:user) }
+ let(:regex_match) { described_class.match('deploy staging to production') }
+
+ before do
+ project.team << [user, :master]
+ end
+
+ subject do
+ described_class.new(project, user).execute(regex_match)
+ end
+
+ context 'if no environment is defined' do
+ it 'returns nil' do
+ expect(subject).to be_nil
+ end
+ end
+
+ context 'with environment' do
+ let!(:staging) { create(:environment, name: 'staging', project: project) }
+ let!(:build) { create(:ci_build, project: project) }
+ let!(:deployment) { create(:deployment, environment: staging, deployable: build) }
+
+ context 'without actions' do
+ it 'returns nil' do
+ expect(subject).to be_nil
+ end
+ end
+
+ context 'with action' do
+ let!(:manual1) do
+ create(:ci_build, :manual, project: project, pipeline: build.pipeline, name: 'first', environment: 'production')
+ end
+
+ it 'returns success result' do
+ expect(subject.type).to eq(:success)
+ expect(subject.message).to include('Deployment from staging to production started')
+ end
+
+ context 'when duplicate action exists' do
+ let!(:manual2) do
+ create(:ci_build, :manual, project: project, pipeline: build.pipeline, name: 'second', environment: 'production')
+ end
+
+ it 'returns error' do
+ expect(subject.type).to eq(:error)
+ expect(subject.message).to include('Too many actions defined')
+ end
+ end
+
+ context 'when teardown action exists' do
+ let!(:teardown) do
+ create(:ci_build, :manual, :teardown_environment,
+ project: project, pipeline: build.pipeline,
+ name: 'teardown', environment: 'production')
+ end
+
+ it 'returns success result' do
+ expect(subject.type).to eq(:success)
+ expect(subject.message).to include('Deployment from staging to production started')
+ end
+ end
+ end
+ end
+ end
+
+ describe 'self.match' do
+ it 'matches the environment' do
+ match = described_class.match('deploy staging to production')
+
+ expect(match[:from]).to eq('staging')
+ expect(match[:to]).to eq('production')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/chat_commands/issue_create_spec.rb b/spec/lib/gitlab/chat_commands/issue_create_spec.rb
new file mode 100644
index 00000000000..6c71e79ff6d
--- /dev/null
+++ b/spec/lib/gitlab/chat_commands/issue_create_spec.rb
@@ -0,0 +1,68 @@
+require 'spec_helper'
+
+describe Gitlab::ChatCommands::IssueCreate, service: true do
+ describe '#execute' do
+ let(:project) { create(:empty_project) }
+ let(:user) { create(:user) }
+ let(:regex_match) { described_class.match("issue create bird is the word") }
+
+ before do
+ project.team << [user, :master]
+ end
+
+ subject do
+ described_class.new(project, user).execute(regex_match)
+ end
+
+ context 'without description' do
+ it 'creates the issue' do
+ expect { subject }.to change { project.issues.count }.by(1)
+
+ expect(subject.title).to eq('bird is the word')
+ end
+ end
+
+ context 'with description' do
+ let(:description) { "Surfin bird" }
+ let(:regex_match) { described_class.match("issue create bird is the word\n#{description}") }
+
+ it 'creates the issue with description' do
+ subject
+
+ expect(Issue.last.description).to eq(description)
+ end
+ end
+
+ context "with more newlines between the title and the description" do
+ let(:description) { "Surfin bird" }
+ let(:regex_match) { described_class.match("issue create bird is the word\n\n#{description}\n") }
+
+ it 'creates the issue' do
+ expect { subject }.to change { project.issues.count }.by(1)
+ end
+ end
+ end
+
+ describe '.match' do
+ it 'matches the title without description' do
+ match = described_class.match("issue create my title")
+
+ expect(match[:title]).to eq('my title')
+ expect(match[:description]).to eq("")
+ end
+
+ it 'matches the title with description' do
+ match = described_class.match("issue create my title\n\ndescription")
+
+ expect(match[:title]).to eq('my title')
+ expect(match[:description]).to eq('description')
+ end
+
+ it 'matches the alias new' do
+ match = described_class.match("issue new my title")
+
+ expect(match).not_to be_nil
+ expect(match[:title]).to eq('my title')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/chat_commands/issue_search_spec.rb b/spec/lib/gitlab/chat_commands/issue_search_spec.rb
new file mode 100644
index 00000000000..24c06a967fa
--- /dev/null
+++ b/spec/lib/gitlab/chat_commands/issue_search_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe Gitlab::ChatCommands::IssueSearch, service: true do
+ describe '#execute' do
+ let!(:issue) { create(:issue, title: 'find me') }
+ let!(:confidential) { create(:issue, :confidential, project: project, title: 'mepmep find') }
+ let(:project) { issue.project }
+ let(:user) { issue.author }
+ let(:regex_match) { described_class.match("issue search find") }
+
+ subject do
+ described_class.new(project, user).execute(regex_match)
+ end
+
+ context 'when the user has no access' do
+ it 'only returns the open issues' do
+ expect(subject).not_to include(confidential)
+ end
+ end
+
+ context 'the user has access' do
+ before do
+ project.team << [user, :master]
+ end
+
+ it 'returns all results' do
+ expect(subject).to include(confidential, issue)
+ end
+ end
+
+ context 'without hits on the query' do
+ it 'returns an empty collection' do
+ expect(subject).to be_empty
+ end
+ end
+ end
+
+ describe 'self.match' do
+ let(:query) { "my search keywords" }
+ it 'matches the query' do
+ match = described_class.match("issue search #{query}")
+
+ expect(match[:query]).to eq(query)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/chat_commands/issue_show_spec.rb b/spec/lib/gitlab/chat_commands/issue_show_spec.rb
new file mode 100644
index 00000000000..2eab73e49e5
--- /dev/null
+++ b/spec/lib/gitlab/chat_commands/issue_show_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+describe Gitlab::ChatCommands::IssueShow, service: true do
+ describe '#execute' do
+ let(:issue) { create(:issue) }
+ let(:project) { issue.project }
+ let(:user) { issue.author }
+ let(:regex_match) { described_class.match("issue show #{issue.iid}") }
+
+ before do
+ project.team << [user, :master]
+ end
+
+ subject do
+ described_class.new(project, user).execute(regex_match)
+ end
+
+ context 'the issue exists' do
+ it 'returns the issue' do
+ expect(subject.iid).to be issue.iid
+ end
+
+ context 'when its reference is given' do
+ let(:regex_match) { described_class.match("issue show #{issue.to_reference}") }
+
+ it 'shows the issue' do
+ expect(subject.iid).to be issue.iid
+ end
+ end
+ end
+
+ context 'the issue does not exist' do
+ let(:regex_match) { described_class.match("issue show 2343242") }
+
+ it "returns nil" do
+ expect(subject).to be_nil
+ end
+ end
+ end
+
+ describe 'self.match' do
+ it 'matches the iid' do
+ match = described_class.match("issue show 123")
+
+ expect(match[:iid]).to eq("123")
+ end
+ end
+end
diff --git a/spec/lib/gitlab/chat_name_token_spec.rb b/spec/lib/gitlab/chat_name_token_spec.rb
new file mode 100644
index 00000000000..8c1e6efa9db
--- /dev/null
+++ b/spec/lib/gitlab/chat_name_token_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+
+describe Gitlab::ChatNameToken, lib: true do
+ context 'when using unknown token' do
+ let(:token) { }
+
+ subject { described_class.new(token).get }
+
+ it 'returns empty data' do
+ is_expected.to be_nil
+ end
+ end
+
+ context 'when storing data' do
+ let(:data) { { key: 'value' } }
+
+ subject { described_class.new(@token) }
+
+ before do
+ @token = described_class.new.store!(data)
+ end
+
+ it 'returns stored data' do
+ expect(subject.get).to eq(data)
+ end
+
+ context 'and after deleting them' do
+ before do
+ subject.delete
+ end
+
+ it 'data are removed' do
+ expect(subject.get).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/credentials/factory_spec.rb b/spec/lib/gitlab/ci/build/credentials/factory_spec.rb
new file mode 100644
index 00000000000..10b4b7a8826
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/credentials/factory_spec.rb
@@ -0,0 +1,38 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Credentials::Factory do
+ let(:build) { create(:ci_build, name: 'spinach', stage: 'test', stage_idx: 0) }
+
+ subject { Gitlab::Ci::Build::Credentials::Factory.new(build).create! }
+
+ class TestProvider
+ def initialize(build); end
+ end
+
+ before do
+ allow_any_instance_of(Gitlab::Ci::Build::Credentials::Factory).to receive(:providers).and_return([TestProvider])
+ end
+
+ context 'when provider is valid' do
+ before do
+ allow_any_instance_of(TestProvider).to receive(:valid?).and_return(true)
+ end
+
+ it 'generates an array of credentials objects' do
+ is_expected.to be_kind_of(Array)
+ is_expected.not_to be_empty
+ expect(subject.first).to be_kind_of(TestProvider)
+ end
+ end
+
+ context 'when provider is not valid' do
+ before do
+ allow_any_instance_of(TestProvider).to receive(:valid?).and_return(false)
+ end
+
+ it 'generates an array without specific credential object' do
+ is_expected.to be_kind_of(Array)
+ is_expected.to be_empty
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/credentials/registry_spec.rb b/spec/lib/gitlab/ci/build/credentials/registry_spec.rb
new file mode 100644
index 00000000000..84e44dd53e2
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/credentials/registry_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Credentials::Registry do
+ let(:build) { create(:ci_build, name: 'spinach', stage: 'test', stage_idx: 0) }
+ let(:registry_url) { 'registry.example.com:5005' }
+
+ subject { Gitlab::Ci::Build::Credentials::Registry.new(build) }
+
+ before do
+ stub_container_registry_config(host_port: registry_url)
+ end
+
+ it 'contains valid DockerRegistry credentials' do
+ expect(subject).to be_kind_of(Gitlab::Ci::Build::Credentials::Registry)
+
+ expect(subject.username).to eq 'gitlab-ci-token'
+ expect(subject.password).to eq build.token
+ expect(subject.url).to eq registry_url
+ expect(subject.type).to eq 'registry'
+ end
+
+ describe '.valid?' do
+ subject { Gitlab::Ci::Build::Credentials::Registry.new(build).valid? }
+
+ context 'when registry is enabled' do
+ before do
+ stub_container_registry_config(enabled: true)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when registry is disabled' do
+ before do
+ stub_container_registry_config(enabled: false)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/node/artifacts_spec.rb b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
index c09a0a9c793..5c31423fdee 100644
--- a/spec/lib/gitlab/ci/config/node/artifacts_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/artifacts_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Artifacts do
+describe Gitlab::Ci::Config::Entry::Artifacts do
let(:entry) { described_class.new(config) }
describe 'validation' do
diff --git a/spec/lib/gitlab/ci/config/node/attributable_spec.rb b/spec/lib/gitlab/ci/config/entry/attributable_spec.rb
index 24d9daafd88..fde03c51e2c 100644
--- a/spec/lib/gitlab/ci/config/node/attributable_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/attributable_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Attributable do
+describe Gitlab::Ci::Config::Entry::Attributable do
let(:node) { Class.new }
let(:instance) { node.new }
diff --git a/spec/lib/gitlab/ci/config/node/boolean_spec.rb b/spec/lib/gitlab/ci/config/entry/boolean_spec.rb
index deafa8bf8a7..5f067cad93c 100644
--- a/spec/lib/gitlab/ci/config/node/boolean_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/boolean_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Boolean do
+describe Gitlab::Ci::Config::Entry::Boolean do
let(:entry) { described_class.new(config) }
describe 'validations' do
diff --git a/spec/lib/gitlab/ci/config/node/cache_spec.rb b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
index e251210949c..70a327c5183 100644
--- a/spec/lib/gitlab/ci/config/node/cache_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Cache do
+describe Gitlab::Ci::Config::Entry::Cache do
let(:entry) { described_class.new(config) }
describe 'validations' do
diff --git a/spec/lib/gitlab/ci/config/node/commands_spec.rb b/spec/lib/gitlab/ci/config/entry/commands_spec.rb
index e373c40706f..b8b0825a1c7 100644
--- a/spec/lib/gitlab/ci/config/node/commands_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/commands_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Commands do
+describe Gitlab::Ci::Config::Entry::Commands do
let(:entry) { described_class.new(config) }
context 'when entry config value is an array' do
diff --git a/spec/lib/gitlab/ci/config/entry/configurable_spec.rb b/spec/lib/gitlab/ci/config/entry/configurable_spec.rb
new file mode 100644
index 00000000000..ae7e628b5b5
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/configurable_spec.rb
@@ -0,0 +1,67 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Entry::Configurable do
+ let(:entry) { Class.new }
+
+ before do
+ entry.include(described_class)
+ end
+
+ describe 'validations' do
+ let(:validator) { entry.validator.new(instance) }
+
+ before do
+ entry.class_eval do
+ attr_reader :config
+
+ def initialize(config)
+ @config = config
+ end
+ end
+
+ validator.validate
+ end
+
+ context 'when entry validator is invalid' do
+ let(:instance) { entry.new('ls') }
+
+ it 'returns invalid validator' do
+ expect(validator).to be_invalid
+ end
+ end
+
+ context 'when entry instance is valid' do
+ let(:instance) { entry.new(key: 'value') }
+
+ it 'returns valid validator' do
+ expect(validator).to be_valid
+ end
+ end
+ end
+
+ describe 'configured entries' do
+ before do
+ entry.class_eval do
+ entry :object, Object, description: 'test object'
+ end
+ end
+
+ describe '.nodes' do
+ it 'has valid nodes' do
+ expect(entry.nodes).to include :object
+ end
+
+ it 'creates a node factory' do
+ expect(entry.nodes[:object])
+ .to be_an_instance_of Gitlab::Ci::Config::Entry::Factory
+ end
+
+ it 'returns a duplicated factory object' do
+ first_factory = entry.nodes[:object]
+ second_factory = entry.nodes[:object]
+
+ expect(first_factory).not_to be_equal(second_factory)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/node/environment_spec.rb b/spec/lib/gitlab/ci/config/entry/environment_spec.rb
index df925ff1afd..d97806295fb 100644
--- a/spec/lib/gitlab/ci/config/node/environment_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/environment_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Environment do
+describe Gitlab::Ci::Config::Entry::Environment do
let(:entry) { described_class.new(config) }
before { entry.compose! }
diff --git a/spec/lib/gitlab/ci/config/node/factory_spec.rb b/spec/lib/gitlab/ci/config/entry/factory_spec.rb
index a699089c563..00dad5d9591 100644
--- a/spec/lib/gitlab/ci/config/node/factory_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/factory_spec.rb
@@ -1,9 +1,9 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Factory do
+describe Gitlab::Ci::Config::Entry::Factory do
describe '#create!' do
- let(:factory) { described_class.new(node) }
- let(:node) { Gitlab::Ci::Config::Node::Script }
+ let(:factory) { described_class.new(entry) }
+ let(:entry) { Gitlab::Ci::Config::Entry::Script }
context 'when setting a concrete value' do
it 'creates entry with valid value' do
@@ -54,7 +54,7 @@ describe Gitlab::Ci::Config::Node::Factory do
context 'when not setting a value' do
it 'raises error' do
expect { factory.create! }.to raise_error(
- Gitlab::Ci::Config::Node::Factory::InvalidFactory
+ Gitlab::Ci::Config::Entry::Factory::InvalidFactory
)
end
end
@@ -66,12 +66,12 @@ describe Gitlab::Ci::Config::Node::Factory do
.create!
expect(entry)
- .to be_an_instance_of Gitlab::Ci::Config::Node::Unspecified
+ .to be_an_instance_of Gitlab::Ci::Config::Entry::Unspecified
end
end
context 'when passing metadata' do
- let(:node) { spy('node') }
+ let(:entry) { spy('entry') }
it 'passes metadata as a parameter' do
factory
@@ -79,7 +79,7 @@ describe Gitlab::Ci::Config::Node::Factory do
.metadata(some: 'hash')
.create!
- expect(node).to have_received(:new)
+ expect(entry).to have_received(:new)
.with('some value', { some: 'hash' })
end
end
diff --git a/spec/lib/gitlab/ci/config/node/global_spec.rb b/spec/lib/gitlab/ci/config/entry/global_spec.rb
index 12232ff7e2f..e64c8d46bd8 100644
--- a/spec/lib/gitlab/ci/config/node/global_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/global_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Global do
+describe Gitlab::Ci::Config::Entry::Global do
let(:global) { described_class.new(hash) }
describe '.nodes' do
@@ -13,7 +13,7 @@ describe Gitlab::Ci::Config::Node::Global do
end
end
- context 'when hash is valid' do
+ context 'when configuration is valid' do
context 'when some entries defined' do
let(:hash) do
{ before_script: ['ls', 'pwd'],
@@ -40,9 +40,9 @@ describe Gitlab::Ci::Config::Node::Global do
it 'creates node object using valid class' do
expect(global.descendants.first)
- .to be_an_instance_of Gitlab::Ci::Config::Node::Script
+ .to be_an_instance_of Gitlab::Ci::Config::Entry::Script
expect(global.descendants.second)
- .to be_an_instance_of Gitlab::Ci::Config::Node::Image
+ .to be_an_instance_of Gitlab::Ci::Config::Entry::Image
end
it 'sets correct description for nodes' do
@@ -60,9 +60,9 @@ describe Gitlab::Ci::Config::Node::Global do
end
context 'when not composed' do
- describe '#before_script' do
+ describe '#before_script_value' do
it 'returns nil' do
- expect(global.before_script).to be nil
+ expect(global.before_script_value).to be nil
end
end
@@ -82,40 +82,40 @@ describe Gitlab::Ci::Config::Node::Global do
end
end
- describe '#before_script' do
+ describe '#before_script_value' do
it 'returns correct script' do
- expect(global.before_script).to eq ['ls', 'pwd']
+ expect(global.before_script_value).to eq ['ls', 'pwd']
end
end
- describe '#image' do
+ describe '#image_value' do
it 'returns valid image' do
- expect(global.image).to eq 'ruby:2.2'
+ expect(global.image_value).to eq 'ruby:2.2'
end
end
- describe '#services' do
+ describe '#services_value' do
it 'returns array of services' do
- expect(global.services).to eq ['postgres:9.1', 'mysql:5.5']
+ expect(global.services_value).to eq ['postgres:9.1', 'mysql:5.5']
end
end
- describe '#after_script' do
+ describe '#after_script_value' do
it 'returns after script' do
- expect(global.after_script).to eq ['make clean']
+ expect(global.after_script_value).to eq ['make clean']
end
end
- describe '#variables' do
+ describe '#variables_value' do
it 'returns variables' do
- expect(global.variables).to eq(VAR: 'value')
+ expect(global.variables_value).to eq(VAR: 'value')
end
end
- describe '#stages' do
+ describe '#stages_value' do
context 'when stages key defined' do
it 'returns array of stages' do
- expect(global.stages).to eq %w[build pages]
+ expect(global.stages_value).to eq %w[build pages]
end
end
@@ -126,21 +126,21 @@ describe Gitlab::Ci::Config::Node::Global do
end
it 'returns array of types as stages' do
- expect(global.stages).to eq %w[test deploy]
+ expect(global.stages_value).to eq %w[test deploy]
end
end
end
- describe '#cache' do
+ describe '#cache_value' do
it 'returns cache configuration' do
- expect(global.cache)
+ expect(global.cache_value)
.to eq(key: 'k', untracked: true, paths: ['public/'])
end
end
- describe '#jobs' do
+ describe '#jobs_value' do
it 'returns jobs configuration' do
- expect(global.jobs).to eq(
+ expect(global.jobs_value).to eq(
rspec: { name: :rspec,
script: %w[rspec ls],
before_script: ['ls', 'pwd'],
@@ -181,25 +181,25 @@ describe Gitlab::Ci::Config::Node::Global do
it 'contains unspecified nodes' do
expect(global.descendants.first)
- .to be_an_instance_of Gitlab::Ci::Config::Node::Unspecified
+ .to be_an_instance_of Gitlab::Ci::Config::Entry::Unspecified
end
end
- describe '#variables' do
+ describe '#variables_value' do
it 'returns default value for variables' do
- expect(global.variables).to eq({})
+ expect(global.variables_value).to eq({})
end
end
- describe '#stages' do
+ describe '#stages_value' do
it 'returns an array of default stages' do
- expect(global.stages).to eq %w[build test deploy]
+ expect(global.stages_value).to eq %w[build test deploy]
end
end
- describe '#cache' do
+ describe '#cache_value' do
it 'returns correct cache definition' do
- expect(global.cache).to eq(key: 'a')
+ expect(global.cache_value).to eq(key: 'a')
end
end
end
@@ -217,37 +217,52 @@ describe Gitlab::Ci::Config::Node::Global do
{ variables: nil, rspec: { script: 'rspec' } }
end
- describe '#variables' do
+ describe '#variables_value' do
it 'undefined entry returns a default value' do
- expect(global.variables).to eq({})
+ expect(global.variables_value).to eq({})
end
end
end
end
- context 'when hash is not valid' do
+ context 'when configuration is not valid' do
before { global.compose! }
- let(:hash) do
- { before_script: 'ls' }
- end
+ context 'when before script is not an array' do
+ let(:hash) do
+ { before_script: 'ls' }
+ end
- describe '#valid?' do
- it 'is not valid' do
- expect(global).not_to be_valid
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(global).not_to be_valid
+ end
end
- end
- describe '#errors' do
- it 'reports errors from child nodes' do
- expect(global.errors)
- .to include 'before_script config should be an array of strings'
+ describe '#errors' do
+ it 'reports errors from child nodes' do
+ expect(global.errors)
+ .to include 'before_script config should be an array of strings'
+ end
+ end
+
+ describe '#before_script_value' do
+ it 'returns nil' do
+ expect(global.before_script_value).to be_nil
+ end
end
end
- describe '#before_script' do
- it 'returns nil' do
- expect(global.before_script).to be_nil
+ context 'when job does not have commands' do
+ let(:hash) do
+ { before_script: ['echo 123'], rspec: { stage: 'test' } }
+ end
+
+ describe '#errors' do
+ it 'reports errors about missing script' do
+ expect(global.errors)
+ .to include "jobs:rspec script can't be blank"
+ end
end
end
end
@@ -281,15 +296,15 @@ describe Gitlab::Ci::Config::Node::Global do
{ cache: { key: 'a' }, rspec: { script: 'ls' } }
end
- context 'when node exists' do
+ context 'when entry exists' do
it 'returns correct entry' do
expect(global[:cache])
- .to be_an_instance_of Gitlab::Ci::Config::Node::Cache
+ .to be_an_instance_of Gitlab::Ci::Config::Entry::Cache
expect(global[:jobs][:rspec][:script].value).to eq ['ls']
end
end
- context 'when node does not exist' do
+ context 'when entry does not exist' do
it 'always return unspecified node' do
expect(global[:some][:unknown][:node])
.not_to be_specified
diff --git a/spec/lib/gitlab/ci/config/node/hidden_spec.rb b/spec/lib/gitlab/ci/config/entry/hidden_spec.rb
index 61e2a554419..459362761e6 100644
--- a/spec/lib/gitlab/ci/config/node/hidden_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/hidden_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Hidden do
+describe Gitlab::Ci::Config::Entry::Hidden do
let(:entry) { described_class.new(config) }
describe 'validations' do
diff --git a/spec/lib/gitlab/ci/config/node/image_spec.rb b/spec/lib/gitlab/ci/config/entry/image_spec.rb
index d11bb39f328..3c99cb0a1ee 100644
--- a/spec/lib/gitlab/ci/config/node/image_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/image_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Image do
+describe Gitlab::Ci::Config::Entry::Image do
let(:entry) { described_class.new(config) }
describe 'validation' do
diff --git a/spec/lib/gitlab/ci/config/node/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index 91f676dae03..fc9b8b86dc4 100644
--- a/spec/lib/gitlab/ci/config/node/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Job do
+describe Gitlab::Ci::Config::Entry::Job do
let(:entry) { described_class.new(config, name: :rspec) }
describe 'validations' do
@@ -19,8 +19,7 @@ describe Gitlab::Ci::Config::Node::Job do
let(:entry) { described_class.new(config, name: ''.to_sym) }
it 'reports error' do
- expect(entry.errors)
- .to include "job name can't be blank"
+ expect(entry.errors).to include "job name can't be blank"
end
end
end
@@ -56,6 +55,15 @@ describe Gitlab::Ci::Config::Node::Job do
end
end
end
+
+ context 'when script is not provided' do
+ let(:config) { { stage: 'test' } }
+
+ it 'returns error about missing script entry' do
+ expect(entry).not_to be_valid
+ expect(entry.errors).to include "job script can't be blank"
+ end
+ end
end
end
@@ -78,7 +86,7 @@ describe Gitlab::Ci::Config::Node::Job do
before { entry.compose!(deps) }
let(:config) do
- { image: 'some_image', cache: { key: 'test' } }
+ { script: 'rspec', image: 'some_image', cache: { key: 'test' } }
end
it 'overrides global config' do
diff --git a/spec/lib/gitlab/ci/config/node/jobs_spec.rb b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
index 929809339ef..aaebf783962 100644
--- a/spec/lib/gitlab/ci/config/node/jobs_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Jobs do
+describe Gitlab::Ci::Config::Entry::Jobs do
let(:entry) { described_class.new(config) }
describe 'validations' do
@@ -74,9 +74,9 @@ describe Gitlab::Ci::Config::Node::Jobs do
it 'creates valid descendant nodes' do
expect(entry.descendants.count).to eq 3
expect(entry.descendants.first(2))
- .to all(be_an_instance_of(Gitlab::Ci::Config::Node::Job))
+ .to all(be_an_instance_of(Gitlab::Ci::Config::Entry::Job))
expect(entry.descendants.last)
- .to be_an_instance_of(Gitlab::Ci::Config::Node::Hidden)
+ .to be_an_instance_of(Gitlab::Ci::Config::Entry::Hidden)
end
end
diff --git a/spec/lib/gitlab/ci/config/node/key_spec.rb b/spec/lib/gitlab/ci/config/entry/key_spec.rb
index 8cda43173fe..a55e5b4b8ac 100644
--- a/spec/lib/gitlab/ci/config/node/key_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/key_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Key do
+describe Gitlab::Ci::Config::Entry::Key do
let(:entry) { described_class.new(config) }
describe 'validations' do
diff --git a/spec/lib/gitlab/ci/config/node/paths_spec.rb b/spec/lib/gitlab/ci/config/entry/paths_spec.rb
index 6fd744b3975..e60c9aaf661 100644
--- a/spec/lib/gitlab/ci/config/node/paths_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/paths_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Paths do
+describe Gitlab::Ci::Config::Entry::Paths do
let(:entry) { described_class.new(config) }
describe 'validations' do
diff --git a/spec/lib/gitlab/ci/config/node/script_spec.rb b/spec/lib/gitlab/ci/config/entry/script_spec.rb
index 219a7e981d3..aa99cee2690 100644
--- a/spec/lib/gitlab/ci/config/node/script_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/script_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Script do
+describe Gitlab::Ci::Config::Entry::Script do
let(:entry) { described_class.new(config) }
describe 'validations' do
diff --git a/spec/lib/gitlab/ci/config/node/services_spec.rb b/spec/lib/gitlab/ci/config/entry/services_spec.rb
index be0fe46befd..66fad3b6b16 100644
--- a/spec/lib/gitlab/ci/config/node/services_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/services_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Services do
+describe Gitlab::Ci::Config::Entry::Services do
let(:entry) { described_class.new(config) }
describe 'validations' do
diff --git a/spec/lib/gitlab/ci/config/node/stage_spec.rb b/spec/lib/gitlab/ci/config/entry/stage_spec.rb
index fb9ec70762a..70c8a0a355a 100644
--- a/spec/lib/gitlab/ci/config/node/stage_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/stage_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Stage do
+describe Gitlab::Ci::Config::Entry::Stage do
let(:stage) { described_class.new(config) }
describe 'validations' do
diff --git a/spec/lib/gitlab/ci/config/node/stages_spec.rb b/spec/lib/gitlab/ci/config/entry/stages_spec.rb
index 1a3818d8997..182c8d867c7 100644
--- a/spec/lib/gitlab/ci/config/node/stages_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/stages_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Stages do
+describe Gitlab::Ci::Config::Entry::Stages do
let(:entry) { described_class.new(config) }
describe 'validations' do
diff --git a/spec/lib/gitlab/ci/config/node/trigger_spec.rb b/spec/lib/gitlab/ci/config/entry/trigger_spec.rb
index a4a3e36754e..e4ee44f1274 100644
--- a/spec/lib/gitlab/ci/config/node/trigger_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/trigger_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Trigger do
+describe Gitlab::Ci::Config::Entry::Trigger do
let(:entry) { described_class.new(config) }
describe 'validations' do
diff --git a/spec/lib/gitlab/ci/config/node/undefined_spec.rb b/spec/lib/gitlab/ci/config/entry/undefined_spec.rb
index 6bde8602963..fdf48d84192 100644
--- a/spec/lib/gitlab/ci/config/node/undefined_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/undefined_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Undefined do
+describe Gitlab::Ci::Config::Entry::Undefined do
let(:entry) { described_class.new }
describe '#leaf?' do
diff --git a/spec/lib/gitlab/ci/config/node/unspecified_spec.rb b/spec/lib/gitlab/ci/config/entry/unspecified_spec.rb
index ba3ceef24ce..66f88fa35b6 100644
--- a/spec/lib/gitlab/ci/config/node/unspecified_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/unspecified_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Unspecified do
+describe Gitlab::Ci::Config::Entry::Unspecified do
let(:unspecified) { described_class.new(entry) }
let(:entry) { spy('Entry') }
diff --git a/spec/lib/gitlab/ci/config/node/validatable_spec.rb b/spec/lib/gitlab/ci/config/entry/validatable_spec.rb
index 64b77fd6e03..d1856801827 100644
--- a/spec/lib/gitlab/ci/config/node/validatable_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/validatable_spec.rb
@@ -1,15 +1,15 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Validatable do
- let(:node) { Class.new }
+describe Gitlab::Ci::Config::Entry::Validatable do
+ let(:entry) { Class.new }
before do
- node.include(described_class)
+ entry.include(described_class)
end
describe '.validator' do
before do
- node.class_eval do
+ entry.class_eval do
attr_accessor :test_attribute
validations do
@@ -19,34 +19,34 @@ describe Gitlab::Ci::Config::Node::Validatable do
end
it 'returns validator' do
- expect(node.validator.superclass)
- .to be Gitlab::Ci::Config::Node::Validator
+ expect(entry.validator.superclass)
+ .to be Gitlab::Ci::Config::Entry::Validator
end
it 'returns only one validator to mitigate leaks' do
- expect { node.validator }.not_to change { node.validator }
+ expect { entry.validator }.not_to change { entry.validator }
end
- context 'when validating node instance' do
- let(:node_instance) { node.new }
+ context 'when validating entry instance' do
+ let(:entry_instance) { entry.new }
context 'when attribute is valid' do
before do
- node_instance.test_attribute = 'valid'
+ entry_instance.test_attribute = 'valid'
end
it 'instance of validator is valid' do
- expect(node.validator.new(node_instance)).to be_valid
+ expect(entry.validator.new(entry_instance)).to be_valid
end
end
context 'when attribute is not valid' do
before do
- node_instance.test_attribute = nil
+ entry_instance.test_attribute = nil
end
it 'instance of validator is invalid' do
- expect(node.validator.new(node_instance)).to be_invalid
+ expect(entry.validator.new(entry_instance)).to be_invalid
end
end
end
diff --git a/spec/lib/gitlab/ci/config/node/validator_spec.rb b/spec/lib/gitlab/ci/config/entry/validator_spec.rb
index 090fd63b844..ad7e6f07d3c 100644
--- a/spec/lib/gitlab/ci/config/node/validator_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/validator_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Validator do
+describe Gitlab::Ci::Config::Entry::Validator do
let(:validator) { Class.new(described_class) }
let(:validator_instance) { validator.new(node) }
let(:node) { spy('node') }
diff --git a/spec/lib/gitlab/ci/config/node/variables_spec.rb b/spec/lib/gitlab/ci/config/entry/variables_spec.rb
index 4b6d971ec71..58327d08904 100644
--- a/spec/lib/gitlab/ci/config/node/variables_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/variables_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Ci::Config::Node::Variables do
+describe Gitlab::Ci::Config::Entry::Variables do
let(:entry) { described_class.new(config) }
describe 'validations' do
diff --git a/spec/lib/gitlab/ci/config/node/configurable_spec.rb b/spec/lib/gitlab/ci/config/node/configurable_spec.rb
deleted file mode 100644
index c468ecf957b..00000000000
--- a/spec/lib/gitlab/ci/config/node/configurable_spec.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Ci::Config::Node::Configurable do
- let(:node) { Class.new }
-
- before do
- node.include(described_class)
- end
-
- describe 'validations' do
- let(:validator) { node.validator.new(instance) }
-
- before do
- node.class_eval do
- attr_reader :config
-
- def initialize(config)
- @config = config
- end
- end
-
- validator.validate
- end
-
- context 'when node validator is invalid' do
- let(:instance) { node.new('ls') }
-
- it 'returns invalid validator' do
- expect(validator).to be_invalid
- end
- end
-
- context 'when node instance is valid' do
- let(:instance) { node.new(key: 'value') }
-
- it 'returns valid validator' do
- expect(validator).to be_valid
- end
- end
- end
-
- describe 'configured nodes' do
- before do
- node.class_eval do
- node :object, Object, description: 'test object'
- end
- end
-
- describe '.nodes' do
- it 'has valid nodes' do
- expect(node.nodes).to include :object
- end
-
- it 'creates a node factory' do
- expect(node.nodes[:object])
- .to be_an_instance_of Gitlab::Ci::Config::Node::Factory
- end
-
- it 'returns a duplicated factory object' do
- first_factory = node.nodes[:object]
- second_factory = node.nodes[:object]
-
- expect(first_factory).not_to be_equal(second_factory)
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb
new file mode 100644
index 00000000000..9376bce17a1
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb
@@ -0,0 +1,86 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Build::Cancelable do
+ let(:status) { double('core status') }
+ let(:user) { double('user') }
+
+ subject do
+ described_class.new(status)
+ end
+
+ describe '#text' do
+ it 'does not override status text' do
+ expect(status).to receive(:text)
+
+ subject.text
+ end
+ end
+
+ describe '#icon' do
+ it 'does not override status icon' do
+ expect(status).to receive(:icon)
+
+ subject.icon
+ end
+ end
+
+ describe '#label' do
+ it 'does not override status label' do
+ expect(status).to receive(:label)
+
+ subject.label
+ end
+ end
+
+ describe 'action details' do
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build) }
+ let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
+
+ describe '#has_action?' do
+ context 'when user is allowed to update build' do
+ before { build.project.team << [user, :developer] }
+
+ it { is_expected.to have_action }
+ end
+
+ context 'when user is not allowed to update build' do
+ it { is_expected.not_to have_action }
+ end
+ end
+
+ describe '#action_path' do
+ it { expect(subject.action_path).to include "#{build.id}/cancel" }
+ end
+
+ describe '#action_icon' do
+ it { expect(subject.action_icon).to eq 'ban' }
+ end
+
+ describe '#action_title' do
+ it { expect(subject.action_title).to eq 'Cancel' }
+ end
+ end
+
+ describe '.matches?' do
+ subject { described_class.matches?(build, user) }
+
+ context 'when build is cancelable' do
+ let(:build) do
+ create(:ci_build, :running)
+ end
+
+ it 'is a correct match' do
+ expect(subject).to be true
+ end
+ end
+
+ context 'when build is not cancelable' do
+ let(:build) { create(:ci_build, :success) }
+
+ it 'does not match' do
+ expect(subject).to be false
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/build/common_spec.rb b/spec/lib/gitlab/ci/status/build/common_spec.rb
new file mode 100644
index 00000000000..40b96b1807b
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/build/common_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Build::Common do
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build) }
+ let(:project) { build.project }
+
+ subject do
+ Gitlab::Ci::Status::Core
+ .new(build, user)
+ .extend(described_class)
+ end
+
+ describe '#has_action?' do
+ it { is_expected.not_to have_action }
+ end
+
+ describe '#has_details?' do
+ context 'when user has access to read build' do
+ before { project.team << [user, :developer] }
+
+ it { is_expected.to have_details }
+ end
+
+ context 'when user does not have access to read build' do
+ before { project.update(public_builds: false) }
+
+ it { is_expected.not_to have_details }
+ end
+ end
+
+ describe '#details_path' do
+ it 'links to the build details page' do
+ expect(subject.details_path).to include "builds/#{build.id}"
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/build/factory_spec.rb b/spec/lib/gitlab/ci/status/build/factory_spec.rb
new file mode 100644
index 00000000000..dccb29b5ef6
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/build/factory_spec.rb
@@ -0,0 +1,141 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Build::Factory do
+ let(:user) { create(:user) }
+ let(:project) { build.project }
+
+ subject { described_class.new(build, user) }
+ let(:status) { subject.fabricate! }
+
+ before { project.team << [user, :developer] }
+
+ context 'when build is successful' do
+ let(:build) { create(:ci_build, :success) }
+
+ it 'fabricates a retryable build status' do
+ expect(status).to be_a Gitlab::Ci::Status::Build::Retryable
+ end
+
+ it 'fabricates status with correct details' do
+ expect(status.text).to eq 'passed'
+ expect(status.icon).to eq 'icon_status_success'
+ expect(status.label).to eq 'passed'
+ expect(status).to have_details
+ expect(status).to have_action
+ end
+ end
+
+ context 'when build is failed' do
+ let(:build) { create(:ci_build, :failed) }
+
+ it 'fabricates a retryable build status' do
+ expect(status).to be_a Gitlab::Ci::Status::Build::Retryable
+ end
+
+ it 'fabricates status with correct details' do
+ expect(status.text).to eq 'failed'
+ expect(status.icon).to eq 'icon_status_failed'
+ expect(status.label).to eq 'failed'
+ expect(status).to have_details
+ expect(status).to have_action
+ end
+ end
+
+ context 'when build is a canceled' do
+ let(:build) { create(:ci_build, :canceled) }
+
+ it 'fabricates a retryable build status' do
+ expect(status).to be_a Gitlab::Ci::Status::Build::Retryable
+ end
+
+ it 'fabricates status with correct details' do
+ expect(status.text).to eq 'canceled'
+ expect(status.icon).to eq 'icon_status_canceled'
+ expect(status.label).to eq 'canceled'
+ expect(status).to have_details
+ expect(status).to have_action
+ end
+ end
+
+ context 'when build is running' do
+ let(:build) { create(:ci_build, :running) }
+
+ it 'fabricates a canceable build status' do
+ expect(status).to be_a Gitlab::Ci::Status::Build::Cancelable
+ end
+
+ it 'fabricates status with correct details' do
+ expect(status.text).to eq 'running'
+ expect(status.icon).to eq 'icon_status_running'
+ expect(status.label).to eq 'running'
+ expect(status).to have_details
+ expect(status).to have_action
+ end
+ end
+
+ context 'when build is pending' do
+ let(:build) { create(:ci_build, :pending) }
+
+ it 'fabricates a cancelable build status' do
+ expect(status).to be_a Gitlab::Ci::Status::Build::Cancelable
+ end
+
+ it 'fabricates status with correct details' do
+ expect(status.text).to eq 'pending'
+ expect(status.icon).to eq 'icon_status_pending'
+ expect(status.label).to eq 'pending'
+ expect(status).to have_details
+ expect(status).to have_action
+ end
+ end
+
+ context 'when build is skipped' do
+ let(:build) { create(:ci_build, :skipped) }
+
+ it 'fabricates a core skipped status' do
+ expect(status).to be_a Gitlab::Ci::Status::Skipped
+ end
+
+ it 'fabricates status with correct details' do
+ expect(status.text).to eq 'skipped'
+ expect(status.icon).to eq 'icon_status_skipped'
+ expect(status.label).to eq 'skipped'
+ expect(status).to have_details
+ expect(status).not_to have_action
+ end
+ end
+
+ context 'when build is a manual action' do
+ context 'when build is a play action' do
+ let(:build) { create(:ci_build, :playable) }
+
+ it 'fabricates a core skipped status' do
+ expect(status).to be_a Gitlab::Ci::Status::Build::Play
+ end
+
+ it 'fabricates status with correct details' do
+ expect(status.text).to eq 'manual'
+ expect(status.icon).to eq 'icon_status_manual'
+ expect(status.label).to eq 'manual play action'
+ expect(status).to have_details
+ expect(status).to have_action
+ end
+ end
+
+ context 'when build is an environment stop action' do
+ let(:build) { create(:ci_build, :playable, :teardown_environment) }
+
+ it 'fabricates a core skipped status' do
+ expect(status).to be_a Gitlab::Ci::Status::Build::Stop
+ end
+
+ it 'fabricates status with correct details' do
+ expect(status.text).to eq 'manual'
+ expect(status.icon).to eq 'icon_status_manual'
+ expect(status.label).to eq 'manual stop action'
+ expect(status).to have_details
+ expect(status).to have_action
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/build/play_spec.rb b/spec/lib/gitlab/ci/status/build/play_spec.rb
new file mode 100644
index 00000000000..4ddf04a8e11
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/build/play_spec.rb
@@ -0,0 +1,82 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Build::Play do
+ let(:status) { double('core') }
+ let(:user) { double('user') }
+
+ subject { described_class.new(status) }
+
+ describe '#text' do
+ it { expect(subject.text).to eq 'manual' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'manual play action' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_manual' }
+ end
+
+ describe 'action details' do
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build) }
+ let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
+
+ describe '#has_action?' do
+ context 'when user is allowed to update build' do
+ before { build.project.team << [user, :developer] }
+
+ it { is_expected.to have_action }
+ end
+
+ context 'when user is not allowed to update build' do
+ it { is_expected.not_to have_action }
+ end
+ end
+
+ describe '#action_path' do
+ it { expect(subject.action_path).to include "#{build.id}/play" }
+ end
+
+ describe '#action_icon' do
+ it { expect(subject.action_icon).to eq 'play' }
+ end
+
+ describe '#action_title' do
+ it { expect(subject.action_title).to eq 'Play' }
+ end
+ end
+
+ describe '.matches?' do
+ subject { described_class.matches?(build, user) }
+
+ context 'when build is playable' do
+ context 'when build stops an environment' do
+ let(:build) do
+ create(:ci_build, :playable, :teardown_environment)
+ end
+
+ it 'does not match' do
+ expect(subject).to be false
+ end
+ end
+
+ context 'when build does not stop an environment' do
+ let(:build) { create(:ci_build, :playable) }
+
+ it 'is a correct match' do
+ expect(subject).to be true
+ end
+ end
+ end
+
+ context 'when build is not playable' do
+ let(:build) { create(:ci_build) }
+
+ it 'does not match' do
+ expect(subject).to be false
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/build/retryable_spec.rb b/spec/lib/gitlab/ci/status/build/retryable_spec.rb
new file mode 100644
index 00000000000..d61e5bbaa6b
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/build/retryable_spec.rb
@@ -0,0 +1,86 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Build::Retryable do
+ let(:status) { double('core status') }
+ let(:user) { double('user') }
+
+ subject do
+ described_class.new(status)
+ end
+
+ describe '#text' do
+ it 'does not override status text' do
+ expect(status).to receive(:text)
+
+ subject.text
+ end
+ end
+
+ describe '#icon' do
+ it 'does not override status icon' do
+ expect(status).to receive(:icon)
+
+ subject.icon
+ end
+ end
+
+ describe '#label' do
+ it 'does not override status label' do
+ expect(status).to receive(:label)
+
+ subject.label
+ end
+ end
+
+ describe 'action details' do
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build) }
+ let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
+
+ describe '#has_action?' do
+ context 'when user is allowed to update build' do
+ before { build.project.team << [user, :developer] }
+
+ it { is_expected.to have_action }
+ end
+
+ context 'when user is not allowed to update build' do
+ it { is_expected.not_to have_action }
+ end
+ end
+
+ describe '#action_path' do
+ it { expect(subject.action_path).to include "#{build.id}/retry" }
+ end
+
+ describe '#action_icon' do
+ it { expect(subject.action_icon).to eq 'refresh' }
+ end
+
+ describe '#action_title' do
+ it { expect(subject.action_title).to eq 'Retry' }
+ end
+ end
+
+ describe '.matches?' do
+ subject { described_class.matches?(build, user) }
+
+ context 'when build is retryable' do
+ let(:build) do
+ create(:ci_build, :success)
+ end
+
+ it 'is a correct match' do
+ expect(subject).to be true
+ end
+ end
+
+ context 'when build is not retryable' do
+ let(:build) { create(:ci_build, :running) }
+
+ it 'does not match' do
+ expect(subject).to be false
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/build/stop_spec.rb b/spec/lib/gitlab/ci/status/build/stop_spec.rb
new file mode 100644
index 00000000000..59a85b55f90
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/build/stop_spec.rb
@@ -0,0 +1,84 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Build::Stop do
+ let(:status) { double('core status') }
+ let(:user) { double('user') }
+
+ subject do
+ described_class.new(status)
+ end
+
+ describe '#text' do
+ it { expect(subject.text).to eq 'manual' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'manual stop action' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_manual' }
+ end
+
+ describe 'action details' do
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build) }
+ let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
+
+ describe '#has_action?' do
+ context 'when user is allowed to update build' do
+ before { build.project.team << [user, :developer] }
+
+ it { is_expected.to have_action }
+ end
+
+ context 'when user is not allowed to update build' do
+ it { is_expected.not_to have_action }
+ end
+ end
+
+ describe '#action_path' do
+ it { expect(subject.action_path).to include "#{build.id}/play" }
+ end
+
+ describe '#action_icon' do
+ it { expect(subject.action_icon).to eq 'stop' }
+ end
+
+ describe '#action_title' do
+ it { expect(subject.action_title).to eq 'Stop' }
+ end
+ end
+
+ describe '.matches?' do
+ subject { described_class.matches?(build, user) }
+
+ context 'when build is playable' do
+ context 'when build stops an environment' do
+ let(:build) do
+ create(:ci_build, :playable, :teardown_environment)
+ end
+
+ it 'is a correct match' do
+ expect(subject).to be true
+ end
+ end
+
+ context 'when build does not stop an environment' do
+ let(:build) { create(:ci_build, :playable) }
+
+ it 'does not match' do
+ expect(subject).to be false
+ end
+ end
+ end
+
+ context 'when build is not playable' do
+ let(:build) { create(:ci_build) }
+
+ it 'does not match' do
+ expect(subject).to be false
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/canceled_spec.rb b/spec/lib/gitlab/ci/status/canceled_spec.rb
new file mode 100644
index 00000000000..4639278ad45
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/canceled_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Canceled do
+ subject do
+ described_class.new(double('subject'), double('user'))
+ end
+
+ describe '#text' do
+ it { expect(subject.label).to eq 'canceled' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'canceled' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_canceled' }
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/created_spec.rb b/spec/lib/gitlab/ci/status/created_spec.rb
new file mode 100644
index 00000000000..2ce176a29d6
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/created_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Created do
+ subject do
+ described_class.new(double('subject'), double('user'))
+ end
+
+ describe '#text' do
+ it { expect(subject.label).to eq 'created' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'created' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_created' }
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/extended_spec.rb b/spec/lib/gitlab/ci/status/extended_spec.rb
new file mode 100644
index 00000000000..c2d74ca5cde
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/extended_spec.rb
@@ -0,0 +1,12 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Extended do
+ subject do
+ Class.new.include(described_class)
+ end
+
+ it 'requires subclass to implement matcher' do
+ expect { subject.matches?(double, double) }
+ .to raise_error(NotImplementedError)
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/factory_spec.rb b/spec/lib/gitlab/ci/status/factory_spec.rb
new file mode 100644
index 00000000000..f92a1c149bf
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/factory_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Factory do
+ subject do
+ described_class.new(resource, user)
+ end
+
+ let(:user) { create(:user) }
+
+ let(:status) { subject.fabricate! }
+
+ context 'when object has a core status' do
+ HasStatus::AVAILABLE_STATUSES.each do |core_status|
+ context "when core status is #{core_status}" do
+ let(:resource) { double(status: core_status) }
+
+ it "fabricates a core status #{core_status}" do
+ expect(status).to be_a(
+ Gitlab::Ci::Status.const_get(core_status.capitalize))
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/failed_spec.rb b/spec/lib/gitlab/ci/status/failed_spec.rb
new file mode 100644
index 00000000000..9d527e6a7ef
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/failed_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Failed do
+ subject do
+ described_class.new(double('subject'), double('user'))
+ end
+
+ describe '#text' do
+ it { expect(subject.label).to eq 'failed' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'failed' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_failed' }
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/pending_spec.rb b/spec/lib/gitlab/ci/status/pending_spec.rb
new file mode 100644
index 00000000000..d03f595d3c7
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/pending_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Pending do
+ subject do
+ described_class.new(double('subject'), double('user'))
+ end
+
+ describe '#text' do
+ it { expect(subject.label).to eq 'pending' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'pending' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_pending' }
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/pipeline/common_spec.rb b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb
new file mode 100644
index 00000000000..d665674bf70
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Pipeline::Common do
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project, :private) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+
+ subject do
+ Gitlab::Ci::Status::Core
+ .new(pipeline, user)
+ .extend(described_class)
+ end
+
+ describe '#has_action?' do
+ it { is_expected.not_to have_action }
+ end
+
+ describe '#has_details?' do
+ context 'when user has access to read pipeline' do
+ before { project.team << [user, :developer] }
+
+ it { is_expected.to have_details }
+ end
+
+ context 'when user does not have access to read pipeline' do
+ it { is_expected.not_to have_details }
+ end
+ end
+
+ describe '#details_path' do
+ it 'links to the pipeline details page' do
+ expect(subject.details_path)
+ .to include "pipelines/#{pipeline.id}"
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb
new file mode 100644
index 00000000000..d4a2dc7fcc1
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb
@@ -0,0 +1,59 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Pipeline::Factory do
+ let(:user) { create(:user) }
+ let(:project) { pipeline.project }
+
+ subject do
+ described_class.new(pipeline, user)
+ end
+
+ let(:status) do
+ subject.fabricate!
+ end
+
+ before do
+ project.team << [user, :developer]
+ end
+
+ context 'when pipeline has a core status' do
+ HasStatus::AVAILABLE_STATUSES.each do |core_status|
+ context "when core status is #{core_status}" do
+ let(:pipeline) do
+ create(:ci_pipeline, status: core_status)
+ end
+
+ it "fabricates a core status #{core_status}" do
+ expect(status).to be_a(
+ Gitlab::Ci::Status.const_get(core_status.capitalize))
+ end
+
+ it 'extends core status with common pipeline methods' do
+ expect(status).to have_details
+ expect(status).not_to have_action
+ expect(status.details_path)
+ .to include "pipelines/#{pipeline.id}"
+ end
+ end
+ end
+ end
+
+ context 'when pipeline has warnings' do
+ let(:pipeline) do
+ create(:ci_pipeline, status: :success)
+ end
+
+ before do
+ create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline)
+ end
+
+ it 'fabricates extended "success with warnings" status' do
+ expect(status)
+ .to be_a Gitlab::Ci::Status::Pipeline::SuccessWithWarnings
+ end
+
+ it 'extends core status with common pipeline methods' do
+ expect(status).to have_details
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb b/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb
new file mode 100644
index 00000000000..7e3383c307f
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb
@@ -0,0 +1,65 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Pipeline::SuccessWithWarnings do
+ subject do
+ described_class.new(double('status'))
+ end
+
+ describe '#test' do
+ it { expect(subject.text).to eq 'passed' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'passed with warnings' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_warning' }
+ end
+
+ describe '.matches?' do
+ context 'when pipeline is successful' do
+ let(:pipeline) do
+ create(:ci_pipeline, status: :success)
+ end
+
+ context 'when pipeline has warnings' do
+ before do
+ allow(pipeline).to receive(:has_warnings?).and_return(true)
+ end
+
+ it 'is a correct match' do
+ expect(described_class.matches?(pipeline, double)).to eq true
+ end
+ end
+
+ context 'when pipeline does not have warnings' do
+ it 'does not match' do
+ expect(described_class.matches?(pipeline, double)).to eq false
+ end
+ end
+ end
+
+ context 'when pipeline is not successful' do
+ let(:pipeline) do
+ create(:ci_pipeline, status: :skipped)
+ end
+
+ context 'when pipeline has warnings' do
+ before do
+ allow(pipeline).to receive(:has_warnings?).and_return(true)
+ end
+
+ it 'does not match' do
+ expect(described_class.matches?(pipeline, double)).to eq false
+ end
+ end
+
+ context 'when pipeline does not have warnings' do
+ it 'does not match' do
+ expect(described_class.matches?(pipeline, double)).to eq false
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/running_spec.rb b/spec/lib/gitlab/ci/status/running_spec.rb
new file mode 100644
index 00000000000..9f47090d396
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/running_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Running do
+ subject do
+ described_class.new(double('subject'), double('user'))
+ end
+
+ describe '#text' do
+ it { expect(subject.label).to eq 'running' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'running' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_running' }
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/skipped_spec.rb b/spec/lib/gitlab/ci/status/skipped_spec.rb
new file mode 100644
index 00000000000..94601648a8d
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/skipped_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Skipped do
+ subject do
+ described_class.new(double('subject'), double('user'))
+ end
+
+ describe '#text' do
+ it { expect(subject.label).to eq 'skipped' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'skipped' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_skipped' }
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/stage/common_spec.rb b/spec/lib/gitlab/ci/status/stage/common_spec.rb
new file mode 100644
index 00000000000..8814a7614a0
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/stage/common_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Stage::Common do
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project) }
+ let(:pipeline) { create(:ci_empty_pipeline, project: project) }
+
+ let(:stage) do
+ build(:ci_stage, pipeline: pipeline, name: 'test')
+ end
+
+ subject do
+ Class.new(Gitlab::Ci::Status::Core)
+ .new(stage, user).extend(described_class)
+ end
+
+ it 'does not have action' do
+ expect(subject).not_to have_action
+ end
+
+ it 'links to the pipeline details page' do
+ expect(subject.details_path)
+ .to include "pipelines/#{pipeline.id}"
+ expect(subject.details_path)
+ .to include "##{stage.name}"
+ end
+
+ context 'when user has permission to read pipeline' do
+ before do
+ project.team << [user, :master]
+ end
+
+ it 'has details' do
+ expect(subject).to have_details
+ end
+ end
+
+ context 'when user does not have permission to read pipeline' do
+ it 'does not have details' do
+ expect(subject).not_to have_details
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/stage/factory_spec.rb b/spec/lib/gitlab/ci/status/stage/factory_spec.rb
new file mode 100644
index 00000000000..6f8721d30c2
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/stage/factory_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Stage::Factory do
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project) }
+ let(:pipeline) { create(:ci_empty_pipeline, project: project) }
+
+ let(:stage) do
+ build(:ci_stage, pipeline: pipeline, name: 'test')
+ end
+
+ subject do
+ described_class.new(stage, user)
+ end
+
+ let(:status) do
+ subject.fabricate!
+ end
+
+ before do
+ project.team << [user, :developer]
+ end
+
+ context 'when stage has a core status' do
+ HasStatus::AVAILABLE_STATUSES.each do |core_status|
+ context "when core status is #{core_status}" do
+ before do
+ create(:ci_build, pipeline: pipeline, stage: 'test', status: core_status)
+ create(:commit_status, pipeline: pipeline, stage: 'test', status: core_status)
+ create(:ci_build, pipeline: pipeline, stage: 'build', status: :failed)
+ end
+
+ it "fabricates a core status #{core_status}" do
+ expect(status).to be_a(
+ Gitlab::Ci::Status.const_get(core_status.capitalize))
+ end
+
+ it 'extends core status with common stage methods' do
+ expect(status).to have_details
+ expect(status.details_path).to include "pipelines/#{pipeline.id}"
+ expect(status.details_path).to include "##{stage.name}"
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/success_spec.rb b/spec/lib/gitlab/ci/status/success_spec.rb
new file mode 100644
index 00000000000..90f9f615e0d
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/success_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Success do
+ subject do
+ described_class.new(double('subject'), double('user'))
+ end
+
+ describe '#text' do
+ it { expect(subject.label).to eq 'passed' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'passed' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'icon_status_success' }
+ end
+end
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index de3f64249a2..1bbaca0739a 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -257,8 +257,9 @@ describe Gitlab::ClosingIssueExtractor, lib: true do
context 'with an external issue tracker reference' do
it 'extracts the referenced issue' do
jira_project = create(:jira_project, name: 'JIRA_EXT1')
+ jira_project.team << [jira_project.creator, :master]
jira_issue = ExternalIssue.new("#{jira_project.name}-1", project: jira_project)
- closing_issue_extractor = described_class.new jira_project
+ closing_issue_extractor = described_class.new(jira_project, jira_project.creator)
message = "Resolve #{jira_issue.to_reference}"
expect(closing_issue_extractor.closed_by_message(message)).to eq([jira_issue])
diff --git a/spec/lib/gitlab/contributions_calendar_spec.rb b/spec/lib/gitlab/contributions_calendar_spec.rb
new file mode 100644
index 00000000000..01b2a55b63c
--- /dev/null
+++ b/spec/lib/gitlab/contributions_calendar_spec.rb
@@ -0,0 +1,104 @@
+require 'spec_helper'
+
+describe Gitlab::ContributionsCalendar do
+ let(:contributor) { create(:user) }
+ let(:user) { create(:user) }
+
+ let(:private_project) do
+ create(:empty_project, :private) do |project|
+ create(:project_member, user: contributor, project: project)
+ end
+ end
+
+ let(:public_project) do
+ create(:empty_project, :public) do |project|
+ create(:project_member, user: contributor, project: project)
+ end
+ end
+
+ let(:feature_project) do
+ create(:empty_project, :public, issues_access_level: ProjectFeature::PRIVATE) do |project|
+ create(:project_member, user: contributor, project: project).project
+ end
+ end
+
+ let(:today) { Time.now.to_date }
+ let(:last_week) { today - 7.days }
+ let(:last_year) { today - 1.year }
+
+ before do
+ travel_to today
+ end
+
+ after do
+ travel_back
+ end
+
+ def calendar(current_user = nil)
+ described_class.new(contributor, current_user)
+ end
+
+ def create_event(project, day)
+ @targets ||= {}
+ @targets[project] ||= create(:issue, project: project, author: contributor)
+
+ Event.create!(
+ project: project,
+ action: Event::CREATED,
+ target: @targets[project],
+ author: contributor,
+ created_at: day,
+ )
+ end
+
+ describe '#activity_dates' do
+ it "returns a hash of date => count" do
+ create_event(public_project, last_week)
+ create_event(public_project, last_week)
+ create_event(public_project, today)
+
+ expect(calendar.activity_dates).to eq(last_week => 2, today => 1)
+ end
+
+ it "only shows private events to authorized users" do
+ create_event(private_project, today)
+ create_event(feature_project, today)
+
+ expect(calendar.activity_dates[today]).to eq(0)
+ expect(calendar(user).activity_dates[today]).to eq(0)
+ expect(calendar(contributor).activity_dates[today]).to eq(2)
+ end
+ end
+
+ describe '#events_by_date' do
+ it "returns all events for a given date" do
+ e1 = create_event(public_project, today)
+ e2 = create_event(public_project, today)
+ create_event(public_project, last_week)
+
+ expect(calendar.events_by_date(today)).to contain_exactly(e1, e2)
+ end
+
+ it "only shows private events to authorized users" do
+ e1 = create_event(public_project, today)
+ e2 = create_event(private_project, today)
+ e3 = create_event(feature_project, today)
+ create_event(public_project, last_week)
+
+ expect(calendar.events_by_date(today)).to contain_exactly(e1)
+ expect(calendar(contributor).events_by_date(today)).to contain_exactly(e1, e2, e3)
+ end
+ end
+
+ describe '#starting_year' do
+ it "should be the start of last year" do
+ expect(calendar.starting_year).to eq(last_year.year)
+ end
+ end
+
+ describe '#starting_month' do
+ it "should be the start of this month" do
+ expect(calendar.starting_month).to eq(today.month)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cycle_analytics/code_event_spec.rb b/spec/lib/gitlab/cycle_analytics/code_event_spec.rb
new file mode 100644
index 00000000000..43f42d1bde8
--- /dev/null
+++ b/spec/lib/gitlab/cycle_analytics/code_event_spec.rb
@@ -0,0 +1,10 @@
+require 'spec_helper'
+require 'lib/gitlab/cycle_analytics/shared_event_spec'
+
+describe Gitlab::CycleAnalytics::CodeEvent do
+ it_behaves_like 'default query config' do
+ it 'does not have the default order' do
+ expect(event.order).not_to eq(event.start_time_attrs)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb
new file mode 100644
index 00000000000..6062e7af4f5
--- /dev/null
+++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb
@@ -0,0 +1,326 @@
+require 'spec_helper'
+
+describe Gitlab::CycleAnalytics::Events do
+ let(:project) { create(:project) }
+ let(:from_date) { 10.days.ago }
+ let(:user) { create(:user, :admin) }
+ let!(:context) { create(:issue, project: project, created_at: 2.days.ago) }
+
+ subject { described_class.new(project: project, options: { from: from_date, current_user: user }) }
+
+ before do
+ allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([context])
+
+ setup(context)
+ end
+
+ describe '#issue_events' do
+ it 'has the total time' do
+ expect(subject.issue_events.first[:total_time]).not_to be_empty
+ end
+
+ it 'has a title' do
+ expect(subject.issue_events.first[:title]).to eq(context.title)
+ end
+
+ it 'has the URL' do
+ expect(subject.issue_events.first[:url]).not_to be_nil
+ end
+
+ it 'has an iid' do
+ expect(subject.issue_events.first[:iid]).to eq(context.iid.to_s)
+ end
+
+ it 'has a created_at timestamp' do
+ expect(subject.issue_events.first[:created_at]).to end_with('ago')
+ end
+
+ it "has the author's URL" do
+ expect(subject.issue_events.first[:author][:web_url]).not_to be_nil
+ end
+
+ it "has the author's avatar URL" do
+ expect(subject.issue_events.first[:author][:avatar_url]).not_to be_nil
+ end
+
+ it "has the author's name" do
+ expect(subject.issue_events.first[:author][:name]).to eq(context.author.name)
+ end
+ end
+
+ describe '#plan_events' do
+ it 'has a title' do
+ expect(subject.plan_events.first[:title]).not_to be_nil
+ end
+
+ it 'has a sha short ID' do
+ expect(subject.plan_events.first[:short_sha]).not_to be_nil
+ end
+
+ it 'has the URL' do
+ expect(subject.plan_events.first[:commit_url]).not_to be_nil
+ end
+
+ it 'has the total time' do
+ expect(subject.plan_events.first[:total_time]).not_to be_empty
+ end
+
+ it "has the author's URL" do
+ expect(subject.plan_events.first[:author][:web_url]).not_to be_nil
+ end
+
+ it "has the author's avatar URL" do
+ expect(subject.plan_events.first[:author][:avatar_url]).not_to be_nil
+ end
+
+ it "has the author's name" do
+ expect(subject.plan_events.first[:author][:name]).not_to be_nil
+ end
+ end
+
+ describe '#code_events' do
+ before do
+ create_commit_referencing_issue(context)
+ end
+
+ it 'has the total time' do
+ expect(subject.code_events.first[:total_time]).not_to be_empty
+ end
+
+ it 'has a title' do
+ expect(subject.code_events.first[:title]).to eq('Awesome merge_request')
+ end
+
+ it 'has an iid' do
+ expect(subject.code_events.first[:iid]).to eq(context.iid.to_s)
+ end
+
+ it 'has a created_at timestamp' do
+ expect(subject.code_events.first[:created_at]).to end_with('ago')
+ end
+
+ it "has the author's URL" do
+ expect(subject.code_events.first[:author][:web_url]).not_to be_nil
+ end
+
+ it "has the author's avatar URL" do
+ expect(subject.code_events.first[:author][:avatar_url]).not_to be_nil
+ end
+
+ it "has the author's name" do
+ expect(subject.code_events.first[:author][:name]).to eq(MergeRequest.first.author.name)
+ end
+ end
+
+ describe '#test_events' do
+ let(:merge_request) { MergeRequest.first }
+ let!(:pipeline) do
+ create(:ci_pipeline,
+ ref: merge_request.source_branch,
+ sha: merge_request.diff_head_sha,
+ project: context.project)
+ end
+
+ before do
+ create(:ci_build, pipeline: pipeline, status: :success, author: user)
+ create(:ci_build, pipeline: pipeline, status: :success, author: user)
+
+ pipeline.run!
+ pipeline.succeed!
+ end
+
+ it 'has the name' do
+ expect(subject.test_events.first[:name]).not_to be_nil
+ end
+
+ it 'has the ID' do
+ expect(subject.test_events.first[:id]).not_to be_nil
+ end
+
+ it 'has the URL' do
+ expect(subject.test_events.first[:url]).not_to be_nil
+ end
+
+ it 'has the branch name' do
+ expect(subject.test_events.first[:branch]).not_to be_nil
+ end
+
+ it 'has the branch URL' do
+ expect(subject.test_events.first[:branch][:url]).not_to be_nil
+ end
+
+ it 'has the short SHA' do
+ expect(subject.test_events.first[:short_sha]).not_to be_nil
+ end
+
+ it 'has the commit URL' do
+ expect(subject.test_events.first[:commit_url]).not_to be_nil
+ end
+
+ it 'has the date' do
+ expect(subject.test_events.first[:date]).not_to be_nil
+ end
+
+ it 'has the total time' do
+ expect(subject.test_events.first[:total_time]).not_to be_empty
+ end
+ end
+
+ describe '#review_events' do
+ let!(:context) { create(:issue, project: project, created_at: 2.days.ago) }
+
+ it 'has the total time' do
+ expect(subject.review_events.first[:total_time]).not_to be_empty
+ end
+
+ it 'has a title' do
+ expect(subject.review_events.first[:title]).to eq('Awesome merge_request')
+ end
+
+ it 'has an iid' do
+ expect(subject.review_events.first[:iid]).to eq(context.iid.to_s)
+ end
+
+ it 'has the URL' do
+ expect(subject.review_events.first[:url]).not_to be_nil
+ end
+
+ it 'has a state' do
+ expect(subject.review_events.first[:state]).not_to be_nil
+ end
+
+ it 'has a created_at timestamp' do
+ expect(subject.review_events.first[:created_at]).not_to be_nil
+ end
+
+ it "has the author's URL" do
+ expect(subject.review_events.first[:author][:web_url]).not_to be_nil
+ end
+
+ it "has the author's avatar URL" do
+ expect(subject.review_events.first[:author][:avatar_url]).not_to be_nil
+ end
+
+ it "has the author's name" do
+ expect(subject.review_events.first[:author][:name]).to eq(MergeRequest.first.author.name)
+ end
+ end
+
+ describe '#staging_events' do
+ let(:merge_request) { MergeRequest.first }
+ let!(:pipeline) do
+ create(:ci_pipeline,
+ ref: merge_request.source_branch,
+ sha: merge_request.diff_head_sha,
+ project: context.project)
+ end
+
+ before do
+ create(:ci_build, pipeline: pipeline, status: :success, author: user)
+ create(:ci_build, pipeline: pipeline, status: :success, author: user)
+
+ pipeline.run!
+ pipeline.succeed!
+
+ merge_merge_requests_closing_issue(context)
+ deploy_master
+ end
+
+ it 'has the name' do
+ expect(subject.staging_events.first[:name]).not_to be_nil
+ end
+
+ it 'has the ID' do
+ expect(subject.staging_events.first[:id]).not_to be_nil
+ end
+
+ it 'has the URL' do
+ expect(subject.staging_events.first[:url]).not_to be_nil
+ end
+
+ it 'has the branch name' do
+ expect(subject.staging_events.first[:branch]).not_to be_nil
+ end
+
+ it 'has the branch URL' do
+ expect(subject.staging_events.first[:branch][:url]).not_to be_nil
+ end
+
+ it 'has the short SHA' do
+ expect(subject.staging_events.first[:short_sha]).not_to be_nil
+ end
+
+ it 'has the commit URL' do
+ expect(subject.staging_events.first[:commit_url]).not_to be_nil
+ end
+
+ it 'has the date' do
+ expect(subject.staging_events.first[:date]).not_to be_nil
+ end
+
+ it 'has the total time' do
+ expect(subject.staging_events.first[:total_time]).not_to be_empty
+ end
+
+ it "has the author's URL" do
+ expect(subject.staging_events.first[:author][:web_url]).not_to be_nil
+ end
+
+ it "has the author's avatar URL" do
+ expect(subject.staging_events.first[:author][:avatar_url]).not_to be_nil
+ end
+
+ it "has the author's name" do
+ expect(subject.staging_events.first[:author][:name]).to eq(MergeRequest.first.author.name)
+ end
+ end
+
+ describe '#production_events' do
+ let!(:context) { create(:issue, project: project, created_at: 2.days.ago) }
+
+ before do
+ merge_merge_requests_closing_issue(context)
+ deploy_master
+ end
+
+ it 'has the total time' do
+ expect(subject.production_events.first[:total_time]).not_to be_empty
+ end
+
+ it 'has a title' do
+ expect(subject.production_events.first[:title]).to eq(context.title)
+ end
+
+ it 'has the URL' do
+ expect(subject.production_events.first[:url]).not_to be_nil
+ end
+
+ it 'has an iid' do
+ expect(subject.production_events.first[:iid]).to eq(context.iid.to_s)
+ end
+
+ it 'has a created_at timestamp' do
+ expect(subject.production_events.first[:created_at]).to end_with('ago')
+ end
+
+ it "has the author's URL" do
+ expect(subject.production_events.first[:author][:web_url]).not_to be_nil
+ end
+
+ it "has the author's avatar URL" do
+ expect(subject.production_events.first[:author][:avatar_url]).not_to be_nil
+ end
+
+ it "has the author's name" do
+ expect(subject.production_events.first[:author][:name]).to eq(context.author.name)
+ end
+ end
+
+ def setup(context)
+ milestone = create(:milestone, project: project)
+ context.update(milestone: milestone)
+ mr = create_merge_request_closing_issue(context)
+
+ ProcessCommitWorker.new.perform(project.id, user.id, mr.commits.last.to_hash)
+ end
+end
diff --git a/spec/lib/gitlab/cycle_analytics/issue_event_spec.rb b/spec/lib/gitlab/cycle_analytics/issue_event_spec.rb
new file mode 100644
index 00000000000..1c5c308da7d
--- /dev/null
+++ b/spec/lib/gitlab/cycle_analytics/issue_event_spec.rb
@@ -0,0 +1,10 @@
+require 'spec_helper'
+require 'lib/gitlab/cycle_analytics/shared_event_spec'
+
+describe Gitlab::CycleAnalytics::IssueEvent do
+ it_behaves_like 'default query config' do
+ it 'has the default order' do
+ expect(event.order).to eq(event.start_time_attrs)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cycle_analytics/permissions_spec.rb b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb
new file mode 100644
index 00000000000..2d85e712db0
--- /dev/null
+++ b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb
@@ -0,0 +1,127 @@
+require 'spec_helper'
+
+describe Gitlab::CycleAnalytics::Permissions do
+ let(:project) { create(:empty_project, public_builds: false) }
+ let(:user) { create(:user) }
+
+ subject { described_class.get(user: user, project: project) }
+
+ context 'user with no relation to the project' do
+ it 'has no permissions to issue stage' do
+ expect(subject[:issue]).to eq(false)
+ end
+
+ it 'has no permissions to test stage' do
+ expect(subject[:test]).to eq(false)
+ end
+
+ it 'has no permissions to staging stage' do
+ expect(subject[:staging]).to eq(false)
+ end
+
+ it 'has no permissions to production stage' do
+ expect(subject[:production]).to eq(false)
+ end
+
+ it 'has no permissions to code stage' do
+ expect(subject[:code]).to eq(false)
+ end
+
+ it 'has no permissions to review stage' do
+ expect(subject[:review]).to eq(false)
+ end
+
+ it 'has no permissions to plan stage' do
+ expect(subject[:plan]).to eq(false)
+ end
+ end
+
+ context 'user is master' do
+ before do
+ project.team << [user, :master]
+ end
+
+ it 'has permissions to issue stage' do
+ expect(subject[:issue]).to eq(true)
+ end
+
+ it 'has permissions to test stage' do
+ expect(subject[:test]).to eq(true)
+ end
+
+ it 'has permissions to staging stage' do
+ expect(subject[:staging]).to eq(true)
+ end
+
+ it 'has permissions to production stage' do
+ expect(subject[:production]).to eq(true)
+ end
+
+ it 'has permissions to code stage' do
+ expect(subject[:code]).to eq(true)
+ end
+
+ it 'has permissions to review stage' do
+ expect(subject[:review]).to eq(true)
+ end
+
+ it 'has permissions to plan stage' do
+ expect(subject[:plan]).to eq(true)
+ end
+ end
+
+ context 'user has no build permissions' do
+ before do
+ project.team << [user, :guest]
+ end
+
+ it 'has permissions to issue stage' do
+ expect(subject[:issue]).to eq(true)
+ end
+
+ it 'has no permissions to test stage' do
+ expect(subject[:test]).to eq(false)
+ end
+
+ it 'has no permissions to staging stage' do
+ expect(subject[:staging]).to eq(false)
+ end
+ end
+
+ context 'user has no merge request permissions' do
+ before do
+ project.team << [user, :guest]
+ end
+
+ it 'has permissions to issue stage' do
+ expect(subject[:issue]).to eq(true)
+ end
+
+ it 'has no permissions to code stage' do
+ expect(subject[:code]).to eq(false)
+ end
+
+ it 'has no permissions to review stage' do
+ expect(subject[:review]).to eq(false)
+ end
+ end
+
+ context 'user has no issue permissions' do
+ before do
+ project.team << [user, :developer]
+ project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED)
+ end
+
+ it 'has permissions to code stage' do
+ expect(subject[:code]).to eq(true)
+ end
+
+ it 'has no permissions to issue stage' do
+ expect(subject[:issue]).to eq(false)
+ end
+
+ it 'has no permissions to production stage' do
+ expect(subject[:production]).to eq(false)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cycle_analytics/plan_event_spec.rb b/spec/lib/gitlab/cycle_analytics/plan_event_spec.rb
new file mode 100644
index 00000000000..4a5604115ec
--- /dev/null
+++ b/spec/lib/gitlab/cycle_analytics/plan_event_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+require 'lib/gitlab/cycle_analytics/shared_event_spec'
+
+describe Gitlab::CycleAnalytics::PlanEvent do
+ it_behaves_like 'default query config' do
+ it 'has the default order' do
+ expect(event.order).to eq(event.start_time_attrs)
+ end
+
+ context 'no commits' do
+ it 'does not blow up if there are no commits' do
+ allow_any_instance_of(Gitlab::CycleAnalytics::EventsQuery).to receive(:execute).and_return([{}])
+
+ expect { event.fetch }.not_to raise_error
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cycle_analytics/production_event_spec.rb b/spec/lib/gitlab/cycle_analytics/production_event_spec.rb
new file mode 100644
index 00000000000..ac17e3b4287
--- /dev/null
+++ b/spec/lib/gitlab/cycle_analytics/production_event_spec.rb
@@ -0,0 +1,10 @@
+require 'spec_helper'
+require 'lib/gitlab/cycle_analytics/shared_event_spec'
+
+describe Gitlab::CycleAnalytics::ProductionEvent do
+ it_behaves_like 'default query config' do
+ it 'has the default order' do
+ expect(event.order).to eq(event.start_time_attrs)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cycle_analytics/review_event_spec.rb b/spec/lib/gitlab/cycle_analytics/review_event_spec.rb
new file mode 100644
index 00000000000..1ff53aa0227
--- /dev/null
+++ b/spec/lib/gitlab/cycle_analytics/review_event_spec.rb
@@ -0,0 +1,10 @@
+require 'spec_helper'
+require 'lib/gitlab/cycle_analytics/shared_event_spec'
+
+describe Gitlab::CycleAnalytics::ReviewEvent do
+ it_behaves_like 'default query config' do
+ it 'has the default order' do
+ expect(event.order).to eq(event.start_time_attrs)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb b/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb
new file mode 100644
index 00000000000..7019e4c3351
--- /dev/null
+++ b/spec/lib/gitlab/cycle_analytics/shared_event_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+shared_examples 'default query config' do
+ let(:event) { described_class.new(project: double, options: {}) }
+
+ it 'has the start attributes' do
+ expect(event.start_time_attrs).not_to be_nil
+ end
+
+ it 'has the stage attribute' do
+ expect(event.stage).not_to be_nil
+ end
+
+ it 'has the end attributes' do
+ expect(event.end_time_attrs).not_to be_nil
+ end
+
+ it 'has the projection attributes' do
+ expect(event.projections).not_to be_nil
+ end
+end
diff --git a/spec/lib/gitlab/cycle_analytics/staging_event_spec.rb b/spec/lib/gitlab/cycle_analytics/staging_event_spec.rb
new file mode 100644
index 00000000000..4862d4765f2
--- /dev/null
+++ b/spec/lib/gitlab/cycle_analytics/staging_event_spec.rb
@@ -0,0 +1,10 @@
+require 'spec_helper'
+require 'lib/gitlab/cycle_analytics/shared_event_spec'
+
+describe Gitlab::CycleAnalytics::StagingEvent do
+ it_behaves_like 'default query config' do
+ it 'does not have the default order' do
+ expect(event.order).not_to eq(event.start_time_attrs)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cycle_analytics/test_event_spec.rb b/spec/lib/gitlab/cycle_analytics/test_event_spec.rb
new file mode 100644
index 00000000000..e249db69fc6
--- /dev/null
+++ b/spec/lib/gitlab/cycle_analytics/test_event_spec.rb
@@ -0,0 +1,10 @@
+require 'spec_helper'
+require 'lib/gitlab/cycle_analytics/shared_event_spec'
+
+describe Gitlab::CycleAnalytics::TestEvent do
+ it_behaves_like 'default query config' do
+ it 'does not have the default order' do
+ expect(event.order).not_to eq(event.start_time_attrs)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cycle_analytics/updater_spec.rb b/spec/lib/gitlab/cycle_analytics/updater_spec.rb
new file mode 100644
index 00000000000..eff54cd3692
--- /dev/null
+++ b/spec/lib/gitlab/cycle_analytics/updater_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+describe Gitlab::CycleAnalytics::Updater do
+ describe 'updates authors' do
+ let(:user) { create(:user) }
+ let(:events) { [{ 'author_id' => user.id }] }
+
+ it 'maps the correct user' do
+ described_class.update!(events, from: 'author_id', to: 'author', klass: User)
+
+ expect(events.first['author']).to eq(user)
+ end
+ end
+
+ describe 'updates builds' do
+ let(:build) { create(:ci_build) }
+ let(:events) { [{ 'id' => build.id }] }
+
+ it 'maps the correct build' do
+ described_class.update!(events, from: 'id', to: 'build', klass: ::Ci::Build)
+
+ expect(events.first['build']).to eq(build)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
new file mode 100644
index 00000000000..2a680f03476
--- /dev/null
+++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe Gitlab::Diff::FileCollection::MergeRequestDiff do
+ let(:merge_request) { create :merge_request }
+
+ it 'does not hightlight binary files' do
+ allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(double("text?" => false))
+
+ expect_any_instance_of(Gitlab::Diff::File).not_to receive(:highlighted_diff_lines)
+
+ described_class.new(merge_request.merge_request_diff, diff_options: nil).diff_files
+ end
+
+ it 'does not hightlight file if blob is not accessable' do
+ allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(nil)
+
+ expect_any_instance_of(Gitlab::Diff::File).not_to receive(:highlighted_diff_lines)
+
+ described_class.new(merge_request.merge_request_diff, diff_options: nil).diff_files
+ end
+end
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index 0650cb291e5..38475792d93 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -46,4 +46,28 @@ describe Gitlab::Diff::File, lib: true do
expect(diff_file.collapsed?).to eq(false)
end
end
+
+ describe '#old_content_commit' do
+ it 'returns base commit' do
+ old_content_commit = diff_file.old_content_commit
+
+ expect(old_content_commit.id).to eq('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9')
+ end
+ end
+
+ describe '#old_blob' do
+ it 'returns blob of commit of base commit' do
+ old_data = diff_file.old_blob.data
+
+ expect(old_data).to include('raise "System commands must be given as an array of strings"')
+ end
+ end
+
+ describe '#blob' do
+ it 'returns blob of new commit' do
+ data = diff_file.blob.data
+
+ expect(data).to include('raise RuntimeError, "System commands must be given as an array of strings"')
+ end
+ end
end
diff --git a/spec/lib/gitlab/email/reply_parser_spec.rb b/spec/lib/gitlab/email/reply_parser_spec.rb
index 6f8e9a4be64..c7a0139d32a 100644
--- a/spec/lib/gitlab/email/reply_parser_spec.rb
+++ b/spec/lib/gitlab/email/reply_parser_spec.rb
@@ -206,5 +206,9 @@ describe Gitlab::Email::ReplyParser, lib: true do
it "properly renders email reply from MS Outlook client" do
expect(test_parse_body(fixture_file("emails/outlook.eml"))).to eq("Microsoft Outlook 2010")
end
+
+ it "properly renders html-only email from MS Outlook" do
+ expect(test_parse_body(fixture_file("emails/outlook_html.eml"))).to eq("Microsoft Outlook 2010")
+ end
end
end
diff --git a/spec/lib/gitlab/file_detector_spec.rb b/spec/lib/gitlab/file_detector_spec.rb
new file mode 100644
index 00000000000..e5ba13bbaf8
--- /dev/null
+++ b/spec/lib/gitlab/file_detector_spec.rb
@@ -0,0 +1,59 @@
+require 'spec_helper'
+
+describe Gitlab::FileDetector do
+ describe '.types_in_paths' do
+ it 'returns the file types for the given paths' do
+ expect(described_class.types_in_paths(%w(README.md CHANGELOG VERSION VERSION))).
+ to eq(%i{readme changelog version})
+ end
+
+ it 'does not include unrecognized file paths' do
+ expect(described_class.types_in_paths(%w(README.md foo.txt))).
+ to eq(%i{readme})
+ end
+ end
+
+ describe '.type_of' do
+ it 'returns the type of a README file' do
+ expect(described_class.type_of('README.md')).to eq(:readme)
+ end
+
+ it 'returns the type of a changelog file' do
+ %w(CHANGELOG HISTORY CHANGES NEWS).each do |file|
+ expect(described_class.type_of(file)).to eq(:changelog)
+ end
+ end
+
+ it 'returns the type of a license file' do
+ %w(LICENSE LICENCE COPYING).each do |file|
+ expect(described_class.type_of(file)).to eq(:license)
+ end
+ end
+
+ it 'returns the type of a version file' do
+ expect(described_class.type_of('VERSION')).to eq(:version)
+ end
+
+ it 'returns the type of a .gitignore file' do
+ expect(described_class.type_of('.gitignore')).to eq(:gitignore)
+ end
+
+ it 'returns the type of a Koding config file' do
+ expect(described_class.type_of('.koding.yml')).to eq(:koding)
+ end
+
+ it 'returns the type of a GitLab CI config file' do
+ expect(described_class.type_of('.gitlab-ci.yml')).to eq(:gitlab_ci)
+ end
+
+ it 'returns the type of an avatar' do
+ %w(logo.gif logo.png logo.jpg).each do |file|
+ expect(described_class.type_of(file)).to eq(:avatar)
+ end
+ end
+
+ it 'returns nil for an unknown file' do
+ expect(described_class.type_of('foo.txt')).to be_nil
+ end
+ end
+end
diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
index f045463c1cb..f4703dc704f 100644
--- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
@@ -6,7 +6,7 @@ describe Gitlab::Gfm::ReferenceRewriter do
let(:new_project) { create(:project, name: 'new') }
let(:user) { create(:user) }
- before { old_project.team << [user, :guest] }
+ before { old_project.team << [user, :reporter] }
describe '#rewrite' do
subject do
@@ -29,7 +29,7 @@ describe Gitlab::Gfm::ReferenceRewriter do
context 'description with ignored elements' do
let(:text) do
"Hi. This references #1, but not `#2`\n" +
- '<pre>and not !1</pre>'
+ '<pre>and not !1</pre>'
end
it { is_expected.to include issue_first.to_reference(new_project) }
@@ -64,7 +64,7 @@ describe Gitlab::Gfm::ReferenceRewriter do
context 'description with project labels' do
let!(:label) { create(:label, id: 123, name: 'test', project: old_project) }
- let(:project_ref) { old_project.to_reference }
+ let(:project_ref) { old_project.to_reference(new_project) }
context 'label referenced by id' do
let(:text) { '#1 and ~123' }
@@ -80,7 +80,7 @@ describe Gitlab::Gfm::ReferenceRewriter do
context 'description with group labels' do
let(:old_group) { create(:group) }
let!(:group_label) { create(:group_label, id: 321, name: 'group label', group: old_group) }
- let(:project_ref) { old_project.to_reference }
+ let(:project_ref) { old_project.to_reference(new_project) }
before do
old_project.update(namespace: old_group)
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index 62aa212f1f6..f1d0a190002 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -66,6 +66,7 @@ describe Gitlab::GitAccess, lib: true do
context 'pull code' do
it { expect(subject.allowed?).to be_falsey }
+ it { expect(subject.message).to match(/You are not allowed to download code/) }
end
end
@@ -77,6 +78,7 @@ describe Gitlab::GitAccess, lib: true do
context 'pull code' do
it { expect(subject.allowed?).to be_falsey }
+ it { expect(subject.message).to match(/Your account has been blocked/) }
end
end
@@ -84,6 +86,29 @@ describe Gitlab::GitAccess, lib: true do
context 'pull code' do
it { expect(subject.allowed?).to be_falsey }
end
+
+ context 'when project is public' do
+ let(:public_project) { create(:project, :public) }
+ let(:guest_access) { Gitlab::GitAccess.new(nil, public_project, 'web', authentication_abilities: []) }
+ subject { guest_access.check('git-upload-pack', '_any') }
+
+ context 'when repository is enabled' do
+ it 'give access to download code' do
+ public_project.project_feature.update_attribute(:repository_access_level, ProjectFeature::ENABLED)
+
+ expect(subject.allowed?).to be_truthy
+ end
+ end
+
+ context 'when repository is disabled' do
+ it 'does not give access to download code' do
+ public_project.project_feature.update_attribute(:repository_access_level, ProjectFeature::DISABLED)
+
+ expect(subject.allowed?).to be_falsey
+ expect(subject.message).to match(/You are not allowed to download code/)
+ end
+ end
+ end
end
describe 'deploy key permissions' do
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index 576cda595bb..578db51631e 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -18,7 +18,7 @@ describe Gitlab::GitAccessWiki, lib: true do
project.team << [user, :developer]
end
- subject { access.push_access_check(changes) }
+ subject { access.check('git-receive-pack', changes) }
it { expect(subject.allowed?).to be_truthy }
end
@@ -26,4 +26,29 @@ describe Gitlab::GitAccessWiki, lib: true do
def changes
['6f6d7e7ed 570e7b2ab refs/heads/master']
end
+
+ describe '#download_access_check' do
+ subject { access.check('git-upload-pack', '_any') }
+
+ before do
+ project.team << [user, :developer]
+ end
+
+ context 'when wiki feature is enabled' do
+ it 'give access to download wiki code' do
+ project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::ENABLED)
+
+ expect(subject.allowed?).to be_truthy
+ end
+ end
+
+ context 'when wiki feature is disabled' do
+ it 'does not give access to download wiki code' do
+ project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED)
+
+ expect(subject.allowed?).to be_falsey
+ expect(subject.message).to match(/You are not allowed to download code/)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/github_import/branch_formatter_spec.rb b/spec/lib/gitlab/github_import/branch_formatter_spec.rb
index e5300dbba1e..462caa5b5fe 100644
--- a/spec/lib/gitlab/github_import/branch_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/branch_formatter_spec.rb
@@ -49,14 +49,20 @@ describe Gitlab::GithubImport::BranchFormatter, lib: true do
end
describe '#valid?' do
- it 'returns true when raw repo is present' do
+ it 'returns true when raw sha and ref are present' do
branch = described_class.new(project, double(raw))
expect(branch.valid?).to eq true
end
- it 'returns false when raw repo is blank' do
- branch = described_class.new(project, double(raw.merge(repo: nil)))
+ it 'returns false when raw sha is blank' do
+ branch = described_class.new(project, double(raw.merge(sha: nil)))
+
+ expect(branch.valid?).to eq false
+ end
+
+ it 'returns false when raw ref is blank' do
+ branch = described_class.new(project, double(raw.merge(ref: nil)))
expect(branch.valid?).to eq false
end
diff --git a/spec/lib/gitlab/github_import/importer_spec.rb b/spec/lib/gitlab/github_import/importer_spec.rb
index 7478f86bd28..9e027839f59 100644
--- a/spec/lib/gitlab/github_import/importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer_spec.rb
@@ -101,7 +101,6 @@ describe Gitlab::GithubImport::Importer, lib: true do
closed_at: nil,
merged_at: nil,
url: 'https://api.github.com/repos/octocat/Hello-World/pulls/1347',
- labels: [double(name: 'Label #3')],
)
end
@@ -156,9 +155,7 @@ describe Gitlab::GithubImport::Importer, lib: true do
message: 'The remote data could not be fully imported.',
errors: [
{ type: :label, url: "https://api.github.com/repos/octocat/Hello-World/labels/bug", errors: "Validation failed: Title can't be blank, Title is invalid" },
- { type: :issue, url: "https://api.github.com/repos/octocat/Hello-World/issues/1348", errors: "Validation failed: Title can't be blank, Title is too short (minimum is 0 characters)" },
- { type: :pull_request, url: "https://api.github.com/repos/octocat/Hello-World/pulls/1347", errors: "Invalid Repository. Use user/repo format." },
- { type: :pull_request, url: "https://api.github.com/repos/octocat/Hello-World/pulls/1347", errors: "Invalid Repository. Use user/repo format." },
+ { type: :issue, url: "https://api.github.com/repos/octocat/Hello-World/issues/1348", errors: "Validation failed: Title can't be blank" },
{ type: :wiki, errors: "Gitlab::Shell::Error" },
{ type: :release, url: 'https://api.github.com/repos/octocat/Hello-World/releases/2', errors: "Validation failed: Description can't be blank" }
]
diff --git a/spec/lib/gitlab/github_import/issue_formatter_spec.rb b/spec/lib/gitlab/github_import/issue_formatter_spec.rb
index c2f1f6b91a1..95339e2f128 100644
--- a/spec/lib/gitlab/github_import/issue_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/issue_formatter_spec.rb
@@ -144,20 +144,20 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
end
end
- describe '#valid?' do
+ describe '#pull_request?' do
context 'when mention a pull request' do
let(:raw_data) { double(base_data.merge(pull_request: double)) }
- it 'returns false' do
- expect(issue.valid?).to eq false
+ it 'returns true' do
+ expect(issue.pull_request?).to eq true
end
end
context 'when does not mention a pull request' do
let(:raw_data) { double(base_data.merge(pull_request: nil)) }
- it 'returns true' do
- expect(issue.valid?).to eq true
+ it 'returns false' do
+ expect(issue.pull_request?).to eq false
end
end
end
diff --git a/spec/lib/gitlab/identifier_spec.rb b/spec/lib/gitlab/identifier_spec.rb
index 47d6f1007d1..bb758a8a202 100644
--- a/spec/lib/gitlab/identifier_spec.rb
+++ b/spec/lib/gitlab/identifier_spec.rb
@@ -40,7 +40,7 @@ describe Gitlab::Identifier do
describe '#identify_using_commit' do
it "returns the User for an existing commit author's Email address" do
- commit = double(:commit, author_email: user.email)
+ commit = double(:commit, author: user, author_email: user.email)
expect(project).to receive(:commit).with('123').and_return(commit)
@@ -62,10 +62,9 @@ describe Gitlab::Identifier do
end
it 'caches the found users per Email' do
- commit = double(:commit, author_email: user.email)
+ commit = double(:commit, author: user, author_email: user.email)
expect(project).to receive(:commit).with('123').twice.and_return(commit)
- expect(User).to receive(:find_by).once.and_call_original
2.times do
expect(identifier.identify_using_commit(project, '123')).to eq(user)
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 02b11bd999a..9b49d6837c3 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -116,6 +116,7 @@ project:
- base_tags
- tag_taggings
- tags
+- chat_services
- creator
- group
- namespace
@@ -127,6 +128,7 @@ project:
- emails_on_push_service
- builds_email_service
- pipelines_email_service
+- mattermost_slash_commands_service
- irker_service
- pivotaltracker_service
- hipchat_service
@@ -134,7 +136,8 @@ project:
- assembla_service
- asana_service
- gemnasium_service
-- slack_service
+- slack_notification_service
+- mattermost_notification_service
- buildkite_service
- bamboo_service
- teamcity_service
@@ -145,6 +148,7 @@ project:
- bugzilla_service
- gitlab_issue_tracker_service
- external_wiki_service
+- kubernetes_service
- forked_project_link
- forked_from_project
- forked_project_links
@@ -184,8 +188,11 @@ project:
- environments
- deployments
- project_feature
+- authorized_users
+- project_authorizations
+- route
award_emoji:
- awardable
- user
priorities:
-- label \ No newline at end of file
+- label
diff --git a/spec/lib/gitlab/import_export/avatar_restorer_spec.rb b/spec/lib/gitlab/import_export/avatar_restorer_spec.rb
index 5ae178414cc..08a42fd27a2 100644
--- a/spec/lib/gitlab/import_export/avatar_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/avatar_restorer_spec.rb
@@ -1,12 +1,14 @@
require 'spec_helper'
describe Gitlab::ImportExport::AvatarRestorer, lib: true do
+ include UploadHelpers
+
let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: 'test') }
let(:project) { create(:empty_project) }
before do
allow_any_instance_of(described_class).to receive(:avatar_export_file)
- .and_return(Rails.root + "spec/fixtures/dk.png")
+ .and_return(uploaded_image_temp_path)
end
after do
diff --git a/spec/lib/gitlab/import_export/members_mapper_spec.rb b/spec/lib/gitlab/import_export/members_mapper_spec.rb
index 770e8b0c2f4..1cb02f8e318 100644
--- a/spec/lib/gitlab/import_export/members_mapper_spec.rb
+++ b/spec/lib/gitlab/import_export/members_mapper_spec.rb
@@ -2,9 +2,9 @@ require 'spec_helper'
describe Gitlab::ImportExport::MembersMapper, services: true do
describe 'map members' do
- let(:user) { create(:user) }
+ let(:user) { create(:user, authorized_projects_populated: true) }
let(:project) { create(:project, :public, name: 'searchable_project') }
- let(:user2) { create(:user) }
+ let(:user2) { create(:user, authorized_projects_populated: true) }
let(:exported_user_id) { 99 }
let(:exported_members) do
[{
@@ -67,5 +67,12 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
expect(ProjectMember.find_by_invite_email('invite@test.com')).not_to be_nil
end
+
+ it 'authorizes the users to the project' do
+ members_mapper.map
+
+ expect(user.authorized_project?(project)).to be true
+ expect(user2.authorized_project?(project)).to be true
+ end
end
end
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 07a2c316899..78d6b2c5032 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -78,6 +78,7 @@ Milestone:
- project_id
- description
- due_date
+- start_date
- created_at
- updated_at
- state
@@ -258,6 +259,7 @@ Service:
- template
- push_events
- issues_events
+- commit_events
- merge_requests_events
- tag_push_events
- note_events
@@ -339,4 +341,4 @@ LabelPriority:
- label_id
- priority
- created_at
-- updated_at \ No newline at end of file
+- updated_at
diff --git a/spec/lib/gitlab/ldap/config_spec.rb b/spec/lib/gitlab/ldap/config_spec.rb
index 835853a83a4..1a6803e01c3 100644
--- a/spec/lib/gitlab/ldap/config_spec.rb
+++ b/spec/lib/gitlab/ldap/config_spec.rb
@@ -1,20 +1,132 @@
require 'spec_helper'
describe Gitlab::LDAP::Config, lib: true do
- let(:config) { Gitlab::LDAP::Config.new provider }
- let(:provider) { 'ldapmain' }
+ include LdapHelpers
+
+ let(:config) { Gitlab::LDAP::Config.new('ldapmain') }
describe '#initalize' do
it 'requires a provider' do
expect{ Gitlab::LDAP::Config.new }.to raise_error ArgumentError
end
- it "works" do
+ it 'works' do
expect(config).to be_a described_class
end
- it "raises an error if a unknow provider is used" do
+ it 'raises an error if a unknown provider is used' do
expect{ Gitlab::LDAP::Config.new 'unknown' }.to raise_error(RuntimeError)
end
end
+
+ describe '#adapter_options' do
+ it 'constructs basic options' do
+ stub_ldap_config(
+ options: {
+ 'host' => 'ldap.example.com',
+ 'port' => 386,
+ 'method' => 'plain'
+ }
+ )
+
+ expect(config.adapter_options).to eq(
+ host: 'ldap.example.com',
+ port: 386,
+ encryption: nil
+ )
+ end
+
+ it 'includes authentication options when auth is configured' do
+ stub_ldap_config(
+ options: {
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'method' => 'ssl',
+ 'bind_dn' => 'uid=admin,dc=example,dc=com',
+ 'password' => 'super_secret'
+ }
+ )
+
+ expect(config.adapter_options).to eq(
+ host: 'ldap.example.com',
+ port: 686,
+ encryption: :simple_tls,
+ auth: {
+ method: :simple,
+ username: 'uid=admin,dc=example,dc=com',
+ password: 'super_secret'
+ }
+ )
+ end
+ end
+
+ describe '#omniauth_options' do
+ it 'constructs basic options' do
+ stub_ldap_config(
+ options: {
+ 'host' => 'ldap.example.com',
+ 'port' => 386,
+ 'base' => 'ou=users,dc=example,dc=com',
+ 'method' => 'plain',
+ 'uid' => 'uid'
+ }
+ )
+
+ expect(config.omniauth_options).to include(
+ host: 'ldap.example.com',
+ port: 386,
+ base: 'ou=users,dc=example,dc=com',
+ method: 'plain',
+ filter: '(uid=%{username})'
+ )
+ expect(config.omniauth_options.keys).not_to include(:bind_dn, :password)
+ end
+
+ it 'includes authentication options when auth is configured' do
+ stub_ldap_config(
+ options: {
+ 'uid' => 'sAMAccountName',
+ 'user_filter' => '(memberOf=cn=group1,ou=groups,dc=example,dc=com)',
+ 'bind_dn' => 'uid=admin,dc=example,dc=com',
+ 'password' => 'super_secret'
+ }
+ )
+
+ expect(config.omniauth_options).to include(
+ filter: '(&(sAMAccountName=%{username})(memberOf=cn=group1,ou=groups,dc=example,dc=com))',
+ bind_dn: 'uid=admin,dc=example,dc=com',
+ password: 'super_secret'
+ )
+ end
+ end
+
+ describe '#has_auth?' do
+ it 'is true when password is set' do
+ stub_ldap_config(
+ options: {
+ 'bind_dn' => 'uid=admin,dc=example,dc=com',
+ 'password' => 'super_secret'
+ }
+ )
+
+ expect(config.has_auth?).to be_truthy
+ end
+
+ it 'is true when bind_dn is set and password is empty' do
+ stub_ldap_config(
+ options: {
+ 'bind_dn' => 'uid=admin,dc=example,dc=com',
+ 'password' => ''
+ }
+ )
+
+ expect(config.has_auth?).to be_truthy
+ end
+
+ it 'is false when password and bind_dn are not set' do
+ stub_ldap_config(options: { 'bind_dn' => nil, 'password' => nil })
+
+ expect(config.has_auth?).to be_falsey
+ end
+ end
end
diff --git a/spec/lib/gitlab/middleware/go_spec.rb b/spec/lib/gitlab/middleware/go_spec.rb
index 117a15264da..fd3769d75b5 100644
--- a/spec/lib/gitlab/middleware/go_spec.rb
+++ b/spec/lib/gitlab/middleware/go_spec.rb
@@ -22,7 +22,7 @@ describe Gitlab::Middleware::Go, lib: true do
resp = middleware.call(env)
expect(resp[0]).to eq(200)
expect(resp[1]['Content-Type']).to eq('text/html')
- expected_body = "<!DOCTYPE html><html><head><meta content='localhost/group/project git http://localhost/group/project.git' name='go-import'></head></html>\n"
+ expected_body = "<!DOCTYPE html><html><head><meta content='#{Gitlab.config.gitlab.host}/group/project git http://#{Gitlab.config.gitlab.host}/group/project.git' name='go-import'></head></html>\n"
expect(resp[2].body).to eq([expected_body])
end
end
diff --git a/spec/lib/gitlab/middleware/multipart_spec.rb b/spec/lib/gitlab/middleware/multipart_spec.rb
new file mode 100644
index 00000000000..ab1ab22795c
--- /dev/null
+++ b/spec/lib/gitlab/middleware/multipart_spec.rb
@@ -0,0 +1,74 @@
+require 'spec_helper'
+
+require 'tempfile'
+
+describe Gitlab::Middleware::Multipart do
+ let(:app) { double(:app) }
+ let(:middleware) { described_class.new(app) }
+
+ it 'opens top-level files' do
+ Tempfile.open('top-level') do |tempfile|
+ env = post_env({ 'file' => tempfile.path }, { 'file.name' => 'filename' }, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+
+ expect(app).to receive(:call) do |env|
+ file = Rack::Request.new(env).params['file']
+ expect(file).to be_a(File)
+ expect(file.path).to eq(tempfile.path)
+ end
+
+ middleware.call(env)
+ end
+ end
+
+ it 'rejects headers signed with the wrong secret' do
+ env = post_env({ 'file' => '/var/empty/nonesuch' }, {}, 'x' * 32, 'gitlab-workhorse')
+
+ expect { middleware.call(env) }.to raise_error(JWT::VerificationError)
+ end
+
+ it 'rejects headers signed with the wrong issuer' do
+ env = post_env({ 'file' => '/var/empty/nonesuch' }, {}, Gitlab::Workhorse.secret, 'acme-inc')
+
+ expect { middleware.call(env) }.to raise_error(JWT::InvalidIssuerError)
+ end
+
+ it 'opens files one level deep' do
+ Tempfile.open('one-level') do |tempfile|
+ in_params = { 'user' => { 'avatar' => { '.name' => 'filename' } } }
+ env = post_env({ 'user[avatar]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+
+ expect(app).to receive(:call) do |env|
+ file = Rack::Request.new(env).params['user']['avatar']
+ expect(file).to be_a(File)
+ expect(file.path).to eq(tempfile.path)
+ end
+
+ middleware.call(env)
+ end
+ end
+
+ it 'opens files two levels deep' do
+ Tempfile.open('two-levels') do |tempfile|
+ in_params = { 'project' => { 'milestone' => { 'themesong' => { '.name' => 'filename' } } } }
+ env = post_env({ 'project[milestone][themesong]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+
+ expect(app).to receive(:call) do |env|
+ file = Rack::Request.new(env).params['project']['milestone']['themesong']
+ expect(file).to be_a(File)
+ expect(file.path).to eq(tempfile.path)
+ end
+
+ middleware.call(env)
+ end
+ end
+
+ def post_env(rewritten_fields, params, secret, issuer)
+ token = JWT.encode({ 'iss' => issuer, 'rewritten_fields' => rewritten_fields }, secret, 'HS256')
+ Rack::MockRequest.env_for(
+ '/',
+ method: 'post',
+ params: params,
+ described_class::RACK_ENV_KEY => token
+ )
+ end
+end
diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb
index 78c669e8fa5..fc9e1cb430a 100644
--- a/spec/lib/gitlab/o_auth/user_spec.rb
+++ b/spec/lib/gitlab/o_auth/user_spec.rb
@@ -137,11 +137,12 @@ describe Gitlab::OAuth::User, lib: true do
allow(ldap_user).to receive(:username) { uid }
allow(ldap_user).to receive(:email) { ['johndoe@example.com', 'john2@example.com'] }
allow(ldap_user).to receive(:dn) { 'uid=user1,ou=People,dc=example' }
- allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
end
context "and no account for the LDAP user" do
it "creates a user with dual LDAP and omniauth identities" do
+ allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
+
oauth_user.save
expect(gl_user).to be_valid
@@ -159,6 +160,8 @@ describe Gitlab::OAuth::User, lib: true do
context "and LDAP user has an account already" do
let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') }
it "adds the omniauth identity to the LDAP account" do
+ allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
+
oauth_user.save
expect(gl_user).to be_valid
@@ -172,6 +175,24 @@ describe Gitlab::OAuth::User, lib: true do
])
end
end
+
+ context 'when an LDAP person is not found by uid' do
+ it 'tries to find an LDAP person by DN and adds the omniauth identity to the user' do
+ allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(nil)
+ allow(Gitlab::LDAP::Person).to receive(:find_by_dn).and_return(ldap_user)
+
+ oauth_user.save
+
+ identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
+ expect(identities_as_hash)
+ .to match_array(
+ [
+ { provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' },
+ { provider: 'twitter', extern_uid: uid }
+ ]
+ )
+ end
+ end
end
context "and no corresponding LDAP person" do
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index 29abb4d4d07..14ee386dba6 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -6,22 +6,73 @@ describe Gitlab::ProjectSearchResults, lib: true do
let(:query) { 'hello world' }
describe 'initialize with empty ref' do
- let(:results) { Gitlab::ProjectSearchResults.new(user, project, query, '') }
+ let(:results) { described_class.new(user, project, query, '') }
it { expect(results.project).to eq(project) }
- it { expect(results.repository_ref).to be_nil }
it { expect(results.query).to eq('hello world') }
end
describe 'initialize with ref' do
let(:ref) { 'refs/heads/test' }
- let(:results) { Gitlab::ProjectSearchResults.new(user, project, query, ref) }
+ let(:results) { described_class.new(user, project, query, ref) }
it { expect(results.project).to eq(project) }
it { expect(results.repository_ref).to eq(ref) }
it { expect(results.query).to eq('hello world') }
end
+ describe 'blob search' do
+ let(:results) { described_class.new(user, project, 'files').objects('blobs') }
+
+ it 'finds by name' do
+ expect(results).to include(["files/images/wm.svg", nil])
+ end
+
+ it 'finds by content' do
+ blob = results.select { |result| result.first == "CHANGELOG" }.flatten.last
+
+ expect(blob.filename).to eq("CHANGELOG")
+ end
+
+ describe 'parsing results' do
+ let(:results) { project.repository.search_files_by_content('feature', 'master') }
+ let(:search_result) { results.first }
+
+ subject { described_class.parse_search_result(search_result) }
+
+ it "returns a valid OpenStruct object" do
+ is_expected.to be_an OpenStruct
+ expect(subject.filename).to eq('CHANGELOG')
+ expect(subject.basename).to eq('CHANGELOG')
+ expect(subject.ref).to eq('master')
+ expect(subject.startline).to eq(188)
+ expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n")
+ end
+
+ context "when filename has extension" do
+ let(:search_result) { "master:CONTRIBUTE.md:5:- [Contribute to GitLab](#contribute-to-gitlab)\n" }
+
+ it { expect(subject.filename).to eq('CONTRIBUTE.md') }
+ it { expect(subject.basename).to eq('CONTRIBUTE') }
+ end
+
+ context "when file under directory" do
+ let(:search_result) { "master:a/b/c.md:5:a b c\n" }
+
+ it { expect(subject.filename).to eq('a/b/c.md') }
+ it { expect(subject.basename).to eq('a/b/c') }
+ end
+ end
+ end
+
+ it 'does not list issues on private projects' do
+ issue = create(:issue, project: project)
+
+ results = described_class.new(user, project, issue.title)
+
+ expect(results.objects('issues')).not_to include issue
+ end
+
describe 'confidential issues' do
let(:query) { 'issue' }
let(:author) { create(:user) }
@@ -29,6 +80,7 @@ describe Gitlab::ProjectSearchResults, lib: true do
let(:non_member) { create(:user) }
let(:member) { create(:user) }
let(:admin) { create(:admin) }
+ let(:project) { create(:empty_project, :internal) }
let!(:issue) { create(:issue, project: project, title: 'Issue 1') }
let!(:security_issue_1) { create(:issue, :confidential, project: project, title: 'Security issue 1', author: author) }
let!(:security_issue_2) { create(:issue, :confidential, title: 'Security issue 2', project: project, assignee: assignee) }
@@ -66,7 +118,7 @@ describe Gitlab::ProjectSearchResults, lib: true do
end
it 'lists project confidential issues for assignee' do
- results = described_class.new(assignee, project.id, query)
+ results = described_class.new(assignee, project, query)
issues = results.objects('issues')
expect(issues).to include issue
@@ -97,4 +149,33 @@ describe Gitlab::ProjectSearchResults, lib: true do
expect(results.issues_count).to eq 3
end
end
+
+ describe 'notes search' do
+ it 'lists notes' do
+ project = create(:empty_project, :public)
+ note = create(:note, project: project)
+
+ results = described_class.new(user, project, note.note)
+
+ expect(results.objects('notes')).to include note
+ end
+
+ it "doesn't list issue notes when access is restricted" do
+ project = create(:empty_project, :public, issues_access_level: ProjectFeature::PRIVATE)
+ note = create(:note_on_issue, project: project)
+
+ results = described_class.new(user, project, note.note)
+
+ expect(results.objects('notes')).not_to include note
+ end
+
+ it "doesn't list merge_request notes when access is restricted" do
+ project = create(:empty_project, :public, merge_requests_access_level: ProjectFeature::PRIVATE)
+ note = create(:note_on_merge_request, project: project)
+
+ results = described_class.new(user, project, note.note)
+
+ expect(results.objects('notes')).not_to include note
+ end
+ end
end
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index 7b4ccc83915..bf0ab9635fd 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -3,6 +3,8 @@ require 'spec_helper'
describe Gitlab::ReferenceExtractor, lib: true do
let(:project) { create(:project) }
+ before { project.team << [project.creator, :developer] }
+
subject { Gitlab::ReferenceExtractor.new(project, project.creator) }
it 'accesses valid user objects' do
@@ -42,7 +44,6 @@ describe Gitlab::ReferenceExtractor, lib: true do
end
it 'accesses valid issue objects' do
- project.team << [project.creator, :developer]
@i0 = create(:issue, project: project)
@i1 = create(:issue, project: project)
diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb
index c51b10bdc69..c78cd30157e 100644
--- a/spec/lib/gitlab/regex_spec.rb
+++ b/spec/lib/gitlab/regex_spec.rb
@@ -29,4 +29,20 @@ describe Gitlab::Regex, lib: true do
describe 'file path regex' do
it { expect('foo@/bar').to match(Gitlab::Regex.file_path_regex) }
end
+
+ describe 'environment slug regex' do
+ def be_matched
+ match(Gitlab::Regex.environment_slug_regex)
+ end
+
+ it { expect('foo').to be_matched }
+ it { expect('foo-1').to be_matched }
+
+ it { expect('FOO').not_to be_matched }
+ it { expect('foo/1').not_to be_matched }
+ it { expect('foo.1').not_to be_matched }
+ it { expect('foo*1').not_to be_matched }
+ it { expect('9foo').not_to be_matched }
+ it { expect('foo-').not_to be_matched }
+ end
end
diff --git a/spec/lib/gitlab/routing_spec.rb b/spec/lib/gitlab/routing_spec.rb
new file mode 100644
index 00000000000..01d5acfc15b
--- /dev/null
+++ b/spec/lib/gitlab/routing_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe Gitlab::Routing do
+ context 'when module is included' do
+ subject do
+ Class.new.include(described_class).new
+ end
+
+ it 'makes it possible to access url helpers' do
+ expect(subject).to respond_to(:namespace_project_path)
+ end
+ end
+
+ context 'when module is not included' do
+ subject do
+ Class.new.include(described_class.url_helpers).new
+ end
+
+ it 'exposes url helpers module through a method' do
+ expect(subject).to respond_to(:namespace_project_path)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb
index dfbefad6367..9614aad3e73 100644
--- a/spec/lib/gitlab/search_results_spec.rb
+++ b/spec/lib/gitlab/search_results_spec.rb
@@ -12,35 +12,57 @@ describe Gitlab::SearchResults do
let!(:milestone) { create(:milestone, project: project, title: 'foo') }
let(:results) { described_class.new(user, Project.all, 'foo') }
- describe '#projects_count' do
- it 'returns the total amount of projects' do
- expect(results.projects_count).to eq(1)
+ context 'as a user with access' do
+ before do
+ project.team << [user, :developer]
end
- end
- describe '#issues_count' do
- it 'returns the total amount of issues' do
- expect(results.issues_count).to eq(1)
+ describe '#projects_count' do
+ it 'returns the total amount of projects' do
+ expect(results.projects_count).to eq(1)
+ end
end
- end
- describe '#merge_requests_count' do
- it 'returns the total amount of merge requests' do
- expect(results.merge_requests_count).to eq(1)
+ describe '#issues_count' do
+ it 'returns the total amount of issues' do
+ expect(results.issues_count).to eq(1)
+ end
+ end
+
+ describe '#merge_requests_count' do
+ it 'returns the total amount of merge requests' do
+ expect(results.merge_requests_count).to eq(1)
+ end
end
- end
- describe '#milestones_count' do
- it 'returns the total amount of milestones' do
- expect(results.milestones_count).to eq(1)
+ describe '#milestones_count' do
+ it 'returns the total amount of milestones' do
+ expect(results.milestones_count).to eq(1)
+ end
end
+
+ it 'includes merge requests from source and target projects' do
+ forked_project = create(:empty_project, forked_from_project: project)
+ merge_request_2 = create(:merge_request, target_project: project, source_project: forked_project, title: 'foo')
+
+ results = described_class.new(user, Project.where(id: forked_project.id), 'foo')
+
+ expect(results.objects('merge_requests')).to include merge_request_2
+ end
+ end
+
+ it 'does not list issues on private projects' do
+ private_project = create(:empty_project, :private)
+ issue = create(:issue, project: private_project, title: 'foo')
+
+ expect(results.objects('issues')).not_to include issue
end
describe 'confidential issues' do
- let(:project_1) { create(:empty_project) }
- let(:project_2) { create(:empty_project) }
- let(:project_3) { create(:empty_project) }
- let(:project_4) { create(:empty_project) }
+ let(:project_1) { create(:empty_project, :internal) }
+ let(:project_2) { create(:empty_project, :internal) }
+ let(:project_3) { create(:empty_project, :internal) }
+ let(:project_4) { create(:empty_project, :internal) }
let(:query) { 'issue' }
let(:limit_projects) { Project.where(id: [project_1.id, project_2.id, project_3.id]) }
let(:author) { create(:user) }
@@ -139,4 +161,11 @@ describe Gitlab::SearchResults do
expect(results.issues_count).to eq 5
end
end
+
+ it 'does not list merge requests on projects with limited access' do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+
+ expect(results.objects('merge_requests')).not_to include merge_request
+ end
end
diff --git a/spec/lib/gitlab/sidekiq_throttler_spec.rb b/spec/lib/gitlab/sidekiq_throttler_spec.rb
new file mode 100644
index 00000000000..ff32e0e699d
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_throttler_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe Gitlab::SidekiqThrottler do
+ before do
+ Sidekiq.options[:concurrency] = 35
+
+ stub_application_setting(
+ sidekiq_throttling_enabled: true,
+ sidekiq_throttling_factor: 0.1,
+ sidekiq_throttling_queues: %w[build project_cache]
+ )
+ end
+
+ describe '#execute!' do
+ it 'sets limits on the selected queues' do
+ Gitlab::SidekiqThrottler.execute!
+
+ expect(Sidekiq::Queue['build'].limit).to eq 4
+ expect(Sidekiq::Queue['project_cache'].limit).to eq 4
+ end
+
+ it 'does not set limits on other queues' do
+ Gitlab::SidekiqThrottler.execute!
+
+ expect(Sidekiq::Queue['merge'].limit).to be_nil
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sql/union_spec.rb b/spec/lib/gitlab/sql/union_spec.rb
index 0cdbab87544..849edb09476 100644
--- a/spec/lib/gitlab/sql/union_spec.rb
+++ b/spec/lib/gitlab/sql/union_spec.rb
@@ -1,16 +1,26 @@
require 'spec_helper'
describe Gitlab::SQL::Union, lib: true do
+ let(:relation_1) { User.where(email: 'alice@example.com').select(:id) }
+ let(:relation_2) { User.where(email: 'bob@example.com').select(:id) }
+
+ def to_sql(relation)
+ relation.reorder(nil).to_sql
+ end
+
describe '#to_sql' do
it 'returns a String joining relations together using a UNION' do
- rel1 = User.where(email: 'alice@example.com')
- rel2 = User.where(email: 'bob@example.com')
- union = described_class.new([rel1, rel2])
+ union = described_class.new([relation_1, relation_2])
+
+ expect(union.to_sql).to eq("#{to_sql(relation_1)}\nUNION\n#{to_sql(relation_2)}")
+ end
- sql1 = rel1.reorder(nil).to_sql
- sql2 = rel2.reorder(nil).to_sql
+ it 'skips Model.none segements' do
+ empty_relation = User.none
+ union = described_class.new([empty_relation, relation_1, relation_2])
- expect(union.to_sql).to eq("#{sql1}\nUNION\n#{sql2}")
+ expect{User.where("users.id IN (#{union.to_sql})").to_a}.not_to raise_error
+ expect(union.to_sql).to eq("#{to_sql(relation_1)}\nUNION\n#{to_sql(relation_2)}")
end
end
end
diff --git a/spec/lib/light_url_builder_spec.rb b/spec/lib/light_url_builder_spec.rb
new file mode 100644
index 00000000000..a826b24419a
--- /dev/null
+++ b/spec/lib/light_url_builder_spec.rb
@@ -0,0 +1,119 @@
+require 'spec_helper'
+
+describe Gitlab::UrlBuilder, lib: true do
+ describe '.build' do
+ context 'when passing a Commit' do
+ it 'returns a proper URL' do
+ commit = build_stubbed(:commit)
+
+ url = described_class.build(commit)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.path_with_namespace}/commit/#{commit.id}"
+ end
+ end
+
+ context 'when passing an Issue' do
+ it 'returns a proper URL' do
+ issue = build_stubbed(:issue, iid: 42)
+
+ url = described_class.build(issue)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}"
+ end
+ end
+
+ context 'when passing a MergeRequest' do
+ it 'returns a proper URL' do
+ merge_request = build_stubbed(:merge_request, iid: 42)
+
+ url = described_class.build(merge_request)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}"
+ end
+ end
+
+ context 'when passing a Note' do
+ context 'on a Commit' do
+ it 'returns a proper URL' do
+ note = build_stubbed(:note_on_commit)
+
+ url = described_class.build(note)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}"
+ end
+ end
+
+ context 'on a Commit Diff' do
+ it 'returns a proper URL' do
+ note = build_stubbed(:diff_note_on_commit)
+
+ url = described_class.build(note)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.path_with_namespace}/commit/#{note.commit_id}#note_#{note.id}"
+ end
+ end
+
+ context 'on an Issue' do
+ it 'returns a proper URL' do
+ issue = create(:issue, iid: 42)
+ note = build_stubbed(:note_on_issue, noteable: issue)
+
+ url = described_class.build(note)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.path_with_namespace}/issues/#{issue.iid}#note_#{note.id}"
+ end
+ end
+
+ context 'on a MergeRequest' do
+ it 'returns a proper URL' do
+ merge_request = create(:merge_request, iid: 42)
+ note = build_stubbed(:note_on_merge_request, noteable: merge_request)
+
+ url = described_class.build(note)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}"
+ end
+ end
+
+ context 'on a MergeRequest Diff' do
+ it 'returns a proper URL' do
+ merge_request = create(:merge_request, iid: 42)
+ note = build_stubbed(:diff_note_on_merge_request, noteable: merge_request)
+
+ url = described_class.build(note)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.path_with_namespace}/merge_requests/#{merge_request.iid}#note_#{note.id}"
+ end
+ end
+
+ context 'on a ProjectSnippet' do
+ it 'returns a proper URL' do
+ project_snippet = create(:project_snippet)
+ note = build_stubbed(:note_on_project_snippet, noteable: project_snippet)
+
+ url = described_class.build(note)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{project_snippet.project.path_with_namespace}/snippets/#{note.noteable_id}#note_#{note.id}"
+ end
+ end
+
+ context 'on another object' do
+ it 'returns a proper URL' do
+ project = build_stubbed(:project)
+
+ expect { described_class.build(project) }.
+ to raise_error(NotImplementedError, 'No URL builder defined for Project')
+ end
+ end
+ end
+
+ context 'when passing a WikiPage' do
+ it 'returns a proper URL' do
+ wiki_page = build(:wiki_page)
+ url = described_class.build(wiki_page)
+
+ expect(url).to eq "#{Gitlab.config.gitlab.url}#{wiki_page.wiki.wiki_base_path}/#{wiki_page.slug}"
+ end
+ end
+ end
+end
diff --git a/spec/lib/mattermost/session_spec.rb b/spec/lib/mattermost/session_spec.rb
new file mode 100644
index 00000000000..3c2eddbd221
--- /dev/null
+++ b/spec/lib/mattermost/session_spec.rb
@@ -0,0 +1,99 @@
+require 'spec_helper'
+
+describe Mattermost::Session, type: :request do
+ let(:user) { create(:user) }
+
+ let(:gitlab_url) { "http://gitlab.com" }
+ let(:mattermost_url) { "http://mattermost.com" }
+
+ subject { described_class.new(user) }
+
+ # Needed for doorkeeper to function
+ it { is_expected.to respond_to(:current_resource_owner) }
+ it { is_expected.to respond_to(:request) }
+ it { is_expected.to respond_to(:authorization) }
+ it { is_expected.to respond_to(:strategy) }
+
+ before do
+ described_class.base_uri(mattermost_url)
+ end
+
+ describe '#with session' do
+ let(:location) { 'http://location.tld' }
+ let!(:stub) do
+ WebMock.stub_request(:get, "#{mattermost_url}/api/v3/oauth/gitlab/login").
+ to_return(headers: { 'location' => location }, status: 307)
+ end
+
+ context 'without oauth uri' do
+ it 'makes a request to the oauth uri' do
+ expect { subject.with_session }.to raise_error(Mattermost::NoSessionError)
+ end
+ end
+
+ context 'with oauth_uri' do
+ let!(:doorkeeper) do
+ Doorkeeper::Application.create(
+ name: "GitLab Mattermost",
+ redirect_uri: "#{mattermost_url}/signup/gitlab/complete\n#{mattermost_url}/login/gitlab/complete",
+ scopes: "")
+ end
+
+ context 'without token_uri' do
+ it 'can not create a session' do
+ expect do
+ subject.with_session
+ end.to raise_error(Mattermost::NoSessionError)
+ end
+ end
+
+ context 'with token_uri' do
+ let(:state) { "state" }
+ let(:params) do
+ { response_type: "code",
+ client_id: doorkeeper.uid,
+ redirect_uri: "#{mattermost_url}/signup/gitlab/complete",
+ state: state }
+ end
+ let(:location) do
+ "#{gitlab_url}/oauth/authorize?#{URI.encode_www_form(params)}"
+ end
+
+ before do
+ WebMock.stub_request(:get, "#{mattermost_url}/signup/gitlab/complete").
+ with(query: hash_including({ 'state' => state })).
+ to_return do |request|
+ post "/oauth/token",
+ client_id: doorkeeper.uid,
+ client_secret: doorkeeper.secret,
+ redirect_uri: params[:redirect_uri],
+ grant_type: 'authorization_code',
+ code: request.uri.query_values['code']
+
+ if response.status == 200
+ { headers: { 'token' => 'thisworksnow' }, status: 202 }
+ end
+ end
+
+ WebMock.stub_request(:post, "#{mattermost_url}/api/v3/users/logout").
+ to_return(headers: { Authorization: 'token thisworksnow' }, status: 200)
+ end
+
+ it 'can setup a session' do
+ subject.with_session do |session|
+ end
+
+ expect(subject.token).not_to be_nil
+ end
+
+ it 'returns the value of the block' do
+ result = subject.with_session do |session|
+ "value"
+ end
+
+ expect(result).to eq("value")
+ end
+ end
+ end
+ end
+end