Merge branch 'revert-e2aa2177' into 'master'
Revert "Merge branch 'zj-improve-gitaly-pb' into 'master'" See merge request gitlab-org/gitlab-ce!23229
This commit is contained in:
commit
fd6e3781ba
18 changed files with 1395 additions and 876 deletions
|
@ -1,5 +0,0 @@
|
||||||
---
|
|
||||||
title: Show what RPC is called in the performance bar
|
|
||||||
merge_request: 23140
|
|
||||||
author:
|
|
||||||
type: other
|
|
|
@ -1,5 +1,7 @@
|
||||||
# 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
|
||||||
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'
|
||||||
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
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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] ||= {}
|
||||||
|
|
|
@ -1,223 +1,233 @@
|
||||||
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))
|
||||||
it 'redirects back to the home edit page' do
|
click_link "Create your first page"
|
||||||
page.within(:css, '.wiki-form .form-actions') do
|
|
||||||
click_on('Cancel')
|
|
||||||
end
|
|
||||||
|
|
||||||
expect(current_path).to eq project_wiki_path(project, :home)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates a page that has a path', :js do
|
context 'in a user namespace' do
|
||||||
click_on('New page')
|
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
|
||||||
|
|
||||||
page.within('#modal-new-wiki') do
|
it 'redirects back to the home edit page' do
|
||||||
fill_in(:new_wiki_path, with: 'one/two/three-test')
|
page.within(:css, '.wiki-form .form-actions') do
|
||||||
click_on('Create page')
|
click_on('Cancel')
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(current_path).to eq project_wiki_path(project, :home)
|
||||||
end
|
end
|
||||||
|
|
||||||
page.within '.wiki-form' do
|
it 'updates a page that has a path', :js do
|
||||||
fill_in(:wiki_content, with: 'wiki content')
|
click_on('New page')
|
||||||
click_on('Create page')
|
|
||||||
|
page.within('#modal-new-wiki') do
|
||||||
|
fill_in(:new_wiki_path, with: 'one/two/three-test')
|
||||||
|
click_on('Create page')
|
||||||
|
end
|
||||||
|
|
||||||
|
page.within '.wiki-form' do
|
||||||
|
fill_in(:wiki_content, with: 'wiki content')
|
||||||
|
click_on('Create page')
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(current_path).to include('one/two/three-test')
|
||||||
|
expect(find('.wiki-pages')).to have_content('Three')
|
||||||
|
|
||||||
|
first(:link, text: 'Three').click
|
||||||
|
|
||||||
|
expect(find('.nav-text')).to have_content('Three')
|
||||||
|
|
||||||
|
click_on('Edit')
|
||||||
|
|
||||||
|
expect(current_path).to include('one/two/three-test')
|
||||||
|
expect(page).to have_content('Edit Page')
|
||||||
|
|
||||||
|
fill_in('Content', with: 'Updated Wiki Content')
|
||||||
|
click_on('Save changes')
|
||||||
|
|
||||||
|
expect(page).to have_content('Updated Wiki Content')
|
||||||
end
|
end
|
||||||
|
|
||||||
expect(current_path).to include('one/two/three-test')
|
it_behaves_like 'wiki file attachments'
|
||||||
expect(find('.wiki-pages')).to have_content('Three')
|
end
|
||||||
|
end
|
||||||
|
|
||||||
first(:link, text: 'Three').click
|
context 'when wiki is not empty' do
|
||||||
|
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' }) }
|
||||||
|
|
||||||
expect(find('.nav-text')).to have_content('Three')
|
before do
|
||||||
|
visit(project_wikis_path(project))
|
||||||
|
|
||||||
click_on('Edit')
|
click_link('Edit')
|
||||||
|
end
|
||||||
|
|
||||||
expect(current_path).to include('one/two/three-test')
|
context 'in a user namespace' do
|
||||||
expect(page).to have_content('Edit Page')
|
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
|
||||||
|
|
||||||
fill_in('Content', with: 'Updated Wiki Content')
|
it 'updates a page' do
|
||||||
click_on('Save changes')
|
# Commit message field should have correct value.
|
||||||
|
expect(page).to have_field('wiki[message]', with: 'Update home')
|
||||||
|
|
||||||
expect(page).to have_content('Updated Wiki Content')
|
fill_in(:wiki_content, with: 'My awesome wiki!')
|
||||||
|
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
|
||||||
|
|
||||||
|
it 'shows a validation error message' do
|
||||||
|
fill_in(:wiki_content, with: '')
|
||||||
|
click_button('Save changes')
|
||||||
|
|
||||||
|
expect(page).to have_selector('.wiki-form')
|
||||||
|
expect(page).to have_content('Edit Page')
|
||||||
|
expect(page).to have_content('The form contains the following error:')
|
||||||
|
expect(page).to have_content("Content can't be blank")
|
||||||
|
expect(find('textarea#wiki_content').value).to eq('')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'shows the emoji autocompletion dropdown', :js do
|
||||||
|
find('#wiki_content').native.send_keys('')
|
||||||
|
fill_in(:wiki_content, with: ':')
|
||||||
|
|
||||||
|
expect(page).to have_selector('.atwho-view')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'shows the error message' do
|
||||||
|
wiki_page.update(content: 'Update')
|
||||||
|
|
||||||
|
click_button('Save changes')
|
||||||
|
|
||||||
|
expect(page).to have_content('Someone edited the page the same time you did.')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates a page' do
|
||||||
|
fill_in('Content', with: 'Updated Wiki Content')
|
||||||
|
click_on('Save changes')
|
||||||
|
|
||||||
|
expect(page).to have_content('Updated Wiki Content')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'cancels editing of a page' do
|
||||||
|
page.within(:css, '.wiki-form .form-actions') do
|
||||||
|
click_on('Cancel')
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(current_path).to eq(project_wiki_path(project, wiki_page))
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'wiki file attachments'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'in a group namespace' do
|
||||||
|
let(:project) { create(:project, :wiki_repo, namespace: create(:group, :public)) }
|
||||||
|
|
||||||
|
it 'updates a page' do
|
||||||
|
# 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')
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
it_behaves_like 'wiki file attachments'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the page is in a subdir' do
|
||||||
|
let!(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
|
||||||
|
let(:project_wiki) { create(:project_wiki, project: project, user: project.creator) }
|
||||||
|
let(:page_name) { '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' }) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
visit(project_wiki_edit_path(project, wiki_page))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'moves the page to the root folder', :skip_gitaly_mock do
|
||||||
|
fill_in(:wiki_title, with: "/#{page_name}")
|
||||||
|
|
||||||
|
click_button('Save changes')
|
||||||
|
|
||||||
|
expect(current_path).to eq(project_wiki_path(project, page_name))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'moves the page to other dir' do
|
||||||
|
new_page_dir = "foo1/bar1/#{page_name}"
|
||||||
|
|
||||||
|
fill_in(:wiki_title, with: new_page_dir)
|
||||||
|
|
||||||
|
click_button('Save changes')
|
||||||
|
|
||||||
|
expect(current_path).to eq(project_wiki_path(project, new_page_dir))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'remains in the same place if title has not changed' do
|
||||||
|
original_path = project_wiki_path(project, wiki_page)
|
||||||
|
|
||||||
|
fill_in(:wiki_title, with: page_name)
|
||||||
|
|
||||||
|
click_button('Save changes')
|
||||||
|
|
||||||
|
expect(current_path).to eq(original_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can be moved to a different dir with a different name' do
|
||||||
|
new_page_dir = "foo1/bar1/new_page_name"
|
||||||
|
|
||||||
|
fill_in(:wiki_title, with: new_page_dir)
|
||||||
|
|
||||||
|
click_button('Save changes')
|
||||||
|
|
||||||
|
expect(current_path).to eq(project_wiki_path(project, new_page_dir))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can be renamed and moved to the root folder' do
|
||||||
|
new_name = 'new_page_name'
|
||||||
|
|
||||||
|
fill_in(:wiki_title, with: "/#{new_name}")
|
||||||
|
|
||||||
|
click_button('Save changes')
|
||||||
|
|
||||||
|
expect(current_path).to eq(project_wiki_path(project, new_name))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'squishes the title before creating the page' do
|
||||||
|
new_page_dir = " foo1 / bar1 / #{page_name} "
|
||||||
|
|
||||||
|
fill_in(:wiki_title, with: new_page_dir)
|
||||||
|
|
||||||
|
click_button('Save changes')
|
||||||
|
|
||||||
|
expect(current_path).to eq(project_wiki_path(project, "foo1/bar1/#{page_name}"))
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'wiki file attachments'
|
it_behaves_like 'wiki file attachments'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when wiki is not empty' do
|
context 'when Gitaly is enabled' do
|
||||||
let(:project_wiki) { create(:project_wiki, project: project, user: project.creator) }
|
it_behaves_like 'wiki page user update'
|
||||||
let!(:wiki_page) { create(:wiki_page, wiki: project_wiki, attrs: { title: 'home', content: 'Home page' }) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
visit(project_wikis_path(project))
|
|
||||||
|
|
||||||
click_link('Edit')
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'in a user namespace' do
|
|
||||||
let(:project) { create(:project, :wiki_repo) }
|
|
||||||
|
|
||||||
it 'updates a page' do
|
|
||||||
# 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')
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
it 'shows a validation error message' do
|
|
||||||
fill_in(:wiki_content, with: '')
|
|
||||||
click_button('Save changes')
|
|
||||||
|
|
||||||
expect(page).to have_selector('.wiki-form')
|
|
||||||
expect(page).to have_content('Edit Page')
|
|
||||||
expect(page).to have_content('The form contains the following error:')
|
|
||||||
expect(page).to have_content("Content can't be blank")
|
|
||||||
expect(find('textarea#wiki_content').value).to eq('')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'shows the emoji autocompletion dropdown', :js do
|
|
||||||
find('#wiki_content').native.send_keys('')
|
|
||||||
fill_in(:wiki_content, with: ':')
|
|
||||||
|
|
||||||
expect(page).to have_selector('.atwho-view')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'shows the error message' do
|
|
||||||
wiki_page.update(content: 'Update')
|
|
||||||
|
|
||||||
click_button('Save changes')
|
|
||||||
|
|
||||||
expect(page).to have_content('Someone edited the page the same time you did.')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates a page' do
|
|
||||||
fill_in('Content', with: 'Updated Wiki Content')
|
|
||||||
click_on('Save changes')
|
|
||||||
|
|
||||||
expect(page).to have_content('Updated Wiki Content')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'cancels editing of a page' do
|
|
||||||
page.within(:css, '.wiki-form .form-actions') do
|
|
||||||
click_on('Cancel')
|
|
||||||
end
|
|
||||||
|
|
||||||
expect(current_path).to eq(project_wiki_path(project, wiki_page))
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'wiki file attachments'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'in a group namespace' do
|
|
||||||
let(:project) { create(:project, :wiki_repo, namespace: create(:group, :public)) }
|
|
||||||
|
|
||||||
it 'updates a page' do
|
|
||||||
# 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')
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
it_behaves_like 'wiki file attachments'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the page is in a subdir' do
|
context 'when Gitaly is disabled', :skip_gitaly_mock do
|
||||||
let!(:project) { create(:project, :wiki_repo) }
|
it_behaves_like 'wiki page user update'
|
||||||
let(:project_wiki) { create(:project_wiki, project: project, user: project.creator) }
|
|
||||||
let(:page_name) { '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' }) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
visit(project_wiki_edit_path(project, wiki_page))
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'moves the page to the root folder' do
|
|
||||||
fill_in(:wiki_title, with: "/#{page_name}")
|
|
||||||
|
|
||||||
click_button('Save changes')
|
|
||||||
|
|
||||||
expect(current_path).to eq(project_wiki_path(project, page_name))
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'moves the page to other dir' do
|
|
||||||
new_page_dir = "foo1/bar1/#{page_name}"
|
|
||||||
|
|
||||||
fill_in(:wiki_title, with: new_page_dir)
|
|
||||||
|
|
||||||
click_button('Save changes')
|
|
||||||
|
|
||||||
expect(current_path).to eq(project_wiki_path(project, new_page_dir))
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'remains in the same place if title has not changed' do
|
|
||||||
original_path = project_wiki_path(project, wiki_page)
|
|
||||||
|
|
||||||
fill_in(:wiki_title, with: page_name)
|
|
||||||
|
|
||||||
click_button('Save changes')
|
|
||||||
|
|
||||||
expect(current_path).to eq(original_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'can be moved to a different dir with a different name' do
|
|
||||||
new_page_dir = "foo1/bar1/new_page_name"
|
|
||||||
|
|
||||||
fill_in(:wiki_title, with: new_page_dir)
|
|
||||||
|
|
||||||
click_button('Save changes')
|
|
||||||
|
|
||||||
expect(current_path).to eq(project_wiki_path(project, new_page_dir))
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'can be renamed and moved to the root folder' do
|
|
||||||
new_name = 'new_page_name'
|
|
||||||
|
|
||||||
fill_in(:wiki_title, with: "/#{new_name}")
|
|
||||||
|
|
||||||
click_button('Save changes')
|
|
||||||
|
|
||||||
expect(current_path).to eq(project_wiki_path(project, new_name))
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'squishes the title before creating the page' do
|
|
||||||
new_page_dir = " foo1 / bar1 / #{page_name} "
|
|
||||||
|
|
||||||
fill_in(:wiki_title, with: new_page_dir)
|
|
||||||
|
|
||||||
click_button('Save changes')
|
|
||||||
|
|
||||||
expect(current_path).to eq(project_wiki_path(project, "foo1/bar1/#{page_name}"))
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'wiki file attachments'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,164 +1,174 @@
|
||||||
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(:user) { create(:user) }
|
||||||
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
|
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
|
||||||
let(:path) { 'image.png' }
|
let(:path) { 'image.png' }
|
||||||
let(:wiki_page) do
|
let(:wiki_page) do
|
||||||
create(:wiki_page,
|
create(:wiki_page,
|
||||||
wiki: project.wiki,
|
wiki: project.wiki,
|
||||||
attrs: { title: 'home', content: "Look at this [image](#{path})\n\n ![alt text](#{path})" })
|
attrs: { title: 'home', content: "Look at this [image](#{path})\n\n ![alt text](#{path})" })
|
||||||
end
|
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
|
||||||
|
fill_in(:new_wiki_path, with: 'one/two/three-test')
|
||||||
|
click_on('Create page')
|
||||||
|
end
|
||||||
|
|
||||||
|
page.within('.wiki-form') do
|
||||||
|
fill_in(:wiki_content, with: 'wiki content')
|
||||||
|
click_on('Create page')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
page.within('.wiki-form') do
|
it 'shows the history of a page that has a path', :js do
|
||||||
fill_in(:wiki_content, with: 'wiki content')
|
expect(current_path).to include('one/two/three-test')
|
||||||
click_on('Create page')
|
|
||||||
|
first(:link, text: 'Three').click
|
||||||
|
click_on('Page history')
|
||||||
|
|
||||||
|
expect(current_path).to include('one/two/three-test')
|
||||||
|
|
||||||
|
page.within(:css, '.nav-text') do
|
||||||
|
expect(page).to have_content('History')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'shows an old version of a page', :js do
|
||||||
|
expect(current_path).to include('one/two/three-test')
|
||||||
|
expect(find('.wiki-pages')).to have_content('Three')
|
||||||
|
|
||||||
|
first(:link, text: 'Three').click
|
||||||
|
|
||||||
|
expect(find('.nav-text')).to have_content('Three')
|
||||||
|
|
||||||
|
click_on('Edit')
|
||||||
|
|
||||||
|
expect(current_path).to include('one/two/three-test')
|
||||||
|
expect(page).to have_content('Edit Page')
|
||||||
|
|
||||||
|
fill_in('Content', with: 'Updated Wiki Content')
|
||||||
|
|
||||||
|
click_on('Save changes')
|
||||||
|
click_on('Page history')
|
||||||
|
|
||||||
|
page.within(:css, '.nav-text') do
|
||||||
|
expect(page).to have_content('History')
|
||||||
|
end
|
||||||
|
|
||||||
|
find('a[href*="?version_id"]')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'shows the history of a page that has a path', :js do
|
context 'when a page does not have history' do
|
||||||
expect(current_path).to include('one/two/three-test')
|
before do
|
||||||
|
visit(project_wiki_path(project, wiki_page))
|
||||||
first(:link, text: 'Three').click
|
|
||||||
click_on('Page history')
|
|
||||||
|
|
||||||
expect(current_path).to include('one/two/three-test')
|
|
||||||
|
|
||||||
page.within(:css, '.nav-text') do
|
|
||||||
expect(page).to have_content('History')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'shows an old version of a page', :js do
|
|
||||||
expect(current_path).to include('one/two/three-test')
|
|
||||||
expect(find('.wiki-pages')).to have_content('Three')
|
|
||||||
|
|
||||||
first(:link, text: 'Three').click
|
|
||||||
|
|
||||||
expect(find('.nav-text')).to have_content('Three')
|
|
||||||
|
|
||||||
click_on('Edit')
|
|
||||||
|
|
||||||
expect(current_path).to include('one/two/three-test')
|
|
||||||
expect(page).to have_content('Edit Page')
|
|
||||||
|
|
||||||
fill_in('Content', with: 'Updated Wiki Content')
|
|
||||||
|
|
||||||
click_on('Save changes')
|
|
||||||
click_on('Page history')
|
|
||||||
|
|
||||||
page.within(:css, '.nav-text') do
|
|
||||||
expect(page).to have_content('History')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
find('a[href*="?version_id"]')
|
it 'shows all the pages' do
|
||||||
end
|
expect(page).to have_content(user.name)
|
||||||
end
|
expect(find('.wiki-pages')).to have_content(wiki_page.title.capitalize)
|
||||||
|
end
|
||||||
|
|
||||||
context 'when a page does not have history' do
|
context 'shows a file stored in a page' do
|
||||||
before do
|
let(:path) { upload_file_to_wiki(project, user, 'dk.png') }
|
||||||
visit(project_wiki_path(project, wiki_page))
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'shows all the pages' do
|
it do
|
||||||
expect(page).to have_content(user.name)
|
expect(page).to have_xpath("//img[@data-src='#{project.wiki.wiki_base_path}/#{path}']")
|
||||||
expect(find('.wiki-pages')).to have_content(wiki_page.title.capitalize)
|
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}")
|
||||||
end
|
|
||||||
|
|
||||||
context 'shows a file stored in a page' do
|
click_on('image')
|
||||||
let(:path) { upload_file_to_wiki(project, user, 'dk.png') }
|
|
||||||
|
|
||||||
it do
|
expect(current_path).to match("wikis/#{path}")
|
||||||
expect(page).to have_xpath("//img[@data-src='#{project.wiki.wiki_base_path}/#{path}']")
|
expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'shows the creation page if file does not exist' do
|
||||||
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')
|
||||||
|
end
|
||||||
|
|
||||||
expect(page).to have_selector('a.btn', text: 'Edit')
|
it 'does not show the "Edit" button' do
|
||||||
|
visit(project_wiki_path(project, wiki_page, version_id: wiki_page.versions.last.id))
|
||||||
|
|
||||||
click_on('Page history')
|
expect(page).not_to have_selector('a.btn', text: 'Edit')
|
||||||
|
|
||||||
expect(page).to have_content(user.name)
|
|
||||||
expect(page).to have_content("#{user.username} created page: home")
|
|
||||||
expect(page).to have_content('updated home')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not show the "Edit" button' do
|
|
||||||
visit(project_wiki_path(project, wiki_page, version_id: wiki_page.versions.last.id))
|
|
||||||
|
|
||||||
expect(page).not_to have_selector('a.btn', text: 'Edit')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when page has invalid content encoding' do
|
|
||||||
let(:content) { 'whatever'.force_encoding('ISO-8859-1') }
|
|
||||||
|
|
||||||
before do
|
|
||||||
allow(Gitlab::EncodingHelper).to receive(:encode!).and_return(content)
|
|
||||||
|
|
||||||
visit(project_wiki_path(project, wiki_page))
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not show "Edit" button' do
|
|
||||||
expect(page).not_to have_selector('a.btn', text: 'Edit')
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
context 'when page has invalid content encoding' do
|
||||||
|
let(:content) { 'whatever'.force_encoding('ISO-8859-1') }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(Gitlab::EncodingHelper).to receive(:encode!).and_return(content)
|
||||||
|
|
||||||
|
visit(project_wiki_path(project, wiki_page))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not show "Edit" button' do
|
||||||
|
expect(page).not_to have_selector('a.btn', text: 'Edit')
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
it 'opens a default wiki page', :js do
|
||||||
|
visit(project_path(project))
|
||||||
|
|
||||||
|
find('.shortcuts-wiki').click
|
||||||
|
click_link "Create your first page"
|
||||||
|
|
||||||
|
expect(page).to have_content('Home · Create Page')
|
||||||
|
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
|
context 'when Gitaly is disabled', :skip_gitaly_mock do
|
||||||
click_link "Create your first page"
|
it_behaves_like 'wiki page user view'
|
||||||
|
|
||||||
expect(page).to have_content('Home · Create Page')
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -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
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
|
|
||||||
|
it 'has 10 elements' do
|
||||||
|
expect(subject.size).to eq(10)
|
||||||
|
end
|
||||||
|
it { is_expected.to include(SeedRepo::EmptyCommit::ID) }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'has 10 elements' do
|
context 'path is nil' do
|
||||||
expect(subject.size).to eq(10)
|
subject do
|
||||||
|
commits = described_class.where(
|
||||||
|
repo: repository,
|
||||||
|
ref: 'master',
|
||||||
|
path: nil,
|
||||||
|
limit: 10
|
||||||
|
)
|
||||||
|
|
||||||
|
commits.map { |c| c.id }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has 10 elements' do
|
||||||
|
expect(subject.size).to eq(10)
|
||||||
|
end
|
||||||
|
it { is_expected.to include(SeedRepo::EmptyCommit::ID) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'ref is branch name' do
|
||||||
|
subject do
|
||||||
|
commits = described_class.where(
|
||||||
|
repo: repository,
|
||||||
|
ref: 'master',
|
||||||
|
path: 'files',
|
||||||
|
limit: 3,
|
||||||
|
offset: 1
|
||||||
|
)
|
||||||
|
|
||||||
|
commits.map { |c| c.id }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has 3 elements' do
|
||||||
|
expect(subject.size).to eq(3)
|
||||||
|
end
|
||||||
|
it { is_expected.to include("d14d6c0abdd253381df51a723d58691b2ee1ab08") }
|
||||||
|
it { is_expected.not_to include("eb49186cfa5c4338011f5f590fac11bd66c5c631") }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'ref is commit id' do
|
||||||
|
subject do
|
||||||
|
commits = described_class.where(
|
||||||
|
repo: repository,
|
||||||
|
ref: "874797c3a73b60d2187ed6e2fcabd289ff75171e",
|
||||||
|
path: 'files',
|
||||||
|
limit: 3,
|
||||||
|
offset: 1
|
||||||
|
)
|
||||||
|
|
||||||
|
commits.map { |c| c.id }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has 3 elements' do
|
||||||
|
expect(subject.size).to eq(3)
|
||||||
|
end
|
||||||
|
it { is_expected.to include("2f63565e7aac07bcdadb654e253078b727143ec4") }
|
||||||
|
it { is_expected.not_to include(SeedRepo::Commit::ID) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'ref is tag' do
|
||||||
|
subject do
|
||||||
|
commits = described_class.where(
|
||||||
|
repo: repository,
|
||||||
|
ref: 'v1.0.0',
|
||||||
|
path: 'files',
|
||||||
|
limit: 3,
|
||||||
|
offset: 1
|
||||||
|
)
|
||||||
|
|
||||||
|
commits.map { |c| c.id }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has 3 elements' do
|
||||||
|
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(SeedRepo::EmptyCommit::ID) }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'path is nil' do
|
describe '.where with gitaly' do
|
||||||
subject do
|
it_should_behave_like '.where'
|
||||||
commits = described_class.where(
|
|
||||||
repo: repository,
|
|
||||||
ref: 'master',
|
|
||||||
path: nil,
|
|
||||||
limit: 10
|
|
||||||
)
|
|
||||||
|
|
||||||
commits.map { |c| c.id }
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'has 10 elements' do
|
|
||||||
expect(subject.size).to eq(10)
|
|
||||||
end
|
|
||||||
it { is_expected.to include(SeedRepo::EmptyCommit::ID) }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'ref is branch name' do
|
describe '.where without gitaly', :skip_gitaly_mock do
|
||||||
subject do
|
it_should_behave_like '.where'
|
||||||
commits = described_class.where(
|
|
||||||
repo: repository,
|
|
||||||
ref: 'master',
|
|
||||||
path: 'files',
|
|
||||||
limit: 3,
|
|
||||||
offset: 1
|
|
||||||
)
|
|
||||||
|
|
||||||
commits.map { |c| c.id }
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'has 3 elements' do
|
|
||||||
expect(subject.size).to eq(3)
|
|
||||||
end
|
|
||||||
it { is_expected.to include("d14d6c0abdd253381df51a723d58691b2ee1ab08") }
|
|
||||||
it { is_expected.not_to include("eb49186cfa5c4338011f5f590fac11bd66c5c631") }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'ref is commit id' do
|
|
||||||
subject do
|
|
||||||
commits = described_class.where(
|
|
||||||
repo: repository,
|
|
||||||
ref: "874797c3a73b60d2187ed6e2fcabd289ff75171e",
|
|
||||||
path: 'files',
|
|
||||||
limit: 3,
|
|
||||||
offset: 1
|
|
||||||
)
|
|
||||||
|
|
||||||
commits.map { |c| c.id }
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'has 3 elements' do
|
|
||||||
expect(subject.size).to eq(3)
|
|
||||||
end
|
|
||||||
it { is_expected.to include("2f63565e7aac07bcdadb654e253078b727143ec4") }
|
|
||||||
it { is_expected.not_to include(SeedRepo::Commit::ID) }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'ref is tag' do
|
|
||||||
subject do
|
|
||||||
commits = described_class.where(
|
|
||||||
repo: repository,
|
|
||||||
ref: 'v1.0.0',
|
|
||||||
path: 'files',
|
|
||||||
limit: 3,
|
|
||||||
offset: 1
|
|
||||||
)
|
|
||||||
|
|
||||||
commits.map { |c| c.id }
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'has 3 elements' do
|
|
||||||
expect(subject.size).to eq(3)
|
|
||||||
end
|
|
||||||
it { is_expected.to include("874797c3a73b60d2187ed6e2fcabd289ff75171e") }
|
|
||||||
it { is_expected.not_to include(SeedRepo::Commit::ID) }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.between' do
|
describe '.between' 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
|
||||||
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
|
||||||
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
|
end
|
||||||
|
|
||||||
it 'gets messages in one batch', :request_store do
|
context 'when Gitaly commit_messages feature is enabled' do
|
||||||
expect { subject.map(&:itself) }.to change { Gitlab::GitalyClient.get_request_count }.by(1)
|
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
|
||||||
|
|
||||||
|
context 'when Gitaly commit_messages feature is disabled', :disable_gitaly do
|
||||||
|
it_behaves_like 'getting commit messages'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
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
|
||||||
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
|
end
|
||||||
|
|
||||||
it 'gets messages in one batch', :request_store do
|
context 'when Gitaly tag_messages feature is enabled' do
|
||||||
expect { subject.map(&:itself) }.to change { Gitlab::GitalyClient.get_request_count }.by(1)
|
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
|
||||||
|
|
||||||
|
context 'when Gitaly tag_messages feature is disabled', :disable_gitaly do
|
||||||
|
it_behaves_like 'getting tag messages'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -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'
|
||||||
|
|
||||||
# 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
|
||||||
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
|
||||||
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
|
||||||
|
let(:squash_path) { File.join(repo_path, "gitlab-worktree", "squash-#{subject.id}") }
|
||||||
|
|
||||||
|
before do
|
||||||
|
system(*%W(#{Gitlab.config.git.bin_path} -C #{repo_path} worktree add --detach #{squash_path} master))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns true when there is a current squash directory' do
|
||||||
|
expect(subject.squash_in_progress?).to be_truthy
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false when there is no squash directory' do
|
||||||
|
FileUtils.rm_rf(squash_path)
|
||||||
|
|
||||||
|
expect(subject.squash_in_progress?).to be_falsey
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false when the squash directory has expired' do
|
||||||
|
time = 20.minutes.ago.to_time
|
||||||
|
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
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
let(:squash_path) { File.join(repo_path, "gitlab-worktree", "squash-#{subject.id}") }
|
|
||||||
|
|
||||||
before do
|
context 'when Gitaly squash_in_progress is enabled' do
|
||||||
system(*%W(#{Gitlab.config.git.bin_path} -C #{repo_path} worktree add --detach #{squash_path} master))
|
it_behaves_like 'checking whether a squash is in progress'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true when there is a current squash directory' do
|
context 'when Gitaly squash_in_progress is disabled', :disable_gitaly do
|
||||||
expect(subject.squash_in_progress?).to be_truthy
|
it_behaves_like 'checking whether a squash is in progress'
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns false when there is no squash directory' do
|
|
||||||
FileUtils.rm_rf(squash_path)
|
|
||||||
|
|
||||||
expect(subject.squash_in_progress?).to be_falsey
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns false when the squash directory has expired' do
|
|
||||||
time = 20.minutes.ago.to_time
|
|
||||||
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
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -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
|
||||||
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")
|
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
|
||||||
subject.pages.each { |page| destroy_page(page.page) }
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns the latest version of the page if it exists" do
|
|
||||||
page = subject.find_page("index page")
|
|
||||||
expect(page.title).to eq("index page")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns nil if the page does not exist" do
|
|
||||||
expect(subject.find_page("non-existent")).to eq(nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "can find a page by slug" do
|
|
||||||
page = subject.find_page("index-page")
|
|
||||||
expect(page.title).to eq("index page")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns a WikiPage instance" do
|
|
||||||
page = subject.find_page("index page")
|
|
||||||
expect(page).to be_a WikiPage
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'pages with multibyte-character title' do
|
|
||||||
before do
|
before do
|
||||||
create_page("autre pagé", "C'est un génial Gollum Wiki")
|
create_page("index page", "This is an awesome Gollum Wiki")
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
subject.pages.each { |page| destroy_page(page.page) }
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the latest version of the page if it exists" do
|
||||||
|
page = subject.find_page("index page")
|
||||||
|
expect(page.title).to eq("index page")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns nil if the page does not exist" do
|
||||||
|
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("autre pagé")
|
page = subject.find_page("index-page")
|
||||||
expect(page.title).to eq("autre pagé")
|
expect(page.title).to eq("index page")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a WikiPage instance" do
|
||||||
|
page = subject.find_page("index page")
|
||||||
|
expect(page).to be_a WikiPage
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'pages with multibyte-character title' do
|
||||||
|
before do
|
||||||
|
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
|
||||||
|
|
||||||
|
context 'pages with invalidly-encoded content' do
|
||||||
|
before do
|
||||||
|
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
|
||||||
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
|
||||||
|
file = subject.find_file('image.png')
|
||||||
|
expect(file.mime_type).to eq('image/png')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns nil if the page does not exist' do
|
||||||
|
expect(subject.find_file('non-existent')).to eq(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a Gitlab::Git::WikiFile instance' do
|
||||||
|
file = subject.find_file('image.png')
|
||||||
|
expect(file).to be_a Gitlab::Git::WikiFile
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the whole file' do
|
||||||
|
file = subject.find_file('image.png')
|
||||||
|
image.rewind
|
||||||
|
|
||||||
|
expect(file.raw_data.b).to eq(image.read.b)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns the latest version of the file if it exists' do
|
context 'when Gitaly wiki_find_file is enabled' do
|
||||||
file = subject.find_file('image.png')
|
it_behaves_like 'finding a wiki file'
|
||||||
expect(file.mime_type).to eq('image/png')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns nil if the page does not exist' do
|
context 'when Gitaly wiki_find_file is disabled', :skip_gitaly_mock do
|
||||||
expect(subject.find_file('non-existent')).to eq(nil)
|
it_behaves_like 'finding a wiki file'
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns a Gitlab::Git::WikiFile instance' do
|
|
||||||
file = subject.find_file('image.png')
|
|
||||||
expect(file).to be_a Gitlab::Git::WikiFile
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns the whole file' do
|
|
||||||
file = subject.find_file('image.png')
|
|
||||||
image.rewind
|
|
||||||
|
|
||||||
expect(file.raw_data.b).to eq(image.read.b)
|
|
||||||
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
|
||||||
|
destroy_page(subject.pages.first.page)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates a new wiki page" do
|
||||||
|
expect(subject.create_page("test page", "this is content")).not_to eq(false)
|
||||||
|
expect(subject.pages.count).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
it "sets the correct commit message" do
|
||||||
|
subject.create_page("test page", "some content", :markdown, "commit message")
|
||||||
|
expect(subject.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 'updates project activity' do
|
||||||
|
subject.create_page('Test Page', 'This is content')
|
||||||
|
|
||||||
|
project.reload
|
||||||
|
|
||||||
|
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)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "creates a new wiki page" do
|
context 'when Gitaly wiki_write_page is enabled' do
|
||||||
expect(subject.create_page("test page", "this is content")).not_to eq(false)
|
it_behaves_like 'creating a wiki page'
|
||||||
expect(subject.pages.count).to eq(1)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns false when a duplicate page exists" do
|
context 'when Gitaly wiki_write_page is disabled', :skip_gitaly_mock do
|
||||||
subject.create_page("test page", "content")
|
it_behaves_like 'creating a wiki page'
|
||||||
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") }
|
|
||||||
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.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 'updates project activity' do
|
|
||||||
subject.create_page('Test Page', 'This is content')
|
|
||||||
|
|
||||||
project.reload
|
|
||||||
|
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -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")
|
||||||
|
@page = subject.wiki.page(title: "index")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "deletes the page" do
|
||||||
|
subject.delete_page(@page)
|
||||||
|
expect(subject.pages.count).to eq(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets the correct commit email' do
|
||||||
|
subject.delete_page(@page)
|
||||||
|
|
||||||
|
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 'updates project activity' do
|
||||||
|
subject.delete_page(@page)
|
||||||
|
|
||||||
|
project.reload
|
||||||
|
|
||||||
|
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)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "deletes the page" do
|
context 'when Gitaly wiki_delete_page is enabled' do
|
||||||
subject.delete_page(@page)
|
it_behaves_like 'deleting a wiki page'
|
||||||
expect(subject.pages.count).to eq(0)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sets the correct commit email' do
|
context 'when Gitaly wiki_delete_page is disabled', :skip_gitaly_mock do
|
||||||
subject.delete_page(@page)
|
it_behaves_like 'deleting a wiki page'
|
||||||
|
|
||||||
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 'updates project activity' do
|
|
||||||
subject.delete_page(@page)
|
|
||||||
|
|
||||||
project.reload
|
|
||||||
|
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -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.to include('v1.1.0') }
|
||||||
it { is_expected.not_to include('v1.0.0') }
|
it { is_expected.not_to include('v1.0.0') }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when gitaly is enabled' do
|
||||||
|
it_behaves_like '#tag_names_contains'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when gitaly is enabled', :skip_gitaly_mock do
|
||||||
|
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
|
||||||
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
|
||||||
|
|
||||||
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) )
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when some commits are not found ' do
|
|
||||||
let(:oids) do
|
|
||||||
['deadbeef'] + TestEnv::BRANCH_SHA.values.first(10)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns only found commits' do
|
|
||||||
expect(subject).not_to include(nil)
|
expect(subject).not_to include(nil)
|
||||||
expect(subject.size).to eq(10)
|
expect(subject.size).to eq(oids.size)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns only Commit instances' do
|
||||||
|
expect(subject).to all( be_a(Commit) )
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when some commits are not found ' do
|
||||||
|
let(:oids) do
|
||||||
|
['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
|
||||||
|
|
||||||
|
context 'when no oids are passed' do
|
||||||
|
let(:oids) { [] }
|
||||||
|
|
||||||
|
it 'does not call #batch_by_oid' do
|
||||||
|
expect(Gitlab::Git::Commit).not_to receive(:batch_by_oid)
|
||||||
|
|
||||||
|
subject
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when no oids are passed' do
|
context 'when Gitaly list_commits_by_oid is enabled' do
|
||||||
let(:oids) { [] }
|
it_behaves_like 'batch commits fetching'
|
||||||
|
end
|
||||||
|
|
||||||
it 'does not call #batch_by_oid' do
|
context 'when Gitaly list_commits_by_oid is enabled', :disable_gitaly do
|
||||||
expect(Gitlab::Git::Commit).not_to receive(:batch_by_oid)
|
it_behaves_like 'batch commits fetching'
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
end
|
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(
|
expect(commit_ids).to include(
|
||||||
'5937ac0a7beb003549fc5fd26fc247adbce4a52e',
|
'5937ac0a7beb003549fc5fd26fc247adbce4a52e',
|
||||||
'6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9',
|
'6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9',
|
||||||
'cfe32cf61b73a0d5e9f13e774abde7ff789b1660'
|
'cfe32cf61b73a0d5e9f13e774abde7ff789b1660'
|
||||||
)
|
)
|
||||||
expect(commit_ids).not_to include('913c66a37b4a45b9769037c55c2d238bd0942d2e')
|
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('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
|
||||||
|
end
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
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
|
||||||
|
merge_commit_id = merge(repository, user, merge_request, message)
|
||||||
|
|
||||||
|
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 'sets the `in_progress_merge_commit_sha` flag for the given merge request' do
|
context 'with gitaly' do
|
||||||
merge_commit_id = merge(repository, user, merge_request, message)
|
it_behaves_like '#merge'
|
||||||
|
|
||||||
expect(merge_request.in_progress_merge_commit_sha).to eq(merge_commit_id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'removes carriage returns from commit message' do
|
context 'without gitaly', :skip_gitaly_mock do
|
||||||
merge_commit_id = merge(repository, user, merge_request, message)
|
it_behaves_like '#merge'
|
||||||
|
|
||||||
expect(repository.commit(merge_commit_id).message).to eq(message.delete("\r"))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def merge(repository, user, merge_request, message)
|
def merge(repository, user, merge_request, message)
|
||||||
|
@ -1320,77 +1392,97 @@ 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
|
context 'when there is a conflict' do
|
||||||
it 'raises an error' do
|
it 'raises an error' do
|
||||||
expect { repository.revert(user, new_image_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError)
|
expect { repository.revert(user, new_image_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when commit was already reverted' do
|
||||||
|
it 'raises an error' do
|
||||||
|
repository.revert(user, update_image_commit, 'master', message)
|
||||||
|
|
||||||
|
expect { repository.revert(user, update_image_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when commit can be reverted' do
|
||||||
|
it 'reverts the changes' do
|
||||||
|
expect(repository.revert(user, update_image_commit, 'master', message)).to be_truthy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'reverting a merge commit' do
|
||||||
|
it 'reverts the changes' do
|
||||||
|
merge_commit
|
||||||
|
expect(repository.blob_at_branch('master', 'files/ruby/feature.rb')).to be_present
|
||||||
|
|
||||||
|
repository.revert(user, merge_commit, 'master', message)
|
||||||
|
expect(repository.blob_at_branch('master', 'files/ruby/feature.rb')).not_to be_present
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when commit was already reverted' do
|
context 'when Gitaly revert feature is enabled' do
|
||||||
it 'raises an error' do
|
it_behaves_like 'reverting a commit'
|
||||||
repository.revert(user, update_image_commit, 'master', message)
|
|
||||||
|
|
||||||
expect { repository.revert(user, update_image_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when commit can be reverted' do
|
context 'when Gitaly revert feature is disabled', :disable_gitaly do
|
||||||
it 'reverts the changes' do
|
it_behaves_like 'reverting a commit'
|
||||||
expect(repository.revert(user, update_image_commit, 'master', message)).to be_truthy
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'reverting a merge commit' do
|
|
||||||
it 'reverts the changes' do
|
|
||||||
merge_commit
|
|
||||||
expect(repository.blob_at_branch('master', 'files/ruby/feature.rb')).to be_present
|
|
||||||
|
|
||||||
repository.revert(user, merge_commit, 'master', message)
|
|
||||||
expect(repository.blob_at_branch('master', 'files/ruby/feature.rb')).not_to be_present
|
|
||||||
end
|
|
||||||
end
|
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
|
context 'when there is a conflict' do
|
||||||
it 'raises an error' do
|
it 'raises an error' do
|
||||||
expect { repository.cherry_pick(user, conflict_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError)
|
expect { repository.cherry_pick(user, conflict_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when commit was already cherry-picked' do
|
||||||
|
it 'raises an error' do
|
||||||
|
repository.cherry_pick(user, pickable_commit, 'master', message)
|
||||||
|
|
||||||
|
expect { repository.cherry_pick(user, pickable_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when commit can be cherry-picked' do
|
||||||
|
it 'cherry-picks the changes' do
|
||||||
|
expect(repository.cherry_pick(user, pickable_commit, 'master', message)).to be_truthy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'cherry-picking a merge commit' do
|
||||||
|
it 'cherry-picks the changes' do
|
||||||
|
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_message = project.commit(cherry_pick_commit_sha).message
|
||||||
|
|
||||||
|
expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).not_to be_nil
|
||||||
|
expect(cherry_pick_commit_message).to eq(message)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when commit was already cherry-picked' do
|
context 'when Gitaly cherry_pick feature is enabled' do
|
||||||
it 'raises an error' do
|
it_behaves_like 'cherry-picking a commit'
|
||||||
repository.cherry_pick(user, pickable_commit, 'master', message)
|
|
||||||
|
|
||||||
expect { repository.cherry_pick(user, pickable_commit, 'master', message) }.to raise_error(Gitlab::Git::Repository::CreateTreeError)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when commit can be cherry-picked' do
|
context 'when Gitaly cherry_pick feature is disabled', :disable_gitaly do
|
||||||
it 'cherry-picks the changes' do
|
it_behaves_like 'cherry-picking a commit'
|
||||||
expect(repository.cherry_pick(user, pickable_commit, 'master', message)).to be_truthy
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'cherry-picking a merge commit' do
|
|
||||||
it 'cherry-picks the changes' do
|
|
||||||
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_message = project.commit(cherry_pick_commit_sha).message
|
|
||||||
|
|
||||||
expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).not_to be_nil
|
|
||||||
expect(cherry_pick_commit_message).to eq(message)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
expect(repository.ancestor?(ancestor.id, commit.id)).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'it is not an ancestor' do
|
||||||
|
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 'it is not an ancestor' do
|
context 'with Gitaly enabled' do
|
||||||
expect(repository.ancestor?(commit.id, ancestor.id)).to eq(false)
|
it_behaves_like('#ancestor?')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false on nil-values' do
|
context 'with Gitaly disabled', :skip_gitaly_mock 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
|
|
||||||
|
|
||||||
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
|
end
|
||||||
|
|
||||||
|
|
|
@ -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('New Page')
|
||||||
destroy_page('bar', 'foo')
|
destroy_page('bar', 'foo')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'if the title is preceded by a / it is removed' do
|
||||||
|
create_page('/New Page', 'content')
|
||||||
|
|
||||||
|
expect(wiki.find_page('New Page')).not_to be_nil
|
||||||
|
|
||||||
|
destroy_page('New Page')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it 'if the title is preceded by a / it is removed' do
|
context 'when Gitaly is enabled' do
|
||||||
create_page('/New Page', 'content')
|
it_behaves_like 'create method'
|
||||||
|
end
|
||||||
|
|
||||||
expect(wiki.find_page('New Page')).not_to be_nil
|
context 'when Gitaly is disabled', :skip_gitaly_mock do
|
||||||
|
it_behaves_like 'create method'
|
||||||
destroy_page('New Page')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#update" do
|
describe "#update" do
|
||||||
before do
|
shared_examples 'update method' do
|
||||||
create_page("Update", "content")
|
before do
|
||||||
@page = wiki.find_page("Update")
|
create_page("Update", "content")
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
|
||||||
destroy_page(@page.title, @page.directory)
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with valid attributes" do
|
|
||||||
it "updates the content of the page" do
|
|
||||||
new_content = "new content"
|
|
||||||
|
|
||||||
@page.update(content: new_content)
|
|
||||||
@page = wiki.find_page("Update")
|
@page = wiki.find_page("Update")
|
||||||
|
|
||||||
expect(@page.content).to eq("new content")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "updates the title of the page" do
|
after do
|
||||||
new_title = "Index v.1.2.4"
|
destroy_page(@page.title, @page.directory)
|
||||||
|
|
||||||
@page.update(title: new_title)
|
|
||||||
@page = wiki.find_page(new_title)
|
|
||||||
|
|
||||||
expect(@page.title).to eq(new_title)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns true" do
|
context "with valid attributes" do
|
||||||
expect(@page.update(content: "more content")).to be_truthy
|
it "updates the content of the page" do
|
||||||
end
|
new_content = "new content"
|
||||||
end
|
|
||||||
|
|
||||||
context 'with same last commit sha' do
|
@page.update(content: new_content)
|
||||||
it 'returns true' do
|
@page = wiki.find_page("Update")
|
||||||
expect(@page.update(content: 'more content', last_commit_sha: @page.last_commit_sha)).to be_truthy
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with different last commit sha' do
|
expect(@page.content).to eq("new content")
|
||||||
it 'raises exception' do
|
end
|
||||||
expect { @page.update(content: 'more content', last_commit_sha: 'xxx') }.to raise_error(WikiPage::PageChangedError)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when renaming a page' do
|
it "updates the title of the page" do
|
||||||
it 'raises an error if the page already exists' do
|
new_title = "Index v.1.2.4"
|
||||||
create_page('Existing Page', 'content')
|
|
||||||
|
|
||||||
expect { @page.update(title: 'Existing Page', content: 'new_content') }.to raise_error(WikiPage::PageRenameError)
|
@page.update(title: new_title)
|
||||||
expect(@page.title).to eq 'Update'
|
@page = wiki.find_page(new_title)
|
||||||
expect(@page.content).to eq 'new_content'
|
|
||||||
|
|
||||||
destroy_page('Existing Page')
|
expect(@page.title).to eq(new_title)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns true" do
|
||||||
|
expect(@page.update(content: "more content")).to be_truthy
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates the content and rename the file' do
|
context 'with same last commit sha' do
|
||||||
new_title = 'Renamed Page'
|
it 'returns true' do
|
||||||
new_content = 'updated content'
|
expect(@page.update(content: 'more content', last_commit_sha: @page.last_commit_sha)).to be_truthy
|
||||||
|
end
|
||||||
expect(@page.update(title: new_title, content: new_content)).to be_truthy
|
|
||||||
|
|
||||||
@page = wiki.find_page(new_title)
|
|
||||||
|
|
||||||
expect(@page).not_to be_nil
|
|
||||||
expect(@page.content).to eq new_content
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when moving a page' do
|
|
||||||
it 'raises an error if the page already exists' do
|
|
||||||
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')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates the content and moves the file' do
|
context 'with different last commit sha' do
|
||||||
new_title = 'foo/Other Page'
|
it 'raises exception' do
|
||||||
new_content = 'new_content'
|
expect { @page.update(content: 'more content', last_commit_sha: 'xxx') }.to raise_error(WikiPage::PageChangedError)
|
||||||
|
end
|
||||||
expect(@page.update(title: new_title, content: new_content)).to be_truthy
|
|
||||||
|
|
||||||
page = wiki.find_page(new_title)
|
|
||||||
|
|
||||||
expect(page).not_to be_nil
|
|
||||||
expect(page.content).to eq new_content
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'in subdir' do
|
context 'when renaming a page' do
|
||||||
before do
|
it 'raises an error if the page already exists' do
|
||||||
|
create_page('Existing Page', 'content')
|
||||||
|
|
||||||
|
expect { @page.update(title: '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')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates the content and rename the file' do
|
||||||
|
new_title = 'Renamed Page'
|
||||||
|
new_content = 'updated content'
|
||||||
|
|
||||||
|
expect(@page.update(title: new_title, content: new_content)).to be_truthy
|
||||||
|
|
||||||
|
@page = wiki.find_page(new_title)
|
||||||
|
|
||||||
|
expect(@page).not_to be_nil
|
||||||
|
expect(@page.content).to eq new_content
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when moving a page' do
|
||||||
|
it 'raises an error if the page already exists' do
|
||||||
create_page('foo/Existing Page', 'content')
|
create_page('foo/Existing Page', 'content')
|
||||||
@page = wiki.find_page('foo/Existing Page')
|
|
||||||
|
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')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'moves the page to the root folder if the title is preceded by /' do
|
it 'updates the content and moves the file' do
|
||||||
expect(@page.slug).to eq 'foo/Existing-Page'
|
new_title = 'foo/Other Page'
|
||||||
expect(@page.update(title: '/Existing Page', content: 'new_content')).to be_truthy
|
new_content = 'new_content'
|
||||||
expect(@page.slug).to eq 'Existing-Page'
|
|
||||||
|
expect(@page.update(title: new_title, content: new_content)).to be_truthy
|
||||||
|
|
||||||
|
page = wiki.find_page(new_title)
|
||||||
|
|
||||||
|
expect(page).not_to be_nil
|
||||||
|
expect(page.content).to eq new_content
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does nothing if it has the same title' do
|
context 'in subdir' do
|
||||||
original_path = @page.slug
|
before do
|
||||||
|
create_page('foo/Existing Page', 'content')
|
||||||
|
@page = wiki.find_page('foo/Existing Page')
|
||||||
|
end
|
||||||
|
|
||||||
expect(@page.update(title: 'Existing Page', content: 'new_content')).to be_truthy
|
it 'moves the page to the root folder if the title is preceded by /', :skip_gitaly_mock do
|
||||||
expect(@page.slug).to eq original_path
|
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
|
||||||
|
|
||||||
|
context 'in root dir' do
|
||||||
|
it 'does nothing if the title is preceded by /' do
|
||||||
|
original_path = @page.slug
|
||||||
|
|
||||||
|
expect(@page.update(title: '/Update', content: 'new_content')).to be_truthy
|
||||||
|
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')
|
context 'when Gitaly is disabled', :skip_gitaly_mock do
|
||||||
expect(page.content).to eq 'content'
|
it_behaves_like 'update method'
|
||||||
|
|
||||||
@page.title = 'Update'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
after do
|
||||||
|
destroy_page("Update")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an array of all commits for the page" do
|
||||||
|
3.times { |i| page.update(content: "content #{i}") }
|
||||||
|
|
||||||
|
expect(page.versions.count).to eq(4)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns instances of WikiPageVersion' do
|
||||||
|
expect(page.versions).to all( be_a(Gitlab::Git::WikiPageVersion) )
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
after do
|
context 'when Gitaly is enabled' do
|
||||||
destroy_page("Update")
|
it_behaves_like 'wiki page versions'
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns an array of all commits for the page" do
|
context 'when Gitaly is disabled', :disable_gitaly do
|
||||||
3.times { |i| page.update(content: "content #{i}") }
|
it_behaves_like 'wiki page versions'
|
||||||
|
|
||||||
expect(page.versions.count).to eq(4)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns instances of WikiPageVersion' do
|
|
||||||
expect(page.versions).to all( be_a(Gitlab::Git::WikiPageVersion) )
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -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')
|
destroy_page('RDoc')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when Gitaly wiki_page_formatted_data is enabled' do
|
||||||
|
it_behaves_like 'fetching page formatted content'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when Gitaly wiki_page_formatted_data is disabled', :disable_gitaly do
|
||||||
|
it_behaves_like 'fetching page formatted content'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
16
spec/support/gitaly.rb
Normal file
16
spec/support/gitaly.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
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
|
Loading…
Reference in a new issue