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
stages:
- sync
- prepare
- quick-test
- test
- post-test
- review-prepare
......
......@@ -660,129 +660,3 @@ Style/UnneededCondition:
# Cop supports --auto-correct.
Style/UnneededInterpolation:
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 {
<a
v-if="titleLink"
:href="titleLink"
target="blank"
target="_blank"
rel="noopener noreferrer"
class="js-cluster-application-title"
>{{ title }}</a
......
......@@ -285,7 +285,7 @@ export default {
ref="viewButton"
v-gl-tooltip.hover
:href="diffFile.view_path"
target="blank"
target="_blank"
class="view-file"
data-track-event="click_toggle_view_sha_button"
data-track-label="diff_toggle_view_sha_button"
......
......@@ -166,7 +166,7 @@ export const setUrlFragment = (url, fragment) => {
export function visitUrl(url, external = false) {
if (external) {
// Simulate `target="blank" rel="noopener noreferrer"`
// Simulate `target="_blank" rel="noopener noreferrer"`
// See https://mathiasbynens.github.io/rel-noopener/
const otherWindow = window.open();
otherWindow.opener = null;
......
......@@ -14,13 +14,24 @@ module Groups
track_event(:list_repositories)
render json: ContainerRepositoriesSerializer
serializer = ContainerRepositoriesSerializer
.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
# The show action renders index to allow frontend routing to work on page refresh
def show
render :index
end
private
def feature_flag_group_container_registry_browser!
......
......@@ -14,13 +14,23 @@ module Projects
track_event(:list_repositories)
render json: ContainerRepositoriesSerializer
serializer = ContainerRepositoriesSerializer
.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
# The show action renders index to allow frontend routing to work on page refresh
def show
render :index
end
def destroy
DeleteContainerRepositoryWorker.perform_async(current_user.id, image.id)
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
ci_config_path.blank? || ci_config_path == Gitlab::FileDetector::PATTERNS[:gitlab_ci]
end
def limited_protected_branches(limit)
protected_branches.limit(limit)
end
private
def closest_namespace_setting(name)
......
......@@ -2,6 +2,7 @@
class ProtectedBranch < ApplicationRecord
include ProtectedRef
include Gitlab::SQL::Pattern
scope :requiring_code_owner_approval,
-> { where(code_owner_approval_required: true) }
......@@ -45,6 +46,12 @@ class ProtectedBranch < ApplicationRecord
# NOOP
#
end
def self.by_name(query)
return none if query.blank?
where(fuzzy_arel_match(:name, query.downcase))
end
end
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
class ContainerRepositoriesSerializer < BaseSerializer
include WithPagination
entity ContainerRepositoryEntity
def represent_read_only(resource)
......
......@@ -81,15 +81,17 @@ module Git
end
def pipeline_params
{
before: oldrev,
after: newrev,
ref: ref,
variables_attributes: generate_vars_from_push_options || [],
push_options: params[:push_options] || {},
checkout_sha: Gitlab::DataBuilder::Push.checkout_sha(
project.repository, newrev, ref)
}
strong_memoize(:pipeline_params) do
{
before: oldrev,
after: newrev,
ref: ref,
variables_attributes: generate_vars_from_push_options || [],
push_options: params[:push_options] || {},
checkout_sha: Gitlab::DataBuilder::Push.checkout_sha(
project.repository, newrev, ref)
}
end
end
def ci_variables_from_push_options
......@@ -156,12 +158,16 @@ module Git
project_path: project.full_path,
message: "Error creating pipeline",
errors: exception.to_s,
pipeline_params: pipeline_params
pipeline_params: sanitized_pipeline_params
}
logger.warn(data)
end
def sanitized_pipeline_params
pipeline_params.except(:push_options)
end
def logger
if Gitlab::Runtime.sidekiq?
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
end
end
resources :container_registries, only: [:index], controller: 'registry/repositories'
resources :container_registries, only: [:index, :show], controller: 'registry/repositories'
end
scope(path: '*id',
......
......@@ -371,7 +371,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
resources :container_registry, only: [:index, :destroy],
resources :container_registry, only: [:index, :destroy, :show],
controller: 'registry/repositories'
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
t.string "encrypted_token_iv"
t.string "encrypted_url"
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 ["type"], name: "index_web_hooks_on_type"
end
......
......@@ -385,7 +385,7 @@ Parameters:
- `skip_confirmation` (optional) - Skip confirmation - true or false (default)
- `external` (optional) - Flags the user as external - true or false (default)
- `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)**
- `extra_shared_runners_minutes_limit` (optional) - Extra pipeline minutes quota for this user **(STARTER)**
......@@ -423,7 +423,7 @@ Parameters:
- `shared_runners_minutes_limit` (optional) - 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
- `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)**
- `extra_shared_runners_minutes_limit` (optional) - Extra pipeline minutes quota for this user **(STARTER)**
- `note` (optional) - Admin notes for this user **(STARTER)**
......
......@@ -83,6 +83,7 @@ Complementary reads:
- [Cycle Analytics development guide](cycle_analytics.md)
- [Issue types vs first-class types](issue_types.md)
- [Application limits](application_limits.md)
- [Redis guidelines](redis.md)
## Performance guides
......
......@@ -19,9 +19,6 @@ The current stages are:
<https://gitlab.com/gitlab-org/gitlab-foss>.
- `prepare`: This stage includes jobs that prepare artifacts that are needed by
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.
- `post-test`: This stage includes jobs that build reports or gather data from
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
**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`).**
### Canonical commits only
### Canonical/security namespace merge requests only
This condition limits jobs creation to commits under the `gitlab-org/` top-level group
on GitLab.com only. This is similar to the `.only:variables-canonical-dot-com` CI definition:
This condition limits jobs creation to merge requests under the `gitlab-org/` top-level group
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
.if-canonical-gitlab: &if-canonical-gitlab
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/)/'
```
The definition for `if-canonical-dot-com-gitlab-org-groups-merge-request` can be
seen in <https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/docs.gitlab-ci.yml>.
### Canonical merge requests only
### Canonical/security namespace tags only
Same as the "Canonical commits only" condition above but further limits jobs creation
to merge requests only (i.e. this won't run for `master`, stable or auto-deploy branches).
This is similar to the `.only:variables-canonical-dot-com` + `.except:refs-master-tags-stable-deploy`
CI definitions:
This condition limits jobs creation to tags under the `gitlab-org/` top-level group
on GitLab.com only.
This is similar to the `.only:variables-canonical-dot-com` + `only:refs: [tags]` CI definition:
```yaml
.if-canonical-gitlab-merge-request: &if-canonical-gitlab-merge-request
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/)/ && $CI_MERGE_REQUEST_IID'
```
The definition for `if-canonical-dot-com-gitlab-org-groups-tag` can be seen in
<https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/cng.gitlab-ci.yml>.
### 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
Similar patterns as for `.only:changes-code`:
```yaml
.code-patterns: &code-patterns
- ...
```
The definition for `code-patterns` can be seen in
<https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/qa.gitlab-ci.yml>.
### QA changes patterns
Similar patterns as for `.only:changes-qa`:
```yaml
.qa-patterns: &qa-patterns
- ...
```
The definition for `qa-patterns` can be seen in
<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
Similar patterns as for `.only:changes-code-qa`:
```yaml
.code-qa-patterns: &code-qa-patterns
- ...
```
The definition for `code-qa-patterns` can be seen in
<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
......@@ -195,21 +247,21 @@ execute jobs out of order for the following jobs:
```mermaid
graph RL;
A[setup-test-env];
B["gitlab:assets:compile pull-push-cache<br/>(master only)"];
C[gitlab:assets:compile pull-cache];
B["gitlab:assets:compile pull-push-cache<br/>(canonical master only)"];
C["gitlab:assets:compile pull-cache<br/>(canonical default refs only)"];
D["cache gems<br/>(master and tags only)"];
E[review-build-cng];
F[build-qa-image];
G[review-deploy];
G2["schedule:review-deploy<br/>(master only)"];
H[karma];
I[jest];
I["karma, jest, webpack-dev-server, static-analysis"];
I2["karma-foss, jest-foss<br/>(EE default refs 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];
L[webpack-dev-server];
K2["compile-assets pull-cache foss<br/>(EE default refs only)"];
M[coverage];
N[pages];
O[static-analysis];
N["pages (master only)"];
Q[package-and-qa];
S["RSpec<br/>(e.g. rspec unit pg9)"]
T[retrieve-tests-metadata];
......@@ -220,58 +272,55 @@ subgraph "`prepare` stage"
C
F
K
K2
J
J2
T
end
subgraph "`test` stage"
D --> |needs| A;
H -.-> |needs and depends on| A;
H -.-> |needs and depends on| K;
D -.-> |needs| A;
I -.-> |needs and depends on| A;
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| K;
O -.-> |needs and depends on| A;
O -.-> |needs and depends on| K;
S -.-> |needs and depends on| A;
S -.-> |needs and depends on| K;
S -.-> |needs and depends on| T;
downtime_check --> |needs and depends on| A;
db:* --> |needs| A;
gitlab:setup --> |needs| A;
downtime_check --> |needs and depends on| A;
graphql-docs-verify --> |needs| A;
L["db:*, gitlab:setup, graphql-docs-verify, downtime_check"] -.-> |needs| A;
end
subgraph "`post-test` stage"
M --> |happens after| S
end
subgraph "`review-prepare` stage"
E --> |needs| C;
X["schedule:review-build-cng<br/>(master schedule only)"] --> |needs| C;
E -.-> |needs| C;
E2["schedule:review-build-cng<br/>(master schedule only)"] -.-> |needs| C;
end
subgraph "`review` stage"
G
G2
G --> |happens after| E
G2 --> |happens after| E2
end
subgraph "`qa` stage"
Q --> |needs| C;
Q --> |needs| F;
review-qa-smoke -.-> |needs and depends on| G;
review-qa-all -.-> |needs and depends on| G;
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;
Q -.-> |needs| C;
Q -.-> |needs| F;
QA1["review-qa-smoke, review-qa-all, review-performance, dast"] -.-> |needs and depends on| G;
QA2["schedule:review-performance<br/>(master only)"] -.-> |needs and depends on| G2;
end
subgraph "`post-test` stage"
M
end
subgraph "`post-qa` stage"
PQA1["parallel-spec-reports"] -.-> |depends on `review-qa-all`| QA1;
end
subgraph "`pages` stage"
N -.-> |depends on| C;
N -.-> |depends on| H;
N -.-> |depends on karma| I;
N -.-> |depends on| M;
N --> |happens after| PQA1
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
end
params do
use :pagination
optional :search, type: String, desc: 'Search for a protected branch by name'
end
# rubocop: disable CodeReuse/ActiveRecord
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
end
......
......@@ -52,7 +52,7 @@ module API
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
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
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:
PIP_REQUIREMENTS_FILE \
MAVEN_CLI_OPTS \
BUNDLER_AUDIT_UPDATE_DISABLED \
BUNDLER_AUDIT_ADVISORY_DB_URL \
BUNDLER_AUDIT_ADVISORY_DB_REF_NAME \
) \
--volume "$PWD:/code" \
--volume /var/run/docker.sock:/var/run/docker.sock \
......
......@@ -3,7 +3,7 @@
module QA
context 'Plan', :reliable 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
unless QA::Runtime::Env.personal_access_token
......
......@@ -18,7 +18,7 @@ module QA
push_commit('Initial commit')
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)
issue.visit!
......
......@@ -17,7 +17,7 @@ module QA
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|
one_reply = "1 reply"
......
......@@ -9,7 +9,7 @@ module QA
Resource::Issue.fabricate_via_api!.visit!
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|
first_version_of_comment = 'First version of the comment'
second_version_of_comment = 'Second version of the comment'
......
......@@ -7,7 +7,7 @@ module QA
Flow::Login.sign_in
end
it 'user creates an issue' do
it 'creates an issue' do
issue = Resource::Issue.fabricate_via_browser_ui!
Page::Project::Menu.perform(&:click_issues)
......@@ -27,7 +27,7 @@ module QA
Resource::Issue.fabricate_via_api!.visit!
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|
show.comment('See attached banana for scale', attachment: file_to_attach)
......
......@@ -9,7 +9,7 @@ module QA
Resource::Issue.fabricate_via_api!.visit!
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|
my_own_comment = "My own comment"
made_the_issue_confidential = "made the issue confidential"
......
......@@ -13,7 +13,7 @@ module QA
end.project.visit!
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::Issue::New.perform do |new_page|
new_page.add_title("issue")
......
......@@ -20,7 +20,7 @@ module QA
end.visit!
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|
at_username = "@#{@user.username}"
......
......@@ -120,7 +120,7 @@ describe QA::Resource::ApiFabricator do
end
end
context '#transform_api_resource' do
describe '#transform_api_resource' do
let(:resource) do
Class.new do
def self.name
......
# frozen_string_literal: true
describe QA::Scenario::Test::Integration::Github do
context '#perform' do
describe '#perform' do
let(:env) { spy('Runtime::Env') }
before do
......
# frozen_string_literal: true
describe QA::Scenario::Test::Integration::InstanceSAML do
context '#perform' do
describe '#perform' do
it_behaves_like 'a QA scenario class' do
let(:tags) { [:instance_saml] }
end
......
# frozen_string_literal: true
describe QA::Scenario::Test::Integration::Kubernetes do
context '#perform' do
describe '#perform' do
it_behaves_like 'a QA scenario class' do
let(:tags) { [:kubernetes] }
end
......
# frozen_string_literal: true
describe QA::Scenario::Test::Integration::LDAPNoTLS do
context '#perform' do
describe '#perform' do
it_behaves_like 'a QA scenario class' do
let(:tags) { [:ldap_no_tls] }
end
......@@ -9,7 +9,7 @@ describe QA::Scenario::Test::Integration::LDAPNoTLS do
end
describe QA::Scenario::Test::Integration::LDAPNoServer do
context '#perform' do
describe '#perform' do
it_behaves_like 'a QA scenario class' do
let(:tags) { [:ldap_no_server] }
end
......@@ -17,7 +17,7 @@ describe QA::Scenario::Test::Integration::LDAPNoServer do
end
describe QA::Scenario::Test::Integration::LDAPTLS do
context '#perform' do
describe '#perform' do
it_behaves_like 'a QA scenario class' do
let(:tags) { [:ldap_tls] }
end
......
# frozen_string_literal: true
describe QA::Scenario::Test::Integration::Mattermost do
context '#perform' do
describe '#perform' do
it_behaves_like 'a QA scenario class' do
let(:args) { %w[gitlab_address mattermost_address] }
let(:args) do
......
# frozen_string_literal: true
describe QA::Scenario::Test::Integration::OAuth do
context '#perform' do
describe '#perform' do
it_behaves_like 'a QA scenario class' do
let(:tags) { [:oauth] }
end
......
# frozen_string_literal: true
describe QA::Scenario::Test::Integration::ObjectStorage do
context '#perform' do
describe '#perform' do
it_behaves_like 'a QA scenario class' do
let(:tags) { [:object_storage] }
end
......
......@@ -11,7 +11,7 @@ describe QA::Specs::Runner do
end
end
context '#perform' do
describe '#perform' do
before do
allow(QA::Runtime::Browser).to receive(:configure!)
end
......
......@@ -896,7 +896,7 @@ describe ApplicationController do
end
end
context '#set_current_context' do
describe '#set_current_context' do
controller(described_class) do
def index
Labkit::Context.with_context do |context|
......
......@@ -14,7 +14,7 @@ describe Groups::Registry::RepositoriesController do
sign_in(user)
end
context 'GET #index' do
shared_examples 'renders a list of repositories' do
context 'when container registry is enabled' do
it 'show index page' do
expect(Gitlab::Tracking).not_to receive(:event)
......@@ -33,6 +33,7 @@ describe Groups::Registry::RepositoriesController do
}
expect(response).to match_response_schema('registry/repositories')
expect(response).to include_pagination_headers
end
it 'returns a list of projects for json format' do
......@@ -89,5 +90,29 @@ describe Groups::Registry::RepositoriesController do
expect(response).to have_gitlab_http_status(:not_found)
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
......@@ -245,7 +245,7 @@ describe Projects::MilestonesController do
end
end
context '#participants' do
describe '#participants' do
render_views
context "when guest user" do
......
......@@ -16,7 +16,7 @@ describe Projects::Registry::RepositoriesController do
project.add_developer(user)
end
describe 'GET index' do
shared_examples 'renders a list of repositories' do
context 'when root container repository exists' do
before do
create(:container_repository, :root, project: project)
......@@ -58,6 +58,7 @@ describe Projects::Registry::RepositoriesController do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('registry/repositories')
expect(response).to include_pagination_headers
end
end
......@@ -84,9 +85,33 @@ describe Projects::Registry::RepositoriesController do
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
describe 'DELETE destroy' do
describe 'DELETE #destroy' do
context 'when root container repository exists' do
let!(:repository) do
create(:container_repository, :root, project: project)
......@@ -115,7 +140,7 @@ describe Projects::Registry::RepositoriesController do
end
context 'when user does not have access to registry' do
describe 'GET index' do
describe 'GET #index' do
it 'responds with 404' do
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
end
end
context '.gitlab-ci.yml' do
describe '.gitlab-ci.yml' do
before do
project.add_maintainer(project.creator)
......@@ -481,7 +481,7 @@ describe 'File blob', :js do
end
end
context '.gitlab/route-map.yml' do
describe '.gitlab/route-map.yml' do
before do
project.add_maintainer(project.creator)
......
......@@ -128,7 +128,7 @@ describe FinderWithCrossProjectAccess do
end
end
context '.finder_model' do
describe '.finder_model' do
it 'is set correctly' do
expect(finder_class.finder_model).to eq(Project)
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
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(:diff_files) { Gitlab::Git::DiffCollection.new(files) }
let(:safe_file) { { too_large: false, diff: '' } }
......@@ -303,7 +303,7 @@ describe DiffHelper do
end
end
context '#diff_file_path_text' do
describe '#diff_file_path_text' do
it 'returns full path by default' do
expect(diff_file_path_text(diff_file)).to eq(diff_file.new_path)
end
......
......@@ -106,13 +106,13 @@ describe NavHelper, :do_not_mock_admin_mode do
end
end
context '.admin_monitoring_nav_links' do
describe '.admin_monitoring_nav_links' do
subject { helper.admin_monitoring_nav_links }
it { is_expected.to all(be_a(String)) }
end
context '.group_issues_sub_menu_items' do
describe '.group_issues_sub_menu_items' do
subject { helper.group_issues_sub_menu_items }
it { is_expected.to all(be_a(String)) }
......
......@@ -34,7 +34,7 @@ describe SourcegraphHelper do
end
end
context '#sourcegraph_experimental_message' do
describe '#sourcegraph_experimental_message' do
let(:feature_conditional) { 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
it { expect(subject).not_to be_nil }
context '#path' do
describe '#path' do
subject { registry.path }
context 'path from URL' do
......
......@@ -70,26 +70,26 @@ describe ContainerRegistry::Tag do
headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v1+prettyjws' })
end
context '#layers' do
describe '#layers' do
subject { tag.layers }
it { expect(subject.length).to eq(1) }
end
context '#total_size' do
describe '#total_size' do
subject { tag.total_size }
it { is_expected.to be_nil }
end
context 'config processing' do
context '#config' do
describe '#config' do
subject { tag.config }
it { is_expected.to be_nil }
end
context '#created_at' do
describe '#created_at' do
subject { tag.created_at }
it { is_expected.to be_nil }
......@@ -113,7 +113,7 @@ describe ContainerRegistry::Tag do
body: File.read(Rails.root + 'spec/fixtures/container_registry/config_blob_helm.json'))
end
context '#created_at' do
describe '#created_at' do
subject { tag.created_at }
it { is_expected.to be_nil }
......@@ -130,13 +130,13 @@ describe ContainerRegistry::Tag do
headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v2+json' })
end
context '#layers' do
describe '#layers' do
subject { tag.layers }
it { expect(subject.length).to eq(1) }
end
context '#total_size' do
describe '#total_size' do
subject { tag.total_size }
it { is_expected.to eq(2319870) }
......@@ -144,13 +144,13 @@ describe ContainerRegistry::Tag do
context 'config processing' do
shared_examples 'a processable' do
context '#config' do
describe '#config' do
subject { tag.config }
it { is_expected.not_to be_nil }
end
context '#created_at' do
describe '#created_at' do
subject { tag.created_at }
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
before do
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
it { is_expected.to be_truthy }
......
......@@ -12,7 +12,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
stub_feature_flags(ci_enable_live_trace: true)
end
context "#initialize" do
describe "#initialize" do
context 'when a chunk exists' do
before do
build.trace.set('ABC')
......@@ -35,7 +35,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
end
end
context "#seek" do
describe "#seek" do
subject { chunked_io.seek(pos, where) }
before do
......@@ -66,7 +66,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
end
end
context "#eof?" do
describe "#eof?" do
subject { chunked_io.eof? }
before do
......@@ -90,7 +90,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
end
end
context "#each_line" do
describe "#each_line" do
let(:string_io) { StringIO.new(sample_trace_raw) }
context 'when buffer size is smaller than file size' do
......@@ -134,7 +134,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
end
end
context "#read" do
describe "#read" do
subject { chunked_io.read(length) }
context 'when read the whole size' do
......@@ -254,7 +254,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
end
end
context "#readline" do
describe "#readline" do
subject { chunked_io.readline }
let(:string_io) { StringIO.new(sample_trace_raw) }
......@@ -334,7 +334,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
end
end
context "#write" do
describe "#write" do
subject { chunked_io.write(data) }
let(:data) { sample_trace_raw }
......@@ -399,7 +399,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
end
end
context "#truncate" do
describe "#truncate" do
let(:offset) { 10 }
context 'when data does not exist' do
......@@ -432,7 +432,7 @@ describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
end
end
context "#destroy!" do
describe "#destroy!" do
subject { chunked_io.destroy! }
before do
......
......@@ -74,7 +74,7 @@ describe Gitlab::Ci::Trace::SectionParser do
let(:lines) { build_lines(trace) }
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!
......
......@@ -8,8 +8,8 @@ describe Gitlab::Cleanup::ProjectUploads do
let(:logger) { double(:logger) }
before do
allow(logger).to receive(:info).at_least(1).times
allow(logger).to receive(:debug).at_least(1).times
allow(logger).to receive(:info).at_least(:once)
allow(logger).to receive(:debug).at_least(:once)
end
describe '#run!' do
......
......@@ -19,7 +19,7 @@ describe Gitlab::ContentSecurityPolicy::ConfigLoader do
}
end
context '.default_settings_hash' do
describe '.default_settings_hash' do
it 'returns empty defaults' do
settings = described_class.default_settings_hash
......@@ -33,7 +33,7 @@ describe Gitlab::ContentSecurityPolicy::ConfigLoader do
end
end
context '#load' do
describe '#load' do
subject { described_class.new(csp_config[:directives]) }
def expected_config(directive)
......
......@@ -176,7 +176,7 @@ describe Gitlab::Danger::Teammate do
it 'returns true if request fails' do
expect(Gitlab::Danger::RequestHelper).to receive(:http_get_json)
.exactly(2).times
.twice
.and_raise(Gitlab::Danger::RequestHelper::HTTPError.new)
expect(subject.available?).to be true
......
......@@ -10,7 +10,7 @@ describe Gitlab::Database::Count do
let(:models) { [Project, Identity] }
context '.approximate_counts' do
describe '.approximate_counts' do
context 'fallbacks' do
subject { described_class.approximate_counts(models, strategies: strategies) }
......
......@@ -31,7 +31,7 @@ describe Gitlab::Git::RuggedImpl::UseRugged, :seed_helper do
Gitlab::GitalyClient.instance_variable_set(:@can_use_disk, {})
end
context '#execute_rugged_call', :request_store do
describe '#execute_rugged_call', :request_store do
let(:args) { ['refs/heads/master', 1] }
before do
......
......@@ -757,7 +757,7 @@ describe Gitlab::GitAccess do
allow(project).to receive(:lfs_enabled?).and_return(true)
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
push_access_check
......
......@@ -5,7 +5,7 @@ require 'spec_helper'
describe Gitlab::GitRefValidator do
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('implement_@all')).to be true }
it { expect(described_class.validate('my_new_feature')).to be true }
......@@ -37,7 +37,7 @@ describe Gitlab::GitRefValidator do
it { expect(described_class.validate("\xA0\u0000\xB0")).to be false }
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('feature/new')).to be true }
it { expect(described_class.validate_merge_request_branch('implement_@all')).to be true }
......
......@@ -215,8 +215,8 @@ describe Gitlab::Gpg do
end
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(FileUtils).to receive(:remove_entry).with(tmp_dir).at_least(2).times.and_raise('Deletion failed')
expect(Retriable).to receive(:sleep).at_least(:twice)
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)
end
......
......@@ -205,7 +205,7 @@ describe Gitlab::LegacyGithubImport::Importer do
let(:gh_pull_request) { Gitlab::LegacyGithubImport::PullRequestFormatter.new(project, closed_pull_request) }
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)
end
end
......
......@@ -8,7 +8,7 @@ describe Gitlab::PrivateCommitEmail do
let(:valid_email) { "#{id}-foo@#{hostname}" }
let(:invalid_email) { "#{id}-foo@users.noreply.bar.com" }
context '.regex' do
describe '.regex' do
subject { described_class.regex }
it { is_expected.to match("1-foo@#{hostname}") }
......@@ -18,7 +18,7 @@ describe Gitlab::PrivateCommitEmail do
it { is_expected.not_to match('foobar@gitlab.com') }
end
context '.user_id_for_email' do
describe '.user_id_for_email' do
it 'parses user id from email' do
expect(described_class.user_id_for_email(valid_email)).to eq(id)
end
......@@ -28,7 +28,7 @@ describe Gitlab::PrivateCommitEmail do
end
end
context '.user_ids_for_email' do
describe '.user_ids_for_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])
......@@ -41,7 +41,7 @@ describe Gitlab::PrivateCommitEmail do
end
end
context '.for_user' do
describe '.for_user' do
it 'returns email in the format id-username@hostname' do
user = create(:user)
......
......@@ -15,7 +15,7 @@ describe Gitlab::RuggedInstrumentation, :request_store do
end
end
context '.increment_query_count' do
describe '.increment_query_count' do
it 'tracks query counts' do
expect(subject.query_count).to eq(0)
......
......@@ -30,7 +30,7 @@ describe Gitlab::Sanitizers::Exif do
end
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)
end
......
......@@ -6,7 +6,7 @@ describe OmniAuth::Strategies::Jwt do
include Rack::Test::Methods
include DeviseHelpers
context '#decoded' do
describe '#decoded' do
subject { described_class.new({}) }
let(:timestamp) { Time.now.to_i }
......
......@@ -25,13 +25,13 @@ describe SafeZip::Entry do
FileUtils.remove_entry_secure(target_path)
end
context '#path_dir' do
describe '#path_dir' do
subject { entry.path_dir }
it { is_expected.to eq(target_path + '/public/folder') }
end
context '#exist?' do
describe '#exist?' do
subject { entry.exist? }
context 'when entry does not exist' do
......
......@@ -12,7 +12,7 @@ describe SafeZip::Extract do
FileUtils.remove_entry_secure(target_path)
end
context '#extract' do
describe '#extract' do
subject { object.extract(directories: directories, to: target_path) }
shared_examples 'extracts archive' do |param|
......
......@@ -81,13 +81,13 @@ describe Badge do
let(:badge) { build(:badge, link_url: placeholder_url, image_url: placeholder_url) }
let!(:project) { create(:project) }
context '#rendered_link_url' do
describe '#rendered_link_url' do
let(:method) { :link_url }
it_behaves_like 'rendered_links'
end
context '#rendered_image_url' do
describe '#rendered_image_url' do
let(:method) { :image_url }
it_behaves_like 'rendered_links'
......
......@@ -30,13 +30,13 @@ describe ProjectBadge do
let(:badge) { build(:project_badge, link_url: placeholder_url, image_url: placeholder_url) }
let!(:project) { badge.project }
context '#rendered_link_url' do
describe '#rendered_link_url' do
let(:method) { :link_url }
it_behaves_like 'rendered_links'
end
context '#rendered_image_url' do
describe '#rendered_image_url' do
let(:method) { :image_url }
it_behaves_like 'rendered_links'
......
......@@ -51,7 +51,7 @@ describe Ci::ArtifactBlob do
allow(Gitlab.config.pages).to receive(:artifacts_server).and_return(true)
end
context '.gif extension' do
describe '.gif extension' do
it 'returns nil' do
expect(subject.external_url(build.project, build)).to be_nil
end
......
......@@ -11,7 +11,7 @@ describe Ci::PersistentRef do
pipeline.succeed!
end
context '#exist?' do
describe '#exist?' do
subject { pipeline.persistent_ref.exist? }
let(:pipeline) { create(:ci_pipeline, sha: sha, project: project) }
......@@ -31,7 +31,7 @@ describe Ci::PersistentRef do
end
end
context '#create' do
describe '#create' do
subject { pipeline.persistent_ref.create }
let(:pipeline) { create(:ci_pipeline, sha: sha, project: project) }
......@@ -81,7 +81,7 @@ describe Ci::PersistentRef do
end
end
context '#delete' do
describe '#delete' do
subject { pipeline.persistent_ref.delete }
let(:pipeline) { create(:ci_pipeline, sha: sha, project: project) }
......
......@@ -25,7 +25,7 @@ describe Ci::Runner do
end
end
context '#exactly_one_group' do
describe '#exactly_one_group' do
let(:group) { create(:group) }
let(:runner) { create(:ci_runner, :group, groups: [group]) }
......
......@@ -48,14 +48,14 @@ describe Avatarable do
end
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: false)).to eq(avatar_url)
end
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(size: 40)).to eq(avatar_path + "?width=40")
......@@ -64,7 +64,7 @@ describe Avatarable do
it 'handles unpersisted objects' do
new_project = build(:project, :with_avatar)
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
expect(new_project.avatar_path).to eq(path)
......
......@@ -58,7 +58,7 @@ describe Group, 'Routable' do
end
end
context '.find_by_full_path' do
describe '.find_by_full_path' do
let!(:nested_group) { create(:group, parent: group) }
context 'without any redirect routes' do
......
......@@ -49,7 +49,7 @@ RSpec.describe TriggerableHooks do
TestableHook.create!(url: 'http://example2.com', push_events: true)
filter1 = double(:filter1)
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(filter2).to receive(:matches?).and_return(false)
......
......@@ -87,7 +87,7 @@ describe DeploymentMetrics do
expect(prometheus_adapter).to receive(:query).with(:deployment, deployment).and_return(simple_metrics)
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
......
......@@ -495,7 +495,7 @@ describe Deployment do
end
end
context '#update_status' do
describe '#update_status' do
let(:deploy) { create(:deployment, status: :running) }
it 'changes the status' do
......
......@@ -359,7 +359,7 @@ describe Issue do
allow(subject.project).to receive(:repository).and_return(repository)
end
context '#to_branch_name does not exists' do
describe '#to_branch_name does not exists' do
before do
allow(repository).to receive(:branch_exists?).and_return(false)
end
......@@ -369,7 +369,7 @@ describe Issue do
end
end
context '#to_branch_name exists not ending with -index' do
describe '#to_branch_name exists not ending with -index' do
before do
allow(repository).to receive(:branch_exists?).and_return(true)
allow(repository).to receive(:branch_exists?).with(/#{subject.to_branch_name}-\d/).and_return(false)
......@@ -380,7 +380,7 @@ describe Issue do
end
end
context '#to_branch_name exists ending with -index' do
describe '#to_branch_name exists ending with -index' do
before do
allow(repository).to receive(:branch_exists?).and_return(true)
allow(repository).to receive(:branch_exists?).with("#{subject.to_branch_name}-3").and_return(false)
......
......@@ -80,7 +80,7 @@ describe NotificationRecipient do
end
end
context '#notification_setting' do
describe '#notification_setting' do
context 'for child groups' do
let!(:moved_group) { create(:group) }
let(:group) { create(:group) }
......
......@@ -148,7 +148,7 @@ describe BambooService, :use_clean_rails_memory_store_caching do
end
shared_examples 'reactive cache calculation' do
context '#build_page' do
describe '#build_page' do
subject { service.calculate_reactive_cache('123', 'unused')[:build_page] }
it 'returns a specific URL when status is 500' do
......@@ -180,7 +180,7 @@ describe BambooService, :use_clean_rails_memory_store_caching do
end
end
context '#commit_status' do
describe '#commit_status' do
subject { service.calculate_reactive_cache('123', 'unused')[:commit_status] }
it 'sets commit status to :error when status is 500' do
......
......@@ -83,7 +83,7 @@ describe BuildkiteService, :use_clean_rails_memory_store_caching do
end
describe '#calculate_reactive_cache' do
context '#commit_status' do
describe '#commit_status' do
subject { service.calculate_reactive_cache('123', 'unused')[:commit_status] }
it 'sets commit status to :error when status is 500' do
......
......@@ -31,7 +31,7 @@ describe ChatMessage::IssueMessage do
context 'without markdown' do
let(:color) { '#C95823' }
context '#initialize' do
describe '#initialize' do
before do
args[:object_attributes][:description] = nil
end
......
......@@ -86,7 +86,7 @@ describe DroneCiService, :use_clean_rails_memory_store_caching do
describe '#calculate_reactive_cache' do
include_context :drone_ci_service
context '#commit_status' do
describe '#commit_status' do
subject { drone.calculate_reactive_cache(sha, branch)[:commit_status] }
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