summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-12-07 21:10:08 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-12-07 21:10:08 +0000
commit3a966afb3ea2ef7a98bdc389e0dc906ef4bf0273 (patch)
treee22ca72e41a6d2eaca58ac9cc1390e5f8114ac1f /spec
parent39d41e02dca2139d0bbd88165affd818c9c82fb6 (diff)
downloadgitlab-ce-3a966afb3ea2ef7a98bdc389e0dc906ef4bf0273.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/confirmations_controller_spec.rb80
-rw-r--r--spec/controllers/projects/feature_flags_controller_spec.rb34
-rw-r--r--spec/controllers/search_controller_spec.rb2
-rw-r--r--spec/controllers/users_controller_spec.rb89
-rw-r--r--spec/features/explore/user_explores_projects_spec.rb11
-rw-r--r--spec/features/markdown/mermaid_spec.rb65
-rw-r--r--spec/finders/projects_finder_spec.rb23
-rw-r--r--spec/finders/starred_projects_finder_spec.rb59
-rw-r--r--spec/helpers/operations_helper_spec.rb34
-rw-r--r--spec/lib/banzai/filter/ascii_doc_sanitization_filter_spec.rb57
-rw-r--r--spec/lib/gitlab/asciidoc/html5_converter_spec.rb15
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb21
-rw-r--r--spec/lib/gitlab/rack_attack/user_allowlist_spec.rb33
-rw-r--r--spec/lib/gitlab/rack_attack_spec.rb17
-rw-r--r--spec/migrations/remove_orphan_service_hooks_spec.rb26
-rw-r--r--spec/requests/api/graphql/user/starred_projects_query_spec.rb27
-rw-r--r--spec/requests/api/graphql/user_query_spec.rb48
-rw-r--r--spec/requests/api/projects_spec.rb45
-rw-r--r--spec/support/shared_examples/requests/rack_attack_shared_examples.rb38
-rw-r--r--spec/validators/zoom_url_validator_spec.rb36
20 files changed, 696 insertions, 64 deletions
diff --git a/spec/controllers/confirmations_controller_spec.rb b/spec/controllers/confirmations_controller_spec.rb
new file mode 100644
index 00000000000..49a39f257fe
--- /dev/null
+++ b/spec/controllers/confirmations_controller_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ConfirmationsController do
+ include DeviseHelpers
+
+ before do
+ set_devise_mapping(context: @request)
+ end
+
+ describe '#show' do
+ render_views
+
+ subject { get :show, params: { confirmation_token: confirmation_token } }
+
+ context 'user is already confirmed' do
+ let_it_be_with_reload(:user) { create(:user, :unconfirmed) }
+ let(:confirmation_token) { user.confirmation_token }
+
+ before do
+ user.confirm
+ subject
+ end
+
+ it 'renders `new`' do
+ expect(response).to render_template(:new)
+ end
+
+ it 'displays an error message' do
+ expect(response.body).to include('Email was already confirmed, please try signing in')
+ end
+
+ it 'does not display the email of the user' do
+ expect(response.body).not_to include(user.email)
+ end
+ end
+
+ context 'user accesses the link after the expiry of confirmation token has passed' do
+ let_it_be_with_reload(:user) { create(:user, :unconfirmed) }
+ let(:confirmation_token) { user.confirmation_token }
+
+ before do
+ allow(Devise).to receive(:confirm_within).and_return(1.day)
+
+ travel_to(3.days.from_now) do
+ subject
+ end
+ end
+
+ it 'renders `new`' do
+ expect(response).to render_template(:new)
+ end
+
+ it 'displays an error message' do
+ expect(response.body).to include('Email needs to be confirmed within 1 day, please request a new one below')
+ end
+
+ it 'does not display the email of the user' do
+ expect(response.body).not_to include(user.email)
+ end
+ end
+
+ context 'with an invalid confirmation token' do
+ let(:confirmation_token) { 'invalid_confirmation_token' }
+
+ before do
+ subject
+ end
+
+ it 'renders `new`' do
+ expect(response).to render_template(:new)
+ end
+
+ it 'displays an error message' do
+ expect(response.body).to include('Confirmation token is invalid')
+ end
+ end
+ end
+end
diff --git a/spec/controllers/projects/feature_flags_controller_spec.rb b/spec/controllers/projects/feature_flags_controller_spec.rb
index ebe964aa465..d5fc80bd5a7 100644
--- a/spec/controllers/projects/feature_flags_controller_spec.rb
+++ b/spec/controllers/projects/feature_flags_controller_spec.rb
@@ -1419,6 +1419,40 @@ RSpec.describe Projects::FeatureFlagsController do
expect(response).to have_gitlab_http_status(:not_found)
end
+ it 'returns not found when trying to update a gitlabUserList strategy with a user list from another project' do
+ user_list = create(:operations_feature_flag_user_list, project: project, name: 'My List', user_xids: 'user1,user2')
+ strategy = create(:operations_strategy, feature_flag: new_version_flag, name: 'gitlabUserList', parameters: {}, user_list: user_list)
+ other_project = create(:project)
+ other_user_list = create(:operations_feature_flag_user_list, project: other_project, name: 'Other List', user_xids: 'some,one')
+
+ put_request(new_version_flag, strategies_attributes: [{
+ id: strategy.id,
+ user_list_id: other_user_list.id
+ }])
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(strategy.reload.user_list).to eq(user_list)
+ end
+
+ it 'allows setting multiple gitlabUserList strategies to the same user list' do
+ user_list_a = create(:operations_feature_flag_user_list, project: project, name: 'My List A', user_xids: 'user1,user2')
+ user_list_b = create(:operations_feature_flag_user_list, project: project, name: 'My List B', user_xids: 'user3,user4')
+ strategy_a = create(:operations_strategy, feature_flag: new_version_flag, name: 'gitlabUserList', parameters: {}, user_list: user_list_a)
+ strategy_b = create(:operations_strategy, feature_flag: new_version_flag, name: 'gitlabUserList', parameters: {}, user_list: user_list_a)
+
+ put_request(new_version_flag, strategies_attributes: [{
+ id: strategy_a.id,
+ user_list_id: user_list_b.id
+ }, {
+ id: strategy_b.id,
+ user_list_id: user_list_b.id
+ }])
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(strategy_a.reload.user_list).to eq(user_list_b)
+ expect(strategy_b.reload.user_list).to eq(user_list_b)
+ end
+
it 'updates an existing strategy' do
strategy = create(:operations_strategy, feature_flag: new_version_flag, name: 'default', parameters: {})
diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb
index afebc6982c1..bbd39fd4c83 100644
--- a/spec/controllers/search_controller_spec.rb
+++ b/spec/controllers/search_controller_spec.rb
@@ -272,7 +272,7 @@ RSpec.describe SearchController do
expect(last_payload[:metadata]['meta.search.group_id']).to eq('123')
expect(last_payload[:metadata]['meta.search.project_id']).to eq('456')
- expect(last_payload[:metadata]['meta.search.search']).to eq('hello world')
+ expect(last_payload[:metadata]).not_to have_key('meta.search.search')
expect(last_payload[:metadata]['meta.search.scope']).to eq('issues')
expect(last_payload[:metadata]['meta.search.force_search_results']).to eq('true')
expect(last_payload[:metadata]['meta.search.filters.confidential']).to eq('true')
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index cabd01bcae1..8b89ced3031 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -354,32 +354,99 @@ RSpec.describe UsersController do
describe 'GET #contributed' do
let(:project) { create(:project, :public) }
- let(:current_user) { create(:user) }
+
+ subject do
+ get :contributed, params: { username: author.username }, format: format
+ end
before do
- sign_in(current_user)
+ sign_in(user)
project.add_developer(public_user)
project.add_developer(private_user)
+ create(:push_event, project: project, author: author)
+
+ subject
end
- context 'with public profile' do
+ shared_examples_for 'renders contributed projects' do
it 'renders contributed projects' do
- create(:push_event, project: project, author: public_user)
+ expect(assigns[:contributed_projects]).not_to be_empty
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
- get :contributed, params: { username: public_user.username }
+ %i(html json).each do |format|
+ context "format: #{format}" do
+ let(:format) { format }
- expect(assigns[:contributed_projects]).not_to be_empty
+ context 'with public profile' do
+ let(:author) { public_user }
+
+ it_behaves_like 'renders contributed projects'
+ end
+
+ context 'with private profile' do
+ let(:author) { private_user }
+
+ it 'returns 404' do
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ context 'with a user that has the ability to read private profiles', :enable_admin_mode do
+ let(:user) { create(:admin) }
+
+ it_behaves_like 'renders contributed projects'
+ end
+ end
+ end
+ end
+ end
+
+ describe 'GET #starred' do
+ let(:project) { create(:project, :public) }
+
+ subject do
+ get :starred, params: { username: author.username }, format: format
+ end
+
+ before do
+ author.toggle_star(project)
+
+ sign_in(user)
+ subject
+ end
+
+ shared_examples_for 'renders starred projects' do
+ it 'renders starred projects' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(assigns[:starred_projects]).not_to be_empty
end
end
- context 'with private profile' do
- it 'does not render contributed projects' do
- create(:push_event, project: project, author: private_user)
+ %i(html json).each do |format|
+ context "format: #{format}" do
+ let(:format) { format }
+
+ context 'with public profile' do
+ let(:author) { public_user }
+
+ it_behaves_like 'renders starred projects'
+ end
+
+ context 'with private profile' do
+ let(:author) { private_user }
+
+ it 'returns 404' do
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
- get :contributed, params: { username: private_user.username }
+ context 'with a user that has the ability to read private profiles', :enable_admin_mode do
+ let(:user) { create(:admin) }
- expect(assigns[:contributed_projects]).to be_empty
+ it_behaves_like 'renders starred projects'
+ end
+ end
end
end
end
diff --git a/spec/features/explore/user_explores_projects_spec.rb b/spec/features/explore/user_explores_projects_spec.rb
index bf4d6c946e1..c082ff1fb0c 100644
--- a/spec/features/explore/user_explores_projects_spec.rb
+++ b/spec/features/explore/user_explores_projects_spec.rb
@@ -47,6 +47,14 @@ RSpec.describe 'User explores projects' do
end
end
+ shared_examples 'minimum search length' do
+ it 'shows a prompt to enter a longer search term', :js do
+ fill_in 'name', with: 'z'
+
+ expect(page).to have_content('Enter at least three characters to search')
+ end
+ end
+
context 'when viewing public projects' do
before do
visit(explore_projects_path)
@@ -54,6 +62,7 @@ RSpec.describe 'User explores projects' do
include_examples 'shows public and internal projects'
include_examples 'empty search results'
+ include_examples 'minimum search length'
end
context 'when viewing most starred projects' do
@@ -63,6 +72,7 @@ RSpec.describe 'User explores projects' do
include_examples 'shows public and internal projects'
include_examples 'empty search results'
+ include_examples 'minimum search length'
end
context 'when viewing trending projects' do
@@ -76,6 +86,7 @@ RSpec.describe 'User explores projects' do
include_examples 'shows public projects'
include_examples 'empty search results'
+ include_examples 'minimum search length'
end
end
end
diff --git a/spec/features/markdown/mermaid_spec.rb b/spec/features/markdown/mermaid_spec.rb
index 9875fda17a9..6df844277db 100644
--- a/spec/features/markdown/mermaid_spec.rb
+++ b/spec/features/markdown/mermaid_spec.rb
@@ -19,6 +19,9 @@ RSpec.describe 'Mermaid rendering', :js do
visit project_issue_path(project, issue)
+ wait_for_requests
+ wait_for_mermaid
+
%w[A B C D].each do |label|
expect(page).to have_selector('svg text', text: label)
end
@@ -39,6 +42,7 @@ RSpec.describe 'Mermaid rendering', :js do
visit project_issue_path(project, issue)
wait_for_requests
+ wait_for_mermaid
expected = '<text style=""><tspan xml:space="preserve" dy="1em" x="1">Line 1</tspan><tspan xml:space="preserve" dy="1em" x="1">Line 2</tspan></text>'
expect(page.html.scan(expected).count).to be(4)
@@ -65,6 +69,9 @@ RSpec.describe 'Mermaid rendering', :js do
visit project_issue_path(project, issue)
+ wait_for_requests
+ wait_for_mermaid
+
page.within('.description') do
expect(page).to have_selector('svg')
expect(page).to have_selector('pre.mermaid')
@@ -92,6 +99,9 @@ RSpec.describe 'Mermaid rendering', :js do
visit project_issue_path(project, issue)
+ wait_for_requests
+ wait_for_mermaid
+
page.within('.description') do
page.find('summary').click
svg = page.find('svg.mermaid')
@@ -118,6 +128,9 @@ RSpec.describe 'Mermaid rendering', :js do
visit project_issue_path(project, issue)
+ wait_for_requests
+ wait_for_mermaid
+
expect(page).to have_css('svg.mermaid[style*="max-width"][width="100%"]')
end
@@ -147,6 +160,7 @@ RSpec.describe 'Mermaid rendering', :js do
end
wait_for_requests
+ wait_for_mermaid
find('.js-lazy-render-mermaid').click
@@ -156,4 +170,55 @@ RSpec.describe 'Mermaid rendering', :js do
expect(page).not_to have_selector('.js-lazy-render-mermaid-container')
end
end
+
+ it 'does not render more than 50 mermaid blocks', :js, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/234081' } do
+ graph_edges = "A-->B;B-->A;"
+
+ description = <<~MERMAID
+ ```mermaid
+ graph LR
+ #{graph_edges}
+ ```
+ MERMAID
+
+ description *= 51
+
+ project = create(:project, :public)
+
+ issue = create(:issue, project: project, description: description)
+
+ visit project_issue_path(project, issue)
+
+ wait_for_requests
+ wait_for_mermaid
+
+ page.within('.description') do
+ expect(page).to have_selector('svg')
+
+ expect(page).to have_selector('.lazy-alert-shown')
+
+ expect(page).to have_selector('.js-lazy-render-mermaid-container')
+ end
+ end
+end
+
+def wait_for_mermaid
+ run_idle_callback = <<~RUN_IDLE_CALLBACK
+ window.requestIdleCallback(() => {
+ window.__CAPYBARA_IDLE_CALLBACK_EXEC__ = 1;
+ })
+ RUN_IDLE_CALLBACK
+
+ page.evaluate_script(run_idle_callback)
+
+ Timeout.timeout(Capybara.default_max_wait_time) do
+ loop until finished_rendering?
+ end
+end
+
+def finished_rendering?
+ check_idle_callback = <<~CHECK_IDLE_CALLBACK
+ window.__CAPYBARA_IDLE_CALLBACK_EXEC__
+ CHECK_IDLE_CALLBACK
+ page.evaluate_script(check_idle_callback) == 1
end
diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb
index 2d712bd44ce..57977fb69b4 100644
--- a/spec/finders/projects_finder_spec.rb
+++ b/spec/finders/projects_finder_spec.rb
@@ -161,6 +161,29 @@ RSpec.describe ProjectsFinder, :do_not_mock_admin_mode do
it { is_expected.to eq([public_project]) }
end
+ describe 'filter by search with minimum search length' do
+ context 'when search term is shorter than minimum length' do
+ let(:params) { { search: 'C', minimum_search_length: 3 } }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'when search term is longer than minimum length' do
+ let(:project) { create(:project, :public, group: group, name: 'test_project') }
+ let(:params) { { search: 'test', minimum_search_length: 3 } }
+
+ it { is_expected.to eq([project]) }
+ end
+
+ context 'when minimum length is invalid' do
+ let(:params) { { search: 'C', minimum_search_length: 'x' } }
+
+ it 'ignores the minimum length param' do
+ is_expected.to eq([public_project])
+ end
+ end
+ end
+
describe 'filter by group name' do
let(:params) { { name: group.name, search_namespaces: true } }
diff --git a/spec/finders/starred_projects_finder_spec.rb b/spec/finders/starred_projects_finder_spec.rb
index 15d4ae52ddd..f5d3314021d 100644
--- a/spec/finders/starred_projects_finder_spec.rb
+++ b/spec/finders/starred_projects_finder_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe StarredProjectsFinder do
let(:project1) { create(:project, :public, :empty_repo) }
let(:project2) { create(:project, :public, :empty_repo) }
- let(:other_project) { create(:project, :public, :empty_repo) }
+ let(:private_project) { create(:project, :private, :empty_repo) }
let(:user) { create(:user) }
let(:other_user) { create(:user) }
@@ -13,6 +13,9 @@ RSpec.describe StarredProjectsFinder do
before do
user.toggle_star(project1)
user.toggle_star(project2)
+
+ private_project.add_maintainer(user)
+ user.toggle_star(private_project)
end
describe '#execute' do
@@ -20,22 +23,56 @@ RSpec.describe StarredProjectsFinder do
subject { finder.execute }
- describe 'as same user' do
- let(:current_user) { user }
+ context 'user has a public profile' do
+ describe 'as same user' do
+ let(:current_user) { user }
- it { is_expected.to contain_exactly(project1, project2) }
- end
+ it { is_expected.to contain_exactly(project1, project2, private_project) }
+ end
+
+ describe 'as other user' do
+ let(:current_user) { other_user }
- describe 'as other user' do
- let(:current_user) { other_user }
+ it { is_expected.to contain_exactly(project1, project2) }
+ end
- it { is_expected.to contain_exactly(project1, project2) }
+ describe 'as no user' do
+ let(:current_user) { nil }
+
+ it { is_expected.to contain_exactly(project1, project2) }
+ end
end
- describe 'as no user' do
- let(:current_user) { nil }
+ context 'user has a private profile' do
+ before do
+ user.update!(private_profile: true)
+ end
+
+ describe 'as same user' do
+ let(:current_user) { user }
+
+ it { is_expected.to contain_exactly(project1, project2, private_project) }
+ end
+
+ describe 'as other user' do
+ context 'user does not have access to view the private profile' do
+ let(:current_user) { other_user }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'user has access to view the private profile', :enable_admin_mode do
+ let(:current_user) { create(:admin) }
+
+ it { is_expected.to contain_exactly(project1, project2, private_project) }
+ end
+ end
+
+ describe 'as no user' do
+ let(:current_user) { nil }
- it { is_expected.to contain_exactly(project1, project2) }
+ it { is_expected.to be_empty }
+ end
end
end
end
diff --git a/spec/helpers/operations_helper_spec.rb b/spec/helpers/operations_helper_spec.rb
index 09f9bba8f9e..801d5de79b1 100644
--- a/spec/helpers/operations_helper_spec.rb
+++ b/spec/helpers/operations_helper_spec.rb
@@ -21,20 +21,15 @@ RSpec.describe OperationsHelper do
end
context 'initial service configuration' do
- let_it_be(:alerts_service) { AlertsService.new(project: project) }
let_it_be(:prometheus_service) { PrometheusService.new(project: project) }
before do
- allow(project).to receive(:find_or_initialize_service).with('alerts').and_return(alerts_service)
+ allow(project).to receive(:find_or_initialize_service).and_call_original
allow(project).to receive(:find_or_initialize_service).with('prometheus').and_return(prometheus_service)
end
it 'returns the correct values' do
expect(subject).to eq(
- 'activated' => 'false',
- 'url' => alerts_service.url,
- 'authorization_key' => nil,
- 'form_path' => project_service_path(project, alerts_service),
'alerts_setup_url' => help_page_path('operations/incident_management/alert_integrations.md', anchor: 'generic-http-endpoint'),
'alerts_usage_url' => project_alert_management_index_path(project),
'prometheus_form_path' => project_service_path(project, prometheus_service),
@@ -104,33 +99,6 @@ RSpec.describe OperationsHelper do
end
end
end
-
- context 'with generic alerts service configured' do
- let_it_be(:alerts_service) { create(:alerts_service, project: project) }
-
- context 'with generic alerts enabled' do
- it 'returns the correct values' do
- expect(subject).to include(
- 'activated' => 'true',
- 'authorization_key' => alerts_service.token,
- 'url' => alerts_service.url
- )
- end
- end
-
- context 'with generic alerts disabled' do
- before do
- alerts_service.update!(active: false)
- end
-
- it 'returns the correct values' do
- expect(subject).to include(
- 'activated' => 'false',
- 'authorization_key' => alerts_service.token
- )
- end
- end
- end
end
describe '#operations_settings_data' do
diff --git a/spec/lib/banzai/filter/ascii_doc_sanitization_filter_spec.rb b/spec/lib/banzai/filter/ascii_doc_sanitization_filter_spec.rb
new file mode 100644
index 00000000000..6eeddee63d7
--- /dev/null
+++ b/spec/lib/banzai/filter/ascii_doc_sanitization_filter_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Banzai::Filter::AsciiDocSanitizationFilter do
+ include FilterSpecHelper
+
+ it 'preserves footnotes refs' do
+ result = filter('<p>This paragraph has a footnote.<sup>[<a id="_footnoteref_1" href="#_footnotedef_1" title="View footnote.">1</a>]</sup></p>').to_html
+ expect(result).to eq('<p>This paragraph has a footnote.<sup>[<a id="_footnoteref_1" href="#_footnotedef_1" title="View footnote.">1</a>]</sup></p>')
+ end
+
+ it 'preserves footnotes defs' do
+ result = filter('<div id="_footnotedef_1">
+<a href="#_footnoteref_1">1</a>. This is the text of the footnote.</div>').to_html
+ expect(result).to eq(%(<div id="_footnotedef_1">
+<a href="#_footnoteref_1">1</a>. This is the text of the footnote.</div>))
+ end
+
+ it 'preserves user-content- prefixed ids on anchors' do
+ result = filter('<p><a id="user-content-cross-references"></a>A link to another location within an AsciiDoc document.</p>').to_html
+ expect(result).to eq(%(<p><a id="user-content-cross-references"></a>A link to another location within an AsciiDoc document.</p>))
+ end
+
+ it 'preserves user-content- prefixed ids on div (blocks)' do
+ html_content = <<~HTML
+<div id="user-content-open-block" class="openblock">
+ <div class="content">
+ <div class="paragraph">
+ <p>This is an open block</p>
+ </div>
+ </div>
+</div>
+ HTML
+ output = <<~SANITIZED_HTML
+ <div id="user-content-open-block">
+ <div>
+ <div>
+ <p>This is an open block</p>
+ </div>
+ </div>
+ </div>
+ SANITIZED_HTML
+ expect(filter(html_content).to_html).to eq(output)
+ end
+ it 'preserves section anchor ids' do
+ result = filter(%(<h2 id="user-content-first-section">
+<a class="anchor" href="#user-content-first-section"></a>First section</h2>)).to_html
+ expect(result).to eq(%(<h2 id="user-content-first-section">
+<a class="anchor" href="#user-content-first-section"></a>First section</h2>))
+ end
+
+ it 'removes non prefixed ids' do
+ result = filter('<p><a id="cross-references"></a>A link to another location within an AsciiDoc document.</p>').to_html
+ expect(result).to eq(%(<p><a></a>A link to another location within an AsciiDoc document.</p>))
+ end
+end
diff --git a/spec/lib/gitlab/asciidoc/html5_converter_spec.rb b/spec/lib/gitlab/asciidoc/html5_converter_spec.rb
new file mode 100644
index 00000000000..84c2cda496e
--- /dev/null
+++ b/spec/lib/gitlab/asciidoc/html5_converter_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Asciidoc::Html5Converter do
+ describe 'convert AsciiDoc to HTML5' do
+ it 'appends user-content- prefix on ref (anchor)' do
+ doc = Asciidoctor::Document.new('')
+ anchor = Asciidoctor::Inline.new(doc, :anchor, '', type: :ref, id: 'cross-references')
+ converter = Gitlab::Asciidoc::Html5Converter.new('gitlab_html5')
+ html = converter.convert_inline_anchor(anchor)
+ expect(html).to eq('<a id="user-content-cross-references"></a>')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index 8012f5c510f..36e4decdead 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -252,6 +252,27 @@ module Gitlab
end
end
+ context 'with xrefs' do
+ it 'preserves ids' do
+ input = <<~ADOC
+ Learn how to xref:cross-references[use cross references].
+
+ [[cross-references]]A link to another location within an AsciiDoc document or between AsciiDoc documents is called a cross reference (also referred to as an xref).
+ ADOC
+
+ output = <<~HTML
+ <div>
+ <p>Learn how to <a href="#cross-references">use cross references</a>.</p>
+ </div>
+ <div>
+ <p><a id="user-content-cross-references"></a>A link to another location within an AsciiDoc document or between AsciiDoc documents is called a cross reference (also referred to as an xref).</p>
+ </div>
+ HTML
+
+ expect(render(input, context)).to include(output.strip)
+ end
+ end
+
context 'with checklist' do
it 'preserves classes' do
input = <<~ADOC
diff --git a/spec/lib/gitlab/rack_attack/user_allowlist_spec.rb b/spec/lib/gitlab/rack_attack/user_allowlist_spec.rb
new file mode 100644
index 00000000000..aa604dfab71
--- /dev/null
+++ b/spec/lib/gitlab/rack_attack/user_allowlist_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::RackAttack::UserAllowlist do
+ using RSpec::Parameterized::TableSyntax
+
+ subject { described_class.new(input)}
+
+ where(:input, :elements) do
+ nil | []
+ '' | []
+ '123' | [123]
+ '123,456' | [123, 456]
+ '123,foobar, 456,' | [123, 456]
+ end
+
+ with_them do
+ it 'has the expected elements' do
+ expect(subject).to contain_exactly(*elements)
+ end
+
+ it 'implements empty?' do
+ expect(subject.empty?).to eq(elements.empty?)
+ end
+
+ it 'implements include?' do
+ unless elements.empty?
+ expect(subject).to include(elements.first)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/rack_attack_spec.rb b/spec/lib/gitlab/rack_attack_spec.rb
index 8b53af6c333..d72863b0103 100644
--- a/spec/lib/gitlab/rack_attack_spec.rb
+++ b/spec/lib/gitlab/rack_attack_spec.rb
@@ -75,5 +75,22 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
expect(fake_rack_attack).to have_received(:throttle).with(throttle.to_s, throttles[throttle])
end
end
+
+ context 'user allowlist' do
+ subject { described_class.user_allowlist }
+
+ it 'is empty' do
+ described_class.configure(fake_rack_attack)
+
+ expect(subject).to be_empty
+ end
+
+ it 'reflects GITLAB_THROTTLE_USER_ALLOWLIST' do
+ stub_env('GITLAB_THROTTLE_USER_ALLOWLIST', '123,456')
+ described_class.configure(fake_rack_attack)
+
+ expect(subject).to contain_exactly(123, 456)
+ end
+ end
end
end
diff --git a/spec/migrations/remove_orphan_service_hooks_spec.rb b/spec/migrations/remove_orphan_service_hooks_spec.rb
new file mode 100644
index 00000000000..c06a8b97738
--- /dev/null
+++ b/spec/migrations/remove_orphan_service_hooks_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+require_migration!
+require Rails.root.join('db', 'migrate', '20201119125730_add_web_hooks_service_foreign_key.rb')
+
+RSpec.describe RemoveOrphanServiceHooks, schema: 20201203123201 do
+ let(:web_hooks) { table(:web_hooks) }
+ let(:services) { table(:services) }
+
+ before do
+ services.create!
+ web_hooks.create!(service_id: services.first.id, type: 'ServiceHook')
+ web_hooks.create!(service_id: nil)
+
+ AddWebHooksServiceForeignKey.new.down
+ web_hooks.create!(service_id: non_existing_record_id, type: 'ServiceHook')
+ AddWebHooksServiceForeignKey.new.up
+ end
+
+ it 'removes service hooks where the referenced service does not exist', :aggregate_failures do
+ expect { RemoveOrphanServiceHooks.new.up }.to change { web_hooks.count }.by(-1)
+ expect(web_hooks.where.not(service_id: services.select(:id)).count).to eq(0)
+ end
+end
diff --git a/spec/requests/api/graphql/user/starred_projects_query_spec.rb b/spec/requests/api/graphql/user/starred_projects_query_spec.rb
index 8a1bd3d172f..b098058a735 100644
--- a/spec/requests/api/graphql/user/starred_projects_query_spec.rb
+++ b/spec/requests/api/graphql/user/starred_projects_query_spec.rb
@@ -70,4 +70,31 @@ RSpec.describe 'Getting starredProjects of the user' do
)
end
end
+
+ context 'the user has a private profile' do
+ before do
+ user.update!(private_profile: true)
+ post_graphql(query, current_user: current_user)
+ end
+
+ context 'the current user does not have access to view the private profile of the user' do
+ let(:current_user) { create(:user) }
+
+ it 'finds no projects' do
+ expect(starred_projects).to be_empty
+ end
+ end
+
+ context 'the current user has access to view the private profile of the user' do
+ let(:current_user) { create(:admin) }
+
+ it 'finds all projects starred by the user, which the current user has access to' do
+ expect(starred_projects).to contain_exactly(
+ a_hash_including('id' => global_id_of(project_a)),
+ a_hash_including('id' => global_id_of(project_b)),
+ a_hash_including('id' => global_id_of(project_c))
+ )
+ end
+ end
+ end
end
diff --git a/spec/requests/api/graphql/user_query_spec.rb b/spec/requests/api/graphql/user_query_spec.rb
index d495537c722..3e7ef8838c0 100644
--- a/spec/requests/api/graphql/user_query_spec.rb
+++ b/spec/requests/api/graphql/user_query_spec.rb
@@ -82,7 +82,7 @@ RSpec.describe 'getting user information' do
'username' => presenter.username,
'webUrl' => presenter.web_url,
'avatarUrl' => presenter.avatar_url,
- 'email' => presenter.email,
+ 'email' => presenter.public_email,
'publicEmail' => presenter.public_email
))
@@ -251,7 +251,7 @@ RSpec.describe 'getting user information' do
context 'the user is private' do
before do
- user.update(private_profile: true)
+ user.update!(private_profile: true)
post_graphql(query, current_user: current_user)
end
@@ -261,6 +261,50 @@ RSpec.describe 'getting user information' do
it_behaves_like 'a working graphql query'
end
+ context 'we request the groupMemberships' do
+ let_it_be(:membership_a) { create(:group_member, user: user) }
+ let(:group_memberships) { graphql_data_at(:user, :group_memberships, :nodes) }
+ let(:user_fields) { 'groupMemberships { nodes { id } }' }
+
+ it_behaves_like 'a working graphql query'
+
+ it 'cannot be found' do
+ expect(group_memberships).to be_empty
+ end
+
+ context 'the current user is the user' do
+ let(:current_user) { user }
+
+ it 'can be found' do
+ expect(group_memberships).to include(
+ a_hash_including('id' => global_id_of(membership_a))
+ )
+ end
+ end
+ end
+
+ context 'we request the projectMemberships' do
+ let_it_be(:membership_a) { create(:project_member, user: user) }
+ let(:project_memberships) { graphql_data_at(:user, :project_memberships, :nodes) }
+ let(:user_fields) { 'projectMemberships { nodes { id } }' }
+
+ it_behaves_like 'a working graphql query'
+
+ it 'cannot be found' do
+ expect(project_memberships).to be_empty
+ end
+
+ context 'the current user is the user' do
+ let(:current_user) { user }
+
+ it 'can be found' do
+ expect(project_memberships).to include(
+ a_hash_including('id' => global_id_of(membership_a))
+ )
+ end
+ end
+ end
+
context 'we request the authoredMergeRequests' do
let(:user_fields) { 'authoredMergeRequests { nodes { id } }' }
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index eb3e610934d..83b32d9b3fd 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1255,13 +1255,46 @@ RSpec.describe API::Projects do
expect(json_response['message']).to eq('404 User Not Found')
end
- it 'returns projects filtered by user' do
- get api("/users/#{user3.id}/starred_projects/", user)
+ context 'with a public profile' do
+ it 'returns projects filtered by user' do
+ get api("/users/#{user3.id}/starred_projects/", user)
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response.map { |project| project['id'] }).to contain_exactly(project.id, project2.id, project3.id)
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |project| project['id'] })
+ .to contain_exactly(project.id, project2.id, project3.id)
+ end
+ end
+
+ context 'with a private profile' do
+ before do
+ user3.update!(private_profile: true)
+ user3.reload
+ end
+
+ context 'user does not have access to view the private profile' do
+ it 'returns no projects' do
+ get api("/users/#{user3.id}/starred_projects/", user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response).to be_empty
+ end
+ end
+
+ context 'user has access to view the private profile' do
+ it 'returns projects filtered by user' do
+ get api("/users/#{user3.id}/starred_projects/", admin)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |project| project['id'] })
+ .to contain_exactly(project.id, project2.id, project3.id)
+ end
+ end
end
end
diff --git a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
index 323ac50808d..5d300d38e4a 100644
--- a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
+++ b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
@@ -23,6 +23,11 @@ RSpec.shared_examples 'rate-limited token-authenticated requests' do
settings_to_set[:"#{throttle_setting_prefix}_period_in_seconds"] = period_in_seconds
end
+ after do
+ stub_env('GITLAB_THROTTLE_USER_ALLOWLIST', nil)
+ Gitlab::RackAttack.configure_user_allowlist
+ end
+
context 'when the throttle is enabled' do
before do
settings_to_set[:"#{throttle_setting_prefix}_enabled"] = true
@@ -30,6 +35,8 @@ RSpec.shared_examples 'rate-limited token-authenticated requests' do
end
it 'rejects requests over the rate limit' do
+ expect(Gitlab::Instrumentation::Throttle).not_to receive(:safelist=)
+
# At first, allow requests under the rate limit.
requests_per_period.times do
make_request(request_args)
@@ -40,6 +47,18 @@ RSpec.shared_examples 'rate-limited token-authenticated requests' do
expect_rejection { make_request(request_args) }
end
+ it 'does not reject requests if the user is in the allowlist' do
+ stub_env('GITLAB_THROTTLE_USER_ALLOWLIST', user.id.to_s)
+ Gitlab::RackAttack.configure_user_allowlist
+
+ expect(Gitlab::Instrumentation::Throttle).to receive(:safelist=).with('throttle_user_allowlist').at_least(:once)
+
+ (requests_per_period + 1).times do
+ make_request(request_args)
+ expect(response).not_to have_gitlab_http_status(:too_many_requests)
+ end
+ end
+
it 'allows requests after throttling and then waiting for the next period' do
requests_per_period.times do
make_request(request_args)
@@ -167,6 +186,11 @@ RSpec.shared_examples 'rate-limited web authenticated requests' do
settings_to_set[:"#{throttle_setting_prefix}_period_in_seconds"] = period_in_seconds
end
+ after do
+ stub_env('GITLAB_THROTTLE_USER_ALLOWLIST', nil)
+ Gitlab::RackAttack.configure_user_allowlist
+ end
+
context 'when the throttle is enabled' do
before do
settings_to_set[:"#{throttle_setting_prefix}_enabled"] = true
@@ -174,6 +198,8 @@ RSpec.shared_examples 'rate-limited web authenticated requests' do
end
it 'rejects requests over the rate limit' do
+ expect(Gitlab::Instrumentation::Throttle).not_to receive(:safelist=)
+
# At first, allow requests under the rate limit.
requests_per_period.times do
request_authenticated_web_url
@@ -184,6 +210,18 @@ RSpec.shared_examples 'rate-limited web authenticated requests' do
expect_rejection { request_authenticated_web_url }
end
+ it 'does not reject requests if the user is in the allowlist' do
+ stub_env('GITLAB_THROTTLE_USER_ALLOWLIST', user.id.to_s)
+ Gitlab::RackAttack.configure_user_allowlist
+
+ expect(Gitlab::Instrumentation::Throttle).to receive(:safelist=).with('throttle_user_allowlist').at_least(:once)
+
+ (requests_per_period + 1).times do
+ request_authenticated_web_url
+ expect(response).not_to have_gitlab_http_status(:too_many_requests)
+ end
+ end
+
it 'allows requests after throttling and then waiting for the next period' do
requests_per_period.times do
request_authenticated_web_url
diff --git a/spec/validators/zoom_url_validator_spec.rb b/spec/validators/zoom_url_validator_spec.rb
new file mode 100644
index 00000000000..7d5c94bc249
--- /dev/null
+++ b/spec/validators/zoom_url_validator_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ZoomUrlValidator do
+ let(:zoom_meeting) { build(:zoom_meeting) }
+
+ describe 'validations' do
+ context 'when zoom link starts with https' do
+ it 'passes validation' do
+ zoom_meeting.url = 'https://zoom.us/j/123456789'
+
+ expect(zoom_meeting.valid?).to eq(true)
+ expect(zoom_meeting.errors).to be_empty
+ end
+ end
+
+ shared_examples 'zoom link does not start with https' do |url|
+ it 'fails validation' do
+ zoom_meeting.url = url
+ expect(zoom_meeting.valid?).to eq(false)
+
+ expect(zoom_meeting.errors).to be_present
+ expect(zoom_meeting.errors.first[1]).to eq 'must contain one valid Zoom URL'
+ end
+ end
+
+ context 'when zoom link does not start with https' do
+ include_examples 'zoom link does not start with https', 'http://zoom.us/j/123456789'
+
+ context 'when zoom link does not start with a scheme' do
+ include_examples 'zoom link does not start with https', 'testinghttp://zoom.us/j/123456789'
+ end
+ end
+ end
+end