Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2024-01-22 21:10:33 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2024-01-22 21:10:33 +0300
commita9a2f9257eae40935e03ca4185d5263bcb7ba45f (patch)
treef12875873819442e10ab04bd15fd975bf4bb7b64 /spec
parent917d93d86da4dffd96abcfcf3aa83b0d6fa45286 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/groups/members/sort_members_spec.rb2
-rw-r--r--spec/features/projects/members/sorting_spec.rb2
-rw-r--r--spec/finders/groups/accepting_project_transfers_finder_spec.rb2
-rw-r--r--spec/frontend/diffs/components/app_spec.js4
-rw-r--r--spec/frontend/diffs/components/diff_inline_findings_item_spec.js38
-rw-r--r--spec/frontend/diffs/components/diff_inline_findings_spec.js33
-rw-r--r--spec/frontend/diffs/components/diff_line_spec.js65
-rw-r--r--spec/frontend/diffs/components/diff_view_spec.js45
-rw-r--r--spec/frontend/diffs/components/inline_findings_spec.js33
-rw-r--r--spec/frontend/diffs/utils/diff_line_spec.js30
-rw-r--r--spec/frontend/members/components/filter_sort/sort_dropdown_spec.js65
-rw-r--r--spec/lib/gitlab/instrumentation/redis_client_middleware_spec.rb6
-rw-r--r--spec/lib/gitlab/instrumentation/redis_helper_spec.rb52
-rw-r--r--spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb2
-rw-r--r--spec/lib/gitlab/patch/redis_client_spec.rb38
-rw-r--r--spec/lib/gitlab/process_supervisor_spec.rb14
-rw-r--r--spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb80
-rw-r--r--spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb235
-rw-r--r--spec/models/group_spec.rb52
-rw-r--r--spec/models/projects/branch_rule_spec.rb24
-rw-r--r--spec/requests/api/commits_spec.rb9
-rw-r--r--spec/requests/api/graphql/mutations/snippets/create_spec.rb4
-rw-r--r--spec/requests/api/graphql/mutations/snippets/update_spec.rb18
-rw-r--r--spec/spec_helper.rb3
-rw-r--r--spec/support/helpers/database/duplicate_indexes.yml3
-rw-r--r--spec/support/rspec_order_todo.yml1
-rw-r--r--spec/support/shared_examples/controllers/internal_event_tracking_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb12
28 files changed, 445 insertions, 429 deletions
diff --git a/spec/features/groups/members/sort_members_spec.rb b/spec/features/groups/members/sort_members_spec.rb
index 1cc9862ff3b..d1927d293b2 100644
--- a/spec/features/groups/members/sort_members_spec.rb
+++ b/spec/features/groups/members/sort_members_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe 'Groups > Members > Sort members', :js, feature_category: :groups
def expect_sort_by(text, sort_direction)
within_testid('members-sort-dropdown') do
- expect(page).to have_css('button[aria-haspopup="menu"]', text: text)
+ expect(page).to have_css('button[aria-haspopup="listbox"]', text: text)
expect(page).to have_button("Sort direction: #{sort_direction == :asc ? 'Ascending' : 'Descending'}")
end
end
diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb
index 94c42c0f098..7457fbc6989 100644
--- a/spec/features/projects/members/sorting_spec.rb
+++ b/spec/features/projects/members/sorting_spec.rb
@@ -148,7 +148,7 @@ RSpec.describe 'Projects > Members > Sorting', :js, feature_category: :groups_an
def expect_sort_by(text, sort_direction)
within('[data-testid="members-sort-dropdown"]') do
- expect(page).to have_css('button[aria-haspopup="menu"]', text: text)
+ expect(page).to have_css('button[aria-haspopup="listbox"]', text: text)
expect(page).to have_button("Sort direction: #{sort_direction == :asc ? 'Ascending' : 'Descending'}")
end
end
diff --git a/spec/finders/groups/accepting_project_transfers_finder_spec.rb b/spec/finders/groups/accepting_project_transfers_finder_spec.rb
index bb6731abbba..47c293bf74b 100644
--- a/spec/finders/groups/accepting_project_transfers_finder_spec.rb
+++ b/spec/finders/groups/accepting_project_transfers_finder_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Groups::AcceptingProjectTransfersFinder do
+RSpec.describe Groups::AcceptingProjectTransfersFinder, feature_category: :groups_and_projects do
let_it_be(:user) { create(:user) }
let_it_be(:group_where_direct_owner) { create(:group) }
let_it_be(:subgroup_of_group_where_direct_owner) { create(:group, parent: group_where_direct_owner) }
diff --git a/spec/frontend/diffs/components/app_spec.js b/spec/frontend/diffs/components/app_spec.js
index 4676f56c47e..5e0dfd8bd09 100644
--- a/spec/frontend/diffs/components/app_spec.js
+++ b/spec/frontend/diffs/components/app_spec.js
@@ -195,10 +195,6 @@ describe('diffs/components/app', () => {
describe('codequality diff', () => {
it('does not fetch code quality data on FOSS', () => {
createComponent();
- jest.spyOn(wrapper.vm, 'fetchCodequality');
- wrapper.vm.fetchData(false);
-
- expect(wrapper.vm.fetchCodequality).not.toHaveBeenCalled();
expect(codeQualityAndSastQueryHandlerSuccess).not.toHaveBeenCalled();
});
});
diff --git a/spec/frontend/diffs/components/diff_inline_findings_item_spec.js b/spec/frontend/diffs/components/diff_inline_findings_item_spec.js
deleted file mode 100644
index cda3273d51e..00000000000
--- a/spec/frontend/diffs/components/diff_inline_findings_item_spec.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import { GlIcon } from '@gitlab/ui';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import DiffInlineFindingsItem from '~/diffs/components/diff_inline_findings_item.vue';
-import { SEVERITY_CLASSES, SEVERITY_ICONS } from '~/ci/reports/codequality_report/constants';
-import { multipleFindingsArrCodeQualityScale } from '../mock_data/inline_findings';
-
-let wrapper;
-
-const [codeQualityFinding] = multipleFindingsArrCodeQualityScale;
-const findIcon = () => wrapper.findComponent(GlIcon);
-const findDescriptionPlainText = () => wrapper.findByTestId('description-plain-text');
-
-describe('DiffCodeQuality', () => {
- const createWrapper = () => {
- return shallowMountExtended(DiffInlineFindingsItem, {
- propsData: {
- finding: codeQualityFinding,
- },
- });
- };
-
- it('shows icon for given degradation', () => {
- wrapper = createWrapper();
- expect(findIcon().exists()).toBe(true);
-
- expect(findIcon().attributes()).toMatchObject({
- class: `inline-findings-severity-icon ${SEVERITY_CLASSES[codeQualityFinding.severity]}`,
- name: SEVERITY_ICONS[codeQualityFinding.severity],
- size: '12',
- });
- });
-
- it('should render severity + description in plain text', () => {
- wrapper = createWrapper();
- expect(findDescriptionPlainText().text()).toContain(codeQualityFinding.severity);
- expect(findDescriptionPlainText().text()).toContain(codeQualityFinding.description);
- });
-});
diff --git a/spec/frontend/diffs/components/diff_inline_findings_spec.js b/spec/frontend/diffs/components/diff_inline_findings_spec.js
deleted file mode 100644
index f654a2e2d4f..00000000000
--- a/spec/frontend/diffs/components/diff_inline_findings_spec.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import DiffInlineFindings from '~/diffs/components/diff_inline_findings.vue';
-import DiffInlineFindingsItem from '~/diffs/components/diff_inline_findings_item.vue';
-import { NEW_CODE_QUALITY_FINDINGS } from '~/diffs/i18n';
-import { multipleFindingsArrCodeQualityScale } from '../mock_data/inline_findings';
-
-let wrapper;
-const heading = () => wrapper.findByTestId('diff-inline-findings-heading');
-const diffInlineFindingsItems = () => wrapper.findAllComponents(DiffInlineFindingsItem);
-
-describe('DiffInlineFindings', () => {
- const createWrapper = () => {
- return shallowMountExtended(DiffInlineFindings, {
- propsData: {
- title: NEW_CODE_QUALITY_FINDINGS,
- findings: multipleFindingsArrCodeQualityScale,
- },
- });
- };
-
- it('renders the title correctly', () => {
- wrapper = createWrapper();
- expect(heading().text()).toBe(NEW_CODE_QUALITY_FINDINGS);
- });
-
- it('renders the correct number of DiffInlineFindingsItem components with correct props', () => {
- wrapper = createWrapper();
- expect(diffInlineFindingsItems()).toHaveLength(multipleFindingsArrCodeQualityScale.length);
- expect(diffInlineFindingsItems().wrappers[0].props('finding')).toEqual(
- wrapper.props('findings')[0],
- );
- });
-});
diff --git a/spec/frontend/diffs/components/diff_line_spec.js b/spec/frontend/diffs/components/diff_line_spec.js
deleted file mode 100644
index 501bd0757c8..00000000000
--- a/spec/frontend/diffs/components/diff_line_spec.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import DiffLine from '~/diffs/components/diff_line.vue';
-import InlineFindings from '~/diffs/components/inline_findings.vue';
-
-const EXAMPLE_LINE_NUMBER = 3;
-const EXAMPLE_DESCRIPTION = 'example description';
-const EXAMPLE_SEVERITY = 'example severity';
-
-const left = {
- line: {
- left: {
- codequality: [
- {
- line: EXAMPLE_LINE_NUMBER,
- description: EXAMPLE_DESCRIPTION,
- severity: EXAMPLE_SEVERITY,
- },
- ],
- },
- },
-};
-
-const right = {
- line: {
- right: {
- codequality: [
- {
- line: EXAMPLE_LINE_NUMBER,
- description: EXAMPLE_DESCRIPTION,
- severity: EXAMPLE_SEVERITY,
- },
- ],
- },
- },
-};
-
-const mockData = [right, left];
-
-describe('DiffLine', () => {
- const createWrapper = (propsData) => {
- return shallowMount(DiffLine, { propsData });
- };
-
- it('should emit event when hideInlineFindings is called', () => {
- const wrapper = createWrapper(right);
-
- wrapper.findComponent(InlineFindings).vm.$emit('hideInlineFindings');
- expect(wrapper.emitted()).toEqual({
- hideInlineFindings: [[EXAMPLE_LINE_NUMBER]],
- });
- });
-
- mockData.forEach((element) => {
- it('should set correct props for InlineFindings', () => {
- const wrapper = createWrapper(element);
- expect(wrapper.findComponent(InlineFindings).props('codeQuality')).toEqual([
- {
- line: EXAMPLE_LINE_NUMBER,
- description: EXAMPLE_DESCRIPTION,
- severity: EXAMPLE_SEVERITY,
- },
- ]);
- });
- });
-});
diff --git a/spec/frontend/diffs/components/diff_view_spec.js b/spec/frontend/diffs/components/diff_view_spec.js
index 2c8f751804e..2d42c0780b5 100644
--- a/spec/frontend/diffs/components/diff_view_spec.js
+++ b/spec/frontend/diffs/components/diff_view_spec.js
@@ -1,11 +1,9 @@
import { shallowMount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
+import Vue from 'vue';
// eslint-disable-next-line no-restricted-imports
import Vuex from 'vuex';
import { throttle } from 'lodash';
import DiffView from '~/diffs/components/diff_view.vue';
-import DiffLine from '~/diffs/components/diff_line.vue';
-import { diffCodeQuality } from '../mock_data/inline_findings';
jest.mock('lodash/throttle', () => jest.fn((fn) => fn));
const lodash = jest.requireActual('lodash');
@@ -19,7 +17,7 @@ describe('DiffView', () => {
const setSelectedCommentPosition = jest.fn();
const getDiffRow = (wrapper) => wrapper.findComponent(DiffRow).vm;
- const createWrapper = ({ props, flag = false } = {}) => {
+ const createWrapper = ({ props } = {}) => {
Vue.use(Vuex);
const batchComments = {
@@ -51,21 +49,10 @@ describe('DiffView', () => {
diffFile: { file_hash: '123' },
diffLines: [],
...props,
- provide: {
- glFeatures: {
- sastReportsInInlineDiff: flag,
- },
- },
- };
-
- const provide = {
- glFeatures: {
- sastReportsInInlineDiff: flag,
- },
};
const stubs = { DiffExpansionCell, DiffRow, DiffCommentCell, DraftNote };
- return shallowMount(DiffView, { propsData, provide, store, stubs });
+ return shallowMount(DiffView, { propsData, store, stubs });
};
beforeEach(() => {
@@ -76,32 +63,6 @@ describe('DiffView', () => {
throttle.mockReset();
});
- it('does not render a diff-line component when there is no finding', () => {
- const wrapper = createWrapper();
- expect(wrapper.findComponent(DiffLine).exists()).toBe(false);
- });
-
- it('does render a diff-line component with the correct props when there is a finding', async () => {
- const wrapper = createWrapper({ props: diffCodeQuality });
- wrapper.findComponent(DiffRow).vm.$emit('toggleCodeQualityFindings', 2);
- await nextTick();
- expect(wrapper.findComponent(DiffLine).props('line')).toBe(diffCodeQuality.diffLines[2]);
- });
-
- it('does not render a diff-line component when there is a finding and sastReportsInInlineDiff flag is true', async () => {
- const wrapper = createWrapper({ props: diffCodeQuality, flag: true });
- wrapper.findComponent(DiffRow).vm.$emit('toggleCodeQualityFindings', 2);
- await nextTick();
- expect(wrapper.findComponent(DiffLine).exists()).toBe(false);
- });
-
- it('does render a diff-line component when there is a finding and sastReportsInInlineDiff flag is false', async () => {
- const wrapper = createWrapper({ props: diffCodeQuality });
- wrapper.findComponent(DiffRow).vm.$emit('toggleCodeQualityFindings', 2);
- await nextTick();
- expect(wrapper.findComponent(DiffLine).exists()).toBe(true);
- });
-
it.each`
type | side | container | sides | total
${'parallel'} | ${'left'} | ${'.old'} | ${{ left: { lineDrafts: [], renderDiscussion: true }, right: { lineDrafts: [], renderDiscussion: true } }} | ${2}
diff --git a/spec/frontend/diffs/components/inline_findings_spec.js b/spec/frontend/diffs/components/inline_findings_spec.js
deleted file mode 100644
index 102287a23b6..00000000000
--- a/spec/frontend/diffs/components/inline_findings_spec.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import InlineFindings from '~/diffs/components/inline_findings.vue';
-import DiffInlineFindings from '~/diffs/components/diff_inline_findings.vue';
-import { NEW_CODE_QUALITY_FINDINGS } from '~/diffs/i18n';
-import { threeCodeQualityFindings } from '../mock_data/inline_findings';
-
-let wrapper;
-
-const diffInlineFindings = () => wrapper.findComponent(DiffInlineFindings);
-
-describe('InlineFindings', () => {
- const createWrapper = () => {
- return mountExtended(InlineFindings, {
- propsData: {
- codeQuality: threeCodeQualityFindings,
- },
- });
- };
-
- it('hides details and throws hideInlineFindings event on close click', async () => {
- wrapper = createWrapper();
- expect(wrapper.findByTestId('inline-findings').exists()).toBe(true);
-
- await wrapper.findByTestId('inline-findings-close').trigger('click');
- expect(wrapper.emitted('hideInlineFindings')).toHaveLength(1);
- });
-
- it('renders diff inline findings component with correct props for codequality array', () => {
- wrapper = createWrapper();
- expect(diffInlineFindings().props('title')).toBe(NEW_CODE_QUALITY_FINDINGS);
- expect(diffInlineFindings().props('findings')).toBe(threeCodeQualityFindings);
- });
-});
diff --git a/spec/frontend/diffs/utils/diff_line_spec.js b/spec/frontend/diffs/utils/diff_line_spec.js
deleted file mode 100644
index adcb4a4433c..00000000000
--- a/spec/frontend/diffs/utils/diff_line_spec.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import { pickDirection } from '~/diffs/utils/diff_line';
-
-describe('diff_line utilities', () => {
- describe('pickDirection', () => {
- const left = {
- line_code: 'left',
- };
- const right = {
- line_code: 'right',
- };
- const defaultLine = {
- left,
- right,
- };
-
- it.each`
- code | pick | line | pickDescription
- ${'left'} | ${left} | ${defaultLine} | ${'the left line'}
- ${'right'} | ${right} | ${defaultLine} | ${'the right line'}
- ${'junk'} | ${left} | ${defaultLine} | ${'the default: the left line'}
- ${'junk'} | ${right} | ${{ right }} | ${"the right line if there's no left line to default to"}
- ${'right'} | ${left} | ${{ left }} | ${"the left line when there isn't a right line to match"}
- `(
- 'when provided a line and a line code `$code`, picks $pickDescription',
- ({ code, line, pick }) => {
- expect(pickDirection({ line, code })).toBe(pick);
- },
- );
- });
-});
diff --git a/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js b/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js
index 849a84b1a6f..1f98d0e7ce0 100644
--- a/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js
+++ b/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js
@@ -1,12 +1,12 @@
-import { GlSorting, GlSortingItem } from '@gitlab/ui';
+import { GlSorting } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
// eslint-disable-next-line no-restricted-imports
import Vuex from 'vuex';
import setWindowLocation from 'helpers/set_window_location_helper';
import * as urlUtilities from '~/lib/utils/url_utility';
import SortDropdown from '~/members/components/filter_sort/sort_dropdown.vue';
-import { MEMBER_TYPES } from '~/members/constants';
+import { MEMBER_TYPES, FIELD_KEY_MAX_ROLE } from '~/members/constants';
Vue.use(Vuex);
@@ -47,59 +47,46 @@ describe('SortDropdown', () => {
const findSortingComponent = () => wrapper.findComponent(GlSorting);
const findSortDirectionToggle = () =>
findSortingComponent().find('button[title^="Sort direction"]');
- const findDropdownToggle = () => wrapper.find('button[aria-haspopup="menu"]');
- const findDropdownItemByText = (text) =>
- wrapper
- .findAllComponents(GlSortingItem)
- .wrappers.find((dropdownItemWrapper) => dropdownItemWrapper.text() === text);
+ const findDropdownToggle = () => wrapper.find('button[aria-haspopup="listbox"]');
beforeEach(() => {
setWindowLocation(URL_HOST);
});
describe('dropdown options', () => {
- it('adds dropdown items for all the sortable fields', () => {
+ it('sets sort options', () => {
const URL_FILTER_PARAMS = '?two_factor=enabled&search=foobar';
- const EXPECTED_BASE_URL = `${URL_HOST}${URL_FILTER_PARAMS}&sort=`;
setWindowLocation(URL_FILTER_PARAMS);
- const expectedDropdownItems = [
+ const expectedSortOptions = [
{
- label: 'Account',
- url: `${EXPECTED_BASE_URL}name_asc`,
+ text: 'Account',
+ value: 'account',
},
{
- label: 'Access granted',
- url: `${EXPECTED_BASE_URL}last_joined`,
+ text: 'Access granted',
+ value: 'granted',
},
{
- label: 'Max role',
- url: `${EXPECTED_BASE_URL}access_level_asc`,
+ text: 'Max role',
+ value: 'maxRole',
},
{
- label: 'Last sign-in',
- url: `${EXPECTED_BASE_URL}recent_sign_in`,
+ text: 'Last sign-in',
+ value: 'lastSignIn',
},
];
createComponent();
- expectedDropdownItems.forEach((expectedDropdownItem) => {
- const dropdownItem = findDropdownItemByText(expectedDropdownItem.label);
-
- expect(dropdownItem).not.toBe(null);
- expect(dropdownItem.find('a').attributes('href')).toBe(expectedDropdownItem.url);
+ expect(findSortingComponent().props()).toMatchObject({
+ text: expectedSortOptions[0].text,
+ isAscending: true,
+ sortBy: expectedSortOptions[0].value,
+ sortOptions: expectedSortOptions,
});
});
-
- it('checks selected sort option', () => {
- setWindowLocation('?sort=access_level_asc');
-
- createComponent();
-
- expect(findDropdownItemByText('Max role').vm.$attrs.active).toBe(true);
- });
});
describe('dropdown toggle', () => {
@@ -117,6 +104,20 @@ describe('SortDropdown', () => {
expect(findDropdownToggle().text()).toBe('Max role');
});
+
+ describe('select new sort field', () => {
+ beforeEach(async () => {
+ jest.spyOn(urlUtilities, 'visitUrl').mockImplementation();
+ createComponent();
+
+ findSortingComponent().vm.$emit('sortByChange', FIELD_KEY_MAX_ROLE);
+ await nextTick();
+ });
+
+ it('sorts by new field', () => {
+ expect(urlUtilities.visitUrl).toHaveBeenCalledWith(`${URL_HOST}?sort=access_level_asc`);
+ });
+ });
});
describe('sort direction toggle', () => {
diff --git a/spec/lib/gitlab/instrumentation/redis_client_middleware_spec.rb b/spec/lib/gitlab/instrumentation/redis_client_middleware_spec.rb
index eca75d93c80..a8bded69696 100644
--- a/spec/lib/gitlab/instrumentation/redis_client_middleware_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_client_middleware_spec.rb
@@ -74,14 +74,14 @@ RSpec.describe Gitlab::Instrumentation::RedisClientMiddleware, :request_store, f
context 'when encountering exceptions' do
before do
allow(redis_client.instance_variable_get(:@raw_connection)).to receive(:call).and_raise(
- RedisClient::ConnectionError, 'Connection was closed or lost')
+ RedisClient::Error)
end
it 'counts exception' do
expect(instrumentation_class).to receive(:instance_count_exception)
- .with(instance_of(RedisClient::ConnectionError)).and_call_original
+ .with(instance_of(RedisClient::Error)).and_call_original
expect(instrumentation_class).to receive(:log_exception)
- .with(instance_of(RedisClient::ConnectionError)).and_call_original
+ .with(instance_of(RedisClient::Error)).and_call_original
expect(instrumentation_class).to receive(:instance_count_request).and_call_original
expect do
diff --git a/spec/lib/gitlab/instrumentation/redis_helper_spec.rb b/spec/lib/gitlab/instrumentation/redis_helper_spec.rb
index 54659ca2c02..84a5886bc4e 100644
--- a/spec/lib/gitlab/instrumentation/redis_helper_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_helper_spec.rb
@@ -37,20 +37,27 @@ RSpec.describe Gitlab::Instrumentation::RedisHelper, :request_store, feature_cat
subject(:minimal_test_class_instance) { MinimalTestClass.new }
describe '.instrument_call' do
+ let(:pipelined) { false }
+ let(:command) { [[:set, 'foo', 'bar']] }
+
+ subject(:instrumented_command) { minimal_test_class_instance.check_command(command, pipelined) }
+
it 'instruments request count' do
expect(Gitlab::Instrumentation::Redis::Cache).to receive(:instance_count_request).with(1)
expect(Gitlab::Instrumentation::Redis::Cache).not_to receive(:instance_count_pipelined_request)
- minimal_test_class_instance.check_command([[:set, 'foo', 'bar']], false)
+ instrumented_command
end
it 'performs cluster validation' do
expect(Gitlab::Instrumentation::Redis::Cache).to receive(:redis_cluster_validate!).once
- minimal_test_class_instance.check_command([[:set, 'foo', 'bar']], false)
+ instrumented_command
end
context 'when command is not valid for Redis Cluster' do
+ let(:command) { [[:mget, 'foo', 'bar']] }
+
before do
allow(Gitlab::Instrumentation::Redis::Cache).to receive(:redis_cluster_validate!).and_return(false)
end
@@ -58,7 +65,7 @@ RSpec.describe Gitlab::Instrumentation::RedisHelper, :request_store, feature_cat
it 'reports cross slot request' do
expect(Gitlab::Instrumentation::Redis::Cache).to receive(:increment_cross_slot_request_count).once
- minimal_test_class_instance.check_command([[:mget, 'foo', 'bar']], false)
+ instrumented_command
end
end
@@ -71,21 +78,52 @@ RSpec.describe Gitlab::Instrumentation::RedisHelper, :request_store, feature_cat
end
it 'ensures duration is tracked' do
- commands = [[:set, 'foo', 'bar']]
allow(Gitlab::Instrumentation::Redis::Cache).to receive(:instance_observe_duration).once
allow(Gitlab::Instrumentation::Redis::Cache).to receive(:increment_request_count).with(1).once
allow(Gitlab::Instrumentation::Redis::Cache).to receive(:add_duration).once
- allow(Gitlab::Instrumentation::Redis::Cache).to receive(:add_call_details).with(anything, commands).once
+ allow(Gitlab::Instrumentation::Redis::Cache).to receive(:add_call_details).with(anything, command).once
+
+ expect { instrumented_command }.to raise_error(StandardError)
+ end
+ end
+
+ context 'when a RedisClient::ConnectionError is raised' do
+ before do
+ allow(Gitlab::Instrumentation::Redis::Cache).to receive(:instance_count_request)
+ .and_raise(RedisClient::ConnectionError)
+ end
+
+ it 'silences connection errors raised during the first attempt' do
+ expect(Gitlab::Instrumentation::Redis::Cache).not_to receive(:log_exception).with(RedisClient::ConnectionError)
+
+ expect { instrumented_command }.to raise_error(StandardError)
- expect { minimal_test_class_instance.check_command(commands, false) }.to raise_error(StandardError)
+ expect(Thread.current[:redis_client_error_count]).to eq(1)
+ end
+
+ context 'when error is raised on the second attempt' do
+ before do
+ Thread.current[:redis_client_error_count] = 1
+ end
+
+ it 'instruments errors on second attempt' do
+ expect(Gitlab::Instrumentation::Redis::Cache).to receive(:log_exception).with(RedisClient::ConnectionError)
+
+ expect { instrumented_command }.to raise_error(StandardError)
+
+ expect(Thread.current[:redis_client_error_count]).to eq(2)
+ end
end
end
context 'when pipelined' do
+ let(:command) { [[:get, '{user1}:bar'], [:get, '{user1}:foo']] }
+ let(:pipelined) { true }
+
it 'instruments pipelined request count' do
expect(Gitlab::Instrumentation::Redis::Cache).to receive(:instance_count_pipelined_request)
- minimal_test_class_instance.check_command([[:get, '{user1}:bar'], [:get, '{user1}:foo']], true)
+ instrumented_command
end
end
end
diff --git a/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb b/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
index e9bd0056e5f..2a160a9d316 100644
--- a/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
@@ -117,6 +117,8 @@ RSpec.describe Gitlab::Instrumentation::RedisInterceptor, :request_store, featur
expect do
redis_store_class.with { |redis| redis.call(:auth, 'foo', 'bar') }
end.to raise_exception(Redis::CommandError)
+
+ expect(Thread.current[:redis_client_error_count]).to eq(0)
end
end
end
diff --git a/spec/lib/gitlab/patch/redis_client_spec.rb b/spec/lib/gitlab/patch/redis_client_spec.rb
new file mode 100644
index 00000000000..af094e9e0d2
--- /dev/null
+++ b/spec/lib/gitlab/patch/redis_client_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Patch::RedisClient, feature_category: :redis do
+ include RedisHelpers
+
+ let_it_be(:redis_store_class) { define_helper_redis_store_class }
+ let_it_be(:redis_client) { RedisClient.new(redis_store_class.redis_client_params) }
+
+ before do
+ Thread.current[:redis_client_error_count] = 1
+ end
+
+ it 'resets tracking count after each call' do
+ expect { redis_client.call("ping") }
+ .to change { Thread.current[:redis_client_error_count] }
+ .from(1).to(0)
+ end
+
+ it 'resets tracking count after each blocking call' do
+ expect { redis_client.blocking_call(false, "ping") }
+ .to change { Thread.current[:redis_client_error_count] }
+ .from(1).to(0)
+ end
+
+ it 'resets tracking count after pipelined' do
+ expect { redis_client.pipelined { |p| p.call("ping") } }
+ .to change { Thread.current[:redis_client_error_count] }
+ .from(1).to(0)
+ end
+
+ it 'resets tracking count after multi' do
+ expect { redis_client.multi { |p| p.call("ping") } }
+ .to change { Thread.current[:redis_client_error_count] }
+ .from(1).to(0)
+ end
+end
diff --git a/spec/lib/gitlab/process_supervisor_spec.rb b/spec/lib/gitlab/process_supervisor_spec.rb
index 94535e96843..5b9878df456 100644
--- a/spec/lib/gitlab/process_supervisor_spec.rb
+++ b/spec/lib/gitlab/process_supervisor_spec.rb
@@ -42,7 +42,7 @@ RSpec.describe Gitlab::ProcessSupervisor, feature_category: :cloud_connector do
pids_killed = []
supervisor.supervise(process_ids) do |dead_pids|
- pids_killed = dead_pids
+ pids_killed += dead_pids
[]
end
@@ -60,7 +60,7 @@ RSpec.describe Gitlab::ProcessSupervisor, feature_category: :cloud_connector do
pids_killed = []
supervisor.supervise(process_ids) do |dead_pids|
- pids_killed = dead_pids
+ pids_killed += dead_pids
[42] # Fake starting a new process in place of the terminated one.
end
@@ -68,7 +68,7 @@ RSpec.describe Gitlab::ProcessSupervisor, feature_category: :cloud_connector do
Process.kill('TERM', process_ids.first)
await_condition(sleep_sec: health_check_interval_seconds) do
- pids_killed == [process_ids.first]
+ pids_killed.include?(process_ids.first)
end
expect(Gitlab::ProcessManagement.process_alive?(process_ids.first)).to be(false)
@@ -81,7 +81,7 @@ RSpec.describe Gitlab::ProcessSupervisor, feature_category: :cloud_connector do
pids_killed = []
supervisor.supervise(process_ids) do |dead_pids|
- pids_killed = dead_pids
+ pids_killed += dead_pids
# Fake a new process having the same pid as one that was just terminated.
[process_ids.last]
end
@@ -90,7 +90,7 @@ RSpec.describe Gitlab::ProcessSupervisor, feature_category: :cloud_connector do
Process.kill('TERM', process_ids.first)
await_condition(sleep_sec: health_check_interval_seconds) do
- pids_killed == [process_ids.first]
+ pids_killed.include?(process_ids.first)
end
expect(supervisor.supervised_pids).to contain_exactly(process_ids.last)
@@ -101,7 +101,7 @@ RSpec.describe Gitlab::ProcessSupervisor, feature_category: :cloud_connector do
pids_killed = []
supervisor.supervise(process_ids) do |dead_pids|
- pids_killed = dead_pids
+ pids_killed += dead_pids
42
end
@@ -109,7 +109,7 @@ RSpec.describe Gitlab::ProcessSupervisor, feature_category: :cloud_connector do
Process.kill('TERM', process_ids.first)
await_condition(sleep_sec: health_check_interval_seconds) do
- pids_killed == [process_ids.first]
+ pids_killed.include?(process_ids.first)
end
expect(supervisor.supervised_pids).to contain_exactly(42, process_ids.last)
diff --git a/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb
deleted file mode 100644
index cbf4d3c8261..00000000000
--- a/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::UsageDataCounters::EditorUniqueCounter, :clean_gitlab_redis_shared_state do
- let(:user) { build(:user, id: 1) }
- let(:user2) { build(:user, id: 2) }
- let(:user3) { build(:user, id: 3) }
- let(:project) { build(:project) }
- let(:namespace) { project.namespace }
- let(:time) { Time.zone.now }
-
- shared_examples 'tracks and counts action' do
- subject { track_action(author: user, project: project) }
-
- before do
- stub_application_setting(usage_ping_enabled: true)
- end
-
- specify do
- aggregate_failures do
- track_action(author: user, project: project)
- track_action(author: user2, project: project)
- track_action(author: user3, project: project)
-
- expect(count_unique(date_from: time.beginning_of_week, date_to: 1.week.from_now)).to eq(3)
- end
- end
-
- it_behaves_like 'internal event tracking'
-
- it 'does not track edit actions if author is not present' do
- track_action(author: nil, project: project)
-
- expect(count_unique(date_from: time.beginning_of_week, date_to: 1.week.from_now)).to eq(0)
- end
- end
-
- context 'for web IDE edit actions' do
- let(:event) { described_class::EDIT_BY_WEB_IDE }
-
- it_behaves_like 'tracks and counts action' do
- def track_action(params)
- described_class.track_web_ide_edit_action(**params)
- end
-
- def count_unique(params)
- described_class.count_web_ide_edit_actions(**params)
- end
- end
- end
-
- context 'for SFE edit actions' do
- let(:event) { described_class::EDIT_BY_SFE }
-
- it_behaves_like 'tracks and counts action' do
- def track_action(params)
- described_class.track_sfe_edit_action(**params)
- end
-
- def count_unique(params)
- described_class.count_sfe_edit_actions(**params)
- end
- end
- end
-
- context 'for snippet editor edit actions' do
- let(:event) { described_class::EDIT_BY_SNIPPET_EDITOR }
-
- it_behaves_like 'tracks and counts action' do
- def track_action(params)
- described_class.track_snippet_editor_edit_action(**params)
- end
-
- def count_unique(params)
- described_class.count_snippet_editor_edit_actions(**params)
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
index da8098bfee1..5077c3532ef 100644
--- a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
@@ -62,15 +62,15 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
describe 'known_events' do
let(:weekly_event) { 'g_analytics_contribution' }
- let(:daily_event) { 'g_analytics_search' }
+ let(:daily_event) { 'g_analytics_issues' }
let(:analytics_slot_event) { 'g_analytics_contribution' }
let(:compliance_slot_event) { 'g_compliance_dashboard' }
- let(:category_analytics_event) { 'g_analytics_search' }
+ let(:category_analytics_event) { 'g_analytics_issues' }
let(:category_productivity_event) { 'g_analytics_productivity' }
let(:no_slot) { 'no_slot' }
let(:different_aggregation) { 'different_aggregation' }
let(:custom_daily_event) { 'g_analytics_custom' }
-
+ let(:event_overridden_for_user) { 'user_created_custom_dashboard' }
let(:global_category) { 'global' }
let(:compliance_category) { 'compliance' }
let(:productivity_category) { 'productivity' }
@@ -84,7 +84,8 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
{ name: category_productivity_event },
{ name: compliance_slot_event },
{ name: no_slot },
- { name: different_aggregation }
+ { name: different_aggregation },
+ { name: event_overridden_for_user }
].map(&:with_indifferent_access)
end
@@ -216,6 +217,97 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
end
end
end
+
+ describe "property_name" do
+ before do
+ stub_feature_flags(redis_hll_property_name_tracking: property_name_flag_enabled)
+ end
+
+ context "with enabled feature flag" do
+ let(:property_name_flag_enabled) { true }
+
+ context "with a property_name for an overridden event" do
+ context "with a property_name sent as a symbol" do
+ it "tracks the events using the Redis key override" do
+ expected_key = "{hll_counters}_#{event_overridden_for_user}-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(event_overridden_for_user, values: entity1, property_name: :user)
+ end
+ end
+
+ context "with a property_name sent in string format" do
+ it "tracks the events using the Redis key override" do
+ expected_key = "{hll_counters}_#{event_overridden_for_user}-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(event_overridden_for_user, values: entity1, property_name: 'user.id')
+ end
+ end
+ end
+
+ context "with a property_name for an overridden event that doesn't include this property_name" do
+ it "tracks the events using a Redis key with the property_name" do
+ expected_key = "{hll_counters}_#{no_slot}-user-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(no_slot, values: entity1, property_name: 'user')
+ end
+ end
+
+ context "with a property_name for a new event" do
+ it "tracks the events using a Redis key with the property_name" do
+ expected_key = "{hll_counters}_#{no_slot}-project-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(no_slot, values: entity1, property_name: 'project')
+ end
+ end
+
+ context "with no property_name for an overridden event" do
+ it "tracks the events using a Redis key with no property_name" do
+ expected_key = "{hll_counters}_#{event_overridden_for_user}-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(event_overridden_for_user, values: entity1)
+ end
+ end
+
+ context "with no property_name for a new event" do
+ it "tracks the events using a Redis key with no property_name" do
+ expected_key = "{hll_counters}_#{no_slot}-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(no_slot, values: entity1)
+ end
+ end
+ end
+
+ context "with disabled feature flag" do
+ let(:property_name_flag_enabled) { false }
+
+ it "uses old Redis key for overridden events" do
+ expected_key = "{hll_counters}_#{event_overridden_for_user}-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(event_overridden_for_user, values: entity1, property_name: 'user')
+ end
+
+ it "uses old Redis key for new events" do
+ expected_key = "{hll_counters}_#{no_slot}-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(no_slot, values: entity1, property_name: 'project')
+ end
+
+ it "uses old Redis key for new events when no property name sent" do
+ expected_key = "{hll_counters}_#{no_slot}-2020-23"
+ expect(Gitlab::Redis::HLL).to receive(:add).with(hash_including(key: expected_key))
+
+ described_class.track_event(no_slot, values: entity1)
+ end
+ end
+ end
end
describe '.unique_events' do
@@ -227,7 +319,7 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
# Events last week
described_class.track_event(weekly_event, values: entity1, time: 2.days.ago)
described_class.track_event(weekly_event, values: entity1, time: 2.days.ago)
- described_class.track_event(no_slot, values: entity1, time: 2.days.ago)
+ described_class.track_event(no_slot, values: entity1, property_name: 'user.id', time: 2.days.ago)
# Events 2 weeks ago
described_class.track_event(weekly_event, values: entity1, time: 2.weeks.ago)
@@ -274,7 +366,7 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
end
context 'when no slot is set' do
- it { expect(described_class.unique_events(event_names: [no_slot], start_date: 7.days.ago, end_date: Date.current)).to eq(1) }
+ it { expect(described_class.unique_events(event_names: [no_slot], property_name: 'user.id', start_date: 7.days.ago, end_date: Date.current)).to eq(1) }
end
context 'when data crosses into new year' do
@@ -283,6 +375,97 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
.not_to raise_error
end
end
+
+ describe "property_names" do
+ before do
+ stub_feature_flags(redis_hll_property_name_tracking: property_name_flag_enabled)
+ end
+
+ context "with enabled feature flag" do
+ let(:property_name_flag_enabled) { true }
+
+ context "with a property_name for an overridden event" do
+ context "with a property_name sent as a symbol" do
+ it "tracks the events using the Redis key override" do
+ expected_key = "{hll_counters}_#{event_overridden_for_user}-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [event_overridden_for_user], property_name: :user, start_date: 7.days.ago, end_date: Date.current)
+ end
+ end
+
+ context "with a property_name sent in string format" do
+ it "tracks the events using the Redis key override" do
+ expected_key = "{hll_counters}_#{event_overridden_for_user}-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [event_overridden_for_user], property_name: 'user.id', start_date: 7.days.ago, end_date: Date.current)
+ end
+ end
+ end
+
+ context "with a property_name for an overridden event that doesn't include this property_name" do
+ it "tracks the events using a Redis key with the property_name" do
+ expected_key = "{hll_counters}_#{no_slot}-user-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [no_slot], property_name: 'user', start_date: 7.days.ago, end_date: Date.current)
+ end
+ end
+
+ context "with a property_name for a new event" do
+ it "tracks the events using a Redis key with the property_name" do
+ expected_key = "{hll_counters}_#{no_slot}-project-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [no_slot], property_name: 'project', start_date: 7.days.ago, end_date: Date.current)
+ end
+ end
+
+ context "with no property_name for a overridden event" do
+ it "tracks the events using a Redis key with no property_name" do
+ expected_key = "{hll_counters}_#{event_overridden_for_user}-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [event_overridden_for_user], start_date: 7.days.ago, end_date: Date.current)
+ end
+ end
+
+ context "with no property_name for a new event" do
+ it "tracks the events using a Redis key with no property_name" do
+ expected_key = "{hll_counters}_#{no_slot}-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [no_slot], start_date: 7.days.ago, end_date: Date.current)
+ end
+ end
+ end
+
+ context "with disabled feature flag" do
+ let(:property_name_flag_enabled) { false }
+
+ it "uses old Redis key for overridden events" do
+ expected_key = "{hll_counters}_#{event_overridden_for_user}-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [event_overridden_for_user], property_name: 'user', start_date: 7.days.ago, end_date: Date.current)
+ end
+
+ it "uses old Redis key for new events" do
+ expected_key = "{hll_counters}_#{no_slot}-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [no_slot], property_name: 'project', start_date: 7.days.ago, end_date: Date.current)
+ end
+
+ it "uses old Redis key for new events when no property name sent" do
+ expected_key = "{hll_counters}_#{no_slot}-2020-22"
+ expect(Gitlab::Redis::HLL).to receive(:count).with(keys: [expected_key])
+
+ described_class.unique_events(event_names: [no_slot], start_date: 7.days.ago, end_date: Date.current)
+ end
+ end
+ end
end
describe 'key overrides file' do
@@ -341,43 +524,43 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
let(:time_range) { { start_date: 7.days.ago, end_date: DateTime.current } }
let(:known_events) do
[
- { name: 'event1_slot' },
- { name: 'event2_slot' },
- { name: 'event3_slot' },
- { name: 'event5_slot' },
- { name: 'event4' }
+ { name: 'g_compliance_dashboard' },
+ { name: 'g_project_management_epic_created' },
+ { name: 'g_project_management_epic_closed' },
+ { name: 'g_project_management_epic_reopened' },
+ { name: 'g_project_management_epic_issue_added' }
].map(&:with_indifferent_access)
end
before do
allow(described_class).to receive(:known_events).and_return(known_events)
- described_class.track_event('event1_slot', values: entity1, time: 2.days.ago)
- described_class.track_event('event1_slot', values: entity2, time: 2.days.ago)
- described_class.track_event('event1_slot', values: entity3, time: 2.days.ago)
- described_class.track_event('event2_slot', values: entity1, time: 2.days.ago)
- described_class.track_event('event2_slot', values: entity2, time: 3.days.ago)
- described_class.track_event('event2_slot', values: entity3, time: 3.days.ago)
- described_class.track_event('event3_slot', values: entity1, time: 3.days.ago)
- described_class.track_event('event3_slot', values: entity2, time: 3.days.ago)
- described_class.track_event('event5_slot', values: entity2, time: 3.days.ago)
+ described_class.track_event('g_compliance_dashboard', values: entity1, time: 2.days.ago)
+ described_class.track_event('g_compliance_dashboard', values: entity2, time: 2.days.ago)
+ described_class.track_event('g_compliance_dashboard', values: entity3, time: 2.days.ago)
+ described_class.track_event('g_project_management_epic_created', values: entity1, time: 2.days.ago)
+ described_class.track_event('g_project_management_epic_created', values: entity2, time: 3.days.ago)
+ described_class.track_event('g_project_management_epic_created', values: entity3, time: 3.days.ago)
+ described_class.track_event('g_project_management_epic_closed', values: entity1, time: 3.days.ago)
+ described_class.track_event('g_project_management_epic_closed', values: entity2, time: 3.days.ago)
+ described_class.track_event('g_project_management_epic_reopened', values: entity2, time: 3.days.ago)
# events out of time scope
- described_class.track_event('event2_slot', values: entity4, time: 8.days.ago)
+ described_class.track_event('g_project_management_epic_created', values: entity4, time: 8.days.ago)
# events in different slots
- described_class.track_event('event4', values: entity1, time: 2.days.ago)
- described_class.track_event('event4', values: entity2, time: 2.days.ago)
+ described_class.track_event('g_project_management_epic_issue_added', values: entity1, time: 2.days.ago)
+ described_class.track_event('g_project_management_epic_issue_added', values: entity2, time: 2.days.ago)
end
it 'calculates union of given events', :aggregate_failures do
- expect(described_class.calculate_events_union(**time_range.merge(event_names: %w[event4]))).to eq 2
- expect(described_class.calculate_events_union(**time_range.merge(event_names: %w[event1_slot event2_slot event3_slot]))).to eq 3
+ expect(described_class.calculate_events_union(**time_range.merge(event_names: %w[g_project_management_epic_issue_added]))).to eq 2
+ expect(described_class.calculate_events_union(**time_range.merge(event_names: %w[g_compliance_dashboard g_project_management_epic_created g_project_management_epic_closed]))).to eq 3
end
it 'returns 0 if there are no keys for given events' do
expect(Gitlab::Redis::HLL).not_to receive(:count)
- expect(described_class.calculate_events_union(event_names: %w[event1_slot event2_slot event3_slot], start_date: Date.current, end_date: 4.weeks.ago)).to eq(-1)
+ expect(described_class.calculate_events_union(event_names: %w[g_compliance_dashboard g_project_management_epic_created g_project_management_epic_closed], start_date: Date.current, end_date: 4.weeks.ago)).to eq(-1)
end
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 90c882c42b9..57482a604e2 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -1122,6 +1122,50 @@ RSpec.describe Group, feature_category: :groups_and_projects do
end
end
+ describe '.excluding_restricted_visibility_levels_for_user' do
+ let_it_be(:admin_user) { create(:admin) }
+
+ context 'when restricted_visibility_level is not configured' do
+ context 'when user is an admin', :enable_admin_mode do
+ it 'returns all groups' do
+ expect(described_class.excluding_restricted_visibility_levels_for_user(admin_user)).to eq(
+ [private_group, internal_group, group]
+ )
+ end
+ end
+
+ context 'when user is not an admin' do
+ it 'returns all groups' do
+ expect(described_class.excluding_restricted_visibility_levels_for_user(user1)).to eq(
+ [private_group, internal_group, group]
+ )
+ end
+ end
+ end
+
+ context 'when restricted_visibility_level is set to private' do
+ before do
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE])
+ end
+
+ context 'and user is an admin', :enable_admin_mode do
+ it 'returns all groups' do
+ expect(described_class.excluding_restricted_visibility_levels_for_user(admin_user)).to eq(
+ [private_group, internal_group, group]
+ )
+ end
+ end
+
+ context 'and user is not an admin' do
+ it 'excludes private groups' do
+ expect(described_class.excluding_restricted_visibility_levels_for_user(user1)).to eq(
+ [internal_group, group]
+ )
+ end
+ end
+ end
+ end
+
describe '.project_creation_allowed' do
let_it_be(:group_1) { create(:group, project_creation_level: Gitlab::Access::NO_ONE_PROJECT_ACCESS) }
let_it_be(:group_2) { create(:group, project_creation_level: Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) }
@@ -1129,7 +1173,9 @@ RSpec.describe Group, feature_category: :groups_and_projects do
let_it_be(:group_4) { create(:group, project_creation_level: nil) }
it 'only includes groups where project creation is allowed' do
- result = described_class.project_creation_allowed
+ expect(described_class).to receive(:excluding_restricted_visibility_levels_for_user).and_call_original
+
+ result = described_class.project_creation_allowed(user1)
expect(result).to include(group_2, group_3, group_4)
expect(result).not_to include(group_1)
@@ -1141,7 +1187,9 @@ RSpec.describe Group, feature_category: :groups_and_projects do
end
it 'only includes groups where project creation is allowed' do
- result = described_class.project_creation_allowed
+ expect(described_class).to receive(:excluding_restricted_visibility_levels_for_user).and_call_original
+
+ result = described_class.project_creation_allowed(user1)
expect(result).to include(group_2, group_3)
diff --git a/spec/models/projects/branch_rule_spec.rb b/spec/models/projects/branch_rule_spec.rb
index 6910fbbb6db..24f94df5c24 100644
--- a/spec/models/projects/branch_rule_spec.rb
+++ b/spec/models/projects/branch_rule_spec.rb
@@ -8,7 +8,31 @@ RSpec.describe Projects::BranchRule, feature_category: :source_code_management d
subject { described_class.new(protected_branch.project, protected_branch) }
+ describe '::find(id)' do
+ context 'when id matches a Project' do
+ it 'finds the project and initializes a branch rule' do
+ instance = described_class.find(protected_branch.id)
+ expect(instance).to be_instance_of(described_class)
+ expect(instance.protected_branch.id).to eq(protected_branch.id)
+ expect(instance.project.id).to eq(project.id)
+ end
+ end
+
+ context 'when id does not match a Project' do
+ it 'raises an ActiveRecord::RecordNotFound error describing the branch rule' do
+ expect { described_class.find(0) }.to raise_error(
+ ActiveRecord::RecordNotFound, "Couldn't find Projects::BranchRule with 'id'=0"
+ )
+ end
+ end
+ end
+
+ it 'generates a valid global id' do
+ expect(subject.to_global_id.to_s).to eq("gid://gitlab/Projects::BranchRule/#{protected_branch.id}")
+ end
+
it 'delegates methods to protected branch' do
+ expect(subject).to delegate_method(:id).to(:protected_branch)
expect(subject).to delegate_method(:name).to(:protected_branch)
expect(subject).to delegate_method(:group).to(:protected_branch)
expect(subject).to delegate_method(:default_branch?).to(:protected_branch)
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 06ef68f190f..8bea823fd64 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -581,7 +581,7 @@ RSpec.describe API::Commits, feature_category: :source_code_management do
context 'when using access token authentication' do
it 'does not increment the usage counters' do
expect(::Gitlab::UsageDataCounters::WebIdeCounter).not_to receive(:increment_commits_count)
- expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_web_ide_edit_action)
+ expect(::Gitlab::InternalEvents).not_to receive(:track_event)
post api(url, user), params: valid_c_params
end
@@ -596,21 +596,16 @@ RSpec.describe API::Commits, feature_category: :source_code_management do
it 'increments usage counters' do
expect(::Gitlab::UsageDataCounters::WebIdeCounter).to receive(:increment_commits_count)
- expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_web_ide_edit_action)
subject
end
it_behaves_like 'internal event tracking' do
- let(:event) { ::Gitlab::UsageDataCounters::EditorUniqueCounter::EDIT_BY_WEB_IDE }
+ let(:event) { 'g_edit_by_web_ide' }
let(:namespace) { project.namespace.reload }
end
context 'counts.web_ide_commits Snowplow event tracking' do
- before do
- allow(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_web_ide_edit_action)
- end
-
it_behaves_like 'Snowplow event tracking' do
let(:action) { :commit }
let(:category) { described_class.to_s }
diff --git a/spec/requests/api/graphql/mutations/snippets/create_spec.rb b/spec/requests/api/graphql/mutations/snippets/create_spec.rb
index 7094cb807b2..1b01f43b829 100644
--- a/spec/requests/api/graphql/mutations/snippets/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/create_spec.rb
@@ -106,7 +106,9 @@ RSpec.describe 'Creating a Snippet', feature_category: :source_code_management d
end
context 'with PersonalSnippet' do
- it_behaves_like 'creates snippet'
+ it_behaves_like 'creates snippet' do
+ let(:project) { nil }
+ end
end
context 'with ProjectSnippet' do
diff --git a/spec/requests/api/graphql/mutations/snippets/update_spec.rb b/spec/requests/api/graphql/mutations/snippets/update_spec.rb
index 0bc475c7105..c84aad85598 100644
--- a/spec/requests/api/graphql/mutations/snippets/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/update_spec.rb
@@ -43,7 +43,8 @@ RSpec.describe 'Updating a Snippet', feature_category: :source_code_management d
shared_examples 'graphql update actions' do
context 'when the user does not have permission' do
- let(:current_user) { create(:user) }
+ let(:user) { create(:user) }
+ let(:current_user) { user }
it_behaves_like 'a mutation that returns top-level errors',
errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
@@ -131,14 +132,18 @@ RSpec.describe 'Updating a Snippet', feature_category: :source_code_management d
it_behaves_like 'graphql update actions'
it_behaves_like 'when the snippet is not found'
- it_behaves_like 'snippet edit usage data counters'
+ it_behaves_like 'snippet edit usage data counters' do
+ let(:user) { current_user }
+ end
+
it_behaves_like 'has spam protection' do
let(:mutation_class) { ::Mutations::Snippets::Update }
end
end
describe 'ProjectSnippet' do
- let_it_be(:project) { create(:project, :private) }
+ let_it_be(:namespace) { create(:namespace) }
+ let_it_be(:project) { create(:project, :private, namespace: namespace) }
let(:snippet) do
create(
@@ -181,7 +186,9 @@ RSpec.describe 'Updating a Snippet', feature_category: :source_code_management d
end
end
- it_behaves_like 'snippet edit usage data counters'
+ it_behaves_like 'snippet edit usage data counters' do
+ let(:user) { current_user }
+ end
it_behaves_like 'has spam protection' do
let(:mutation_class) { ::Mutations::Snippets::Update }
@@ -193,9 +200,8 @@ RSpec.describe 'Updating a Snippet', feature_category: :source_code_management d
end
it_behaves_like 'internal event tracking' do
- let(:event) { ::Gitlab::UsageDataCounters::EditorUniqueCounter::EDIT_BY_SNIPPET_EDITOR }
+ let(:event) { 'g_edit_by_snippet_ide' }
let(:user) { current_user }
- let(:namespace) { project.namespace }
end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index d08f6ef9d0d..2add01807cb 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -330,6 +330,9 @@ RSpec.configure do |config|
# Postgres is the primary data source, and ClickHouse only when enabled in certain cases.
stub_feature_flags(clickhouse_data_collection: false)
+
+ # This is going to be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/432866
+ stub_feature_flags(redis_hll_property_name_tracking: false)
else
unstub_all_feature_flags
end
diff --git a/spec/support/helpers/database/duplicate_indexes.yml b/spec/support/helpers/database/duplicate_indexes.yml
index 29e1c1da245..941ea4305c5 100644
--- a/spec/support/helpers/database/duplicate_indexes.yml
+++ b/spec/support/helpers/database/duplicate_indexes.yml
@@ -125,9 +125,6 @@ project_compliance_standards_adherence:
project_repositories:
index_project_repositories_on_shard_id_and_project_id:
- index_project_repositories_on_shard_id
-protected_environments:
- index_protected_environments_on_project_id_and_name:
- - index_protected_environments_on_project_id
protected_tags:
index_protected_tags_on_project_id_and_name:
- index_protected_tags_on_project_id
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index 91d423fcd67..a11f2cdb41b 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -6727,7 +6727,6 @@
- './spec/lib/gitlab/usage_data_counters/code_review_events_spec.rb'
- './spec/lib/gitlab/usage_data_counters/cycle_analytics_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters/designs_counter_spec.rb'
-- './spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters/gitlab_cli_activity_unique_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters/ipynb_diff_activity_counter_spec.rb'
diff --git a/spec/support/shared_examples/controllers/internal_event_tracking_examples.rb b/spec/support/shared_examples/controllers/internal_event_tracking_examples.rb
index 0c19865999f..2332d6cd2d0 100644
--- a/spec/support/shared_examples/controllers/internal_event_tracking_examples.rb
+++ b/spec/support/shared_examples/controllers/internal_event_tracking_examples.rb
@@ -23,7 +23,7 @@ RSpec.shared_examples 'internal event tracking' do
project = try(:project)
user = try(:user)
- namespace = try(:namespace)
+ namespace = try(:namespace) || project&.namespace
expect(Gitlab::Tracking::StandardContext)
.to have_received(:new)
diff --git a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
index 53329c5caec..8ed3464b009 100644
--- a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
@@ -14,7 +14,7 @@ RSpec.shared_examples 'snippet edit usage data counters' do
context 'when user is sessionless' do
it 'does not track usage data actions' do
- expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_snippet_editor_edit_action)
+ expect(::Gitlab::InternalEvents).not_to receive(:track_event)
post_graphql_mutation(mutation, current_user: current_user)
end
@@ -25,17 +25,19 @@ RSpec.shared_examples 'snippet edit usage data counters' do
stub_session('warden.user.user.key' => [[current_user.id], current_user.authenticatable_salt])
end
- it 'tracks usage data actions', :clean_gitlab_redis_sessions do
- expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_snippet_editor_edit_action)
-
+ subject do
post_graphql_mutation(mutation)
end
+ it_behaves_like 'internal event tracking' do
+ let(:event) { 'g_edit_by_snippet_ide' }
+ end
+
context 'when mutation result raises an error' do
it 'does not track usage data actions' do
mutation_vars[:title] = nil
- expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_snippet_editor_edit_action)
+ expect(::Gitlab::InternalEvents).not_to receive(:track_event)
post_graphql_mutation(mutation)
end