Commit a89cb5cb authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 0d6fa033
...@@ -3,7 +3,6 @@ image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.1 ...@@ -3,7 +3,6 @@ image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.1
stages: stages:
- sync - sync
- prepare - prepare
- quick-test
- test - test
- post-test - post-test
- review-prepare - review-prepare
......
...@@ -660,129 +660,3 @@ Style/UnneededCondition: ...@@ -660,129 +660,3 @@ Style/UnneededCondition:
# Cop supports --auto-correct. # Cop supports --auto-correct.
Style/UnneededInterpolation: Style/UnneededInterpolation:
Enabled: false Enabled: false
RSpec/ReceiveCounts:
Exclude:
- 'ee/spec/models/broadcast_message_spec.rb'
- 'ee/spec/services/geo/project_housekeeping_service_spec.rb'
- 'ee/spec/services/geo/repository_sync_service_spec.rb'
- 'spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb'
- 'spec/lib/gitlab/ci/trace/section_parser_spec.rb'
- 'spec/lib/gitlab/cleanup/project_uploads_spec.rb'
- 'spec/lib/gitlab/danger/teammate_spec.rb'
- 'spec/lib/gitlab/git_access_spec.rb'
- 'spec/lib/gitlab/gpg_spec.rb'
- 'spec/lib/gitlab/legacy_github_import/importer_spec.rb'
- 'spec/lib/gitlab/sanitizers/exif_spec.rb'
- 'spec/models/concerns/avatarable_spec.rb'
- 'spec/models/concerns/triggerable_hooks_spec.rb'
- 'spec/requests/api/graphql_spec.rb'
- 'spec/services/ci/ensure_stage_service_spec.rb'
- 'spec/services/clusters/cleanup/app_service_spec.rb'
- 'spec/services/notification_service_spec.rb'
- 'spec/services/projects/housekeeping_service_spec.rb'
- 'spec/tasks/gitlab/cleanup_rake_spec.rb'
- 'spec/uploaders/gitlab_uploader_spec.rb'
- 'spec/workers/create_gpg_signature_worker_spec.rb'
RSpec/ContextMethod:
Exclude:
- 'ee/spec/controllers/ee/projects/autocomplete_sources_controller_spec.rb'
- 'ee/spec/controllers/groups/autocomplete_sources_controller_spec.rb'
- 'ee/spec/finders/geo/file_registry_finder_spec.rb'
- 'ee/spec/helpers/feature_flags_helper_spec.rb'
- 'ee/spec/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules_spec.rb'
- 'ee/spec/lib/ee/gitlab/ci/config/entry/trigger_spec.rb'
- 'ee/spec/lib/elastic/latest/snippet_instance_proxy_spec.rb'
- 'ee/spec/lib/gitlab/contribution_analytics/data_collector_spec.rb'
- 'ee/spec/lib/gitlab/geo/replication/file_transfer_spec.rb'
- 'ee/spec/lib/gitlab/geo/replication/job_artifact_downloader_spec.rb'
- 'ee/spec/lib/gitlab/geo/replication/job_artifact_retriever_spec.rb'
- 'ee/spec/lib/gitlab/geo/replication/job_artifact_transfer_spec.rb'
- 'ee/spec/lib/gitlab/geo/replication/lfs_downloader_spec.rb'
- 'ee/spec/lib/gitlab/geo/replication/lfs_retriever_spec.rb'
- 'ee/spec/lib/gitlab/geo/replication/lfs_transfer_spec.rb'
- 'ee/spec/lib/gitlab/geo_spec.rb'
- 'ee/spec/models/approval_state_spec.rb'
- 'ee/spec/models/approval_wrapped_any_approver_rule_spec.rb'
- 'ee/spec/models/concerns/approval_rule_like_spec.rb'
- 'ee/spec/models/concerns/elastic/issue_spec.rb'
- 'ee/spec/models/concerns/elastic/note_spec.rb'
- 'ee/spec/models/concerns/elastic/project_spec.rb'
- 'ee/spec/models/concerns/epic_tree_sorting_spec.rb'
- 'ee/spec/models/concerns/has_timelogs_report_spec.rb'
- 'ee/spec/models/ee/clusters/applications/prometheus_spec.rb'
- 'ee/spec/models/ee/clusters/platforms/kubernetes_spec.rb'
- 'ee/spec/models/user_spec.rb'
- 'ee/spec/presenters/ci/pipeline_presenter_spec.rb'
- 'ee/spec/requests/api/epics_spec.rb'
- 'ee/spec/services/boards/update_service_spec.rb'
- 'ee/spec/services/ee/boards/issues/list_service_spec.rb'
- 'ee/spec/services/ee/merge_requests/refresh_service_spec.rb'
- 'ee/spec/services/groups/update_service_spec.rb'
- 'ee/spec/services/projects/create_from_template_service_spec.rb'
- 'ee/spec/services/projects/update_service_spec.rb'
- 'ee/spec/services/update_build_minutes_service_spec.rb'
- 'ee/spec/support/shared_examples/features/gold_trial_callout_shared_examples.rb'
- 'ee/spec/support/shared_examples/models/concerns/elastic/limited_indexing_shared_examples.rb'
- 'qa/spec/ee/scenario/test/integration/group_saml_spec.rb'
- 'qa/spec/resource/api_fabricator_spec.rb'
- 'qa/spec/scenario/test/integration/github_spec.rb'
- 'qa/spec/scenario/test/integration/instance_saml_spec.rb'
- 'qa/spec/scenario/test/integration/kubernetes_spec.rb'
- 'qa/spec/scenario/test/integration/ldap_spec.rb'
- 'qa/spec/scenario/test/integration/mattermost_spec.rb'
- 'qa/spec/scenario/test/integration/oauth_spec.rb'
- 'qa/spec/scenario/test/integration/object_storage_spec.rb'
- 'qa/spec/specs/runner_spec.rb'
- 'spec/controllers/application_controller_spec.rb'
- 'spec/controllers/projects/milestones_controller_spec.rb'
- 'spec/features/projects/blobs/blob_show_spec.rb'
- 'spec/finders/concerns/finder_with_cross_project_access_spec.rb'
- 'spec/helpers/diff_helper_spec.rb'
- 'spec/helpers/nav_helper_spec.rb'
- 'spec/helpers/sourcegraph_helper_spec.rb'
- 'spec/lib/container_registry/registry_spec.rb'
- 'spec/lib/container_registry/tag_spec.rb'
- 'spec/lib/gitlab/ci/trace/chunked_io_spec.rb'
- 'spec/lib/gitlab/content_security_policy/config_loader_spec.rb'
- 'spec/lib/gitlab/database/count_spec.rb'
- 'spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb'
- 'spec/lib/gitlab/git_ref_validator_spec.rb'
- 'spec/lib/gitlab/private_commit_email_spec.rb'
- 'spec/lib/gitlab/rugged_instrumentation_spec.rb'
- 'spec/lib/omni_auth/strategies/jwt_spec.rb'
- 'spec/lib/safe_zip/entry_spec.rb'
- 'spec/lib/safe_zip/extract_spec.rb'
- 'spec/models/badge_spec.rb'
- 'spec/models/badges/project_badge_spec.rb'
- 'spec/models/ci/artifact_blob_spec.rb'
- 'spec/models/ci/persistent_ref_spec.rb'
- 'spec/models/ci/runner_spec.rb'
- 'spec/models/concerns/routable_spec.rb'
- 'spec/models/deployment_spec.rb'
- 'spec/models/issue_spec.rb'
- 'spec/models/notification_recipient_spec.rb'
- 'spec/models/project_services/bamboo_service_spec.rb'
- 'spec/models/project_services/buildkite_service_spec.rb'
- 'spec/models/project_services/chat_message/issue_message_spec.rb'
- 'spec/models/project_services/drone_ci_service_spec.rb'
- 'spec/models/project_services/hipchat_service_spec.rb'
- 'spec/models/project_services/jira_service_spec.rb'
- 'spec/models/project_spec.rb'
- 'spec/models/remote_mirror_spec.rb'
- 'spec/models/user_spec.rb'
- 'spec/models/wiki_page_spec.rb'
- 'spec/requests/api/issues/get_group_issues_spec.rb'
- 'spec/requests/api/merge_requests_spec.rb'
- 'spec/serializers/diff_file_entity_spec.rb'
- 'spec/services/branches/delete_merged_service_spec.rb'
- 'spec/services/labels/available_labels_service_spec.rb'
- 'spec/services/projects/batch_open_issues_count_service_spec.rb'
- 'spec/services/projects/destroy_service_spec.rb'
- 'spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb'
- 'spec/services/projects/hashed_storage/rollback_attachments_service_spec.rb'
- 'spec/services/projects/open_issues_count_service_spec.rb'
- 'spec/services/quick_actions/interpret_service_spec.rb'
- 'spec/support/shared_examples/services/boards/issues_list_service_shared_examples.rb'
- 'spec/uploaders/namespace_file_uploader_spec.rb'
...@@ -307,7 +307,7 @@ export default { ...@@ -307,7 +307,7 @@ export default {
<a <a
v-if="titleLink" v-if="titleLink"
:href="titleLink" :href="titleLink"
target="blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="js-cluster-application-title" class="js-cluster-application-title"
>{{ title }}</a >{{ title }}</a
......
...@@ -285,7 +285,7 @@ export default { ...@@ -285,7 +285,7 @@ export default {
ref="viewButton" ref="viewButton"
v-gl-tooltip.hover v-gl-tooltip.hover
:href="diffFile.view_path" :href="diffFile.view_path"
target="blank" target="_blank"
class="view-file" class="view-file"
data-track-event="click_toggle_view_sha_button" data-track-event="click_toggle_view_sha_button"
data-track-label="diff_toggle_view_sha_button" data-track-label="diff_toggle_view_sha_button"
......
...@@ -166,7 +166,7 @@ export const setUrlFragment = (url, fragment) => { ...@@ -166,7 +166,7 @@ export const setUrlFragment = (url, fragment) => {
export function visitUrl(url, external = false) { export function visitUrl(url, external = false) {
if (external) { if (external) {
// Simulate `target="blank" rel="noopener noreferrer"` // Simulate `target="_blank" rel="noopener noreferrer"`
// See https://mathiasbynens.github.io/rel-noopener/ // See https://mathiasbynens.github.io/rel-noopener/
const otherWindow = window.open(); const otherWindow = window.open();
otherWindow.opener = null; otherWindow.opener = null;
......
...@@ -14,13 +14,24 @@ module Groups ...@@ -14,13 +14,24 @@ module Groups
track_event(:list_repositories) track_event(:list_repositories)
render json: ContainerRepositoriesSerializer serializer = ContainerRepositoriesSerializer
.new(current_user: current_user) .new(current_user: current_user)
.represent_read_only(@images)
if Feature.enabled?(:vue_container_registry_explorer)
render json: serializer.with_pagination(request, response)
.represent_read_only(@images)
else
render json: serializer.represent_read_only(@images)
end
end end
end end
end end
# The show action renders index to allow frontend routing to work on page refresh
def show
render :index
end
private private
def feature_flag_group_container_registry_browser! def feature_flag_group_container_registry_browser!
......
...@@ -14,13 +14,23 @@ module Projects ...@@ -14,13 +14,23 @@ module Projects
track_event(:list_repositories) track_event(:list_repositories)
render json: ContainerRepositoriesSerializer serializer = ContainerRepositoriesSerializer
.new(project: project, current_user: current_user) .new(project: project, current_user: current_user)
.represent(@images)
if Feature.enabled?(:vue_container_registry_explorer)
render json: serializer.with_pagination(request, response).represent(@images)
else
render json: serializer.represent(@images)
end
end end
end end
end end
# The show action renders index to allow frontend routing to work on page refresh
def show
render :index
end
def destroy def destroy
DeleteContainerRepositoryWorker.perform_async(current_user.id, image.id) DeleteContainerRepositoryWorker.perform_async(current_user.id, image.id)
track_event(:delete_repository) track_event(:delete_repository)
......
# frozen_string_literal: true
module Projects
module Prometheus
# Find Prometheus alerts by +project+, +environment+, +id+,
# or any combo thereof.
#
# Optionally filter by +metric+.
#
# Arguments:
# params:
# project: Project | integer
# environment: Environment | integer
# metric: PrometheusMetric | integer
class AlertsFinder
def initialize(params = {})
unless params[:project] || params[:environment] || params[:id]
raise ArgumentError,
'Please provide one or more of the following params: :project, :environment, :id'
end
@params = params
end
# Find all matching alerts
#
# @return [ActiveRecord::Relation<PrometheusAlert>]
def execute
relation = by_project(PrometheusAlert)
relation = by_environment(relation)
relation = by_metric(relation)
relation = by_id(relation)
relation = ordered(relation)
relation
end
private
attr_reader :params
def by_project(relation)
return relation unless params[:project]
relation.for_project(params[:project])
end
def by_environment(relation)
return relation unless params[:environment]
relation.for_environment(params[:environment])
end
def by_metric(relation)
return relation unless params[:metric]
relation.for_metric(params[:metric])
end
def by_id(relation)
return relation unless params[:id]
relation.id_in(params[:id])
end
def ordered(relation)
relation.order_by('id_asc')
end
end
end
end
# frozen_string_literal: true
# ProtectedBranchesFinder
#
# Used to filter protected branches by set of params
#
# Arguments:
# project - which project to scope to
# params:
# search: string
class ProtectedBranchesFinder
LIMIT = 100
attr_accessor :project, :params
def initialize(project, params = {})
@project = project
@params = params
end
def execute
protected_branches = project.limited_protected_branches(LIMIT)
protected_branches = by_name(protected_branches)
protected_branches
end
private
def by_name(protected_branches)
return protected_branches unless params[:search].present?
protected_branches.by_name(params[:search])
end
end
...@@ -2304,6 +2304,10 @@ class Project < ApplicationRecord ...@@ -2304,6 +2304,10 @@ class Project < ApplicationRecord
ci_config_path.blank? || ci_config_path == Gitlab::FileDetector::PATTERNS[:gitlab_ci] ci_config_path.blank? || ci_config_path == Gitlab::FileDetector::PATTERNS[:gitlab_ci]
end end
def limited_protected_branches(limit)
protected_branches.limit(limit)
end
private private
def closest_namespace_setting(name) def closest_namespace_setting(name)
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
class ProtectedBranch < ApplicationRecord class ProtectedBranch < ApplicationRecord
include ProtectedRef include ProtectedRef
include Gitlab::SQL::Pattern
scope :requiring_code_owner_approval, scope :requiring_code_owner_approval,
-> { where(code_owner_approval_required: true) } -> { where(code_owner_approval_required: true) }
...@@ -45,6 +46,12 @@ class ProtectedBranch < ApplicationRecord ...@@ -45,6 +46,12 @@ class ProtectedBranch < ApplicationRecord
# NOOP # NOOP
# #
end end
def self.by_name(query)
return none if query.blank?
where(fuzzy_arel_match(:name, query.downcase))
end
end end
ProtectedBranch.prepend_if_ee('EE::ProtectedBranch') ProtectedBranch.prepend_if_ee('EE::ProtectedBranch')
# frozen_string_literal: true
module Projects
module Prometheus
class AlertPresenter < Gitlab::View::Presenter::Delegated
RESERVED_ANNOTATIONS = %w(gitlab_incident_markdown title).freeze
GENERIC_ALERT_SUMMARY_ANNOTATIONS = %w(monitoring_tool service hosts).freeze
MARKDOWN_LINE_BREAK = " \n".freeze
def full_title
[environment_name, alert_title].compact.join(': ')
end
def project_full_path
project.full_path
end
def metric_query
gitlab_alert&.full_query
end
def environment_name
environment&.name
end
def performance_dashboard_link
if environment
metrics_project_environment_url(project, environment)
else
metrics_project_environments_url(project)
end
end
def starts_at
super&.rfc3339
end
def issue_summary_markdown
<<~MARKDOWN.chomp
#### Summary
#{metadata_list}
#{alert_details}
MARKDOWN
end
private
def alert_title
query_title || title
end
def query_title
return unless gitlab_alert
"#{gitlab_alert.title} #{gitlab_alert.computed_operator} #{gitlab_alert.threshold} for 5 minutes"
end
def metadata_list
metadata = []
metadata << list_item('Start time', starts_at) if starts_at
metadata << list_item('full_query', backtick(full_query)) if full_query
metadata << list_item(service.label.humanize, service.value) if service
metadata << list_item(monitoring_tool.label.humanize, monitoring_tool.value) if monitoring_tool
metadata << list_item(hosts.label.humanize, host_links) if hosts
metadata.join(MARKDOWN_LINE_BREAK)
end
def alert_details
if annotation_list.present?
<<~MARKDOWN.chomp
#### Alert Details
#{annotation_list}
MARKDOWN
end
end
def annotation_list
strong_memoize(:annotation_list) do
annotations
.reject { |annotation| annotation.label.in?(RESERVED_ANNOTATIONS | GENERIC_ALERT_SUMMARY_ANNOTATIONS) }
.map { |annotation| list_item(annotation.label, annotation.value) }
.join(MARKDOWN_LINE_BREAK)
end
end
def list_item(key, value)
"**#{key}:** #{value}".strip
end
def backtick(value)
"`#{value}`"
end
GENERIC_ALERT_SUMMARY_ANNOTATIONS.each do |annotation_name|
define_method(annotation_name) do
annotations.find { |a| a.label == annotation_name }
end
end
def host_links
Array(hosts.value).join(' ')
end
end
end
end
# frozen_string_literal: true # frozen_string_literal: true
class ContainerRepositoriesSerializer < BaseSerializer class ContainerRepositoriesSerializer < BaseSerializer
include WithPagination
entity ContainerRepositoryEntity entity ContainerRepositoryEntity
def represent_read_only(resource) def represent_read_only(resource)
......
...@@ -81,15 +81,17 @@ module Git ...@@ -81,15 +81,17 @@ module Git
end end
def pipeline_params def pipeline_params
{ strong_memoize(:pipeline_params) do
before: oldrev, {
after: newrev, before: oldrev,
ref: ref, after: newrev,
variables_attributes: generate_vars_from_push_options || [], ref: ref,
push_options: params[:push_options] || {}, variables_attributes: generate_vars_from_push_options || [],
checkout_sha: Gitlab::DataBuilder::Push.checkout_sha( push_options: params[:push_options] || {},
project.repository, newrev, ref) checkout_sha: Gitlab::DataBuilder::Push.checkout_sha(
} project.repository, newrev, ref)
}
end
end end
def ci_variables_from_push_options def ci_variables_from_push_options
...@@ -156,12 +158,16 @@ module Git ...@@ -156,12 +158,16 @@ module Git
project_path: project.full_path, project_path: project.full_path,
message: "Error creating pipeline", message: "Error creating pipeline",
errors: exception.to_s, errors: exception.to_s,
pipeline_params: pipeline_params pipeline_params: sanitized_pipeline_params
} }
logger.warn(data) logger.warn(data)
end end
def sanitized_pipeline_params
pipeline_params.except(:push_options)
end
def logger def logger
if Gitlab::Runtime.sidekiq? if Gitlab::Runtime.sidekiq?
Sidekiq.logger Sidekiq.logger
......
---
title: Add show routes for group and project repositories_controllers and add pagination
to the index responses
merge_request: 23151
author:
type: added
---
title: 'Add CI variables to configure bundler-audit advisory database (Dependency Scanning)'
merge_request: 23717
author:
type: added
---
title: Add search support for protected branches API
merge_request: 24137
author:
type: added
---
title: Fix inconditionally setting user profile to public when updating via
API and private_profile parameter is not present in the request
merge_request: 24456
author: Diego Louzán
type: fixed
---
title: Redact push options from error logs
merge_request: 24540
author:
type: fixed
...@@ -76,7 +76,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do ...@@ -76,7 +76,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
end end
end end
resources :container_registries, only: [:index], controller: 'registry/repositories' resources :container_registries, only: [:index, :show], controller: 'registry/repositories'
end end
scope(path: '*id', scope(path: '*id',
......
...@@ -371,7 +371,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -371,7 +371,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end end
end end
resources :container_registry, only: [:index, :destroy], resources :container_registry, only: [:index, :destroy, :show],
controller: 'registry/repositories' controller: 'registry/repositories'
namespace :registry do namespace :registry do
......
# frozen_string_literal: true
class AddIndexWebHooksOnGroupId < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :web_hooks, :group_id, where: "type = 'GroupHook'"
end
def down
remove_concurrent_index :web_hooks, :group_id, where: "type = 'GroupHook'"
end
end
...@@ -4477,6 +4477,7 @@ ActiveRecord::Schema.define(version: 2020_02_05_143231) do ...@@ -4477,6 +4477,7 @@ ActiveRecord::Schema.define(version: 2020_02_05_143231) do
t.string "encrypted_token_iv" t.string "encrypted_token_iv"
t.string "encrypted_url" t.string "encrypted_url"
t.string "encrypted_url_iv" t.string "encrypted_url_iv"
t.index ["group_id"], name: "index_web_hooks_on_group_id", where: "((type)::text = 'GroupHook'::text)"
t.index ["project_id"], name: "index_web_hooks_on_project_id" t.index ["project_id"], name: "index_web_hooks_on_project_id"
t.index ["type"], name: "index_web_hooks_on_type" t.index ["type"], name: "index_web_hooks_on_type"
end end
......
...@@ -385,7 +385,7 @@ Parameters: ...@@ -385,7 +385,7 @@ Parameters:
- `skip_confirmation` (optional) - Skip confirmation - true or false (default) - `skip_confirmation` (optional) - Skip confirmation - true or false (default)
- `external` (optional) - Flags the user as external - true or false (default) - `external` (optional) - Flags the user as external - true or false (default)
- `avatar` (optional) - Image file for user's avatar - `avatar` (optional) - Image file for user's avatar
- `private_profile` (optional) - User's profile is private - true or false (default) - `private_profile` (optional) - User's profile is private - true, false (default), or null (will be converted to false)
- `shared_runners_minutes_limit` (optional) - Pipeline minutes quota for this user **(STARTER)** - `shared_runners_minutes_limit` (optional) - Pipeline minutes quota for this user **(STARTER)**
- `extra_shared_runners_minutes_limit` (optional) - Extra pipeline minutes quota for this user **(STARTER)** - `extra_shared_runners_minutes_limit` (optional) - Extra pipeline minutes quota for this user **(STARTER)**
...@@ -423,7 +423,7 @@ Parameters: ...@@ -423,7 +423,7 @@ Parameters:
- `shared_runners_minutes_limit` (optional) - Pipeline minutes quota for this user - `shared_runners_minutes_limit` (optional) - Pipeline minutes quota for this user
- `extra_shared_runners_minutes_limit` (optional) - Extra pipeline minutes quota for this user - `extra_shared_runners_minutes_limit` (optional) - Extra pipeline minutes quota for this user
- `avatar` (optional) - Image file for user's avatar - `avatar` (optional) - Image file for user's avatar
- `private_profile` (optional) - User's profile is private - true or false (default) - `private_profile` (optional) - User's profile is private - true, false (default), or null (will be converted to false)
- `shared_runners_minutes_limit` (optional) - Pipeline minutes quota for this user **(STARTER)** - `shared_runners_minutes_limit` (optional) - Pipeline minutes quota for this user **(STARTER)**
- `extra_shared_runners_minutes_limit` (optional) - Extra pipeline minutes quota for this user **(STARTER)** - `extra_shared_runners_minutes_limit` (optional) - Extra pipeline minutes quota for this user **(STARTER)**
- `note` (optional) - Admin notes for this user **(STARTER)** - `note` (optional) - Admin notes for this user **(STARTER)**
......
...@@ -83,6 +83,7 @@ Complementary reads: ...@@ -83,6 +83,7 @@ Complementary reads:
- [Cycle Analytics development guide](cycle_analytics.md) - [Cycle Analytics development guide](cycle_analytics.md)
- [Issue types vs first-class types](issue_types.md) - [Issue types vs first-class types](issue_types.md)
- [Application limits](application_limits.md) - [Application limits](application_limits.md)
- [Redis guidelines](redis.md)
## Performance guides ## Performance guides
......
...@@ -19,9 +19,6 @@ The current stages are: ...@@ -19,9 +19,6 @@ The current stages are:
<https://gitlab.com/gitlab-org/gitlab-foss>. <https://gitlab.com/gitlab-org/gitlab-foss>.
- `prepare`: This stage includes jobs that prepare artifacts that are needed by - `prepare`: This stage includes jobs that prepare artifacts that are needed by
jobs in subsequent stages. jobs in subsequent stages.
- `quick-test`: This stage includes test jobs that should run first and fail the
pipeline early (currently used to run Geo tests when the branch name starts
with `geo-`, `geo/`, or ends with `-geo`).
- `test`: This stage includes most of the tests, DB/migration jobs, and static analysis jobs. - `test`: This stage includes most of the tests, DB/migration jobs, and static analysis jobs.
- `post-test`: This stage includes jobs that build reports or gather data from - `post-test`: This stage includes jobs that build reports or gather data from
the `test` stage's jobs (e.g. coverage, Knapsack metadata etc.). the `test` stage's jobs (e.g. coverage, Knapsack metadata etc.).
...@@ -138,54 +135,109 @@ duplicating the `if` conditions and `changes` patterns lists since they cannot b ...@@ -138,54 +135,109 @@ duplicating the `if` conditions and `changes` patterns lists since they cannot b
**If you update an `if` condition or `changes` **If you update an `if` condition or `changes`
patterns list, make sure to mass-update those across all the CI config files (i.e. `.gitlab/ci/*.yml`).** patterns list, make sure to mass-update those across all the CI config files (i.e. `.gitlab/ci/*.yml`).**
### Canonical commits only ### Canonical/security namespace merge requests only
This condition limits jobs creation to commits under the `gitlab-org/` top-level group This condition limits jobs creation to merge requests under the `gitlab-org/` top-level group
on GitLab.com only. This is similar to the `.only:variables-canonical-dot-com` CI definition: on GitLab.com only (i.e. this won't run for `master`, stable or auto-deploy branches).
This is similar to the `.only:variables-canonical-dot-com` + `only:refs: [merge_requests]`
CI definitions.
```yaml The definition for `if-canonical-dot-com-gitlab-org-groups-merge-request` can be
.if-canonical-gitlab: &if-canonical-gitlab seen in <https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/docs.gitlab-ci.yml>.
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/)/'
```
### Canonical merge requests only ### Canonical/security namespace tags only
Same as the "Canonical commits only" condition above but further limits jobs creation This condition limits jobs creation to tags under the `gitlab-org/` top-level group
to merge requests only (i.e. this won't run for `master`, stable or auto-deploy branches). on GitLab.com only.
This is similar to the `.only:variables-canonical-dot-com` + `.except:refs-master-tags-stable-deploy` This is similar to the `.only:variables-canonical-dot-com` + `only:refs: [tags]` CI definition:
CI definitions:
```yaml The definition for `if-canonical-dot-com-gitlab-org-groups-tag` can be seen in
.if-canonical-gitlab-merge-request: &if-canonical-gitlab-merge-request <https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/cng.gitlab-ci.yml>.
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/)/ && $CI_MERGE_REQUEST_IID'
``` ### Canonical namespace `master` only
This condition limits jobs creation to `master` pipelines for the `gitlab-org` top-level group
on GitLab.com only.
This is similar to the `.only:variables-canonical-dot-com` + `only:refs: [master]` CI definition:
The definition for `if-canonical-dot-com-gitlab-org-group-master-refs` can be
seen in <https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/pages.gitlab-ci.yml>.
### Canonical namespace schedules only
This condition limits jobs creation to scheduled pipelines for the `gitlab-org` top-level group
on GitLab.com only.
This is similar to the `.only:variables-canonical-dot-com` + `only:refs: [schedules]` CI definition:
The definition for `if-canonical-dot-com-gitlab-org-group-schedule` can be seen
in <https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/qa.gitlab-ci.yml>.
### Not canonical/security namespace
This condition matches if the project isn't in the canonical/security namespace.
Useful to **not** create a job if the project is a fork, or in other words, when
a job should only run in the canonical projects.
The definition for `if-not-canonical-namespace` can be seen in
<https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml>.
### Not EE
This condition matches if the project isn't EE. Useful to **not** create a job if
the project is GitLab, or in other words, when a job should only run in the GitLab
FOSS project.
The definition for `if-not-ee` can be seen in
<https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml>.
### Default refs only
This condition is the equivalent of `.default-only`.
The definition for `if-default-refs` can be seen in
<https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml>.
### `master` refs only
This condition is the equivalent of `only:refs: [master]`.
The definition for `if-master-refs` can be seen in
<https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml>.
### Code changes patterns ### Code changes patterns
Similar patterns as for `.only:changes-code`: Similar patterns as for `.only:changes-code`:
```yaml The definition for `code-patterns` can be seen in
.code-patterns: &code-patterns <https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/qa.gitlab-ci.yml>.
- ...
```
### QA changes patterns ### QA changes patterns
Similar patterns as for `.only:changes-qa`: Similar patterns as for `.only:changes-qa`:
```yaml The definition for `qa-patterns` can be seen in
.qa-patterns: &qa-patterns <https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/qa.gitlab-ci.yml>.
- ...
``` ### Docs changes patterns
Similar patterns as for `.only:changes-docs`:
The definition for `docs-patterns` can be seen in
<https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/docs.gitlab-ci.yml>.
### Code and QA changes patterns ### Code and QA changes patterns
Similar patterns as for `.only:changes-code-qa`: Similar patterns as for `.only:changes-code-qa`:
```yaml The definition for `code-qa-patterns` can be seen in
.code-qa-patterns: &code-qa-patterns <https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/review.gitlab-ci.yml>.
- ...
``` ### Code, backstage and QA changes patterns
Similar patterns as for `.only:changes-code-backstage-qa`:
The definition for `code-backstage-qa-patterns` can be seen in
<https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml>.
## Directed acyclic graph ## Directed acyclic graph
...@@ -195,21 +247,21 @@ execute jobs out of order for the following jobs: ...@@ -195,21 +247,21 @@ execute jobs out of order for the following jobs:
```mermaid ```mermaid
graph RL; graph RL;
A[setup-test-env]; A[setup-test-env];
B["gitlab:assets:compile pull-push-cache<br/>(master only)"]; B["gitlab:assets:compile pull-push-cache<br/>(canonical master only)"];
C[gitlab:assets:compile pull-cache]; C["gitlab:assets:compile pull-cache<br/>(canonical default refs only)"];
D["cache gems<br/>(master and tags only)"]; D["cache gems<br/>(master and tags only)"];
E[review-build-cng]; E[review-build-cng];
F[build-qa-image]; F[build-qa-image];
G[review-deploy]; G[review-deploy];
G2["schedule:review-deploy<br/>(master only)"]; G2["schedule:review-deploy<br/>(master only)"];
H[karma]; I["karma, jest, webpack-dev-server, static-analysis"];
I[jest]; I2["karma-foss, jest-foss<br/>(EE default refs only)"];
J["compile-assets pull-push-cache<br/>(master only)"]; J["compile-assets pull-push-cache<br/>(master only)"];
J2["compile-assets pull-push-cache foss<br/>(EE master only)"];
K[compile-assets pull-cache]; K[compile-assets pull-cache];
L[webpack-dev-server]; K2["compile-assets pull-cache foss<br/>(EE default refs only)"];
M[coverage]; M[coverage];
N[pages]; N["pages (master only)"];
O[static-analysis];
Q[package-and-qa]; Q[package-and-qa];
S["RSpec<br/>(e.g. rspec unit pg9)"] S["RSpec<br/>(e.g. rspec unit pg9)"]
T[retrieve-tests-metadata]; T[retrieve-tests-metadata];
...@@ -220,58 +272,55 @@ subgraph "`prepare` stage" ...@@ -220,58 +272,55 @@ subgraph "`prepare` stage"
C C
F F
K K
K2
J J
J2
T T
end end
subgraph "`test` stage" subgraph "`test` stage"
D --> |needs| A; D -.-> |needs| A;
H -.-> |needs and depends on| A;
H -.-> |needs and depends on| K;
I -.-> |needs and depends on| A; I -.-> |needs and depends on| A;
I -.-> |needs and depends on| K; I -.-> |needs and depends on| K;
I2 -.-> |needs and depends on| A;
I2 -.-> |needs and depends on| K;
L -.-> |needs and depends on| A; L -.-> |needs and depends on| A;
L -.-> |needs and depends on| K;
O -.-> |needs and depends on| A;
O -.-> |needs and depends on| K;
S -.-> |needs and depends on| A; S -.-> |needs and depends on| A;
S -.-> |needs and depends on| K; S -.-> |needs and depends on| K;
S -.-> |needs and depends on| T; S -.-> |needs and depends on| T;
downtime_check --> |needs and depends on| A; L["db:*, gitlab:setup, graphql-docs-verify, downtime_check"] -.-> |needs| A;
db:* --> |needs| A; end
gitlab:setup --> |needs| A;
downtime_check --> |needs and depends on| A; subgraph "`post-test` stage"
graphql-docs-verify --> |needs| A; M --> |happens after| S
end end
subgraph "`review-prepare` stage" subgraph "`review-prepare` stage"
E --> |needs| C; E -.-> |needs| C;
X["schedule:review-build-cng<br/>(master schedule only)"] --> |needs| C; E2["schedule:review-build-cng<br/>(master schedule only)"] -.-> |needs| C;
end end
subgraph "`review` stage" subgraph "`review` stage"
G G --> |happens after| E
G2 G2 --> |happens after| E2
end end
subgraph "`qa` stage" subgraph "`qa` stage"
Q --> |needs| C; Q -.-> |needs| C;
Q --> |needs| F; Q -.-> |needs| F;
review-qa-smoke -.-> |needs and depends on| G; QA1["review-qa-smoke, review-qa-all, review-performance, dast"] -.-> |needs and depends on| G;
review-qa-all -.-> |needs and depends on| G; QA2["schedule:review-performance<br/>(master only)"] -.-> |needs and depends on| G2;
review-performance -.-> |needs and depends on| G;
X2["schedule:review-performance<br/>(master only)"] -.-> |needs and depends on| G2;
dast -.-> |needs and depends on| G;
end end
subgraph "`post-test` stage" subgraph "`post-qa` stage"
M PQA1["parallel-spec-reports"] -.-> |depends on `review-qa-all`| QA1;
end end
subgraph "`pages` stage" subgraph "`pages` stage"
N -.-> |depends on| C; N -.-> |depends on| C;
N -.-> |depends on| H; N -.-> |depends on karma| I;
N -.-> |depends on| M; N -.-> |depends on| M;
N --> |happens after| PQA1
end end
``` ```
......
# Redis guidelines
GitLab uses [Redis](https://redis.io) for three distinct purposes:
- Caching via `Rails.cache`.
- As a job processing queue with [Sidekiq](sidekiq_style_guide.md).
- To manage the shared application state.
Every application process is configured to use the same Redis servers, so they
can be used for inter-process communication in cases where [PostgreSQL](sql.md)
is less appropriate, for example, transient state or data that is written much
more often than it is read.
If [Geo](geo.md) is enabled, each Geo node gets its own, independent Redis
database.
## Key naming
Redis is a flat namespace with no hierarchy, which means we must pay attention
to key names to avoid collisions. Typically we use colon-separated elements to
provide a semblence of structure at application level. An example might be
`projects:1:somekey`.
Although we split our Redis usage into three separate purposes, and those may
map to separate Redis servers in a [Highly Available](../administration/high_availability/redis.md)
configuration, the default Omnibus and GDK setups share a single Redis server.
This means that keys should **always** be globally unique across the three
purposes.
It is usually better to use immutable identifiers - project ID rather than
full path, for instance - in Redis key names. If full path is used, the key will
stop being consulted if the project is renamed. If the contents of the key are
invalidated by a name change, it is better to include a hook that will expire
the entry, instead of relying on the key changing.
We don't use [Redis Cluster](https://redis.io/topics/cluster-tutorial) at the
moment, but may wish to in the future: [#118820](https://gitlab.com/gitlab-org/gitlab/issues/118820).
This imposes an additional constraint on naming: where GitLab is performing
operations that require several keys to be held on the same Redis server - for
instance, diffing two sets held in Redis - the keys should ensure that by
enclosing the changeable parts in curly braces, such as, `project:{1}:set_a` and
`project:{1}:set_b`.
...@@ -19,10 +19,15 @@ module API ...@@ -19,10 +19,15 @@ module API
end end
params do params do
use :pagination use :pagination
optional :search, type: String, desc: 'Search for a protected branch by name'
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
get ':id/protected_branches' do get ':id/protected_branches' do
protected_branches = user_project.protected_branches.preload(:push_access_levels, :merge_access_levels) protected_branches =
ProtectedBranchesFinder
.new(user_project, params)
.execute
.preload(:push_access_levels, :merge_access_levels)
present paginate(protected_branches), with: Entities::ProtectedBranch, project: user_project present paginate(protected_branches), with: Entities::ProtectedBranch, project: user_project
end end
......
...@@ -52,7 +52,7 @@ module API ...@@ -52,7 +52,7 @@ module API
optional :external, type: Boolean, desc: 'Flag indicating the user is an external user' optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
# TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960 # TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960
optional :avatar, type: File, desc: 'Avatar image for user' # rubocop:disable Scalability/FileUploads optional :avatar, type: File, desc: 'Avatar image for user' # rubocop:disable Scalability/FileUploads
optional :private_profile, type: Boolean, default: false, desc: 'Flag indicating the user has a private profile' optional :private_profile, type: Boolean, desc: 'Flag indicating the user has a private profile'
all_or_none_of :extern_uid, :provider all_or_none_of :extern_uid, :provider
use :optional_params_ee use :optional_params_ee
......
# frozen_string_literal: true
module Gitlab
module Alerting
class Alert
include ActiveModel::Model
include Gitlab::Utils::StrongMemoize
include Presentable
attr_accessor :project, :payload
def gitlab_alert
strong_memoize(:gitlab_alert) do
parse_gitlab_alert_from_payload
end
end
def metric_id
strong_memoize(:metric_id) do
payload&.dig('labels', 'gitlab_alert_id')
end
end
def title
strong_memoize(:title) do
gitlab_alert&.title || parse_title_from_payload
end
end
def description
strong_memoize(:description) do
parse_description_from_payload
end
end
def environment
strong_memoize(:environment) do
gitlab_alert&.environment || parse_environment_from_payload
end
end
def annotations
strong_memoize(:annotations) do
parse_annotations_from_payload || []
end
end
def starts_at
strong_memoize(:starts_at) do
parse_datetime_from_payload('startsAt')
end
end
def starts_at_raw
strong_memoize(:starts_at_raw) do
payload&.dig('startsAt')
end
end
def ends_at
strong_memoize(:ends_at) do
parse_datetime_from_payload('endsAt')
end
end
def full_query
strong_memoize(:full_query) do
gitlab_alert&.full_query || parse_expr_from_payload
end
end
def alert_markdown
strong_memoize(:alert_markdown) do
parse_alert_markdown_from_payload
end
end
def status
strong_memoize(:status) do
payload&.dig('status')
end
end
def firing?
status == 'firing'
end
def resolved?
status == 'resolved'
end
def gitlab_managed?
metric_id.present?
end
def valid?
payload.respond_to?(:dig) && project && title && starts_at
end
def present
super(presenter_class: Projects::Prometheus::AlertPresenter)
end
private
def parse_environment_from_payload
environment_name = payload&.dig('labels', 'gitlab_environment_name')
return unless environment_name
EnvironmentsFinder.new(project, nil, { name: environment_name })
.find
&.first
end
def parse_gitlab_alert_from_payload
return unless metric_id
Projects::Prometheus::AlertsFinder
.new(project: project, metric: metric_id)
.execute
.first
end
def parse_title_from_payload
payload&.dig('annotations', 'title') ||
payload&.dig('annotations', 'summary') ||
payload&.dig('labels', 'alertname')
end
def parse_description_from_payload
payload&.dig('annotations', 'description')
end
def parse_annotations_from_payload
payload&.dig('annotations')&.map do |label, value|
Alerting::AlertAnnotation.new(label: label, value: value)
end
end
def parse_datetime_from_payload(field)
value = payload&.dig(field)
return unless value
Time.rfc3339(value)
rescue ArgumentError
end
# Parses `g0.expr` from `generatorURL`.
#
# Example: http://localhost:9090/graph?g0.expr=vector%281%29&g0.tab=1
def parse_expr_from_payload
url = payload&.dig('generatorURL')
return unless url
uri = URI(url)
Rack::Utils.parse_query(uri.query).fetch('g0.expr')
rescue URI::InvalidURIError, KeyError
end
def parse_alert_markdown_from_payload
payload&.dig('annotations', 'gitlab_incident_markdown')
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Alerting
class AlertAnnotation
include ActiveModel::Model
attr_accessor :label, :value
end
end
end
...@@ -57,6 +57,8 @@ dependency_scanning: ...@@ -57,6 +57,8 @@ dependency_scanning:
PIP_REQUIREMENTS_FILE \ PIP_REQUIREMENTS_FILE \
MAVEN_CLI_OPTS \ MAVEN_CLI_OPTS \
BUNDLER_AUDIT_UPDATE_DISABLED \ BUNDLER_AUDIT_UPDATE_DISABLED \
BUNDLER_AUDIT_ADVISORY_DB_URL \
BUNDLER_AUDIT_ADVISORY_DB_REF_NAME \
) \ ) \
--volume "$PWD:/code" \ --volume "$PWD:/code" \
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module QA module QA
context 'Plan', :reliable do context 'Plan', :reliable do
describe 'check xss occurence in @mentions in issues', :requires_admin do describe 'check xss occurence in @mentions in issues', :requires_admin do
it 'user mentions a user in comment' do it 'mentions a user in a comment' do
QA::Runtime::Env.personal_access_token = QA::Runtime::Env.admin_personal_access_token QA::Runtime::Env.personal_access_token = QA::Runtime::Env.admin_personal_access_token
unless QA::Runtime::Env.personal_access_token unless QA::Runtime::Env.personal_access_token
......
...@@ -18,7 +18,7 @@ module QA ...@@ -18,7 +18,7 @@ module QA
push_commit('Initial commit') push_commit('Initial commit')
end end
it 'user closes an issue by pushing commit' do it 'closes an issue by pushing a commit' do
push_commit("Closes ##{issue_id}", false) push_commit("Closes ##{issue_id}", false)
issue.visit! issue.visit!
......
...@@ -17,7 +17,7 @@ module QA ...@@ -17,7 +17,7 @@ module QA
end end
end end
it 'user collapses and expands reply for comments in an issue' do it 'collapses and expands reply for comments in an issue' do
Page::Project::Issue::Show.perform do |show| Page::Project::Issue::Show.perform do |show|
one_reply = "1 reply" one_reply = "1 reply"
......
...@@ -9,7 +9,7 @@ module QA ...@@ -9,7 +9,7 @@ module QA
Resource::Issue.fabricate_via_api!.visit! Resource::Issue.fabricate_via_api!.visit!
end end
it 'user comments on an issue and edits the comment' do it 'comments on an issue and edits the comment' do
Page::Project::Issue::Show.perform do |show| Page::Project::Issue::Show.perform do |show|
first_version_of_comment = 'First version of the comment' first_version_of_comment = 'First version of the comment'
second_version_of_comment = 'Second version of the comment' second_version_of_comment = 'Second version of the comment'
......
...@@ -7,7 +7,7 @@ module QA ...@@ -7,7 +7,7 @@ module QA
Flow::Login.sign_in Flow::Login.sign_in
end end
it 'user creates an issue' do it 'creates an issue' do
issue = Resource::Issue.fabricate_via_browser_ui! issue = Resource::Issue.fabricate_via_browser_ui!
Page::Project::Menu.perform(&:click_issues) Page::Project::Menu.perform(&:click_issues)
...@@ -27,7 +27,7 @@ module QA ...@@ -27,7 +27,7 @@ module QA
Resource::Issue.fabricate_via_api!.visit! Resource::Issue.fabricate_via_api!.visit!
end end
it 'user comments on an issue with an attachment' do it 'comments on an issue with an attachment' do
Page::Project::Issue::Show.perform do |show| Page::Project::Issue::Show.perform do |show|
show.comment('See attached banana for scale', attachment: file_to_attach) show.comment('See attached banana for scale', attachment: file_to_attach)
......
...@@ -9,7 +9,7 @@ module QA ...@@ -9,7 +9,7 @@ module QA
Resource::Issue.fabricate_via_api!.visit! Resource::Issue.fabricate_via_api!.visit!
end end
it 'user filters comments and activities in an issue' do it 'filters comments and activities in an issue' do
Page::Project::Issue::Show.perform do |show| Page::Project::Issue::Show.perform do |show|
my_own_comment = "My own comment" my_own_comment = "My own comment"
made_the_issue_confidential = "made the issue confidential" made_the_issue_confidential = "made the issue confidential"
......
...@@ -13,7 +13,7 @@ module QA ...@@ -13,7 +13,7 @@ module QA
end.project.visit! end.project.visit!
end end
it 'user sees issue suggestions when creating a new issue' do it 'shows issue suggestions when creating a new issue' do
Page::Project::Show.perform(&:go_to_new_issue) Page::Project::Show.perform(&:go_to_new_issue)
Page::Project::Issue::New.perform do |new_page| Page::Project::Issue::New.perform do |new_page|
new_page.add_title("issue") new_page.add_title("issue")
......
...@@ -20,7 +20,7 @@ module QA ...@@ -20,7 +20,7 @@ module QA
end.visit! end.visit!
end end
it 'user mentions another user in an issue' do it 'mentions another user in an issue' do
Page::Project::Issue::Show.perform do |show| Page::Project::Issue::Show.perform do |show|
at_username = "@#{@user.username}" at_username = "@#{@user.username}"
......
...@@ -120,7 +120,7 @@ describe QA::Resource::ApiFabricator do ...@@ -120,7 +120,7 @@ describe QA::Resource::ApiFabricator do
end end
end end
context '#transform_api_resource' do describe '#transform_api_resource' do
let(:resource) do let(:resource) do
Class.new do Class.new do
def self.name def self.name
......
# frozen_string_literal: true # frozen_string_literal: true
describe QA::Scenario::Test::Integration::Github do describe QA::Scenario::Test::Integration::Github do
context '#perform' do describe '#perform' do
let(:env) { spy('Runtime::Env') } let(:env) { spy('Runtime::Env') }
before do before do
......
# frozen_string_literal: true # frozen_string_literal: true
describe QA::Scenario::Test::Integration::InstanceSAML do describe QA::Scenario::Test::Integration::InstanceSAML do
context '#perform' do describe '#perform' do
it_behaves_like 'a QA scenario class' do it_behaves_like 'a QA scenario class' do
let(:tags) { [:instance_saml] } let(:tags) { [:instance_saml] }
end end
......
# frozen_string_literal: true # frozen_string_literal: true
describe QA::Scenario::Test::Integration::Kubernetes do describe QA::Scenario::Test::Integration::Kubernetes do
context '#perform' do describe '#perform' do
it_behaves_like 'a QA scenario class' do it_behaves_like 'a QA scenario class' do
let(:tags) { [:kubernetes] } let(:tags) { [:kubernetes] }
end end
......
# frozen_string_literal: true # frozen_string_literal: true
describe QA::Scenario::Test::Integration::LDAPNoTLS do describe QA::Scenario::Test::Integration::LDAPNoTLS do
context '#perform' do describe '#perform' do
it_behaves_like 'a QA scenario class' do it_behaves_like 'a QA scenario class' do
let(:tags) { [:ldap_no_tls] } let(:tags) { [:ldap_no_tls] }
end end
...@@ -9,7 +9,7 @@ describe QA::Scenario::Test::Integration::LDAPNoTLS do ...@@ -9,7 +9,7 @@ describe QA::Scenario::Test::Integration::LDAPNoTLS do
end end
describe QA::Scenario::Test::Integration::LDAPNoServer do describe QA::Scenario::Test::Integration::LDAPNoServer do
context '#perform' do describe '#perform' do
it_behaves_like 'a QA scenario class' do it_behaves_like 'a QA scenario class' do
let(:tags) { [:ldap_no_server] } let(:tags) { [:ldap_no_server] }
end end
...@@ -17,7 +17,7 @@ describe QA::Scenario::Test::Integration::LDAPNoServer do ...@@ -17,7 +17,7 @@ describe QA::Scenario::Test::Integration::LDAPNoServer do
end end
describe QA::Scenario::Test::Integration::LDAPTLS do describe QA::Scenario::Test::Integration::LDAPTLS do
context '#perform' do describe '#perform' do
it_behaves_like 'a QA scenario class' do it_behaves_like 'a QA scenario class' do
let(:tags) { [:ldap_tls] } let(:tags) { [:ldap_tls] }
end end
......
# frozen_string_literal: true # frozen_string_literal: true
describe QA::Scenario::Test::Integration::Mattermost do describe QA::Scenario::Test::Integration::Mattermost do
context '#perform' do describe '#perform' do
it_behaves_like 'a QA scenario class' do it_behaves_like 'a QA scenario class' do
let(:args) { %w[gitlab_address mattermost_address] } let(:args) { %w[gitlab_address mattermost_address] }
let(:args) do let(:args) do
......
# frozen_string_literal: true # frozen_string_literal: true
describe QA::Scenario::Test::Integration::OAuth do describe QA::Scenario::Test::Integration::OAuth do
context '#perform' do describe '#perform' do
it_behaves_like 'a QA scenario class' do it_behaves_like 'a QA scenario class' do
let(:tags) { [:oauth] } let(:tags) { [:oauth] }
end end
......
# frozen_string_literal: true # frozen_string_literal: true
describe QA::Scenario::Test::Integration::ObjectStorage do describe QA::Scenario::Test::Integration::ObjectStorage do
context '#perform' do describe '#perform' do
it_behaves_like 'a QA scenario class' do it_behaves_like 'a QA scenario class' do
let(:tags) { [:object_storage] } let(:tags) { [:object_storage] }
end end
......
...@@ -11,7 +11,7 @@ describe QA::Specs::Runner do ...@@ -11,7 +11,7 @@ describe QA::Specs::Runner do
end end
end end
context '#perform' do describe '#perform' do
before do before do
allow(QA::Runtime::Browser).to receive(:configure!) allow(QA::Runtime::Browser).to receive(:configure!)
end end
......
...@@ -896,7 +896,7 @@ describe ApplicationController do ...@@ -896,7 +896,7 @@ describe ApplicationController do
end end
end end
context '#set_current_context' do describe '#set_current_context' do
controller(described_class) do controller(described_class) do
def index def index
Labkit::Context.with_context do |context| Labkit::Context.with_context do |context|
......
...@@ -14,7 +14,7 @@ describe Groups::Registry::RepositoriesController do ...@@ -14,7 +14,7 @@ describe Groups::Registry::RepositoriesController do
sign_in(user) sign_in(user)
end end
context 'GET #index' do shared_examples 'renders a list of repositories' do
context 'when container registry is enabled' do context 'when container registry is enabled' do
it 'show index page' do it 'show index page' do
expect(Gitlab::Tracking).not_to receive(:event) expect(Gitlab::Tracking).not_to receive(:event)
...@@ -33,6 +33,7 @@ describe Groups::Registry::RepositoriesController do ...@@ -33,6 +33,7 @@ describe Groups::Registry::RepositoriesController do
} }
expect(response).to match_response_schema('registry/repositories') expect(response).to match_response_schema('registry/repositories')
expect(response).to include_pagination_headers
end end
it 'returns a list of projects for json format' do it 'returns a list of projects for json format' do
...@@ -89,5 +90,29 @@ describe Groups::Registry::RepositoriesController do ...@@ -89,5 +90,29 @@ describe Groups::Registry::RepositoriesController do
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
end end
context 'with :vue_container_registry_explorer feature flag disabled' do
before do
stub_feature_flags(vue_container_registry_explorer: false)
end
it 'has the correct response schema' do
get :index, params: {
group_id: group,
format: :json
}
expect(response).to match_response_schema('registry/repositories')
expect(response).not_to include_pagination_headers
end
end
end
context 'GET #index' do
it_behaves_like 'renders a list of repositories'
end
context 'GET #show' do
it_behaves_like 'renders a list of repositories'
end end
end end
...@@ -245,7 +245,7 @@ describe Projects::MilestonesController do ...@@ -245,7 +245,7 @@ describe Projects::MilestonesController do
end end
end end
context '#participants' do describe '#participants' do
render_views render_views
context "when guest user" do context "when guest user" do
......
...@@ -16,7 +16,7 @@ describe Projects::Registry::RepositoriesController do ...@@ -16,7 +16,7 @@ describe Projects::Registry::RepositoriesController do
project.add_developer(user) project.add_developer(user)
end end
describe 'GET index' do shared_examples 'renders a list of repositories' do
context 'when root container repository exists' do context 'when root container repository exists' do
before do before do
create(:container_repository, :root, project: project) create(:container_repository, :root, project: project)
...@@ -58,6 +58,7 @@ describe Projects::Registry::RepositoriesController do ...@@ -58,6 +58,7 @@ describe Projects::Registry::RepositoriesController do
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('registry/repositories') expect(response).to match_response_schema('registry/repositories')
expect(response).to include_pagination_headers
end end
end end
...@@ -84,9 +85,33 @@ describe Projects::Registry::RepositoriesController do ...@@ -84,9 +85,33 @@ describe Projects::Registry::RepositoriesController do
end end
end end
end end
context 'with :vue_container_registry_explorer feature flag disabled' do
before do
stub_feature_flags(vue_container_registry_explorer: false)
stub_container_registry_tags(repository: project.full_path,
tags: %w[rc1 latest])
end
it 'json has a list of projects' do
go_to_index(format: :json)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('registry/repositories')
expect(response).not_to include_pagination_headers
end
end
end
describe 'GET #index' do
it_behaves_like 'renders a list of repositories'
end
describe 'GET #show' do
it_behaves_like 'renders a list of repositories'
end end
describe 'DELETE destroy' do describe 'DELETE #destroy' do
context 'when root container repository exists' do context 'when root container repository exists' do
let!(:repository) do let!(:repository) do
create(:container_repository, :root, project: project) create(:container_repository, :root, project: project)
...@@ -115,7 +140,7 @@ describe Projects::Registry::RepositoriesController do ...@@ -115,7 +140,7 @@ describe Projects::Registry::RepositoriesController do
end end
context 'when user does not have access to registry' do context 'when user does not have access to registry' do
describe 'GET index' do describe 'GET #index' do
it 'responds with 404' do it 'responds with 404' do
go_to_index go_to_index
......
# frozen_string_literal: true
FactoryBot.define do
factory :alerting_alert, class: 'Gitlab::Alerting::Alert' do
project
payload { {} }
transient do
metric_id { nil }
after(:build) do |alert, evaluator|
unless alert.payload.key?('startsAt')
alert.payload['startsAt'] = Time.now.rfc3339
end
if metric_id = evaluator.metric_id
alert.payload['labels'] ||= {}
alert.payload['labels']['gitlab_alert_id'] = metric_id.to_s
end
end
end
skip_create
end
end
...@@ -453,7 +453,7 @@ describe 'File blob', :js do ...@@ -453,7 +453,7 @@ describe 'File blob', :js do
end end
end end
context '.gitlab-ci.yml' do describe '.gitlab-ci.yml' do
before do before do
project.add_maintainer(project.creator) project.add_maintainer(project.creator)
...@@ -481,7 +481,7 @@ describe 'File blob', :js do ...@@ -481,7 +481,7 @@ describe 'File blob', :js do
end end
end end
context '.gitlab/route-map.yml' do describe '.gitlab/route-map.yml' do
before do before do
project.add_maintainer(project.creator) project.add_maintainer(project.creator)
......
...@@ -128,7 +128,7 @@ describe FinderWithCrossProjectAccess do ...@@ -128,7 +128,7 @@ describe FinderWithCrossProjectAccess do
end end
end end
context '.finder_model' do describe '.finder_model' do
it 'is set correctly' do it 'is set correctly' do
expect(finder_class.finder_model).to eq(Project) expect(finder_class.finder_model).to eq(Project)
end end
......
# frozen_string_literal: true
require 'spec_helper'
describe Projects::Prometheus::AlertsFinder do
let(:finder) { described_class.new(params) }
let(:params) { {} }
describe 'with params' do
let_it_be(:project) { create(:project) }
let_it_be(:other_project) { create(:project) }
let_it_be(:other_env) { create(:environment, project: other_project) }
let_it_be(:production) { create(:environment, project: project) }
let_it_be(:staging) { create(:environment, project: project) }
let_it_be(:alert) { create_alert(project, production) }
let_it_be(:alert2) { create_alert(project, production) }
let_it_be(:stg_alert) { create_alert(project, staging) }
let_it_be(:other_alert) { create_alert(other_project, other_env) }
describe '#execute' do
subject { finder.execute }
context 'with project' do
before do
params[:project] = project
end
it { is_expected.to eq([alert, alert2, stg_alert]) }
context 'with matching metric' do
before do
params[:metric] = alert.prometheus_metric
end
it { is_expected.to eq([alert]) }
end
context 'with matching metric id' do
before do
params[:metric] = alert.prometheus_metric_id
end
it { is_expected.to eq([alert]) }
end
context 'with project non-specific metric' do
before do
params[:metric] = other_alert.prometheus_metric
end
it { is_expected.to be_empty }
end
end
context 'with environment' do
before do
params[:environment] = production
end
it { is_expected.to eq([alert, alert2]) }
context 'with matching metric' do
before do
params[:metric] = alert.prometheus_metric
end
it { is_expected.to eq([alert]) }
end
context 'with environment non-specific metric' do
before do
params[:metric] = stg_alert.prometheus_metric
end
it { is_expected.to be_empty }
end
end
context 'with matching project and environment' do
before do
params[:project] = project
params[:environment] = production
end
it { is_expected.to eq([alert, alert2]) }
context 'with matching metric' do
before do
params[:metric] = alert.prometheus_metric
end
it { is_expected.to eq([alert]) }
end
context 'with environment non-specific metric' do
before do
params[:metric] = stg_alert.prometheus_metric
end
it { is_expected.to be_empty }
end
context 'with matching id' do
before do
params[:id] = alert.id
end
it { is_expected.to eq([alert]) }
end
context 'with a nil id' do
before do
params[:id] = nil
end
it { is_expected.to eq([alert, alert2]) }
end
end
context 'with non-matching project-environment pair' do
before do
params[:project] = project
params[:environment] = other_env
end
it { is_expected.to be_empty }
end
context 'with id' do
before do
params[:id] = alert.id
end
it { is_expected.to eq([alert]) }
end
context 'with multiple ids' do
before do
params[:id] = [alert.id, other_alert.id]
end
it { is_expected.to eq([alert, other_alert]) }
end
context 'with non-matching id' do
before do
params[:id] = -5
end
it { is_expected.to be_empty }
end
end
private
def create_alert(project, environment)
create(:prometheus_alert, project: project, environment: environment)
end
end
describe 'without params' do
subject { finder }
it 'raises an error' do
expect { subject }
.to raise_error(ArgumentError, 'Please provide one or more of the following params: :project, :environment, :id')
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe ProtectedBranchesFinder do
let(:project) { create(:project) }
let!(:protected_branch) { create(:protected_branch, project: project) }
let!(:another_protected_branch) { create(:protected_branch, project: project) }
let!(:other_protected_branch) { create(:protected_branch) }
let(:params) { {} }
describe '#execute' do
subject { described_class.new(project, params).execute }
it 'returns all protected branches of project by default' do
expect(subject).to match_array([protected_branch, another_protected_branch])
end
context 'when search param is present' do
let(:params) { { search: protected_branch.name } }
it 'filters by search param' do
expect(subject).to eq([protected_branch])
end
end
context 'when there are more protected branches than the limit' do
before do
stub_const("#{described_class}::LIMIT", 1)
end
it 'returns limited protected branches of project' do
expect(subject).to eq([another_protected_branch])
end
end
end
end
import { shallowMount } from '@vue/test-utils';
import ExternalUrlComp from '~/environments/components/environment_external_url.vue';
describe('External URL Component', () => {
let wrapper;
const externalUrl = 'https://gitlab.com';
beforeEach(() => {
wrapper = shallowMount(ExternalUrlComp, { propsData: { externalUrl } });
});
it('should link to the provided externalUrl prop', () => {
expect(wrapper.attributes('href')).toEqual(externalUrl);
expect(wrapper.find('a').exists()).toBe(true);
});
});
...@@ -258,7 +258,7 @@ describe DiffHelper do ...@@ -258,7 +258,7 @@ describe DiffHelper do
end end
end end
context '#render_overflow_warning?' do describe '#render_overflow_warning?' do
let(:diffs_collection) { instance_double(Gitlab::Diff::FileCollection::MergeRequestDiff, raw_diff_files: diff_files) } let(:diffs_collection) { instance_double(Gitlab::Diff::FileCollection::MergeRequestDiff, raw_diff_files: diff_files) }
let(:diff_files) { Gitlab::Git::DiffCollection.new(files) } let(:diff_files) { Gitlab::Git::DiffCollection.new(files) }
let(:safe_file) { { too_large: false, diff: '' } } let(:safe_file) { { too_large: false, diff: '' } }
...@@ -303,7 +303,7 @@ describe DiffHelper do ...@@ -303,7 +303,7 @@ describe DiffHelper do
end end
end end
context '#diff_file_path_text' do describe '#diff_file_path_text' do
it 'returns full path by default' do it 'returns full path by default' do
expect(diff_file_path_text(diff_file)).to eq(diff_file.new_path) expect(diff_file_path_text(diff_file)).to eq(diff_file.new_path)
end end
......
...@@ -106,13 +106,13 @@ describe NavHelper, :do_not_mock_admin_mode do ...@@ -106,13 +106,13 @@ describe NavHelper, :do_not_mock_admin_mode do
end end
end end
context '.admin_monitoring_nav_links' do describe '.admin_monitoring_nav_links' do
subject { helper.admin_monitoring_nav_links } subject { helper.admin_monitoring_nav_links }
it { is_expected.to all(be_a(String)) } it { is_expected.to all(be_a(String)) }
end end
context '.group_issues_sub_menu_items' do describe '.group_issues_sub_menu_items' do
subject { helper.group_issues_sub_menu_items } subject { helper.group_issues_sub_menu_items }
it { is_expected.to all(be_a(String)) } it { is_expected.to all(be_a(String)) }
......
...@@ -34,7 +34,7 @@ describe SourcegraphHelper do ...@@ -34,7 +34,7 @@ describe SourcegraphHelper do
end end
end end
context '#sourcegraph_experimental_message' do describe '#sourcegraph_experimental_message' do
let(:feature_conditional) { false } let(:feature_conditional) { false }
let(:public_only) { false } let(:public_only) { false }
......
import Vue from 'vue';
import externalUrlComp from '~/environments/components/environment_external_url.vue';
describe('External URL Component', () => {
let ExternalUrlComponent;
beforeEach(() => {
ExternalUrlComponent = Vue.extend(externalUrlComp);
});
it('should link to the provided externalUrl prop', () => {
const externalURL = 'https://gitlab.com';
const component = new ExternalUrlComponent({
propsData: {
externalUrl: externalURL,
},
}).$mount();
expect(component.$el.getAttribute('href')).toEqual(externalURL);
expect(component.$el.querySelector('fa-external-link')).toBeDefined();
});
});
...@@ -14,7 +14,7 @@ describe ContainerRegistry::Registry do ...@@ -14,7 +14,7 @@ describe ContainerRegistry::Registry do
it { expect(subject).not_to be_nil } it { expect(subject).not_to be_nil }
context '#path' do describe '#path' do
subject { registry.path } subject { registry.path }
context 'path from URL' do context 'path from URL' do
......
...@@ -70,26 +70,26 @@ describe ContainerRegistry::Tag do ...@@ -70,26 +70,26 @@ describe ContainerRegistry::Tag do
headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v1+prettyjws' }) headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v1+prettyjws' })
end end
context '#layers' do describe '#layers' do
subject { tag.layers } subject { tag.layers }
it { expect(subject.length).to eq(1) } it { expect(subject.length).to eq(1) }
end end
context '#total_size' do describe '#total_size' do
subject { tag.total_size } subject { tag.total_size }
it { is_expected.to be_nil } it { is_expected.to be_nil }
end end
context 'config processing' do context 'config processing' do
context '#config' do describe '#config' do
subject { tag.config } subject { tag.config }
it { is_expected.to be_nil } it { is_expected.to be_nil }
end end
context '#created_at' do describe '#created_at' do
subject { tag.created_at } subject { tag.created_at }
it { is_expected.to be_nil } it { is_expected.to be_nil }
...@@ -113,7 +113,7 @@ describe ContainerRegistry::Tag do ...@@ -113,7 +113,7 @@ describe ContainerRegistry::Tag do
body: File.read(Rails.root + 'spec/fixtures/container_registry/config_blob_helm.json')) body: File.read(Rails.root + 'spec/fixtures/container_registry/config_blob_helm.json'))
end end
context '#created_at' do describe '#created_at' do
subject { tag.created_at } subject { tag.created_at }
it { is_expected.to be_nil } it { is_expected.to be_nil }
...@@ -130,13 +130,13 @@ describe ContainerRegistry::Tag do ...@@ -130,13 +130,13 @@ describe ContainerRegistry::Tag do
headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v2+json' }) headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v2+json' })
end end
context '#layers' do describe '#layers' do
subject { tag.layers } subject { tag.layers }
it { expect(subject.length).to eq(1) } it { expect(subject.length).to eq(1) }
end end
context '#total_size' do describe '#total_size' do
subject { tag.total_size } subject { tag.total_size }
it { is_expected.to eq(2319870) } it { is_expected.to eq(2319870) }
...@@ -144,13 +144,13 @@ describe ContainerRegistry::Tag do ...@@ -144,13 +144,13 @@ describe ContainerRegistry::Tag do
context 'config processing' do context 'config processing' do
shared_examples 'a processable' do shared_examples 'a processable' do
context '#config' do describe '#config' do
subject { tag.config } subject { tag.config }
it { is_expected.not_to be_nil } it { is_expected.not_to be_nil }
end end
context '#created_at' do describe '#created_at' do
subject { tag.created_at } subject { tag.created_at }
it { is_expected.not_to be_nil } it { is_expected.not_to be_nil }
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Alerting::Alert do
let_it_be(:project) { create(:project) }
let(:alert) { build(:alerting_alert, project: project, payload: payload) }
let(:payload) { {} }
shared_context 'gitlab alert' do
let(:gitlab_alert_id) { gitlab_alert.prometheus_metric_id.to_s }
let!(:gitlab_alert) { create(:prometheus_alert, project: project) }
before do
payload['labels'] = { 'gitlab_alert_id' => gitlab_alert_id }
end
end
shared_examples 'invalid alert' do
it 'is invalid' do
expect(alert).not_to be_valid
end
end
shared_examples 'parse payload' do |*pairs|
context 'without payload' do
it { is_expected.to be_nil }
end
pairs.each do |pair|
context "with #{pair}" do
let(:value) { 'some value' }
before do
section, name = pair.split('/')
payload[section] = { name => value }
end
it { is_expected.to eq(value) }
end
end
end
describe '#gitlab_alert' do
subject { alert.gitlab_alert }
context 'without payload' do
it { is_expected.to be_nil }
end
context 'with gitlab alert' do
include_context 'gitlab alert'
it { is_expected.to eq(gitlab_alert) }
end
context 'with unknown gitlab alert' do
include_context 'gitlab alert' do
let(:gitlab_alert_id) { 'unknown' }
end
it { is_expected.to be_nil }
end
end
describe '#title' do
subject { alert.title }
it_behaves_like 'parse payload',
'annotations/title',
'annotations/summary',
'labels/alertname'
context 'with gitlab alert' do
include_context 'gitlab alert'
context 'with annotations/title' do
let(:value) { 'annotation title' }
before do
payload['annotations'] = { 'title' => value }
end
it { is_expected.to eq(gitlab_alert.title) }
end
end
end
describe '#description' do
subject { alert.description }
it_behaves_like 'parse payload', 'annotations/description'
end
describe '#annotations' do
subject { alert.annotations }
context 'without payload' do
it { is_expected.to eq([]) }
end
context 'with payload' do
before do
payload['annotations'] = { 'foo' => 'value1', 'bar' => 'value2' }
end
it 'parses annotations' do
expect(subject.size).to eq(2)
expect(subject.map(&:label)).to eq(%w[foo bar])
expect(subject.map(&:value)).to eq(%w[value1 value2])
end
end
end
describe '#environment' do
subject { alert.environment }
context 'without gitlab_alert' do
it { is_expected.to be_nil }
end
context 'with gitlab alert' do
include_context 'gitlab alert'
it { is_expected.to eq(gitlab_alert.environment) }
end
end
describe '#starts_at' do
subject { alert.starts_at }
context 'with empty startsAt' do
before do
payload['startsAt'] = nil
end
it { is_expected.to be_nil }
end
context 'with invalid startsAt' do
before do
payload['startsAt'] = 'invalid'
end
it { is_expected.to be_nil }
end
context 'with payload' do
let(:time) { Time.now.change(usec: 0) }
before do
payload['startsAt'] = time.rfc3339
end
it { is_expected.to eq(time) }
end
end
describe '#full_query' do
using RSpec::Parameterized::TableSyntax
subject { alert.full_query }
where(:generator_url, :expected_query) do
nil | nil
'http://localhost' | nil
'invalid url' | nil
'http://localhost:9090/graph?g1.expr=vector%281%29' | nil
'http://localhost:9090/graph?g0.expr=vector%281%29' | 'vector(1)'
end
with_them do
before do
payload['generatorURL'] = generator_url
end
it { is_expected.to eq(expected_query) }
end
context 'with gitlab alert' do
include_context 'gitlab alert'
before do
payload['generatorURL'] = 'http://localhost:9090/graph?g0.expr=vector%281%29'
end
it { is_expected.to eq(gitlab_alert.full_query) }
end
end
describe '#alert_markdown' do
subject { alert.alert_markdown }
it_behaves_like 'parse payload', 'annotations/gitlab_incident_markdown'
end
describe '#valid?' do
before do
payload.update(
'annotations' => { 'title' => 'some title' },
'startsAt' => Time.now.rfc3339
)
end
subject { alert }
it { is_expected.to be_valid }
context 'without project' do
# Redefine to prevent:
# project is a NilClass - rspec-set works with ActiveRecord models only
let(:alert) { build(:alerting_alert, project: nil, payload: payload) }
it { is_expected.not_to be_valid }
end
context 'without starts_at' do
before do
payload['startsAt'] = nil
end
it { is_expected.not_to be_valid }
end
end
end
...@@ -18,7 +18,7 @@ describe Gitlab::Ci::Build::Rules::Rule::Clause::Exists do ...@@ -18,7 +18,7 @@ describe Gitlab::Ci::Build::Rules::Rule::Clause::Exists do
before do before do
stub_const('Gitlab::Ci::Build::Rules::Rule::Clause::Exists::MAX_PATTERN_COMPARISONS', 2) stub_const('Gitlab::Ci::Build::Rules::Rule::Clause::Exists::MAX_PATTERN_COMPARISONS', 2)
expect(File).to receive(:fnmatch?).exactly(2).times.and_call_original expect(File).to receive(:fnmatch?).twice.and_call_original
end end
it { is_expected.to be_truthy } it { is_expected.to be_truthy }
......
...@@ -12,7 +12,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do ...@@ -12,7 +12,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
stub_feature_flags(ci_enable_live_trace: true) stub_feature_flags(ci_enable_live_trace: true)
end end
context "#initialize" do describe "#initialize" do
context 'when a chunk exists' do context 'when a chunk exists' do
before do before do
build.trace.set('ABC') build.trace.set('ABC')
...@@ -35,7 +35,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do ...@@ -35,7 +35,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
end end
end end
context "#seek" do describe "#seek" do
subject { chunked_io.seek(pos, where) } subject { chunked_io.seek(pos, where) }
before do before do
...@@ -66,7 +66,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do ...@@ -66,7 +66,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
end end
end end
context "#eof?" do describe "#eof?" do
subject { chunked_io.eof? } subject { chunked_io.eof? }
before do before do
...@@ -90,7 +90,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do ...@@ -90,7 +90,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
end end
end end
context "#each_line" do describe "#each_line" do
let(:string_io) { StringIO.new(sample_trace_raw) } let(:string_io) { StringIO.new(sample_trace_raw) }
context 'when buffer size is smaller than file size' do context 'when buffer size is smaller than file size' do
...@@ -134,7 +134,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do ...@@ -134,7 +134,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
end end
end end
context "#read" do describe "#read" do
subject { chunked_io.read(length) } subject { chunked_io.read(length) }
context 'when read the whole size' do context 'when read the whole size' do
...@@ -254,7 +254,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do ...@@ -254,7 +254,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
end end
end end
context "#readline" do describe "#readline" do
subject { chunked_io.readline } subject { chunked_io.readline }
let(:string_io) { StringIO.new(sample_trace_raw) } let(:string_io) { StringIO.new(sample_trace_raw) }
...@@ -334,7 +334,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do ...@@ -334,7 +334,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
end end
end end
context "#write" do describe "#write" do
subject { chunked_io.write(data) } subject { chunked_io.write(data) }
let(:data) { sample_trace_raw } let(:data) { sample_trace_raw }
...@@ -399,7 +399,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do ...@@ -399,7 +399,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
end end
end end
context "#truncate" do describe "#truncate" do
let(:offset) { 10 } let(:offset) { 10 }
context 'when data does not exist' do context 'when data does not exist' do
...@@ -432,7 +432,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do ...@@ -432,7 +432,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
end end
end end
context "#destroy!" do describe "#destroy!" do
subject { chunked_io.destroy! } subject { chunked_io.destroy! }
before do before do
......
...@@ -74,7 +74,7 @@ describe Gitlab::Ci::Trace::SectionParser do ...@@ -74,7 +74,7 @@ describe Gitlab::Ci::Trace::SectionParser do
let(:lines) { build_lines(trace) } let(:lines) { build_lines(trace) }
it 'must handle correctly byte positioning' do it 'must handle correctly byte positioning' do
expect(subject).to receive(:find_next_marker).exactly(2).times.and_call_original expect(subject).to receive(:find_next_marker).twice.and_call_original
subject.parse! subject.parse!
......
...@@ -8,8 +8,8 @@ describe Gitlab::Cleanup::ProjectUploads do ...@@ -8,8 +8,8 @@ describe Gitlab::Cleanup::ProjectUploads do
let(:logger) { double(:logger) } let(:logger) { double(:logger) }
before do before do
allow(logger).to receive(:info).at_least(1).times allow(logger).to receive(:info).at_least(:once)
allow(logger).to receive(:debug).at_least(1).times allow(logger).to receive(:debug).at_least(:once)
end end
describe '#run!' do describe '#run!' do
......
...@@ -19,7 +19,7 @@ describe Gitlab::ContentSecurityPolicy::ConfigLoader do ...@@ -19,7 +19,7 @@ describe Gitlab::ContentSecurityPolicy::ConfigLoader do
} }
end end
context '.default_settings_hash' do describe '.default_settings_hash' do
it 'returns empty defaults' do it 'returns empty defaults' do
settings = described_class.default_settings_hash settings = described_class.default_settings_hash
...@@ -33,7 +33,7 @@ describe Gitlab::ContentSecurityPolicy::ConfigLoader do ...@@ -33,7 +33,7 @@ describe Gitlab::ContentSecurityPolicy::ConfigLoader do
end end
end end
context '#load' do describe '#load' do
subject { described_class.new(csp_config[:directives]) } subject { described_class.new(csp_config[:directives]) }
def expected_config(directive) def expected_config(directive)
......
...@@ -176,7 +176,7 @@ describe Gitlab::Danger::Teammate do ...@@ -176,7 +176,7 @@ describe Gitlab::Danger::Teammate do
it 'returns true if request fails' do it 'returns true if request fails' do
expect(Gitlab::Danger::RequestHelper).to receive(:http_get_json) expect(Gitlab::Danger::RequestHelper).to receive(:http_get_json)
.exactly(2).times .twice
.and_raise(Gitlab::Danger::RequestHelper::HTTPError.new) .and_raise(Gitlab::Danger::RequestHelper::HTTPError.new)
expect(subject.available?).to be true expect(subject.available?).to be true
......
...@@ -10,7 +10,7 @@ describe Gitlab::Database::Count do ...@@ -10,7 +10,7 @@ describe Gitlab::Database::Count do
let(:models) { [Project, Identity] } let(:models) { [Project, Identity] }
context '.approximate_counts' do describe '.approximate_counts' do
context 'fallbacks' do context 'fallbacks' do
subject { described_class.approximate_counts(models, strategies: strategies) } subject { described_class.approximate_counts(models, strategies: strategies) }
......
...@@ -31,7 +31,7 @@ describe Gitlab::Git::RuggedImpl::UseRugged, :seed_helper do ...@@ -31,7 +31,7 @@ describe Gitlab::Git::RuggedImpl::UseRugged, :seed_helper do
Gitlab::GitalyClient.instance_variable_set(:@can_use_disk, {}) Gitlab::GitalyClient.instance_variable_set(:@can_use_disk, {})
end end
context '#execute_rugged_call', :request_store do describe '#execute_rugged_call', :request_store do
let(:args) { ['refs/heads/master', 1] } let(:args) { ['refs/heads/master', 1] }
before do before do
......
...@@ -757,7 +757,7 @@ describe Gitlab::GitAccess do ...@@ -757,7 +757,7 @@ describe Gitlab::GitAccess do
allow(project).to receive(:lfs_enabled?).and_return(true) allow(project).to receive(:lfs_enabled?).and_return(true)
expect_next_instance_of(Gitlab::Checks::LfsIntegrity) do |instance| expect_next_instance_of(Gitlab::Checks::LfsIntegrity) do |instance|
expect(instance).to receive(:objects_missing?).exactly(1).times expect(instance).to receive(:objects_missing?).once
end end
push_access_check push_access_check
......
...@@ -5,7 +5,7 @@ require 'spec_helper' ...@@ -5,7 +5,7 @@ require 'spec_helper'
describe Gitlab::GitRefValidator do describe Gitlab::GitRefValidator do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
context '.validate' do describe '.validate' do
it { expect(described_class.validate('feature/new')).to be true } it { expect(described_class.validate('feature/new')).to be true }
it { expect(described_class.validate('implement_@all')).to be true } it { expect(described_class.validate('implement_@all')).to be true }
it { expect(described_class.validate('my_new_feature')).to be true } it { expect(described_class.validate('my_new_feature')).to be true }
...@@ -37,7 +37,7 @@ describe Gitlab::GitRefValidator do ...@@ -37,7 +37,7 @@ describe Gitlab::GitRefValidator do
it { expect(described_class.validate("\xA0\u0000\xB0")).to be false } it { expect(described_class.validate("\xA0\u0000\xB0")).to be false }
end end
context '.validate_merge_request_branch' do describe '.validate_merge_request_branch' do
it { expect(described_class.validate_merge_request_branch('HEAD')).to be true } it { expect(described_class.validate_merge_request_branch('HEAD')).to be true }
it { expect(described_class.validate_merge_request_branch('feature/new')).to be true } it { expect(described_class.validate_merge_request_branch('feature/new')).to be true }
it { expect(described_class.validate_merge_request_branch('implement_@all')).to be true } it { expect(described_class.validate_merge_request_branch('implement_@all')).to be true }
......
...@@ -215,8 +215,8 @@ describe Gitlab::Gpg do ...@@ -215,8 +215,8 @@ describe Gitlab::Gpg do
end end
it 'tries at least 2 times to remove the tmp dir before raising', :aggregate_failures do it 'tries at least 2 times to remove the tmp dir before raising', :aggregate_failures do
expect(Retriable).to receive(:sleep).at_least(2).times expect(Retriable).to receive(:sleep).at_least(:twice)
expect(FileUtils).to receive(:remove_entry).with(tmp_dir).at_least(2).times.and_raise('Deletion failed') expect(FileUtils).to receive(:remove_entry).with(tmp_dir).at_least(:twice).and_raise('Deletion failed')
expect { described_class.using_tmp_keychain { } }.to raise_error(described_class::CleanupError) expect { described_class.using_tmp_keychain { } }.to raise_error(described_class::CleanupError)
end end
......
...@@ -205,7 +205,7 @@ describe Gitlab::LegacyGithubImport::Importer do ...@@ -205,7 +205,7 @@ describe Gitlab::LegacyGithubImport::Importer do
let(:gh_pull_request) { Gitlab::LegacyGithubImport::PullRequestFormatter.new(project, closed_pull_request) } let(:gh_pull_request) { Gitlab::LegacyGithubImport::PullRequestFormatter.new(project, closed_pull_request) }
it 'does remove branches' do it 'does remove branches' do
expect(subject).to receive(:remove_branch).at_least(2).times expect(subject).to receive(:remove_branch).at_least(:twice)
subject.send(:clean_up_restored_branches, gh_pull_request) subject.send(:clean_up_restored_branches, gh_pull_request)
end end
end end
......
...@@ -8,7 +8,7 @@ describe Gitlab::PrivateCommitEmail do ...@@ -8,7 +8,7 @@ describe Gitlab::PrivateCommitEmail do
let(:valid_email) { "#{id}-foo@#{hostname}" } let(:valid_email) { "#{id}-foo@#{hostname}" }
let(:invalid_email) { "#{id}-foo@users.noreply.bar.com" } let(:invalid_email) { "#{id}-foo@users.noreply.bar.com" }
context '.regex' do describe '.regex' do
subject { described_class.regex } subject { described_class.regex }
it { is_expected.to match("1-foo@#{hostname}") } it { is_expected.to match("1-foo@#{hostname}") }
...@@ -18,7 +18,7 @@ describe Gitlab::PrivateCommitEmail do ...@@ -18,7 +18,7 @@ describe Gitlab::PrivateCommitEmail do
it { is_expected.not_to match('foobar@gitlab.com') } it { is_expected.not_to match('foobar@gitlab.com') }
end end
context '.user_id_for_email' do describe '.user_id_for_email' do
it 'parses user id from email' do it 'parses user id from email' do
expect(described_class.user_id_for_email(valid_email)).to eq(id) expect(described_class.user_id_for_email(valid_email)).to eq(id)
end end
...@@ -28,7 +28,7 @@ describe Gitlab::PrivateCommitEmail do ...@@ -28,7 +28,7 @@ describe Gitlab::PrivateCommitEmail do
end end
end end
context '.user_ids_for_email' do describe '.user_ids_for_email' do
it 'returns deduplicated user IDs for each valid email' do it 'returns deduplicated user IDs for each valid email' do
result = described_class.user_ids_for_emails([valid_email, valid_email, invalid_email]) result = described_class.user_ids_for_emails([valid_email, valid_email, invalid_email])
...@@ -41,7 +41,7 @@ describe Gitlab::PrivateCommitEmail do ...@@ -41,7 +41,7 @@ describe Gitlab::PrivateCommitEmail do
end end
end end
context '.for_user' do describe '.for_user' do
it 'returns email in the format id-username@hostname' do it 'returns email in the format id-username@hostname' do
user = create(:user) user = create(:user)
......
...@@ -15,7 +15,7 @@ describe Gitlab::RuggedInstrumentation, :request_store do ...@@ -15,7 +15,7 @@ describe Gitlab::RuggedInstrumentation, :request_store do
end end
end end
context '.increment_query_count' do describe '.increment_query_count' do
it 'tracks query counts' do it 'tracks query counts' do
expect(subject.query_count).to eq(0) expect(subject.query_count).to eq(0)
......
...@@ -30,7 +30,7 @@ describe Gitlab::Sanitizers::Exif do ...@@ -30,7 +30,7 @@ describe Gitlab::Sanitizers::Exif do
end end
it 'processes only uploads created since specified date' do it 'processes only uploads created since specified date' do
expect(sanitizer).to receive(:clean).exactly(2).times expect(sanitizer).to receive(:clean).twice
sanitizer.batch_clean(since: 2.days.ago) sanitizer.batch_clean(since: 2.days.ago)
end end
......
...@@ -6,7 +6,7 @@ describe OmniAuth::Strategies::Jwt do ...@@ -6,7 +6,7 @@ describe OmniAuth::Strategies::Jwt do
include Rack::Test::Methods include Rack::Test::Methods
include DeviseHelpers include DeviseHelpers
context '#decoded' do describe '#decoded' do
subject { described_class.new({}) } subject { described_class.new({}) }
let(:timestamp) { Time.now.to_i } let(:timestamp) { Time.now.to_i }
......
...@@ -25,13 +25,13 @@ describe SafeZip::Entry do ...@@ -25,13 +25,13 @@ describe SafeZip::Entry do
FileUtils.remove_entry_secure(target_path) FileUtils.remove_entry_secure(target_path)
end end
context '#path_dir' do describe '#path_dir' do
subject { entry.path_dir } subject { entry.path_dir }
it { is_expected.to eq(target_path + '/public/folder') } it { is_expected.to eq(target_path + '/public/folder') }
end end
context '#exist?' do describe '#exist?' do
subject { entry.exist? } subject { entry.exist? }
context 'when entry does not exist' do context 'when entry does not exist' do
......
...@@ -12,7 +12,7 @@ describe SafeZip::Extract do ...@@ -12,7 +12,7 @@ describe SafeZip::Extract do
FileUtils.remove_entry_secure(target_path) FileUtils.remove_entry_secure(target_path)
end end
context '#extract' do describe '#extract' do
subject { object.extract(directories: directories, to: target_path) } subject { object.extract(directories: directories, to: target_path) }
shared_examples 'extracts archive' do |param| shared_examples 'extracts archive' do |param|
......
...@@ -81,13 +81,13 @@ describe Badge do ...@@ -81,13 +81,13 @@ describe Badge do
let(:badge) { build(:badge, link_url: placeholder_url, image_url: placeholder_url) } let(:badge) { build(:badge, link_url: placeholder_url, image_url: placeholder_url) }
let!(:project) { create(:project) } let!(:project) { create(:project) }
context '#rendered_link_url' do describe '#rendered_link_url' do
let(:method) { :link_url } let(:method) { :link_url }
it_behaves_like 'rendered_links' it_behaves_like 'rendered_links'
end end
context '#rendered_image_url' do describe '#rendered_image_url' do
let(:method) { :image_url } let(:method) { :image_url }
it_behaves_like 'rendered_links' it_behaves_like 'rendered_links'
......
...@@ -30,13 +30,13 @@ describe ProjectBadge do ...@@ -30,13 +30,13 @@ describe ProjectBadge do
let(:badge) { build(:project_badge, link_url: placeholder_url, image_url: placeholder_url) } let(:badge) { build(:project_badge, link_url: placeholder_url, image_url: placeholder_url) }
let!(:project) { badge.project } let!(:project) { badge.project }
context '#rendered_link_url' do describe '#rendered_link_url' do
let(:method) { :link_url } let(:method) { :link_url }
it_behaves_like 'rendered_links' it_behaves_like 'rendered_links'
end end
context '#rendered_image_url' do describe '#rendered_image_url' do
let(:method) { :image_url } let(:method) { :image_url }
it_behaves_like 'rendered_links' it_behaves_like 'rendered_links'
......
...@@ -51,7 +51,7 @@ describe Ci::ArtifactBlob do ...@@ -51,7 +51,7 @@ describe Ci::ArtifactBlob do
allow(Gitlab.config.pages).to receive(:artifacts_server).and_return(true) allow(Gitlab.config.pages).to receive(:artifacts_server).and_return(true)
end end
context '.gif extension' do describe '.gif extension' do
it 'returns nil' do it 'returns nil' do
expect(subject.external_url(build.project, build)).to be_nil expect(subject.external_url(build.project, build)).to be_nil
end end
......
...@@ -11,7 +11,7 @@ describe Ci::PersistentRef do ...@@ -11,7 +11,7 @@ describe Ci::PersistentRef do
pipeline.succeed! pipeline.succeed!
end end
context '#exist?' do describe '#exist?' do
subject { pipeline.persistent_ref.exist? } subject { pipeline.persistent_ref.exist? }
let(:pipeline) { create(:ci_pipeline, sha: sha, project: project) } let(:pipeline) { create(:ci_pipeline, sha: sha, project: project) }
...@@ -31,7 +31,7 @@ describe Ci::PersistentRef do ...@@ -31,7 +31,7 @@ describe Ci::PersistentRef do
end end
end end
context '#create' do describe '#create' do
subject { pipeline.persistent_ref.create } subject { pipeline.persistent_ref.create }
let(:pipeline) { create(:ci_pipeline, sha: sha, project: project) } let(:pipeline) { create(:ci_pipeline, sha: sha, project: project) }
...@@ -81,7 +81,7 @@ describe Ci::PersistentRef do ...@@ -81,7 +81,7 @@ describe Ci::PersistentRef do
end end
end end
context '#delete' do describe '#delete' do
subject { pipeline.persistent_ref.delete } subject { pipeline.persistent_ref.delete }
let(:pipeline) { create(:ci_pipeline, sha: sha, project: project) } let(:pipeline) { create(:ci_pipeline, sha: sha, project: project) }
......
...@@ -25,7 +25,7 @@ describe Ci::Runner do ...@@ -25,7 +25,7 @@ describe Ci::Runner do
end end
end end
context '#exactly_one_group' do describe '#exactly_one_group' do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:runner) { create(:ci_runner, :group, groups: [group]) } let(:runner) { create(:ci_runner, :group, groups: [group]) }
......
...@@ -48,14 +48,14 @@ describe Avatarable do ...@@ -48,14 +48,14 @@ describe Avatarable do
end end
it 'calls local_url twice for path and URLs' do it 'calls local_url twice for path and URLs' do
expect(project.avatar).to receive(:local_url).exactly(2).times.and_call_original expect(project.avatar).to receive(:local_url).twice.and_call_original
expect(project.avatar_path(only_path: true)).to eq(avatar_path) expect(project.avatar_path(only_path: true)).to eq(avatar_path)
expect(project.avatar_path(only_path: false)).to eq(avatar_url) expect(project.avatar_path(only_path: false)).to eq(avatar_url)
end end
it 'calls local_url twice for different sizes' do it 'calls local_url twice for different sizes' do
expect(project.avatar).to receive(:local_url).exactly(2).times.and_call_original expect(project.avatar).to receive(:local_url).twice.and_call_original
expect(project.avatar_path).to eq(avatar_path) expect(project.avatar_path).to eq(avatar_path)
expect(project.avatar_path(size: 40)).to eq(avatar_path + "?width=40") expect(project.avatar_path(size: 40)).to eq(avatar_path + "?width=40")
...@@ -64,7 +64,7 @@ describe Avatarable do ...@@ -64,7 +64,7 @@ describe Avatarable do
it 'handles unpersisted objects' do it 'handles unpersisted objects' do
new_project = build(:project, :with_avatar) new_project = build(:project, :with_avatar)
path = [relative_url_root, new_project.avatar.local_url].join path = [relative_url_root, new_project.avatar.local_url].join
expect(new_project.avatar).to receive(:local_url).exactly(2).times.and_call_original expect(new_project.avatar).to receive(:local_url).twice.and_call_original
2.times do 2.times do
expect(new_project.avatar_path).to eq(path) expect(new_project.avatar_path).to eq(path)
......
...@@ -58,7 +58,7 @@ describe Group, 'Routable' do ...@@ -58,7 +58,7 @@ describe Group, 'Routable' do
end end
end end
context '.find_by_full_path' do describe '.find_by_full_path' do
let!(:nested_group) { create(:group, parent: group) } let!(:nested_group) { create(:group, parent: group) }
context 'without any redirect routes' do context 'without any redirect routes' do
......
...@@ -49,7 +49,7 @@ RSpec.describe TriggerableHooks do ...@@ -49,7 +49,7 @@ RSpec.describe TriggerableHooks do
TestableHook.create!(url: 'http://example2.com', push_events: true) TestableHook.create!(url: 'http://example2.com', push_events: true)
filter1 = double(:filter1) filter1 = double(:filter1)
filter2 = double(:filter2) filter2 = double(:filter2)
allow(ActiveHookFilter).to receive(:new).exactly(2).times.and_return(filter1, filter2) allow(ActiveHookFilter).to receive(:new).twice.and_return(filter1, filter2)
expect(filter1).to receive(:matches?).and_return(true) expect(filter1).to receive(:matches?).and_return(true)
expect(filter2).to receive(:matches?).and_return(false) expect(filter2).to receive(:matches?).and_return(false)
......
...@@ -87,7 +87,7 @@ describe DeploymentMetrics do ...@@ -87,7 +87,7 @@ describe DeploymentMetrics do
expect(prometheus_adapter).to receive(:query).with(:deployment, deployment).and_return(simple_metrics) expect(prometheus_adapter).to receive(:query).with(:deployment, deployment).and_return(simple_metrics)
end end
it { is_expected.to eq(simple_metrics.merge({ deployment_time: deployment.created_at.to_i })) } it { is_expected.to eq(simple_metrics.merge({ deployment_time: deployment.finished_at.to_i })) }
end end
end end
......
...@@ -495,7 +495,7 @@ describe Deployment do ...@@ -495,7 +495,7 @@ describe Deployment do
end end
end end
context '#update_status' do describe '#update_status' do
let(:deploy) { create(:deployment, status: :running) } let(:deploy) { create(:deployment, status: :running) }
it 'changes the status' do it 'changes the status' do
......
...@@ -359,7 +359,7 @@ describe Issue do ...@@ -359,7 +359,7 @@ describe Issue do
allow(subject.project).to receive(:repository).and_return(repository) allow(subject.project).to receive(:repository).and_return(repository)
end end
context '#to_branch_name does not exists' do describe '#to_branch_name does not exists' do
before do before do
allow(repository).to receive(:branch_exists?).and_return(false) allow(repository).to receive(:branch_exists?).and_return(false)
end end
...@@ -369,7 +369,7 @@ describe Issue do ...@@ -369,7 +369,7 @@ describe Issue do
end end
end end
context '#to_branch_name exists not ending with -index' do describe '#to_branch_name exists not ending with -index' do
before do before do
allow(repository).to receive(:branch_exists?).and_return(true) allow(repository).to receive(:branch_exists?).and_return(true)
allow(repository).to receive(:branch_exists?).with(/#{subject.to_branch_name}-\d/).and_return(false) allow(repository).to receive(:branch_exists?).with(/#{subject.to_branch_name}-\d/).and_return(false)
...@@ -380,7 +380,7 @@ describe Issue do ...@@ -380,7 +380,7 @@ describe Issue do
end end
end end
context '#to_branch_name exists ending with -index' do describe '#to_branch_name exists ending with -index' do
before do before do
allow(repository).to receive(:branch_exists?).and_return(true) allow(repository).to receive(:branch_exists?).and_return(true)
allow(repository).to receive(:branch_exists?).with("#{subject.to_branch_name}-3").and_return(false) allow(repository).to receive(:branch_exists?).with("#{subject.to_branch_name}-3").and_return(false)
......
...@@ -80,7 +80,7 @@ describe NotificationRecipient do ...@@ -80,7 +80,7 @@ describe NotificationRecipient do
end end
end end
context '#notification_setting' do describe '#notification_setting' do
context 'for child groups' do context 'for child groups' do
let!(:moved_group) { create(:group) } let!(:moved_group) { create(:group) }
let(:group) { create(:group) } let(:group) { create(:group) }
......
...@@ -148,7 +148,7 @@ describe BambooService, :use_clean_rails_memory_store_caching do ...@@ -148,7 +148,7 @@ describe BambooService, :use_clean_rails_memory_store_caching do
end end
shared_examples 'reactive cache calculation' do shared_examples 'reactive cache calculation' do
context '#build_page' do describe '#build_page' do
subject { service.calculate_reactive_cache('123', 'unused')[:build_page] } subject { service.calculate_reactive_cache('123', 'unused')[:build_page] }
it 'returns a specific URL when status is 500' do it 'returns a specific URL when status is 500' do
...@@ -180,7 +180,7 @@ describe BambooService, :use_clean_rails_memory_store_caching do ...@@ -180,7 +180,7 @@ describe BambooService, :use_clean_rails_memory_store_caching do
end end
end end
context '#commit_status' do describe '#commit_status' do
subject { service.calculate_reactive_cache('123', 'unused')[:commit_status] } subject { service.calculate_reactive_cache('123', 'unused')[:commit_status] }
it 'sets commit status to :error when status is 500' do it 'sets commit status to :error when status is 500' do
......
...@@ -83,7 +83,7 @@ describe BuildkiteService, :use_clean_rails_memory_store_caching do ...@@ -83,7 +83,7 @@ describe BuildkiteService, :use_clean_rails_memory_store_caching do
end end
describe '#calculate_reactive_cache' do describe '#calculate_reactive_cache' do
context '#commit_status' do describe '#commit_status' do
subject { service.calculate_reactive_cache('123', 'unused')[:commit_status] } subject { service.calculate_reactive_cache('123', 'unused')[:commit_status] }
it 'sets commit status to :error when status is 500' do it 'sets commit status to :error when status is 500' do
......
...@@ -31,7 +31,7 @@ describe ChatMessage::IssueMessage do ...@@ -31,7 +31,7 @@ describe ChatMessage::IssueMessage do
context 'without markdown' do context 'without markdown' do
let(:color) { '#C95823' } let(:color) { '#C95823' }
context '#initialize' do describe '#initialize' do
before do before do
args[:object_attributes][:description] = nil args[:object_attributes][:description] = nil
end end
......
...@@ -86,7 +86,7 @@ describe DroneCiService, :use_clean_rails_memory_store_caching do ...@@ -86,7 +86,7 @@ describe DroneCiService, :use_clean_rails_memory_store_caching do
describe '#calculate_reactive_cache' do describe '#calculate_reactive_cache' do
include_context :drone_ci_service include_context :drone_ci_service
context '#commit_status' do describe '#commit_status' do
subject { drone.calculate_reactive_cache(sha, branch)[:commit_status] } subject { drone.calculate_reactive_cache(sha, branch)[:commit_status] }
it 'sets commit status to :error when status is 500' do it 'sets commit status to :error when status is 500' do
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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