diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-19 07:33:21 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-19 07:33:21 +0000 |
commit | 36a59d088eca61b834191dacea009677a96c052f (patch) | |
tree | e4f33972dab5d8ef79e3944a9f403035fceea43f /spec/support/shared_examples/models | |
parent | a1761f15ec2cae7c7f7bbda39a75494add0dfd6f (diff) | |
download | gitlab-ce-36a59d088eca61b834191dacea009677a96c052f.tar.gz |
Add latest changes from gitlab-org/gitlab@15-0-stable-eev15.0.0-rc42
Diffstat (limited to 'spec/support/shared_examples/models')
6 files changed, 513 insertions, 277 deletions
diff --git a/spec/support/shared_examples/models/chat_integration_shared_examples.rb b/spec/support/shared_examples/models/chat_integration_shared_examples.rb index e6b270c6188..fa10b03fa90 100644 --- a/spec/support/shared_examples/models/chat_integration_shared_examples.rb +++ b/spec/support/shared_examples/models/chat_integration_shared_examples.rb @@ -199,7 +199,7 @@ RSpec.shared_examples "chat integration" do |integration_name| { title: "Awesome wiki_page", content: "Some text describing some thing or another", - format: "md", + format: :markdown, message: "user created page: Awesome wiki_page" } end diff --git a/spec/support/shared_examples/models/concerns/integrations/reset_secret_fields_shared_examples.rb b/spec/support/shared_examples/models/concerns/integrations/reset_secret_fields_shared_examples.rb new file mode 100644 index 00000000000..873f858e432 --- /dev/null +++ b/spec/support/shared_examples/models/concerns/integrations/reset_secret_fields_shared_examples.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +RSpec.shared_examples Integrations::ResetSecretFields do + describe '#exposing_secrets_fields' do + it 'returns an array of strings' do + expect(integration.exposing_secrets_fields).to be_a(Array) + expect(integration.exposing_secrets_fields).to all(be_a(String)) + end + end + + describe '#reset_secret_fields?' do + let(:exposing_fields) { integration.exposing_secrets_fields } + + it 'returns false if no exposing field has changed' do + exposing_fields.each do |field| + allow(integration).to receive("#{field}_changed?").and_return(false) + end + + expect(integration.send(:reset_secret_fields?)).to be(false) + end + + it 'returns true if any exposing field has changed' do + exposing_fields.each do |field| + allow(integration).to receive("#{field}_changed?").and_return(true) + + other_exposing_fields = exposing_fields.without(field) + other_exposing_fields.each do |other_field| + allow(integration).to receive("#{other_field}_changed?").and_return(false) + end + + expect(integration.send(:reset_secret_fields?)).to be(true) + end + end + end + + describe 'validation callback' do + before do + # Store a value in each password field + integration.secret_fields.each do |field| + integration.public_send("#{field}=", 'old value') + end + + # Treat values as persisted + integration.reset_updated_properties + integration.instance_variable_set('@old_data_fields', nil) if integration.supports_data_fields? + end + + context 'when an exposing field has changed' do + let(:exposing_field) { integration.exposing_secrets_fields.first } + + before do + integration.public_send("#{exposing_field}=", 'new value') + end + + it 'clears all secret fields' do + integration.valid? + + integration.secret_fields.each do |field| + expect(integration.public_send(field)).to be_nil + expect(integration.properties[field]).to be_nil if integration.properties.present? + expect(integration.data_fields[field]).to be_nil if integration.supports_data_fields? + end + end + + context 'when a secret field has been updated' do + let(:secret_field) { integration.secret_fields.first } + let(:other_secret_fields) { integration.secret_fields.without(secret_field) } + let(:new_value) { 'new value' } + + before do + integration.public_send("#{secret_field}=", new_value) + end + + it 'does not clear this secret field' do + integration.valid? + + expect(integration.public_send(secret_field)).to eq('new value') + + other_secret_fields.each do |field| + expect(integration.public_send(field)).to be_nil + end + end + + context 'when a secret field has been updated with the same value' do + let(:new_value) { 'old value' } + + it 'does not clear this secret field' do + integration.valid? + + expect(integration.public_send(secret_field)).to eq('old value') + + other_secret_fields.each do |field| + expect(integration.public_send(field)).to be_nil + end + end + end + end + end + + context 'when no exposing field has changed' do + it 'does not clear any secret fields' do + integration.valid? + + integration.secret_fields.each do |field| + expect(integration.public_send(field)).to eq('old value') + end + end + end + end +end diff --git a/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb b/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb index da5c35c970a..2e062cda4e9 100644 --- a/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb +++ b/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb @@ -45,9 +45,33 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |integration_name end it "notifies about #{event_type} events" do + expect(chat_integration).not_to receive(:log_error) + chat_integration.execute(data) + expect(WebMock).to have_requested(:post, stubbed_resolved_hostname) end + + context 'when the response is not successful' do + let!(:stubbed_resolved_hostname) do + stub_full_request(webhook_url, method: :post) + .to_return(status: 409, body: 'error message') + .request_pattern.uri_pattern.to_s + end + + it 'logs an error' do + expect(chat_integration).to receive(:log_error).with( + 'SlackMattermostNotifier HTTP error response', + request_host: 'example.gitlab.com', + response_code: 409, + response_body: 'error message' + ) + + chat_integration.execute(data) + + expect(WebMock).to have_requested(:post, stubbed_resolved_hostname) + end + end end shared_examples "untriggered #{integration_name} integration" do |event_type: nil, branches_to_be_notified: nil| @@ -59,8 +83,9 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |integration_name stub_full_request(webhook_url, method: :post).request_pattern.uri_pattern.to_s end - it "notifies about #{event_type} events" do + it "does not notify about #{event_type} events" do chat_integration.execute(data) + expect(WebMock).not_to have_requested(:post, stubbed_resolved_hostname) end end diff --git a/spec/support/shared_examples/models/member_shared_examples.rb b/spec/support/shared_examples/models/member_shared_examples.rb index a329a6dca91..e293d10964b 100644 --- a/spec/support/shared_examples/models/member_shared_examples.rb +++ b/spec/support/shared_examples/models/member_shared_examples.rb @@ -77,312 +77,309 @@ RSpec.shared_examples '#valid_level_roles' do |entity_name| end RSpec.shared_examples_for "member creation" do - let_it_be(:user) { create(:user) } let_it_be(:admin) { create(:admin) } - describe '#execute' do - it 'returns a Member object', :aggregate_failures do - member = described_class.new(source, user, :maintainer).execute - - expect(member).to be_a member_type - expect(member).to be_persisted - end + it 'returns a Member object', :aggregate_failures do + member = described_class.new(source, user, :maintainer).execute - context 'when adding a project_bot' do - let_it_be(:project_bot) { create(:user, :project_bot) } - - before_all do - source.add_owner(user) - end + expect(member).to be_a member_type + expect(member).to be_persisted + end - context 'when project_bot is already a member' do - before do - source.add_developer(project_bot) - end + context 'when adding a project_bot' do + let_it_be(:project_bot) { create(:user, :project_bot) } - it 'does not update the member' do - member = described_class.new(source, project_bot, :maintainer, current_user: user).execute + before_all do + source.add_owner(user) + end - expect(source.users.reload).to include(project_bot) - expect(member).to be_persisted - expect(member.access_level).to eq(Gitlab::Access::DEVELOPER) - expect(member.errors.full_messages).to include(/not authorized to update member/) - end + context 'when project_bot is already a member' do + before do + source.add_developer(project_bot) end - context 'when project_bot is not already a member' do - it 'adds the member' do - member = described_class.new(source, project_bot, :maintainer, current_user: user).execute + it 'does not update the member' do + member = described_class.new(source, project_bot, :maintainer, current_user: user).execute - expect(source.users.reload).to include(project_bot) - expect(member).to be_persisted - end + expect(source.users.reload).to include(project_bot) + expect(member).to be_persisted + expect(member.access_level).to eq(Gitlab::Access::DEVELOPER) + expect(member.errors.full_messages).to include(/not authorized to update member/) end end - context 'when admin mode is enabled', :enable_admin_mode, :aggregate_failures do - it 'sets members.created_by to the given admin current_user' do - member = described_class.new(source, user, :maintainer, current_user: admin).execute + context 'when project_bot is not already a member' do + it 'adds the member' do + member = described_class.new(source, project_bot, :maintainer, current_user: user).execute + expect(source.users.reload).to include(project_bot) expect(member).to be_persisted - expect(source.users.reload).to include(user) - expect(member.created_by).to eq(admin) end end + end - context 'when admin mode is disabled' do - it 'rejects setting members.created_by to the given admin current_user', :aggregate_failures do - member = described_class.new(source, user, :maintainer, current_user: admin).execute + context 'when admin mode is enabled', :enable_admin_mode, :aggregate_failures do + it 'sets members.created_by to the given admin current_user' do + member = described_class.new(source, user, :maintainer, current_user: admin).execute - expect(member).not_to be_persisted - expect(source.users.reload).not_to include(user) - expect(member.errors.full_messages).to include(/not authorized to create member/) - end + expect(member).to be_persisted + expect(source.users.reload).to include(user) + expect(member.created_by).to eq(admin) end + end - it 'sets members.expires_at to the given expires_at' do - member = described_class.new(source, user, :maintainer, expires_at: Date.new(2016, 9, 22)).execute + context 'when admin mode is disabled' do + it 'rejects setting members.created_by to the given admin current_user', :aggregate_failures do + member = described_class.new(source, user, :maintainer, current_user: admin).execute - expect(member.expires_at).to eq(Date.new(2016, 9, 22)) + expect(member).not_to be_persisted + expect(source.users.reload).not_to include(user) + expect(member.errors.full_messages).to include(/not authorized to create member/) end + end - described_class.access_levels.each do |sym_key, int_access_level| - it "accepts the :#{sym_key} symbol as access level", :aggregate_failures do - expect(source.users).not_to include(user) + it 'sets members.expires_at to the given expires_at' do + member = described_class.new(source, user, :maintainer, expires_at: Date.new(2016, 9, 22)).execute - member = described_class.new(source, user.id, sym_key).execute + expect(member.expires_at).to eq(Date.new(2016, 9, 22)) + end - expect(member.access_level).to eq(int_access_level) - expect(source.users.reload).to include(user) - end + described_class.access_levels.each do |sym_key, int_access_level| + it "accepts the :#{sym_key} symbol as access level", :aggregate_failures do + expect(source.users).not_to include(user) + + member = described_class.new(source, user.id, sym_key).execute - it "accepts the #{int_access_level} integer as access level", :aggregate_failures do + expect(member.access_level).to eq(int_access_level) + expect(source.users.reload).to include(user) + end + + it "accepts the #{int_access_level} integer as access level", :aggregate_failures do + expect(source.users).not_to include(user) + + member = described_class.new(source, user.id, int_access_level).execute + + expect(member.access_level).to eq(int_access_level) + expect(source.users.reload).to include(user) + end + end + + context 'with no current_user' do + context 'when called with a known user id' do + it 'adds the user as a member' do expect(source.users).not_to include(user) - member = described_class.new(source, user.id, int_access_level).execute + described_class.new(source, user.id, :maintainer).execute - expect(member.access_level).to eq(int_access_level) expect(source.users.reload).to include(user) end end - context 'with no current_user' do - context 'when called with a known user id' do - it 'adds the user as a member' do - expect(source.users).not_to include(user) + context 'when called with an unknown user id' do + it 'does not add the user as a member' do + expect(source.users).not_to include(user) - described_class.new(source, user.id, :maintainer).execute + described_class.new(source, non_existing_record_id, :maintainer).execute - expect(source.users.reload).to include(user) - end + expect(source.users.reload).not_to include(user) end + end - context 'when called with an unknown user id' do - it 'does not add the user as a member' do - expect(source.users).not_to include(user) + context 'when called with a user object' do + it 'adds the user as a member' do + expect(source.users).not_to include(user) - described_class.new(source, non_existing_record_id, :maintainer).execute + described_class.new(source, user, :maintainer).execute - expect(source.users.reload).not_to include(user) - end + expect(source.users.reload).to include(user) + end + end + + context 'when called with a requester user object' do + before do + source.request_access(user) end - context 'when called with a user object' do - it 'adds the user as a member' do - expect(source.users).not_to include(user) + it 'adds the requester as a member', :aggregate_failures do + expect(source.users).not_to include(user) + expect(source.requesters.exists?(user_id: user)).to be_truthy + expect do described_class.new(source, user, :maintainer).execute + end.to raise_error(Gitlab::Access::AccessDeniedError) - expect(source.users.reload).to include(user) - end + expect(source.users.reload).not_to include(user) + expect(source.requesters.reload.exists?(user_id: user)).to be_truthy end + end - context 'when called with a requester user object' do - before do - source.request_access(user) - end - - it 'adds the requester as a member', :aggregate_failures do - expect(source.users).not_to include(user) - expect(source.requesters.exists?(user_id: user)).to be_truthy + context 'when called with a known user email' do + it 'adds the user as a member' do + expect(source.users).not_to include(user) - expect do - described_class.new(source, user, :maintainer).execute - end.to raise_error(Gitlab::Access::AccessDeniedError) + described_class.new(source, user.email, :maintainer).execute - expect(source.users.reload).not_to include(user) - expect(source.requesters.reload.exists?(user_id: user)).to be_truthy - end + expect(source.users.reload).to include(user) end + end - context 'when called with a known user email' do - it 'adds the user as a member' do - expect(source.users).not_to include(user) + context 'when called with an unknown user email' do + it 'creates an invited member' do + expect(source.users).not_to include(user) - described_class.new(source, user.email, :maintainer).execute + described_class.new(source, 'user@example.com', :maintainer).execute - expect(source.users.reload).to include(user) - end + expect(source.members.invite.pluck(:invite_email)).to include('user@example.com') end + end - context 'when called with an unknown user email' do - it 'creates an invited member' do - expect(source.users).not_to include(user) + context 'when called with an unknown user email starting with a number' do + it 'creates an invited member', :aggregate_failures do + email_starting_with_number = "#{user.id}_email@example.com" - described_class.new(source, 'user@example.com', :maintainer).execute + described_class.new(source, email_starting_with_number, :maintainer).execute - expect(source.members.invite.pluck(:invite_email)).to include('user@example.com') - end + expect(source.members.invite.pluck(:invite_email)).to include(email_starting_with_number) + expect(source.users.reload).not_to include(user) end + end + end - context 'when called with an unknown user email starting with a number' do - it 'creates an invited member', :aggregate_failures do - email_starting_with_number = "#{user.id}_email@example.com" + context 'when current_user can update member', :enable_admin_mode do + it 'creates the member' do + expect(source.users).not_to include(user) - described_class.new(source, email_starting_with_number, :maintainer).execute + described_class.new(source, user, :maintainer, current_user: admin).execute - expect(source.members.invite.pluck(:invite_email)).to include(email_starting_with_number) - expect(source.users.reload).not_to include(user) - end - end + expect(source.users.reload).to include(user) end - context 'when current_user can update member', :enable_admin_mode do - it 'creates the member' do + context 'when called with a requester user object' do + before do + source.request_access(user) + end + + it 'adds the requester as a member', :aggregate_failures do expect(source.users).not_to include(user) + expect(source.requesters.exists?(user_id: user)).to be_truthy described_class.new(source, user, :maintainer, current_user: admin).execute expect(source.users.reload).to include(user) + expect(source.requesters.reload.exists?(user_id: user)).to be_falsy end + end + end - context 'when called with a requester user object' do - before do - source.request_access(user) - end + context 'when current_user cannot update member' do + it 'does not create the member', :aggregate_failures do + expect(source.users).not_to include(user) - it 'adds the requester as a member', :aggregate_failures do - expect(source.users).not_to include(user) - expect(source.requesters.exists?(user_id: user)).to be_truthy + member = described_class.new(source, user, :maintainer, current_user: user).execute - described_class.new(source, user, :maintainer, current_user: admin).execute + expect(source.users.reload).not_to include(user) + expect(member).not_to be_persisted + end - expect(source.users.reload).to include(user) - expect(source.requesters.reload.exists?(user_id: user)).to be_falsy - end + context 'when called with a requester user object' do + before do + source.request_access(user) end - end - context 'when current_user cannot update member' do - it 'does not create the member', :aggregate_failures do + it 'does not destroy the requester', :aggregate_failures do expect(source.users).not_to include(user) + expect(source.requesters.exists?(user_id: user)).to be_truthy - member = described_class.new(source, user, :maintainer, current_user: user).execute + described_class.new(source, user, :maintainer, current_user: user).execute expect(source.users.reload).not_to include(user) - expect(member).not_to be_persisted + expect(source.requesters.exists?(user_id: user)).to be_truthy end + end + end - context 'when called with a requester user object' do - before do - source.request_access(user) - end + context 'when member already exists' do + before do + source.add_user(user, :developer) + end - it 'does not destroy the requester', :aggregate_failures do - expect(source.users).not_to include(user) - expect(source.requesters.exists?(user_id: user)).to be_truthy + context 'with no current_user' do + it 'updates the member' do + expect(source.users).to include(user) - described_class.new(source, user, :maintainer, current_user: user).execute + described_class.new(source, user, :maintainer).execute - expect(source.users.reload).not_to include(user) - expect(source.requesters.exists?(user_id: user)).to be_truthy - end + expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER) end end - context 'when member already exists' do - before do - source.add_user(user, :developer) - end - - context 'with no current_user' do - it 'updates the member' do - expect(source.users).to include(user) + context 'when current_user can update member', :enable_admin_mode do + it 'updates the member' do + expect(source.users).to include(user) - described_class.new(source, user, :maintainer).execute + described_class.new(source, user, :maintainer, current_user: admin).execute - expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER) - end + expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER) end + end - context 'when current_user can update member', :enable_admin_mode do - it 'updates the member' do - expect(source.users).to include(user) + context 'when current_user cannot update member' do + it 'does not update the member' do + expect(source.users).to include(user) - described_class.new(source, user, :maintainer, current_user: admin).execute + described_class.new(source, user, :maintainer, current_user: user).execute - expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::MAINTAINER) - end + expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::DEVELOPER) end + end + end - context 'when current_user cannot update member' do - it 'does not update the member' do - expect(source.users).to include(user) + context 'when `tasks_to_be_done` and `tasks_project_id` are passed' do + let(:task_project) { source.is_a?(Group) ? create(:project, group: source) : source } - described_class.new(source, user, :maintainer, current_user: user).execute + it 'creates a member_task with the correct attributes', :aggregate_failures do + described_class.new(source, user, :developer, tasks_to_be_done: %w(ci code), tasks_project_id: task_project.id).execute - expect(source.members.find_by(user_id: user).access_level).to eq(Gitlab::Access::DEVELOPER) - end - end - end + member = source.members.last - context 'when `tasks_to_be_done` and `tasks_project_id` are passed' do - let(:task_project) { source.is_a?(Group) ? create(:project, group: source) : source } + expect(member.tasks_to_be_done).to match_array([:ci, :code]) + expect(member.member_task.project).to eq(task_project) + end - it 'creates a member_task with the correct attributes', :aggregate_failures do - described_class.new(source, user, :developer, tasks_to_be_done: %w(ci code), tasks_project_id: task_project.id).execute + context 'with an already existing member' do + before do + source.add_user(user, :developer) + end - member = source.members.last + it 'does not update tasks to be done if tasks already exist', :aggregate_failures do + member = source.members.find_by(user_id: user.id) + create(:member_task, member: member, project: task_project, tasks_to_be_done: %w(code ci)) - expect(member.tasks_to_be_done).to match_array([:ci, :code]) + expect do + described_class.new(source, + user, + :developer, + tasks_to_be_done: %w(issues), + tasks_project_id: task_project.id).execute + end.not_to change(MemberTask, :count) + + member.reset + expect(member.tasks_to_be_done).to match_array([:code, :ci]) expect(member.member_task.project).to eq(task_project) end - context 'with an already existing member' do - before do - source.add_user(user, :developer) - end - - it 'does not update tasks to be done if tasks already exist', :aggregate_failures do - member = source.members.find_by(user_id: user.id) - create(:member_task, member: member, project: task_project, tasks_to_be_done: %w(code ci)) - - expect do - described_class.new(source, - user, - :developer, - tasks_to_be_done: %w(issues), - tasks_project_id: task_project.id).execute - end.not_to change(MemberTask, :count) - - member.reset - expect(member.tasks_to_be_done).to match_array([:code, :ci]) - expect(member.member_task.project).to eq(task_project) - end - - it 'adds tasks to be done if they do not exist', :aggregate_failures do - expect do - described_class.new(source, - user, - :developer, - tasks_to_be_done: %w(issues), - tasks_project_id: task_project.id).execute - end.to change(MemberTask, :count).by(1) - - member = source.members.find_by(user_id: user.id) - expect(member.tasks_to_be_done).to match_array([:issues]) - expect(member.member_task.project).to eq(task_project) - end + it 'adds tasks to be done if they do not exist', :aggregate_failures do + expect do + described_class.new(source, + user, + :developer, + tasks_to_be_done: %w(issues), + tasks_project_id: task_project.id).execute + end.to change(MemberTask, :count).by(1) + + member = source.members.find_by(user_id: user.id) + expect(member.tasks_to_be_done).to match_array([:issues]) + expect(member.member_task.project).to eq(task_project) end end end diff --git a/spec/support/shared_examples/models/reviewer_state_shared_examples.rb b/spec/support/shared_examples/models/reviewer_state_shared_examples.rb deleted file mode 100644 index f1392768b06..00000000000 --- a/spec/support/shared_examples/models/reviewer_state_shared_examples.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -RSpec.shared_examples 'having reviewer state' do - describe 'mr_attention_requests feature flag is disabled' do - before do - stub_feature_flags(mr_attention_requests: false) - end - - it { is_expected.to have_attributes(state: 'unreviewed') } - end - - describe 'mr_attention_requests feature flag is enabled' do - it { is_expected.to have_attributes(state: 'attention_requested') } - end -end diff --git a/spec/support/shared_examples/models/wiki_shared_examples.rb b/spec/support/shared_examples/models/wiki_shared_examples.rb index 03e9dd65e33..6f17231a040 100644 --- a/spec/support/shared_examples/models/wiki_shared_examples.rb +++ b/spec/support/shared_examples/models/wiki_shared_examples.rb @@ -392,41 +392,161 @@ RSpec.shared_examples 'wiki model' do end describe '#create_page' do - it 'creates a new wiki page' do - expect(subject.create_page('test page', 'this is content')).not_to eq(false) - expect(subject.list_pages.count).to eq(1) - end + shared_examples 'create_page tests' do + it 'creates a new wiki page' do + expect(subject.create_page('test page', 'this is content')).not_to eq(false) + expect(subject.list_pages.count).to eq(1) + end - it 'returns false when a duplicate page exists' do - subject.create_page('test page', 'content') + it 'returns false when a duplicate page exists' do + subject.create_page('test page', 'content') - expect(subject.create_page('test page', 'content')).to eq(false) - end + expect(subject.create_page('test page', 'content')).to eq(false) + end - it 'stores an error message when a duplicate page exists' do - 2.times { subject.create_page('test page', 'content') } + it 'stores an error message when a duplicate page exists' do + 2.times { subject.create_page('test page', 'content') } - expect(subject.error_message).to match(/Duplicate page:/) - end + expect(subject.error_message).to match(/Duplicate page:/) + end + + it 'sets the correct commit message' do + subject.create_page('test page', 'some content', :markdown, 'commit message') + + expect(subject.list_pages.first.page.version.message).to eq('commit message') + end + + it 'sets the correct commit email' do + subject.create_page('test page', 'content') + + expect(user.commit_email).not_to eq(user.email) + expect(commit.author_email).to eq(user.commit_email) + expect(commit.committer_email).to eq(user.commit_email) + end + + it 'runs after_wiki_activity callbacks' do + expect(subject).to receive(:after_wiki_activity) - it 'sets the correct commit message' do - subject.create_page('test page', 'some content', :markdown, 'commit message') + subject.create_page('Test Page', 'This is content') + end + + it 'cannot create two pages with the same title but different format' do + subject.create_page('test page', 'content', :markdown) + subject.create_page('test page', 'content', :rdoc) + + expect(subject.error_message).to match(/Duplicate page:/) + end + + it 'cannot create two pages with the same title but different capitalization' do + subject.create_page('test page', 'content') + subject.create_page('Test page', 'content') + + expect(subject.error_message).to match(/Duplicate page:/) + end - expect(subject.list_pages.first.page.version.message).to eq('commit message') + it 'cannot create two pages with the same title, different capitalization, and different format' do + subject.create_page('test page', 'content') + subject.create_page('Test page', 'content', :rdoc) + + expect(subject.error_message).to match(/Duplicate page:/) + end end - it 'sets the correct commit email' do - subject.create_page('test page', 'content') + it_behaves_like 'create_page tests' do + it 'returns false if a page exists already in the repository', :aggregate_failures do + subject.create_page('test page', 'content') - expect(user.commit_email).not_to eq(user.email) - expect(commit.author_email).to eq(user.commit_email) - expect(commit.committer_email).to eq(user.commit_email) + allow(subject).to receive(:file_exists_by_regex?).and_return(false) + + expect(subject.create_page('test page', 'content')).to eq false + expect(subject.error_message).to match(/Duplicate page:/) + end + + it 'returns false if it has an invalid format', :aggregate_failures do + expect(subject.create_page('test page', 'content', :foobar)).to eq false + expect(subject.error_message).to match(/Invalid format selected/) + end + + using RSpec::Parameterized::TableSyntax + + where(:new_file, :format, :existing_repo_files, :success) do + 'foo' | :markdown | [] | true + 'foo' | :rdoc | [] | true + 'foo' | :asciidoc | [] | true + 'foo' | :org | [] | true + 'foo' | :textile | [] | false + 'foo' | :creole | [] | false + 'foo' | :rest | [] | false + 'foo' | :mediawiki | [] | false + 'foo' | :pod | [] | false + 'foo' | :plaintext | [] | false + 'foo' | :markdown | ['foo.md'] | false + 'foo' | :markdown | ['foO.md'] | false + 'foO' | :markdown | ['foo.md'] | false + 'foo' | :markdown | ['foo.mdfoo'] | true + 'foo' | :markdown | ['foo.markdown'] | false + 'foo' | :markdown | ['foo.mkd'] | false + 'foo' | :markdown | ['foo.mkdn'] | false + 'foo' | :markdown | ['foo.mdown'] | false + 'foo' | :markdown | ['foo.adoc'] | false + 'foo' | :markdown | ['foo.asciidoc'] | false + 'foo' | :markdown | ['foo.org'] | false + 'foo' | :markdown | ['foo.rdoc'] | false + 'foo' | :markdown | ['foo.textile'] | false + 'foo' | :markdown | ['foo.creole'] | false + 'foo' | :markdown | ['foo.rest'] | false + 'foo' | :markdown | ['foo.rest.txt'] | false + 'foo' | :markdown | ['foo.rst'] | false + 'foo' | :markdown | ['foo.rst.txt'] | false + 'foo' | :markdown | ['foo.rst.txtfoo'] | true + 'foo' | :markdown | ['foo.mediawiki'] | false + 'foo' | :markdown | ['foo.wiki'] | false + 'foo' | :markdown | ['foo.pod'] | false + 'foo' | :markdown | ['foo.txt'] | false + 'foo' | :markdown | ['foo.Md'] | false + 'foo' | :markdown | ['foo.jpg'] | true + 'foo' | :rdoc | ['foo.md'] | false + 'foo' | :rdoc | ['foO.md'] | false + 'foO' | :rdoc | ['foo.md'] | false + 'foo' | :asciidoc | ['foo.md'] | false + 'foo' | :org | ['foo.md'] | false + 'foo' | :markdown | ['dir/foo.md'] | true + '/foo' | :markdown | ['foo.md'] | false + './foo' | :markdown | ['foo.md'] | false + '../foo' | :markdown | ['foo.md'] | false + '../../foo' | :markdown | ['foo.md'] | false + '../../foo' | :markdown | ['dir/foo.md'] | true + 'dir/foo' | :markdown | ['foo.md'] | true + 'dir/foo' | :markdown | ['dir/foo.md'] | false + 'dir/foo' | :markdown | ['dir/foo.rdoc'] | false + '/dir/foo' | :markdown | ['dir/foo.rdoc'] | false + './dir/foo' | :markdown | ['dir/foo.rdoc'] | false + '../dir/foo' | :markdown | ['dir/foo.rdoc'] | false + '../dir/../foo' | :markdown | ['dir/foo.rdoc'] | true + '../dir/../foo' | :markdown | ['foo.rdoc'] | false + '../dir/../dir/foo' | :markdown | ['dir/foo.rdoc'] | false + '../dir/../another/foo' | :markdown | ['dir/foo.rdoc'] | true + 'another/dir/foo' | :markdown | ['dir/foo.md'] | true + 'foo bar' | :markdown | ['foo-bar.md'] | false + 'foo bar' | :markdown | ['foo-bar.md'] | true + 'föö'.encode('ISO-8859-1') | :markdown | ['f��.md'] | false + end + + with_them do + specify do + allow(subject.repository).to receive(:ls_files).and_return(existing_repo_files) + + expect(subject.create_page(new_file, 'content', format)).to eq success + end + end end - it 'runs after_wiki_activity callbacks' do - expect(subject).to receive(:after_wiki_activity) + context 'when feature flag :gitaly_replace_wiki_create_page is disabled' do + before do + stub_feature_flags(gitaly_replace_wiki_create_page: false) + end - subject.create_page('Test Page', 'This is content') + it_behaves_like 'create_page tests' end end @@ -452,7 +572,7 @@ RSpec.shared_examples 'wiki model' do expect(subject).to receive(:after_wiki_activity) expect(update_page).to eq true - page = subject.find_page(updated_title.presence || original_title) + page = subject.find_page(expected_title) expect(page.raw_content).to eq(updated_content) expect(page.path).to eq(expected_path) @@ -467,23 +587,25 @@ RSpec.shared_examples 'wiki model' do shared_context 'common examples' do using RSpec::Parameterized::TableSyntax - where(:original_title, :original_format, :updated_title, :updated_format, :expected_path) do - 'test page' | :markdown | 'new test page' | :markdown | 'new-test-page.md' - 'test page' | :markdown | 'test page' | :markdown | 'test-page.md' - 'test page' | :markdown | 'test page' | :asciidoc | 'test-page.asciidoc' + where(:original_title, :original_format, :updated_title, :updated_format, :expected_title, :expected_path) do + 'test page' | :markdown | 'new test page' | :markdown | 'new test page' | 'new-test-page.md' + 'test page' | :markdown | 'test page' | :markdown | 'test page' | 'test-page.md' + 'test page' | :markdown | 'test page' | :asciidoc | 'test page' | 'test-page.asciidoc' + + 'test page' | :markdown | 'new dir/new test page' | :markdown | 'new dir/new test page' | 'new-dir/new-test-page.md' + 'test page' | :markdown | 'new dir/test page' | :markdown | 'new dir/test page' | 'new-dir/test-page.md' - 'test page' | :markdown | 'new dir/new test page' | :markdown | 'new-dir/new-test-page.md' - 'test page' | :markdown | 'new dir/test page' | :markdown | 'new-dir/test-page.md' + 'test dir/test page' | :markdown | 'new dir/new test page' | :markdown | 'new dir/new test page' | 'new-dir/new-test-page.md' + 'test dir/test page' | :markdown | 'test dir/test page' | :markdown | 'test dir/test page' | 'test-dir/test-page.md' + 'test dir/test page' | :markdown | 'test dir/test page' | :asciidoc | 'test dir/test page' | 'test-dir/test-page.asciidoc' - 'test dir/test page' | :markdown | 'new dir/new test page' | :markdown | 'new-dir/new-test-page.md' - 'test dir/test page' | :markdown | 'test dir/test page' | :markdown | 'test-dir/test-page.md' - 'test dir/test page' | :markdown | 'test dir/test page' | :asciidoc | 'test-dir/test-page.asciidoc' + 'test dir/test page' | :markdown | 'new test page' | :markdown | 'new test page' | 'new-test-page.md' + 'test dir/test page' | :markdown | 'test page' | :markdown | 'test page' | 'test-page.md' - 'test dir/test page' | :markdown | 'new test page' | :markdown | 'new-test-page.md' - 'test dir/test page' | :markdown | 'test page' | :markdown | 'test-page.md' + 'test page' | :markdown | nil | :markdown | 'test page' | 'test-page.md' + 'test.page' | :markdown | nil | :markdown | 'test.page' | 'test.page.md' - 'test page' | :markdown | nil | :markdown | 'test-page.md' - 'test.page' | :markdown | nil | :markdown | 'test.page.md' + 'testpage' | :markdown | './testpage' | :markdown | 'testpage' | 'testpage.md' end end @@ -497,16 +619,23 @@ RSpec.shared_examples 'wiki model' do shared_context 'extended examples' do using RSpec::Parameterized::TableSyntax - where(:original_title, :original_format, :updated_title, :updated_format, :expected_path) do - 'test page' | :markdown | 'new test page' | :asciidoc | 'new-test-page.asciidoc' - 'test page' | :markdown | 'new dir/new test page' | :asciidoc | 'new-dir/new-test-page.asciidoc' - 'test dir/test page' | :markdown | 'new dir/new test page' | :asciidoc | 'new-dir/new-test-page.asciidoc' - 'test dir/test page' | :markdown | 'new test page' | :asciidoc | 'new-test-page.asciidoc' - 'test page' | :markdown | nil | :asciidoc | 'test-page.asciidoc' - 'test dir/test page' | :markdown | nil | :asciidoc | 'test-dir/test-page.asciidoc' - 'test dir/test page' | :markdown | nil | :markdown | 'test-dir/test-page.md' - 'test page' | :markdown | '' | :markdown | 'test-page.md' - 'test.page' | :markdown | '' | :markdown | 'test.page.md' + where(:original_title, :original_format, :updated_title, :updated_format, :expected_title, :expected_path) do + 'test page' | :markdown | 'new test page' | :asciidoc | 'new test page' | 'new-test-page.asciidoc' + 'test page' | :markdown | 'new dir/new test page' | :asciidoc | 'new dir/new test page' | 'new-dir/new-test-page.asciidoc' + 'test dir/test page' | :markdown | 'new dir/new test page' | :asciidoc | 'new dir/new test page' | 'new-dir/new-test-page.asciidoc' + 'test dir/test page' | :markdown | 'new test page' | :asciidoc | 'new test page' | 'new-test-page.asciidoc' + 'test page' | :markdown | nil | :asciidoc | 'test page' | 'test-page.asciidoc' + 'test dir/test page' | :markdown | nil | :asciidoc | 'test dir/test page' | 'test-dir/test-page.asciidoc' + 'test dir/test page' | :markdown | nil | :markdown | 'test dir/test page' | 'test-dir/test-page.md' + 'test page' | :markdown | '' | :markdown | 'test page' | 'test-page.md' + 'test.page' | :markdown | '' | :markdown | 'test.page' | 'test.page.md' + 'testpage' | :markdown | '../testpage' | :markdown | 'testpage' | 'testpage.md' + 'dir/testpage' | :markdown | 'dir/../testpage' | :markdown | 'testpage' | 'testpage.md' + 'dir/testpage' | :markdown | './dir/testpage' | :markdown | 'dir/testpage' | 'dir/testpage.md' + 'dir/testpage' | :markdown | '../dir/testpage' | :markdown | 'dir/testpage' | 'dir/testpage.md' + 'dir/testpage' | :markdown | '../dir/../testpage' | :markdown | 'testpage' | 'testpage.md' + 'dir/testpage' | :markdown | '../dir/../dir/testpage' | :markdown | 'dir/testpage' | 'dir/testpage.md' + 'dir/testpage' | :markdown | '../dir/../another/testpage' | :markdown | 'another/testpage' | 'another/testpage.md' end end @@ -547,16 +676,6 @@ RSpec.shared_examples 'wiki model' do end end end - - context 'when feature flag :gitaly_replace_wiki_update_page is disabled' do - before do - stub_feature_flags(gitaly_replace_wiki_update_page: false) - end - - it_behaves_like 'update_page tests' do - include_context 'common examples' - end - end end describe '#delete_page' do |