Commit 0be466e5 authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'ce-to-ee-2018-05-02' into 'master'

CE upstream - 2018-05-02 09:27 UTC

See merge request gitlab-org/gitlab-ee!5540
parents 3bc72dc8 ee13cad7
......@@ -194,6 +194,9 @@ gem 're2', '~> 1.1.1'
gem 'version_sorter', '~> 2.1.0'
# User agent parsing
gem 'device_detector'
# Cache
gem 'redis-rails', '~> 5.0.2'
......
......@@ -169,6 +169,7 @@ GEM
activerecord (>= 3.2.0, < 5.1)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
device_detector (1.0.0)
devise (4.2.0)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
......@@ -1056,6 +1057,7 @@ DEPENDENCIES
database_cleaner (~> 1.5.0)
deckar01-task_list (= 2.0.0)
default_value_for (~> 3.0.0)
device_detector
devise (~> 4.2)
devise-two-factor (~> 3.0.0)
diffy (~> 3.1.0)
......
......@@ -304,12 +304,12 @@ GEM
flowdock (~> 0.7)
gitlab-grit (>= 2.4.1)
multi_json
gitlab-gollum-lib (4.2.7.1)
gitlab-gollum-lib (4.2.7.2)
gemojione (~> 3.2)
github-markup (~> 1.6)
gollum-grit_adapter (~> 1.0)
nokogiri (>= 1.6.1, < 2.0)
rouge (~> 2.1)
rouge (~> 3.1)
sanitize (~> 2.1)
stringex (~> 2.6)
gitlab-gollum-rugged_adapter (0.4.4)
......@@ -602,8 +602,6 @@ GEM
atomic (>= 1.0.0)
mysql2
peek
peek-performance_bar (1.3.1)
peek (>= 0.1.0)
peek-pg (1.3.0)
concurrent-ruby
concurrent-ruby-ext
......@@ -752,7 +750,7 @@ GEM
retriable (3.1.1)
rinku (2.0.4)
rotp (2.1.2)
rouge (2.2.1)
rouge (3.1.1)
rqrcode (0.10.1)
chunky_png (~> 1.0)
rqrcode-rails3 (0.1.7)
......@@ -1134,7 +1132,6 @@ DEPENDENCIES
peek (~> 1.0.1)
peek-gc (~> 0.0.2)
peek-mysql2 (~> 1.1.0)
peek-performance_bar (~> 1.3.0)
peek-pg (~> 1.3.0)
peek-rblineprof (~> 0.2.0)
peek-redis (~> 1.2.0)
......@@ -1166,7 +1163,7 @@ DEPENDENCIES
redis-rails (~> 5.0.2)
request_store (~> 1.3)
responders (~> 2.0)
rouge (~> 2.0)
rouge (~> 3.1)
rqrcode-rails3 (~> 0.1.7)
rspec-parameterized
rspec-rails (~> 3.6.0)
......
......@@ -460,6 +460,7 @@ img.emoji {
/** COMMON CLASSES **/
.prepend-top-0 { margin-top: 0; }
.prepend-top-2 { margin-top: 2px; }
.prepend-top-5 { margin-top: 5px; }
.prepend-top-8 { margin-top: $grid-size; }
.prepend-top-10 { margin-top: 10px; }
......
......@@ -39,35 +39,10 @@
svg {
fill: currentColor;
&.s8 {
@include svg-size(8px);
}
&.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);
$svg-sizes: 8 12 16 18 24 32 48 72;
@each $svg-size in $svg-sizes {
&.s#{$svg-size} {
@include svg-size(#{$svg-size}px);
}
}
}
......@@ -114,7 +114,8 @@ class ApplicationController < ActionController::Base
def log_exception(exception)
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" }
logger.error "\n#{exception.class.name} (#{exception.message}):\n#{application_trace.join}"
end
......
......@@ -57,7 +57,7 @@ module IssuableCollections
out_of_range = @issuables.current_page > total_pages # rubocop:disable Gitlab/ModuleWithInstanceVariables
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
out_of_range
......
......@@ -34,6 +34,6 @@ class Groups::ApplicationController < ApplicationController
def build_canonical_path(group)
params[:group_id] = group.to_param
url_for(params)
url_for(safe_params)
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
params[:namespace_id] = project.namespace.to_param
params[:project_id] = project.to_param
url_for(params)
url_for(safe_params)
end
def repository
......
......@@ -77,8 +77,7 @@ class Projects::LfsStorageController < Projects::GitHttpClientController
def link_to_project!(object)
if object && !object.projects.exists?(storage_project.id)
object.projects << storage_project
object.save!
object.lfs_objects_projects.create!(project: storage_project)
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
after_save :update_project_statistics_after_save, if: :size_changed?
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_remotely, -> { where(file_store: ::JobArtifactUploader::Store::REMOTE) }
......
......@@ -548,6 +548,17 @@ module Ci
@latest_builds_with_artifacts ||= builds.latest.with_artifacts_archive.to_a
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
def ci_yaml_from_repo
......
......@@ -13,14 +13,27 @@ module Ci
has_many :statuses, class_name: 'CommitStatus', foreign_key: :stage_id
has_many :builds, foreign_key: :stage_id
validates :project, presence: true, unless: :importing?
validates :pipeline, presence: true, unless: :importing?
validates :name, presence: true, unless: :importing?
with_options unless: :importing? do
validates :project, presence: true
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?
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
event :enqueue do
transition created: :pending
......
......@@ -424,6 +424,12 @@ class Commit
# no-op but needs to be defined since #persisted? is defined
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
def work_in_progress?
......
......@@ -189,4 +189,11 @@ class CommitStatus < ActiveRecord::Base
v =~ /\d+/ ? v.to_i : v
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
......@@ -13,7 +13,7 @@ class LfsObject < ActiveRecord::Base
mount_uploader :file, LfsObjectUploader
after_save :update_file_store
after_save :update_file_store, if: :file_changed?
def update_file_store
# The file.object_store is set during `uploader.store!`
......
......@@ -928,7 +928,7 @@ class User < ActiveRecord::Base
def delete_async(deleted_by:, params: {})
block if params[:hard_delete]
DeleteUserWorker.perform_async(deleted_by.id, id, params)
DeleteUserWorker.perform_async(deleted_by.id, id, params.to_h)
end
def notification_service
......
......@@ -42,6 +42,7 @@ module Ci
def create_stage
Ci::Stage.create!(name: @build.stage,
position: @build.stage_idx,
pipeline: @build.pipeline,
project: @build.project)
end
......
......@@ -141,6 +141,17 @@
= link_to profile_preferences_path do
%strong.fly-out-top-item-name
#{ _('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
= link_to audit_log_profile_path do
.nav-icon-container
......
......@@ -3,5 +3,5 @@
#js-peek{ data: { env: Peek.env,
request_id: Peek.request_id,
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 }
- 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 @@
.files-changed-inner
.inline-parallel-buttons.hidden-xs.hidden-sm
- 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 current_controller?(:commit)
= 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,19 +15,15 @@ cookie_key = if Rails.env.development?
"_gitlab_session"
end
if Rails.env.test?
Gitlab::Application.config.session_store :cookie_store, key: "_gitlab_session"
else
sessions_config = Gitlab::Redis::SharedState.params
sessions_config[:namespace] = Gitlab::Redis::SharedState::SESSION_NAMESPACE
sessions_config = Gitlab::Redis::SharedState.params
sessions_config[:namespace] = Gitlab::Redis::SharedState::SESSION_NAMESPACE
Gitlab::Application.config.session_store(
:redis_store, # Using the cookie_store would enable session replay attacks.
servers: sessions_config,
key: cookie_key,
secure: Gitlab.config.gitlab.https,
httponly: true,
expires_in: Settings.gitlab['session_expire_delay'] * 60,
path: Rails.application.config.relative_url_root.nil? ? '/' : Gitlab::Application.config.relative_url_root
)
end
Gitlab::Application.config.session_store(
:redis_store, # Using the cookie_store would enable session replay attacks.
servers: sessions_config,
key: cookie_key,
secure: Gitlab.config.gitlab.https,
httponly: true,
expires_in: Settings.gitlab['session_expire_delay'] * 60,
path: Rails.application.config.relative_url_root.nil? ? '/' : Gitlab::Application.config.relative_url_root
)
......@@ -6,4 +6,16 @@ Rails.application.configure do |config|
Warden::Manager.before_failure do |env, opts|
Gitlab::Auth::BlockedUserTracker.log_if_user_blocked(env)
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
......@@ -30,6 +30,7 @@ resource :profile, only: [:show, :update] do
put :revoke
end
end
resources :active_sessions, only: [:index, :destroy]
resources :emails, only: [:index, :create, :destroy] do
member do
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
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", ["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", ["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
......@@ -597,6 +598,7 @@ ActiveRecord::Schema.define(version: 20180425131009) do
t.string "name"
t.integer "status"
t.integer "lock_version"
t.integer "position"
end
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 :
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
......
# 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:
- Manage [SSH keys](../../ssh/README.md#ssh) to access your account via SSH
- Manage your [preferences](preferences.md#syntax-highlighting-theme)
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
## Changing your username
......
......@@ -93,7 +93,7 @@ describe Oauth::GeoAuthController do
render_views
before do
allow(controller).to receive(:current_user) { user }
sign_in(user)
end
it 'logs out when correct access_token is informed' do
......
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
This diff is collapsed.
module SharedMarkdown
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
expect(find('.gfm-form .js-md-preview')).not_to be_visible
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
def initialize(cron, cron_timezone = 'UTC')
@cron = cron
@cron_timezone = ActiveSupport::TimeZone.find_tzinfo(cron_timezone).name
@cron_timezone = timezone_name(cron_timezone)
end
def next_time_from(time)
......@@ -24,6 +24,12 @@ module Gitlab
private
def timezone_name(timezone)
ActiveSupport::TimeZone.find_tzinfo(timezone).name
rescue TZInfo::InvalidTimezoneIdentifier
timezone
end
# NOTE:
# cron_timezone can only accept timezones listed in TZInfo::Timezone.
# Aliases of Timezones from ActiveSupport::TimeZone are NOT accepted,
......
......@@ -19,6 +19,7 @@ module Gitlab
def attributes
{ name: @attributes.fetch(:name),
position: @attributes.fetch(:index),
pipeline: @pipeline,
project: @pipeline.project }
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 Database
module MigrationHelpers
include Gitlab::Database::ArelMethods
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
......@@ -314,7 +316,7 @@ module Gitlab
stop_arel = yield table, stop_arel if block_given?
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)
.set([[table[column], value]])
.where(table[:id].gteq(start_id))
......
......@@ -3,6 +3,8 @@ module Gitlab
module RenameReservedPathsMigration
module V1
class RenameBase
include Gitlab::Database::ArelMethods
attr_reader :paths, :migration
delegate :update_column_in_batches,
......@@ -62,10 +64,10 @@ module Gitlab
old_full_path,
new_full_path)
update = Arel::UpdateManager.new(ActiveRecord::Base)
.table(routes)
.set([[routes[:path], replace_statement]])
.where(Arel::Nodes::SqlLiteral.new(filter))
update = arel_update_manager
.table(routes)
.set([[routes[:path], replace_statement]])
.where(Arel::Nodes::SqlLiteral.new(filter))
execute(update.to_sql)
end
......
......@@ -5,6 +5,8 @@ module Gitlab
module Redis
class SharedState < ::Gitlab::Redis::Wrapper
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
REDIS_SHARED_STATE_CONFIG_ENV_VAR_NAME = 'GITLAB_REDIS_SHARED_STATE_CONFIG_FILE'.freeze
......
......@@ -142,7 +142,7 @@ describe Projects::Clusters::GcpController do
context 'when google project billing is enabled' do
before do
redis_double = double
redis_double = double.as_null_object
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')
end
......
......@@ -54,9 +54,9 @@ describe Projects::RawController do
end
context 'and lfs uses object storage' do
let(:lfs_object) { create(:lfs_object, :with_file, oid: '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', size: '1575078') }
before do
lfs_object.file = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png")
lfs_object.save!
stub_lfs_object_storage
lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
end
......
......@@ -21,6 +21,7 @@ FactoryBot.define do
pipeline factory: :ci_empty_pipeline
name 'test'
position 1
status 'pending'
end
end
......@@ -2,6 +2,7 @@ FactoryBot.define do
factory :commit_status, class: CommitStatus do
name 'default'
stage 'test'
stage_idx 0
status 'success'
description 'commit status'
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'
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
context 'when pipeline is ready to be saved' do
before do
pipeline.stages.build(name: 'test', project: project)
pipeline.stages.build(name: 'test', position: 0, project: project)
step.perform!
end
......
......@@ -24,7 +24,8 @@ describe Gitlab::Ci::Pipeline::Seed::Stage do
describe '#attributes' do
it 'returns hash attributes of a stage' do
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
......
......@@ -236,6 +236,7 @@ Ci::Stage:
- id
- name
- status
- position
- lock_version
- project_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
end
end
describe 'update_status' do
describe '#update_status' do
context 'when stage objects needs to be updated' do
before do
create(:ci_build, :success, stage_id: stage.id)
......@@ -87,4 +87,36 @@ describe Ci::Stage, :models do
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
......@@ -62,9 +62,7 @@ describe LfsObject do
.with('LfsObjectUploader', described_class.name, :file, kind_of(Numeric))
.once
lfs_object = create(:lfs_object)
lfs_object.file = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png")
lfs_object.save!
create(:lfs_object, :with_file)
end
end
end
......
......@@ -40,7 +40,12 @@ RSpec.describe NotificationSetting do
expect(notification_setting.new_issue).to eq(true)
expect(notification_setting.close_issue).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)
end
end
......
require 'spec_helper'
require "spec_helper"
describe ::Applications::CreateService do
let(:user) { create(:user) }
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) }
it 'creates an application' do
expect { subject.execute(request) }.to change { Doorkeeper::Application.count }.by(1)
end
it { expect { subject.execute(request) }.to change { Doorkeeper::Application.count }.by(1) }
end
......@@ -6,7 +6,9 @@ describe Ci::RetryBuildService do
set(:pipeline) { create(:ci_pipeline, project: project) }
let(:stage) do
Ci::Stage.create!(project: project, pipeline: pipeline, name: 'test')
create(:ci_stage_entity, project: project,
pipeline: pipeline,
name: 'test')
end
let(:build) { create(:ci_build, pipeline: pipeline, stage_id: stage.id) }
......
......@@ -123,11 +123,13 @@ describe Projects::UpdatePagesService do
expect(execute).not_to eq(:success)
end
it 'fails for empty file fails' do
build.job_artifacts_archive.update_attributes(file: empty_file)
context 'when using empty file' do
let(:file) { empty_file }
expect { execute }
.to raise_error(Projects::UpdatePagesService::FailedToExtractError)
it 'fails to extract' do
expect { execute }
.to raise_error(Projects::UpdatePagesService::FailedToExtractError)
end
end
context 'when timeout happens by DNS error' do
......
......@@ -46,8 +46,7 @@ describe LfsObjectUploader do
end
describe 'remote file' do
let(:remote) { described_class::Store::REMOTE }
let(:lfs_object) { create(:lfs_object, file_store: remote) }
let(:lfs_object) { create(:lfs_object, :object_storage, :with_file) }
context 'with object storage enabled' do
before do
......@@ -57,16 +56,11 @@ describe LfsObjectUploader do
it 'can store file remotely' do
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
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
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