Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
bd9860f691
commit
bdb97ece68
|
|
@ -1,6 +1,7 @@
|
|||
<script>
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import NotebookLab from '~/notebook/index.vue';
|
||||
|
||||
export default {
|
||||
|
|
@ -51,7 +52,7 @@ export default {
|
|||
this.loading = false;
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.status !== 200) {
|
||||
if (e.status !== HTTP_STATUS_OK) {
|
||||
this.loadError = true;
|
||||
}
|
||||
this.error = true;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import { getWebSocketUrl, mergeUrlParams } from '~/lib/utils/url_utility';
|
||||
import { __ } from '~/locale';
|
||||
import createDiff from './create_diff';
|
||||
|
|
@ -26,7 +27,7 @@ const cancellableWait = (time) => {
|
|||
|
||||
const isErrorResponse = (error) => error && error.code !== 0;
|
||||
|
||||
const isErrorPayload = (payload) => payload && payload.status_code !== 200;
|
||||
const isErrorPayload = (payload) => payload && payload.status_code !== HTTP_STATUS_OK;
|
||||
|
||||
const getErrorFromResponse = (data) => {
|
||||
if (isErrorResponse(data.error)) {
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ module Clusters
|
|||
|
||||
if project_entries
|
||||
allowed_projects.where_full_path_in(project_entries.keys).map do |project|
|
||||
{ project_id: project.id, config: project_entries[project.full_path] }
|
||||
{ project_id: project.id, config: project_entries[project.full_path.downcase] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -70,7 +70,7 @@ module Clusters
|
|||
|
||||
if group_entries
|
||||
allowed_groups.where_full_path_in(group_entries.keys).map do |group|
|
||||
{ group_id: group.id, config: group_entries[group.full_path] }
|
||||
{ group_id: group.id, config: group_entries[group.full_path.downcase] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -79,7 +79,7 @@ module Clusters
|
|||
def extract_config_entries(entity:)
|
||||
config.dig('ci_access', entity)
|
||||
&.first(AUTHORIZED_ENTITY_LIMIT)
|
||||
&.index_by { |config| config.delete('id') }
|
||||
&.index_by { |config| config.delete('id').downcase }
|
||||
end
|
||||
|
||||
def allowed_projects
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RetryBackfillTraversalIds < ActiveRecord::Migration[6.1]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
ROOTS_MIGRATION = 'BackfillNamespaceTraversalIdsRoots'
|
||||
CHILDREN_MIGRATION = 'BackfillNamespaceTraversalIdsChildren'
|
||||
DOWNTIME = false
|
||||
DELAY_INTERVAL = 2.minutes
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
duration = requeue_background_migration_jobs_by_range_at_intervals(ROOTS_MIGRATION, DELAY_INTERVAL)
|
||||
requeue_background_migration_jobs_by_range_at_intervals(CHILDREN_MIGRATION, DELAY_INTERVAL, initial_delay: duration)
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
end
|
||||
end
|
||||
|
|
@ -1 +0,0 @@
|
|||
ec44b7f134de2ea6537c6fe3109fa9d7e32785233f3d1b8e9ea118474d21526a
|
||||
|
|
@ -378,6 +378,7 @@ Component statuses are linked to configuration documentation for each component.
|
|||
| [Runner](#gitlab-runner) | Executes GitLab CI/CD jobs | ⤓ | ⤓ | ✅ | ⚙ | ✅ | ⚙ | ⚙ | CE & EE |
|
||||
| [Sentry integration](#sentry) | Error tracking for deployed apps | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | CE & EE |
|
||||
| [Sidekiq](#sidekiq) | Background jobs processor | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | CE & EE |
|
||||
| [Token Revocation API](sec/token_revocation_api.md) | Receives and revokes leaked secrets | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | EE Only |
|
||||
|
||||
### Component details
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
---
|
||||
stage: Secure
|
||||
group: Static 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
|
||||
---
|
||||
|
||||
# Token Revocation API
|
||||
|
||||
The Token Revocation API is an externally-deployed HTTP API that interfaces with GitLab
|
||||
to receive and revoke API tokens and other secrets detected by GitLab Secret Detection.
|
||||
See the [high-level architecture](../../user/application_security/secret_detection/post_processing.md)
|
||||
to understand the Secret Detection post-processing and revocation flow.
|
||||
|
||||
GitLab.com uses the internally-maintained [Secret Revocation Service](https://gitlab.com/gitlab-com/gl-security/engineering-and-research/automation-team/secret-revocation-service)
|
||||
(team-members only) as its Token Revocation API. For GitLab self-managed, you can create
|
||||
your own API and configure GitLab to use it.
|
||||
|
||||
## Implement a Token Revocation API for self-managed
|
||||
|
||||
GitLab self-managed instances interested in using the revocation capabilities must:
|
||||
|
||||
- Implement and deploy your own Token Revocation API.
|
||||
- Configure the GitLab instance to use the Token Revocation API.
|
||||
|
||||
Your service must:
|
||||
|
||||
- Match the API specification below.
|
||||
- Provide two endpoints:
|
||||
- Fetching revocable token types.
|
||||
- Revoking leaked tokens.
|
||||
- Be rate-limited and idempotent.
|
||||
|
||||
Requests to the documented endpoints are authenticated using API tokens passed in
|
||||
the `Authorization` header. Request and response bodies, if present, are
|
||||
expected to have the content type `application/json`.
|
||||
|
||||
All endpoints may return these responses:
|
||||
|
||||
- `401 Unauthorized`
|
||||
- `405 Method Not Allowed`
|
||||
- `500 Internal Server Error`
|
||||
|
||||
### `GET /v1/revocable_token_types`
|
||||
|
||||
Returns the valid `type` values for use in the `revoke_tokens` endpoint.
|
||||
|
||||
NOTE:
|
||||
These values match the concatenation of [the `secrets` analyzer's](../../user/application_security/secret_detection/index.md)
|
||||
[primary identifier](../integrations/secure.md#identifiers) by means
|
||||
of concatenating the `primary_identifier.type` and `primary_identifier.value`.
|
||||
For example, the value `gitleaks_rule_id_gitlab_personal_access_token` matches the following finding identifier:
|
||||
|
||||
```json
|
||||
{"type": "gitleaks_rule_id", "name": "Gitleaks rule ID GitLab Personal Access Token", "value": "GitLab Personal Access Token"}
|
||||
```
|
||||
|
||||
| Status Code | Description |
|
||||
| ----- | --- |
|
||||
| `200` | The response body contains the valid token `type` values. |
|
||||
|
||||
Example response body:
|
||||
|
||||
```json
|
||||
{
|
||||
"types": ["gitleaks_rule_id_gitlab_personal_access_token"]
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /v1/revoke_tokens`
|
||||
|
||||
Accepts a list of tokens to be revoked by the appropriate provider. Your service is responsible for communicating
|
||||
with each provider to revoke the token.
|
||||
|
||||
| Status Code | Description |
|
||||
| ----- | --- |
|
||||
| `204` | All submitted tokens have been accepted for eventual revocation. |
|
||||
| `400` | The request body is invalid or one of the submitted token types is not supported. The request should not be retried. |
|
||||
| `429` | The provider has received too many requests. The request should be retried later. |
|
||||
|
||||
Example request body:
|
||||
|
||||
```json
|
||||
[{
|
||||
"type": "gitleaks_rule_id_gitlab_personal_access_token",
|
||||
"token": "glpat--8GMtG8Mf4EnMJzmAWDU",
|
||||
"location": "https://example.com/some-repo/blob/abcdefghijklmnop/compromisedfile1.java"
|
||||
},
|
||||
{
|
||||
"type": "gitleaks_rule_id_gitlab_personal_access_token",
|
||||
"token": "glpat--tG84EGK33nMLLDE70zU",
|
||||
"location": "https://example.com/some-repo/blob/abcdefghijklmnop/compromisedfile2.java"
|
||||
}]
|
||||
```
|
||||
|
||||
### Configure GitLab to interface with the Token Revocation API
|
||||
|
||||
You must configure the following database settings in the GitLab instance:
|
||||
|
||||
| Setting | Type | Description |
|
||||
| ------- | ---- | ----------- |
|
||||
| `secret_detection_token_revocation_enabled` | Boolean | Whether automatic token revocation is enabled |
|
||||
| `secret_detection_token_revocation_url` | String | A fully-qualified URL to the `/v1/revoke_tokens` endpoint of the Token Revocation API |
|
||||
| `secret_detection_revocation_token_types_url` | String | A fully-qualified URL to the `/v1/revocable_token_types` endpoint of the Token Revocation API |
|
||||
| `secret_detection_token_revocation_token` | String | A pre-shared token to authenticate requests to the Token Revocation API |
|
||||
|
||||
For example, to configure these values in the
|
||||
[Rails console](../../administration/operations/rails_console.md#starting-a-rails-console-session):
|
||||
|
||||
```ruby
|
||||
::Gitlab::CurrentSettings.update!(secret_detection_token_revocation_token: 'MYSECRETTOKEN')
|
||||
::Gitlab::CurrentSettings.update!(secret_detection_token_revocation_url: 'https://example.gitlab.com/revocation_service/v1/revoke_tokens')
|
||||
::Gitlab::CurrentSettings.update!(secret_detection_revocation_token_types_url: 'https://example.gitlab.com/revocation_service/v1/revocable_token_types')
|
||||
::Gitlab::CurrentSettings.update!(secret_detection_token_revocation_enabled: true)
|
||||
```
|
||||
|
||||
After you configure these values, the Token Revocation API will be called according to the
|
||||
[high-level architecture](../../user/application_security/secret_detection/post_processing.md#high-level-architecture)
|
||||
diagram.
|
||||
|
|
@ -195,7 +195,12 @@ Pipelines now include a Secret Detection job.
|
|||
|
||||
## Responding to a leaked secret
|
||||
|
||||
If the scanner detects a secret you should rotate it immediately. [Purging a file from the repository's history](../../project/repository/reducing_the_repo_size_using_git.md#purge-files-from-repository-history) may not be effective in removing all references to the file. Also, the secret remains in any forks of the repository.
|
||||
Secrets detected by the analyzer should be immediately rotated.
|
||||
[Purging a file from the repository's history](../../project/repository/reducing_the_repo_size_using_git.md#purge-files-from-repository-history)
|
||||
may not be effective in removing all references to the file. Additionally, the secret will remain in any existing
|
||||
forks or clones of the repository.
|
||||
|
||||
GitLab will attempt to [automatically revoke](post_processing.md) some types of leaked secrets.
|
||||
|
||||
## Pinning to specific analyzer version
|
||||
|
||||
|
|
|
|||
|
|
@ -10,26 +10,28 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
> - [Disabled by default for GitLab personal access tokens](https://gitlab.com/gitlab-org/gitlab/-/issues/371658) in GitLab 15.6 [with a flag](../../../administration/feature_flags.md) named `gitlab_pat_auto_revocation`. Available to GitLab.com only.
|
||||
> - [Enabled by default for GitLab personal access tokens](https://gitlab.com/gitlab-org/gitlab/-/issues/371658) in GitLab 15.9
|
||||
|
||||
GitLab supports running post-processing hooks after detecting a secret. These
|
||||
hooks can perform actions, like notifying the cloud service that issued the secret.
|
||||
The cloud provider can then confirm the credentials and take remediation actions, like:
|
||||
GitLab.com and self-managed supports running post-processing hooks after detecting a secret. These
|
||||
hooks can perform actions, like notifying the vendor that issued the secret.
|
||||
The vendor can then confirm the credentials and take remediation actions, like:
|
||||
|
||||
- Revoking a secret.
|
||||
- Reissuing a secret.
|
||||
- Notifying the creator of the secret.
|
||||
|
||||
GitLab SaaS supports post-processing for [GitLab personal access tokens](../../profile/personal_access_tokens.md) and Amazon Web Services (AWS).
|
||||
Post-processing workflows vary by supported cloud providers.
|
||||
GitLab supports post-processing for the following vendors and secrets:
|
||||
|
||||
Post-processing is limited to a project's default branch. The epic
|
||||
[Post-processing of leaked secrets](https://gitlab.com/groups/gitlab-org/-/epics/4639).
|
||||
contains:
|
||||
| Vendor | Secret | GitLab.com | Self-managed |
|
||||
| ----- | --- | --- | --- |
|
||||
| GitLab | [Personal access tokens](../../profile/personal_access_tokens.md) | ✅ | ✅ 15.9 and later |
|
||||
| Amazon Web Services (AWS) | [IAM access keys](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) | ✅ | ⚙ |
|
||||
|
||||
- Technical details of post-processing secrets.
|
||||
- Discussions of efforts to support additional branches.
|
||||
**Component legend**
|
||||
|
||||
- ✅ - Available by default
|
||||
- ⚙ - Requires manual integration using a [Token Revocation API](../../../development/sec/token_revocation_api.md)
|
||||
|
||||
NOTE:
|
||||
Post-processing is currently limited to a project's default branch
|
||||
Post-processing is limited to a project's default branch.
|
||||
|
||||
## High-level architecture
|
||||
|
||||
|
|
@ -40,142 +42,51 @@ sequenceDiagram
|
|||
autonumber
|
||||
GitLab Rails->>+Sidekiq: gl-secret-detection-report.json
|
||||
Sidekiq-->+Sidekiq: StoreSecurityReportsWorker
|
||||
Sidekiq-->+RevocationAPI: GET revocable keys types
|
||||
RevocationAPI-->>-Sidekiq: OK
|
||||
Sidekiq->>+RevocationAPI: POST revoke revocable keys
|
||||
RevocationAPI-->>-Sidekiq: ACCEPTED
|
||||
RevocationAPI-->>+Cloud Vendor: revoke revocable keys
|
||||
Cloud Vendor-->>+RevocationAPI: ACCEPTED
|
||||
Sidekiq-->+Token Revocation API: GET revocable keys types
|
||||
Token Revocation API-->>-Sidekiq: OK
|
||||
Sidekiq->>+Token Revocation API: POST revoke revocable keys
|
||||
Token Revocation API-->>-Sidekiq: ACCEPTED
|
||||
Token Revocation API-->>+Receiver Service: revoke revocable keys
|
||||
Receiver Service-->>+Token Revocation API: ACCEPTED
|
||||
```
|
||||
|
||||
## Integrate your cloud provider service with GitLab SaaS
|
||||
1. A pipeline with a Secret Detection job completes on the project's default branch, producing a scan
|
||||
report (**1**).
|
||||
1. The report is processed (**2**) by an asynchronous worker, which communicates with an externally
|
||||
deployed HTTP service (**3** and **4**) to determine which kinds of secrets can be automatically
|
||||
revoked.
|
||||
1. The worker sends (**5** and **6**) the list of detected secrets which the Token Revocation API is able to
|
||||
revoke.
|
||||
1. The Token Revocation API sends (**7** and **8**) each revocable token to their respective vendor's [receiver service](#integrate-your-cloud-provider-service-with-gitlabcom).
|
||||
|
||||
Third party cloud and SaaS providers can [express integration interest by filling out this form](https://forms.gle/wWpvrtLRK21Q2WJL9).
|
||||
See the [Token Revocation API](../../../development/sec/token_revocation_api.md) documentation for more
|
||||
information.
|
||||
|
||||
### Implement a vendor revocation receiver service
|
||||
## Integrate your cloud provider service with GitLab.com
|
||||
|
||||
A vendor revocation receiver service integrates with a GitLab instance to receive
|
||||
a web notification and respond to leaked token requests.
|
||||
Third-party cloud and SaaS vendors interested in automated token revocation can
|
||||
[express integration interest by filling out this form](https://forms.gle/wWpvrtLRK21Q2WJL9).
|
||||
Vendors must [implement a revocation receiver service](#implement-a-revocation-receiver-service)
|
||||
which will be called by the Token Revocation API.
|
||||
|
||||
To implement a receiver service to revoke leaked tokens:
|
||||
### Implement a revocation receiver service
|
||||
|
||||
1. Create a publicly accessible HTTP service matching the corresponding API contract
|
||||
below. Your service should be idempotent and rate-limited.
|
||||
1. When a pipeline corresponding to its revocable token type (in the example, `my_api_token`)
|
||||
completes, GitLab sends a request to your receiver service.
|
||||
1. The included URL should be publicly accessible, and contain the commit where the
|
||||
leaked token can be found. For example:
|
||||
A revocation receiver service integrates with a GitLab instance's Token Revocation API to receive and respond
|
||||
to leaked token revocation requests. The service should be a publicly accessible HTTP API that is
|
||||
idempotent and rate-limited. Requests to your service from the Token Revocation API will follow the example
|
||||
below:
|
||||
|
||||
```plaintext
|
||||
POST / HTTP/2
|
||||
Accept: */*
|
||||
Content-Type: application/json
|
||||
X-Gitlab-Token: MYSECRETTOKEN
|
||||
```plaintext
|
||||
POST / HTTP/2
|
||||
Accept: */*
|
||||
Content-Type: application/json
|
||||
X-Gitlab-Token: MYSECRETTOKEN
|
||||
|
||||
[
|
||||
{"type": "my_api_token", "token":"XXXXXXXXXXXXXXXX","url": "https://example.com/some-repo/blob/abcdefghijklmnop/compromisedfile1.java"}
|
||||
]
|
||||
```
|
||||
|
||||
## Implement a revocation service for self-managed
|
||||
|
||||
Self-managed instances interested in using the revocation capabilities must:
|
||||
|
||||
- Deploy the [RevocationAPI](#high-level-architecture).
|
||||
- Configure the GitLab instance to use the RevocationAPI.
|
||||
|
||||
A RevocationAPI must:
|
||||
|
||||
- Match a minimal API specification.
|
||||
- Provide two endpoints:
|
||||
- Fetching revocable token types.
|
||||
- Revoking leaked tokens.
|
||||
- Be rate-limited and idempotent.
|
||||
|
||||
Requests to the documented endpoints are authenticated via API tokens passed in
|
||||
the `Authorization` header. Request and response bodies, if present, are
|
||||
expected to have the content type `application/json`.
|
||||
|
||||
All endpoints may return these responses:
|
||||
|
||||
- `401 Unauthorized`
|
||||
- `405 Method Not Allowed`
|
||||
- `500 Internal Server Error`
|
||||
|
||||
### `GET /v1/revocable_token_types`
|
||||
|
||||
Returns the valid `type` values for use in the `revoke_tokens` endpoint.
|
||||
|
||||
NOTE:
|
||||
These values match the concatenation of [the `secrets` analyzer's](index.md)
|
||||
[primary identifier](../../../development/integrations/secure.md#identifiers) by means
|
||||
of concatenating the `primary_identifier.type` and `primary_identifier.value`.
|
||||
In the case below, a finding identifier matches:
|
||||
|
||||
```json
|
||||
{"type": "gitleaks_rule_id", "name": "Gitleaks rule ID GitLab Personal Access Token", "value": "GitLab Personal Access Token"}
|
||||
[
|
||||
{"type": "my_api_token", "token":"XXXXXXXXXXXXXXXX","url": "https://example.com/some-repo/~/raw/abcdefghijklmnop/compromisedfile1.java"}
|
||||
]
|
||||
```
|
||||
|
||||
| Status Code | Description |
|
||||
| ----- | --- |
|
||||
| `200` | The response body contains the valid token `type` values. |
|
||||
|
||||
Example response body:
|
||||
|
||||
```json
|
||||
{
|
||||
"types": ["gitleaks_rule_id_gitlab_personal_access_token"]
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /v1/revoke_tokens`
|
||||
|
||||
Accepts a list of tokens to be revoked by the appropriate provider.
|
||||
|
||||
| Status Code | Description |
|
||||
| ----- | --- |
|
||||
| `204` | All submitted tokens have been accepted for eventual revocation. |
|
||||
| `400` | The request body is invalid or one of the submitted token types is not supported. The request should not be retried. |
|
||||
| `429` | The provider has received too many requests. The request should be retried later. |
|
||||
|
||||
Example request body:
|
||||
|
||||
```json
|
||||
[{
|
||||
"type": "gitleaks_rule_id_gitlab_personal_access_token",
|
||||
"token": "glpat--8GMtG8Mf4EnMJzmAWDU",
|
||||
"location": "https://example.com/some-repo/blob/abcdefghijklmnop/compromisedfile1.java"
|
||||
},
|
||||
{
|
||||
"type": "gitleaks_rule_id_gitlab_personal_access_token",
|
||||
"token": "glpat--tG84EGK33nMLLDE70zU",
|
||||
"location": "https://example.com/some-repo/blob/abcdefghijklmnop/compromisedfile2.java"
|
||||
}]
|
||||
```
|
||||
|
||||
### Configure GitLab to interface with RevocationAPI
|
||||
|
||||
You must configure the following database settings in the GitLab instance:
|
||||
|
||||
- `secret_detection_token_revocation_enabled`
|
||||
- `secret_detection_token_revocation_url`
|
||||
- `secret_detection_token_revocation_token`
|
||||
- `secret_detection_revocation_token_types_url`
|
||||
|
||||
For example, to configure these values in the
|
||||
[Rails console](../../../administration/operations/rails_console.md#starting-a-rails-console-session):
|
||||
|
||||
```ruby
|
||||
::Gitlab::CurrentSettings.update!(secret_detection_token_revocation_token: 'MYSECRETTOKEN')
|
||||
::Gitlab::CurrentSettings.update!(secret_detection_token_revocation_url: 'https://example.gitlab.com/revocation_service/v1/revoke_tokens')
|
||||
::Gitlab::CurrentSettings.update!(secret_detection_revocation_token_types_url: 'https://example.gitlab.com/revocation_service/v1/revocable_token_types')
|
||||
::Gitlab::CurrentSettings.update!(secret_detection_token_revocation_enabled: true)
|
||||
```
|
||||
|
||||
After you configure these values, completing a pipeline performs these actions:
|
||||
|
||||
1. The revocation service is triggered once.
|
||||
1. A request is made to `secret_detection_revocation_token_types_url` to fetch a
|
||||
list of revocable tokens.
|
||||
1. Any Secret Detection findings matching the results of the `token_types` request
|
||||
are included in the subsequent revocation request.
|
||||
In this example, Secret Detection has determined that an instance of `my_api_token` has been leaked. The
|
||||
value of the token is provided to you, in addition to a publicly accessible URL to the raw content of the
|
||||
file containing the leaked token.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import actions, { transformBackendBadge } from '~/badges/store/actions';
|
|||
import mutationTypes from '~/badges/store/mutation_types';
|
||||
import createState from '~/badges/store/state';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
|
||||
import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import { createDummyBadge, createDummyBadgeResponse } from '../dummy_badge';
|
||||
|
||||
describe('Badges store actions', () => {
|
||||
|
|
@ -99,7 +99,7 @@ describe('Badges store actions', () => {
|
|||
|
||||
expect(dispatch.mock.calls).toEqual([['requestNewBadge']]);
|
||||
dispatch.mockClear();
|
||||
return [200, dummyResponse];
|
||||
return [HTTP_STATUS_OK, dummyResponse];
|
||||
});
|
||||
|
||||
const dummyBadge = transformBackendBadge(dummyResponse);
|
||||
|
|
@ -177,7 +177,7 @@ describe('Badges store actions', () => {
|
|||
endpointMock.replyOnce(() => {
|
||||
expect(dispatch.mock.calls).toEqual([['requestDeleteBadge', badgeId]]);
|
||||
dispatch.mockClear();
|
||||
return [200, ''];
|
||||
return [HTTP_STATUS_OK, ''];
|
||||
});
|
||||
|
||||
await actions.deleteBadge({ state, dispatch }, { id: badgeId });
|
||||
|
|
@ -266,7 +266,7 @@ describe('Badges store actions', () => {
|
|||
endpointMock.replyOnce(() => {
|
||||
expect(dispatch.mock.calls).toEqual([['requestLoadBadges', dummyData]]);
|
||||
dispatch.mockClear();
|
||||
return [200, dummyReponse];
|
||||
return [HTTP_STATUS_OK, dummyReponse];
|
||||
});
|
||||
|
||||
await actions.loadBadges({ state, dispatch }, dummyData);
|
||||
|
|
@ -381,7 +381,7 @@ describe('Badges store actions', () => {
|
|||
endpointMock.replyOnce(() => {
|
||||
expect(dispatch.mock.calls).toEqual([['requestRenderedBadge']]);
|
||||
dispatch.mockClear();
|
||||
return [200, dummyReponse];
|
||||
return [HTTP_STATUS_OK, dummyReponse];
|
||||
});
|
||||
|
||||
await actions.renderBadge({ state, dispatch });
|
||||
|
|
@ -468,7 +468,7 @@ describe('Badges store actions', () => {
|
|||
|
||||
expect(dispatch.mock.calls).toEqual([['requestUpdatedBadge']]);
|
||||
dispatch.mockClear();
|
||||
return [200, dummyResponse];
|
||||
return [HTTP_STATUS_OK, dummyResponse];
|
||||
});
|
||||
|
||||
const updatedBadge = transformBackendBadge(dummyResponse);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { GlFilteredSearchToken } from '@gitlab/ui';
|
||||
import { keyBy } from 'lodash';
|
||||
import { ListType } from '~/boards/constants';
|
||||
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import {
|
||||
OPERATORS_IS,
|
||||
OPERATORS_IS_NOT,
|
||||
|
|
@ -460,7 +461,7 @@ export const BoardsMockData = {
|
|||
|
||||
export const boardsMockInterceptor = (config) => {
|
||||
const body = BoardsMockData[config.method.toUpperCase()][config.url];
|
||||
return [200, body];
|
||||
return [HTTP_STATUS_OK, body];
|
||||
};
|
||||
|
||||
export const mockList = {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import Clusters from '~/clusters_list/components/clusters.vue';
|
|||
import ClustersEmptyState from '~/clusters_list/components/clusters_empty_state.vue';
|
||||
import ClusterStore from '~/clusters_list/store';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import { apiData } from '../mock_data';
|
||||
|
||||
describe('Clusters', () => {
|
||||
|
|
@ -68,7 +69,7 @@ describe('Clusters', () => {
|
|||
captureException = jest.spyOn(Sentry, 'captureException');
|
||||
|
||||
mock = new MockAdapter(axios);
|
||||
mockPollingApi(200, apiData, paginationHeader());
|
||||
mockPollingApi(HTTP_STATUS_OK, apiData, paginationHeader());
|
||||
|
||||
return createWrapper({});
|
||||
});
|
||||
|
|
@ -255,7 +256,7 @@ describe('Clusters', () => {
|
|||
const totalSecondPage = 500;
|
||||
|
||||
beforeEach(() => {
|
||||
mockPollingApi(200, apiData, paginationHeader(totalFirstPage, perPage, 1));
|
||||
mockPollingApi(HTTP_STATUS_OK, apiData, paginationHeader(totalFirstPage, perPage, 1));
|
||||
return createWrapper({});
|
||||
});
|
||||
|
||||
|
|
@ -269,7 +270,7 @@ describe('Clusters', () => {
|
|||
|
||||
describe('when updating currentPage', () => {
|
||||
beforeEach(() => {
|
||||
mockPollingApi(200, apiData, paginationHeader(totalSecondPage, perPage, 2));
|
||||
mockPollingApi(HTTP_STATUS_OK, apiData, paginationHeader(totalSecondPage, perPage, 2));
|
||||
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
wrapper.setData({ currentPage: 2 });
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import waitForPromises from 'helpers/wait_for_promises';
|
|||
import EditEnvironment from '~/environments/components/edit_environment.vue';
|
||||
import { createAlert } from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
|
||||
jest.mock('~/lib/utils/url_utility');
|
||||
|
|
@ -68,7 +69,7 @@ describe('~/environments/components/edit.vue', () => {
|
|||
|
||||
expect(showsLoading()).toBe(false);
|
||||
|
||||
await submitForm(expected, [200, { path: '/test' }]);
|
||||
await submitForm(expected, [HTTP_STATUS_OK, { path: '/test' }]);
|
||||
|
||||
expect(showsLoading()).toBe(true);
|
||||
});
|
||||
|
|
@ -76,7 +77,7 @@ describe('~/environments/components/edit.vue', () => {
|
|||
it('submits the updated environment on submit', async () => {
|
||||
const expected = { url: 'https://google.ca' };
|
||||
|
||||
await submitForm(expected, [200, { path: '/test' }]);
|
||||
await submitForm(expected, [HTTP_STATUS_OK, { path: '/test' }]);
|
||||
|
||||
expect(visitUrl).toHaveBeenCalledWith('/test');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import waitForPromises from 'helpers/wait_for_promises';
|
|||
import NewEnvironment from '~/environments/components/new_environment.vue';
|
||||
import { createAlert } from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
|
||||
jest.mock('~/lib/utils/url_utility');
|
||||
|
|
@ -79,7 +80,7 @@ describe('~/environments/components/new.vue', () => {
|
|||
|
||||
expect(showsLoading()).toBe(false);
|
||||
|
||||
await submitForm(expected, [200, { path: '/test' }]);
|
||||
await submitForm(expected, [HTTP_STATUS_OK, { path: '/test' }]);
|
||||
|
||||
expect(showsLoading()).toBe(true);
|
||||
});
|
||||
|
|
@ -87,7 +88,7 @@ describe('~/environments/components/new.vue', () => {
|
|||
it('submits the new environment on submit', async () => {
|
||||
const expected = { name: 'test', url: 'https://google.ca' };
|
||||
|
||||
await submitForm(expected, [200, { path: '/test' }]);
|
||||
await submitForm(expected, [HTTP_STATUS_OK, { path: '/test' }]);
|
||||
|
||||
expect(visitUrl).toHaveBeenCalledWith('/test');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ describe('error tracking settings actions', () => {
|
|||
});
|
||||
|
||||
it('should request and transform the project list', async () => {
|
||||
mock.onGet(TEST_HOST).reply(() => [200, { projects: projectList }]);
|
||||
mock.onGet(TEST_HOST).reply(() => [HTTP_STATUS_OK, { projects: projectList }]);
|
||||
await testAction(
|
||||
actions.fetchProjects,
|
||||
null,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
MSG_CONNECTION_ERROR,
|
||||
SERVICE_DELAY,
|
||||
} from '~/ide/lib/mirror';
|
||||
import { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
|
||||
import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import { getWebSocketUrl } from '~/lib/utils/url_utility';
|
||||
|
||||
jest.mock('~/ide/lib/create_diff', () => jest.fn());
|
||||
|
|
@ -19,10 +19,13 @@ const TEST_DIFF = {
|
|||
};
|
||||
const TEST_ERROR = 'Something bad happened...';
|
||||
const TEST_SUCCESS_RESPONSE = {
|
||||
data: JSON.stringify({ error: { code: 0 }, payload: { status_code: 200 } }),
|
||||
data: JSON.stringify({ error: { code: 0 }, payload: { status_code: HTTP_STATUS_OK } }),
|
||||
};
|
||||
const TEST_ERROR_RESPONSE = {
|
||||
data: JSON.stringify({ error: { code: 1, Message: TEST_ERROR }, payload: { status_code: 200 } }),
|
||||
data: JSON.stringify({
|
||||
error: { code: 1, Message: TEST_ERROR },
|
||||
payload: { status_code: HTTP_STATUS_OK },
|
||||
}),
|
||||
};
|
||||
const TEST_ERROR_PAYLOAD_RESPONSE = {
|
||||
data: JSON.stringify({
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import * as terminalService from '~/ide/services/terminals';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
|
||||
const TEST_PROJECT_PATH = 'lorem/ipsum/dolar';
|
||||
const TEST_BRANCH = 'ref';
|
||||
|
|
@ -11,7 +12,7 @@ describe('~/ide/services/terminals', () => {
|
|||
const prevRelativeUrlRoot = gon.relative_url_root;
|
||||
|
||||
beforeEach(() => {
|
||||
axiosSpy = jest.fn().mockReturnValue([200, {}]);
|
||||
axiosSpy = jest.fn().mockReturnValue([HTTP_STATUS_OK, {}]);
|
||||
|
||||
mock = new MockAdapter(axios);
|
||||
mock.onPost(/.*/).reply((...args) => axiosSpy(...args));
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import PinnedLinks from '~/issues/show/components/pinned_links.vue';
|
|||
import { POLLING_DELAY } from '~/issues/show/constants';
|
||||
import eventHub from '~/issues/show/event_hub';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
import {
|
||||
appProps,
|
||||
|
|
@ -100,7 +101,7 @@ describe('Issuable output', () => {
|
|||
mock
|
||||
.onGet('/gitlab-org/gitlab-shell/-/issues/9/realtime_changes/realtime_changes')
|
||||
.reply(() => {
|
||||
const res = Promise.resolve([200, REALTIME_REQUEST_STACK[realtimeRequestCount]]);
|
||||
const res = Promise.resolve([HTTP_STATUS_OK, REALTIME_REQUEST_STACK[realtimeRequestCount]]);
|
||||
realtimeRequestCount += 1;
|
||||
return res;
|
||||
});
|
||||
|
|
@ -336,7 +337,9 @@ describe('Issuable output', () => {
|
|||
const mockData = {
|
||||
test: [{ name: 'test', id: 'test', project_path: '/', namespace_path: '/' }],
|
||||
};
|
||||
mock.onGet('/issuable-templates-path').reply(() => Promise.resolve([200, mockData]));
|
||||
mock
|
||||
.onGet('/issuable-templates-path')
|
||||
.reply(() => Promise.resolve([HTTP_STATUS_OK, mockData]));
|
||||
|
||||
return wrapper.vm.requestTemplatesAndShowForm().then(() => {
|
||||
expect(formSpy).toHaveBeenCalledWith(mockData);
|
||||
|
|
@ -345,7 +348,9 @@ describe('Issuable output', () => {
|
|||
|
||||
it('shows the form if template names as array request is successful', () => {
|
||||
const mockData = [{ name: 'test', id: 'test', project_path: '/', namespace_path: '/' }];
|
||||
mock.onGet('/issuable-templates-path').reply(() => Promise.resolve([200, mockData]));
|
||||
mock
|
||||
.onGet('/issuable-templates-path')
|
||||
.reply(() => Promise.resolve([HTTP_STATUS_OK, mockData]));
|
||||
|
||||
return wrapper.vm.requestTemplatesAndShowForm().then(() => {
|
||||
expect(formSpy).toHaveBeenCalledWith(mockData);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { setHTMLFixture } from 'helpers/fixtures';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
|
||||
beforeAll(async () => {
|
||||
// @rails/ujs expects jQuery.ajaxPrefilter to exist if jQuery exists at
|
||||
|
|
@ -20,7 +21,7 @@ function mockXHRResponse({ responseText, responseContentType } = {}) {
|
|||
jest.spyOn(global.XMLHttpRequest.prototype, 'send').mockImplementation(function send() {
|
||||
Object.defineProperties(this, {
|
||||
readyState: { value: XMLHttpRequest.DONE },
|
||||
status: { value: 200 },
|
||||
status: { value: HTTP_STATUS_OK },
|
||||
response: { value: responseText },
|
||||
});
|
||||
this.onreadystatechange();
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import axios from 'axios';
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
|
||||
import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import { ENTER_KEY } from '~/lib/utils/keys';
|
||||
import MilestoneCombobox from '~/milestones/components/milestone_combobox.vue';
|
||||
import createStore from '~/milestones/stores/';
|
||||
|
|
@ -64,15 +64,15 @@ describe('Milestone combobox component', () => {
|
|||
|
||||
projectMilestonesApiCallSpy = jest
|
||||
.fn()
|
||||
.mockReturnValue([200, projectMilestones, { [X_TOTAL_HEADER]: '6' }]);
|
||||
.mockReturnValue([HTTP_STATUS_OK, projectMilestones, { [X_TOTAL_HEADER]: '6' }]);
|
||||
|
||||
groupMilestonesApiCallSpy = jest
|
||||
.fn()
|
||||
.mockReturnValue([200, groupMilestones, { [X_TOTAL_HEADER]: '6' }]);
|
||||
.mockReturnValue([HTTP_STATUS_OK, groupMilestones, { [X_TOTAL_HEADER]: '6' }]);
|
||||
|
||||
searchApiCallSpy = jest
|
||||
.fn()
|
||||
.mockReturnValue([200, projectMilestones, { [X_TOTAL_HEADER]: '6' }]);
|
||||
.mockReturnValue([HTTP_STATUS_OK, projectMilestones, { [X_TOTAL_HEADER]: '6' }]);
|
||||
|
||||
mock
|
||||
.onGet(`/api/v4/projects/${projectId}/milestones`)
|
||||
|
|
@ -248,9 +248,11 @@ describe('Milestone combobox component', () => {
|
|||
beforeEach(() => {
|
||||
projectMilestonesApiCallSpy = jest
|
||||
.fn()
|
||||
.mockReturnValue([200, [], { [X_TOTAL_HEADER]: '0' }]);
|
||||
.mockReturnValue([HTTP_STATUS_OK, [], { [X_TOTAL_HEADER]: '0' }]);
|
||||
|
||||
groupMilestonesApiCallSpy = jest.fn().mockReturnValue([200, [], { [X_TOTAL_HEADER]: '0' }]);
|
||||
groupMilestonesApiCallSpy = jest
|
||||
.fn()
|
||||
.mockReturnValue([HTTP_STATUS_OK, [], { [X_TOTAL_HEADER]: '0' }]);
|
||||
|
||||
createComponent();
|
||||
|
||||
|
|
@ -301,7 +303,7 @@ describe('Milestone combobox component', () => {
|
|||
beforeEach(() => {
|
||||
projectMilestonesApiCallSpy = jest
|
||||
.fn()
|
||||
.mockReturnValue([200, [], { [X_TOTAL_HEADER]: '0' }]);
|
||||
.mockReturnValue([HTTP_STATUS_OK, [], { [X_TOTAL_HEADER]: '0' }]);
|
||||
|
||||
createComponent();
|
||||
|
||||
|
|
@ -366,7 +368,7 @@ describe('Milestone combobox component', () => {
|
|||
createComponent();
|
||||
projectMilestonesApiCallSpy = jest
|
||||
.fn()
|
||||
.mockReturnValue([200, [{ title: 'v1.0' }], { [X_TOTAL_HEADER]: '1' }]);
|
||||
.mockReturnValue([HTTP_STATUS_OK, [{ title: 'v1.0' }], { [X_TOTAL_HEADER]: '1' }]);
|
||||
|
||||
return waitForRequests();
|
||||
});
|
||||
|
|
@ -430,7 +432,7 @@ describe('Milestone combobox component', () => {
|
|||
beforeEach(() => {
|
||||
groupMilestonesApiCallSpy = jest
|
||||
.fn()
|
||||
.mockReturnValue([200, [], { [X_TOTAL_HEADER]: '0' }]);
|
||||
.mockReturnValue([HTTP_STATUS_OK, [], { [X_TOTAL_HEADER]: '0' }]);
|
||||
|
||||
createComponent();
|
||||
|
||||
|
|
@ -495,7 +497,11 @@ describe('Milestone combobox component', () => {
|
|||
createComponent();
|
||||
groupMilestonesApiCallSpy = jest
|
||||
.fn()
|
||||
.mockReturnValue([200, [{ title: 'group-v1.0' }], { [X_TOTAL_HEADER]: '1' }]);
|
||||
.mockReturnValue([
|
||||
HTTP_STATUS_OK,
|
||||
[{ title: 'group-v1.0' }],
|
||||
{ [X_TOTAL_HEADER]: '1' },
|
||||
]);
|
||||
|
||||
return waitForRequests();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// Copied to ee/spec/frontend/notes/mock_data.js
|
||||
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
export const notesDataMock = {
|
||||
|
|
@ -655,11 +656,11 @@ export const DISCUSSION_NOTE_RESPONSE_MAP = {
|
|||
};
|
||||
|
||||
export function getIndividualNoteResponse(config) {
|
||||
return [200, INDIVIDUAL_NOTE_RESPONSE_MAP[config.method.toUpperCase()][config.url]];
|
||||
return [HTTP_STATUS_OK, INDIVIDUAL_NOTE_RESPONSE_MAP[config.method.toUpperCase()][config.url]];
|
||||
}
|
||||
|
||||
export function getDiscussionNoteResponse(config) {
|
||||
return [200, DISCUSSION_NOTE_RESPONSE_MAP[config.method.toUpperCase()][config.url]];
|
||||
return [HTTP_STATUS_OK, DISCUSSION_NOTE_RESPONSE_MAP[config.method.toUpperCase()][config.url]];
|
||||
}
|
||||
|
||||
export const notesWithDescriptionChanges = [
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { nextTick } from 'vue';
|
|||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import { createAlert } from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import UpdateUsername from '~/profile/account/components/update_username.vue';
|
||||
|
||||
jest.mock('~/flash');
|
||||
|
|
@ -97,7 +97,7 @@ describe('UpdateUsername component', () => {
|
|||
});
|
||||
|
||||
it('executes API call on confirmation button click', async () => {
|
||||
axiosMock.onPut(actionUrl).replyOnce(() => [200, { message: 'Username changed' }]);
|
||||
axiosMock.onPut(actionUrl).replyOnce(() => [HTTP_STATUS_OK, { message: 'Username changed' }]);
|
||||
jest.spyOn(axios, 'put');
|
||||
|
||||
await wrapper.vm.onConfirm();
|
||||
|
|
@ -114,7 +114,7 @@ describe('UpdateUsername component', () => {
|
|||
expect(openModalBtn.props('disabled')).toBe(false);
|
||||
expect(openModalBtn.props('loading')).toBe(true);
|
||||
|
||||
return [200, { message: 'Username changed' }];
|
||||
return [HTTP_STATUS_OK, { message: 'Username changed' }];
|
||||
});
|
||||
|
||||
await wrapper.vm.onConfirm();
|
||||
|
|
|
|||
|
|
@ -9,7 +9,11 @@ import commit from 'test_fixtures/api/commits/commit.json';
|
|||
import branches from 'test_fixtures/api/branches/branches.json';
|
||||
import tags from 'test_fixtures/api/tags/tags.json';
|
||||
import { trimText } from 'helpers/text_helper';
|
||||
import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_NOT_FOUND } from '~/lib/utils/http_status';
|
||||
import {
|
||||
HTTP_STATUS_INTERNAL_SERVER_ERROR,
|
||||
HTTP_STATUS_NOT_FOUND,
|
||||
HTTP_STATUS_OK,
|
||||
} from '~/lib/utils/http_status';
|
||||
import { ENTER_KEY } from '~/lib/utils/keys';
|
||||
import { sprintf } from '~/locale';
|
||||
import RefSelector from '~/ref/components/ref_selector.vue';
|
||||
|
|
@ -69,9 +73,11 @@ describe('Ref selector component', () => {
|
|||
|
||||
branchesApiCallSpy = jest
|
||||
.fn()
|
||||
.mockReturnValue([200, fixtures.branches, { [X_TOTAL_HEADER]: '123' }]);
|
||||
tagsApiCallSpy = jest.fn().mockReturnValue([200, fixtures.tags, { [X_TOTAL_HEADER]: '456' }]);
|
||||
commitApiCallSpy = jest.fn().mockReturnValue([200, fixtures.commit]);
|
||||
.mockReturnValue([HTTP_STATUS_OK, fixtures.branches, { [X_TOTAL_HEADER]: '123' }]);
|
||||
tagsApiCallSpy = jest
|
||||
.fn()
|
||||
.mockReturnValue([HTTP_STATUS_OK, fixtures.tags, { [X_TOTAL_HEADER]: '456' }]);
|
||||
commitApiCallSpy = jest.fn().mockReturnValue([HTTP_STATUS_OK, fixtures.commit]);
|
||||
requestSpies = { branchesApiCallSpy, tagsApiCallSpy, commitApiCallSpy };
|
||||
|
||||
mock
|
||||
|
|
@ -309,8 +315,10 @@ describe('Ref selector component', () => {
|
|||
|
||||
describe('when no results are found', () => {
|
||||
beforeEach(() => {
|
||||
branchesApiCallSpy = jest.fn().mockReturnValue([200, [], { [X_TOTAL_HEADER]: '0' }]);
|
||||
tagsApiCallSpy = jest.fn().mockReturnValue([200, [], { [X_TOTAL_HEADER]: '0' }]);
|
||||
branchesApiCallSpy = jest
|
||||
.fn()
|
||||
.mockReturnValue([HTTP_STATUS_OK, [], { [X_TOTAL_HEADER]: '0' }]);
|
||||
tagsApiCallSpy = jest.fn().mockReturnValue([HTTP_STATUS_OK, [], { [X_TOTAL_HEADER]: '0' }]);
|
||||
commitApiCallSpy = jest.fn().mockReturnValue([HTTP_STATUS_NOT_FOUND]);
|
||||
|
||||
createComponent();
|
||||
|
|
@ -386,7 +394,9 @@ describe('Ref selector component', () => {
|
|||
|
||||
describe('when the branches search returns no results', () => {
|
||||
beforeEach(() => {
|
||||
branchesApiCallSpy = jest.fn().mockReturnValue([200, [], { [X_TOTAL_HEADER]: '0' }]);
|
||||
branchesApiCallSpy = jest
|
||||
.fn()
|
||||
.mockReturnValue([HTTP_STATUS_OK, [], { [X_TOTAL_HEADER]: '0' }]);
|
||||
|
||||
createComponent();
|
||||
|
||||
|
|
@ -451,7 +461,9 @@ describe('Ref selector component', () => {
|
|||
|
||||
describe('when the tags search returns no results', () => {
|
||||
beforeEach(() => {
|
||||
tagsApiCallSpy = jest.fn().mockReturnValue([200, [], { [X_TOTAL_HEADER]: '0' }]);
|
||||
tagsApiCallSpy = jest
|
||||
.fn()
|
||||
.mockReturnValue([HTTP_STATUS_OK, [], { [X_TOTAL_HEADER]: '0' }]);
|
||||
|
||||
createComponent();
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { mountExtended } from 'helpers/vue_test_utils_helper';
|
|||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import api from '~/api';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
|
||||
import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import Poll from '~/lib/utils/poll';
|
||||
import extensionsContainer from '~/vue_merge_request_widget/components/extensions/container';
|
||||
import { registerExtension } from '~/vue_merge_request_widget/components/extensions';
|
||||
|
|
@ -23,7 +23,6 @@ describe('Terraform extension', () => {
|
|||
let mock;
|
||||
|
||||
const endpoint = '/path/to/terraform/report.json';
|
||||
const successStatusCode = 200;
|
||||
|
||||
const findListItem = (at) => wrapper.findAllByTestId('extension-list-item').at(at);
|
||||
|
||||
|
|
@ -57,7 +56,7 @@ describe('Terraform extension', () => {
|
|||
describe('while loading', () => {
|
||||
const loadingText = 'Loading Terraform reports...';
|
||||
it('should render loading text', async () => {
|
||||
mockPollingApi(successStatusCode, plans, {});
|
||||
mockPollingApi(HTTP_STATUS_OK, plans, {});
|
||||
createComponent();
|
||||
|
||||
expect(wrapper.text()).toContain(loadingText);
|
||||
|
|
@ -85,7 +84,7 @@ describe('Terraform extension', () => {
|
|||
${'1 valid and 2 invalid reports'} | ${{ 0: validPlanWithName, 1: invalidPlanWithName, 2: invalidPlanWithName }} | ${'Terraform report was generated in your pipelines'} | ${'2 Terraform reports failed to generate'}
|
||||
`('and received $responseType', ({ response, summaryTitle, summarySubtitle }) => {
|
||||
beforeEach(async () => {
|
||||
mockPollingApi(successStatusCode, response, {});
|
||||
mockPollingApi(HTTP_STATUS_OK, response, {});
|
||||
return createComponent();
|
||||
});
|
||||
|
||||
|
|
@ -102,7 +101,7 @@ describe('Terraform extension', () => {
|
|||
|
||||
describe('expanded data', () => {
|
||||
beforeEach(async () => {
|
||||
mockPollingApi(successStatusCode, plans, {});
|
||||
mockPollingApi(HTTP_STATUS_OK, plans, {});
|
||||
await createComponent();
|
||||
|
||||
wrapper.findByTestId('toggle-button').trigger('click');
|
||||
|
|
@ -164,7 +163,7 @@ describe('Terraform extension', () => {
|
|||
|
||||
describe('successful poll', () => {
|
||||
beforeEach(() => {
|
||||
mockPollingApi(successStatusCode, plans, {});
|
||||
mockPollingApi(HTTP_STATUS_OK, plans, {});
|
||||
|
||||
return createComponent();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { Response } from 'miragejs';
|
||||
import emojis from 'public/-/emojis/2/emojis.json';
|
||||
import { EMOJI_VERSION } from '~/emoji';
|
||||
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
|
||||
export default (server) => {
|
||||
server.get(`/-/emojis/${EMOJI_VERSION}/emojis.json`, () => {
|
||||
return new Response(200, {}, emojis);
|
||||
return new Response(HTTP_STATUS_OK, {}, emojis);
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,93 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe RetryBackfillTraversalIds, :migration, feature_category: :subgroups do
|
||||
include ReloadHelpers
|
||||
|
||||
let!(:namespaces_table) { table(:namespaces) }
|
||||
|
||||
context 'when BackfillNamespaceTraversalIdsRoots jobs are pending' do
|
||||
before do
|
||||
table(:background_migration_jobs).create!(
|
||||
class_name: 'BackfillNamespaceTraversalIdsRoots',
|
||||
arguments: [1, 4, 100],
|
||||
status: Gitlab::Database::BackgroundMigrationJob.statuses['pending']
|
||||
)
|
||||
table(:background_migration_jobs).create!(
|
||||
class_name: 'BackfillNamespaceTraversalIdsRoots',
|
||||
arguments: [5, 9, 100],
|
||||
status: Gitlab::Database::BackgroundMigrationJob.statuses['succeeded']
|
||||
)
|
||||
end
|
||||
|
||||
it 'queues pending jobs' do
|
||||
migrate!
|
||||
|
||||
expect(BackgroundMigrationWorker.jobs.length).to eq(1)
|
||||
expect(BackgroundMigrationWorker.jobs[0]['args']).to eq(['BackfillNamespaceTraversalIdsRoots', [1, 4, 100]])
|
||||
expect(BackgroundMigrationWorker.jobs[0]['at']).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when BackfillNamespaceTraversalIdsChildren jobs are pending' do
|
||||
before do
|
||||
table(:background_migration_jobs).create!(
|
||||
class_name: 'BackfillNamespaceTraversalIdsChildren',
|
||||
arguments: [1, 4, 100],
|
||||
status: Gitlab::Database::BackgroundMigrationJob.statuses['pending']
|
||||
)
|
||||
table(:background_migration_jobs).create!(
|
||||
class_name: 'BackfillNamespaceTraversalIdsRoots',
|
||||
arguments: [5, 9, 100],
|
||||
status: Gitlab::Database::BackgroundMigrationJob.statuses['succeeded']
|
||||
)
|
||||
end
|
||||
|
||||
it 'queues pending jobs' do
|
||||
migrate!
|
||||
|
||||
expect(BackgroundMigrationWorker.jobs.length).to eq(1)
|
||||
expect(BackgroundMigrationWorker.jobs[0]['args']).to eq(['BackfillNamespaceTraversalIdsChildren', [1, 4, 100]])
|
||||
expect(BackgroundMigrationWorker.jobs[0]['at']).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when BackfillNamespaceTraversalIdsRoots and BackfillNamespaceTraversalIdsChildren jobs are pending' do
|
||||
before do
|
||||
table(:background_migration_jobs).create!(
|
||||
class_name: 'BackfillNamespaceTraversalIdsRoots',
|
||||
arguments: [1, 4, 100],
|
||||
status: Gitlab::Database::BackgroundMigrationJob.statuses['pending']
|
||||
)
|
||||
table(:background_migration_jobs).create!(
|
||||
class_name: 'BackfillNamespaceTraversalIdsChildren',
|
||||
arguments: [5, 9, 100],
|
||||
status: Gitlab::Database::BackgroundMigrationJob.statuses['pending']
|
||||
)
|
||||
table(:background_migration_jobs).create!(
|
||||
class_name: 'BackfillNamespaceTraversalIdsRoots',
|
||||
arguments: [11, 14, 100],
|
||||
status: Gitlab::Database::BackgroundMigrationJob.statuses['succeeded']
|
||||
)
|
||||
table(:background_migration_jobs).create!(
|
||||
class_name: 'BackfillNamespaceTraversalIdsChildren',
|
||||
arguments: [15, 19, 100],
|
||||
status: Gitlab::Database::BackgroundMigrationJob.statuses['succeeded']
|
||||
)
|
||||
end
|
||||
|
||||
it 'queues pending jobs' do
|
||||
freeze_time do
|
||||
migrate!
|
||||
|
||||
expect(BackgroundMigrationWorker.jobs.length).to eq(2)
|
||||
expect(BackgroundMigrationWorker.jobs[0]['args']).to eq(['BackfillNamespaceTraversalIdsRoots', [1, 4, 100]])
|
||||
expect(BackgroundMigrationWorker.jobs[0]['at']).to be_nil
|
||||
expect(BackgroundMigrationWorker.jobs[1]['args']).to eq(['BackfillNamespaceTraversalIdsChildren', [5, 9, 100]])
|
||||
expect(BackgroundMigrationWorker.jobs[1]['at']).to eq(RetryBackfillTraversalIds::DELAY_INTERVAL.from_now.to_f)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2,17 +2,17 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Clusters::Agents::RefreshAuthorizationService do
|
||||
RSpec.describe Clusters::Agents::RefreshAuthorizationService, feature_category: :kubernetes_management do
|
||||
describe '#execute' do
|
||||
let_it_be(:root_ancestor) { create(:group) }
|
||||
|
||||
let_it_be(:removed_group) { create(:group, parent: root_ancestor) }
|
||||
let_it_be(:modified_group) { create(:group, parent: root_ancestor) }
|
||||
let_it_be(:added_group) { create(:group, parent: root_ancestor) }
|
||||
let_it_be(:added_group) { create(:group, path: 'group-path-with-UPPERCASE', parent: root_ancestor) }
|
||||
|
||||
let_it_be(:removed_project) { create(:project, namespace: root_ancestor) }
|
||||
let_it_be(:modified_project) { create(:project, namespace: root_ancestor) }
|
||||
let_it_be(:added_project) { create(:project, namespace: root_ancestor) }
|
||||
let_it_be(:added_project) { create(:project, path: 'project-path-with-UPPERCASE', namespace: root_ancestor) }
|
||||
|
||||
let(:project) { create(:project, namespace: root_ancestor) }
|
||||
let(:agent) { create(:cluster_agent, project: project) }
|
||||
|
|
@ -22,11 +22,13 @@ RSpec.describe Clusters::Agents::RefreshAuthorizationService do
|
|||
ci_access: {
|
||||
groups: [
|
||||
{ id: added_group.full_path, default_namespace: 'default' },
|
||||
{ id: modified_group.full_path, default_namespace: 'new-namespace' }
|
||||
# Uppercase path verifies case-insensitive matching.
|
||||
{ id: modified_group.full_path.upcase, default_namespace: 'new-namespace' }
|
||||
],
|
||||
projects: [
|
||||
{ id: added_project.full_path, default_namespace: 'default' },
|
||||
{ id: modified_project.full_path, default_namespace: 'new-namespace' }
|
||||
# Uppercase path verifies case-insensitive matching.
|
||||
{ id: modified_project.full_path.upcase, default_namespace: 'new-namespace' }
|
||||
]
|
||||
}
|
||||
}.deep_stringify_keys
|
||||
|
|
|
|||
Loading…
Reference in New Issue