Commit fd6e3781 authored by Sean McGivern's avatar Sean McGivern

Merge branch 'revert-e2aa2177' into 'master'

Revert "Merge branch 'zj-improve-gitaly-pb' into 'master'"

See merge request gitlab-org/gitlab-ce!23229
parents 52272f92 2742b871
---
title: Show what RPC is called in the performance bar
merge_request: 23140
author:
type: other
# frozen_string_literal: true # frozen_string_literal: true
# Gitaly note: JV: seems to be completely migrated (behind feature flags).
module Gitlab module Gitlab
module Git module Git
class Blob class Blob
......
...@@ -885,6 +885,12 @@ module Gitlab ...@@ -885,6 +885,12 @@ module Gitlab
Gitlab::GitalyClient::ConflictsService.new(self, our_commit_oid, their_commit_oid) Gitlab::GitalyClient::ConflictsService.new(self, our_commit_oid, their_commit_oid)
end end
def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block)
wrapped_gitaly_errors do
Gitlab::GitalyClient.migrate(method, status: status, &block)
end
end
def clean_stale_repository_files def clean_stale_repository_files
wrapped_gitaly_errors do wrapped_gitaly_errors do
gitaly_repository_client.cleanup if exists? gitaly_repository_client.cleanup if exists?
......
...@@ -9,6 +9,11 @@ require 'grpc/health/v1/health_services_pb' ...@@ -9,6 +9,11 @@ require 'grpc/health/v1/health_services_pb'
module Gitlab module Gitlab
module GitalyClient module GitalyClient
include Gitlab::Metrics::Methods include Gitlab::Metrics::Methods
module MigrationStatus
DISABLED = 1
OPT_IN = 2
OPT_OUT = 3
end
class TooManyInvocationsError < StandardError class TooManyInvocationsError < StandardError
attr_reader :call_site, :invocation_count, :max_call_stack attr_reader :call_site, :invocation_count, :max_call_stack
...@@ -26,7 +31,7 @@ module Gitlab ...@@ -26,7 +31,7 @@ module Gitlab
end end
end end
SERVER_VERSION_FILE = 'GITALY_SERVER_VERSION' SERVER_VERSION_FILE = 'GITALY_SERVER_VERSION'.freeze
MAXIMUM_GITALY_CALLS = 35 MAXIMUM_GITALY_CALLS = 35
CLIENT_NAME = (Sidekiq.server? ? 'gitlab-sidekiq' : 'gitlab-web').freeze CLIENT_NAME = (Sidekiq.server? ? 'gitlab-sidekiq' : 'gitlab-web').freeze
...@@ -38,6 +43,11 @@ module Gitlab ...@@ -38,6 +43,11 @@ module Gitlab
self.query_time = 0 self.query_time = 0
define_histogram :gitaly_migrate_call_duration_seconds do
docstring "Gitaly migration call execution timings"
base_labels gitaly_enabled: nil, feature: nil
end
define_histogram :gitaly_controller_action_duration_seconds do define_histogram :gitaly_controller_action_duration_seconds do
docstring "Gitaly endpoint histogram by controller and action combination" docstring "Gitaly endpoint histogram by controller and action combination"
base_labels Gitlab::Metrics::Transaction::BASE_LABELS.merge(gitaly_service: nil, rpc: nil) base_labels Gitlab::Metrics::Transaction::BASE_LABELS.merge(gitaly_service: nil, rpc: nil)
...@@ -116,6 +126,7 @@ module Gitlab ...@@ -116,6 +126,7 @@ module Gitlab
def self.call(storage, service, rpc, request, remote_storage: nil, timeout: nil) def self.call(storage, service, rpc, request, remote_storage: nil, timeout: nil)
start = Gitlab::Metrics::System.monotonic_time start = Gitlab::Metrics::System.monotonic_time
request_hash = request.is_a?(Google::Protobuf::MessageExts) ? request.to_h : {} request_hash = request.is_a?(Google::Protobuf::MessageExts) ? request.to_h : {}
@current_call_id ||= SecureRandom.uuid
enforce_gitaly_request_limits(:call) enforce_gitaly_request_limits(:call)
...@@ -134,7 +145,9 @@ module Gitlab ...@@ -134,7 +145,9 @@ module Gitlab
current_transaction_labels.merge(gitaly_service: service.to_s, rpc: rpc.to_s), current_transaction_labels.merge(gitaly_service: service.to_s, rpc: rpc.to_s),
duration) duration)
add_call_details(feature: "#{service}##{rpc}", duration: duration, request: request_hash, rpc: rpc) add_call_details(id: @current_call_id, feature: service, duration: duration, request: request_hash)
@current_call_id = nil
end end
def self.handle_grpc_unavailable!(ex) def self.handle_grpc_unavailable!(ex)
...@@ -209,7 +222,7 @@ module Gitlab ...@@ -209,7 +222,7 @@ module Gitlab
result result
end end
SERVER_FEATURE_FLAGS = %w[].freeze SERVER_FEATURE_FLAGS = %w[gogit_findcommit].freeze
def self.server_feature_flags def self.server_feature_flags
SERVER_FEATURE_FLAGS.map do |f| SERVER_FEATURE_FLAGS.map do |f|
...@@ -224,8 +237,82 @@ module Gitlab ...@@ -224,8 +237,82 @@ module Gitlab
params['gitaly_token'].presence || Gitlab.config.gitaly['token'] params['gitaly_token'].presence || Gitlab.config.gitaly['token']
end end
def self.feature_enabled?(feature_name) # Evaluates whether a feature toggle is on or off
Feature.enabled?("gitaly_#{feature_name}") def self.feature_enabled?(feature_name, status: MigrationStatus::OPT_IN)
# Disabled features are always off!
return false if status == MigrationStatus::DISABLED
feature = Feature.get("gitaly_#{feature_name}")
# If the feature has been set, always evaluate
if Feature.persisted?(feature)
if feature.percentage_of_time_value > 0
# Probabilistically enable this feature
return Random.rand() * 100 < feature.percentage_of_time_value
end
return feature.enabled?
end
# If the feature has not been set, the default depends
# on it's status
case status
when MigrationStatus::OPT_OUT
true
when MigrationStatus::OPT_IN
opt_into_all_features? && !explicit_opt_in_required.include?(feature_name)
else
false
end
rescue => ex
# During application startup feature lookups in SQL can fail
Rails.logger.warn "exception while checking Gitaly feature status for #{feature_name}: #{ex}"
false
end
# We have a mechanism to let GitLab automatically opt in to all Gitaly
# features. We want to be able to exclude some features from automatic
# opt-in. This function has an override in EE.
def self.explicit_opt_in_required
[]
end
# opt_into_all_features? returns true when the current environment
# is one in which we opt into features automatically
def self.opt_into_all_features?
Rails.env.development? || ENV["GITALY_FEATURE_DEFAULT_ON"] == "1"
end
private_class_method :opt_into_all_features?
def self.migrate(feature, status: MigrationStatus::OPT_IN)
# Enforce limits at both the `migrate` and `call` sites to ensure that
# problems are not hidden by a feature being disabled
enforce_gitaly_request_limits(:migrate)
is_enabled = feature_enabled?(feature, status: status)
metric_name = feature.to_s
metric_name += "_gitaly" if is_enabled
Gitlab::Metrics.measure(metric_name) do
# Some migrate calls wrap other migrate calls
allow_n_plus_1_calls do
feature_stack = Thread.current[:gitaly_feature_stack] ||= []
feature_stack.unshift(feature)
begin
start = Gitlab::Metrics::System.monotonic_time
@current_call_id = SecureRandom.uuid
call_details = { id: @current_call_id }
yield is_enabled
ensure
total_time = Gitlab::Metrics::System.monotonic_time - start
gitaly_migrate_call_duration_seconds.observe({ gitaly_enabled: is_enabled, feature: feature }, total_time)
feature_stack.shift
Thread.current[:gitaly_feature_stack] = nil if feature_stack.empty?
add_call_details(call_details.merge(feature: feature, duration: total_time))
end
end
end
end end
# Ensures that Gitaly is not being abuse through n+1 misuse etc # Ensures that Gitaly is not being abuse through n+1 misuse etc
...@@ -281,20 +368,38 @@ module Gitlab ...@@ -281,20 +368,38 @@ module Gitlab
end end
private_class_method :decrement_call_count private_class_method :decrement_call_count
# Returns the of the number of Gitaly calls made for this request # Returns an estimate of the number of Gitaly calls made for this
# request
def self.get_request_count def self.get_request_count
get_call_count("gitaly_call_actual") return 0 unless Gitlab::SafeRequestStore.active?
gitaly_migrate_count = get_call_count("gitaly_migrate_actual")
gitaly_call_count = get_call_count("gitaly_call_actual")
# Using the maximum of migrate and call_count will provide an
# indicator of how many Gitaly calls will be made, even
# before a feature is enabled. This provides us with a single
# metric, but not an exact number, but this tradeoff is acceptable
if gitaly_migrate_count > gitaly_call_count
gitaly_migrate_count
else
gitaly_call_count
end
end end
def self.reset_counts def self.reset_counts
return unless Gitlab::SafeRequestStore.active? return unless Gitlab::SafeRequestStore.active?
Gitlab::SafeRequestStore["gitaly_call_actual"] = 0 %w[migrate call].each do |call_site|
Gitlab::SafeRequestStore["gitaly_call_permitted"] = 0 Gitlab::SafeRequestStore["gitaly_#{call_site}_actual"] = 0
Gitlab::SafeRequestStore["gitaly_#{call_site}_permitted"] = 0
end
end end
def self.add_call_details(details) def self.add_call_details(details)
return unless Gitlab::SafeRequestStore[:peek_enabled] id = details.delete(:id)
return unless id && Gitlab::SafeRequestStore[:peek_enabled]
Gitlab::SafeRequestStore['gitaly_call_details'] ||= {} Gitlab::SafeRequestStore['gitaly_call_details'] ||= {}
Gitlab::SafeRequestStore['gitaly_call_details'][id] ||= {} Gitlab::SafeRequestStore['gitaly_call_details'][id] ||= {}
......
require 'spec_helper' require 'spec_helper'
describe 'User updates wiki page' do describe 'User updates wiki page' do
let(:user) { create(:user) } shared_examples 'wiki page user update' do
let(:user) { create(:user) }
before do
project.add_maintainer(user)
sign_in(user)
end
context 'when wiki is empty' do
before do before do
visit(project_wikis_path(project)) project.add_maintainer(user)
click_link "Create your first page" sign_in(user)
end end
context 'in a user namespace' do context 'when wiki is empty' do
let(:project) { create(:project, :wiki_repo) } before do
visit(project_wikis_path(project))
click_link "Create your first page"
end
context 'in a user namespace' do
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
it 'redirects back to the home edit page' do it 'redirects back to the home edit page' do
page.within(:css, '.wiki-form .form-actions') do page.within(:css, '.wiki-form .form-actions') do
click_on('Cancel') click_on('Cancel')
end
expect(current_path).to eq project_wiki_path(project, :home)
end end
expect(current_path).to eq project_wiki_path(project, :home) it 'updates a page that has a path', :js do
end click_on('New page')
it 'updates a page that has a path', :js do page.within('#modal-new-wiki') do
click_on('New page') fill_in(:new_wiki_path, with: 'one/two/three-test')
click_on('Create page')
end
page.within('#modal-new-wiki') do page.within '.wiki-form' do
fill_in(:new_wiki_path, with: 'one/two/three-test') fill_in(:wiki_content, with: 'wiki content')
click_on('Create page') click_on('Create page')
end end
page.within '.wiki-form' do expect(current_path).to include('one/two/three-test')
fill_in(:wiki_content, with: 'wiki content') expect(find('.wiki-pages')).to have_content('Three')
click_on('Create page')
end
expect(current_path).to include('one/two/three-test') first(:link, text: 'Three').click
expect(find('.wiki-pages')).to have_content('Three')
first(:link, text: 'Three').click expect(find('.nav-text')).to have_content('Three')
expect(find('.nav-text')).to have_content('Three') click_on('Edit')
click_on('Edit') expect(current_path).to include('one/two/three-test')
expect(page).to have_content('Edit Page')
expect(current_path).to include('one/two/three-test') fill_in('Content', with: 'Updated Wiki Content')
expect(page).to have_content('Edit Page') click_on('Save changes')
fill_in('Content', with: 'Updated Wiki Content') expect(page).to have_content('Updated Wiki Content')
click_on('Save changes') end
expect(page).to have_content('Updated Wiki Content') it_behaves_like 'wiki file attachments'
end end
it_behaves_like 'wiki file attachments'
end end
end
context 'when wiki is not empty' do context 'when wiki is not empty' do
let(:project_wiki) { create(:project_wiki, project: project, user: project.creator) } let(:project_wiki) { create(:project_wiki, project: project, user: project.creator) }
let!(:wiki_page) { create(:wiki_page, wiki: project_wiki, attrs: { title: 'home', content: 'Home page' }) } let!(:wiki_page) { create(:wiki_page, wiki: project_wiki, attrs: { title: 'home', content: 'Home page' }) }
before do before do
visit(project_wikis_path(project)) visit(project_wikis_path(project))
click_link('Edit') click_link('Edit')
end end
context 'in a user namespace' do context 'in a user namespace' do
let(:project) { create(:project, :wiki_repo) } let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
it 'updates a page' do it 'updates a page' do
# Commit message field should have correct value. # Commit message field should have correct value.
expect(page).to have_field('wiki[message]', with: 'Update home') expect(page).to have_field('wiki[message]', with: 'Update home')
fill_in(:wiki_content, with: 'My awesome wiki!') fill_in(:wiki_content, with: 'My awesome wiki!')
click_button('Save changes') click_button('Save changes')
expect(page).to have_content('Home') expect(page).to have_content('Home')
expect(page).to have_content("Last edited by #{user.name}") expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!') expect(page).to have_content('My awesome wiki!')
end end
it 'shows a validation error message' do it 'shows a validation error message' do
fill_in(:wiki_content, with: '') fill_in(:wiki_content, with: '')
click_button('Save changes') click_button('Save changes')
expect(page).to have_selector('.wiki-form') expect(page).to have_selector('.wiki-form')
expect(page).to have_content('Edit Page') expect(page).to have_content('Edit Page')
expect(page).to have_content('The form contains the following error:') expect(page).to have_content('The form contains the following error:')
expect(page).to have_content("Content can't be blank") expect(page).to have_content("Content can't be blank")
expect(find('textarea#wiki_content').value).to eq('') expect(find('textarea#wiki_content').value).to eq('')
end end
it 'shows the emoji autocompletion dropdown', :js do it 'shows the emoji autocompletion dropdown', :js do
find('#wiki_content').native.send_keys('') find('#wiki_content').native.send_keys('')
fill_in(:wiki_content, with: ':') fill_in(:wiki_content, with: ':')
expect(page).to have_selector('.atwho-view') expect(page).to have_selector('.atwho-view')
end end
it 'shows the error message' do it 'shows the error message' do
wiki_page.update(content: 'Update') wiki_page.update(content: 'Update')
click_button('Save changes') click_button('Save changes')
expect(page).to have_content('Someone edited the page the same time you did.') expect(page).to have_content('Someone edited the page the same time you did.')
end end
it 'updates a page' do it 'updates a page' do
fill_in('Content', with: 'Updated Wiki Content') fill_in('Content', with: 'Updated Wiki Content')
click_on('Save changes') click_on('Save changes')
expect(page).to have_content('Updated Wiki Content') expect(page).to have_content('Updated Wiki Content')
end end
it 'cancels editing of a page' do it 'cancels editing of a page' do
page.within(:css, '.wiki-form .form-actions') do page.within(:css, '.wiki-form .form-actions') do
click_on('Cancel') click_on('Cancel')
end
expect(current_path).to eq(project_wiki_path(project, wiki_page))
end end
expect(current_path).to eq(project_wiki_path(project, wiki_page)) it_behaves_like 'wiki file attachments'
end end
it_behaves_like 'wiki file attachments' context 'in a group namespace' do
end let(:project) { create(:project, :wiki_repo, namespace: create(:group, :public)) }
context 'in a group namespace' do it 'updates a page' do
let(:project) { create(:project, :wiki_repo, namespace: create(:group, :public)) } # Commit message field should have correct value.
expect(page).to have_field('wiki[message]', with: 'Update home')
it 'updates a page' do fill_in(:wiki_content, with: 'My awesome wiki!')
# Commit message field should have correct value.
expect(page).to have_field('wiki[message]', with: 'Update home')
fill_in(:wiki_content, with: 'My awesome wiki!') click_button('Save changes')
click_button('Save changes') expect(page).to have_content('Home')
expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end
expect(page).to have_content('Home') it_behaves_like 'wiki file attachments'
expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!')
end end
it_behaves_like 'wiki file attachments'
end end
end
context 'when the page is in a subdir' do context 'when the page is in a subdir' do
let!(:project) { create(:project, :wiki_repo) } let!(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:project_wiki) { create(:project_wiki, project: project, user: project.creator) } let(:project_wiki) { create(:project_wiki, project: project, user: project.creator) }
let(:page_name) { 'page_name' } let(:page_name) { 'page_name' }
let(:page_dir) { "foo/bar/#{page_name}" } let(:page_dir) { "foo/bar/#{page_name}" }
let!(:wiki_page) { create(:wiki_page, wiki: project_wiki, attrs: { title: page_dir, content: 'Home page' }) } let!(:wiki_page) { create(:wiki_page, wiki: project_wiki, attrs: { title: page_dir, content: 'Home page' }) }
before do before do
visit(project_wiki_edit_path(project, wiki_page)) visit(project_wiki_edit_path(project, wiki_page))
end end
it 'moves the page to the root folder' do it 'moves the page to the root folder', :skip_gitaly_mock do
fill_in(:wiki_title, with: "/#{page_name}") fill_in(:wiki_title, with: "/#{page_name}")
click_button('Save changes') click_button('Save changes')
expect(current_path).to eq(project_wiki_path(project, page_name)) expect(current_path).to eq(project_wiki_path(project, page_name))
end end
it 'moves the page to other dir' do it 'moves the page to other dir' do
new_page_dir = "foo1/bar1/#{page_name}" new_page_dir = "foo1/bar1/#{page_name}"
fill_in(:wiki_title, with: new_page_dir) fill_in(:wiki_title, with: new_page_dir)
click_button('Save changes') click_button('Save changes')
expect(current_path).to eq(project_wiki_path(project, new_page_dir)) expect(current_path).to eq(project_wiki_path(project, new_page_dir))
end end
it 'remains in the same place if title has not changed' do
original_path = project_wiki_path(project, wiki_page)
it 'remains in the same place if title has not changed' do fill_in(:wiki_title, with: page_name)
original_path = project_wiki_path(project, wiki_page)
fill_in(:wiki_title, with: page_name) click_button('Save changes')
click_button('Save changes') expect(current_path).to eq(original_path)
end
expect(current_path).to eq(original_path) it 'can be moved to a different dir with a different name' do
end new_page_dir = "foo1/bar1/new_page_name"
it 'can be moved to a different dir with a different name' do fill_in(:wiki_title, with: new_page_dir)
new_page_dir = "foo1/bar1/new_page_name"
fill_in(:wiki_title, with: new_page_dir) click_button('Save changes')
click_button('Save changes') expect(current_path).to eq(project_wiki_path(project, new_page_dir))
end
expect(current_path).to eq(project_wiki_path(project, new_page_dir)) it 'can be renamed and moved to the root folder' do
end new_name = 'new_page_name'
it 'can be renamed and moved to the root folder' do fill_in(:wiki_title, with: "/#{new_name}")
new_name = 'new_page_name'
fill_in(:wiki_title, with: "/#{new_name}") click_button('Save changes')
click_button('Save changes') expect(current_path).to eq(project_wiki_path(project, new_name))
end
expect(current_path).to eq(project_wiki_path(project, new_name)) it 'squishes the title before creating the page' do
end new_page_dir = " foo1 / bar1 / #{page_name} "
it 'squishes the title before creating the page' do fill_in(:wiki_title, with: new_page_dir)
new_page_dir = " foo1 / bar1 / #{page_name} "
fill_in(:wiki_title, with: new_page_dir) click_button('Save changes')
click_button('Save changes') expect(current_path).to eq(project_wiki_path(project, "foo1/bar1/#{page_name}"))
end
expect(current_path).to eq(project_wiki_path(project, "foo1/bar1/#{page_name}")) it_behaves_like 'wiki file attachments'
end end
end
context 'when Gitaly is enabled' do
it_behaves_like 'wiki page user update'
end
it_behaves_like 'wiki file attachments' context 'when Gitaly is disabled', :skip_gitaly_mock do
it_behaves_like 'wiki page user update'
end end
end end
require 'spec_helper' require 'spec_helper'
describe 'User views a wiki page' do describe 'User views a wiki page' do
include WikiHelpers shared_examples 'wiki page user view' do
include WikiHelpers
let(:user) { create(:user) }
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) } let(:user) { create(:user) }
let(:path) { 'image.png' } let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:wiki_page) do let(:path) { 'image.png' }
create(:wiki_page, let(:wiki_page) do
wiki: project.wiki, create(:wiki_page,
attrs: { title: 'home', content: "Look at this [image](#{path})\n\n ![alt text](#{path})" }) wiki: project.wiki,
end attrs: { title: 'home', content: "Look at this [image](#{path})\n\n ![alt text](#{path})" })
end
before do
project.add_maintainer(user)
sign_in(user)
end
context 'when wiki is empty' do
before do before do
visit(project_wikis_path(project)) project.add_maintainer(user)
click_link "Create your first page" sign_in(user)
end
click_on('New page') context 'when wiki is empty' do
before do
visit(project_wikis_path(project))
click_link "Create your first page"
page.within('#modal-new-wiki') do click_on('New page')
fill_in(:new_wiki_path, with: 'one/two/three-test')
click_on('Create page') page.within('#modal-new-wiki') do
end fill_in(:new_wiki_path, with: 'one/two/three-test')
click_on('Create page')
end
page.within('.wiki-form') do page.within('.wiki-form') do
fill_in(:wiki_content, with: 'wiki content') fill_in(:wiki_content, with: 'wiki content')
click_on('Create page') click_on('Create page')
end
end end
end
it 'shows the history of a page that has a path', :js do it 'shows the history of a page that has a path', :js do
expect(current_path).to include('one/two/three-test') expect(current_path).to include('one/two/three-test')
first(:link, text: 'Three').click first(:link, text: 'Three').click
click_on('Page history') click_on('Page history')
expect(current_path).to include('one/two/three-test') expect(current_path).to include('one/two/three-test')
page.within(:css, '.nav-text') do page.within(:css, '.nav-text') do
expect(page).to have_content('History') expect(page).to have_content('History')
end
end end
end
it 'shows an old version of a page', :js do it 'shows an old version of a page', :js do
expect(current_path).to include('one/two/three-test') expect(current_path).to include('one/two/three-test')
expect(find('.wiki-pages')).to have_content('Three') expect(find('.wiki-pages')).to have_content('Three')
first(:link, text: 'Three').click first(:link, text: 'Three').click
expect(find('.nav-text')).to have_content('Three') expect(find('.nav-text')).to have_content('Three')
click_on('Edit') click_on('Edit')
expect(current_path).to include('one/two/three-test') expect(current_path).to include('one/two/three-test')
expect(page).to have_content('Edit Page') expect(page).to have_content('Edit Page')
fill_in('Content', with: 'Updated Wiki Content') fill_in('Content', with: 'Updated Wiki Content')
click_on('Save changes') click_on('Save changes')
click_on('Page history') click_on('Page history')
page.within(:css, '.nav-text') do page.within(:css, '.nav-text') do
expect(page).to have_content('History') expect(page).to have_content('History')
end end
find('a[href*="?version_id"]') find('a[href*="?version_id"]')
end
end end
end
context 'when a page does not have history' do context 'when a page does not have history' do
before do before do
visit(project_wiki_path(project, wiki_page)) visit(project_wiki_path(project, wiki_page))
end end
it 'shows all the pages' do it 'shows all the pages' do
expect(page).to have_content(user.name) expect(page).to have_content(user.name)
expect(find('.wiki-pages')).to have_content(wiki_page.title.capitalize) expect(find('.wiki-pages')).to have_content(wiki_page.title.capitalize)
end end
context 'shows a file stored in a page' do
let(:path) { upload_file_to_wiki(project, user, 'dk.png') }
it do
expect(page).to have_xpath("//img[@data-src='#{project.wiki.wiki_base_path}/#{path}']")
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}")
click_on('image')
context 'shows a file stored in a page' do expect(current_path).to match("wikis/#{path}")
let(:path) { upload_file_to_wiki(project, user, 'dk.png') } expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved
end
end
it do it 'shows the creation page if file does not exist' do
expect(page).to have_xpath("//img[@data-src='#{project.wiki.wiki_base_path}/#{path}']")
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}") expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}")
click_on('image') click_on('image')
expect(current_path).to match("wikis/#{path}") expect(current_path).to match("wikis/#{path}")
expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved expect(page).to have_content('New Wiki Page')
expect(page).to have_content('Create page')
end end
end end
it 'shows the creation page if file does not exist' do context 'when a page has history' do
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}") before do
wiki_page.update(message: 'updated home', content: 'updated [some link](other-page)')
end
click_on('image') it 'shows the page history' do
visit(project_wiki_path(project, wiki_page))
expect(current_path).to match("wikis/#{path}") expect(page).to have_selector('a.btn', text: 'Edit')
expect(page).to have_content('New Wiki Page')
expect(page).to have_content('Create page')
end
end
context 'when a page has history' do click_on('Page history')
before do
wiki_page.update(message: 'updated home', content: 'updated [some link](other-page)')
end
it 'shows the page history' do expect(page).to have_content(user.name)
visit(project_wiki_path(project, wiki_page)) expect(page).to have_content("#{user.username} created page: home")
expect(page).to have_content('updated home')
expect(page).to have_selector('a.btn', text: 'Edit') end
click_on('Page history') it 'does not show the "Edit" button' do
visit(project_wiki_path(project, wiki_page, version_id: wiki_page.versions.last.id))
expect(page).to have_content(user.name) expect(page).not_to have_selector('a.btn', text: 'Edit')
expect(page).to have_content("#{user.username} created page: home") end
expect(page).to have_content('updated home')
end end
it 'does not show the "Edit" button' do context 'when page has invalid content encoding' do
visit(project_wiki_path(project, wiki_page, version_id: wiki_page.versions.last.id)) let(:content) { 'whatever'.force_encoding('ISO-8859-1') }
expect(page).not_to have_selector('a.btn', text: 'Edit') before do
end allow(Gitlab::EncodingHelper).to receive(:encode!).and_return(content)
end
context 'when page has invalid content encoding' do visit(project_wiki_path(project, wiki_page))
let(:content) { 'whatever'.force_encoding('ISO-8859-1') } end
before do it 'does not show "Edit" button' do
allow(Gitlab::EncodingHelper).to receive(:encode!).and_return(content) expect(page).not_to have_selector('a.btn', text: 'Edit')
end
visit(project_wiki_path(project, wiki_page)) it 'shows error' do
page.within(:css, '.flash-notice') do
expect(page).to have_content('The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository.')
end
end
end end
it 'does not show "Edit" button' do it 'opens a default wiki page', :js do
expect(page).not_to have_selector('a.btn', text: 'Edit') visit(project_path(project))
end
it 'shows error' do find('.shortcuts-wiki').click
page.within(:css, '.flash-notice') do click_link "Create your first page"
expect(page).to have_content('The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository.')
end expect(page).to have_content('Home · Create Page')
end end
end end
it 'opens a default wiki page', :js do context 'when Gitaly is enabled' do
visit(project_path(project)) it_behaves_like 'wiki page user view'
end
find('.shortcuts-wiki').click
click_link "Create your first page"
expect(page).to have_content('Home · Create Page') context 'when Gitaly is disabled', :skip_gitaly_mock do
it_behaves_like 'wiki page user view'
end end
end end
...@@ -205,18 +205,28 @@ describe ExtractsPath do ...@@ -205,18 +205,28 @@ describe ExtractsPath do
end end
describe '#lfs_blob_ids' do describe '#lfs_blob_ids' do
let(:tag) { @project.repository.add_tag(@project.owner, 'my-annotated-tag', 'master', 'test tag') } shared_examples '#lfs_blob_ids' do
let(:ref) { tag.target } let(:tag) { @project.repository.add_tag(@project.owner, 'my-annotated-tag', 'master', 'test tag') }
let(:params) { { ref: ref, path: 'README.md' } } let(:ref) { tag.target }
let(:params) { { ref: ref, path: 'README.md' } }
before do before do
@project = create(:project, :repository) @project = create(:project, :repository)
end
it 'handles annotated tags' do
assign_ref_vars
expect(lfs_blob_ids).to eq([])
end
end end
it 'handles annotated tags' do context 'when gitaly is enabled' do
assign_ref_vars it_behaves_like '#lfs_blob_ids'
end
expect(lfs_blob_ids).to eq([]) context 'when gitaly is disabled', :skip_gitaly_mock do
it_behaves_like '#lfs_blob_ids'
end end
end end
end end
...@@ -37,7 +37,17 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiff do ...@@ -37,7 +37,17 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiff do
let(:stub_path) { '.gitignore' } let(:stub_path) { '.gitignore' }
end end
it 'returns a valid instance of a DiffCollection' do shared_examples 'initializes a DiffCollection' do
expect(diff_files).to be_a(Gitlab::Git::DiffCollection) it 'returns a valid instance of a DiffCollection' do
expect(diff_files).to be_a(Gitlab::Git::DiffCollection)
end
end
context 'with Gitaly disabled', :disable_gitaly do
it_behaves_like 'initializes a DiffCollection'
end
context 'with Gitaly enabled' do
it_behaves_like 'initializes a DiffCollection'
end end
end end
...@@ -128,7 +128,7 @@ describe Gitlab::Git::Blob, :seed_helper do ...@@ -128,7 +128,7 @@ describe Gitlab::Git::Blob, :seed_helper do
end end
end end
describe '.raw' do shared_examples 'finding blobs by ID' do
let(:raw_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::RubyBlob::ID) } let(:raw_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::RubyBlob::ID) }
let(:bad_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::BigCommit::ID) } let(:bad_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::BigCommit::ID) }
...@@ -166,6 +166,16 @@ describe Gitlab::Git::Blob, :seed_helper do ...@@ -166,6 +166,16 @@ describe Gitlab::Git::Blob, :seed_helper do
end end
end end
describe '.raw' do
context 'when the blob_raw Gitaly feature is enabled' do
it_behaves_like 'finding blobs by ID'
end
context 'when the blob_raw Gitaly feature is disabled', :skip_gitaly_mock do
it_behaves_like 'finding blobs by ID'
end
end
describe '.batch' do describe '.batch' do
let(:blob_references) do let(:blob_references) do
[ [
......
...@@ -183,100 +183,110 @@ describe Gitlab::Git::Commit, :seed_helper do ...@@ -183,100 +183,110 @@ describe Gitlab::Git::Commit, :seed_helper do
end end
end end
context 'path is empty string' do shared_examples '.where' do
subject do context 'path is empty string' do
commits = described_class.where( subject do
repo: repository, commits = described_class.where(
ref: 'master', repo: repository,
path: '', ref: 'master',
limit: 10 path: '',
) limit: 10
)
commits.map { |c| c.id } commits.map { |c| c.id }
end end
it 'has 10 elements' do it 'has 10 elements' do
expect(subject.size).to eq(10) expect(subject.size).to eq(10)
end
it { is_expected.to include(SeedRepo::EmptyCommit::ID) }
end end
it { is_expected.to include(SeedRepo::EmptyCommit::ID) }
end
context 'path is nil' do context 'path is nil' do
subject do subject do
commits = described_class.where( commits = described_class.where(
repo: repository, repo: repository,
ref: 'master', ref: 'master',
path: nil, path: nil,
limit: 10 limit: 10
) )
commits.map { |c| c.id } commits.map { |c| c.id }
end end
it 'has 10 elements' do it 'has 10 elements' do
expect(subject.size).to eq(10) expect(subject.size).to eq(10)
end
it { is_expected.to include(SeedRepo::EmptyCommit::ID) }
end end
it { is_expected.to include(SeedRepo::EmptyCommit::ID) }
end
context 'ref is branch name' do context 'ref is branch name' do
subject do subject do
commits = described_class.where( commits = described_class.where(
repo: repository, repo: repository,
ref: 'master', ref: 'master',
path: 'files', path: 'files',
limit: 3, limit: 3,
offset: 1 offset: 1
) )
commits.map { |c| c.id } commits.map { |c| c.id }
end end
it 'has 3 elements' do it 'has 3 elements' do
expect(subject.size).to eq(3) expect(subject.size).to eq(3)
end
it { is_expected.to include("d14d6c0abdd253381df51a723d58691b2ee1ab08") }
it { is_expected.not_to include("eb49186cfa5c4338011f5f590fac11bd66c5c631") }
end end
it { is_expected.to include("d14d6c0abdd253381df51a723d58691b2ee1ab08") }
it { is_expected.not_to include("eb49186cfa5c4338011f5f590fac11bd66c5c631") }
end
context 'ref is commit id' do context 'ref is commit id' do
subject do subject do
commits = described_class.where( commits = described_class.where(
repo: repository, repo: repository,
ref: "874797c3a73b60d2187ed6e2fcabd289ff75171e", ref: "874797c3a73b60d2187ed6e2fcabd289ff75171e",
path: 'files', path: 'files',
limit: 3, limit: 3,
offset: 1 offset: 1
) )
commits.map { |c| c.id } commits.map { |c| c.id }
end end
it 'has 3 elements' do it 'has 3 elements' do
expect(subject.size).to eq(3) expect(subject.size).to eq(3)
end
it { is_expected.to include("2f63565e7aac07bcdadb654e253078b727143ec4") }
it { is_expected.not_to include(SeedRepo::Commit::ID) }
end end
it { is_expected.to include("2f63565e7aac07bcdadb654e253078b727143ec4") }
it { is_expected.not_to include(SeedRepo::Commit::ID) }
end
context 'ref is tag' do context 'ref is tag' do
subject do subject do
commits = described_class.where( commits = described_class.where(
repo: repository, repo: repository,
ref: 'v1.0.0', ref: 'v1.0.0',
path: 'files', path: 'files',
limit: 3, limit: 3,
offset: 1 offset: 1
) )
commits.map { |c| c.id } commits.map { |c| c.id }
end end
it 'has 3 elements' do it 'has 3 elements' do
expect(subject.size).to eq(3) expect(subject.size).to eq(3)
end
it { is_expected.to include("874797c3a73b60d2187ed6e2fcabd289ff75171e") }
it { is_expected.not_to include(SeedRepo::Commit::ID) }
end end
it { is_expected.to include("874797c3a73b60d2187ed6e2fcabd289ff75171e") } end
it { is_expected.not_to include(SeedRepo::Commit::ID) }
describe '.where with gitaly' do
it_should_behave_like '.where'
end
describe '.where without gitaly', :skip_gitaly_mock do
it_should_behave_like '.where'
end end
describe '.between' do describe '.between' do
...@@ -498,7 +508,7 @@ describe Gitlab::Git::Commit, :seed_helper do ...@@ -498,7 +508,7 @@ describe Gitlab::Git::Commit, :seed_helper do
end end
end end
describe '#stats' do shared_examples '#stats' do
subject { commit.stats } subject { commit.stats }
describe '#additions' do describe '#additions' do
...@@ -517,6 +527,14 @@ describe Gitlab::Git::Commit, :seed_helper do ...@@ -517,6 +527,14 @@ describe Gitlab::Git::Commit, :seed_helper do
end end
end end
describe '#stats with gitaly on' do
it_should_behave_like '#stats'
end
describe '#stats with gitaly disabled', :skip_gitaly_mock do
it_should_behave_like '#stats'
end
describe '#has_zero_stats?' do describe '#has_zero_stats?' do
it { expect(commit.has_zero_stats?).to eq(false) } it { expect(commit.has_zero_stats?).to eq(false) }
end end
...@@ -559,15 +577,25 @@ describe Gitlab::Git::Commit, :seed_helper do ...@@ -559,15 +577,25 @@ describe Gitlab::Git::Commit, :seed_helper do
commit_ids.map { |id| described_class.get_message(repository, id) } commit_ids.map { |id| described_class.get_message(repository, id) }
end end
it 'gets commit messages' do shared_examples 'getting commit messages' do
expect(subject).to contain_exactly( it 'gets commit messages' do
"Added contributing guide\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n", expect(subject).to contain_exactly(
"Add submodule\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n" "Added contributing guide\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n",
) "Add submodule\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n"
)
end
end
context 'when Gitaly commit_messages feature is enabled' do
it_behaves_like 'getting commit messages'
it 'gets messages in one batch', :request_store do
expect { subject.map(&:itself) }.to change { Gitlab::GitalyClient.get_request_count }.by(1)
end
end end
it 'gets messages in one batch', :request_store do context 'when Gitaly commit_messages feature is disabled', :disable_gitaly do
expect { subject.map(&:itself) }.to change { Gitlab::GitalyClient.get_request_count }.by(1) it_behaves_like 'getting commit messages'
end end
end end
......
...@@ -3,7 +3,7 @@ require "spec_helper" ...@@ -3,7 +3,7 @@ require "spec_helper"
describe Gitlab::Git::Tag, :seed_helper do describe Gitlab::Git::Tag, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
describe '#tags' do shared_examples 'Gitlab::Git::Repository#tags' do
describe 'first tag' do describe 'first tag' do
let(:tag) { repository.tags.first } let(:tag) { repository.tags.first }
...@@ -25,6 +25,14 @@ describe Gitlab::Git::Tag, :seed_helper do ...@@ -25,6 +25,14 @@ describe Gitlab::Git::Tag, :seed_helper do
it { expect(repository.tags.size).to eq(SeedRepo::Repo::TAGS.size) } it { expect(repository.tags.size).to eq(SeedRepo::Repo::TAGS.size) }
end end
context 'when Gitaly tags feature is enabled' do
it_behaves_like 'Gitlab::Git::Repository#tags'
end
context 'when Gitaly tags feature is disabled', :skip_gitaly_mock do
it_behaves_like 'Gitlab::Git::Repository#tags'
end
describe '.get_message' do describe '.get_message' do
let(:tag_ids) { %w[f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8 8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b] } let(:tag_ids) { %w[f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8 8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b] }
...@@ -32,13 +40,23 @@ describe Gitlab::Git::Tag, :seed_helper do ...@@ -32,13 +40,23 @@ describe Gitlab::Git::Tag, :seed_helper do
tag_ids.map { |id| described_class.get_message(repository, id) } tag_ids.map { |id| described_class.get_message(repository, id) }
end end
it 'gets tag messages' do shared_examples 'getting tag messages' do
expect(subject[0]).to eq("Release\n") it 'gets tag messages' do
expect(subject[1]).to eq("Version 1.1.0\n") expect(subject[0]).to eq("Release\n")
expect(subject[1]).to eq("Version 1.1.0\n")
end
end
context 'when Gitaly tag_messages feature is enabled' do
it_behaves_like 'getting tag messages'
it 'gets messages in one batch', :request_store do
expect { subject.map(&:itself) }.to change { Gitlab::GitalyClient.get_request_count }.by(1)
end
end end
it 'gets messages in one batch', :request_store do context 'when Gitaly tag_messages feature is disabled', :disable_gitaly do
expect { subject.map(&:itself) }.to change { Gitlab::GitalyClient.get_request_count }.by(1) it_behaves_like 'getting tag messages'
end end
end end
......
...@@ -80,8 +80,18 @@ describe Gitlab::Git::Tree, :seed_helper do ...@@ -80,8 +80,18 @@ describe Gitlab::Git::Tree, :seed_helper do
end end
describe '#where' do describe '#where' do
it 'returns an empty array when called with an invalid ref' do shared_examples '#where' do
expect(described_class.where(repository, 'foobar-does-not-exist')).to eq([]) it 'returns an empty array when called with an invalid ref' do
expect(described_class.where(repository, 'foobar-does-not-exist')).to eq([])
end
end
context 'with gitaly' do
it_behaves_like '#where'
end
context 'without gitaly', :skip_gitaly_mock do
it_behaves_like '#where'
end end
end end
end end
...@@ -2,7 +2,7 @@ require 'spec_helper' ...@@ -2,7 +2,7 @@ require 'spec_helper'
# We stub Gitaly in `spec/support/gitaly.rb` for other tests. We don't want # We stub Gitaly in `spec/support/gitaly.rb` for other tests. We don't want
# those stubs while testing the GitalyClient itself. # those stubs while testing the GitalyClient itself.
describe Gitlab::GitalyClient do describe Gitlab::GitalyClient, skip_gitaly_mock: true do
describe '.stub_class' do describe '.stub_class' do
it 'returns the gRPC health check stub' do it 'returns the gRPC health check stub' do
expect(described_class.stub_class(:health_check)).to eq(::Grpc::Health::V1::Health::Stub) expect(described_class.stub_class(:health_check)).to eq(::Grpc::Health::V1::Health::Stub)
...@@ -191,13 +191,102 @@ describe Gitlab::GitalyClient do ...@@ -191,13 +191,102 @@ describe Gitlab::GitalyClient do
let(:feature_name) { 'my_feature' } let(:feature_name) { 'my_feature' }
let(:real_feature_name) { "gitaly_#{feature_name}" } let(:real_feature_name) { "gitaly_#{feature_name}" }
before do context 'when Gitaly is disabled' do
allow(Feature).to receive(:enabled?).and_return(false) before do
allow(described_class).to receive(:enabled?).and_return(false)
end
it 'returns false' do
expect(described_class.feature_enabled?(feature_name)).to be(false)
end
end end
it 'returns false' do context 'when the feature status is DISABLED' do
expect(Feature).to receive(:enabled?).with(real_feature_name) let(:feature_status) { Gitlab::GitalyClient::MigrationStatus::DISABLED }
expect(described_class.feature_enabled?(feature_name)).to be(false)
it 'returns false' do
expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(false)
end
end
context 'when the feature_status is OPT_IN' do
let(:feature_status) { Gitlab::GitalyClient::MigrationStatus::OPT_IN }
context "when the feature flag hasn't been set" do
it 'returns false' do
expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(false)
end
end
context "when the feature flag is set to disable" do
before do
Feature.get(real_feature_name).disable
end
it 'returns false' do
expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(false)
end
end
context "when the feature flag is set to enable" do
before do
Feature.get(real_feature_name).enable
end
it 'returns true' do
expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(true)
end
end
context "when the feature flag is set to a percentage of time" do
before do
Feature.get(real_feature_name).enable_percentage_of_time(70)
end
it 'bases the result on pseudo-random numbers' do
expect(Random).to receive(:rand).and_return(0.3)
expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(true)
expect(Random).to receive(:rand).and_return(0.8)
expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(false)
end
end
context "when a feature is not persisted" do
it 'returns false when opt_into_all_features is off' do
allow(Feature).to receive(:persisted?).and_return(false)
allow(described_class).to receive(:opt_into_all_features?).and_return(false)
expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(false)
end
it 'returns true when the override is on' do
allow(Feature).to receive(:persisted?).and_return(false)
allow(described_class).to receive(:opt_into_all_features?).and_return(true)
expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(true)
end
end
end
context 'when the feature_status is OPT_OUT' do
let(:feature_status) { Gitlab::GitalyClient::MigrationStatus::OPT_OUT }
context "when the feature flag hasn't been set" do
it 'returns true' do
expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(true)
end
end
context "when the feature flag is set to disable" do
before do
Feature.get(real_feature_name).disable
end
it 'returns false' do
expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(false)
end
end
end end
end end
......
...@@ -30,38 +30,48 @@ describe MergeRequest do ...@@ -30,38 +30,48 @@ describe MergeRequest do
end end
describe '#squash_in_progress?' do describe '#squash_in_progress?' do
let(:repo_path) do shared_examples 'checking whether a squash is in progress' do
Gitlab::GitalyClient::StorageSettings.allow_disk_access do let(:repo_path) do
subject.source_project.repository.path Gitlab::GitalyClient::StorageSettings.allow_disk_access do
subject.source_project.repository.path
end
end end
end let(:squash_path) { File.join(repo_path, "gitlab-worktree", "squash-#{subject.id}") }
let(:squash_path) { File.join(repo_path, "gitlab-worktree", "squash-#{subject.id}") }
before do before do
system(*%W(#{Gitlab.config.git.bin_path} -C #{repo_path} worktree add --detach #{squash_path} master)) system(*%W(#{Gitlab.config.git.bin_path} -C #{repo_path} worktree add --detach #{squash_path} master))
end end
it 'returns true when there is a current squash directory' do it 'returns true when there is a current squash directory' do
expect(subject.squash_in_progress?).to be_truthy expect(subject.squash_in_progress?).to be_truthy
end end
it 'returns false when there is no squash directory' do it 'returns false when there is no squash directory' do
FileUtils.rm_rf(squash_path) FileUtils.rm_rf(squash_path)
expect(subject.squash_in_progress?).to be_falsey expect(subject.squash_in_progress?).to be_falsey
end end
it 'returns false when the squash directory has expired' do it 'returns false when the squash directory has expired' do
time = 20.minutes.ago.to_time time = 20.minutes.ago.to_time
File.utime(time, time, squash_path) File.utime(time, time, squash_path)
expect(subject.squash_in_progress?).to be_falsey
end
it 'returns false when the source project has been removed' do
allow(subject).to receive(:source_project).and_return(nil)
expect(subject.squash_in_progress?).to be_falsey expect(subject.squash_in_progress?).to be_falsey
end
end end
it 'returns false when the source project has been removed' do context 'when Gitaly squash_in_progress is enabled' do
allow(subject).to receive(:source_project).and_return(nil) it_behaves_like 'checking whether a squash is in progress'
end
expect(subject.squash_in_progress?).to be_falsey context 'when Gitaly squash_in_progress is disabled', :disable_gitaly do
it_behaves_like 'checking whether a squash is in progress'
end end
end end
...@@ -2577,6 +2587,14 @@ describe MergeRequest do ...@@ -2577,6 +2587,14 @@ describe MergeRequest do
expect(subject.rebase_in_progress?).to be_falsey expect(subject.rebase_in_progress?).to be_falsey
end end
end end
context 'when Gitaly rebase_in_progress is enabled' do
it_behaves_like 'checking whether a rebase is in progress'
end
context 'when Gitaly rebase_in_progress is enabled', :disable_gitaly do
it_behaves_like 'checking whether a rebase is in progress'
end
end end
describe '#allow_collaboration' do describe '#allow_collaboration' do
......
...@@ -130,53 +130,63 @@ describe ProjectWiki do ...@@ -130,53 +130,63 @@ describe ProjectWiki do
end end
describe "#find_page" do describe "#find_page" do
before do shared_examples 'finding a wiki page' do
create_page("index page", "This is an awesome Gollum Wiki") before do
end create_page("index page", "This is an awesome Gollum Wiki")
end
after do after do
subject.pages.each { |page| destroy_page(page.page) } subject.pages.each { |page| destroy_page(page.page) }
end end
it "returns the latest version of the page if it exists" do it "returns the latest version of the page if it exists" do
page = subject.find_page("index page") page = subject.find_page("index page")
expect(page.title).to eq("index page") expect(page.title).to eq("index page")
end end
it "returns nil if the page does not exist" do it "returns nil if the page does not exist" do
expect(subject.find_page("non-existent")).to eq(nil) expect(subject.find_page("non-existent")).to eq(nil)
end end
it "can find a page by slug" do it "can find a page by slug" do
page = subject.find_page("index-page") page = subject.find_page("index-page")
expect(page.title).to eq("index page") expect(page.title).to eq("index page")
end end
it "returns a WikiPage instance" do it "returns a WikiPage instance" do
page = subject.find_page("index page") page = subject.find_page("index page")
expect(page).to be_a WikiPage expect(page).to be_a WikiPage
end end
context 'pages with multibyte-character title' do context 'pages with multibyte-character title' do
before do before do
create_page("autre pagé", "C'est un génial Gollum Wiki") create_page("autre pagé", "C'est un génial Gollum Wiki")
end
it "can find a page by slug" do
page = subject.find_page("autre pagé")
expect(page.title).to eq("autre pagé")
end
end end
it "can find a page by slug" do context 'pages with invalidly-encoded content' do
page = subject.find_page("autre pagé") before do
expect(page.title).to eq("autre pagé") create_page("encoding is fun", "f\xFCr".b)
end
it "can find the page" do
page = subject.find_page("encoding is fun")
expect(page.content).to eq("fr")
end
end end
end end
context 'pages with invalidly-encoded content' do context 'when Gitaly wiki_find_page is enabled' do
before do it_behaves_like 'finding a wiki page'
create_page("encoding is fun", "f\xFCr".b) end
end
it "can find the page" do context 'when Gitaly wiki_find_page is disabled', :skip_gitaly_mock do
page = subject.find_page("encoding is fun") it_behaves_like 'finding a wiki page'
expect(page.content).to eq("fr")
end
end end
end end
...@@ -197,80 +207,100 @@ describe ProjectWiki do ...@@ -197,80 +207,100 @@ describe ProjectWiki do
end end
describe '#find_file' do describe '#find_file' do
let(:image) { File.open(Rails.root.join('spec', 'fixtures', 'big-image.png')) } shared_examples 'finding a wiki file' do
let(:image) { File.open(Rails.root.join('spec', 'fixtures', 'big-image.png')) }
before do before do
subject.wiki # Make sure the wiki repo exists subject.wiki # Make sure the wiki repo exists
repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
subject.repository.path_to_repo subject.repository.path_to_repo
end
BareRepoOperations.new(repo_path).commit_file(image, 'image.png')
end end
BareRepoOperations.new(repo_path).commit_file(image, 'image.png') it 'returns the latest version of the file if it exists' do
end file = subject.find_file('image.png')
expect(file.mime_type).to eq('image/png')
end
it 'returns the latest version of the file if it exists' do it 'returns nil if the page does not exist' do
file = subject.find_file('image.png') expect(subject.find_file('non-existent')).to eq(nil)
expect(file.mime_type).to eq('image/png') end
end
it 'returns nil if the page does not exist' do it 'returns a Gitlab::Git::WikiFile instance' do
expect(subject.find_file('non-existent')).to eq(nil) file = subject.find_file('image.png')
end expect(file).to be_a Gitlab::Git::WikiFile
end
it 'returns the whole file' do
file = subject.find_file('image.png')
image.rewind
it 'returns a Gitlab::Git::WikiFile instance' do expect(file.raw_data.b).to eq(image.read.b)
file = subject.find_file('image.png') end
expect(file).to be_a Gitlab::Git::WikiFile
end end
it 'returns the whole file' do context 'when Gitaly wiki_find_file is enabled' do
file = subject.find_file('image.png') it_behaves_like 'finding a wiki file'
image.rewind end
expect(file.raw_data.b).to eq(image.read.b) context 'when Gitaly wiki_find_file is disabled', :skip_gitaly_mock do
it_behaves_like 'finding a wiki file'
end end
end end
describe "#create_page" do describe "#create_page" do
after do shared_examples 'creating a wiki page' do
destroy_page(subject.pages.first.page) after do
end destroy_page(subject.pages.first.page)
end
it "creates a new wiki page" do it "creates a new wiki page" do
expect(subject.create_page("test page", "this is content")).not_to eq(false) expect(subject.create_page("test page", "this is content")).not_to eq(false)
expect(subject.pages.count).to eq(1) expect(subject.pages.count).to eq(1)
end end
it "returns false when a duplicate page exists" do it "returns false when a duplicate page exists" do
subject.create_page("test page", "content") subject.create_page("test page", "content")
expect(subject.create_page("test page", "content")).to eq(false) expect(subject.create_page("test page", "content")).to eq(false)
end end
it "stores an error message when a duplicate page exists" do it "stores an error message when a duplicate page exists" do
2.times { subject.create_page("test page", "content") } 2.times { subject.create_page("test page", "content") }
expect(subject.error_message).to match(/Duplicate page:/) expect(subject.error_message).to match(/Duplicate page:/)
end end
it "sets the correct commit message" do it "sets the correct commit message" do
subject.create_page("test page", "some content", :markdown, "commit message") subject.create_page("test page", "some content", :markdown, "commit message")
expect(subject.pages.first.page.version.message).to eq("commit message") expect(subject.pages.first.page.version.message).to eq("commit message")
end end
it 'sets the correct commit email' do it 'sets the correct commit email' do
subject.create_page('test page', 'content') subject.create_page('test page', 'content')
expect(user.commit_email).not_to eq(user.email) expect(user.commit_email).not_to eq(user.email)
expect(commit.author_email).to eq(user.commit_email) expect(commit.author_email).to eq(user.commit_email)
expect(commit.committer_email).to eq(user.commit_email) expect(commit.committer_email).to eq(user.commit_email)
end end
it 'updates project activity' do it 'updates project activity' do
subject.create_page('Test Page', 'This is content') subject.create_page('Test Page', 'This is content')
project.reload project.reload
expect(project.last_activity_at).to be_within(1.minute).of(Time.now) expect(project.last_activity_at).to be_within(1.minute).of(Time.now)
expect(project.last_repository_updated_at).to be_within(1.minute).of(Time.now) expect(project.last_repository_updated_at).to be_within(1.minute).of(Time.now)
end
end
context 'when Gitaly wiki_write_page is enabled' do
it_behaves_like 'creating a wiki page'
end
context 'when Gitaly wiki_write_page is disabled', :skip_gitaly_mock do
it_behaves_like 'creating a wiki page'
end end
end end
...@@ -321,31 +351,41 @@ describe ProjectWiki do ...@@ -321,31 +351,41 @@ describe ProjectWiki do
end end
describe "#delete_page" do describe "#delete_page" do
before do shared_examples 'deleting a wiki page' do
create_page("index", "some content") before do
@page = subject.wiki.page(title: "index") create_page("index", "some content")
end @page = subject.wiki.page(title: "index")
end
it "deletes the page" do it "deletes the page" do
subject.delete_page(@page) subject.delete_page(@page)
expect(subject.pages.count).to eq(0) expect(subject.pages.count).to eq(0)
end end
it 'sets the correct commit email' do it 'sets the correct commit email' do
subject.delete_page(@page) subject.delete_page(@page)
expect(user.commit_email).not_to eq(user.email) expect(user.commit_email).not_to eq(user.email)
expect(commit.author_email).to eq(user.commit_email) expect(commit.author_email).to eq(user.commit_email)
expect(commit.committer_email).to eq(user.commit_email) expect(commit.committer_email).to eq(user.commit_email)
end end
it 'updates project activity' do it 'updates project activity' do
subject.delete_page(@page) subject.delete_page(@page)
project.reload project.reload
expect(project.last_activity_at).to be_within(1.minute).of(Time.now) expect(project.last_activity_at).to be_within(1.minute).of(Time.now)
expect(project.last_repository_updated_at).to be_within(1.minute).of(Time.now) expect(project.last_repository_updated_at).to be_within(1.minute).of(Time.now)
end
end
context 'when Gitaly wiki_delete_page is enabled' do
it_behaves_like 'deleting a wiki page'
end
context 'when Gitaly wiki_delete_page is disabled', :skip_gitaly_mock do
it_behaves_like 'deleting a wiki page'
end end
end end
......
...@@ -38,29 +38,49 @@ describe Repository do ...@@ -38,29 +38,49 @@ describe Repository do
end end
describe '#branch_names_contains' do describe '#branch_names_contains' do
set(:project) { create(:project, :repository) } shared_examples '#branch_names_contains' do
let(:repository) { project.repository } set(:project) { create(:project, :repository) }
let(:repository) { project.repository }
subject { repository.branch_names_contains(sample_commit.id) } subject { repository.branch_names_contains(sample_commit.id) }
it { is_expected.to include('master') } it { is_expected.to include('master') }
it { is_expected.not_to include('feature') } it { is_expected.not_to include('feature') }
it { is_expected.not_to include('fix') } it { is_expected.not_to include('fix') }
describe 'when storage is broken', :broken_storage do describe 'when storage is broken', :broken_storage do
it 'should raise a storage error' do it 'should raise a storage error' do
expect_to_raise_storage_error do expect_to_raise_storage_error do
broken_repository.branch_names_contains(sample_commit.id) broken_repository.branch_names_contains(sample_commit.id)
end
end end
end end
end end
context 'when gitaly is enabled' do
it_behaves_like '#branch_names_contains'
end
context 'when gitaly is disabled', :skip_gitaly_mock do
it_behaves_like '#branch_names_contains'
end
end end
describe '#tag_names_contains' do describe '#tag_names_contains' do
subject { repository.tag_names_contains(sample_commit.id) } shared_examples '#tag_names_contains' do
subject { repository.tag_names_contains(sample_commit.id) }
it { is_expected.to include('v1.1.0') }
it { is_expected.not_to include('v1.0.0') }
end
context 'when gitaly is enabled' do
it_behaves_like '#tag_names_contains'
end
it { is_expected.to include('v1.1.0') } context 'when gitaly is enabled', :skip_gitaly_mock do
it { is_expected.not_to include('v1.0.0') } it_behaves_like '#tag_names_contains'
end
end end
describe 'tags_sorted_by' do describe 'tags_sorted_by' do
...@@ -218,41 +238,61 @@ describe Repository do ...@@ -218,41 +238,61 @@ describe Repository do
end end
describe '#last_commit_for_path' do describe '#last_commit_for_path' do
subject { repository.last_commit_for_path(sample_commit.id, '.gitignore').id } shared_examples 'getting last commit for path' do
subject { repository.last_commit_for_path(sample_commit.id, '.gitignore').id }
it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') } it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') }
describe 'when storage is broken', :broken_storage do describe 'when storage is broken', :broken_storage do
it 'should raise a storage error' do it 'should raise a storage error' do
expect_to_raise_storage_error do expect_to_raise_storage_error do
broken_repository.last_commit_id_for_path(sample_commit.id, '.gitignore') broken_repository.last_commit_id_for_path(sample_commit.id, '.gitignore')
end
end end
end end
end end
context 'when Gitaly feature last_commit_for_path is enabled' do
it_behaves_like 'getting last commit for path'
end
context 'when Gitaly feature last_commit_for_path is disabled', :skip_gitaly_mock do
it_behaves_like 'getting last commit for path'
end
end end
describe '#last_commit_id_for_path' do describe '#last_commit_id_for_path' do
subject { repository.last_commit_id_for_path(sample_commit.id, '.gitignore') } shared_examples 'getting last commit ID for path' do
subject { repository.last_commit_id_for_path(sample_commit.id, '.gitignore') }
it "returns last commit id for a given path" do it "returns last commit id for a given path" do
is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8')
end end
it "caches last commit id for a given path" do it "caches last commit id for a given path" do
cache = repository.send(:cache) cache = repository.send(:cache)
key = "last_commit_id_for_path:#{sample_commit.id}:#{Digest::SHA1.hexdigest('.gitignore')}" key = "last_commit_id_for_path:#{sample_commit.id}:#{Digest::SHA1.hexdigest('.gitignore')}"
expect(cache).to receive(:fetch).with(key).and_return('c1acaa5') expect(cache).to receive(:fetch).with(key).and_return('c1acaa5')
is_expected.to eq('c1acaa5') is_expected.to eq('c1acaa5')
end end
describe 'when storage is broken', :broken_storage do describe 'when storage is broken', :broken_storage do
it 'should raise a storage error' do it 'should raise a storage error' do
expect_to_raise_storage_error do expect_to_raise_storage_error do
broken_repository.last_commit_for_path(sample_commit.id, '.gitignore').id broken_repository.last_commit_for_path(sample_commit.id, '.gitignore').id
end
end end
end end
end end
context 'when Gitaly feature last_commit_for_path is enabled' do
it_behaves_like 'getting last commit ID for path'
end
context 'when Gitaly feature last_commit_for_path is disabled', :skip_gitaly_mock do
it_behaves_like 'getting last commit ID for path'
end
end end
describe '#commits' do describe '#commits' do
...@@ -334,57 +374,78 @@ describe Repository do ...@@ -334,57 +374,78 @@ describe Repository do
describe '#commits_by' do describe '#commits_by' do
set(:project) { create(:project, :repository) } set(:project) { create(:project, :repository) }
let(:oids) { TestEnv::BRANCH_SHA.values }
subject { project.repository.commits_by(oids: oids) } shared_examples 'batch commits fetching' do
let(:oids) { TestEnv::BRANCH_SHA.values }
it 'finds each commit' do subject { project.repository.commits_by(oids: oids) }
expect(subject).not_to include(nil)
expect(subject.size).to eq(oids.size)
end
it 'returns only Commit instances' do it 'finds each commit' do
expect(subject).to all( be_a(Commit) ) expect(subject).not_to include(nil)
end expect(subject.size).to eq(oids.size)
end
context 'when some commits are not found ' do it 'returns only Commit instances' do
let(:oids) do expect(subject).to all( be_a(Commit) )
['deadbeef'] + TestEnv::BRANCH_SHA.values.first(10)
end end
it 'returns only found commits' do context 'when some commits are not found ' do
expect(subject).not_to include(nil) let(:oids) do
expect(subject.size).to eq(10) ['deadbeef'] + TestEnv::BRANCH_SHA.values.first(10)
end
it 'returns only found commits' do
expect(subject).not_to include(nil)
expect(subject.size).to eq(10)
end
end end
end
context 'when no oids are passed' do context 'when no oids are passed' do
let(:oids) { [] } let(:oids) { [] }
it 'does not call #batch_by_oid' do it 'does not call #batch_by_oid' do
expect(Gitlab::Git::Commit).not_to receive(:batch_by_oid) expect(Gitlab::Git::Commit).not_to receive(:batch_by_oid)
subject subject
end
end end
end end
context 'when Gitaly list_commits_by_oid is enabled' do
it_behaves_like 'batch commits fetching'
end
context 'when Gitaly list_commits_by_oid is enabled', :disable_gitaly do
it_behaves_like 'batch commits fetching'
end
end end
describe '#find_commits_by_message' do describe '#find_commits_by_message' do
it 'returns commits with messages containing a given string' do shared_examples 'finding commits by message' do
commit_ids = repository.find_commits_by_message('submodule').map(&:id) it 'returns commits with messages containing a given string' do
commit_ids = repository.find_commits_by_message('submodule').map(&:id)
expect(commit_ids).to include(
'5937ac0a7beb003549fc5fd26fc247adbce4a52e',
'6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9',
'cfe32cf61b73a0d5e9f13e774abde7ff789b1660'
)
expect(commit_ids).not_to include('913c66a37b4a45b9769037c55c2d238bd0942d2e')
end
it 'is case insensitive' do
commit_ids = repository.find_commits_by_message('SUBMODULE').map(&:id)
expect(commit_ids).to include( expect(commit_ids).to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
'5937ac0a7beb003549fc5fd26fc247adbce4a52e', end
'6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9',
'cfe32cf61b73a0d5e9f13e774abde7ff789b1660'
)
expect(commit_ids).not_to include('913c66a37b4a45b9769037c55c2d238bd0942d2e')
end end
it 'is case insensitive' do context 'when Gitaly commits_by_message feature is enabled' do
commit_ids = repository.find_commits_by_message('SUBMODULE').map(&:id) it_behaves_like 'finding commits by message'
end
expect(commit_ids).to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') context 'when Gitaly commits_by_message feature is disabled', :skip_gitaly_mock do
it_behaves_like 'finding commits by message'
end end
describe 'when storage is broken', :broken_storage do describe 'when storage is broken', :broken_storage do
...@@ -1267,23 +1328,34 @@ describe Repository do ...@@ -1267,23 +1328,34 @@ describe Repository do
describe '#merge' do describe '#merge' do
let(:merge_request) { create(:merge_request, source_branch: 'feature', target_branch: 'master', source_project: project) } let(:merge_request) { create(:merge_request, source_branch: 'feature', target_branch: 'master', source_project: project) }
let(:message) { 'Test \r\n\r\n message' } let(:message) { 'Test \r\n\r\n message' }
it 'merges the code and returns the commit id' do shared_examples '#merge' do
expect(merge_commit).to be_present it 'merges the code and returns the commit id' do
expect(repository.blob_at(merge_commit.id, 'files/ruby/feature.rb')).to be_present expect(merge_commit).to be_present
end expect(repository.blob_at(merge_commit.id, 'files/ruby/feature.rb')).to be_present
end
it 'sets the `in_progress_merge_commit_sha` flag for the given merge request' do it 'sets the `in_progress_merge_commit_sha` flag for the given merge request' do
merge_commit_id = merge(repository, user, merge_request, message) merge_commit_id = merge(repository, user, merge_request, message)
expect(merge_request.in_progress_merge_commit_sha).to eq(merge_commit_id) expect(merge_request.in_progress_merge_commit_sha).to eq(merge_commit_id)
end
it 'removes carriage returns from commit message' do
merge_commit_id = merge(repository, user, merge_request, message)
expect(repository.commit(merge_commit_id).message).to eq(message.delete("\r"))
end
end end
it 'removes carriage returns from commit message' do context 'with gitaly' do
merge_commit_id = merge(repository, user, merge_request, message) it_behaves_like '#merge'
end
expect(repository.commit(merge_commit_id).message).to eq(message.delete("\r")) context 'without gitaly', :skip_gitaly_mock do
it_behaves_like '#merge'
end end
def merge(repository, user, merge_request, message) def merge(repository, user, merge_request, message)
...@@ -1320,78 +1392,98 @@ describe Repository do ...@@ -1320,78 +1392,98 @@ describe Repository do
end end
describe '#revert' do describe '#revert' do
let(:new_image_commit) { repository.commit('33f3729a45c02fc67d00adb1b8bca394b0e761d9') } shared_examples 'reverting a commit' do
let(:update_image_commit) { repository.commit('2f63565e7aac07bcdadb654e253078b727143ec4') } let(:new_image_commit) { repository.commit('33f3729a45c02fc67d00adb1b8bca394b0e761d9') }
let(:message) { 'revert message' } let(:update_image_commit) { repository.commit('2f63565e7aac07bcdadb654e253078b727143ec4') }
let(:message) { 'revert message' }
context 'when there is a conflict' do
it 'raises an error' do context 'when there is a conflict' do
expect { repository.revert(user, new_image_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError) it 'raises an error' do
expect { repository.revert(user, new_image_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError)
end
end end
end
context 'when commit was already reverted' do context 'when commit was already reverted' do
it 'raises an error' do it 'raises an error' do
repository.revert(user, update_image_commit, 'master', message) repository.revert(user, update_image_commit, 'master', message)
expect { repository.revert(user, update_image_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError) expect { repository.revert(user, update_image_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError)
end
end end
end
context 'when commit can be reverted' do context 'when commit can be reverted' do
it 'reverts the changes' do it 'reverts the changes' do
expect(repository.revert(user, update_image_commit, 'master', message)).to be_truthy expect(repository.revert(user, update_image_commit, 'master', message)).to be_truthy
end
end end
end
context 'reverting a merge commit' do context 'reverting a merge commit' do
it 'reverts the changes' do it 'reverts the changes' do
merge_commit merge_commit
expect(repository.blob_at_branch('master', 'files/ruby/feature.rb')).to be_present expect(repository.blob_at_branch('master', 'files/ruby/feature.rb')).to be_present
repository.revert(user, merge_commit, 'master', message) repository.revert(user, merge_commit, 'master', message)
expect(repository.blob_at_branch('master', 'files/ruby/feature.rb')).not_to be_present expect(repository.blob_at_branch('master', 'files/ruby/feature.rb')).not_to be_present
end
end end
end end
context 'when Gitaly revert feature is enabled' do
it_behaves_like 'reverting a commit'
end
context 'when Gitaly revert feature is disabled', :disable_gitaly do
it_behaves_like 'reverting a commit'
end
end end
describe '#cherry_pick' do describe '#cherry_pick' do
let(:conflict_commit) { repository.commit('c642fe9b8b9f28f9225d7ea953fe14e74748d53b') } shared_examples 'cherry-picking a commit' do
let(:pickable_commit) { repository.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') } let(:conflict_commit) { repository.commit('c642fe9b8b9f28f9225d7ea953fe14e74748d53b') }
let(:pickable_merge) { repository.commit('e56497bb5f03a90a51293fc6d516788730953899') } let(:pickable_commit) { repository.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') }
let(:message) { 'cherry-pick message' } let(:pickable_merge) { repository.commit('e56497bb5f03a90a51293fc6d516788730953899') }
let(:message) { 'cherry-pick message' }
context 'when there is a conflict' do
it 'raises an error' do context 'when there is a conflict' do
expect { repository.cherry_pick(user, conflict_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError) it 'raises an error' do
expect { repository.cherry_pick(user, conflict_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError)
end
end end
end
context 'when commit was already cherry-picked' do context 'when commit was already cherry-picked' do
it 'raises an error' do it 'raises an error' do
repository.cherry_pick(user, pickable_commit, 'master', message) repository.cherry_pick(user, pickable_commit, 'master', message)
expect { repository.cherry_pick(user, pickable_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError) expect { repository.cherry_pick(user, pickable_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError)
end
end end
end
context 'when commit can be cherry-picked' do context 'when commit can be cherry-picked' do
it 'cherry-picks the changes' do it 'cherry-picks the changes' do
expect(repository.cherry_pick(user, pickable_commit, 'master', message)).to be_truthy expect(repository.cherry_pick(user, pickable_commit, 'master', message)).to be_truthy
end
end end
end
context 'cherry-picking a merge commit' do context 'cherry-picking a merge commit' do
it 'cherry-picks the changes' do it 'cherry-picks the changes' do
expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).to be_nil expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).to be_nil
cherry_pick_commit_sha = repository.cherry_pick(user, pickable_merge, 'improve/awesome', message) cherry_pick_commit_sha = repository.cherry_pick(user, pickable_merge, 'improve/awesome', message)
cherry_pick_commit_message = project.commit(cherry_pick_commit_sha).message cherry_pick_commit_message = project.commit(cherry_pick_commit_sha).message
expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).not_to be_nil expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).not_to be_nil
expect(cherry_pick_commit_message).to eq(message) expect(cherry_pick_commit_message).to eq(message)
end
end end
end end
context 'when Gitaly cherry_pick feature is enabled' do
it_behaves_like 'cherry-picking a commit'
end
context 'when Gitaly cherry_pick feature is disabled', :disable_gitaly do
it_behaves_like 'cherry-picking a commit'
end
end end
describe '#before_delete' do describe '#before_delete' do
...@@ -2098,23 +2190,33 @@ describe Repository do ...@@ -2098,23 +2190,33 @@ describe Repository do
let(:commit) { repository.commit } let(:commit) { repository.commit }
let(:ancestor) { commit.parents.first } let(:ancestor) { commit.parents.first }
it 'it is an ancestor' do shared_examples '#ancestor?' do
expect(repository.ancestor?(ancestor.id, commit.id)).to eq(true) it 'it is an ancestor' do
end expect(repository.ancestor?(ancestor.id, commit.id)).to eq(true)
end
it 'it is not an ancestor' do it 'it is not an ancestor' do
expect(repository.ancestor?(commit.id, ancestor.id)).to eq(false) expect(repository.ancestor?(commit.id, ancestor.id)).to eq(false)
end
it 'returns false on nil-values' do
expect(repository.ancestor?(nil, commit.id)).to eq(false)
expect(repository.ancestor?(ancestor.id, nil)).to eq(false)
expect(repository.ancestor?(nil, nil)).to eq(false)
end
it 'returns false for invalid commit IDs' do
expect(repository.ancestor?(commit.id, Gitlab::Git::BLANK_SHA)).to eq(false)
expect(repository.ancestor?( Gitlab::Git::BLANK_SHA, commit.id)).to eq(false)
end
end end
it 'returns false on nil-values' do context 'with Gitaly enabled' do
expect(repository.ancestor?(nil, commit.id)).to eq(false) it_behaves_like('#ancestor?')
expect(repository.ancestor?(ancestor.id, nil)).to eq(false)
expect(repository.ancestor?(nil, nil)).to eq(false)
end end
it 'returns false for invalid commit IDs' do context 'with Gitaly disabled', :skip_gitaly_mock do
expect(repository.ancestor?(commit.id, Gitlab::Git::BLANK_SHA)).to eq(false) it_behaves_like('#ancestor?')
expect(repository.ancestor?( Gitlab::Git::BLANK_SHA, commit.id)).to eq(false)
end end
end end
......
...@@ -200,160 +200,180 @@ describe WikiPage do ...@@ -200,160 +200,180 @@ describe WikiPage do
end end
describe '#create' do describe '#create' do
context 'with valid attributes' do shared_examples 'create method' do
it 'raises an error if a page with the same path already exists' do context 'with valid attributes' do
create_page('New Page', 'content') it 'raises an error if a page with the same path already exists' do
create_page('foo/bar', 'content') create_page('New Page', 'content')
expect { create_page('New Page', 'other content') }.to raise_error Gitlab::Git::Wiki::DuplicatePageError create_page('foo/bar', 'content')
expect { create_page('foo/bar', 'other content') }.to raise_error Gitlab::Git::Wiki::DuplicatePageError expect { create_page('New Page', 'other content') }.to raise_error Gitlab::Git::Wiki::DuplicatePageError
expect { create_page('foo/bar', 'other content') }.to raise_error Gitlab::Git::Wiki::DuplicatePageError
destroy_page('New Page')
destroy_page('bar', 'foo') destroy_page('New Page')
end destroy_page('bar', 'foo')
end
it 'if the title is preceded by a / it is removed' do it 'if the title is preceded by a / it is removed' do
create_page('/New Page', 'content') create_page('/New Page', 'content')
expect(wiki.find_page('New Page')).not_to be_nil expect(wiki.find_page('New Page')).not_to be_nil
destroy_page('New Page') destroy_page('New Page')
end
end end
end end
end
describe "#update" do context 'when Gitaly is enabled' do
before do it_behaves_like 'create method'
create_page("Update", "content")
@page = wiki.find_page("Update")
end end
after do context 'when Gitaly is disabled', :skip_gitaly_mock do
destroy_page(@page.title, @page.directory) it_behaves_like 'create method'
end end
end
context "with valid attributes" do describe "#update" do
it "updates the content of the page" do shared_examples 'update method' do
new_content = "new content" before do
create_page("Update", "content")
@page.update(content: new_content)
@page = wiki.find_page("Update") @page = wiki.find_page("Update")
end
expect(@page.content).to eq("new content") after do
destroy_page(@page.title, @page.directory)
end end
it "updates the title of the page" do context "with valid attributes" do
new_title = "Index v.1.2.4" it "updates the content of the page" do
new_content = "new content"
@page.update(content: new_content)
@page = wiki.find_page("Update")
@page.update(title: new_title) expect(@page.content).to eq("new content")
@page = wiki.find_page(new_title) end
expect(@page.title).to eq(new_title) it "updates the title of the page" do
end new_title = "Index v.1.2.4"
it "returns true" do @page.update(title: new_title)
expect(@page.update(content: "more content")).to be_truthy @page = wiki.find_page(new_title)
expect(@page.title).to eq(new_title)
end
it "returns true" do
expect(@page.update(content: "more content")).to be_truthy
end
end end
end
context 'with same last commit sha' do context 'with same last commit sha' do
it 'returns true' do it 'returns true' do
expect(@page.update(content: 'more content', last_commit_sha: @page.last_commit_sha)).to be_truthy expect(@page.update(content: 'more content', last_commit_sha: @page.last_commit_sha)).to be_truthy
end
end end
end
context 'with different last commit sha' do context 'with different last commit sha' do
it 'raises exception' do it 'raises exception' do
expect { @page.update(content: 'more content', last_commit_sha: 'xxx') }.to raise_error(WikiPage::PageChangedError) expect { @page.update(content: 'more content', last_commit_sha: 'xxx') }.to raise_error(WikiPage::PageChangedError)
end
end end
end
context 'when renaming a page' do context 'when renaming a page' do
it 'raises an error if the page already exists' do it 'raises an error if the page already exists' do
create_page('Existing Page', 'content') create_page('Existing Page', 'content')
expect { @page.update(title: 'Existing Page', content: 'new_content') }.to raise_error(WikiPage::PageRenameError) expect { @page.update(title: 'Existing Page', content: 'new_content') }.to raise_error(WikiPage::PageRenameError)
expect(@page.title).to eq 'Update' expect(@page.title).to eq 'Update'
expect(@page.content).to eq 'new_content' expect(@page.content).to eq 'new_content'
destroy_page('Existing Page') destroy_page('Existing Page')
end end
it 'updates the content and rename the file' do it 'updates the content and rename the file' do
new_title = 'Renamed Page' new_title = 'Renamed Page'
new_content = 'updated content' new_content = 'updated content'
expect(@page.update(title: new_title, content: new_content)).to be_truthy expect(@page.update(title: new_title, content: new_content)).to be_truthy
@page = wiki.find_page(new_title) @page = wiki.find_page(new_title)
expect(@page).not_to be_nil expect(@page).not_to be_nil
expect(@page.content).to eq new_content expect(@page.content).to eq new_content
end
end end
end
context 'when moving a page' do context 'when moving a page' do
it 'raises an error if the page already exists' do it 'raises an error if the page already exists' do
create_page('foo/Existing Page', 'content') create_page('foo/Existing Page', 'content')
expect { @page.update(title: 'foo/Existing Page', content: 'new_content') }.to raise_error(WikiPage::PageRenameError)
expect(@page.title).to eq 'Update'
expect(@page.content).to eq 'new_content'
destroy_page('Existing Page', 'foo') expect { @page.update(title: 'foo/Existing Page', content: 'new_content') }.to raise_error(WikiPage::PageRenameError)
end expect(@page.title).to eq 'Update'
expect(@page.content).to eq 'new_content'
it 'updates the content and moves the file' do destroy_page('Existing Page', 'foo')
new_title = 'foo/Other Page' end
new_content = 'new_content'
expect(@page.update(title: new_title, content: new_content)).to be_truthy it 'updates the content and moves the file' do
new_title = 'foo/Other Page'
new_content = 'new_content'
page = wiki.find_page(new_title) expect(@page.update(title: new_title, content: new_content)).to be_truthy
expect(page).not_to be_nil page = wiki.find_page(new_title)
expect(page.content).to eq new_content
end
context 'in subdir' do expect(page).not_to be_nil
before do expect(page.content).to eq new_content
create_page('foo/Existing Page', 'content')
@page = wiki.find_page('foo/Existing Page')
end end
it 'moves the page to the root folder if the title is preceded by /' do context 'in subdir' do
expect(@page.slug).to eq 'foo/Existing-Page' before do
expect(@page.update(title: '/Existing Page', content: 'new_content')).to be_truthy create_page('foo/Existing Page', 'content')
expect(@page.slug).to eq 'Existing-Page' @page = wiki.find_page('foo/Existing Page')
end
it 'moves the page to the root folder if the title is preceded by /', :skip_gitaly_mock do
expect(@page.slug).to eq 'foo/Existing-Page'
expect(@page.update(title: '/Existing Page', content: 'new_content')).to be_truthy
expect(@page.slug).to eq 'Existing-Page'
end
it 'does nothing if it has the same title' do
original_path = @page.slug
expect(@page.update(title: 'Existing Page', content: 'new_content')).to be_truthy
expect(@page.slug).to eq original_path
end
end end
it 'does nothing if it has the same title' do context 'in root dir' do
original_path = @page.slug it 'does nothing if the title is preceded by /' do
original_path = @page.slug
expect(@page.update(title: 'Existing Page', content: 'new_content')).to be_truthy expect(@page.update(title: '/Update', content: 'new_content')).to be_truthy
expect(@page.slug).to eq original_path expect(@page.slug).to eq original_path
end
end end
end end
context 'in root dir' do context "with invalid attributes" do
it 'does nothing if the title is preceded by /' do it 'aborts update if title blank' do
original_path = @page.slug expect(@page.update(title: '', content: 'new_content')).to be_falsey
expect(@page.content).to eq 'new_content'
expect(@page.update(title: '/Update', content: 'new_content')).to be_truthy page = wiki.find_page('Update')
expect(@page.slug).to eq original_path expect(page.content).to eq 'content'
@page.title = 'Update'
end end
end end
end end
context "with invalid attributes" do context 'when Gitaly is enabled' do
it 'aborts update if title blank' do it_behaves_like 'update method'
expect(@page.update(title: '', content: 'new_content')).to be_falsey end
expect(@page.content).to eq 'new_content'
page = wiki.find_page('Update')
expect(page.content).to eq 'content'
@page.title = 'Update' context 'when Gitaly is disabled', :skip_gitaly_mock do
end it_behaves_like 'update method'
end end
end end
...@@ -374,24 +394,34 @@ describe WikiPage do ...@@ -374,24 +394,34 @@ describe WikiPage do
end end
describe "#versions" do describe "#versions" do
let(:page) { wiki.find_page("Update") } shared_examples 'wiki page versions' do
let(:page) { wiki.find_page("Update") }
before do before do
create_page("Update", "content") create_page("Update", "content")
end end
after do after do
destroy_page("Update") destroy_page("Update")
end end
it "returns an array of all commits for the page" do
3.times { |i| page.update(content: "content #{i}") }
it "returns an array of all commits for the page" do expect(page.versions.count).to eq(4)
3.times { |i| page.update(content: "content #{i}") } end
expect(page.versions.count).to eq(4) it 'returns instances of WikiPageVersion' do
expect(page.versions).to all( be_a(Gitlab::Git::WikiPageVersion) )
end
end end
it 'returns instances of WikiPageVersion' do context 'when Gitaly is enabled' do
expect(page.versions).to all( be_a(Gitlab::Git::WikiPageVersion) ) it_behaves_like 'wiki page versions'
end
context 'when Gitaly is disabled', :disable_gitaly do
it_behaves_like 'wiki page versions'
end end
end end
...@@ -525,13 +555,23 @@ describe WikiPage do ...@@ -525,13 +555,23 @@ describe WikiPage do
end end
describe '#formatted_content' do describe '#formatted_content' do
it 'returns processed content of the page' do shared_examples 'fetching page formatted content' do
subject.create({ title: "RDoc", content: "*bold*", format: "rdoc" }) it 'returns processed content of the page' do
page = wiki.find_page('RDoc') subject.create({ title: "RDoc", content: "*bold*", format: "rdoc" })
page = wiki.find_page('RDoc')
expect(page.formatted_content).to eq("\n<p><strong>bold</strong></p>\n")
expect(page.formatted_content).to eq("\n<p><strong>bold</strong></p>\n") destroy_page('RDoc')
end
end
context 'when Gitaly wiki_page_formatted_data is enabled' do
it_behaves_like 'fetching page formatted content'
end
destroy_page('RDoc') context 'when Gitaly wiki_page_formatted_data is disabled', :disable_gitaly do
it_behaves_like 'fetching page formatted content'
end end
end end
......
RSpec.configure do |config|
config.before(:each) do |example|
if example.metadata[:disable_gitaly]
# Use 'and_wrap_original' to make sure the arguments are valid
allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_wrap_original { |m, *args| m.call(*args) && false }
else
next if example.metadata[:skip_gitaly_mock]
# Use 'and_wrap_original' to make sure the arguments are valid
allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_wrap_original do |m, *args|
m.call(*args)
!Gitlab::GitalyClient.explicit_opt_in_required.include?(args.first)
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment