Commit 62b8756d authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-05-02

# Conflicts:
#	app/views/groups/_group_admin_settings.html.haml
#	spec/requests/api/runner_spec.rb

[ci skip]
parents 976e2e11 c22a3fa0
...@@ -194,6 +194,9 @@ gem 're2', '~> 1.1.1' ...@@ -194,6 +194,9 @@ gem 're2', '~> 1.1.1'
gem 'version_sorter', '~> 2.1.0' gem 'version_sorter', '~> 2.1.0'
# User agent parsing
gem 'device_detector'
# Cache # Cache
gem 'redis-rails', '~> 5.0.2' gem 'redis-rails', '~> 5.0.2'
......
...@@ -169,6 +169,7 @@ GEM ...@@ -169,6 +169,7 @@ GEM
activerecord (>= 3.2.0, < 5.1) activerecord (>= 3.2.0, < 5.1)
descendants_tracker (0.0.4) descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
device_detector (1.0.0)
devise (4.2.0) devise (4.2.0)
bcrypt (~> 3.0) bcrypt (~> 3.0)
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
...@@ -1056,6 +1057,7 @@ DEPENDENCIES ...@@ -1056,6 +1057,7 @@ DEPENDENCIES
database_cleaner (~> 1.5.0) database_cleaner (~> 1.5.0)
deckar01-task_list (= 2.0.0) deckar01-task_list (= 2.0.0)
default_value_for (~> 3.0.0) default_value_for (~> 3.0.0)
device_detector
devise (~> 4.2) devise (~> 4.2)
devise-two-factor (~> 3.0.0) devise-two-factor (~> 3.0.0)
diffy (~> 3.1.0) diffy (~> 3.1.0)
......
...@@ -304,12 +304,12 @@ GEM ...@@ -304,12 +304,12 @@ GEM
flowdock (~> 0.7) flowdock (~> 0.7)
gitlab-grit (>= 2.4.1) gitlab-grit (>= 2.4.1)
multi_json multi_json
gitlab-gollum-lib (4.2.7.1) gitlab-gollum-lib (4.2.7.2)
gemojione (~> 3.2) gemojione (~> 3.2)
github-markup (~> 1.6) github-markup (~> 1.6)
gollum-grit_adapter (~> 1.0) gollum-grit_adapter (~> 1.0)
nokogiri (>= 1.6.1, < 2.0) nokogiri (>= 1.6.1, < 2.0)
rouge (~> 2.1) rouge (~> 3.1)
sanitize (~> 2.1) sanitize (~> 2.1)
stringex (~> 2.6) stringex (~> 2.6)
gitlab-gollum-rugged_adapter (0.4.4) gitlab-gollum-rugged_adapter (0.4.4)
...@@ -602,8 +602,6 @@ GEM ...@@ -602,8 +602,6 @@ GEM
atomic (>= 1.0.0) atomic (>= 1.0.0)
mysql2 mysql2
peek peek
peek-performance_bar (1.3.1)
peek (>= 0.1.0)
peek-pg (1.3.0) peek-pg (1.3.0)
concurrent-ruby concurrent-ruby
concurrent-ruby-ext concurrent-ruby-ext
...@@ -752,7 +750,7 @@ GEM ...@@ -752,7 +750,7 @@ GEM
retriable (3.1.1) retriable (3.1.1)
rinku (2.0.4) rinku (2.0.4)
rotp (2.1.2) rotp (2.1.2)
rouge (2.2.1) rouge (3.1.1)
rqrcode (0.10.1) rqrcode (0.10.1)
chunky_png (~> 1.0) chunky_png (~> 1.0)
rqrcode-rails3 (0.1.7) rqrcode-rails3 (0.1.7)
...@@ -1134,7 +1132,6 @@ DEPENDENCIES ...@@ -1134,7 +1132,6 @@ DEPENDENCIES
peek (~> 1.0.1) peek (~> 1.0.1)
peek-gc (~> 0.0.2) peek-gc (~> 0.0.2)
peek-mysql2 (~> 1.1.0) peek-mysql2 (~> 1.1.0)
peek-performance_bar (~> 1.3.0)
peek-pg (~> 1.3.0) peek-pg (~> 1.3.0)
peek-rblineprof (~> 0.2.0) peek-rblineprof (~> 0.2.0)
peek-redis (~> 1.2.0) peek-redis (~> 1.2.0)
...@@ -1166,7 +1163,7 @@ DEPENDENCIES ...@@ -1166,7 +1163,7 @@ DEPENDENCIES
redis-rails (~> 5.0.2) redis-rails (~> 5.0.2)
request_store (~> 1.3) request_store (~> 1.3)
responders (~> 2.0) responders (~> 2.0)
rouge (~> 2.0) rouge (~> 3.1)
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-parameterized rspec-parameterized
rspec-rails (~> 3.6.0) rspec-rails (~> 3.6.0)
......
...@@ -460,6 +460,7 @@ img.emoji { ...@@ -460,6 +460,7 @@ img.emoji {
/** COMMON CLASSES **/ /** COMMON CLASSES **/
.prepend-top-0 { margin-top: 0; } .prepend-top-0 { margin-top: 0; }
.prepend-top-2 { margin-top: 2px; }
.prepend-top-5 { margin-top: 5px; } .prepend-top-5 { margin-top: 5px; }
.prepend-top-8 { margin-top: $grid-size; } .prepend-top-8 { margin-top: $grid-size; }
.prepend-top-10 { margin-top: 10px; } .prepend-top-10 { margin-top: 10px; }
......
...@@ -39,35 +39,10 @@ ...@@ -39,35 +39,10 @@
svg { svg {
fill: currentColor; fill: currentColor;
&.s8 { $svg-sizes: 8 12 16 18 24 32 48 72;
@include svg-size(8px); @each $svg-size in $svg-sizes {
&.s#{$svg-size} {
@include svg-size(#{$svg-size}px);
} }
&.s12 {
@include svg-size(12px);
}
&.s16 {
@include svg-size(16px);
}
&.s18 {
@include svg-size(18px);
}
&.s24 {
@include svg-size(24px);
}
&.s32 {
@include svg-size(32px);
}
&.s48 {
@include svg-size(48px);
}
&.s72 {
@include svg-size(72px);
} }
} }
...@@ -114,7 +114,8 @@ class ApplicationController < ActionController::Base ...@@ -114,7 +114,8 @@ class ApplicationController < ActionController::Base
def log_exception(exception) def log_exception(exception)
Raven.capture_exception(exception) if sentry_enabled? Raven.capture_exception(exception) if sentry_enabled?
application_trace = ActionDispatch::ExceptionWrapper.new(env, exception).application_trace backtrace_cleaner = Gitlab.rails5? ? env["action_dispatch.backtrace_cleaner"] : env
application_trace = ActionDispatch::ExceptionWrapper.new(backtrace_cleaner, exception).application_trace
application_trace.map! { |t| " #{t}\n" } application_trace.map! { |t| " #{t}\n" }
logger.error "\n#{exception.class.name} (#{exception.message}):\n#{application_trace.join}" logger.error "\n#{exception.class.name} (#{exception.message}):\n#{application_trace.join}"
end end
......
...@@ -57,7 +57,7 @@ module IssuableCollections ...@@ -57,7 +57,7 @@ module IssuableCollections
out_of_range = @issuables.current_page > total_pages # rubocop:disable Gitlab/ModuleWithInstanceVariables out_of_range = @issuables.current_page > total_pages # rubocop:disable Gitlab/ModuleWithInstanceVariables
if out_of_range if out_of_range
redirect_to(url_for(params.merge(page: total_pages, only_path: true))) redirect_to(url_for(safe_params.merge(page: total_pages, only_path: true)))
end end
out_of_range out_of_range
......
...@@ -34,6 +34,6 @@ class Groups::ApplicationController < ApplicationController ...@@ -34,6 +34,6 @@ class Groups::ApplicationController < ApplicationController
def build_canonical_path(group) def build_canonical_path(group)
params[:group_id] = group.to_param params[:group_id] = group.to_param
url_for(params) url_for(safe_params)
end end
end end
class Profiles::ActiveSessionsController < Profiles::ApplicationController
def index
@sessions = ActiveSession.list(current_user)
end
def destroy
ActiveSession.destroy(current_user, params[:id])
respond_to do |format|
format.html { redirect_to profile_active_sessions_url, status: 302 }
format.js { head :ok }
end
end
end
...@@ -26,7 +26,7 @@ class Projects::ApplicationController < ApplicationController ...@@ -26,7 +26,7 @@ class Projects::ApplicationController < ApplicationController
params[:namespace_id] = project.namespace.to_param params[:namespace_id] = project.namespace.to_param
params[:project_id] = project.to_param params[:project_id] = project.to_param
url_for(params) url_for(safe_params)
end end
def repository def repository
......
...@@ -77,8 +77,7 @@ class Projects::LfsStorageController < Projects::GitHttpClientController ...@@ -77,8 +77,7 @@ class Projects::LfsStorageController < Projects::GitHttpClientController
def link_to_project!(object) def link_to_project!(object)
if object && !object.projects.exists?(storage_project.id) if object && !object.projects.exists?(storage_project.id)
object.projects << storage_project object.lfs_objects_projects.create!(project: storage_project)
object.save!
end end
end end
end end
module ActiveSessionsHelper
# Maps a device type as defined in `ActiveSession` to an svg icon name and
# outputs the icon html.
#
# see `DeviceDetector::Device::DEVICE_NAMES` about the available device types
def active_session_device_type_icon(active_session)
icon_name =
case active_session.device_type
when 'smartphone', 'feature phone', 'phablet'
'mobile'
when 'tablet'
'tablet'
when 'tv', 'smart display', 'camera', 'portable media player', 'console'
'media'
when 'car browser'
'car'
else
'monitor-o'
end
sprite_icon(icon_name, size: 16, css_class: 'prepend-top-2')
end
end
class ActiveSession
include ActiveModel::Model
attr_accessor :created_at, :updated_at,
:session_id, :ip_address,
:browser, :os, :device_name, :device_type
def current?(session)
return false if session_id.nil? || session.id.nil?
session_id == session.id
end
def human_device_type
device_type&.titleize
end
def self.set(user, request)
Gitlab::Redis::SharedState.with do |redis|
session_id = request.session.id
client = DeviceDetector.new(request.user_agent)
timestamp = Time.current
active_user_session = new(
ip_address: request.ip,
browser: client.name,
os: client.os_name,
device_name: client.device_name,
device_type: client.device_type,
created_at: user.current_sign_in_at || timestamp,
updated_at: timestamp,
session_id: session_id
)
redis.pipelined do
redis.setex(
key_name(user.id, session_id),
Settings.gitlab['session_expire_delay'] * 60,
Marshal.dump(active_user_session)
)
redis.sadd(
lookup_key_name(user.id),
session_id
)
end
end
end
def self.list(user)
Gitlab::Redis::SharedState.with do |redis|
cleaned_up_lookup_entries(redis, user.id).map do |entry|
# rubocop:disable Security/MarshalLoad
Marshal.load(entry)
# rubocop:enable Security/MarshalLoad
end
end
end
def self.destroy(user, session_id)
Gitlab::Redis::SharedState.with do |redis|
redis.srem(lookup_key_name(user.id), session_id)
deleted_keys = redis.del(key_name(user.id, session_id))
# only allow deleting the devise session if we could actually find a
# related active session. this prevents another user from deleting
# someone else's session.
if deleted_keys > 0
redis.del("#{Gitlab::Redis::SharedState::SESSION_NAMESPACE}:#{session_id}")
end
end
end
def self.cleanup(user)
Gitlab::Redis::SharedState.with do |redis|
cleaned_up_lookup_entries(redis, user.id)
end
end
def self.key_name(user_id, session_id = '*')
"#{Gitlab::Redis::SharedState::USER_SESSIONS_NAMESPACE}:#{user_id}:#{session_id}"
end
def self.lookup_key_name(user_id)
"#{Gitlab::Redis::SharedState::USER_SESSIONS_LOOKUP_NAMESPACE}:#{user_id}"
end
def self.cleaned_up_lookup_entries(redis, user_id)
lookup_key = lookup_key_name(user_id)
session_ids = redis.smembers(lookup_key)
entry_keys = session_ids.map { |session_id| key_name(user_id, session_id) }
return [] if entry_keys.empty?
entries = redis.mget(entry_keys)
session_ids_and_entries = session_ids.zip(entries)
# remove expired keys.
# only the single key entries are automatically expired by redis, the
# lookup entries in the set need to be removed manually.
session_ids_and_entries.reject { |_session_id, entry| entry }.each do |session_id, _entry|
redis.srem(lookup_key, session_id)
end
session_ids_and_entries.select { |_session_id, entry| entry }.map { |_session_id, entry| entry }
end
end
...@@ -15,7 +15,7 @@ module Ci ...@@ -15,7 +15,7 @@ module Ci
after_save :update_project_statistics_after_save, if: :size_changed? after_save :update_project_statistics_after_save, if: :size_changed?
after_destroy :update_project_statistics_after_destroy, unless: :project_destroyed? after_destroy :update_project_statistics_after_destroy, unless: :project_destroyed?
after_save :update_file_store after_save :update_file_store, if: :file_changed?
scope :with_files_stored_locally, -> { where(file_store: [nil, ::JobArtifactUploader::Store::LOCAL]) } scope :with_files_stored_locally, -> { where(file_store: [nil, ::JobArtifactUploader::Store::LOCAL]) }
scope :with_files_stored_remotely, -> { where(file_store: ::JobArtifactUploader::Store::REMOTE) } scope :with_files_stored_remotely, -> { where(file_store: ::JobArtifactUploader::Store::REMOTE) }
......
...@@ -548,6 +548,17 @@ module Ci ...@@ -548,6 +548,17 @@ module Ci
@latest_builds_with_artifacts ||= builds.latest.with_artifacts_archive.to_a @latest_builds_with_artifacts ||= builds.latest.with_artifacts_archive.to_a
end end
# Rails 5.0 autogenerated question mark enum methods return wrong result if enum value is nil.
# They always return `false`.
# These methods overwrite autogenerated ones to return correct results.
def unknown?
Gitlab.rails5? ? source.nil? : super
end
def unknown_source?
Gitlab.rails5? ? config_source.nil? : super
end
private private
def ci_yaml_from_repo def ci_yaml_from_repo
......
...@@ -13,14 +13,27 @@ module Ci ...@@ -13,14 +13,27 @@ module Ci
has_many :statuses, class_name: 'CommitStatus', foreign_key: :stage_id has_many :statuses, class_name: 'CommitStatus', foreign_key: :stage_id
has_many :builds, foreign_key: :stage_id has_many :builds, foreign_key: :stage_id
validates :project, presence: true, unless: :importing? with_options unless: :importing? do
validates :pipeline, presence: true, unless: :importing? validates :project, presence: true
validates :name, presence: true, unless: :importing? validates :pipeline, presence: true
validates :name, presence: true
validates :position, presence: true
end
after_initialize do |stage| after_initialize do
self.status = DEFAULT_STATUS if self.status.nil? self.status = DEFAULT_STATUS if self.status.nil?
end end
before_validation unless: :importing? do
next if position.present?
self.position = statuses.select(:stage_idx)
.where('stage_idx IS NOT NULL')
.group(:stage_idx)
.order('COUNT(*) DESC')
.first&.stage_idx.to_i
end
state_machine :status, initial: :created do state_machine :status, initial: :created do
event :enqueue do event :enqueue do
transition created: :pending transition created: :pending
......
...@@ -424,6 +424,12 @@ class Commit ...@@ -424,6 +424,12 @@ class Commit
# no-op but needs to be defined since #persisted? is defined # no-op but needs to be defined since #persisted? is defined
end end
def touch_later
# No-op.
# This method is called by ActiveRecord.
# We don't want to do anything for `Commit` model, so this is empty.
end
WIP_REGEX = /\A\s*(((?i)(\[WIP\]|WIP:|WIP)\s|WIP$))|(fixup!|squash!)\s/.freeze WIP_REGEX = /\A\s*(((?i)(\[WIP\]|WIP:|WIP)\s|WIP$))|(fixup!|squash!)\s/.freeze
def work_in_progress? def work_in_progress?
......
...@@ -189,4 +189,11 @@ class CommitStatus < ActiveRecord::Base ...@@ -189,4 +189,11 @@ class CommitStatus < ActiveRecord::Base
v =~ /\d+/ ? v.to_i : v v =~ /\d+/ ? v.to_i : v
end end
end end
# Rails 5.0 autogenerated question mark enum methods return wrong result if enum value is nil.
# They always return `false`.
# This method overwrites the autogenerated one to return correct result.
def unknown_failure?
Gitlab.rails5? ? failure_reason.nil? : super
end
end end
...@@ -13,7 +13,7 @@ class LfsObject < ActiveRecord::Base ...@@ -13,7 +13,7 @@ class LfsObject < ActiveRecord::Base
mount_uploader :file, LfsObjectUploader mount_uploader :file, LfsObjectUploader
after_save :update_file_store after_save :update_file_store, if: :file_changed?
def update_file_store def update_file_store
# The file.object_store is set during `uploader.store!` # The file.object_store is set during `uploader.store!`
......
...@@ -928,7 +928,7 @@ class User < ActiveRecord::Base ...@@ -928,7 +928,7 @@ class User < ActiveRecord::Base
def delete_async(deleted_by:, params: {}) def delete_async(deleted_by:, params: {})
block if params[:hard_delete] block if params[:hard_delete]
DeleteUserWorker.perform_async(deleted_by.id, id, params) DeleteUserWorker.perform_async(deleted_by.id, id, params.to_h)
end end
def notification_service def notification_service
......
...@@ -42,6 +42,7 @@ module Ci ...@@ -42,6 +42,7 @@ module Ci
def create_stage def create_stage
Ci::Stage.create!(name: @build.stage, Ci::Stage.create!(name: @build.stage,
position: @build.stage_idx,
pipeline: @build.pipeline, pipeline: @build.pipeline,
project: @build.project) project: @build.project)
end end
......
...@@ -10,8 +10,11 @@ ...@@ -10,8 +10,11 @@
%br/ %br/
%span.descr This setting can be overridden in each project. %span.descr This setting can be overridden in each project.
<<<<<<< HEAD
= render partial: 'groups/ee/project_creation_level', locals: { form: f, group: @group } = render partial: 'groups/ee/project_creation_level', locals: { form: f, group: @group }
=======
>>>>>>> upstream/master
.form-group .form-group
= f.label :require_two_factor_authentication, 'Two-factor authentication', class: 'control-label col-sm-2' = f.label :require_two_factor_authentication, 'Two-factor authentication', class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
......
...@@ -141,6 +141,17 @@ ...@@ -141,6 +141,17 @@
= link_to profile_preferences_path do = link_to profile_preferences_path do
%strong.fly-out-top-item-name %strong.fly-out-top-item-name
#{ _('Preferences') } #{ _('Preferences') }
= nav_link(controller: :active_sessions) do
= link_to profile_active_sessions_path do
.nav-icon-container
= sprite_icon('monitor-lines')
%span.nav-item-name
Active Sessions
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :active_sessions, html_options: { class: "fly-out-top-item" } ) do
= link_to profile_active_sessions_path do
%strong.fly-out-top-item-name
#{ _('Active Sessions') }
= nav_link(path: 'profiles#audit_log') do = nav_link(path: 'profiles#audit_log') do
= link_to audit_log_profile_path do = link_to audit_log_profile_path do
.nav-icon-container .nav-icon-container
......
...@@ -3,5 +3,5 @@ ...@@ -3,5 +3,5 @@
#js-peek{ data: { env: Peek.env, #js-peek{ data: { env: Peek.env,
request_id: Peek.request_id, request_id: Peek.request_id,
peek_url: peek_routes.results_url, peek_url: peek_routes.results_url,
profile_url: url_for(params.merge(lineprofiler: 'true')) }, profile_url: url_for(safe_params.merge(lineprofiler: 'true')) },
class: Peek.env } class: Peek.env }
- is_current_session = active_session.current?(session)
%li
.pull-left.append-right-10{ data: { toggle: 'tooltip' }, title: active_session.human_device_type }
= active_session_device_type_icon(active_session)
.description.pull-left
%div
%strong= active_session.ip_address
- if is_current_session
%div This is your current session
- else
%div
Last accessed on
= l(active_session.updated_at, format: :short)
%div
%strong= active_session.browser
on
%strong= active_session.os
%div
%strong Signed in
on
= l(active_session.created_at, format: :short)
- unless is_current_session
.pull-right
= link_to profile_active_session_path(active_session.session_id), data: { confirm: 'Are you sure? The device will be signed out of GitLab.' }, method: :delete, class: "btn btn-danger prepend-left-10" do
%span.sr-only Revoke
Revoke
- page_title 'Active Sessions'
- @content_class = "limit-container-width" unless fluid_layout
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
= page_title
%p
This is a list of devices that have logged into your account. Revoke any sessions that you do not recognize.
.col-lg-8
.append-bottom-default
%ul.well-list
= render partial: 'profiles/active_sessions/active_session', collection: @sessions
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
.files-changed-inner .files-changed-inner
.inline-parallel-buttons.hidden-xs.hidden-sm .inline-parallel-buttons.hidden-xs.hidden-sm
- if !diffs_expanded? && diff_files.any? { |diff_file| diff_file.collapsed? } - if !diffs_expanded? && diff_files.any? { |diff_file| diff_file.collapsed? }
= link_to 'Expand all', url_for(params.merge(expanded: 1, format: nil)), class: 'btn btn-default' = link_to 'Expand all', url_for(safe_params.merge(expanded: 1, format: nil)), class: 'btn btn-default'
- if show_whitespace_toggle - if show_whitespace_toggle
- if current_controller?(:commit) - if current_controller?(:commit)
= commit_diff_whitespace_link(diffs.project, @commit, class: 'hidden-xs') = commit_diff_whitespace_link(diffs.project, @commit, class: 'hidden-xs')
......
---
title: Replace the `project/source/markdown_render.feature` spinach test with an rspec analog
merge_request: 18525
author: "@blackst0ne"
type: other
---
title: Display active sessions and allow the user to revoke any of it
merge_request: 17867
author: Alexis Reigel
type: added
---
title: Fix file_store for artifacts and lfs when saving
merge_request:
author:
type: fixed
...@@ -15,13 +15,10 @@ cookie_key = if Rails.env.development? ...@@ -15,13 +15,10 @@ cookie_key = if Rails.env.development?
"_gitlab_session" "_gitlab_session"
end end
if Rails.env.test? sessions_config = Gitlab::Redis::SharedState.params
Gitlab::Application.config.session_store :cookie_store, key: "_gitlab_session" sessions_config[:namespace] = Gitlab::Redis::SharedState::SESSION_NAMESPACE
else
sessions_config = Gitlab::Redis::SharedState.params
sessions_config[:namespace] = Gitlab::Redis::SharedState::SESSION_NAMESPACE
Gitlab::Application.config.session_store( Gitlab::Application.config.session_store(
:redis_store, # Using the cookie_store would enable session replay attacks. :redis_store, # Using the cookie_store would enable session replay attacks.
servers: sessions_config, servers: sessions_config,
key: cookie_key, key: cookie_key,
...@@ -29,5 +26,4 @@ else ...@@ -29,5 +26,4 @@ else
httponly: true, httponly: true,
expires_in: Settings.gitlab['session_expire_delay'] * 60, expires_in: Settings.gitlab['session_expire_delay'] * 60,
path: Rails.application.config.relative_url_root.nil? ? '/' : Gitlab::Application.config.relative_url_root path: Rails.application.config.relative_url_root.nil? ? '/' : Gitlab::Application.config.relative_url_root
) )
end
...@@ -6,4 +6,16 @@ Rails.application.configure do |config| ...@@ -6,4 +6,16 @@ Rails.application.configure do |config|
Warden::Manager.before_failure do |env, opts| Warden::Manager.before_failure do |env, opts|
Gitlab::Auth::BlockedUserTracker.log_if_user_blocked(env) Gitlab::Auth::BlockedUserTracker.log_if_user_blocked(env)
end end
Warden::Manager.after_authentication do |user, auth, opts|
ActiveSession.cleanup(user)
end
Warden::Manager.after_set_user only: :fetch do |user, auth, opts|
ActiveSession.set(user, auth.request)
end
Warden::Manager.before_logout do |user, auth, opts|
ActiveSession.destroy(user || auth.user, auth.request.session.id)
end
end end
...@@ -30,6 +30,7 @@ resource :profile, only: [:show, :update] do ...@@ -30,6 +30,7 @@ resource :profile, only: [:show, :update] do
put :revoke put :revoke
end end
end end
resources :active_sessions, only: [:index, :destroy]
resources :emails, only: [:index, :create, :destroy] do resources :emails, only: [:index, :create, :destroy] do
member do member do
put :resend_confirmation_instructions put :resend_confirmation_instructions
......
class AddTmpStagePriorityIndexToCiBuilds < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index(:ci_builds, [:stage_id, :stage_idx],
where: 'stage_idx IS NOT NULL', name: 'tmp_build_stage_position_index')
end
def down
remove_concurrent_index_by_name(:ci_builds, 'tmp_build_stage_position_index')
end
end
class AddIndexToCiStage < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :ci_stages, :position, :integer
end
end
class ScheduleStagesIndexMigration < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
MIGRATION = 'MigrateStageIndex'.freeze
BATCH_SIZE = 10000
disable_ddl_transaction!
class Stage < ActiveRecord::Base
include EachBatch
self.table_name = 'ci_stages'
end
def up
disable_statement_timeout
Stage.all.tap do |relation|
queue_background_migration_jobs_by_range_at_intervals(relation,
MIGRATION,
5.minutes,
batch_size: BATCH_SIZE)
end
end
def down
# noop
end
end
...@@ -411,6 +411,7 @@ ActiveRecord::Schema.define(version: 20180425131009) do ...@@ -411,6 +411,7 @@ ActiveRecord::Schema.define(version: 20180425131009) do
add_index "ci_builds", ["project_id", "id"], name: "index_ci_builds_on_project_id_and_id", using: :btree add_index "ci_builds", ["project_id", "id"], name: "index_ci_builds_on_project_id_and_id", using: :btree
add_index "ci_builds", ["protected"], name: "index_ci_builds_on_protected", using: :btree add_index "ci_builds", ["protected"], name: "index_ci_builds_on_protected", using: :btree
add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree
add_index "ci_builds", ["stage_id", "stage_idx"], name: "tmp_build_stage_position_index", where: "(stage_idx IS NOT NULL)", using: :btree
add_index "ci_builds", ["stage_id"], name: "index_ci_builds_on_stage_id", using: :btree add_index "ci_builds", ["stage_id"], name: "index_ci_builds_on_stage_id", using: :btree
add_index "ci_builds", ["status", "type", "runner_id"], name: "index_ci_builds_on_status_and_type_and_runner_id", using: :btree add_index "ci_builds", ["status", "type", "runner_id"], name: "index_ci_builds_on_status_and_type_and_runner_id", using: :btree
add_index "ci_builds", ["status"], name: "index_ci_builds_on_status", using: :btree add_index "ci_builds", ["status"], name: "index_ci_builds_on_status", using: :btree
...@@ -597,6 +598,7 @@ ActiveRecord::Schema.define(version: 20180425131009) do ...@@ -597,6 +598,7 @@ ActiveRecord::Schema.define(version: 20180425131009) do
t.string "name" t.string "name"
t.integer "status" t.integer "status"
t.integer "lock_version" t.integer "lock_version"
t.integer "position"
end end
add_index "ci_stages", ["pipeline_id", "name"], name: "index_ci_stages_on_pipeline_id_and_name", unique: true, using: :btree add_index "ci_stages", ["pipeline_id", "name"], name: "index_ci_stages_on_pipeline_id_and_name", unique: true, using: :btree
......
...@@ -49,7 +49,7 @@ Please use the following function inside JS to render an icon : ...@@ -49,7 +49,7 @@ Please use the following function inside JS to render an icon :
All Icons and Illustrations are managed in the [gitlab-svgs](https://gitlab.com/gitlab-org/gitlab-svgs) repository which is added as a dev-dependency. All Icons and Illustrations are managed in the [gitlab-svgs](https://gitlab.com/gitlab-org/gitlab-svgs) repository which is added as a dev-dependency.
To upgrade to a new SVG Sprite version run `yarn upgrade @gitlab-org/gitlab-svgs` and then run `yarn run svg`. This task will copy the svg sprite and all illustrations in the correct folders. The updated files should be tracked in Git as those are referenced. To upgrade to a new SVG Sprite version run `yarn upgrade @gitlab-org/gitlab-svgs`.
# SVG Illustrations # SVG Illustrations
......
# Active Sessions
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17867)
> in GitLab 10.8.
GitLab lists all devices that have logged into your account. This allows you to
review the sessions and revoke any of it that you don't recognize.
## Listing all active sessions
1. On the upper right corner, click on your avatar and go to your **Settings**.
1. Navigate to the **Active Sessions** tab.
![Active sessions list](img/active_sessions_list.png)
## Revoking a session
1. Navigate to your [profile's](#profile-settings) **Settings > Active Sessions**.
1. Click on **Revoke** besides a session. The current session cannot be
revoked, as this would sign you out of GitLab.
...@@ -39,6 +39,7 @@ From there, you can: ...@@ -39,6 +39,7 @@ From there, you can:
- Manage [SSH keys](../../ssh/README.md#ssh) to access your account via SSH - Manage [SSH keys](../../ssh/README.md#ssh) to access your account via SSH
- Manage your [preferences](preferences.md#syntax-highlighting-theme) - Manage your [preferences](preferences.md#syntax-highlighting-theme)
to customize your own GitLab experience to customize your own GitLab experience
- [View your active sessions](active_sessions.md) and revoke any of them if necessary
- Access your audit log, a security log of important events involving your account - Access your audit log, a security log of important events involving your account
## Changing your username ## Changing your username
......
Feature: Project Source Markdown Render
Background:
Given I sign in as a user
And I own project "Delta"
And I visit markdown branch
# Tree README
@javascript
Scenario: Tree view should have correct links in README
Given I go directory which contains README file
And I click on a relative link in README
Then I should see the correct markdown
@javascript
Scenario: I browse files from markdown branch
Then I should see files from repository in markdown
And I should see rendered README which contains correct links
And I click on Gitlab API in README
Then I should see correct document rendered
@javascript
Scenario: I view README in markdown branch
Then I should see files from repository in markdown
And I should see rendered README which contains correct links
And I click on Rake tasks in README
Then I should see correct directory rendered
@javascript
Scenario: I view README in markdown branch to see reference links to directory
Then I should see files from repository in markdown
And I should see rendered README which contains correct links
And I click on GitLab API doc directory in README
Then I should see correct doc/api directory rendered
@javascript
Scenario: I view README in markdown branch to see reference links to file
Then I should see files from repository in markdown
And I should see rendered README which contains correct links
And I click on Maintenance in README
Then I should see correct maintenance file rendered
@javascript
Scenario: README headers should have header links
Then I should see rendered README which contains correct links
And Header "Application details" should have correct id and link
# Blob
@javascript
Scenario: I navigate to doc directory to view documentation in markdown
And I navigate to the doc/api/README
And I see correct file rendered
And I click on users in doc/api/README
Then I should see the correct document file
@javascript
Scenario: I navigate to doc directory to view user doc in markdown
And I navigate to the doc/api/README
And I see correct file rendered
And I click on raketasks in doc/api/README
Then I should see correct directory rendered
@javascript
Scenario: I navigate to doc directory to view user doc in markdown
And I navigate to the doc/api/README
And Header "GitLab API" should have correct id and link
# Markdown branch
@javascript
Scenario: I browse files from markdown branch
When I visit markdown branch
Then I should see files from repository in markdown branch
And I should see rendered README which contains correct links
And I click on Gitlab API in README
Then I should see correct document rendered for markdown branch
@javascript
Scenario: I browse directory from markdown branch
When I visit markdown branch
Then I should see files from repository in markdown branch
And I should see rendered README which contains correct links
And I click on Rake tasks in README
Then I should see correct directory rendered for markdown branch
@javascript
Scenario: I navigate to doc directory to view documentation in markdown branch
When I visit markdown branch
And I navigate to the doc/api/README
And I see correct file rendered in markdown branch
And I click on users in doc/api/README
Then I should see the users document file in markdown branch
@javascript
Scenario: I navigate to doc directory to view user doc in markdown branch
When I visit markdown branch
And I navigate to the doc/api/README
And I see correct file rendered in markdown branch
And I click on raketasks in doc/api/README
Then I should see correct directory rendered for markdown branch
@javascript
Scenario: Tree markdown links view empty urls should have correct urls
When I visit markdown branch
Then The link with text "empty" should have url "tree/markdown"
When I visit markdown branch "README.md" blob
Then The link with text "empty" should have url "blob/markdown/README.md"
When I visit markdown branch "d" tree
Then The link with text "empty" should have url "tree/markdown/d"
When I visit markdown branch "d/README.md" blob
Then The link with text "empty" should have url "blob/markdown/d/README.md"
# "ID" means "#id" on the tests below, because we are unable to escape the hash sign.
# which Spinach interprets as the start of a comment.
@javascript
Scenario: All markdown links with ids should have correct urls
When I visit markdown branch
Then The link with text "ID" should have url "tree/markdownID"
Then The link with text "/ID" should have url "tree/markdownID"
Then The link with text "README.mdID" should have url "blob/markdown/README.mdID"
Then The link with text "d/README.mdID" should have url "blob/markdown/d/README.mdID"
When I visit markdown branch "README.md" blob
Then The link with text "ID" should have url "blob/markdown/README.mdID"
Then The link with text "/ID" should have url "blob/markdown/README.mdID"
Then The link with text "README.mdID" should have url "blob/markdown/README.mdID"
Then The link with text "d/README.mdID" should have url "blob/markdown/d/README.mdID"
# Wiki
Scenario: I create a wiki page with different links
Given I go to wiki page
And I add various links to the wiki page
Then Wiki page should have added links
And I click on test link
Then I see new wiki page named test
When I go back to wiki page home
And I click on GitLab API doc link
Then I see Gitlab API document
When I go back to wiki page home
And I click on Rake tasks link
Then I see Rake tasks directory
Scenario: Wiki headers should have should have ids generated for them.
Given I go to wiki page
And I add a header to the wiki page
Then Wiki header should have correct id and link
# If you need to modify the existing seed repository for your tests,
# it is recommended that you make the changes on the `markdown` branch of the seed project repository,
# which should only be used by tests in this file. See `/spec/factories.rb#project` for more info.
class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedMarkdown
include WaitForRequests
step 'I own project "Delta"' do
@project = ::Project.find_by(name: "Delta")
@project ||= create(:project, :repository, name: "Delta", namespace: @user.namespace)
@project.add_master(@user)
end
step 'I should see files from repository in markdown' do
expect(current_path).to eq project_tree_path(@project, "markdown")
expect(page).to have_content "README.md"
expect(page).to have_content "CHANGELOG"
end
step 'I should see rendered README which contains correct links' do
expect(page).to have_content "Welcome to GitLab GitLab is a free project and repository management application"
expect(page).to have_link "GitLab API doc"
expect(page).to have_link "GitLab API website"
expect(page).to have_link "Rake tasks"
expect(page).to have_link "backup and restore procedure"
expect(page).to have_link "GitLab API doc directory"
expect(page).to have_link "Maintenance"
end
step 'I click on Gitlab API in README' do
click_link "GitLab API doc"
end
step 'I should see correct document rendered' do
expect(current_path).to eq project_blob_path(@project, "markdown/doc/api/README.md")
wait_for_requests
expect(page).to have_content "All API requests require authentication"
end
step 'I click on Rake tasks in README' do
click_link "Rake tasks"
end
step 'I should see correct directory rendered' do
expect(current_path).to eq project_tree_path(@project, "markdown/doc/raketasks")
expect(page).to have_content "backup_restore.md"
expect(page).to have_content "maintenance.md"
end
step 'I click on GitLab API doc directory in README' do
click_link "GitLab API doc directory"
end
step 'I should see correct doc/api directory rendered' do
expect(current_path).to eq project_tree_path(@project, "markdown/doc/api")
expect(page).to have_content "README.md"
expect(page).to have_content "users.md"
end
step 'I click on Maintenance in README' do
click_link "Maintenance"
end
step 'I should see correct maintenance file rendered' do
expect(current_path).to eq project_blob_path(@project, "markdown/doc/raketasks/maintenance.md")
wait_for_requests
expect(page).to have_content "bundle exec rake gitlab:env:info RAILS_ENV=production"
end
step 'I click on link "empty" in the README' do
page.within('.readme-holder') do
click_link "empty"
end
end
step 'I click on link "id" in the README' do
page.within('.readme-holder') do
click_link "#id"
end
end
step 'I navigate to the doc/api/README' do
page.within '.tree-table' do
click_link "doc"
end
page.within '.tree-table' do
click_link "api"
end
wait_for_requests
page.within '.tree-table' do
click_link "README.md"
end
end
step 'I see correct file rendered' do
expect(current_path).to eq project_blob_path(@project, "markdown/doc/api/README.md")
wait_for_requests
expect(page).to have_content "Contents"
expect(page).to have_link "Users"
expect(page).to have_link "Rake tasks"
end
step 'I click on users in doc/api/README' do
click_link "Users"
end
step 'I should see the correct document file' do
expect(current_path).to eq project_blob_path(@project, "markdown/doc/api/users.md")
expect(page).to have_content "Get a list of users."
end
step 'I click on raketasks in doc/api/README' do
click_link "Rake tasks"
end
# Markdown branch
When 'I visit markdown branch' do
visit project_tree_path(@project, "markdown")
wait_for_requests
end
When 'I visit markdown branch "README.md" blob' do
visit project_blob_path(@project, "markdown/README.md")
end
When 'I visit markdown branch "d" tree' do
visit project_tree_path(@project, "markdown/d")
end
When 'I visit markdown branch "d/README.md" blob' do
visit project_blob_path(@project, "markdown/d/README.md")
end
step 'I should see files from repository in markdown branch' do
expect(current_path).to eq project_tree_path(@project, "markdown")
expect(page).to have_content "README.md"
expect(page).to have_content "CHANGELOG"
end
step 'I see correct file rendered in markdown branch' do
expect(current_path).to eq project_blob_path(@project, "markdown/doc/api/README.md")
wait_for_requests
expect(page).to have_content "Contents"
expect(page).to have_link "Users"
expect(page).to have_link "Rake tasks"
end
step 'I should see correct document rendered for markdown branch' do
expect(current_path).to eq project_blob_path(@project, "markdown/doc/api/README.md")
wait_for_requests
expect(page).to have_content "All API requests require authentication"
end
step 'I should see correct directory rendered for markdown branch' do
expect(current_path).to eq project_tree_path(@project, "markdown/doc/raketasks")
expect(page).to have_content "backup_restore.md"
expect(page).to have_content "maintenance.md"
end
step 'I should see the users document file in markdown branch' do
expect(current_path).to eq project_blob_path(@project, "markdown/doc/api/users.md")
expect(page).to have_content "Get a list of users."
end
# Expected link contents
step 'The link with text "empty" should have url "tree/markdown"' do
wait_for_requests
find('a', text: /^empty$/)['href'] == current_host + project_tree_path(@project, "markdown")
end
step 'The link with text "empty" should have url "blob/markdown/README.md"' do
find('a', text: /^empty$/)['href'] == current_host + project_blob_path(@project, "markdown/README.md")
end
step 'The link with text "empty" should have url "tree/markdown/d"' do
find('a', text: /^empty$/)['href'] == current_host + project_tree_path(@project, "markdown/d")
end
step 'The link with text "empty" should have '\
'url "blob/markdown/d/README.md"' do
find('a', text: /^empty$/)['href'] == current_host + project_blob_path(@project, "markdown/d/README.md")
end
step 'The link with text "ID" should have url "tree/markdownID"' do
find('a', text: /^#id$/)['href'] == current_host + project_tree_path(@project, "markdown") + '#id'
end
step 'The link with text "/ID" should have url "tree/markdownID"' do
find('a', text: %r{^/#id$})['href'] == current_host + project_tree_path(@project, "markdown") + '#id'
end
step 'The link with text "README.mdID" '\
'should have url "blob/markdown/README.mdID"' do
find('a', text: /^README.md#id$/)['href'] == current_host + project_blob_path(@project, "markdown/README.md") + '#id'
end
step 'The link with text "d/README.mdID" should have '\
'url "blob/markdown/d/README.mdID"' do
find('a', text: %r{^d/README.md#id$})['href'] == current_host + project_blob_path(@project, "d/markdown/README.md") + '#id'
end
step 'The link with text "ID" should have url "blob/markdown/README.mdID"' do
wait_for_requests
find('a', text: /^#id$/)['href'] == current_host + project_blob_path(@project, "markdown/README.md") + '#id'
end
step 'The link with text "/ID" should have url "blob/markdown/README.mdID"' do
find('a', text: %r{^/#id$})['href'] == current_host + project_blob_path(@project, "markdown/README.md") + '#id'
end
# Wiki
step 'I go to wiki page' do
first(:link, "Wiki").click
expect(current_path).to eq project_wiki_path(@project, "home")
end
step 'I add various links to the wiki page' do
fill_in "wiki[content]", with: "[test](test)\n[GitLab API doc](api)\n[Rake tasks](raketasks)\n"
fill_in "wiki[message]", with: "Adding links to wiki"
page.within '.wiki-form' do
click_button "Create page"
end
end
step 'Wiki page should have added links' do
expect(current_path).to eq project_wiki_path(@project, "home")
expect(page).to have_content "test GitLab API doc Rake tasks"
end
step 'I add a header to the wiki page' do
fill_in "wiki[content]", with: "# Wiki header\n"
fill_in "wiki[message]", with: "Add header to wiki"
page.within '.wiki-form' do
click_button "Create page"
end
end
step 'Wiki header should have correct id and link' do
header_should_have_correct_id_and_link(1, 'Wiki header', 'wiki-header')
end
step 'I click on test link' do
click_link "test"
end
step 'I see new wiki page named test' do
expect(current_path).to eq project_wiki_path(@project, "test")
page.within(:css, ".nav-text") do
expect(page).to have_content "Test"
expect(page).to have_content "Create Page"
end
end
When 'I go back to wiki page home' do
visit project_wiki_path(@project, "home")
expect(current_path).to eq project_wiki_path(@project, "home")
end
step 'I click on GitLab API doc link' do
click_link "GitLab API"
end
step 'I see Gitlab API document' do
expect(current_path).to eq project_wiki_path(@project, "api")
page.within(:css, ".nav-text") do
expect(page).to have_content "Create"
expect(page).to have_content "Api"
end
end
step 'I click on Rake tasks link' do
click_link "Rake tasks"
end
step 'I see Rake tasks directory' do
expect(current_path).to eq project_wiki_path(@project, "raketasks")
page.within(:css, ".nav-text") do
expect(page).to have_content "Create"
expect(page).to have_content "Rake"
end
end
step 'I go directory which contains README file' do
visit project_tree_path(@project, "markdown/doc/api")
expect(current_path).to eq project_tree_path(@project, "markdown/doc/api")
end
step 'I click on a relative link in README' do
click_link "Users"
end
step 'I should see the correct markdown' do
expect(current_path).to eq project_blob_path(@project, "markdown/doc/api/users.md")
wait_for_requests
expect(page).to have_content "List users"
end
step 'Header "Application details" should have correct id and link' do
wait_for_requests
header_should_have_correct_id_and_link(2, 'Application details', 'application-details')
end
step 'Header "GitLab API" should have correct id and link' do
header_should_have_correct_id_and_link(1, 'GitLab API', 'gitlab-api')
end
end
module SharedMarkdown module SharedMarkdown
include Spinach::DSL include Spinach::DSL
def header_should_have_correct_id_and_link(level, text, id, parent = ".wiki")
node = find("#{parent} h#{level} a#user-content-#{id}")
expect(node[:href]).to end_with "##{id}"
# Work around a weird Capybara behavior where calling `parent` on a node
# returns the whole document, not the node's actual parent element
expect(find(:xpath, "#{node.path}/..").text).to eq text
end
step 'I should not see the Markdown preview' do step 'I should not see the Markdown preview' do
expect(find('.gfm-form .js-md-preview')).not_to be_visible expect(find('.gfm-form .js-md-preview')).not_to be_visible
end end
......
# frozen_string_literal: true
# rubocop:disable Style/Documentation
module Gitlab
module BackgroundMigration
class MigrateStageIndex
def perform(start_id, stop_id)
migrate_stage_index_sql(start_id.to_i, stop_id.to_i).tap do |sql|
ActiveRecord::Base.connection.execute(sql)
end
end
private
def migrate_stage_index_sql(start_id, stop_id)
if Gitlab::Database.postgresql?
<<~SQL
WITH freqs AS (
SELECT stage_id, stage_idx, COUNT(*) AS freq FROM ci_builds
WHERE stage_id BETWEEN #{start_id} AND #{stop_id}
AND stage_idx IS NOT NULL
GROUP BY stage_id, stage_idx
), indexes AS (
SELECT DISTINCT stage_id, last_value(stage_idx)
OVER (PARTITION BY stage_id ORDER BY freq ASC) AS index
FROM freqs
)
UPDATE ci_stages SET position = indexes.index
FROM indexes WHERE indexes.stage_id = ci_stages.id
AND ci_stages.position IS NULL;
SQL
else
<<~SQL
UPDATE ci_stages
SET position =
(SELECT stage_idx FROM ci_builds
WHERE ci_builds.stage_id = ci_stages.id
GROUP BY ci_builds.stage_idx ORDER BY COUNT(*) DESC LIMIT 1)
WHERE ci_stages.id BETWEEN #{start_id} AND #{stop_id}
AND ci_stages.position IS NULL
SQL
end
end
end
end
end
...@@ -6,7 +6,7 @@ module Gitlab ...@@ -6,7 +6,7 @@ module Gitlab
def initialize(cron, cron_timezone = 'UTC') def initialize(cron, cron_timezone = 'UTC')
@cron = cron @cron = cron
@cron_timezone = ActiveSupport::TimeZone.find_tzinfo(cron_timezone).name @cron_timezone = timezone_name(cron_timezone)
end end
def next_time_from(time) def next_time_from(time)
...@@ -24,6 +24,12 @@ module Gitlab ...@@ -24,6 +24,12 @@ module Gitlab
private private
def timezone_name(timezone)
ActiveSupport::TimeZone.find_tzinfo(timezone).name
rescue TZInfo::InvalidTimezoneIdentifier
timezone
end
# NOTE: # NOTE:
# cron_timezone can only accept timezones listed in TZInfo::Timezone. # cron_timezone can only accept timezones listed in TZInfo::Timezone.
# Aliases of Timezones from ActiveSupport::TimeZone are NOT accepted, # Aliases of Timezones from ActiveSupport::TimeZone are NOT accepted,
......
...@@ -19,6 +19,7 @@ module Gitlab ...@@ -19,6 +19,7 @@ module Gitlab
def attributes def attributes
{ name: @attributes.fetch(:name), { name: @attributes.fetch(:name),
position: @attributes.fetch(:index),
pipeline: @pipeline, pipeline: @pipeline,
project: @pipeline.project } project: @pipeline.project }
end end
......
module Gitlab
module Database
module ArelMethods
private
# In Arel 7.0.0 (Arel 7.1.4 is used in Rails 5.0) the `engine` parameter of `Arel::UpdateManager#initializer`
# was removed.
# Remove this file and inline this method when removing rails5? code.
def arel_update_manager
if Gitlab.rails5?
Arel::UpdateManager.new
else
Arel::UpdateManager.new(ActiveRecord::Base)
end
end
end
end
end
module Gitlab module Gitlab
module Database module Database
module MigrationHelpers module MigrationHelpers
include Gitlab::Database::ArelMethods
BACKGROUND_MIGRATION_BATCH_SIZE = 1000 # Number of rows to process per job BACKGROUND_MIGRATION_BATCH_SIZE = 1000 # Number of rows to process per job
BACKGROUND_MIGRATION_JOB_BUFFER_SIZE = 1000 # Number of jobs to bulk queue at a time BACKGROUND_MIGRATION_JOB_BUFFER_SIZE = 1000 # Number of jobs to bulk queue at a time
...@@ -314,7 +316,7 @@ module Gitlab ...@@ -314,7 +316,7 @@ module Gitlab
stop_arel = yield table, stop_arel if block_given? stop_arel = yield table, stop_arel if block_given?
stop_row = exec_query(stop_arel.to_sql).to_hash.first stop_row = exec_query(stop_arel.to_sql).to_hash.first
update_arel = Arel::UpdateManager.new(ActiveRecord::Base) update_arel = arel_update_manager
.table(table) .table(table)
.set([[table[column], value]]) .set([[table[column], value]])
.where(table[:id].gteq(start_id)) .where(table[:id].gteq(start_id))
......
...@@ -3,6 +3,8 @@ module Gitlab ...@@ -3,6 +3,8 @@ module Gitlab
module RenameReservedPathsMigration module RenameReservedPathsMigration
module V1 module V1
class RenameBase class RenameBase
include Gitlab::Database::ArelMethods
attr_reader :paths, :migration attr_reader :paths, :migration
delegate :update_column_in_batches, delegate :update_column_in_batches,
...@@ -62,7 +64,7 @@ module Gitlab ...@@ -62,7 +64,7 @@ module Gitlab
old_full_path, old_full_path,
new_full_path) new_full_path)
update = Arel::UpdateManager.new(ActiveRecord::Base) update = arel_update_manager
.table(routes) .table(routes)
.set([[routes[:path], replace_statement]]) .set([[routes[:path], replace_statement]])
.where(Arel::Nodes::SqlLiteral.new(filter)) .where(Arel::Nodes::SqlLiteral.new(filter))
......
...@@ -5,6 +5,8 @@ module Gitlab ...@@ -5,6 +5,8 @@ module Gitlab
module Redis module Redis
class SharedState < ::Gitlab::Redis::Wrapper class SharedState < ::Gitlab::Redis::Wrapper
SESSION_NAMESPACE = 'session:gitlab'.freeze SESSION_NAMESPACE = 'session:gitlab'.freeze
USER_SESSIONS_NAMESPACE = 'session:user:gitlab'.freeze
USER_SESSIONS_LOOKUP_NAMESPACE = 'session:lookup:user:gitlab'.freeze
DEFAULT_REDIS_SHARED_STATE_URL = 'redis://localhost:6382'.freeze DEFAULT_REDIS_SHARED_STATE_URL = 'redis://localhost:6382'.freeze
REDIS_SHARED_STATE_CONFIG_ENV_VAR_NAME = 'GITLAB_REDIS_SHARED_STATE_CONFIG_FILE'.freeze REDIS_SHARED_STATE_CONFIG_ENV_VAR_NAME = 'GITLAB_REDIS_SHARED_STATE_CONFIG_FILE'.freeze
......
...@@ -142,7 +142,7 @@ describe Projects::Clusters::GcpController do ...@@ -142,7 +142,7 @@ describe Projects::Clusters::GcpController do
context 'when google project billing is enabled' do context 'when google project billing is enabled' do
before do before do
redis_double = double redis_double = double.as_null_object
allow(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis_double) allow(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis_double)
allow(redis_double).to receive(:get).with(CheckGcpProjectBillingWorker.redis_shared_state_key_for('token')).and_return('true') allow(redis_double).to receive(:get).with(CheckGcpProjectBillingWorker.redis_shared_state_key_for('token')).and_return('true')
end end
......
...@@ -54,9 +54,9 @@ describe Projects::RawController do ...@@ -54,9 +54,9 @@ describe Projects::RawController do
end end
context 'and lfs uses object storage' do context 'and lfs uses object storage' do
let(:lfs_object) { create(:lfs_object, :with_file, oid: '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', size: '1575078') }
before do before do
lfs_object.file = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png")
lfs_object.save!
stub_lfs_object_storage stub_lfs_object_storage
lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE) lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
end end
......
...@@ -21,6 +21,7 @@ FactoryBot.define do ...@@ -21,6 +21,7 @@ FactoryBot.define do
pipeline factory: :ci_empty_pipeline pipeline factory: :ci_empty_pipeline
name 'test' name 'test'
position 1
status 'pending' status 'pending'
end end
end end
...@@ -2,6 +2,7 @@ FactoryBot.define do ...@@ -2,6 +2,7 @@ FactoryBot.define do
factory :commit_status, class: CommitStatus do factory :commit_status, class: CommitStatus do
name 'default' name 'default'
stage 'test' stage 'test'
stage_idx 0
status 'success' status 'success'
description 'commit status' description 'commit status'
pipeline factory: :ci_pipeline_with_one_job pipeline factory: :ci_pipeline_with_one_job
......
require 'rails_helper'
feature 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
let(:user) do
create(:user).tap do |user|
user.current_sign_in_at = Time.current
end
end
around do |example|
Timecop.freeze(Time.zone.parse('2018-03-12 09:06')) do
example.run
end
end
scenario 'User sees their active sessions' do
Capybara::Session.new(:session1)
Capybara::Session.new(:session2)
# note: headers can only be set on the non-js (aka. rack-test) driver
using_session :session1 do
Capybara.page.driver.header(
'User-Agent',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0'
)
gitlab_sign_in(user)
end
# set an additional session on another device
using_session :session2 do
Capybara.page.driver.header(
'User-Agent',
'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12B466 [FBDV/iPhone7,2]'
)
gitlab_sign_in(user)
end
using_session :session1 do
visit profile_active_sessions_path
expect(page).to have_content(
'127.0.0.1 ' \
'This is your current session ' \
'Firefox on Ubuntu ' \
'Signed in on 12 Mar 09:06'
)
expect(page).to have_selector '[title="Desktop"]', count: 1
expect(page).to have_content(
'127.0.0.1 ' \
'Last accessed on 12 Mar 09:06 ' \
'Mobile Safari on iOS ' \
'Signed in on 12 Mar 09:06'
)
expect(page).to have_selector '[title="Smartphone"]', count: 1
end
end
scenario 'User can revoke a session', :js, :redis_session_store do
Capybara::Session.new(:session1)
Capybara::Session.new(:session2)
# set an additional session in another browser
using_session :session2 do
gitlab_sign_in(user)
end
using_session :session1 do
gitlab_sign_in(user)
visit profile_active_sessions_path
expect(page).to have_link('Revoke', count: 1)
accept_confirm { click_on 'Revoke' }
expect(page).not_to have_link('Revoke')
end
using_session :session2 do
visit profile_active_sessions_path
expect(page).to have_content('You need to sign in or sign up before continuing.')
end
end
end
require 'spec_helper' require "spec_helper"
describe 'Projects > Files > User browses files' do describe "User browses files" do
let(:fork_message) do let(:fork_message) do
"You're not allowed to make changes to this project directly. "\ "You're not allowed to make changes to this project directly. "\
"A fork of this project has been created that you can make changes in, so you can submit a merge request." "A fork of this project has been created that you can make changes in, so you can submit a merge request."
end end
let(:project) { create(:project, :repository, name: 'Shop') } let(:project) { create(:project, :repository, name: "Shop") }
let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } let(:project2) { create(:project, :repository, name: "Another Project", path: "another-project") }
let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) }
let(:tree_path_ref_6d39438) { project_tree_path(project, '6d39438') }
let(:tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) } let(:tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) }
let(:user) { project.owner } let(:user) { project.owner }
...@@ -16,57 +14,55 @@ describe 'Projects > Files > User browses files' do ...@@ -16,57 +14,55 @@ describe 'Projects > Files > User browses files' do
sign_in(user) sign_in(user)
end end
it 'shows last commit for current directory' do it "shows last commit for current directory" do
visit(tree_path_root_ref) visit(tree_path_root_ref)
click_link 'files' click_link("files")
last_commit = project.repository.last_commit_for_path(project.default_branch, 'files') last_commit = project.repository.last_commit_for_path(project.default_branch, "files")
page.within('.blob-commit-info') do
expect(page).to have_content last_commit.short_id page.within(".blob-commit-info") do
expect(page).to have_content last_commit.author_name expect(page).to have_content(last_commit.short_id).and have_content(last_commit.author_name)
end end
end end
context 'when browsing the master branch' do context "when browsing the master branch" do
before do before do
visit(tree_path_root_ref) visit(tree_path_root_ref)
end end
it 'shows files from a repository' do it "shows files from a repository" do
expect(page).to have_content('VERSION') expect(page).to have_content("VERSION")
expect(page).to have_content('.gitignore') .and have_content(".gitignore")
expect(page).to have_content('LICENSE') .and have_content("LICENSE")
end end
it 'shows the "Browse Directory" link' do it "shows the `Browse Directory` link" do
click_link('files') click_link("files")
click_link('History') click_link("History")
expect(page).to have_link('Browse Directory') expect(page).to have_link("Browse Directory").and have_no_link("Browse Code")
expect(page).not_to have_link('Browse Code')
end end
it 'shows the "Browse File" link' do it "shows the `Browse File` link" do
page.within('.tree-table') do page.within(".tree-table") do
click_link('README.md') click_link("README.md")
end end
click_link('History')
expect(page).to have_link('Browse File') click_link("History")
expect(page).not_to have_link('Browse Files')
expect(page).to have_link("Browse File").and have_no_link("Browse Files")
end end
it 'shows the "Browse Files" link' do it "shows the `Browse Files` link" do
click_link('History') click_link("History")
expect(page).to have_link('Browse Files') expect(page).to have_link("Browse Files").and have_no_link("Browse Directory")
expect(page).not_to have_link('Browse Directory')
end end
it 'redirects to the permalink URL' do it "redirects to the permalink URL" do
click_link('.gitignore') click_link(".gitignore")
click_link('Permalink') click_link("Permalink")
permalink_path = project_blob_path(project, "#{project.repository.commit.sha}/.gitignore") permalink_path = project_blob_path(project, "#{project.repository.commit.sha}/.gitignore")
...@@ -74,80 +70,180 @@ describe 'Projects > Files > User browses files' do ...@@ -74,80 +70,180 @@ describe 'Projects > Files > User browses files' do
end end
end end
context 'when browsing a specific ref' do context "when browsing the `markdown` branch", :js do
context "when browsing the root" do
before do before do
visit(tree_path_ref_6d39438) visit(project_tree_path(project, "markdown"))
end
it "shows correct files and links" do
# rubocop:disable Lint/Void
# Test the full URLs of links instead of relative paths by `have_link(text: "...", href: "...")`.
find("a", text: /^empty$/)["href"] == project_tree_url(project, "markdown")
find("a", text: /^#id$/)["href"] == project_tree_url(project, "markdown", anchor: "#id")
find("a", text: %r{^/#id$})["href"] == project_tree_url(project, "markdown", anchor: "#id")
find("a", text: /^README.md#id$/)["href"] == project_blob_url(project, "markdown/README.md", anchor: "#id")
find("a", text: %r{^d/README.md#id$})["href"] == project_blob_url(project, "d/markdown/README.md", anchor: "#id")
# rubocop:enable Lint/Void
expect(current_path).to eq(project_tree_path(project, "markdown"))
expect(page).to have_content("README.md")
.and have_content("CHANGELOG")
.and have_content("Welcome to GitLab GitLab is a free project and repository management application")
.and have_link("GitLab API doc")
.and have_link("GitLab API website")
.and have_link("Rake tasks")
.and have_link("backup and restore procedure")
.and have_link("GitLab API doc directory")
.and have_link("Maintenance")
.and have_header_with_correct_id_and_link(2, "Application details", "application-details")
end
it "shows correct content of file" do
click_link("GitLab API doc")
expect(current_path).to eq(project_blob_path(project, "markdown/doc/api/README.md"))
expect(page).to have_content("All API requests require authentication")
.and have_content("Contents")
.and have_link("Users")
.and have_link("Rake tasks")
.and have_header_with_correct_id_and_link(1, "GitLab API", "gitlab-api")
click_link("Users")
expect(current_path).to eq(project_blob_path(project, "markdown/doc/api/users.md"))
expect(page).to have_content("Get a list of users.")
page.go_back
click_link("Rake tasks")
expect(current_path).to eq(project_tree_path(project, "markdown/doc/raketasks"))
expect(page).to have_content("backup_restore.md").and have_content("maintenance.md")
click_link("shop")
click_link("Maintenance")
expect(current_path).to eq(project_blob_path(project, "markdown/doc/raketasks/maintenance.md"))
expect(page).to have_content("bundle exec rake gitlab:env:info RAILS_ENV=production")
click_link("shop")
page.within(".tree-table") do
click_link("README.md")
end end
it 'shows files from a repository for "6d39438"' do page.go_back
expect(current_path).to eq(tree_path_ref_6d39438)
expect(page).to have_content('.gitignore') page.within(".tree-table") do
expect(page).to have_content('LICENSE') click_link("d")
end end
it 'shows files from a repository with apostroph in its name', :js do # rubocop:disable Lint/Void
first('.js-project-refs-dropdown').click # Test the full URLs of links instead of relative paths by `have_link(text: "...", href: "...")`.
find("a", text: /^empty$/)["href"] == project_tree_url(project, "markdown/d")
# rubocop:enable Lint/Void
page.within(".tree-table") do
click_link("README.md")
end
page.within('.project-refs-form') do # rubocop:disable Lint/Void
# Test the full URLs of links instead of relative paths by `have_link(text: "...", href: "...")`.
find("a", text: /^empty$/)["href"] == project_blob_url(project, "markdown/d/README.md")
# rubocop:enable Lint/Void
end
it "shows correct content of directory" do
click_link("GitLab API doc directory")
expect(current_path).to eq(project_tree_path(project, "markdown/doc/api"))
expect(page).to have_content("README.md").and have_content("users.md")
click_link("Users")
expect(current_path).to eq(project_blob_path(project, "markdown/doc/api/users.md"))
expect(page).to have_content("List users").and have_content("Get a list of users.")
end
end
end
context "when browsing a specific ref" do
let(:ref) { project_tree_path(project, "6d39438") }
before do
visit(ref)
end
it "shows files from a repository for `6d39438`" do
expect(current_path).to eq(ref)
expect(page).to have_content(".gitignore").and have_content("LICENSE")
end
it "shows files from a repository with apostroph in its name", :js do
first(".js-project-refs-dropdown").click
page.within(".project-refs-form") do
click_link("'test'") click_link("'test'")
end end
expect(page).to have_selector('.dropdown-toggle-text', text: "'test'") expect(page).to have_selector(".dropdown-toggle-text", text: "'test'")
visit(project_tree_path(project, "'test'")) visit(project_tree_path(project, "'test'"))
expect(page).to have_css('.tree-commit-link', visible: true) expect(page).to have_css(".tree-commit-link").and have_no_content("Loading commit data...")
expect(page).not_to have_content('Loading commit data...')
end end
it 'shows the code with a leading dot in the directory', :js do it "shows the code with a leading dot in the directory", :js do
first('.js-project-refs-dropdown').click first(".js-project-refs-dropdown").click
page.within('.project-refs-form') do page.within(".project-refs-form") do
click_link('fix') click_link("fix")
end end
visit(project_tree_path(project, 'fix/.testdir')) visit(project_tree_path(project, "fix/.testdir"))
expect(page).to have_css('.tree-commit-link', visible: true) expect(page).to have_css(".tree-commit-link").and have_no_content("Loading commit data...")
expect(page).not_to have_content('Loading commit data...')
end end
it 'does not show the permalink link' do it "does not show the permalink link" do
click_link('.gitignore') click_link(".gitignore")
expect(page).not_to have_link('permalink') expect(page).not_to have_link("permalink")
end end
end end
context 'when browsing a file content' do context "when browsing a file content" do
before do before do
visit(tree_path_root_ref) visit(tree_path_root_ref)
click_link('.gitignore')
click_link(".gitignore")
end end
it 'shows a file content', :js do it "shows a file content", :js do
wait_for_requests expect(page).to have_content("*.rbc")
expect(page).to have_content('*.rbc')
end end
it 'is possible to blame' do it "is possible to blame" do
click_link 'Blame' click_link("Blame")
expect(page).to have_content "*.rb" expect(page).to have_content("*.rb")
expect(page).to have_content "Dmitriy Zaporozhets" .and have_content("Dmitriy Zaporozhets")
expect(page).to have_content "Initial commit" .and have_content("Initial commit")
end end
end end
context 'when browsing a raw file' do context "when browsing a raw file" do
before do before do
visit(project_blob_path(project, File.join(RepoHelpers.sample_commit.id, RepoHelpers.sample_blob.path))) path = File.join(RepoHelpers.sample_commit.id, RepoHelpers.sample_blob.path)
visit(project_blob_path(project, path))
end end
it 'shows a raw file content' do it "shows a raw file content" do
click_link('Open raw') click_link("Open raw")
expect(source).to eq('') # Body is filled in by gitlab-workhorse
expect(source).to eq("") # Body is filled in by gitlab-workhorse
end end
end end
end end
require 'spec_helper' require "spec_helper"
describe 'User creates wiki page' do describe "User creates wiki page" do
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
...@@ -10,67 +10,104 @@ describe 'User creates wiki page' do ...@@ -10,67 +10,104 @@ describe 'User creates wiki page' do
visit(project_wikis_path(project)) visit(project_wikis_path(project))
end end
context 'when wiki is empty' do context "when wiki is empty" do
context 'in a user namespace' do context "in a user namespace" do
let(:project) { create(:project, namespace: user.namespace) } let(:project) { create(:project, namespace: user.namespace) }
it 'shows validation error message' do it "shows validation error message" do
page.within('.wiki-form') do page.within(".wiki-form") do
fill_in(:wiki_content, with: '') fill_in(:wiki_content, with: "")
click_on('Create page')
click_on("Create page")
end end
expect(page).to have_content('The form contains the following error:') expect(page).to have_content("The form contains the following error:").and have_content("Content can't be blank")
expect(page).to have_content("Content can't be blank")
page.within(".wiki-form") do
fill_in(:wiki_content, with: "[link test](test)")
page.within('.wiki-form') do click_on("Create page")
fill_in(:wiki_content, with: '[link test](test)')
click_on('Create page')
end end
expect(page).to have_content('Home') expect(page).to have_content("Home").and have_content("link test")
expect(page).to have_content('link test')
click_link('link test') click_link("link test")
expect(page).to have_content('Create Page') expect(page).to have_content("Create Page")
end end
it 'shows non-escaped link in the pages list', :js do it "shows non-escaped link in the pages list", :js do
click_link('New page') click_link("New page")
page.within('#modal-new-wiki') do page.within("#modal-new-wiki") do
fill_in(:new_wiki_path, with: 'one/two/three-test') fill_in(:new_wiki_path, with: "one/two/three-test")
click_on('Create page')
click_on("Create page")
end end
page.within('.wiki-form') do page.within(".wiki-form") do
fill_in(:wiki_content, with: 'wiki content') fill_in(:wiki_content, with: "wiki content")
click_on('Create page')
click_on("Create page")
end end
expect(current_path).to include('one/two/three-test') expect(current_path).to include("one/two/three-test")
expect(page).to have_xpath("//a[@href='/#{project.full_path}/wikis/one/two/three-test']") expect(page).to have_xpath("//a[@href='/#{project.full_path}/wikis/one/two/three-test']")
end end
it 'has "Create home" as a commit message' do it "has `Create home` as a commit message" do
expect(page).to have_field('wiki[message]', with: 'Create home') expect(page).to have_field("wiki[message]", with: "Create home")
end
it "creates a page from the home page" do
fill_in(:wiki_content, with: "[test](test)\n[GitLab API doc](api)\n[Rake tasks](raketasks)\n# Wiki header\n")
fill_in(:wiki_message, with: "Adding links to wiki")
page.within(".wiki-form") do
click_button("Create page")
end
expect(current_path).to eq(project_wiki_path(project, "home"))
expect(page).to have_content("test GitLab API doc Rake tasks Wiki header")
.and have_content("Home")
.and have_content("Last edited by #{user.name}")
.and have_header_with_correct_id_and_link(1, "Wiki header", "wiki-header")
click_link("test")
expect(current_path).to eq(project_wiki_path(project, "test"))
page.within(:css, ".nav-text") do
expect(page).to have_content("Test").and have_content("Create Page")
end end
it 'creates a page from the home page' do click_link("Home")
fill_in(:wiki_content, with: 'My awesome wiki!')
page.within('.wiki-form') do expect(current_path).to eq(project_wiki_path(project, "home"))
click_button('Create page')
click_link("GitLab API")
expect(current_path).to eq(project_wiki_path(project, "api"))
page.within(:css, ".nav-text") do
expect(page).to have_content("Create").and have_content("Api")
end end
expect(page).to have_content('Home') click_link("Home")
expect(page).to have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!') expect(current_path).to eq(project_wiki_path(project, "home"))
click_link("Rake tasks")
expect(current_path).to eq(project_wiki_path(project, "raketasks"))
page.within(:css, ".nav-text") do
expect(page).to have_content("Create").and have_content("Rake")
end
end end
it 'creates ASCII wiki with LaTeX blocks', :js do it "creates ASCII wiki with LaTeX blocks", :js do
stub_application_setting(plantuml_url: 'http://localhost', plantuml_enabled: true) stub_application_setting(plantuml_url: "http://localhost", plantuml_enabled: true)
ascii_content = <<~MD ascii_content = <<~MD
:stem: latexmath :stem: latexmath
...@@ -90,153 +127,164 @@ describe 'User creates wiki page' do ...@@ -90,153 +127,164 @@ describe 'User creates wiki page' do
stem:[2+2] is 4 stem:[2+2] is 4
MD MD
find('#wiki_format option[value=asciidoc]').select_option find("#wiki_format option[value=asciidoc]").select_option
fill_in(:wiki_content, with: ascii_content) fill_in(:wiki_content, with: ascii_content)
page.within('.wiki-form') do page.within(".wiki-form") do
click_button('Create page') click_button("Create page")
end end
page.within '.wiki' do page.within ".wiki" do
expect(page).to have_selector('.katex', count: 3) expect(page).to have_selector(".katex", count: 3).and have_content("2+2 is 4")
expect(page).to have_content('2+2 is 4')
end end
end end
end end
context 'in a group namespace', :js do context "in a group namespace", :js do
let(:project) { create(:project, namespace: create(:group, :public)) } let(:project) { create(:project, namespace: create(:group, :public)) }
it 'has "Create home" as a commit message' do it "has `Create home` as a commit message" do
expect(page).to have_field('wiki[message]', with: 'Create home') expect(page).to have_field("wiki[message]", with: "Create home")
end end
it 'creates a page from from the home page' do it "creates a page from from the home page" do
page.within('.wiki-form') do page.within(".wiki-form") do
fill_in(:wiki_content, with: 'My awesome wiki!') fill_in(:wiki_content, with: "My awesome wiki!")
click_button('Create page')
click_button("Create page")
end end
expect(page).to have_content('Home') expect(page).to have_content("Home")
expect(page).to have_content("Last edited by #{user.name}") .and have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!') .and have_content("My awesome wiki!")
end end
end end
end end
context 'when wiki is not empty', :js do context "when wiki is not empty", :js do
before do before do
create(:wiki_page, wiki: create(:project, namespace: user.namespace).wiki, attrs: { title: 'home', content: 'Home page' }) create(:wiki_page, wiki: create(:project, namespace: user.namespace).wiki, attrs: { title: "home", content: "Home page" })
end end
context 'in a user namespace' do context "in a user namespace" do
let(:project) { create(:project, namespace: user.namespace) } let(:project) { create(:project, namespace: user.namespace) }
context 'via the "new wiki page" page' do context "via the `new wiki page` page" do
it 'creates a page with a single word' do it "creates a page with a single word" do
click_link('New page') click_link("New page")
page.within('#modal-new-wiki') do page.within("#modal-new-wiki") do
fill_in(:new_wiki_path, with: 'foo') fill_in(:new_wiki_path, with: "foo")
click_button('Create page')
click_button("Create page")
end end
# Commit message field should have correct value. # Commit message field should have correct value.
expect(page).to have_field('wiki[message]', with: 'Create foo') expect(page).to have_field("wiki[message]", with: "Create foo")
page.within(".wiki-form") do
fill_in(:wiki_content, with: "My awesome wiki!")
page.within('.wiki-form') do click_button("Create page")
fill_in(:wiki_content, with: 'My awesome wiki!')
click_button('Create page')
end end
expect(page).to have_content('Foo') expect(page).to have_content("Foo")
expect(page).to have_content("Last edited by #{user.name}") .and have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!') .and have_content("My awesome wiki!")
end end
it 'creates a page with spaces in the name' do it "creates a page with spaces in the name" do
click_link('New page') click_link("New page")
page.within('#modal-new-wiki') do page.within("#modal-new-wiki") do
fill_in(:new_wiki_path, with: 'Spaces in the name') fill_in(:new_wiki_path, with: "Spaces in the name")
click_button('Create page')
click_button("Create page")
end end
# Commit message field should have correct value. # Commit message field should have correct value.
expect(page).to have_field('wiki[message]', with: 'Create spaces in the name') expect(page).to have_field("wiki[message]", with: "Create spaces in the name")
page.within(".wiki-form") do
fill_in(:wiki_content, with: "My awesome wiki!")
page.within('.wiki-form') do click_button("Create page")
fill_in(:wiki_content, with: 'My awesome wiki!')
click_button('Create page')
end end
expect(page).to have_content('Spaces in the name') expect(page).to have_content("Spaces in the name")
expect(page).to have_content("Last edited by #{user.name}") .and have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!') .and have_content("My awesome wiki!")
end end
it 'creates a page with hyphens in the name' do it "creates a page with hyphens in the name" do
click_link('New page') click_link("New page")
page.within('#modal-new-wiki') do page.within("#modal-new-wiki") do
fill_in(:new_wiki_path, with: 'hyphens-in-the-name') fill_in(:new_wiki_path, with: "hyphens-in-the-name")
click_button('Create page')
click_button("Create page")
end end
# Commit message field should have correct value. # Commit message field should have correct value.
expect(page).to have_field('wiki[message]', with: 'Create hyphens in the name') expect(page).to have_field("wiki[message]", with: "Create hyphens in the name")
page.within(".wiki-form") do
fill_in(:wiki_content, with: "My awesome wiki!")
page.within('.wiki-form') do click_button("Create page")
fill_in(:wiki_content, with: 'My awesome wiki!')
click_button('Create page')
end end
expect(page).to have_content('Hyphens in the name') expect(page).to have_content("Hyphens in the name")
expect(page).to have_content("Last edited by #{user.name}") .and have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!') .and have_content("My awesome wiki!")
end end
end end
it 'shows the autocompletion dropdown' do it "shows the autocompletion dropdown" do
click_link('New page') click_link("New page")
page.within('#modal-new-wiki') do page.within("#modal-new-wiki") do
fill_in(:new_wiki_path, with: 'test-autocomplete') fill_in(:new_wiki_path, with: "test-autocomplete")
click_button('Create page')
click_button("Create page")
end end
page.within('.wiki-form') do page.within(".wiki-form") do
find('#wiki_content').native.send_keys('') find("#wiki_content").native.send_keys("")
fill_in(:wiki_content, with: '@')
fill_in(:wiki_content, with: "@")
end end
expect(page).to have_selector('.atwho-view') expect(page).to have_selector(".atwho-view")
end end
end end
context 'in a group namespace' do context "in a group namespace" do
let(:project) { create(:project, namespace: create(:group, :public)) } let(:project) { create(:project, namespace: create(:group, :public)) }
context 'via the "new wiki page" page' do context "via the `new wiki page` page" do
it 'creates a page' do it "creates a page" do
click_link('New page') click_link("New page")
page.within('#modal-new-wiki') do page.within("#modal-new-wiki") do
fill_in(:new_wiki_path, with: 'foo') fill_in(:new_wiki_path, with: "foo")
click_button('Create page')
click_button("Create page")
end end
# Commit message field should have correct value. # Commit message field should have correct value.
expect(page).to have_field('wiki[message]', with: 'Create foo') expect(page).to have_field("wiki[message]", with: "Create foo")
page.within(".wiki-form") do
fill_in(:wiki_content, with: "My awesome wiki!")
page.within('.wiki-form') do click_button("Create page")
fill_in(:wiki_content, with: 'My awesome wiki!')
click_button('Create page')
end end
expect(page).to have_content('Foo') expect(page).to have_content("Foo")
expect(page).to have_content("Last edited by #{user.name}") .and have_content("Last edited by #{user.name}")
expect(page).to have_content('My awesome wiki!') .and have_content("My awesome wiki!")
end end
end end
end end
......
require 'spec_helper'
feature 'Active user sessions', :clean_gitlab_redis_shared_state do
scenario 'Successful login adds a new active user login' do
now = Time.zone.parse('2018-03-12 09:06')
Timecop.freeze(now) do
user = create(:user)
gitlab_sign_in(user)
expect(current_path).to eq root_path
sessions = ActiveSession.list(user)
expect(sessions.count).to eq 1
# refresh the current page updates the updated_at
Timecop.freeze(now + 1.minute) do
visit current_path
sessions = ActiveSession.list(user)
expect(sessions.first).to have_attributes(
created_at: Time.zone.parse('2018-03-12 09:06'),
updated_at: Time.zone.parse('2018-03-12 09:07')
)
end
end
end
scenario 'Successful login cleans up obsolete entries' do
user = create(:user)
Gitlab::Redis::SharedState.with do |redis|
redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d')
end
gitlab_sign_in(user)
Gitlab::Redis::SharedState.with do |redis|
expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).not_to include '59822c7d9fcdfa03725eff41782ad97d'
end
end
scenario 'Sessionless login does not clean up obsolete entries' do
user = create(:user)
personal_access_token = create(:personal_access_token, user: user)
Gitlab::Redis::SharedState.with do |redis|
redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d')
end
visit user_path(user, :atom, private_token: personal_access_token.token)
expect(page.status_code).to eq 200
Gitlab::Redis::SharedState.with do |redis|
expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to include '59822c7d9fcdfa03725eff41782ad97d'
end
end
scenario 'Logout deletes the active user login' do
user = create(:user)
gitlab_sign_in(user)
expect(current_path).to eq root_path
expect(ActiveSession.list(user).count).to eq 1
gitlab_sign_out
expect(current_path).to eq new_user_session_path
expect(ActiveSession.list(user)).to be_empty
end
end
require 'spec_helper'
describe Gitlab::BackgroundMigration::MigrateStageIndex, :migration, schema: 20180420080616 do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:pipelines) { table(:ci_pipelines) }
let(:stages) { table(:ci_stages) }
let(:jobs) { table(:ci_builds) }
before do
namespaces.create(id: 10, name: 'gitlab-org', path: 'gitlab-org')
projects.create!(id: 11, namespace_id: 10, name: 'gitlab', path: 'gitlab')
pipelines.create!(id: 12, project_id: 11, ref: 'master', sha: 'adf43c3a')
stages.create(id: 100, project_id: 11, pipeline_id: 12, name: 'build')
stages.create(id: 101, project_id: 11, pipeline_id: 12, name: 'test')
jobs.create!(id: 121, commit_id: 12, project_id: 11,
stage_idx: 2, stage_id: 100)
jobs.create!(id: 122, commit_id: 12, project_id: 11,
stage_idx: 2, stage_id: 100)
jobs.create!(id: 123, commit_id: 12, project_id: 11,
stage_idx: 10, stage_id: 100)
jobs.create!(id: 124, commit_id: 12, project_id: 11,
stage_idx: 3, stage_id: 101)
end
it 'correctly migrates stages indices' do
expect(stages.all.pluck(:position)).to all(be_nil)
described_class.new.perform(100, 101)
expect(stages.all.pluck(:position)).to eq [2, 3]
end
end
...@@ -17,7 +17,7 @@ describe Gitlab::Ci::Pipeline::Chain::Create do ...@@ -17,7 +17,7 @@ describe Gitlab::Ci::Pipeline::Chain::Create do
context 'when pipeline is ready to be saved' do context 'when pipeline is ready to be saved' do
before do before do
pipeline.stages.build(name: 'test', project: project) pipeline.stages.build(name: 'test', position: 0, project: project)
step.perform! step.perform!
end end
......
...@@ -24,7 +24,8 @@ describe Gitlab::Ci::Pipeline::Seed::Stage do ...@@ -24,7 +24,8 @@ describe Gitlab::Ci::Pipeline::Seed::Stage do
describe '#attributes' do describe '#attributes' do
it 'returns hash attributes of a stage' do it 'returns hash attributes of a stage' do
expect(subject.attributes).to be_a Hash expect(subject.attributes).to be_a Hash
expect(subject.attributes).to include(:name, :project) expect(subject.attributes)
.to include(:name, :position, :pipeline, :project)
end end
end end
......
...@@ -236,6 +236,7 @@ Ci::Stage: ...@@ -236,6 +236,7 @@ Ci::Stage:
- id - id
- name - name
- status - status
- position
- lock_version - lock_version
- project_id - project_id
- pipeline_id - pipeline_id
......
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20180420080616_schedule_stages_index_migration')
describe ScheduleStagesIndexMigration, :sidekiq, :migration do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:pipelines) { table(:ci_pipelines) }
let(:stages) { table(:ci_stages) }
before do
stub_const("#{described_class}::BATCH_SIZE", 1)
namespaces.create(id: 12, name: 'gitlab-org', path: 'gitlab-org')
projects.create!(id: 123, namespace_id: 12, name: 'gitlab', path: 'gitlab')
pipelines.create!(id: 1, project_id: 123, ref: 'master', sha: 'adf43c3a')
stages.create!(id: 121, project_id: 123, pipeline_id: 1, name: 'build')
stages.create!(id: 122, project_id: 123, pipeline_id: 1, name: 'test')
stages.create!(id: 123, project_id: 123, pipeline_id: 1, name: 'deploy')
end
it 'schedules delayed background migrations in batches' do
Sidekiq::Testing.fake! do
Timecop.freeze do
expect(stages.all).to all(have_attributes(position: be_nil))
migrate!
expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, 121, 121)
expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, 122, 122)
expect(described_class::MIGRATION).to be_scheduled_delayed_migration(15.minutes, 123, 123)
expect(BackgroundMigrationWorker.jobs.size).to eq 3
end
end
end
end
require 'rails_helper'
RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
let(:user) do
create(:user).tap do |user|
user.current_sign_in_at = Time.current
end
end
let(:session) { double(:session, id: '6919a6f1bb119dd7396fadc38fd18d0d') }
let(:request) do
double(:request, {
user_agent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_3 like Mac OS X) AppleWebKit/600.1.4 ' \
'(KHTML, like Gecko) Mobile/12B466 [FBDV/iPhone7,2]',
ip: '127.0.0.1',
session: session
})
end
describe '#current?' do
it 'returns true if the active session matches the current session' do
active_session = ActiveSession.new(session_id: '6919a6f1bb119dd7396fadc38fd18d0d')
expect(active_session.current?(session)).to be true
end
it 'returns false if the active session does not match the current session' do
active_session = ActiveSession.new(session_id: '59822c7d9fcdfa03725eff41782ad97d')
expect(active_session.current?(session)).to be false
end
it 'returns false if the session id is nil' do
active_session = ActiveSession.new(session_id: nil)
session = double(:session, id: nil)
expect(active_session.current?(session)).to be false
end
end
describe '.list' do
it 'returns all sessions by user' do
Gitlab::Redis::SharedState.with do |redis|
redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", Marshal.dump({ session_id: 'a' }))
redis.set("session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d", Marshal.dump({ session_id: 'b' }))
redis.set("session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358", '')
redis.sadd(
"session:lookup:user:gitlab:#{user.id}",
%w[
6919a6f1bb119dd7396fadc38fd18d0d
59822c7d9fcdfa03725eff41782ad97d
]
)
end
expect(ActiveSession.list(user)).to match_array [{ session_id: 'a' }, { session_id: 'b' }]
end
it 'does not return obsolete entries and cleans them up' do
Gitlab::Redis::SharedState.with do |redis|
redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", Marshal.dump({ session_id: 'a' }))
redis.sadd(
"session:lookup:user:gitlab:#{user.id}",
%w[
6919a6f1bb119dd7396fadc38fd18d0d
59822c7d9fcdfa03725eff41782ad97d
]
)
end
expect(ActiveSession.list(user)).to eq [{ session_id: 'a' }]
Gitlab::Redis::SharedState.with do |redis|
expect(redis.sscan_each("session:lookup:user:gitlab:#{user.id}").to_a).to eq ['6919a6f1bb119dd7396fadc38fd18d0d']
end
end
it 'returns an empty array if the use does not have any active session' do
expect(ActiveSession.list(user)).to eq []
end
end
describe '.set' do
it 'sets a new redis entry for the user session and a lookup entry' do
ActiveSession.set(user, request)
Gitlab::Redis::SharedState.with do |redis|
expect(redis.scan_each.to_a).to match_array [
"session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d",
"session:lookup:user:gitlab:#{user.id}"
]
end
end
it 'adds timestamps and information from the request' do
Timecop.freeze(Time.zone.parse('2018-03-12 09:06')) do
ActiveSession.set(user, request)
session = ActiveSession.list(user)
expect(session.count).to eq 1
expect(session.first).to have_attributes(
ip_address: '127.0.0.1',
browser: 'Mobile Safari',
os: 'iOS',
device_name: 'iPhone 6',
device_type: 'smartphone',
created_at: Time.zone.parse('2018-03-12 09:06'),
updated_at: Time.zone.parse('2018-03-12 09:06'),
session_id: '6919a6f1bb119dd7396fadc38fd18d0d'
)
end
end
it 'keeps the created_at from the login on consecutive requests' do
now = Time.zone.parse('2018-03-12 09:06')
Timecop.freeze(now) do
ActiveSession.set(user, request)
Timecop.freeze(now + 1.minute) do
ActiveSession.set(user, request)
session = ActiveSession.list(user)
expect(session.first).to have_attributes(
created_at: Time.zone.parse('2018-03-12 09:06'),
updated_at: Time.zone.parse('2018-03-12 09:07')
)
end
end
end
end
describe '.destroy' do
it 'removes the entry associated with the currently killed user session' do
Gitlab::Redis::SharedState.with do |redis|
redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '')
redis.set("session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d", '')
redis.set("session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358", '')
end
ActiveSession.destroy(user, request.session.id)
Gitlab::Redis::SharedState.with do |redis|
expect(redis.scan_each(match: "session:user:gitlab:*")).to match_array [
"session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d",
"session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358"
]
end
end
it 'removes the lookup entry' do
Gitlab::Redis::SharedState.with do |redis|
redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '')
redis.sadd("session:lookup:user:gitlab:#{user.id}", '6919a6f1bb119dd7396fadc38fd18d0d')
end
ActiveSession.destroy(user, request.session.id)
Gitlab::Redis::SharedState.with do |redis|
expect(redis.scan_each(match: "session:lookup:user:gitlab:#{user.id}").to_a).to be_empty
end
end
it 'removes the devise session' do
Gitlab::Redis::SharedState.with do |redis|
redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '')
redis.set("session:gitlab:6919a6f1bb119dd7396fadc38fd18d0d", '')
end
ActiveSession.destroy(user, request.session.id)
Gitlab::Redis::SharedState.with do |redis|
expect(redis.scan_each(match: "session:gitlab:*").to_a).to be_empty
end
end
it 'does not remove the devise session if the active session could not be found' do
Gitlab::Redis::SharedState.with do |redis|
redis.set("session:gitlab:6919a6f1bb119dd7396fadc38fd18d0d", '')
end
other_user = create(:user)
ActiveSession.destroy(other_user, request.session.id)
Gitlab::Redis::SharedState.with do |redis|
expect(redis.scan_each(match: "session:gitlab:*").to_a).not_to be_empty
end
end
end
describe '.cleanup' do
it 'removes obsolete lookup entries' do
Gitlab::Redis::SharedState.with do |redis|
redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '')
redis.sadd("session:lookup:user:gitlab:#{user.id}", '6919a6f1bb119dd7396fadc38fd18d0d')
redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d')
end
ActiveSession.cleanup(user)
Gitlab::Redis::SharedState.with do |redis|
expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to eq ['6919a6f1bb119dd7396fadc38fd18d0d']
end
end
it 'does not bail if there are no lookup entries' do
ActiveSession.cleanup(user)
end
end
end
...@@ -51,7 +51,7 @@ describe Ci::Stage, :models do ...@@ -51,7 +51,7 @@ describe Ci::Stage, :models do
end end
end end
describe 'update_status' do describe '#update_status' do
context 'when stage objects needs to be updated' do context 'when stage objects needs to be updated' do
before do before do
create(:ci_build, :success, stage_id: stage.id) create(:ci_build, :success, stage_id: stage.id)
...@@ -87,4 +87,36 @@ describe Ci::Stage, :models do ...@@ -87,4 +87,36 @@ describe Ci::Stage, :models do
end end
end end
end end
describe '#index' do
context 'when stage has been imported and does not have position index set' do
before do
stage.update_column(:position, nil)
end
context 'when stage has statuses' do
before do
create(:ci_build, :running, stage_id: stage.id, stage_idx: 10)
end
it 'recalculates index before updating status' do
expect(stage.reload.position).to be_nil
stage.update_status
expect(stage.reload.position).to eq 10
end
end
context 'when stage does not have statuses' do
it 'fallbacks to zero' do
expect(stage.reload.position).to be_nil
stage.update_status
expect(stage.reload.position).to eq 0
end
end
end
end
end end
...@@ -62,9 +62,7 @@ describe LfsObject do ...@@ -62,9 +62,7 @@ describe LfsObject do
.with('LfsObjectUploader', described_class.name, :file, kind_of(Numeric)) .with('LfsObjectUploader', described_class.name, :file, kind_of(Numeric))
.once .once
lfs_object = create(:lfs_object) create(:lfs_object, :with_file)
lfs_object.file = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png")
lfs_object.save!
end end
end end
end end
......
...@@ -40,7 +40,12 @@ RSpec.describe NotificationSetting do ...@@ -40,7 +40,12 @@ RSpec.describe NotificationSetting do
expect(notification_setting.new_issue).to eq(true) expect(notification_setting.new_issue).to eq(true)
expect(notification_setting.close_issue).to eq(true) expect(notification_setting.close_issue).to eq(true)
expect(notification_setting.merge_merge_request).to eq(true) expect(notification_setting.merge_merge_request).to eq(true)
expect(notification_setting.close_merge_request).to eq(false)
# In Rails 5 assigning a value which is not explicitly `true` or `false` ("nil" in this case)
# to a boolean column transforms it to `true`.
# In Rails 4 it transforms the value to `false` with deprecation warning.
# Replace `eq(Gitlab.rails5?)` with `eq(true)` when removing rails5? code.
expect(notification_setting.close_merge_request).to eq(Gitlab.rails5?)
expect(notification_setting.reopen_merge_request).to eq(false) expect(notification_setting.reopen_merge_request).to eq(false)
end end
end end
......
...@@ -1331,7 +1331,11 @@ describe API::Runner do ...@@ -1331,7 +1331,11 @@ describe API::Runner do
end end
it 'download artifacts' do it 'download artifacts' do
<<<<<<< HEAD
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
=======
expect(response).to have_http_status(200)
>>>>>>> upstream/master
expect(response.headers.to_h).to include download_headers expect(response.headers.to_h).to include download_headers
end end
end end
......
require 'spec_helper' require "spec_helper"
describe ::Applications::CreateService do describe ::Applications::CreateService do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:params) { attributes_for(:application) } let(:params) { attributes_for(:application) }
let(:request) { ActionController::TestRequest.new(remote_ip: '127.0.0.1') } let(:request) do
if Gitlab.rails5?
ActionController::TestRequest.new({ remote_ip: "127.0.0.1" }, ActionController::TestSession.new)
else
ActionController::TestRequest.new(remote_ip: "127.0.0.1")
end
end
subject { described_class.new(user, params) } subject { described_class.new(user, params) }
it 'creates an application' do it { expect { subject.execute(request) }.to change { Doorkeeper::Application.count }.by(1) }
expect { subject.execute(request) }.to change { Doorkeeper::Application.count }.by(1)
end
end end
...@@ -6,7 +6,9 @@ describe Ci::RetryBuildService do ...@@ -6,7 +6,9 @@ describe Ci::RetryBuildService do
set(:pipeline) { create(:ci_pipeline, project: project) } set(:pipeline) { create(:ci_pipeline, project: project) }
let(:stage) do let(:stage) do
Ci::Stage.create!(project: project, pipeline: pipeline, name: 'test') create(:ci_stage_entity, project: project,
pipeline: pipeline,
name: 'test')
end end
let(:build) { create(:ci_build, pipeline: pipeline, stage_id: stage.id) } let(:build) { create(:ci_build, pipeline: pipeline, stage_id: stage.id) }
......
...@@ -123,12 +123,14 @@ describe Projects::UpdatePagesService do ...@@ -123,12 +123,14 @@ describe Projects::UpdatePagesService do
expect(execute).not_to eq(:success) expect(execute).not_to eq(:success)
end end
it 'fails for empty file fails' do context 'when using empty file' do
build.job_artifacts_archive.update_attributes(file: empty_file) let(:file) { empty_file }
it 'fails to extract' do
expect { execute } expect { execute }
.to raise_error(Projects::UpdatePagesService::FailedToExtractError) .to raise_error(Projects::UpdatePagesService::FailedToExtractError)
end end
end
context 'when timeout happens by DNS error' do context 'when timeout happens by DNS error' do
before do before do
......
...@@ -46,8 +46,7 @@ describe LfsObjectUploader do ...@@ -46,8 +46,7 @@ describe LfsObjectUploader do
end end
describe 'remote file' do describe 'remote file' do
let(:remote) { described_class::Store::REMOTE } let(:lfs_object) { create(:lfs_object, :object_storage, :with_file) }
let(:lfs_object) { create(:lfs_object, file_store: remote) }
context 'with object storage enabled' do context 'with object storage enabled' do
before do before do
...@@ -57,16 +56,11 @@ describe LfsObjectUploader do ...@@ -57,16 +56,11 @@ describe LfsObjectUploader do
it 'can store file remotely' do it 'can store file remotely' do
allow(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async) allow(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async)
store_file(lfs_object) lfs_object
expect(lfs_object.file_store).to eq remote expect(lfs_object.file_store).to eq(described_class::Store::REMOTE)
expect(lfs_object.file.path).not_to be_blank expect(lfs_object.file.path).not_to be_blank
end end
end end
end end
def store_file(lfs_object)
lfs_object.file = fixture_file_upload(Rails.root.join("spec/fixtures/dk.png"), "`/png")
lfs_object.save!
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment