diff --git a/Gemfile b/Gemfile
index 026d7bee543..bdc0c246298 100644
--- a/Gemfile
+++ b/Gemfile
@@ -561,3 +561,6 @@ gem 'ed25519', '~> 1.3.0'
# Error Tracking OpenAPI client
# See https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/development/rake_tasks.md#update-openapi-client-for-error-tracking-feature
gem 'error_tracking_open_api', path: 'vendor/gems/error_tracking_open_api'
+
+# Vulnerability advisories
+gem 'cvss-suite', '~> 3.0.1', require: 'cvss_suite'
diff --git a/Gemfile.checksum b/Gemfile.checksum
index 4480ebef05e..02da640c42f 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -91,6 +91,7 @@
{"name":"creole","version":"0.5.0","platform":"ruby","checksum":"951701e2d80760f156b1cb2a93471ca97c076289becc067a33b745133ed32c03"},
{"name":"crystalball","version":"0.7.0","platform":"ruby","checksum":"6e729f372a5071daec877adb40c5df4cb25fe21f350635e2a9624373fc151ef2"},
{"name":"css_parser","version":"1.11.0","platform":"ruby","checksum":"568926c3193579446ad3e3f9d761c73e2918ee5b3b7757a1a49ec166c67d6de1"},
+{"name":"cvss-suite","version":"3.0.1","platform":"ruby","checksum":"b5ca9e9e94032a42fd0dc28c1e305378b62c949e35ed7111fc4a1d76f68ad3f9"},
{"name":"danger","version":"8.6.1","platform":"ruby","checksum":"d95eb58b41f68d3aaa9bbef697916b6b4d161a38819517c98562531be75cdfd8"},
{"name":"danger-gitlab","version":"8.0.0","platform":"ruby","checksum":"497dd7d0f6513913de651019223d8058cf494df10acbd17de92b175dfa04a3a8"},
{"name":"database_cleaner","version":"1.7.0","platform":"ruby","checksum":"bdf833c197afac7054015bcde2567c3834c366bbfe6a377c30151ca984b32016"},
diff --git a/Gemfile.lock b/Gemfile.lock
index 54209a20cbd..6a360b08e6e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -303,6 +303,7 @@ GEM
git
css_parser (1.11.0)
addressable
+ cvss-suite (3.0.1)
danger (8.6.1)
claide (~> 1.0)
claide-plugins (>= 0.9.2)
@@ -1562,6 +1563,7 @@ DEPENDENCIES
countries (~> 3.0)
creole (~> 0.5.0)
crystalball (~> 0.7.0)
+ cvss-suite (~> 3.0.1)
database_cleaner (~> 1.7.0)
deckar01-task_list (= 2.3.1)
declarative_policy (~> 1.1.0)
diff --git a/app/assets/javascripts/add_context_commits_modal/components/add_context_commits_modal_wrapper.vue b/app/assets/javascripts/add_context_commits_modal/components/add_context_commits_modal_wrapper.vue
index 8ad218ab97b..a41ff42df20 100644
--- a/app/assets/javascripts/add_context_commits_modal/components/add_context_commits_modal_wrapper.vue
+++ b/app/assets/javascripts/add_context_commits_modal/components/add_context_commits_modal_wrapper.vue
@@ -2,7 +2,7 @@
import { GlModal, GlTabs, GlTab, GlSearchBoxByType, GlSprintf, GlBadge } from '@gitlab/ui';
import { mapState, mapActions } from 'vuex';
import ReviewTabContainer from '~/add_context_commits_modal/components/review_tab_container.vue';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import { BV_SHOW_MODAL } from '~/lib/utils/constants';
import { s__ } from '~/locale';
import eventHub from '../event_hub';
@@ -193,7 +193,7 @@ export default {
window.location.reload();
}
if (!values[0] && !values[1]) {
- createFlash({
+ createAlert({
message: s__(
'ContextCommits|Failed to create/remove context commits. Please try again.',
),
diff --git a/app/assets/javascripts/add_context_commits_modal/store/actions.js b/app/assets/javascripts/add_context_commits_modal/store/actions.js
index 4e5a2c7b371..d4c9db2fa33 100644
--- a/app/assets/javascripts/add_context_commits_modal/store/actions.js
+++ b/app/assets/javascripts/add_context_commits_modal/store/actions.js
@@ -1,6 +1,6 @@
import _ from 'lodash';
import Api from '~/api';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { s__ } from '~/locale';
import * as types from './mutation_types';
@@ -71,7 +71,7 @@ export const createContextCommits = ({ state }, { commits, forceReload = false }
})
.catch(() => {
if (forceReload) {
- createFlash({
+ createAlert({
message: s__('ContextCommits|Failed to create context commits. Please try again.'),
});
}
@@ -113,7 +113,7 @@ export const removeContextCommits = ({ state }, forceReload = false) =>
})
.catch(() => {
if (forceReload) {
- createFlash({
+ createAlert({
message: s__('ContextCommits|Failed to delete context commits. Please try again.'),
});
}
diff --git a/app/assets/javascripts/admin/deploy_keys/components/table.vue b/app/assets/javascripts/admin/deploy_keys/components/table.vue
index 6b140590938..be85ee43891 100644
--- a/app/assets/javascripts/admin/deploy_keys/components/table.vue
+++ b/app/assets/javascripts/admin/deploy_keys/components/table.vue
@@ -5,7 +5,7 @@ import { __ } from '~/locale';
import Api, { DEFAULT_PER_PAGE } from '~/api';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { cleanLeadingSeparator } from '~/lib/utils/url_utility';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import csrf from '~/lib/utils/csrf';
export default {
@@ -151,7 +151,7 @@ export default {
}),
);
} catch (error) {
- createFlash({
+ createAlert({
message: this.$options.i18n.apiErrorMessage,
captureError: true,
error,
diff --git a/app/assets/javascripts/admin/statistics_panel/store/actions.js b/app/assets/javascripts/admin/statistics_panel/store/actions.js
index 77782cdc187..4f952698d7a 100644
--- a/app/assets/javascripts/admin/statistics_panel/store/actions.js
+++ b/app/assets/javascripts/admin/statistics_panel/store/actions.js
@@ -1,5 +1,5 @@
import Api from '~/api';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { s__ } from '~/locale';
import * as types from './mutation_types';
@@ -21,7 +21,7 @@ export const receiveStatisticsSuccess = ({ commit }, statistics) =>
export const receiveStatisticsError = ({ commit }, error) => {
commit(types.RECEIVE_STATISTICS_ERROR, error);
- createFlash({
+ createAlert({
message: s__('AdminDashboard|Error loading the statistics. Please try again'),
});
};
diff --git a/app/assets/javascripts/admin/users/components/users_table.vue b/app/assets/javascripts/admin/users/components/users_table.vue
index b4b84594276..f569cda0a4b 100644
--- a/app/assets/javascripts/admin/users/components/users_table.vue
+++ b/app/assets/javascripts/admin/users/components/users_table.vue
@@ -1,6 +1,6 @@
-
+
:`).
+
+You can also use different agents for different Auto DevOps jobs. For instance, you can use one agent for `staging` jobs and a different agent for `production` jobs. To use multiple agents, define a unique CI/CD variable for each agent.
+
+For example:
+
+1. Add two [environment-scoped CI/CD variables](../../../ci/variables/index.md#limit-the-environment-scope-of-a-cicd-variable) and name both `KUBE_CONTEXT`.
+1. Set the `environment` of the first variable to `staging`. Set the value of the variable to `:`.
+1. Set the `environment` of the second variable to `production`. Set the value of the variable to `:`.
+
+When the `staging` job runs, it will connect to the cluster via the agent named ``, and when the `production` job runs it will connect to the cluster via the agent named ``.
+
## Restrict project and group access by using impersonation **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345014) in GitLab 14.5.
@@ -260,6 +274,7 @@ See the [official Kubernetes documentation for details](https://kubernetes.io/do
## Related topics
- [Self-paced classroom workshop](https://gitlab-for-eks.awsworkshop.io) (Uses AWS EKS, but you can use for other Kubernetes clusters)
+- [Configure Auto DevOps](../../../topics/autodevops/cloud_deployments/auto_devops_with_gke.md#configure-auto-devops)
## Troubleshooting
diff --git a/doc/user/clusters/agent/install/index.md b/doc/user/clusters/agent/install/index.md
index 6f22bbec440..6d9122c3449 100644
--- a/doc/user/clusters/agent/install/index.md
+++ b/doc/user/clusters/agent/install/index.md
@@ -110,6 +110,9 @@ in your cluster. You can either:
If you do not know which one to choose, we recommend starting with Helm.
+NOTE:
+To connect to multiple clusters, you must configure, register, and install an agent in each cluster. Make sure to give each agent a unique name.
+
#### Install the agent with Helm
To install the agent on your cluster using Helm:
diff --git a/lib/api/entities/bulk_imports/entity_failure.rb b/lib/api/entities/bulk_imports/entity_failure.rb
index a3dbe3280ee..db553176999 100644
--- a/lib/api/entities/bulk_imports/entity_failure.rb
+++ b/lib/api/entities/bulk_imports/entity_failure.rb
@@ -4,9 +4,12 @@ module API
module Entities
module BulkImports
class EntityFailure < Grape::Entity
+ expose :exception_message do |failure|
+ ::Projects::ImportErrorFilter.filter_message(failure.exception_message.truncate(72))
+ end
+ expose :exception_class
expose :pipeline_class
expose :pipeline_step
- expose :exception_class
expose :correlation_id_value
expose :created_at
end
diff --git a/lib/gitlab/diff/highlight_cache.rb b/lib/gitlab/diff/highlight_cache.rb
index 084ce63e36a..d6f5e45c034 100644
--- a/lib/gitlab/diff/highlight_cache.rb
+++ b/lib/gitlab/diff/highlight_cache.rb
@@ -82,16 +82,6 @@ module Gitlab
private
- def expiration
- return 1.day unless Feature.enabled?(:highlight_diffs_renewable_expiration, diffable.project)
-
- if Feature.enabled?(:highlight_diffs_short_renewable_expiration, diffable.project)
- EXPIRATION
- else
- 8.hours
- end
- end
-
def set_highlighted_diff_lines(diff_file, content)
diff_file.highlighted_diff_lines = content.map do |line|
Gitlab::Diff::Line.safe_init_from_hash(line)
@@ -147,7 +137,7 @@ module Gitlab
end
# HSETs have to have their expiration date manually updated
- pipeline.expire(key, expiration)
+ pipeline.expire(key, EXPIRATION)
end
record_memory_usage(fetch_memory_usage(redis, key))
@@ -197,14 +187,12 @@ module Gitlab
return {} unless file_paths.any?
results = []
- cache_key = key
- highlight_diffs_renewable_expiration_enabled = Feature.enabled?(:highlight_diffs_renewable_expiration, diffable.project)
- expiration_period = expiration
+ cache_key = key # Moving out redis calls for feature flags out of redis.pipelined
Gitlab::Redis::Cache.with do |redis|
redis.pipelined do |pipeline|
results = pipeline.hmget(cache_key, file_paths)
- pipeline.expire(key, expiration_period) if highlight_diffs_renewable_expiration_enabled
+ pipeline.expire(key, EXPIRATION)
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 56108695bf5..d3b3a76a50b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -34532,9 +34532,6 @@ msgstr ""
msgid "Runners|Members of the %{type} can register runners"
msgstr ""
-msgid "Runners|Multi-project runners cannot be deleted"
-msgstr ""
-
msgid "Runners|Name"
msgstr ""
@@ -34648,9 +34645,6 @@ msgstr ""
msgid "Runners|Runner authentication tokens will expire based on a set interval. They will automatically rotate once expired."
msgstr ""
-msgid "Runners|Runner cannot be deleted, please contact your administrator"
-msgstr ""
-
msgid "Runners|Runner has contacted GitLab within the last %{elapsedTime}"
msgstr ""
diff --git a/spec/features/groups/group_runners_spec.rb b/spec/features/groups/group_runners_spec.rb
index ada03726c97..9c7e1877bb3 100644
--- a/spec/features/groups/group_runners_spec.rb
+++ b/spec/features/groups/group_runners_spec.rb
@@ -123,7 +123,7 @@ RSpec.describe "Group Runners" do
visit group_runners_path(group)
within_runner_row(runner.id) do
- expect(page).to have_button 'Delete runner', disabled: true
+ expect(page).not_to have_button 'Delete runner'
end
end
end
diff --git a/spec/frontend/admin/deploy_keys/components/table_spec.js b/spec/frontend/admin/deploy_keys/components/table_spec.js
index a18506c0916..4d4a2caedde 100644
--- a/spec/frontend/admin/deploy_keys/components/table_spec.js
+++ b/spec/frontend/admin/deploy_keys/components/table_spec.js
@@ -9,7 +9,7 @@ import { stubComponent } from 'helpers/stub_component';
import DeployKeysTable from '~/admin/deploy_keys/components/table.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import Api, { DEFAULT_PER_PAGE } from '~/api';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
jest.mock('~/api');
jest.mock('~/flash');
@@ -243,7 +243,7 @@ describe('DeployKeysTable', () => {
itRendersTheEmptyState();
it('displays flash', () => {
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: DeployKeysTable.i18n.apiErrorMessage,
captureError: true,
error,
diff --git a/spec/frontend/admin/users/components/users_table_spec.js b/spec/frontend/admin/users/components/users_table_spec.js
index fe07f0fce00..a0aec347b6b 100644
--- a/spec/frontend/admin/users/components/users_table_spec.js
+++ b/spec/frontend/admin/users/components/users_table_spec.js
@@ -10,7 +10,7 @@ import AdminUserActions from '~/admin/users/components/user_actions.vue';
import AdminUserAvatar from '~/admin/users/components/user_avatar.vue';
import AdminUsersTable from '~/admin/users/components/users_table.vue';
import getUsersGroupCountsQuery from '~/admin/users/graphql/queries/get_users_group_counts.query.graphql';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import AdminUserDate from '~/vue_shared/components/user_date.vue';
import { users, paths, createGroupCountResponse } from '../mock_data';
@@ -135,7 +135,7 @@ describe('AdminUsersTable component', () => {
});
it('creates a flash message and captures the error', () => {
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: 'Could not load user group counts. Please refresh the page to try again.',
captureError: true,
error: expect.any(Error),
diff --git a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
index 0266adeb6c7..fcefcb7cf66 100644
--- a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
+++ b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
@@ -30,7 +30,7 @@ import {
INTEGRATION_INACTIVE_PAYLOAD_TEST_ERROR,
DELETE_INTEGRATION_ERROR,
} from '~/alerts_settings/utils/error_messages';
-import createFlash, { FLASH_TYPES } from '~/flash';
+import { createAlert, VARIANT_SUCCESS } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import httpStatusCodes from '~/lib/utils/http_status';
import {
@@ -327,7 +327,7 @@ describe('AlertsSettingsWrapper', () => {
await waitForPromises();
- expect(createFlash).toHaveBeenCalledWith({ message: ADD_INTEGRATION_ERROR });
+ expect(createAlert).toHaveBeenCalledWith({ message: ADD_INTEGRATION_ERROR });
});
it('shows an error alert when integration token reset fails', async () => {
@@ -336,7 +336,7 @@ describe('AlertsSettingsWrapper', () => {
findAlertsSettingsForm().vm.$emit('reset-token', {});
await waitForPromises();
- expect(createFlash).toHaveBeenCalledWith({ message: RESET_INTEGRATION_TOKEN_ERROR });
+ expect(createAlert).toHaveBeenCalledWith({ message: RESET_INTEGRATION_TOKEN_ERROR });
});
it('shows an error alert when integration update fails', async () => {
@@ -345,7 +345,7 @@ describe('AlertsSettingsWrapper', () => {
findAlertsSettingsForm().vm.$emit('update-integration', {});
await waitForPromises();
- expect(createFlash).toHaveBeenCalledWith({ message: UPDATE_INTEGRATION_ERROR });
+ expect(createAlert).toHaveBeenCalledWith({ message: UPDATE_INTEGRATION_ERROR });
});
describe('Test alert failure', () => {
@@ -360,17 +360,17 @@ describe('AlertsSettingsWrapper', () => {
it('shows an error alert when integration test payload is invalid', async () => {
mock.onPost(/(.*)/).replyOnce(httpStatusCodes.UNPROCESSABLE_ENTITY);
await wrapper.vm.testAlertPayload({ endpoint: '', data: '', token: '' });
- expect(createFlash).toHaveBeenCalledWith({ message: INTEGRATION_PAYLOAD_TEST_ERROR });
- expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createAlert).toHaveBeenCalledWith({ message: INTEGRATION_PAYLOAD_TEST_ERROR });
+ expect(createAlert).toHaveBeenCalledTimes(1);
});
it('shows an error alert when integration is not activated', async () => {
mock.onPost(/(.*)/).replyOnce(httpStatusCodes.FORBIDDEN);
await wrapper.vm.testAlertPayload({ endpoint: '', data: '', token: '' });
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: INTEGRATION_INACTIVE_PAYLOAD_TEST_ERROR,
});
- expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createAlert).toHaveBeenCalledTimes(1);
});
});
@@ -444,9 +444,9 @@ describe('AlertsSettingsWrapper', () => {
jest.spyOn(alertsUpdateService, 'updateTestAlert').mockResolvedValueOnce({});
findAlertsSettingsForm().vm.$emit('test-alert-payload', '');
await waitForPromises();
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: i18n.alertSent,
- type: FLASH_TYPES.SUCCESS,
+ variant: VARIANT_SUCCESS,
});
});
@@ -454,7 +454,7 @@ describe('AlertsSettingsWrapper', () => {
jest.spyOn(alertsUpdateService, 'updateTestAlert').mockRejectedValueOnce({});
findAlertsSettingsForm().vm.$emit('test-alert-payload', '');
await waitForPromises();
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: INTEGRATION_PAYLOAD_TEST_ERROR,
});
});
@@ -486,7 +486,7 @@ describe('AlertsSettingsWrapper', () => {
await destroyHttpIntegration(wrapper);
await waitForPromises();
- expect(createFlash).toHaveBeenCalledWith({ message: 'Houston, we have a problem' });
+ expect(createAlert).toHaveBeenCalledWith({ message: 'Houston, we have a problem' });
});
it('displays flash if mutation had a non-recoverable error', async () => {
@@ -497,7 +497,7 @@ describe('AlertsSettingsWrapper', () => {
await destroyHttpIntegration(wrapper);
await waitForPromises();
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: DELETE_INTEGRATION_ERROR,
});
});
diff --git a/spec/frontend/cycle_analytics/value_stream_metrics_spec.js b/spec/frontend/cycle_analytics/value_stream_metrics_spec.js
index 9c8cd6a3dbc..948dc5c9be2 100644
--- a/spec/frontend/cycle_analytics/value_stream_metrics_spec.js
+++ b/spec/frontend/cycle_analytics/value_stream_metrics_spec.js
@@ -8,7 +8,7 @@ import { METRIC_TYPE_SUMMARY } from '~/api/analytics_api';
import { VSA_METRICS_GROUPS, METRICS_POPOVER_CONTENT } from '~/analytics/shared/constants';
import { prepareTimeMetricsData } from '~/analytics/shared/utils';
import MetricTile from '~/analytics/shared/components/metric_tile.vue';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import { group } from './mock_data';
jest.mock('~/flash');
@@ -177,7 +177,7 @@ describe('ValueStreamMetrics', () => {
});
it('should render an error message', () => {
- expect(createFlash).toHaveBeenCalledWith({
+ expect(createAlert).toHaveBeenCalledWith({
message: `There was an error while fetching value stream analytics ${fakeReqName} data.`,
});
});
diff --git a/spec/frontend/runner/components/cells/runner_actions_cell_spec.js b/spec/frontend/runner/components/cells/runner_actions_cell_spec.js
index ffd6f126627..58974d4f85f 100644
--- a/spec/frontend/runner/components/cells/runner_actions_cell_spec.js
+++ b/spec/frontend/runner/components/cells/runner_actions_cell_spec.js
@@ -122,7 +122,7 @@ describe('RunnerActionsCell', () => {
expect(wrapper.emitted('deleted')).toEqual([[value]]);
});
- it('Renders the runner delete disabled button when user cannot delete', () => {
+ it('Does not render the runner delete button when user cannot delete', () => {
createComponent({
runner: {
userPermissions: {
@@ -132,7 +132,7 @@ describe('RunnerActionsCell', () => {
},
});
- expect(findDeleteBtn().props('disabled')).toBe(true);
+ expect(findDeleteBtn().exists()).toBe(false);
});
});
});
diff --git a/spec/frontend/runner/components/runner_delete_button_spec.js b/spec/frontend/runner/components/runner_delete_button_spec.js
index 52fe803c536..c8fb7a69379 100644
--- a/spec/frontend/runner/components/runner_delete_button_spec.js
+++ b/spec/frontend/runner/components/runner_delete_button_spec.js
@@ -9,11 +9,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import { captureException } from '~/runner/sentry_utils';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { createAlert } from '~/flash';
-import {
- I18N_DELETE_RUNNER,
- I18N_DELETE_DISABLED_MANY_PROJECTS,
- I18N_DELETE_DISABLED_UNKNOWN_REASON,
-} from '~/runner/constants';
+import { I18N_DELETE_RUNNER } from '~/runner/constants';
import RunnerDeleteButton from '~/runner/components/runner_delete_button.vue';
import RunnerDeleteModal from '~/runner/components/runner_delete_modal.vue';
@@ -267,29 +263,4 @@ describe('RunnerDeleteButton', () => {
});
});
});
-
- describe.each`
- reason | runner | tooltip
- ${'runner belongs to more than 1 project'} | ${{ projectCount: 2 }} | ${I18N_DELETE_DISABLED_MANY_PROJECTS}
- ${'unknown reason'} | ${{}} | ${I18N_DELETE_DISABLED_UNKNOWN_REASON}
- `('When button is disabled because $reason', ({ runner, tooltip }) => {
- beforeEach(() => {
- createComponent({
- props: {
- disabled: true,
- runner,
- },
- });
- });
-
- it('Displays a disabled delete button', () => {
- expect(findBtn().props('disabled')).toBe(true);
- });
-
- it(`Tooltip "${tooltip}" is shown`, () => {
- // tabindex is required for a11y
- expect(wrapper.attributes('tabindex')).toBe('0');
- expect(getTooltip()).toBe(tooltip);
- });
- });
});
diff --git a/spec/lib/api/entities/bulk_imports/entity_failure_spec.rb b/spec/lib/api/entities/bulk_imports/entity_failure_spec.rb
index adc8fdcdd9c..c4cfcc7e0fd 100644
--- a/spec/lib/api/entities/bulk_imports/entity_failure_spec.rb
+++ b/spec/lib/api/entities/bulk_imports/entity_failure_spec.rb
@@ -12,8 +12,23 @@ RSpec.describe API::Entities::BulkImports::EntityFailure do
:pipeline_class,
:pipeline_step,
:exception_class,
+ :exception_message,
:correlation_id_value,
:created_at
)
end
+
+ describe 'exception message' do
+ it 'truncates exception message to 72 characters' do
+ failure.update!(exception_message: 'a' * 100)
+
+ expect(subject[:exception_message].length).to eq(72)
+ end
+
+ it 'removes paths from the message' do
+ failure.update!(exception_message: 'Test /foo/bar')
+
+ expect(subject[:exception_message]).to eq('Test [FILTERED]')
+ end
+ end
end
diff --git a/spec/lib/gitlab/diff/highlight_cache_spec.rb b/spec/lib/gitlab/diff/highlight_cache_spec.rb
index 53e74748234..33e9360ee01 100644
--- a/spec/lib/gitlab/diff/highlight_cache_spec.rb
+++ b/spec/lib/gitlab/diff/highlight_cache_spec.rb
@@ -109,58 +109,36 @@ RSpec.describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do
end
shared_examples 'caches missing entries' do
- where(:expiration_period, :renewable_expiration_ff, :short_renewable_expiration_ff) do
- [
- [1.day, false, true],
- [1.day, false, false],
- [1.hour, true, true],
- [8.hours, true, false]
- ]
+ it 'filters the key/value list of entries to be caches for each invocation' do
+ expect(cache).to receive(:write_to_redis_hash)
+ .with(hash_including(*paths))
+ .once
+ .and_call_original
+
+ 2.times { cache.write_if_empty }
end
- with_them do
- before do
- stub_feature_flags(
- highlight_diffs_renewable_expiration: renewable_expiration_ff,
- highlight_diffs_short_renewable_expiration: short_renewable_expiration_ff
- )
- end
+ it 'reads from cache once' do
+ expect(cache).to receive(:read_cache).once.and_call_original
- it 'filters the key/value list of entries to be caches for each invocation' do
- expect(cache).to receive(:write_to_redis_hash)
- .with(hash_including(*paths))
- .once
- .and_call_original
+ cache.write_if_empty
+ end
- 2.times { cache.write_if_empty }
- end
+ it 'refreshes TTL of the key on read' do
+ cache.write_if_empty
- it 'reads from cache once' do
- expect(cache).to receive(:read_cache).once.and_call_original
+ time_until_expire = 30.minutes
- cache.write_if_empty
- end
+ Gitlab::Redis::Cache.with do |redis|
+ # Emulate that a key is going to expire soon
+ redis.expire(cache.key, time_until_expire)
- it 'refreshes TTL of the key on read' do
- cache.write_if_empty
+ expect(redis.ttl(cache.key)).to be <= time_until_expire
- time_until_expire = 30.minutes
+ cache.send(:read_cache)
- Gitlab::Redis::Cache.with do |redis|
- # Emulate that a key is going to expire soon
- redis.expire(cache.key, time_until_expire)
-
- expect(redis.ttl(cache.key)).to be <= time_until_expire
-
- cache.send(:read_cache)
-
- if renewable_expiration_ff
- expect(redis.ttl(cache.key)).to be > time_until_expire
- expect(redis.ttl(cache.key)).to be_within(1.minute).of(expiration_period)
- else
- expect(redis.ttl(cache.key)).to be <= time_until_expire
- end
- end
+ expect(redis.ttl(cache.key)).to be > time_until_expire
+ expect(redis.ttl(cache.key)).to be_within(1.minute).of(described_class::EXPIRATION)
end
end
end
diff --git a/spec/views/shared/projects/_project.html.haml_spec.rb b/spec/views/shared/projects/_project.html.haml_spec.rb
index 62f23338c48..8f49240d87c 100644
--- a/spec/views/shared/projects/_project.html.haml_spec.rb
+++ b/spec/views/shared/projects/_project.html.haml_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe 'shared/projects/_project.html.haml' do
it 'renders creator avatar if project has a creator' do
render 'shared/projects/project', use_creator_avatar: true, project: project
- expect(rendered).to have_selector('img.avatar')
+ expect(rendered).to have_selector('img.gl-avatar')
end
it 'renders a generic avatar if project does not have a creator' do