Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
c097ca59a1
commit
bd9b2731b4
|
|
@ -128,7 +128,7 @@ The Geo primary site needs to checksum every replicable so secondaries can verif
|
|||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CreateCoolWidgetStates < Gitlab::Database::Migration[1.0]
|
||||
class CreateCoolWidgetStates < Gitlab::Database::Migration[2.0]
|
||||
VERIFICATION_STATE_INDEX_NAME = "index_cool_widget_states_on_verification_state"
|
||||
PENDING_VERIFICATION_INDEX_NAME = "index_cool_widget_states_pending_verification"
|
||||
FAILED_VERIFICATION_INDEX_NAME = "index_cool_widget_states_failed_verification"
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ The Geo primary site needs to checksum every replicable so secondaries can verif
|
|||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CreateCoolWidgetStates < Gitlab::Database::Migration[1.0]
|
||||
class CreateCoolWidgetStates < Gitlab::Database::Migration[2.0]
|
||||
VERIFICATION_STATE_INDEX_NAME = "index_cool_widget_states_on_verification_state"
|
||||
PENDING_VERIFICATION_INDEX_NAME = "index_cool_widget_states_pending_verification"
|
||||
FAILED_VERIFICATION_INDEX_NAME = "index_cool_widget_states_failed_verification"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import $ from 'jquery';
|
||||
import createFlash, { FLASH_TYPES } from '~/flash';
|
||||
import { VARIANT_DANGER, VARIANT_INFO, createAlert } from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import { Rails } from '~/lib/utils/rails_ujs';
|
||||
|
|
@ -10,7 +10,7 @@ import TimezoneDropdown, {
|
|||
export default class Profile {
|
||||
constructor({ form } = {}) {
|
||||
this.onSubmitForm = this.onSubmitForm.bind(this);
|
||||
this.form = form || $('.edit-user');
|
||||
this.form = form || $('.js-edit-user');
|
||||
this.setRepoRadio();
|
||||
this.bindEvents();
|
||||
this.initAvatarGlCrop();
|
||||
|
|
@ -84,9 +84,9 @@ export default class Profile {
|
|||
this.updateHeaderAvatar();
|
||||
}
|
||||
|
||||
createFlash({
|
||||
createAlert({
|
||||
message: data.message,
|
||||
type: data.status === 'error' ? FLASH_TYPES.ALERT : FLASH_TYPES.NOTICE,
|
||||
variant: data.status === 'error' ? VARIANT_DANGER : VARIANT_INFO,
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
|
|
@ -95,8 +95,9 @@ export default class Profile {
|
|||
self.form.find(':input[disabled]').enable();
|
||||
})
|
||||
.catch((error) =>
|
||||
createFlash({
|
||||
createAlert({
|
||||
message: error.message,
|
||||
variant: VARIANT_DANGER,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ export default {
|
|||
:label="issuableCategoryHeaderText"
|
||||
label-for="linked-issue-type-radio"
|
||||
label-class="label-bold"
|
||||
class="mb-2"
|
||||
class="gl-mb-3"
|
||||
>
|
||||
<gl-form-radio-group
|
||||
id="linked-issue-type-radio"
|
||||
|
|
@ -216,12 +216,12 @@ export default {
|
|||
:disabled="isSubmitButtonDisabled"
|
||||
:loading="isSubmitting"
|
||||
type="submit"
|
||||
class="float-left"
|
||||
class="gl-float-left"
|
||||
data-qa-selector="add_issue_button"
|
||||
>
|
||||
{{ __('Add') }}
|
||||
</gl-button>
|
||||
<gl-button class="float-right" @click="onFormCancel">
|
||||
<gl-button class="gl-float-right" @click="onFormCancel">
|
||||
{{ __('Cancel') }}
|
||||
</gl-button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
.application-theme {
|
||||
$ui-gray-bg: #2e2e2e;
|
||||
$ui-light-gray-bg: #dfdfdf;
|
||||
$ui-gray-bg: #303030;
|
||||
$ui-light-gray-bg: #f0f0f0;
|
||||
$ui-dark-mode-bg: #1f1f1f;
|
||||
|
||||
.preview {
|
||||
|
|
|
|||
|
|
@ -5,9 +5,7 @@
|
|||
- availability = availability_values
|
||||
- custom_emoji = show_status_emoji?(@user.status)
|
||||
|
||||
= gitlab_ui_form_for @user, url: profile_path, method: :put, html: { multipart: true, class: 'edit-user gl-mt-3 js-quick-submit gl-show-field-errors js-password-prompt-form', remote: true }, authenticity_token: true do |f|
|
||||
= form_errors(@user)
|
||||
|
||||
= gitlab_ui_form_for @user, url: profile_path, method: :put, html: { multipart: true, class: 'edit-user js-edit-user gl-mt-3 js-quick-submit gl-show-field-errors js-password-prompt-form', remote: true }, authenticity_token: true do |f|
|
||||
.row.js-search-settings-section
|
||||
.col-lg-4.profile-settings-sidebar
|
||||
%h4.gl-mt-0
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: ci_minutes_cost_factor_for_all_public_projects
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85357
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/359094
|
||||
milestone: '15.0'
|
||||
type: development
|
||||
group: group::pipeline execution
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ChangePublicProjectsCostFactor < Gitlab::Database::Migration[2.0]
|
||||
# This migration updates SaaS Runner cost factors for public projects.
|
||||
# Previously we had a disabled cost factor for public projects, meaning
|
||||
# that no CI minutes were counted by default. With a low cost factor
|
||||
# we count CI minutes consumption at a very low rate to prevent
|
||||
# abuses.
|
||||
disable_ddl_transaction!
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_ci
|
||||
|
||||
DISABLED_COST_FACTOR = 0
|
||||
LOW_COST_FACTOR = 0.008
|
||||
|
||||
class Runner < MigrationRecord
|
||||
self.table_name = 'ci_runners'
|
||||
|
||||
scope :shared, -> { where(runner_type: 1) }
|
||||
end
|
||||
|
||||
def up
|
||||
return unless Gitlab.com?
|
||||
|
||||
Runner.shared.where(public_projects_minutes_cost_factor: DISABLED_COST_FACTOR)
|
||||
.update_all(public_projects_minutes_cost_factor: LOW_COST_FACTOR)
|
||||
end
|
||||
|
||||
def down
|
||||
return unless Gitlab.com?
|
||||
|
||||
Runner.shared.where(public_projects_minutes_cost_factor: LOW_COST_FACTOR)
|
||||
.update_all(public_projects_minutes_cost_factor: DISABLED_COST_FACTOR)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DropTempIndexOnProjectsOnIdAndRunnersToken < Gitlab::Database::Migration[2.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
TEMP_INDEX_NAME = 'tmp_index_projects_on_id_and_runners_token'
|
||||
|
||||
def up
|
||||
finalize_background_migration 'ResetDuplicateCiRunnersTokenValuesOnProjects'
|
||||
|
||||
remove_concurrent_index_by_name :projects, TEMP_INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index :projects,
|
||||
[:id, :runners_token],
|
||||
where: "runners_token IS NOT NULL",
|
||||
unique: false,
|
||||
name: TEMP_INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DropTempIndexOnProjectsOnIdAndRunnersTokenEncrypted < Gitlab::Database::Migration[2.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
TEMP_INDEX_NAME = 'tmp_index_projects_on_id_and_runners_token_encrypted'
|
||||
|
||||
def up
|
||||
finalize_background_migration 'ResetDuplicateCiRunnersTokenEncryptedValuesOnProjects'
|
||||
|
||||
remove_concurrent_index_by_name :projects, TEMP_INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index :projects,
|
||||
[:id, :runners_token_encrypted],
|
||||
where: "runners_token_encrypted IS NOT NULL",
|
||||
unique: false,
|
||||
name: TEMP_INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddUniqueIndexOnProjectsOnRunnersToken < Gitlab::Database::Migration[2.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
INDEX_NAME = 'index_uniq_projects_on_runners_token'
|
||||
|
||||
def up
|
||||
add_concurrent_index :projects,
|
||||
:runners_token,
|
||||
name: INDEX_NAME,
|
||||
unique: true
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :projects, INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddUniqueIndexOnProjectsOnRunnersTokenEncrypted < Gitlab::Database::Migration[2.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
INDEX_NAME = 'index_uniq_projects_on_runners_token_encrypted'
|
||||
|
||||
def up
|
||||
add_concurrent_index :projects,
|
||||
:runners_token_encrypted,
|
||||
name: INDEX_NAME,
|
||||
unique: true
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :projects, INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DropIndexOnProjectsOnRunnersToken < Gitlab::Database::Migration[2.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
INDEX_NAME = 'index_projects_on_runners_token'
|
||||
|
||||
def up
|
||||
remove_concurrent_index_by_name :projects, INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index :projects,
|
||||
:runners_token,
|
||||
name: INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DropIndexOnProjectsOnRunnersTokenEncrypted < Gitlab::Database::Migration[2.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
INDEX_NAME = 'index_projects_on_runners_token_encrypted'
|
||||
|
||||
def up
|
||||
remove_concurrent_index_by_name :projects, INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index :projects,
|
||||
:runners_token_encrypted,
|
||||
name: INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
9c64f9fb286992b6cdad8f7f22084c2d538bccf97e9c649f47284c5828a850e3
|
||||
|
|
@ -0,0 +1 @@
|
|||
247c6cba3cee4413a17193aeebd77eae79a7ced17a5a2b785f0ecd682e823c02
|
||||
|
|
@ -0,0 +1 @@
|
|||
d72ffd09437a576edb2d046963e8d004c5a2b13586f7318361fea6d673f5cece
|
||||
|
|
@ -0,0 +1 @@
|
|||
d382bfcfcf79ba38a388ac5496a194adc0c392ba6685c024d7bd55a14d57c1b8
|
||||
|
|
@ -0,0 +1 @@
|
|||
ebe1b2db48b987720e7c561b30ce41e7542d8cd190e4b454bd28d6fdfa8bff0d
|
||||
|
|
@ -0,0 +1 @@
|
|||
2fcb9e7ecdc387d5dd4dfe78544289026bb2626eca2650da590f6181deeaadef
|
||||
|
|
@ -0,0 +1 @@
|
|||
5a03ed4bc5791d0feb72203553f77ed37f37127309eda6c7dc75c7ac950e28e3
|
||||
|
|
@ -29124,10 +29124,6 @@ CREATE UNIQUE INDEX index_projects_on_project_namespace_id ON projects USING btr
|
|||
|
||||
CREATE INDEX index_projects_on_repository_storage ON projects USING btree (repository_storage);
|
||||
|
||||
CREATE INDEX index_projects_on_runners_token ON projects USING btree (runners_token);
|
||||
|
||||
CREATE INDEX index_projects_on_runners_token_encrypted ON projects USING btree (runners_token_encrypted);
|
||||
|
||||
CREATE INDEX index_projects_on_star_count ON projects USING btree (star_count);
|
||||
|
||||
CREATE INDEX index_projects_on_updated_at_and_id ON projects USING btree (updated_at, id);
|
||||
|
|
@ -29594,6 +29590,10 @@ CREATE INDEX index_u2f_registrations_on_user_id ON u2f_registrations USING btree
|
|||
|
||||
CREATE UNIQUE INDEX index_uniq_im_issuable_escalation_statuses_on_issue_id ON incident_management_issuable_escalation_statuses USING btree (issue_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_uniq_projects_on_runners_token ON projects USING btree (runners_token);
|
||||
|
||||
CREATE UNIQUE INDEX index_uniq_projects_on_runners_token_encrypted ON projects USING btree (runners_token_encrypted);
|
||||
|
||||
CREATE UNIQUE INDEX index_unique_ci_runner_projects_on_runner_id_and_project_id ON ci_runner_projects USING btree (runner_id, project_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_unique_issue_metrics_issue_id ON issue_metrics USING btree (issue_id);
|
||||
|
|
@ -30008,10 +30008,6 @@ CREATE UNIQUE INDEX tmp_index_on_tmp_project_id_on_namespaces ON namespaces USIN
|
|||
|
||||
CREATE INDEX tmp_index_on_vulnerabilities_non_dismissed ON vulnerabilities USING btree (id) WHERE (state <> 2);
|
||||
|
||||
CREATE INDEX tmp_index_projects_on_id_and_runners_token ON projects USING btree (id, runners_token) WHERE (runners_token IS NOT NULL);
|
||||
|
||||
CREATE INDEX tmp_index_projects_on_id_and_runners_token_encrypted ON projects USING btree (id, runners_token_encrypted) WHERE (runners_token_encrypted IS NOT NULL);
|
||||
|
||||
CREATE UNIQUE INDEX uniq_pkgs_deb_grp_architectures_on_distribution_id_and_name ON packages_debian_group_architectures USING btree (distribution_id, name);
|
||||
|
||||
CREATE UNIQUE INDEX uniq_pkgs_deb_grp_components_on_distribution_id_and_name ON packages_debian_group_components USING btree (distribution_id, name);
|
||||
|
|
|
|||
|
|
@ -825,7 +825,9 @@ information, see the [relevant documentation](monitoring.md#monitor-gitaly-concu
|
|||
|
||||
## Control groups
|
||||
|
||||
> Introduced in GitLab 13.10.
|
||||
FLAG:
|
||||
On self-managed GitLab, by default cgroups are not available. To make it available, ask an administrator to
|
||||
[enable the feature flag](../feature_flags.md) named `gitaly_run_cmds_in_cgroup`.
|
||||
|
||||
Gitaly shells out to Git for many of its operations. Git can consume a lot of resources for certain operations,
|
||||
especially for large repositories.
|
||||
|
|
@ -862,10 +864,6 @@ Two ways of configuring cgroups are available.
|
|||
|
||||
> This method of configuring cgroups introduced in GitLab 15.1.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this method of configuring cgroups is not available. To make it available, ask an administrator to
|
||||
[enable the feature flag](../feature_flags.md) named `gitaly_run_cmds_in_cgroup`.
|
||||
|
||||
Gitaly creates a pool of cgroups that are isolated based on the repository used in the Git command to be placed under one of these cgroups.
|
||||
|
||||
To configure cgroups in Gitaly, add `gitaly['cgroups']` to `/etc/gitlab/gitlab.rb`.
|
||||
|
|
|
|||
|
|
@ -48,8 +48,8 @@ the Gitaly logs and Prometheus:
|
|||
|
||||
You can observe the status of [control groups (cgroups)](configure_gitaly.md#control-groups) using Prometheus:
|
||||
|
||||
- `gitaly_cgroups_memory_failed_total`, a gauge for the total number of times
|
||||
the memory limit has been hit. This number resets each time a server is
|
||||
- `gitaly_cgroups_reclaim_attempts_total`, a gauge for the total number of times
|
||||
there has been a memory relcaim attempt. This number resets each time a server is
|
||||
restarted.
|
||||
- `gitaly_cgroups_cpu_usage`, a gauge that measures CPU usage per cgroup.
|
||||
- `gitaly_cgroup_procs_total`, a gauge that measures the total number of
|
||||
|
|
|
|||
|
|
@ -195,12 +195,12 @@ the duration of the pipeline if many jobs ran at the same time.
|
|||
|
||||
The cost factor for a job running on a shared runner is:
|
||||
|
||||
- `0.008` for public projects on GitLab SaaS, if [created 2021-07-17 or later](https://gitlab.com/gitlab-org/gitlab/-/issues/332708).
|
||||
- `0.008` for public projects on GitLab SaaS.
|
||||
(For every 125 minutes of job time, you accrue 1 CI/CD minute.)
|
||||
- `0.008` for projects members of GitLab [Open Source program](../../subscriptions/index.md#gitlab-for-open-source).
|
||||
(For every 125 minutes of job time, you accrue 1 CI/CD minute.)
|
||||
- `0` for public projects on GitLab self-managed instances, and for GitLab SaaS public projects created before 2021-07-17.
|
||||
- `1` for internal and private projects.
|
||||
- `0` for public projects on GitLab self-managed instances.
|
||||
- `1` for internal or private projects.
|
||||
|
||||
### Additional costs on GitLab SaaS
|
||||
|
||||
|
|
|
|||
|
|
@ -10,13 +10,15 @@ This tutorial guides you through writing a consumer test from scratch. To start,
|
|||
|
||||
## Create the skeleton
|
||||
|
||||
Start by creating the skeleton of a consumer test. Create a file under `spec/contracts/consumer/specs` called `discussions.spec.js`.
|
||||
Start by creating the skeleton of a consumer test. Create a file under `spec/contracts/consumer/specs/project/merge_request` called `discussions.spec.js`.
|
||||
Then, populate it with the following function and parameters:
|
||||
|
||||
- [`pactWith`](#the-pactwith-function)
|
||||
- [`PactOptions`](#the-pactoptions-parameter)
|
||||
- [`PactFn`](#the-pactfn-parameter)
|
||||
|
||||
To learn more about how the contract test directory is structured, see the contract testing [test suite folder structure](index.md#test-suite-folder-structure).
|
||||
|
||||
### The `pactWith` function
|
||||
|
||||
The Pact consumer test is defined through the `pactWith` function that takes `PactOptions` and the `PactFn`.
|
||||
|
|
@ -36,15 +38,17 @@ const { pactWith } = require('jest-pact');
|
|||
|
||||
pactWith(
|
||||
{
|
||||
consumer: 'Merge Request Page',
|
||||
consumer: 'MergeRequest#show',
|
||||
provider: 'Merge Request Discussions Endpoint',
|
||||
log: '../logs/consumer.log',
|
||||
dir: '../contracts',
|
||||
dir: '../contracts/project/merge_request/show',
|
||||
},
|
||||
PactFn
|
||||
);
|
||||
```
|
||||
|
||||
To learn more about how to name the consumers and providers, see contract testing [naming conventions](index.md#naming-conventions).
|
||||
|
||||
### The `PactFn` parameter
|
||||
|
||||
The `PactFn` is where your tests are defined. This is where you set up the mock provider and where you can use the standard Jest methods like [`Jest.describe`](https://jestjs.io/docs/api#describename-fn), [`Jest.beforeEach`](https://jestjs.io/docs/api#beforeeachfn-timeout), and [`Jest.it`](https://jestjs.io/docs/api#testname-fn-timeout). For more information, see [https://jestjs.io/docs/api](https://jestjs.io/docs/api).
|
||||
|
|
@ -54,14 +58,14 @@ const { pactWith } = require('jest-pact');
|
|||
|
||||
pactWith(
|
||||
{
|
||||
consumer: 'Merge Request Page',
|
||||
consumer: 'MergeRequest#show',
|
||||
provider: 'Merge Request Discussions Endpoint',
|
||||
log: '../logs/consumer.log',
|
||||
dir: '../contracts',
|
||||
},
|
||||
|
||||
(provider) => {
|
||||
describe('Discussions Endpoint', () => {
|
||||
describe('Merge Request Discussions Endpoint', () => {
|
||||
beforeEach(() => {
|
||||
|
||||
});
|
||||
|
|
@ -93,14 +97,14 @@ const { Matchers } = require('@pact-foundation/pact');
|
|||
|
||||
pactWith(
|
||||
{
|
||||
consumer: 'Merge Request Page',
|
||||
consumer: 'MergeRequest#show',
|
||||
provider: 'Merge Request Discussions Endpoint',
|
||||
log: '../logs/consumer.log',
|
||||
dir: '../contracts',
|
||||
dir: '../contracts/project/merge_request/show',
|
||||
},
|
||||
|
||||
(provider) => {
|
||||
describe('Discussions Endpoint', () => {
|
||||
describe('Merge Request Discussions Endpoint', () => {
|
||||
beforeEach(() => {
|
||||
const interaction = {
|
||||
state: 'a merge request with discussions exists',
|
||||
|
|
@ -144,7 +148,7 @@ Notice how we use `Matchers` in the `body` of the expected response. This allows
|
|||
|
||||
After the mock provider is set up, you can write the test. For this test, you make a request and expect a particular response.
|
||||
|
||||
First, set up the client that makes the API request. To do that, either create or find an existing file under `spec/contracts/consumer/endpoints` and add the following API request.
|
||||
First, set up the client that makes the API request. To do that, create `spec/contracts/consumer/endpoints/project/merge_requests.js` and add the following API request.
|
||||
|
||||
```javascript
|
||||
const axios = require('axios');
|
||||
|
|
@ -169,18 +173,18 @@ After that's set up, import it to the test file and call it to make the request.
|
|||
const { pactWith } = require('jest-pact');
|
||||
const { Matchers } = require('@pact-foundation/pact');
|
||||
|
||||
const { getDiscussions } = require('../endpoints/merge_requests');
|
||||
const { getDiscussions } = require('../endpoints/project/merge_requests');
|
||||
|
||||
pactWith(
|
||||
{
|
||||
consumer: 'Merge Request Page',
|
||||
consumer: 'MergeRequest#show',
|
||||
provider: 'Merge Request Discussions Endpoint',
|
||||
log: '../logs/consumer.log',
|
||||
dir: '../contracts',
|
||||
dir: '../contracts/project/merge_request/show',
|
||||
},
|
||||
|
||||
(provider) => {
|
||||
describe('Discussions Endpoint', () => {
|
||||
describe('Merge Request Discussions Endpoint', () => {
|
||||
beforeEach(() => {
|
||||
const interaction = {
|
||||
state: 'a merge request with discussions exists',
|
||||
|
|
@ -230,7 +234,7 @@ There we have it! The consumer test is now set up. You can now try [running this
|
|||
|
||||
As you may have noticed, the request and response definitions can get large. This results in the test being difficult to read, with a lot of scrolling to find what you want. You can make the test easier to read by extracting these out to a `fixture`.
|
||||
|
||||
Create a file under `spec/contracts/consumer/fixtures` called `discussions.fixture.js`. You place the `request` and `response` definitions here.
|
||||
Create a file under `spec/contracts/consumer/fixtures/project/merge_request` called `discussions.fixture.js` where you will place the `request` and `response` definitions.
|
||||
|
||||
```javascript
|
||||
const { Matchers } = require('@pact-foundation/pact');
|
||||
|
|
@ -274,18 +278,18 @@ With all of that moved to the `fixture`, you can simplify the test to the follow
|
|||
const { pactWith } = require('jest-pact');
|
||||
|
||||
const { Discussions } = require('../fixtures/discussions.fixture');
|
||||
const { getDiscussions } = require('../endpoints/merge_requests');
|
||||
const { getDiscussions } = require('../endpoints/project/merge_requests');
|
||||
|
||||
pactWith(
|
||||
{
|
||||
consumer: 'Merge Request Page',
|
||||
consumer: 'MergeRequest#show',
|
||||
provider: 'Merge Request Discussions Endpoint',
|
||||
log: '../logs/consumer.log',
|
||||
dir: '../contracts',
|
||||
dir: '../contracts/project/merge_request/show',
|
||||
},
|
||||
|
||||
(provider) => {
|
||||
describe('Discussions Endpoint', () => {
|
||||
describe('Merge Request Discussions Endpoint', () => {
|
||||
beforeEach(() => {
|
||||
const interaction = {
|
||||
state: 'a merge request with discussions exists',
|
||||
|
|
|
|||
|
|
@ -37,3 +37,42 @@ rake contracts:mr:pact:verify:discussions # Verify provider against the
|
|||
rake contracts:mr:pact:verify:metadata # Verify provider against the consumer pacts for metadata
|
||||
rake contracts:mr:test:merge_request[contract_mr] # Run all merge request contract tests
|
||||
```
|
||||
|
||||
## Test suite folder structure and naming conventions
|
||||
|
||||
To keep the consumer and provider test suite organized and maintainable, it's important that tests are organized, also that consumers and providers are named consistently. Therefore, it's important to adhere to the following conventions.
|
||||
|
||||
### Test suite folder structure
|
||||
|
||||
Having an organized and sensible folder structure for the test suite makes it easier to find relevant files when reviewing, debugging, or introducing tests.
|
||||
|
||||
#### Consumer tests
|
||||
|
||||
The consumer tests are grouped according to the different pages in the application. Each file contains various types of requests found in a page. As such, the consumer test files are named using the Rails standards of how pages are referenced. For example, the project pipelines page would be the `Project::Pipeline#index` page so the equivalent consumer test would be located in `consumer/specs/project/pipelines/index.spec.js`.
|
||||
|
||||
When defining the location to output the contract generated by the test, we want to follow the same file structure which would be `contracts/project/pipelines/` for this example. This is the structure in `consumer/endpoints` and `consumer/fixtures` as well.
|
||||
|
||||
#### Provider tests
|
||||
|
||||
The provider tests are grouped similarly to our controllers. Each of these tests contains various tests for an API endpoint. For example, the API endpoint to get a list of pipelines for a project would be located in `provider/pact_helpers/project/pipelines/get_list_project_pipelines_helper.rb`. The provider states are structured the same way.
|
||||
|
||||
### Naming conventions
|
||||
|
||||
When writing the consumer and provider tests, there are parts where a name is required for the consumer and provider. Since there are no restrictions imposed by Pact on how these should be named, a naming convention is important to keep it easy for us to figure out which consumer and provider tests are involved during debugging. Pact also uses the consumer and provider names to generate the generated contracts in the `#{consumer_name}-#{provider_name}` format.
|
||||
|
||||
#### Consumer naming
|
||||
|
||||
As mentioned in the [folder structure section](#consumer-tests), consumer tests are grouped according to the different pages in the application. As such, consumer names should follow the same naming format using the Rails standard. For example, the consumer test for `Project::Pipeline#index` would be `ProjectPipeline#index` as the consumer name. Since Pact uses this name to name the contracts it generates, the colons (`::`) are dropped as colons are not valid characters in file names.
|
||||
|
||||
#### Provider naming
|
||||
|
||||
These are the API endpoints that provides the data to the consumer so they are simply named according to the API endpoint they pertain to. Be mindful that this name is as descriptive as possible. For example, if we're writing a test for the `GET /groups/:id/projects` endpoint, we don't want to simply name it "Projects endpoint" as there is a `GET /projects` endpoint as well that also fetches a list of projects the user has access to across all of GitLab. An easy way to name them is by checking out our [API documentation](../../../api/api_resources.md) and naming it the same way it is named in there. So the [`GET /groups/:id/projects`](../../../api/groups.md#list-a-groups-projects) would be called `List a group’s projects` and [`GET /projects`](../../../api/projects.md#list-all-projects) would be called `List all projects`. Subsequently, the test files are named `list_a_groups_projects_helper.rb` and `list_all_projects_helper.rb` respectively.
|
||||
|
||||
There are some cases where the provider being tested may not be documented so, in those cases, fall back to choosing a name that is as descriptive as possible to ensure it's easy to tell what the provider is for.
|
||||
|
||||
#### Conventions summary
|
||||
|
||||
| Tests | Folder structure | Naming convention |
|
||||
| ----- | ---------------- | ----------------- |
|
||||
| Consumer Test | Follows the Rails reference standards. For example, `Project::Pipeline#index` would be `consumer/specs/project/pipelines/index.spec.js` | Follows the Rails naming standard. For example, `Project::Pipeline#index` would be `ProjectPipeline#index` |
|
||||
| Provider Test | Grouped like the Rails controllers. For example, [`List project pipelines` API endpoint](../../../api/pipelines.md#list-project-pipelines) would be `provider/pact_helpers/project/pipelines/provider/pact_helpers/project/pipelines/get_list_project_pipelines_helper.rb` | Follows the API documentation naming scheme. For example, [`GET /projects/:id/pipelines`](../../../api/pipelines.md#list-project-pipelines) would be called `List project pipelines`. |
|
||||
|
|
|
|||
|
|
@ -6,18 +6,20 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Writing provider tests
|
||||
|
||||
This tutorial guides you through writing a provider test from scratch. It is a continuation of the [consumer test tutorial](consumer_tests.md). To start, the provider tests are written using [`pact-ruby`](https://github.com/pact-foundation/pact-ruby). In this tutorial, you write a provider test that addresses the contract generated by `discussions.spec.js`.
|
||||
This tutorial guides you through writing a provider test from scratch. It is a continuation of the [consumer test tutorial](consumer_tests.md). To start, the provider tests are written using [`pact-ruby`](https://github.com/pact-foundation/pact-ruby). In this tutorial, you write a provider test that addresses the contract generated by `discussions.spec.js`. As Pact is a consumer-driven testing tool, this tutorial assumes that there is an existing consumer test that had already generated a contract for us to work with.
|
||||
|
||||
## Create the skeleton
|
||||
|
||||
Provider tests are quite simple. The goal is to set up the test data and then link that with the corresponding contract. Start by creating a file called `discussions_helper.rb` under `spec/contracts/provider/specs`. Note that the files are called `helpers` to match how they are called by Pact in the Rake tasks, which are set up at the end of this tutorial.
|
||||
Provider tests are quite simple. The goal is to set up the test data and then link that with the corresponding contract. Start by creating a file called `discussions_helper.rb` under `spec/contracts/provider/pact_helpers/project/merge_request`. Note that the files are called `helpers` to match how they are called by Pact in the Rake tasks, which are set up at the end of this tutorial.
|
||||
|
||||
To learn more about how the contract test directory is structured, see the contract testing [test suite folder structure](index.md#test-suite-folder-structure).
|
||||
|
||||
### The `service_provider` block
|
||||
|
||||
The `service_provider` block is where the provider test is defined. For this block, put in a description of the service provider. Name it exactly as it is called in the contracts that are derived from the consumer tests.
|
||||
|
||||
```ruby
|
||||
require_relative '../spec_helper'
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
module Provider
|
||||
module DiscussionsHelper
|
||||
|
|
@ -33,12 +35,12 @@ end
|
|||
The `honours_pact_with` block describes which consumer this provider test is addressing. Similar to the `service_provider` block, name this exactly the same as it's called in the contracts that are derived from the consumer tests.
|
||||
|
||||
```ruby
|
||||
require_relative '../spec_helper'
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
module Provider
|
||||
module DiscussionsHelper
|
||||
Pact.service_provider 'Merge Request Discussions Endpoint' do
|
||||
honours_pact_with 'Merge Request Page' do
|
||||
honours_pact_with 'MergeRequest#show' do
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -46,19 +48,21 @@ module Provider
|
|||
end
|
||||
```
|
||||
|
||||
To learn more about how to name the consumers and providers, see contract testing [naming conventions](index.md#naming-conventions).
|
||||
|
||||
## Configure the test app
|
||||
|
||||
For the provider tests to verify the contracts, you must hook it up to a test app that makes the actual request and return a response to verify against the contract. To do this, configure the `app` the test uses as `Environment::Test.app`, which is defined in [`spec/contracts/provider/environments/test.rb`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/spec/contracts/provider/environments/test.rb).
|
||||
|
||||
```ruby
|
||||
require_relative '../spec_helper'
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
module Provider
|
||||
module DiscussionsHelper
|
||||
Pact.service_provider 'Merge Request Discussions Endpoint' do
|
||||
app { Environment::Test.app }
|
||||
|
||||
honours_pact_with 'Merge Request Page' do
|
||||
honours_pact_with 'MergeRequest#show' do
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -71,15 +75,15 @@ end
|
|||
Now that the test app is configured, all that is left is to define which contract this provider test is verifying. To do this, set the `pact_uri`.
|
||||
|
||||
```ruby
|
||||
require_relative '../spec_helper'
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
module Provider
|
||||
module DiscussionsHelper
|
||||
Pact.service_provider 'Merge Request Discussions Endpoint' do
|
||||
app { Environment::Test.app }
|
||||
|
||||
honours_pact_with 'Merge Request Page' do
|
||||
pact_uri '../contracts/merge_request_page-merge_request_discussions_endpoint.json'
|
||||
honours_pact_with 'MergeRequest#show' do
|
||||
pact_uri '../contracts/project/merge_request/show/mergerequest#show-merge_request_discussions_endpoint.json'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -95,8 +99,8 @@ Under the `contracts:mr` namespace, introduce the Rake task to run this new test
|
|||
```ruby
|
||||
Pact::VerificationTask.new(:discussions) do |pact|
|
||||
pact.uri(
|
||||
"#{contracts}/contracts/merge_request_page-merge_request_discussions_endpoint.json",
|
||||
pact_helper: "#{provider}/specs/discussions_helper.rb"
|
||||
"#{contracts}/contracts/project/merge_request/show/merge_request#show-merge_request_discussions_endpoint.json",
|
||||
pact_helper: "#{provider}/pact_helpers/project/merge_request/discussions_helper.rb"
|
||||
)
|
||||
end
|
||||
```
|
||||
|
|
@ -109,7 +113,7 @@ As the last step, create the test data that allows the provider test to return t
|
|||
|
||||
You can read more about [provider states](https://docs.pact.io/implementation_guides/ruby/provider_states). We can do global provider states but for this tutorial, the provider state is for one specific `state`.
|
||||
|
||||
To create the test data, create `discussions_state.rb` under `spec/contracts/provider/states`. As a quick aside, make sure to also import this state file in the `discussions_helper.rb` file.
|
||||
To create the test data, create `discussions_state.rb` under `spec/contracts/provider/states/project/merge_request`. Be sure to also import this state file in the `discussions_helper.rb` file.
|
||||
|
||||
### Default user in `spec/contracts/provider/spec_helper.rb`
|
||||
|
||||
|
|
@ -118,10 +122,13 @@ Before you create the test data, note that a default user is created in the [`sp
|
|||
```ruby
|
||||
RSpec.configure do |config|
|
||||
config.include Devise::Test::IntegrationHelpers
|
||||
config.include FactoryBot::Syntax::Methods
|
||||
|
||||
config.before do
|
||||
user = FactoryBot.create(:user, name: "Contract Test").tap do |user|
|
||||
user = create(:user, name: Provider::UsersHelper::CONTRACT_USER_NAME).tap do |user|
|
||||
user.current_sign_in_at = Time.current
|
||||
end
|
||||
|
||||
sign_in user
|
||||
end
|
||||
end
|
||||
|
|
@ -134,7 +141,7 @@ Any further modifications to the user that's needed can be done through the indi
|
|||
In the state file, you must define which consumer this provider state is for. You can do that with `provider_states_for`. Make sure that the `name` provided matches the name defined for the consumer.
|
||||
|
||||
```ruby
|
||||
Pact.provider_states_for 'Merge Request Page' do
|
||||
Pact.provider_states_for 'MergeRequest#show' do
|
||||
end
|
||||
```
|
||||
|
||||
|
|
@ -143,9 +150,9 @@ end
|
|||
In the `provider_states_for` block, you then define the state the test data is for. These states are also defined in the consumer test. In this case, there is a `'a merge request with discussions exists'` state.
|
||||
|
||||
```ruby
|
||||
Pact.provider_states_for "Merge Request Page" do
|
||||
Pact.provider_states_for "MergeRequest#show" do
|
||||
provider_state "a merge request with discussions exists" do
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
```
|
||||
|
|
@ -155,7 +162,7 @@ end
|
|||
This is where you define the test data creation steps. Use `FactoryBot` to create the data. As you create the test data, you can keep [running the provider test](index.md#run-the-provider-tests) to check on the status of the test and figure out what else is missing in your data setup.
|
||||
|
||||
```ruby
|
||||
Pact.provider_states_for "Merge Request Page" do
|
||||
Pact.provider_states_for "MergeRequest#show" do
|
||||
provider_state "a merge request with discussions exists" do
|
||||
set_up do
|
||||
user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
|
||||
|
|
@ -172,6 +179,28 @@ Pact.provider_states_for "Merge Request Page" do
|
|||
end
|
||||
```
|
||||
|
||||
Note the `Provider::UsersHelper::CONTRACT_USER_NAME` here to fetch a user is a user that is from the [`spec_helper`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/spec/contracts/provider/spec_helper.rb) that sets up a user before any of these tests run.
|
||||
## Using the test data
|
||||
|
||||
And with that, the provider tests for `discussion_helper.rb` should now pass with this.
|
||||
Now that the provider state file is created, you need to import the state file to the provider test.
|
||||
|
||||
```ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../../../spec_helper'
|
||||
require_relative '../../../states/project/merge_request/discussions_state'
|
||||
|
||||
module Provider
|
||||
module DiscussionsHelper
|
||||
Pact.service_provider "/merge_request/discussions" do
|
||||
app { Environments::Test.app }
|
||||
|
||||
honours_pact_with 'Merge Request#show' do
|
||||
pact_uri '../contracts/project/merge_request/show/merge_request#show-merge_request_discussions_endpoint.json'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
```
|
||||
|
||||
And there we have it. The provider test for `discussions_helper.rb` should now pass with this.
|
||||
|
|
|
|||
|
|
@ -458,11 +458,27 @@ NOTE:
|
|||
Specific information that follow related to Ruby and Git versions do not apply to [Omnibus installations](https://docs.gitlab.com/omnibus/)
|
||||
and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with appropriate Ruby and Git versions and are not using system binaries for Ruby and Git. There is no need to install Ruby or Git when utilizing these two approaches.
|
||||
|
||||
### 15.2.0 (unreleased)
|
||||
|
||||
GitLab installations that have multiple web nodes should be
|
||||
[upgraded to 15.1](#1510) before upgrading to 15.2 (and later) due to a
|
||||
configuration change in Rails that can result in inconsistent ETag key
|
||||
generation.
|
||||
|
||||
### 15.1.0
|
||||
|
||||
- If you run external PostgreSQL, particularly AWS RDS,
|
||||
[check you have a PostgreSQL bug fix](#postgresql-segmentation-fault-issue)
|
||||
to avoid the database crashing.
|
||||
- In GitLab 15.1.0, we are switching Rails `ActiveSupport::Digest` to use SHA256 instead of MD5.
|
||||
This affects ETag key generation for resources such as raw Snippet file
|
||||
downloads. In order to ensure consistent ETag key generation across multiple
|
||||
web nodes when upgrading, all servers must first be upgraded to 15.1.Z before
|
||||
upgrading to 15.2.0 or later:
|
||||
|
||||
1. Ensure all GitLab web nodes are running GitLab 15.1.Z.
|
||||
1. [Enable the `active_support_hash_digest_sha256` feature flag](../administration/feature_flags.md#how-to-enable-and-disable-features-behind-flags) to switch `ActiveSupport::Digest` to use SHA256:
|
||||
1. Only then, continue to upgrade to later versions of GitLab.
|
||||
|
||||
### 15.0.0
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
stage: Secure
|
||||
group: Dynamic Analysis
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Content-Security-Policy violations
|
||||
|
||||
## Description
|
||||
|
||||
A `Content-Security-Policy` (CSP) was identified on the target site that is reporting violations when
|
||||
attempting to load the page in a browser. This may cause disruption to your users when attempting to visit the page.
|
||||
|
||||
## Remediation
|
||||
|
||||
Review the violations to determine if any action is necessary.
|
||||
|
||||
## Details
|
||||
|
||||
| ID | Aggregated | CWE | Type | Risk |
|
||||
|:---|:--------|:--------|:--------|:--------|
|
||||
| 16.10 | true | 16 | Passive | Info |
|
||||
|
||||
## Links
|
||||
|
||||
- [CWE](https://cwe.mitre.org/data/definitions/16.html)
|
||||
- [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html)
|
||||
- [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
|
||||
- [Content Security Policy Level 3](https://www.w3.org/TR/CSP3/)
|
||||
- [CSP Evaluator](https://csp-evaluator.withgoogle.com/)
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
stage: Secure
|
||||
group: Dynamic Analysis
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Content-Security-Policy analysis
|
||||
|
||||
## Description
|
||||
|
||||
A `Content-Security-Policy` (CSP) was identified on the target site. CSP can aid in hardening
|
||||
a website against various client side attacks such as Cross-Site Scripting (XSS).
|
||||
|
||||
## Remediation
|
||||
|
||||
Follow the recommendations to determine if any actions are necessary to harden this `Content-Security-Policy`.
|
||||
|
||||
## Details
|
||||
|
||||
| ID | Aggregated | CWE | Type | Risk |
|
||||
|:---|:--------|:--------|:--------|:--------|
|
||||
| 16.8 | true | 16 | Passive | Info |
|
||||
|
||||
## Links
|
||||
|
||||
- [CWE](https://cwe.mitre.org/data/definitions/16.html)
|
||||
- [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html)
|
||||
- [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
|
||||
- [Content Security Policy Level 3](https://www.w3.org/TR/CSP3/)
|
||||
- [CSP Evaluator](https://csp-evaluator.withgoogle.com/)
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
stage: Secure
|
||||
group: Dynamic Analysis
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Content-Security-Policy-Report-Only analysis
|
||||
|
||||
## Description
|
||||
|
||||
A `Content-Security-Policy-Report-Only` (CSPRO) was identified on the target site. CSP-Report-Only headers
|
||||
aid in determining how to implement a `Content-Security-Policy` that does not disrupt normal use of the target
|
||||
site.
|
||||
|
||||
## Remediation
|
||||
|
||||
Follow the recommendations to determine if any actions are necessary to harden this `Content-Security-Policy-Report-Only`.
|
||||
After all alerts have been resolved, we recommended that this header be changed to `Content-Security-Policy`.
|
||||
|
||||
## Details
|
||||
|
||||
| ID | Aggregated | CWE | Type | Risk |
|
||||
|:---|:--------|:--------|:--------|:--------|
|
||||
| 16.9 | true | 16 | Passive | Info |
|
||||
|
||||
## Links
|
||||
|
||||
- [CWE](https://cwe.mitre.org/data/definitions/16.html)
|
||||
- [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html)
|
||||
- [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
|
||||
- [Content Security Policy Level 3](https://www.w3.org/TR/CSP3/)
|
||||
- [CSP Evaluator](https://csp-evaluator.withgoogle.com/)
|
||||
|
|
@ -12,12 +12,15 @@ The [DAST browser-based crawler](../browser_based.md) provides a number of vulne
|
|||
|:---|:------|:---------|:-----|
|
||||
| [1004.1](1004.1.md) | Sensitive cookie without HttpOnly attribute | Low | Passive |
|
||||
| [16.1](16.1.md) | Missing Content-Type header | Low | Passive |
|
||||
| [16.10](16.10.md) | Content-Security-Policy violations | Info | Passive |
|
||||
| [16.2](16.2.md) | Server header exposes version information | Low | Passive |
|
||||
| [16.3](16.3.md) | X-Powered-By header exposes version information | Low | Passive |
|
||||
| [16.4](16.4.md) | X-Backend-Server header exposes server information | Info | Passive |
|
||||
| [16.5](16.5.md) | AspNet header exposes version information | Low | Passive |
|
||||
| [16.6](16.6.md) | AspNetMvc header exposes version information | Low | Passive |
|
||||
| [16.7](16.7.md) | Strict-Transport-Security header missing or invalid | Low | Passive |
|
||||
| [16.8](16.8.md) | Content-Security-Policy analysis | Info | Passive |
|
||||
| [16.9](16.9.md) | Content-Security-Policy-Report-Only analysis | Info | Passive |
|
||||
| [200.1](200.1.md) | Exposure of sensitive information to an unauthorized actor (private IP address) | Low | Passive |
|
||||
| [209.1](209.1.md) | Generation of error message containing sensitive information | Low | Passive |
|
||||
| [319.1](319.1.md) | Mixed Content | Info | Passive |
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ module Gitlab
|
|||
text_field :activation_code
|
||||
button :activate
|
||||
label :terms_of_services, text: /I agree that/
|
||||
link :remove_license, 'data-testid': 'license-remove-action'
|
||||
button :confirm_ok_button
|
||||
p :plan
|
||||
p :started
|
||||
p :name
|
||||
|
|
@ -21,6 +23,9 @@ module Gitlab
|
|||
h2 :users_over_subscription
|
||||
table :subscription_history
|
||||
|
||||
span :no_valid_license_alert, text: /no longer has a valid license/
|
||||
h3 :no_active_subscription_title, text: /do not have an active subscription/
|
||||
|
||||
def accept_terms
|
||||
terms_of_services_element.click # workaround for hidden checkbox
|
||||
end
|
||||
|
|
@ -39,7 +44,7 @@ module Gitlab
|
|||
# @param license_type [Hash] Type of the license
|
||||
# @option license_type [String] 'license file'
|
||||
# @option license_type [String] 'cloud license'
|
||||
# @return [Boolean] True if record exsists, false if not
|
||||
# @return [Boolean] True if record exists, false if not
|
||||
def has_subscription_record?(plan, users_in_license, license_type)
|
||||
# find any records that have a matching plan and seats and type
|
||||
subscription_history_element.hashes.any? do |record|
|
||||
|
|
|
|||
|
|
@ -163,6 +163,20 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
# Get users from the API
|
||||
#
|
||||
# @param [Integer] per_page the number of pages to traverse (used for pagination)
|
||||
# @return [Array<Hash>] parsed response body
|
||||
def self.all(per_page: 100)
|
||||
response = nil
|
||||
Resource::User.init do |user|
|
||||
response = user.get(Runtime::API::Request.new(Runtime::API::Client.as_admin,
|
||||
'/users',
|
||||
per_page: per_page.to_s).url)
|
||||
raise ResourceQueryError unless response.code == 200
|
||||
end.parse_body(response)
|
||||
end
|
||||
|
||||
def approve!
|
||||
response = post(Runtime::API::Request.new(api_client, api_approve_path).url, nil)
|
||||
return if response.code == 201
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe ChangePublicProjectsCostFactor, :migration do
|
||||
# This is a workaround to force the migration to run against the
|
||||
# `gitlab_ci` schema. Otherwise it only runs against `gitlab_main`.
|
||||
around do |example| # rubocop: disable Style/MultilineIfModifier
|
||||
with_reestablished_active_record_base do
|
||||
reconfigure_db_connection(name: :ci)
|
||||
example.run
|
||||
end
|
||||
end if Gitlab::Database.has_config?(:ci)
|
||||
|
||||
let(:runners) { table(:ci_runners) }
|
||||
|
||||
let!(:shared_1) { runners.create!(runner_type: 1, public_projects_minutes_cost_factor: 0) }
|
||||
let!(:shared_2) { runners.create!(runner_type: 1, public_projects_minutes_cost_factor: 0) }
|
||||
let!(:shared_3) { runners.create!(runner_type: 1, public_projects_minutes_cost_factor: 1) }
|
||||
let!(:group_1) { runners.create!(runner_type: 2, public_projects_minutes_cost_factor: 0) }
|
||||
|
||||
describe '#up' do
|
||||
context 'when on SaaS' do
|
||||
before do
|
||||
allow(Gitlab).to receive(:com?).and_return(true)
|
||||
end
|
||||
|
||||
it 'updates the cost factor from 0 only for shared runners', :aggregate_failures do
|
||||
migrate!
|
||||
|
||||
expect(shared_1.reload.public_projects_minutes_cost_factor).to eq(0.008)
|
||||
expect(shared_2.reload.public_projects_minutes_cost_factor).to eq(0.008)
|
||||
expect(shared_3.reload.public_projects_minutes_cost_factor).to eq(1)
|
||||
expect(group_1.reload.public_projects_minutes_cost_factor).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when on self-managed', :aggregate_failures do
|
||||
it 'skips the migration' do
|
||||
migrate!
|
||||
|
||||
expect(shared_1.public_projects_minutes_cost_factor).to eq(0)
|
||||
expect(shared_2.public_projects_minutes_cost_factor).to eq(0)
|
||||
expect(shared_3.public_projects_minutes_cost_factor).to eq(1)
|
||||
expect(group_1.public_projects_minutes_cost_factor).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#down' do
|
||||
context 'when on SaaS' do
|
||||
before do
|
||||
allow(Gitlab).to receive(:com?).and_return(true)
|
||||
end
|
||||
|
||||
it 'resets the cost factor to 0 only for shared runners that were updated', :aggregate_failures do
|
||||
migrate!
|
||||
schema_migrate_down!
|
||||
|
||||
expect(shared_1.public_projects_minutes_cost_factor).to eq(0)
|
||||
expect(shared_2.public_projects_minutes_cost_factor).to eq(0)
|
||||
expect(shared_3.public_projects_minutes_cost_factor).to eq(1)
|
||||
expect(group_1.public_projects_minutes_cost_factor).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue