Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
2824b15286
commit
ae69a88c2a
31 changed files with 873 additions and 58 deletions
|
@ -259,8 +259,17 @@ export default {
|
|||
);
|
||||
}
|
||||
},
|
||||
expandedPanel: {
|
||||
handler({ group, panel }) {
|
||||
const dashboardPath = this.currentDashboard || this.firstDashboard.path;
|
||||
updateHistory({
|
||||
url: panelToUrl(dashboardPath, group, panel),
|
||||
title: document.title,
|
||||
});
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
this.setInitialState({
|
||||
metricsEndpoint: this.metricsEndpoint,
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { pickBy } from 'lodash';
|
||||
import { queryToObject, mergeUrlParams, removeParams } from '~/lib/utils/url_utility';
|
||||
import {
|
||||
timeRangeParamNames,
|
||||
|
@ -174,25 +173,30 @@ export const expandedPanelPayloadFromUrl = (dashboard, search = window.location.
|
|||
* Convert panel information to a URL for the user to
|
||||
* bookmark or share highlighting a specific panel.
|
||||
*
|
||||
* @param {String} dashboardPath - Dashboard path used as identifier
|
||||
* @param {String} group - Group Identifier
|
||||
* If no group/panel is set, the dashboard URL is returned.
|
||||
*
|
||||
* @param {?String} dashboard - Dashboard path, used as identifier for a dashboard
|
||||
* @param {?String} group - Group Identifier
|
||||
* @param {?Object} panel - Panel object from the dashboard
|
||||
* @param {?String} url - Base URL including current search params
|
||||
* @returns Dashboard URL which expands a panel (chart)
|
||||
*/
|
||||
export const panelToUrl = (dashboardPath, group, panel, url = window.location.href) => {
|
||||
if (!group || !panel) {
|
||||
return null;
|
||||
export const panelToUrl = (dashboard = null, group, panel, url = window.location.href) => {
|
||||
const params = {
|
||||
dashboard,
|
||||
};
|
||||
|
||||
if (group && panel) {
|
||||
params.group = group;
|
||||
params.title = panel.title;
|
||||
params.y_label = panel.y_label;
|
||||
} else {
|
||||
// Remove existing parameters if any
|
||||
params.group = null;
|
||||
params.title = null;
|
||||
params.y_label = null;
|
||||
}
|
||||
const params = pickBy(
|
||||
{
|
||||
dashboard: dashboardPath,
|
||||
group,
|
||||
title: panel.title,
|
||||
y_label: panel.y_label,
|
||||
},
|
||||
value => value != null,
|
||||
);
|
||||
|
||||
return mergeUrlParams(params, url);
|
||||
};
|
||||
|
||||
|
|
|
@ -683,6 +683,8 @@ module Ci
|
|||
variables.concat(merge_request.predefined_variables)
|
||||
end
|
||||
|
||||
variables.append(key: 'CI_KUBERNETES_ACTIVE', value: 'true') if has_kubernetes_active?
|
||||
|
||||
if external_pull_request_event? && external_pull_request
|
||||
variables.concat(external_pull_request.predefined_variables)
|
||||
end
|
||||
|
|
12
app/serializers/accessibility_error_entity.rb
Normal file
12
app/serializers/accessibility_error_entity.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AccessibilityErrorEntity < Grape::Entity
|
||||
expose :code
|
||||
expose :type
|
||||
expose :typeCode, as: :type_code
|
||||
expose :message
|
||||
expose :context
|
||||
expose :selector
|
||||
expose :runner
|
||||
expose :runnerExtras, as: :runner_extras
|
||||
end
|
15
app/serializers/accessibility_reports_comparer_entity.rb
Normal file
15
app/serializers/accessibility_reports_comparer_entity.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AccessibilityReportsComparerEntity < Grape::Entity
|
||||
expose :status
|
||||
|
||||
expose :new_errors, using: AccessibilityErrorEntity
|
||||
expose :resolved_errors, using: AccessibilityErrorEntity
|
||||
expose :existing_errors, using: AccessibilityErrorEntity
|
||||
|
||||
expose :summary do
|
||||
expose :total_count, as: :total
|
||||
expose :resolved_count, as: :resolved
|
||||
expose :errors_count, as: :errored
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AccessibilityReportsComparerSerializer < BaseSerializer
|
||||
entity AccessibilityReportsComparerEntity
|
||||
end
|
5
changelogs/unreleased/215473-url-update-single-panel.yml
Normal file
5
changelogs/unreleased/215473-url-update-single-panel.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update metrics dashboard url when a panel is expanded or contracted
|
||||
merge_request: 30704
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Add a CI variable CI_KUBERNETES_ACTIVE as an alternative to only:kubernetes/except:kubernetes
|
||||
that works with the rules syntax
|
||||
merge_request: 31146
|
||||
author:
|
||||
type: added
|
|
@ -167,6 +167,44 @@ do this manually.
|
|||
previously for the **secondary**.
|
||||
1. Success! The **secondary** has now been promoted to **primary**.
|
||||
|
||||
#### Promoting a **secondary** node with an external PostgreSQL database
|
||||
|
||||
The `gitlab-ctl promote-to-primary-node` command cannot be used in conjunction with
|
||||
an external PostgreSQL database, as it can only perform changes on a **secondary**
|
||||
node with GitLab and the database on the same machine. As a result, a manual process is
|
||||
required. For example, PostgreSQL databases hosted on Amazon RDS:
|
||||
|
||||
1. Promote the replica database associated with the **secondary** site. This will
|
||||
set the database to read-write:
|
||||
- Amazon RDS - [Promoting a Read Replica to Be a Standalone DB Instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ReadRepl.html#USER_ReadRepl.Promote)
|
||||
|
||||
1. Edit `/etc/gitlab/gitlab.rb` on every node in the **secondary** site to
|
||||
reflect its new status as **primary** by removing any lines that enabled the
|
||||
`geo_secondary_role`:
|
||||
|
||||
```ruby
|
||||
## In GitLab 11.4 and earlier, remove this line.
|
||||
geo_secondary_role['enable'] = true
|
||||
|
||||
## In GitLab 11.5 and later, remove this line.
|
||||
roles ['geo_secondary_role']
|
||||
```
|
||||
|
||||
After making these changes [Reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure)
|
||||
on each node so the changes take effect.
|
||||
|
||||
1. Promote the **secondary** to **primary**. SSH into a single secondary application
|
||||
node and execute:
|
||||
|
||||
```shell
|
||||
sudo gitlab-rake geo:set_secondary_as_primary
|
||||
```
|
||||
|
||||
1. Verify you can connect to the newly promoted **primary** site using the URL used
|
||||
previously for the **secondary** site.
|
||||
|
||||
Success! The **secondary** site has now been promoted to **primary**.
|
||||
|
||||
### Step 4. (Optional) Updating the primary domain DNS record
|
||||
|
||||
Updating the DNS records for the primary domain to point to the **secondary** node
|
||||
|
|
|
@ -64,6 +64,7 @@ future GitLab releases.**
|
|||
| `CI_JOB_TOKEN` | 9.0 | 1.2 | Token used for authenticating with the [GitLab Container Registry](../../user/packages/container_registry/index.md) and downloading [dependent repositories](../../user/project/new_ci_build_permissions_model.md#dependent-repositories) |
|
||||
| `CI_JOB_JWT` | 12.10 | all | RS256 JSON web token that can be used for authenticating with third party systems that support JWT authentication, for example [HashiCorp's Vault](../examples/authenticating-with-hashicorp-vault). |
|
||||
| `CI_JOB_URL` | 11.1 | 0.5 | Job details URL |
|
||||
| `CI_KUBERNETES_ACTIVE` | 13.0 | all | Included with the value `true` only if the pipeline has a Kubernetes cluster available for deployments. Not included if no cluster is availble. Can be used as an alternative to [`only:kubernetes`/`except:kubernetes`](../yaml/README.md#onlykubernetesexceptkubernetes) with [`rules:if`](../yaml/README.md#rulesif) |
|
||||
| `CI_MERGE_REQUEST_ASSIGNEES` | 11.9 | all | Comma-separated list of username(s) of assignee(s) for the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` or [`rules`](../yaml/README.md#rules) syntax is used and the merge request is created. |
|
||||
| `CI_MERGE_REQUEST_CHANGED_PAGE_PATHS` | 12.9 | all | Comma-separated list of paths of changed pages in a deployed [Review App](../review_apps/index.md) for a [Merge Request](../merge_request_pipelines/index.md). A [Route Map](../review_apps/index.md#route-maps) must be configured. |
|
||||
| `CI_MERGE_REQUEST_CHANGED_PAGE_URLS` | 12.9 | all | Comma-separated list of URLs of changed pages in a deployed [Review App](../review_apps/index.md) for a [Merge Request](../merge_request_pipelines/index.md). A [Route Map](../review_apps/index.md#route-maps) must be configured. |
|
||||
|
|
|
@ -171,8 +171,39 @@ Adding or removing a NOT NULL clause (or another constraint) can typically be
|
|||
done without requiring downtime. However, this does require that any application
|
||||
changes are deployed _first_. Thus, changing the constraints of a column should
|
||||
happen in a post-deployment migration.
|
||||
NOTE: Avoid using `change_column` as it produces inefficient query because it re-defines
|
||||
the whole column type. For example, to add a NOT NULL constraint, prefer `change_column_null`
|
||||
|
||||
NOTE: Avoid using `change_column` as it produces an inefficient query because it re-defines
|
||||
the whole column type.
|
||||
|
||||
To add a NOT NULL constraint, use the `add_not_null_constraint` migration helper:
|
||||
|
||||
```ruby
|
||||
# A post-deployment migration in db/post_migrate
|
||||
class AddNotNull < ActiveRecord::Migration[4.2]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_not_null_constraint :users, :username
|
||||
end
|
||||
|
||||
def down
|
||||
remove_not_null_constraint :users, :username
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
If the column to be updated requires cleaning first (e.g. there are `NULL` values), you should:
|
||||
|
||||
1. Add the `NOT NULL` constraint with `validate: false`
|
||||
|
||||
`add_not_null_constraint :users, :username, validate: false`
|
||||
|
||||
1. Clean up the data with a data migration
|
||||
1. Validate the `NOT NULL` constraint with a followup migration
|
||||
|
||||
`validate_not_null_constraint :users, :username`
|
||||
|
||||
## Changing Column Types
|
||||
|
||||
|
|
|
@ -338,3 +338,77 @@ To fix this issue, you can either:
|
|||
|
||||
[Learn more on overriding the SAST template](sast/index.md#overriding-the-sast-template).
|
||||
All the security scanning tools define their stage, so this error can occur with all of them.
|
||||
|
||||
### Getting error message `sast job: config key may not be used with 'rules': only/except`
|
||||
|
||||
When including a security job template like [`SAST`](sast/index.md#overriding-the-sast-template),
|
||||
the following error may occur, depending on your GitLab CI/CD configuration:
|
||||
|
||||
```plaintext
|
||||
Found errors in your .gitlab-ci.yml:
|
||||
|
||||
jobs:sast config key may not be used with `rules`: only/except
|
||||
```
|
||||
|
||||
This error appears when the included job's `rules` configuration has been [overridden](sast/index.md#overriding-the-sast-template)
|
||||
with [the deprecated `only` or `except` syntax.](../../ci/yaml/README.md#onlyexcept-basic)
|
||||
To fix this issue, you must either:
|
||||
|
||||
- [Transition your `only/except` syntax to `rules`](#transitioning-your-onlyexcept-syntax-to-rules).
|
||||
- (Temporarily) [Pin your templates to the deprecated versions](#pin-your-templates-to-the-deprecated-versions)
|
||||
|
||||
[Learn more on overriding the SAST template](sast/index.md#overriding-the-sast-template).
|
||||
|
||||
#### Transitioning your `only/except` syntax to `rules`
|
||||
|
||||
When overriding the template to control job execution, previous instances of
|
||||
[`only` or `except`](../../ci/yaml/README.md#onlyexcept-basic) are no longer compatible
|
||||
and must be transitioned to [the `rules` syntax](../../ci/yaml/README.md#rules).
|
||||
|
||||
If your override is aimed at limiting jobs to only run on `master`, the previous syntax
|
||||
would look similar to:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- template: SAST.gitlab-ci.yml
|
||||
|
||||
# Ensure that the scanning is only executed on master or merge requests
|
||||
spotbugs-sast:
|
||||
only:
|
||||
refs:
|
||||
- master
|
||||
- merge_requests
|
||||
```
|
||||
|
||||
To transition the above configuration to the new `rules` syntax, the override
|
||||
would be written as follows:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- template: SAST.gitlab-ci.yml
|
||||
|
||||
# Ensure that the scanning is only executed on master or merge requests
|
||||
spotbugs-sast:
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "master"
|
||||
- if: $CI_MERGE_REQUEST_ID
|
||||
```
|
||||
|
||||
[Learn more on the usage of `rules`](../../ci/yaml/README.md#rules).
|
||||
|
||||
#### Pin your templates to the deprecated versions
|
||||
|
||||
To ensure the latest support, we **strongly** recommend that you migrate to [`rules`](../../ci/yaml/README.md#rules).
|
||||
|
||||
If you're unable to immediately update your CI configuration, there are several workarounds that
|
||||
involve pinning to the previous template versions, for example:
|
||||
|
||||
```yaml
|
||||
include:
|
||||
remote: 'https://gitlab.com/gitlab-org/gitlab/-/raw/12-10-stable-ee/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml'
|
||||
```
|
||||
|
||||
Additionally, we provide a dedicated project containing the versioned legacy templates.
|
||||
This can be useful for offline setups or anyone wishing to use [Auto DevOps](../../topics/autodevops/index.md)..
|
||||
|
||||
Instructions are available in the [legacy template project](https://gitlab.com/gitlab-org/auto-devops-v12-10).
|
||||
|
|
|
@ -6,7 +6,7 @@ module Gitlab
|
|||
module Accessibility
|
||||
class Pa11y
|
||||
def parse!(json_data, accessibility_report)
|
||||
root = Gitlab::Json.parse(json_data)
|
||||
root = Gitlab::Json.parse(json_data).with_indifferent_access
|
||||
|
||||
parse_all(root, accessibility_report)
|
||||
rescue JSON::ParserError => e
|
||||
|
|
|
@ -265,12 +265,19 @@ module Gitlab
|
|||
# or `RESET ALL` is executed
|
||||
def disable_statement_timeout
|
||||
if block_given?
|
||||
begin
|
||||
execute('SET statement_timeout TO 0')
|
||||
|
||||
if statement_timeout_disabled?
|
||||
# Don't do anything if the statement_timeout is already disabled
|
||||
# Allows for nested calls of disable_statement_timeout without
|
||||
# resetting the timeout too early (before the outer call ends)
|
||||
yield
|
||||
ensure
|
||||
execute('RESET ALL')
|
||||
else
|
||||
begin
|
||||
execute('SET statement_timeout TO 0')
|
||||
|
||||
yield
|
||||
ensure
|
||||
execute('RESET ALL')
|
||||
end
|
||||
end
|
||||
else
|
||||
unless transaction_open?
|
||||
|
@ -495,7 +502,7 @@ module Gitlab
|
|||
update_column_in_batches(table, column, default_after_type_cast, &block)
|
||||
end
|
||||
|
||||
change_column_null(table, column, false) unless allow_null
|
||||
add_not_null_constraint(table, column) unless allow_null
|
||||
# We want to rescue _all_ exceptions here, even those that don't inherit
|
||||
# from StandardError.
|
||||
rescue Exception => error # rubocop: disable all
|
||||
|
@ -1334,12 +1341,73 @@ into similar problems in the future (e.g. when new tables are created).
|
|||
check_constraint_exists?(table, text_limit_name(table, column, name: constraint_name))
|
||||
end
|
||||
|
||||
# Migration Helpers for managing not null constraints
|
||||
def add_not_null_constraint(table, column, constraint_name: nil, validate: true)
|
||||
if column_is_nullable?(table, column)
|
||||
add_check_constraint(
|
||||
table,
|
||||
"#{column} IS NOT NULL",
|
||||
not_null_constraint_name(table, column, name: constraint_name),
|
||||
validate: validate
|
||||
)
|
||||
else
|
||||
warning_message = <<~MESSAGE
|
||||
NOT NULL check constraint was not created:
|
||||
column #{table}.#{column} is already defined as `NOT NULL`
|
||||
MESSAGE
|
||||
|
||||
Rails.logger.warn warning_message
|
||||
end
|
||||
end
|
||||
|
||||
def validate_not_null_constraint(table, column, constraint_name: nil)
|
||||
validate_check_constraint(
|
||||
table,
|
||||
not_null_constraint_name(table, column, name: constraint_name)
|
||||
)
|
||||
end
|
||||
|
||||
def remove_not_null_constraint(table, column, constraint_name: nil)
|
||||
remove_check_constraint(
|
||||
table,
|
||||
not_null_constraint_name(table, column, name: constraint_name)
|
||||
)
|
||||
end
|
||||
|
||||
def check_not_null_constraint_exists?(table, column, constraint_name: nil)
|
||||
check_constraint_exists?(
|
||||
table,
|
||||
not_null_constraint_name(table, column, name: constraint_name)
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def statement_timeout_disabled?
|
||||
# This is a string of the form "100ms" or "0" when disabled
|
||||
connection.select_value('SHOW statement_timeout') == "0"
|
||||
end
|
||||
|
||||
def column_is_nullable?(table, column)
|
||||
# Check if table.column has not been defined with NOT NULL
|
||||
check_sql = <<~SQL
|
||||
SELECT c.is_nullable
|
||||
FROM information_schema.columns c
|
||||
WHERE c.table_name = '#{table}'
|
||||
AND c.column_name = '#{column}'
|
||||
SQL
|
||||
|
||||
connection.select_value(check_sql) == 'YES'
|
||||
end
|
||||
|
||||
def text_limit_name(table, column, name: nil)
|
||||
name.presence || check_constraint_name(table, column, 'max_length')
|
||||
end
|
||||
|
||||
def not_null_constraint_name(table, column, name: nil)
|
||||
name.presence || check_constraint_name(table, column, 'not_null')
|
||||
end
|
||||
|
||||
def missing_schema_object_message(table, type, name)
|
||||
<<~MESSAGE
|
||||
Could not find #{type} "#{name}" on table "#{table}" which was referenced during the migration.
|
||||
|
@ -1383,7 +1451,7 @@ into similar problems in the future (e.g. when new tables are created).
|
|||
|
||||
update_column_in_batches(table, new, Arel::Table.new(table)[old], batch_column_name: batch_column_name)
|
||||
|
||||
change_column_null(table, new, false) unless old_col.null
|
||||
add_not_null_constraint(table, new) unless old_col.null
|
||||
|
||||
copy_indexes(table, old, new)
|
||||
copy_foreign_keys(table, old, new)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
context 'Create' do
|
||||
describe 'Gitaly repository storage', :orchestrated, :repository_storage, :requires_admin, quarantine: { type: :new } do
|
||||
context 'Create', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/217002', type: :investigating } do
|
||||
describe 'Gitaly repository storage', :orchestrated, :repository_storage, :requires_admin do
|
||||
let(:user) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) }
|
||||
let(:parent_project) do
|
||||
Resource::Project.fabricate_via_api! do |project|
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
context 'Create', quarantine: { type: :new } do
|
||||
context 'Create' do
|
||||
describe 'Review a merge request in Web IDE' do
|
||||
let(:new_file) { 'awesome_new_file.txt' }
|
||||
let(:original_text) { 'Text' }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
context 'Release', :docker, quarantine: { type: :new } do
|
||||
context 'Release', :docker do
|
||||
describe 'Parent-child pipelines dependent relationship' do
|
||||
let!(:project) do
|
||||
Resource::Project.fabricate_via_api! do |project|
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
context 'Release', :docker, quarantine: { type: :new } do
|
||||
context 'Release', :docker do
|
||||
describe 'Parent-child pipelines independent relationship' do
|
||||
let!(:project) do
|
||||
Resource::Project.fabricate_via_api! do |project|
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
context 'Configure' do
|
||||
describe 'Kubernetes Cluster Integration', :orchestrated, :kubernetes, :requires_admin, quarantine: { type: :new } do
|
||||
context 'Configure', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/209085', type: :investigating } do
|
||||
describe 'Kubernetes Cluster Integration', :orchestrated, :kubernetes, :requires_admin do
|
||||
context 'Project Clusters' do
|
||||
let(:cluster) { Service::KubernetesCluster.new(provider_class: Service::ClusterProvider::K3s).create! }
|
||||
let(:project) do
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module QA
|
||||
context 'Monitor' do
|
||||
describe 'Dashboards', :orchestrated, :kubernetes, quarantine: { type: :new } do
|
||||
describe 'Dashboards', :orchestrated, :kubernetes, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29262', type: :waiting_on } do
|
||||
before(:all) do
|
||||
@cluster = Service::KubernetesCluster.new.create!
|
||||
Flow::Login.sign_in
|
||||
|
|
40
spec/fixtures/api/schemas/entities/accessibility_error.json
vendored
Normal file
40
spec/fixtures/api/schemas/entities/accessibility_error.json
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"code",
|
||||
"type",
|
||||
"type_code",
|
||||
"message",
|
||||
"context",
|
||||
"selector",
|
||||
"runner",
|
||||
"runner_extras"
|
||||
],
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"type_code": {
|
||||
"type": "integer"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"context": {
|
||||
"type": "string"
|
||||
},
|
||||
"selector": {
|
||||
"type": "string"
|
||||
},
|
||||
"runner": {
|
||||
"type": "string"
|
||||
},
|
||||
"runner_extras": {
|
||||
"type": ["object", "null"]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
43
spec/fixtures/api/schemas/entities/accessibility_reports_comparer.json
vendored
Normal file
43
spec/fixtures/api/schemas/entities/accessibility_reports_comparer.json
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"type": "object",
|
||||
"required": ["status", "summary", "new_errors", "resolved_errors", "existing_errors"],
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"summary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"total": {
|
||||
"type": "integer"
|
||||
},
|
||||
"resolved": {
|
||||
"type": "integer"
|
||||
},
|
||||
"errored": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": ["total", "resolved", "errored"]
|
||||
},
|
||||
"new_errors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "accessibility_error.json"
|
||||
}
|
||||
},
|
||||
"resolved_errors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "accessibility_error.json"
|
||||
}
|
||||
},
|
||||
"existing_errors": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "accessibility_error.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
|
@ -234,6 +234,90 @@ describe('Dashboard', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('when the panel is expanded', () => {
|
||||
let group;
|
||||
let panel;
|
||||
|
||||
const expandPanel = (mockGroup, mockPanel) => {
|
||||
store.commit(`monitoringDashboard/${types.SET_EXPANDED_PANEL}`, {
|
||||
group: mockGroup,
|
||||
panel: mockPanel,
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
setupStoreWithData(store);
|
||||
|
||||
const { panelGroups } = store.state.monitoringDashboard.dashboard;
|
||||
group = panelGroups[0].group;
|
||||
[panel] = panelGroups[0].panels;
|
||||
|
||||
jest.spyOn(window.history, 'pushState').mockImplementation();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.history.pushState.mockRestore();
|
||||
});
|
||||
|
||||
it('URL is updated with panel parameters', () => {
|
||||
createMountedWrapper({ hasMetrics: true });
|
||||
expandPanel(group, panel);
|
||||
|
||||
const expectedSearch = objectToQuery({
|
||||
group,
|
||||
title: panel.title,
|
||||
y_label: panel.y_label,
|
||||
});
|
||||
|
||||
return wrapper.vm.$nextTick(() => {
|
||||
expect(window.history.pushState).toHaveBeenCalledTimes(1);
|
||||
expect(window.history.pushState).toHaveBeenCalledWith(
|
||||
expect.anything(), // state
|
||||
expect.any(String), // document title
|
||||
expect.stringContaining(`?${expectedSearch}`),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('URL is updated with panel parameters and custom dashboard', () => {
|
||||
const dashboard = 'dashboard.yml';
|
||||
|
||||
createMountedWrapper({ hasMetrics: true, currentDashboard: dashboard });
|
||||
expandPanel(group, panel);
|
||||
|
||||
const expectedSearch = objectToQuery({
|
||||
dashboard,
|
||||
group,
|
||||
title: panel.title,
|
||||
y_label: panel.y_label,
|
||||
});
|
||||
|
||||
return wrapper.vm.$nextTick(() => {
|
||||
expect(window.history.pushState).toHaveBeenCalledTimes(1);
|
||||
expect(window.history.pushState).toHaveBeenCalledWith(
|
||||
expect.anything(), // state
|
||||
expect.any(String), // document title
|
||||
expect.stringContaining(`?${expectedSearch}`),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('URL is updated with no parameters', () => {
|
||||
expandPanel(group, panel);
|
||||
createMountedWrapper({ hasMetrics: true });
|
||||
expandPanel(null, null);
|
||||
|
||||
return wrapper.vm.$nextTick(() => {
|
||||
expect(window.history.pushState).toHaveBeenCalledTimes(1);
|
||||
expect(window.history.pushState).toHaveBeenCalledWith(
|
||||
expect.anything(), // state
|
||||
expect.any(String), // document title
|
||||
expect.not.stringContaining('?'), // no params
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when all requests have been commited by the store', () => {
|
||||
beforeEach(() => {
|
||||
createMountedWrapper({ hasMetrics: true });
|
||||
|
|
|
@ -274,9 +274,10 @@ describe('monitoring/utils', () => {
|
|||
const [panelGroup] = metricsDashboardViewModel.panelGroups;
|
||||
const [panel] = panelGroup.panels;
|
||||
|
||||
const getUrlParams = url => urlUtils.queryToObject(url.split('?')[1]);
|
||||
|
||||
it('returns URL for a panel when query parameters are given', () => {
|
||||
const [, query] = panelToUrl(dashboard, panelGroup.group, panel).split('?');
|
||||
const params = urlUtils.queryToObject(query);
|
||||
const params = getUrlParams(panelToUrl(dashboard, panelGroup.group, panel));
|
||||
|
||||
expect(params).toEqual({
|
||||
dashboard,
|
||||
|
@ -286,12 +287,14 @@ describe('monitoring/utils', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('returns `null` if group is missing', () => {
|
||||
expect(panelToUrl(dashboard, null, panel)).toBe(null);
|
||||
it('returns a dashboard only URL if group is missing', () => {
|
||||
const params = getUrlParams(panelToUrl(dashboard, null, panel));
|
||||
expect(params).toEqual({ dashboard: 'metrics.yml' });
|
||||
});
|
||||
|
||||
it('returns `null` if panel is missing', () => {
|
||||
expect(panelToUrl(dashboard, panelGroup.group, null)).toBe(null);
|
||||
it('returns a dashboard only URL if panel is missing', () => {
|
||||
const params = getUrlParams(panelToUrl(dashboard, panelGroup.group, null));
|
||||
expect(params).toEqual({ dashboard: 'metrics.yml' });
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -88,6 +88,7 @@ describe Gitlab::Ci::Parsers::Accessibility::Pa11y do
|
|||
expect(accessibility_report.passes_count).to eq(0)
|
||||
expect(accessibility_report.scans_count).to eq(1)
|
||||
expect(accessibility_report.urls['https://about.gitlab.com/']).to be_present
|
||||
expect(accessibility_report.urls['https://about.gitlab.com/'].first[:code]).to be_present
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -217,9 +217,10 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
it 'appends ON DELETE SET NULL statement' do
|
||||
expect(model).to receive(:with_lock_retries).and_call_original
|
||||
expect(model).to receive(:disable_statement_timeout).and_call_original
|
||||
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
|
||||
expect(model).to receive(:execute).with(/statement_timeout/)
|
||||
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
|
||||
expect(model).to receive(:execute).with(/RESET ALL/)
|
||||
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
|
||||
|
||||
expect(model).to receive(:execute).with(/ON DELETE SET NULL/)
|
||||
|
||||
|
@ -233,9 +234,10 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
it 'appends ON DELETE CASCADE statement' do
|
||||
expect(model).to receive(:with_lock_retries).and_call_original
|
||||
expect(model).to receive(:disable_statement_timeout).and_call_original
|
||||
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
|
||||
expect(model).to receive(:execute).with(/statement_timeout/)
|
||||
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
|
||||
expect(model).to receive(:execute).with(/RESET ALL/)
|
||||
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
|
||||
|
||||
expect(model).to receive(:execute).with(/ON DELETE CASCADE/)
|
||||
|
||||
|
@ -249,9 +251,10 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
it 'appends no ON DELETE statement' do
|
||||
expect(model).to receive(:with_lock_retries).and_call_original
|
||||
expect(model).to receive(:disable_statement_timeout).and_call_original
|
||||
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
|
||||
expect(model).to receive(:execute).with(/statement_timeout/)
|
||||
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
|
||||
expect(model).to receive(:execute).with(/RESET ALL/)
|
||||
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
|
||||
|
||||
expect(model).not_to receive(:execute).with(/ON DELETE/)
|
||||
|
||||
|
@ -266,10 +269,11 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
it 'creates a concurrent foreign key and validates it' do
|
||||
expect(model).to receive(:with_lock_retries).and_call_original
|
||||
expect(model).to receive(:disable_statement_timeout).and_call_original
|
||||
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
|
||||
expect(model).to receive(:execute).with(/statement_timeout/)
|
||||
expect(model).to receive(:execute).ordered.with(/NOT VALID/)
|
||||
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
|
||||
expect(model).to receive(:execute).with(/RESET ALL/)
|
||||
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
|
||||
|
||||
model.add_concurrent_foreign_key(:projects, :users, column: :user_id)
|
||||
end
|
||||
|
@ -293,10 +297,11 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
it 'creates a new foreign key' do
|
||||
expect(model).to receive(:with_lock_retries).and_call_original
|
||||
expect(model).to receive(:disable_statement_timeout).and_call_original
|
||||
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
|
||||
expect(model).to receive(:execute).with(/statement_timeout/)
|
||||
expect(model).to receive(:execute).ordered.with(/NOT VALID/)
|
||||
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT.+foo/)
|
||||
expect(model).to receive(:execute).with(/RESET ALL/)
|
||||
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
|
||||
|
||||
model.add_concurrent_foreign_key(:projects, :users, column: :user_id, name: :foo)
|
||||
end
|
||||
|
@ -321,10 +326,11 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
it 'creates a new foreign key' do
|
||||
expect(model).to receive(:with_lock_retries).and_call_original
|
||||
expect(model).to receive(:disable_statement_timeout).and_call_original
|
||||
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
|
||||
expect(model).to receive(:execute).with(/statement_timeout/)
|
||||
expect(model).to receive(:execute).ordered.with(/NOT VALID/)
|
||||
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT.+bar/)
|
||||
expect(model).to receive(:execute).with(/RESET ALL/)
|
||||
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
|
||||
|
||||
model.add_concurrent_foreign_key(:projects, :users, column: :user_id, name: :bar)
|
||||
end
|
||||
|
@ -361,6 +367,7 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
aggregate_failures do
|
||||
expect(model).not_to receive(:concurrent_foreign_key_name)
|
||||
expect(model).to receive(:disable_statement_timeout).and_call_original
|
||||
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
|
||||
expect(model).to receive(:execute).with(/statement_timeout/)
|
||||
expect(model).to receive(:execute).ordered.with(/ALTER TABLE projects VALIDATE CONSTRAINT/)
|
||||
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
|
||||
|
@ -377,6 +384,7 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
aggregate_failures do
|
||||
expect(model).to receive(:concurrent_foreign_key_name)
|
||||
expect(model).to receive(:disable_statement_timeout).and_call_original
|
||||
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
|
||||
expect(model).to receive(:execute).with(/statement_timeout/)
|
||||
expect(model).to receive(:execute).ordered.with(/ALTER TABLE projects VALIDATE CONSTRAINT/)
|
||||
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
|
||||
|
@ -527,6 +535,26 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This spec runs without an enclosing transaction (:delete truncation method for db_cleaner)
|
||||
context 'when the statement_timeout is already disabled', :delete do
|
||||
before do
|
||||
ActiveRecord::Base.connection.execute('SET statement_timeout TO 0')
|
||||
end
|
||||
|
||||
after do
|
||||
# Use ActiveRecord::Base.connection instead of model.execute
|
||||
# so that this call is not counted below
|
||||
ActiveRecord::Base.connection.execute('RESET ALL')
|
||||
end
|
||||
|
||||
it 'yields control without disabling the timeout or resetting' do
|
||||
expect(model).not_to receive(:execute).with('SET statement_timeout TO 0')
|
||||
expect(model).not_to receive(:execute).with('RESET ALL')
|
||||
|
||||
expect { |block| model.disable_statement_timeout(&block) }.to yield_control
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#true_value' do
|
||||
|
@ -619,7 +647,7 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
expect(model).to receive(:update_column_in_batches)
|
||||
.with(:projects, :foo, 10)
|
||||
|
||||
expect(model).not_to receive(:change_column_null)
|
||||
expect(model).not_to receive(:add_not_null_constraint)
|
||||
|
||||
model.add_column_with_default(:projects, :foo, :integer,
|
||||
default: 10,
|
||||
|
@ -630,8 +658,8 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
expect(model).to receive(:update_column_in_batches)
|
||||
.with(:projects, :foo, 10)
|
||||
|
||||
expect(model).to receive(:change_column_null)
|
||||
.with(:projects, :foo, false)
|
||||
expect(model).to receive(:add_not_null_constraint)
|
||||
.with(:projects, :foo)
|
||||
|
||||
model.add_column_with_default(:projects, :foo, :integer, default: 10)
|
||||
end
|
||||
|
@ -650,16 +678,16 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
end
|
||||
|
||||
it 'removes the added column whenever changing a column NULL constraint fails' do
|
||||
expect(model).to receive(:change_column_null)
|
||||
.with(:projects, :foo, false)
|
||||
.and_raise(RuntimeError)
|
||||
expect(model).to receive(:add_not_null_constraint)
|
||||
.with(:projects, :foo)
|
||||
.and_raise(ActiveRecord::ActiveRecordError)
|
||||
|
||||
expect(model).to receive(:remove_column)
|
||||
.with(:projects, :foo)
|
||||
|
||||
expect do
|
||||
model.add_column_with_default(:projects, :foo, :integer, default: 10)
|
||||
end.to raise_error(RuntimeError)
|
||||
end.to raise_error(ActiveRecord::ActiveRecordError)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -671,7 +699,7 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
allow(model).to receive(:transaction).and_yield
|
||||
allow(model).to receive(:column_for).with(:user_details, :foo).and_return(column)
|
||||
allow(model).to receive(:update_column_in_batches).with(:user_details, :foo, 10, batch_column_name: :user_id)
|
||||
allow(model).to receive(:change_column_null).with(:user_details, :foo, false)
|
||||
allow(model).to receive(:add_not_null_constraint).with(:user_details, :foo)
|
||||
allow(model).to receive(:change_column_default).with(:user_details, :foo, 10)
|
||||
|
||||
expect(model).to receive(:add_column)
|
||||
|
@ -693,7 +721,7 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
allow(model).to receive(:transaction).and_yield
|
||||
allow(model).to receive(:column_for).with(:projects, :foo).and_return(column)
|
||||
allow(model).to receive(:update_column_in_batches).with(:projects, :foo, 10)
|
||||
allow(model).to receive(:change_column_null).with(:projects, :foo, false)
|
||||
allow(model).to receive(:add_not_null_constraint).with(:projects, :foo)
|
||||
allow(model).to receive(:change_column_default).with(:projects, :foo, 10)
|
||||
|
||||
expect(model).to receive(:add_column)
|
||||
|
@ -782,7 +810,7 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
|
||||
expect(model).to receive(:update_column_in_batches)
|
||||
|
||||
expect(model).to receive(:change_column_null).with(:users, :new, false)
|
||||
expect(model).to receive(:add_not_null_constraint).with(:users, :new)
|
||||
|
||||
expect(model).to receive(:copy_indexes).with(:users, :old, :new)
|
||||
expect(model).to receive(:copy_foreign_keys).with(:users, :old, :new)
|
||||
|
@ -915,7 +943,7 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
|
||||
expect(model).to receive(:update_column_in_batches)
|
||||
|
||||
expect(model).to receive(:change_column_null).with(:users, :old, false)
|
||||
expect(model).to receive(:add_not_null_constraint).with(:users, :old)
|
||||
|
||||
expect(model).to receive(:copy_indexes).with(:users, :new, :old)
|
||||
expect(model).to receive(:copy_foreign_keys).with(:users, :new, :old)
|
||||
|
@ -2225,6 +2253,7 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
.and_return(false).exactly(1)
|
||||
|
||||
expect(model).to receive(:disable_statement_timeout).and_call_original
|
||||
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
|
||||
expect(model).to receive(:execute).with(/statement_timeout/)
|
||||
expect(model).to receive(:with_lock_retries).and_call_original
|
||||
expect(model).to receive(:execute).with(/ADD CONSTRAINT check_name_not_null/)
|
||||
|
@ -2268,6 +2297,7 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
.and_return(false).exactly(1)
|
||||
|
||||
expect(model).to receive(:disable_statement_timeout).and_call_original
|
||||
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
|
||||
expect(model).to receive(:execute).with(/statement_timeout/)
|
||||
expect(model).to receive(:with_lock_retries).and_call_original
|
||||
expect(model).to receive(:execute).with(/ADD CONSTRAINT check_name_not_null/)
|
||||
|
@ -2309,6 +2339,7 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
|
||||
expect(model).to receive(:check_constraint_exists?).and_return(true)
|
||||
expect(model).to receive(:disable_statement_timeout).and_call_original
|
||||
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
|
||||
expect(model).to receive(:execute).with(/statement_timeout/)
|
||||
expect(model).to receive(:execute).ordered.with(validate_sql)
|
||||
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
|
||||
|
@ -2448,4 +2479,135 @@ describe Gitlab::Database::MigrationHelpers do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#add_not_null_constraint' do
|
||||
context 'when it is called with the default options' do
|
||||
it 'calls add_check_constraint with an infered constraint name and validate: true' do
|
||||
constraint_name = model.check_constraint_name(:test_table,
|
||||
:name,
|
||||
'not_null')
|
||||
check = "name IS NOT NULL"
|
||||
|
||||
expect(model).to receive(:column_is_nullable?).and_return(true)
|
||||
expect(model).to receive(:check_constraint_name).and_call_original
|
||||
expect(model).to receive(:add_check_constraint)
|
||||
.with(:test_table, check, constraint_name, validate: true)
|
||||
|
||||
model.add_not_null_constraint(:test_table, :name)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when all parameters are provided' do
|
||||
it 'calls add_check_constraint with the correct parameters' do
|
||||
constraint_name = 'check_name_not_null'
|
||||
check = "name IS NOT NULL"
|
||||
|
||||
expect(model).to receive(:column_is_nullable?).and_return(true)
|
||||
expect(model).not_to receive(:check_constraint_name)
|
||||
expect(model).to receive(:add_check_constraint)
|
||||
.with(:test_table, check, constraint_name, validate: false)
|
||||
|
||||
model.add_not_null_constraint(
|
||||
:test_table,
|
||||
:name,
|
||||
constraint_name: constraint_name,
|
||||
validate: false
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the column is defined as NOT NULL' do
|
||||
it 'does not add a check constraint' do
|
||||
expect(model).to receive(:column_is_nullable?).and_return(false)
|
||||
expect(model).not_to receive(:check_constraint_name)
|
||||
expect(model).not_to receive(:add_check_constraint)
|
||||
|
||||
model.add_not_null_constraint(:test_table, :name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#validate_not_null_constraint' do
|
||||
context 'when constraint_name is not provided' do
|
||||
it 'calls validate_check_constraint with an infered constraint name' do
|
||||
constraint_name = model.check_constraint_name(:test_table,
|
||||
:name,
|
||||
'not_null')
|
||||
|
||||
expect(model).to receive(:check_constraint_name).and_call_original
|
||||
expect(model).to receive(:validate_check_constraint)
|
||||
.with(:test_table, constraint_name)
|
||||
|
||||
model.validate_not_null_constraint(:test_table, :name)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when constraint_name is provided' do
|
||||
it 'calls validate_check_constraint with the correct parameters' do
|
||||
constraint_name = 'check_name_not_null'
|
||||
|
||||
expect(model).not_to receive(:check_constraint_name)
|
||||
expect(model).to receive(:validate_check_constraint)
|
||||
.with(:test_table, constraint_name)
|
||||
|
||||
model.validate_not_null_constraint(:test_table, :name, constraint_name: constraint_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#remove_not_null_constraint' do
|
||||
context 'when constraint_name is not provided' do
|
||||
it 'calls remove_check_constraint with an infered constraint name' do
|
||||
constraint_name = model.check_constraint_name(:test_table,
|
||||
:name,
|
||||
'not_null')
|
||||
|
||||
expect(model).to receive(:check_constraint_name).and_call_original
|
||||
expect(model).to receive(:remove_check_constraint)
|
||||
.with(:test_table, constraint_name)
|
||||
|
||||
model.remove_not_null_constraint(:test_table, :name)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when constraint_name is provided' do
|
||||
it 'calls remove_check_constraint with the correct parameters' do
|
||||
constraint_name = 'check_name_not_null'
|
||||
|
||||
expect(model).not_to receive(:check_constraint_name)
|
||||
expect(model).to receive(:remove_check_constraint)
|
||||
.with(:test_table, constraint_name)
|
||||
|
||||
model.remove_not_null_constraint(:test_table, :name, constraint_name: constraint_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#check_not_null_constraint_exists?' do
|
||||
context 'when constraint_name is not provided' do
|
||||
it 'calls check_constraint_exists? with an infered constraint name' do
|
||||
constraint_name = model.check_constraint_name(:test_table,
|
||||
:name,
|
||||
'not_null')
|
||||
|
||||
expect(model).to receive(:check_constraint_name).and_call_original
|
||||
expect(model).to receive(:check_constraint_exists?)
|
||||
.with(:test_table, constraint_name)
|
||||
|
||||
model.check_not_null_constraint_exists?(:test_table, :name)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when constraint_name is provided' do
|
||||
it 'calls check_constraint_exists? with the correct parameters' do
|
||||
constraint_name = 'check_name_not_null'
|
||||
|
||||
expect(model).not_to receive(:check_constraint_name)
|
||||
expect(model).to receive(:check_constraint_exists?)
|
||||
.with(:test_table, constraint_name)
|
||||
|
||||
model.check_not_null_constraint_exists?(:test_table, :name, constraint_name: constraint_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -719,6 +719,28 @@ describe Ci::Pipeline, :mailer do
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'variable CI_KUBERNETES_ACTIVE' do
|
||||
context 'when pipeline.has_kubernetes_active? is true' do
|
||||
before do
|
||||
allow(pipeline).to receive(:has_kubernetes_active?).and_return(true)
|
||||
end
|
||||
|
||||
it "is incldued with value 'true'" do
|
||||
expect(subject.to_hash).to include('CI_KUBERNETES_ACTIVE' => 'true')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pipeline.has_kubernetes_active? is false' do
|
||||
before do
|
||||
allow(pipeline).to receive(:has_kubernetes_active?).and_return(false)
|
||||
end
|
||||
|
||||
it 'is not included' do
|
||||
expect(subject.to_hash).not_to have_key('CI_KUBERNETES_ACTIVE')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#protected_ref?' do
|
||||
|
|
37
spec/serializers/accessibility_error_entity_spec.rb
Normal file
37
spec/serializers/accessibility_error_entity_spec.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe AccessibilityErrorEntity do
|
||||
let(:entity) { described_class.new(accessibility_error) }
|
||||
|
||||
describe '#as_json' do
|
||||
subject { entity.as_json }
|
||||
|
||||
context 'when accessibility contains an error' do
|
||||
let(:accessibility_error) do
|
||||
{
|
||||
code: "WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent",
|
||||
type: "error",
|
||||
typeCode: 1,
|
||||
message: "Anchor element found with a valid href attribute, but no link content has been supplied.",
|
||||
context: "<a href=\"/\" class=\"navbar-brand animated\"><svg height=\"36\" viewBox=\"0 0 1...</a>",
|
||||
selector: "#main-nav > div:nth-child(1) > a",
|
||||
runner: "htmlcs",
|
||||
runnerExtras: {}
|
||||
}
|
||||
end
|
||||
|
||||
it 'contains correct accessibility error details', :aggregate_failures do
|
||||
expect(subject[:code]).to eq("WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent")
|
||||
expect(subject[:type]).to eq("error")
|
||||
expect(subject[:type_code]).to eq(1)
|
||||
expect(subject[:message]).to eq("Anchor element found with a valid href attribute, but no link content has been supplied.")
|
||||
expect(subject[:context]).to eq("<a href=\"/\" class=\"navbar-brand animated\"><svg height=\"36\" viewBox=\"0 0 1...</a>")
|
||||
expect(subject[:selector]).to eq("#main-nav > div:nth-child(1) > a")
|
||||
expect(subject[:runner]).to eq("htmlcs")
|
||||
expect(subject[:runner_extras]).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,87 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe AccessibilityReportsComparerEntity do
|
||||
let(:entity) { described_class.new(comparer) }
|
||||
let(:comparer) { Gitlab::Ci::Reports::AccessibilityReportsComparer.new(base_report, head_report) }
|
||||
let(:base_report) { Gitlab::Ci::Reports::AccessibilityReports.new }
|
||||
let(:head_report) { Gitlab::Ci::Reports::AccessibilityReports.new }
|
||||
let(:url) { "https://gitlab.com" }
|
||||
let(:single_error) do
|
||||
[
|
||||
{
|
||||
code: "WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent",
|
||||
type: "error",
|
||||
typeCode: 1,
|
||||
message: "Anchor element found with a valid href attribute, but no link content has been supplied.",
|
||||
context: "<a href=\"/\" class=\"navbar-brand animated\"><svg height=\"36\" viewBox=\"0 0 1...</a>",
|
||||
selector: "#main-nav > div:nth-child(1) > a",
|
||||
runner: "htmlcs",
|
||||
runnerExtras: {}
|
||||
}
|
||||
]
|
||||
end
|
||||
let(:different_error) do
|
||||
[
|
||||
{
|
||||
code: "WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail",
|
||||
type: "error",
|
||||
typeCode: 1,
|
||||
message: "This element has insufficient contrast at this conformance level.",
|
||||
context: "<a href=\"/stages-devops-lifecycle/\" class=\"main-nav-link\">Product</a>",
|
||||
selector: "#main-nav > div:nth-child(2) > ul > li:nth-child(1) > a",
|
||||
runner: "htmlcs",
|
||||
runnerExtras: {}
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
describe '#as_json' do
|
||||
subject { entity.as_json }
|
||||
|
||||
context 'when base report has error and head has a different error' do
|
||||
before do
|
||||
base_report.add_url(url, single_error)
|
||||
head_report.add_url(url, different_error)
|
||||
end
|
||||
|
||||
it 'contains correct compared accessibility report details', :aggregate_failures do
|
||||
expect(subject[:status]).to eq(Gitlab::Ci::Reports::AccessibilityReportsComparer::STATUS_FAILED)
|
||||
expect(subject[:resolved_errors].first).to include(:code, :type, :type_code, :message, :context, :selector, :runner, :runner_extras)
|
||||
expect(subject[:new_errors].first).to include(:code, :type, :type_code, :message, :context, :selector, :runner, :runner_extras)
|
||||
expect(subject[:existing_errors].first).to include(:code, :type, :type_code, :message, :context, :selector, :runner, :runner_extras)
|
||||
expect(subject[:summary]).to include(total: 2, resolved: 1, errored: 1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when base report has error and head has the same error' do
|
||||
before do
|
||||
base_report.add_url(url, single_error)
|
||||
head_report.add_url(url, single_error)
|
||||
end
|
||||
|
||||
it 'contains correct compared accessibility report details', :aggregate_failures do
|
||||
expect(subject[:status]).to eq(Gitlab::Ci::Reports::AccessibilityReportsComparer::STATUS_FAILED)
|
||||
expect(subject[:new_errors]).to be_empty
|
||||
expect(subject[:resolved_errors]).to be_empty
|
||||
expect(subject[:existing_errors].first).to include(:code, :type, :type_code, :message, :context, :selector, :runner, :runner_extras)
|
||||
expect(subject[:summary]).to include(total: 1, resolved: 0, errored: 1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when base report has no error and head has errors' do
|
||||
before do
|
||||
head_report.add_url(url, single_error)
|
||||
end
|
||||
|
||||
it 'contains correct compared accessibility report details', :aggregate_failures do
|
||||
expect(subject[:status]).to eq(Gitlab::Ci::Reports::AccessibilityReportsComparer::STATUS_FAILED)
|
||||
expect(subject[:resolved_errors]).to be_empty
|
||||
expect(subject[:existing_errors]).to be_empty
|
||||
expect(subject[:new_errors].first).to include(:code, :type, :type_code, :message, :context, :selector, :runner, :runner_extras)
|
||||
expect(subject[:summary]).to include(total: 1, resolved: 0, errored: 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,65 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe AccessibilityReportsComparerSerializer do
|
||||
let(:project) { double(:project) }
|
||||
let(:serializer) { described_class.new(project: project).represent(comparer) }
|
||||
let(:comparer) { Gitlab::Ci::Reports::AccessibilityReportsComparer.new(base_report, head_report) }
|
||||
let(:base_report) { Gitlab::Ci::Reports::AccessibilityReports.new }
|
||||
let(:head_report) { Gitlab::Ci::Reports::AccessibilityReports.new }
|
||||
let(:url) { "https://gitlab.com" }
|
||||
let(:single_error) do
|
||||
[
|
||||
{
|
||||
code: "WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent",
|
||||
type: "error",
|
||||
typeCode: 1,
|
||||
message: "Anchor element found with a valid href attribute, but no link content has been supplied.",
|
||||
context: "<a href=\"/\" class=\"navbar-brand animated\"><svg height=\"36\" viewBox=\"0 0 1...</a>",
|
||||
selector: "#main-nav > divnth-child(1) > a",
|
||||
runner: "htmlcs",
|
||||
runnerExtras: {}
|
||||
}
|
||||
]
|
||||
end
|
||||
let(:different_error) do
|
||||
[
|
||||
{
|
||||
code: "WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail",
|
||||
type: "error",
|
||||
typeCode: 1,
|
||||
message: "This element has insufficient contrast at this conformance level.",
|
||||
context: "<a href=\"/stages-devops-lifecycle/\" class=\"main-nav-link\">Product</a>",
|
||||
selector: "#main-nav > divnth-child(2) > ul > linth-child(1) > a",
|
||||
runner: "htmlcs",
|
||||
runnerExtras: {}
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
describe '#to_json' do
|
||||
subject { serializer.as_json }
|
||||
|
||||
context 'when base report has error and head has a different error' do
|
||||
before do
|
||||
base_report.add_url(url, single_error)
|
||||
head_report.add_url(url, different_error)
|
||||
end
|
||||
|
||||
it 'matches the schema' do
|
||||
expect(subject).to match_schema('entities/accessibility_reports_comparer')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when base report has no error and head has errors' do
|
||||
before do
|
||||
head_report.add_url(url, single_error)
|
||||
end
|
||||
|
||||
it 'matches the schema' do
|
||||
expect(subject).to match_schema('entities/accessibility_reports_comparer')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -13,10 +13,11 @@ end
|
|||
RSpec.shared_examples 'performs validation' do |validation_option|
|
||||
it 'performs validation' do
|
||||
expect(model).to receive(:disable_statement_timeout).and_call_original
|
||||
expect(model).to receive(:statement_timeout_disabled?).and_return(false)
|
||||
expect(model).to receive(:execute).with(/statement_timeout/)
|
||||
expect(model).to receive(:execute).ordered.with(/NOT VALID/)
|
||||
expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/)
|
||||
expect(model).to receive(:execute).with(/RESET ALL/)
|
||||
expect(model).to receive(:execute).ordered.with(/RESET ALL/)
|
||||
|
||||
model.add_concurrent_foreign_key(*args, **options.merge(validation_option))
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue