Commit a92d6b36 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 39a48637
# frozen_string_literal: true
module Groups
module GroupLinks
class DestroyService < BaseService
def execute(one_or_more_links)
links = Array(one_or_more_links)
GroupGroupLink.transaction do
GroupGroupLink.delete(links)
groups_to_refresh = links.map(&:shared_with_group)
groups_to_refresh.uniq.each do |group|
group.refresh_members_authorized_projects
end
Gitlab::AppLogger.info("GroupGroupLinks with ids: #{links.map(&:id)} have been deleted.")
rescue => ex
Gitlab::AppLogger.error(ex)
raise
end
end
end
end
end
...@@ -8,5 +8,9 @@ class RemoveExpiredGroupLinksWorker ...@@ -8,5 +8,9 @@ class RemoveExpiredGroupLinksWorker
def perform def perform
ProjectGroupLink.expired.destroy_all # rubocop: disable DestroyAll ProjectGroupLink.expired.destroy_all # rubocop: disable DestroyAll
GroupGroupLink.expired.find_in_batches do |link_batch|
Groups::GroupLinks::DestroyService.new(nil, nil).execute(link_batch)
end
end end
end end
---
title: Add worker attributes to Sidekiq metrics
merge_request: 19491
author:
type: other
...@@ -425,6 +425,9 @@ data before running `pg_basebackup`. ...@@ -425,6 +425,9 @@ data before running `pg_basebackup`.
--host=<primary_node_ip> --host=<primary_node_ip>
``` ```
NOTE: **Note:**
Replication slot names must only contain lowercase letters, numbers, and the underscore character.
When prompted, enter the _plaintext_ password you set up for the `gitlab_replicator` When prompted, enter the _plaintext_ password you set up for the `gitlab_replicator`
user in the first step. user in the first step.
......
...@@ -60,7 +60,7 @@ module.exports = { ...@@ -60,7 +60,7 @@ module.exports = {
cacheDirectory: '<rootDir>/tmp/cache/jest', cacheDirectory: '<rootDir>/tmp/cache/jest',
modulePathIgnorePatterns: ['<rootDir>/.yarn-cache/'], modulePathIgnorePatterns: ['<rootDir>/.yarn-cache/'],
reporters, reporters,
setupFilesAfterEnv: ['<rootDir>/spec/frontend/test_setup.js'], setupFilesAfterEnv: ['<rootDir>/spec/frontend/test_setup.js', 'jest-canvas-mock'],
restoreMocks: true, restoreMocks: true,
transform: { transform: {
'^.+\\.(gql|graphql)$': 'jest-transform-graphql', '^.+\\.(gql|graphql)$': 'jest-transform-graphql',
......
...@@ -47,27 +47,29 @@ module Gitlab ...@@ -47,27 +47,29 @@ module Gitlab
] ]
}.freeze }.freeze
def [](identifier) def self.[](identifier)
events.find { |e| e.identifier.to_s.eql?(identifier.to_s) } || raise(KeyError) events.find { |e| e.identifier.to_s.eql?(identifier.to_s) } || raise(KeyError)
end end
# hash for defining ActiveRecord enum: identifier => number # hash for defining ActiveRecord enum: identifier => number
def to_enum def self.to_enum
ENUM_MAPPING.each_with_object({}) { |(k, v), hash| hash[k.identifier] = v } enum_mapping.each_with_object({}) { |(k, v), hash| hash[k.identifier] = v }
end end
# will be overridden in EE with custom events def self.pairing_rules
def pairing_rules
PAIRING_RULES PAIRING_RULES
end end
# will be overridden in EE with custom events def self.events
def events
EVENTS EVENTS
end end
module_function :[], :to_enum, :pairing_rules, :events def self.enum_mapping
ENUM_MAPPING
end
end end
end end
end end
end end
Gitlab::Analytics::CycleAnalytics::StageEvents.prepend_if_ee('::EE::Gitlab::Analytics::CycleAnalytics::StageEvents')
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
module Analytics module Analytics
module CycleAnalytics module CycleAnalytics
module StageEvents module StageEvents
class CodeStageStart < SimpleStageEvent class CodeStageStart < StageEvent
def self.name def self.name
s_("CycleAnalyticsEvent|Issue first mentioned in a commit") s_("CycleAnalyticsEvent|Issue first mentioned in a commit")
end end
......
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
module Analytics module Analytics
module CycleAnalytics module CycleAnalytics
module StageEvents module StageEvents
class IssueCreated < SimpleStageEvent class IssueCreated < StageEvent
def self.name def self.name
s_("CycleAnalyticsEvent|Issue created") s_("CycleAnalyticsEvent|Issue created")
end end
......
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
module Analytics module Analytics
module CycleAnalytics module CycleAnalytics
module StageEvents module StageEvents
class IssueFirstMentionedInCommit < SimpleStageEvent class IssueFirstMentionedInCommit < MetricsBasedStageEvent
def self.name def self.name
s_("CycleAnalyticsEvent|Issue first mentioned in a commit") s_("CycleAnalyticsEvent|Issue first mentioned in a commit")
end end
...@@ -20,12 +20,6 @@ module Gitlab ...@@ -20,12 +20,6 @@ module Gitlab
def timestamp_projection def timestamp_projection
issue_metrics_table[:first_mentioned_in_commit_at] issue_metrics_table[:first_mentioned_in_commit_at]
end end
# rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query)
query.joins(:metrics)
end
# rubocop: enable CodeReuse/ActiveRecord
end end
end end
end end
......
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
module Analytics module Analytics
module CycleAnalytics module CycleAnalytics
module StageEvents module StageEvents
class IssueStageEnd < SimpleStageEvent class IssueStageEnd < MetricsBasedStageEvent
def self.name def self.name
PlanStageStart.name PlanStageStart.name
end end
...@@ -26,7 +26,7 @@ module Gitlab ...@@ -26,7 +26,7 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query) def apply_query_customization(query)
query.joins(:metrics).where(issue_metrics_table[:first_added_to_board_at].not_eq(nil).or(issue_metrics_table[:first_associated_with_milestone_at].not_eq(nil))) super.where(issue_metrics_table[:first_added_to_board_at].not_eq(nil).or(issue_metrics_table[:first_associated_with_milestone_at].not_eq(nil)))
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
end end
......
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
module Analytics module Analytics
module CycleAnalytics module CycleAnalytics
module StageEvents module StageEvents
class MergeRequestCreated < SimpleStageEvent class MergeRequestCreated < StageEvent
def self.name def self.name
s_("CycleAnalyticsEvent|Merge request created") s_("CycleAnalyticsEvent|Merge request created")
end end
......
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
module Analytics module Analytics
module CycleAnalytics module CycleAnalytics
module StageEvents module StageEvents
class MergeRequestFirstDeployedToProduction < SimpleStageEvent class MergeRequestFirstDeployedToProduction < MetricsBasedStageEvent
def self.name def self.name
s_("CycleAnalyticsEvent|Merge request first deployed to production") s_("CycleAnalyticsEvent|Merge request first deployed to production")
end end
...@@ -23,7 +23,7 @@ module Gitlab ...@@ -23,7 +23,7 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query) def apply_query_customization(query)
query.joins(:metrics).where(timestamp_projection.gteq(mr_table[:created_at])) super.where(timestamp_projection.gteq(mr_table[:created_at]))
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
end end
......
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
module Analytics module Analytics
module CycleAnalytics module CycleAnalytics
module StageEvents module StageEvents
class MergeRequestLastBuildFinished < SimpleStageEvent class MergeRequestLastBuildFinished < MetricsBasedStageEvent
def self.name def self.name
s_("CycleAnalyticsEvent|Merge request last build finish time") s_("CycleAnalyticsEvent|Merge request last build finish time")
end end
...@@ -20,12 +20,6 @@ module Gitlab ...@@ -20,12 +20,6 @@ module Gitlab
def timestamp_projection def timestamp_projection
mr_metrics_table[:latest_build_finished_at] mr_metrics_table[:latest_build_finished_at]
end end
# rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query)
query.joins(:metrics)
end
# rubocop: enable CodeReuse/ActiveRecord
end end
end end
end end
......
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
module Analytics module Analytics
module CycleAnalytics module CycleAnalytics
module StageEvents module StageEvents
class MergeRequestLastBuildStarted < SimpleStageEvent class MergeRequestLastBuildStarted < MetricsBasedStageEvent
def self.name def self.name
s_("CycleAnalyticsEvent|Merge request last build start time") s_("CycleAnalyticsEvent|Merge request last build start time")
end end
...@@ -20,12 +20,6 @@ module Gitlab ...@@ -20,12 +20,6 @@ module Gitlab
def timestamp_projection def timestamp_projection
mr_metrics_table[:latest_build_started_at] mr_metrics_table[:latest_build_started_at]
end end
# rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query)
query.joins(:metrics)
end
# rubocop: enable CodeReuse/ActiveRecord
end end
end end
end end
......
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
module Analytics module Analytics
module CycleAnalytics module CycleAnalytics
module StageEvents module StageEvents
class MergeRequestMerged < SimpleStageEvent class MergeRequestMerged < MetricsBasedStageEvent
def self.name def self.name
s_("CycleAnalyticsEvent|Merge request merged") s_("CycleAnalyticsEvent|Merge request merged")
end end
...@@ -20,12 +20,6 @@ module Gitlab ...@@ -20,12 +20,6 @@ module Gitlab
def timestamp_projection def timestamp_projection
mr_metrics_table[:merged_at] mr_metrics_table[:merged_at]
end end
# rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query)
query.joins(:metrics)
end
# rubocop: enable CodeReuse/ActiveRecord
end end
end end
end end
......
...@@ -4,8 +4,12 @@ module Gitlab ...@@ -4,8 +4,12 @@ module Gitlab
module Analytics module Analytics
module CycleAnalytics module CycleAnalytics
module StageEvents module StageEvents
# Represents a simple event that usually refers to one database column and does not require additional user input class MetricsBasedStageEvent < StageEvent
class SimpleStageEvent < StageEvent # rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query)
query.joins(:metrics)
end
# rubocop: enable CodeReuse/ActiveRecord
end end
end end
end end
......
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
module Analytics module Analytics
module CycleAnalytics module CycleAnalytics
module StageEvents module StageEvents
class PlanStageStart < SimpleStageEvent class PlanStageStart < MetricsBasedStageEvent
def self.name def self.name
s_("CycleAnalyticsEvent|Issue first associated with a milestone or issue first added to a board") s_("CycleAnalyticsEvent|Issue first associated with a milestone or issue first added to a board")
end end
...@@ -26,8 +26,7 @@ module Gitlab ...@@ -26,8 +26,7 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query) def apply_query_customization(query)
query super
.joins(:metrics)
.where(issue_metrics_table[:first_added_to_board_at].not_eq(nil).or(issue_metrics_table[:first_associated_with_milestone_at].not_eq(nil))) .where(issue_metrics_table[:first_added_to_board_at].not_eq(nil).or(issue_metrics_table[:first_associated_with_milestone_at].not_eq(nil)))
.where(issue_metrics_table[:first_mentioned_in_commit_at].not_eq(nil)) .where(issue_metrics_table[:first_mentioned_in_commit_at].not_eq(nil))
end end
......
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
module Analytics module Analytics
module CycleAnalytics module CycleAnalytics
module StageEvents module StageEvents
class ProductionStageEnd < SimpleStageEvent class ProductionStageEnd < StageEvent
def self.name def self.name
PlanStageStart.name PlanStageStart.name
end end
......
...@@ -13,8 +13,8 @@ module Gitlab ...@@ -13,8 +13,8 @@ module Gitlab
@metrics[:sidekiq_concurrency].set({}, Sidekiq.options[:concurrency].to_i) @metrics[:sidekiq_concurrency].set({}, Sidekiq.options[:concurrency].to_i)
end end
def call(worker, job, queue) def call(_worker, job, queue)
labels = create_labels(worker, queue) labels = create_labels(queue)
queue_duration = ::Gitlab::InstrumentationHelper.queue_duration_for_job(job) queue_duration = ::Gitlab::InstrumentationHelper.queue_duration_for_job(job)
@metrics[:sidekiq_jobs_queue_duration_seconds].observe(labels, queue_duration) if queue_duration @metrics[:sidekiq_jobs_queue_duration_seconds].observe(labels, queue_duration) if queue_duration
...@@ -62,20 +62,10 @@ module Gitlab ...@@ -62,20 +62,10 @@ module Gitlab
} }
end end
def create_labels(worker, queue) def create_labels(queue)
labels = { queue: queue } {
return labels unless worker.include? WorkerAttributes queue: queue
}
labels[:latency_sensitive] = true if worker.latency_sensitive_worker?
labels[:external_deps] = true if worker.worker_has_external_dependencies?
feature_category = worker.get_feature_category
labels[:feat_cat] = feature_category if feature_category
resource_boundary = worker.get_worker_resource_boundary
labels[:boundary] = resource_boundary if resource_boundary && resource_boundary != :unknown
labels
end end
def get_thread_cputime def get_thread_cputime
......
...@@ -4995,15 +4995,30 @@ msgstr "" ...@@ -4995,15 +4995,30 @@ msgstr ""
msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project." msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
msgstr "" msgstr ""
msgid "CycleAnalyticsEvent|Issue closed"
msgstr ""
msgid "CycleAnalyticsEvent|Issue created" msgid "CycleAnalyticsEvent|Issue created"
msgstr "" msgstr ""
msgid "CycleAnalyticsEvent|Issue first added to a board"
msgstr ""
msgid "CycleAnalyticsEvent|Issue first associated with a milestone"
msgstr ""
msgid "CycleAnalyticsEvent|Issue first associated with a milestone or issue first added to a board" msgid "CycleAnalyticsEvent|Issue first associated with a milestone or issue first added to a board"
msgstr "" msgstr ""
msgid "CycleAnalyticsEvent|Issue first mentioned in a commit" msgid "CycleAnalyticsEvent|Issue first mentioned in a commit"
msgstr "" msgstr ""
msgid "CycleAnalyticsEvent|Issue last edited"
msgstr ""
msgid "CycleAnalyticsEvent|Merge request closed"
msgstr ""
msgid "CycleAnalyticsEvent|Merge request created" msgid "CycleAnalyticsEvent|Merge request created"
msgstr "" msgstr ""
...@@ -5016,6 +5031,9 @@ msgstr "" ...@@ -5016,6 +5031,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request last build start time" msgid "CycleAnalyticsEvent|Merge request last build start time"
msgstr "" msgstr ""
msgid "CycleAnalyticsEvent|Merge request last edited"
msgstr ""
msgid "CycleAnalyticsEvent|Merge request merged" msgid "CycleAnalyticsEvent|Merge request merged"
msgstr "" msgstr ""
......
...@@ -2,21 +2,34 @@ import { shallowMount } from '@vue/test-utils'; ...@@ -2,21 +2,34 @@ import { shallowMount } from '@vue/test-utils';
import { createStore } from '~/monitoring/stores'; import { createStore } from '~/monitoring/stores';
import { GlLink } from '@gitlab/ui'; import { GlLink } from '@gitlab/ui';
import { GlAreaChart, GlLineChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts'; import { GlAreaChart, GlLineChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts';
import { shallowWrapperContainsSlotText } from 'spec/helpers/vue_test_utils_helper'; import { shallowWrapperContainsSlotText } from 'helpers/vue_test_utils_helper';
import TimeSeries from '~/monitoring/components/charts/time_series.vue'; import TimeSeries from '~/monitoring/components/charts/time_series.vue';
import * as types from '~/monitoring/stores/mutation_types'; import * as types from '~/monitoring/stores/mutation_types';
import { TEST_HOST } from 'spec/test_constants'; import { TEST_HOST } from 'spec/test_constants';
import MonitoringMock, { deploymentData, mockProjectPath } from '../mock_data'; import MonitoringMock, {
deploymentData,
mockProjectPath,
} from '../../../javascripts/monitoring/mock_data';
import * as iconUtils from '~/lib/utils/icon_utils';
const mockSvgPathContent = 'mockSvgPathContent';
const mockSha = 'mockSha';
const mockWidgets = 'mockWidgets';
const projectPath = `${TEST_HOST}${mockProjectPath}`;
const commitUrl = `${projectPath}/commit/${mockSha}`;
jest.mock('~/lib/utils/icon_utils', () => ({
getSvgIconPathContent: jest.fn().mockImplementation(
() =>
new Promise(resolve => {
resolve(mockSvgPathContent);
}),
),
}));
describe('Time series component', () => { describe('Time series component', () => {
const mockSha = 'mockSha';
const mockWidgets = 'mockWidgets';
const mockSvgPathContent = 'mockSvgPathContent';
const projectPath = `${TEST_HOST}${mockProjectPath}`;
const commitUrl = `${projectPath}/commit/${mockSha}`;
let mockGraphData; let mockGraphData;
let makeTimeSeriesChart; let makeTimeSeriesChart;
let spriteSpy;
let store; let store;
beforeEach(() => { beforeEach(() => {
...@@ -27,6 +40,7 @@ describe('Time series component', () => { ...@@ -27,6 +40,7 @@ describe('Time series component', () => {
makeTimeSeriesChart = (graphData, type) => makeTimeSeriesChart = (graphData, type) =>
shallowMount(TimeSeries, { shallowMount(TimeSeries, {
attachToDocument: true,
propsData: { propsData: {
graphData: { ...graphData, type }, graphData: { ...graphData, type },
deploymentData: store.state.monitoringDashboard.deploymentData, deploymentData: store.state.monitoringDashboard.deploymentData,
...@@ -38,10 +52,6 @@ describe('Time series component', () => { ...@@ -38,10 +52,6 @@ describe('Time series component', () => {
sync: false, sync: false,
store, store,
}); });
spriteSpy = spyOnDependency(TimeSeries, 'getSvgIconPathContent').and.callFake(
() => new Promise(resolve => resolve(mockSvgPathContent)),
);
}); });
describe('general functions', () => { describe('general functions', () => {
...@@ -147,7 +157,7 @@ describe('Time series component', () => { ...@@ -147,7 +157,7 @@ describe('Time series component', () => {
}); });
it('gets svg path content', () => { it('gets svg path content', () => {
expect(spriteSpy).toHaveBeenCalledWith(mockSvgName); expect(iconUtils.getSvgIconPathContent).toHaveBeenCalledWith(mockSvgName);
}); });
it('sets svg path content', () => { it('sets svg path content', () => {
...@@ -171,7 +181,7 @@ describe('Time series component', () => { ...@@ -171,7 +181,7 @@ describe('Time series component', () => {
const mockWidth = 233; const mockWidth = 233;
beforeEach(() => { beforeEach(() => {
spyOn(Element.prototype, 'getBoundingClientRect').and.callFake(() => ({ jest.spyOn(Element.prototype, 'getBoundingClientRect').mockImplementation(() => ({
width: mockWidth, width: mockWidth,
})); }));
timeSeriesChart.vm.onResize(); timeSeriesChart.vm.onResize();
...@@ -227,7 +237,7 @@ describe('Time series component', () => { ...@@ -227,7 +237,7 @@ describe('Time series component', () => {
option: mockOption, option: mockOption,
}); });
expect(timeSeriesChart.vm.chartOptions).toEqual(jasmine.objectContaining(mockOption)); expect(timeSeriesChart.vm.chartOptions).toEqual(expect.objectContaining(mockOption));
}); });
it('additional series', () => { it('additional series', () => {
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import AxiosMockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import PanelType from '~/monitoring/components/panel_type.vue'; import PanelType from '~/monitoring/components/panel_type.vue';
import EmptyChart from '~/monitoring/components/charts/empty_chart.vue'; import EmptyChart from '~/monitoring/components/charts/empty_chart.vue';
import TimeSeriesChart from '~/monitoring/components/charts/time_series.vue'; import TimeSeriesChart from '~/monitoring/components/charts/time_series.vue';
import AnomalyChart from '~/monitoring/components/charts/anomaly.vue'; import AnomalyChart from '~/monitoring/components/charts/anomaly.vue';
import { graphDataPrometheusQueryRange } from './mock_data'; import { graphDataPrometheusQueryRange } from '../../javascripts/monitoring/mock_data';
import { anomalyMockGraphData } from '../../frontend/monitoring/mock_data'; import { anomalyMockGraphData } from '../../frontend/monitoring/mock_data';
import { createStore } from '~/monitoring/stores'; import { createStore } from '~/monitoring/stores';
global.IS_EE = true;
global.URL.createObjectURL = jest.fn(() => {});
describe('Panel Type component', () => { describe('Panel Type component', () => {
let axiosMock;
let store; let store;
let panelType; let panelType;
const dashboardWidth = 100; const dashboardWidth = 100;
beforeEach(() => {
axiosMock = new AxiosMockAdapter(axios);
});
afterEach(() => {
axiosMock.reset();
});
describe('When no graphData is available', () => { describe('When no graphData is available', () => {
let glEmptyChart; let glEmptyChart;
// Deep clone object before modifying // Deep clone object before modifying
...@@ -25,6 +39,7 @@ describe('Panel Type component', () => { ...@@ -25,6 +39,7 @@ describe('Panel Type component', () => {
dashboardWidth, dashboardWidth,
graphData: graphDataNoResult, graphData: graphDataNoResult,
}, },
sync: false,
}); });
}); });
...@@ -57,14 +72,14 @@ describe('Panel Type component', () => { ...@@ -57,14 +72,14 @@ describe('Panel Type component', () => {
graphData: graphDataPrometheusQueryRange, graphData: graphDataPrometheusQueryRange,
}; };
beforeEach(done => { beforeEach(() => {
store = createStore(); store = createStore();
panelType = shallowMount(PanelType, { panelType = shallowMount(PanelType, {
propsData, propsData,
sync: false,
store, store,
sync: false,
attachToDocument: true,
}); });
panelType.vm.$nextTick(done);
}); });
describe('Time Series Chart panel type', () => { describe('Time Series Chart panel type', () => {
......
# frozen_string_literal: true # frozen_string_literal: true
require 'fast_spec_helper' require 'fast_spec_helper'
require 'rspec-parameterized'
describe Gitlab::SidekiqMiddleware::Metrics do describe Gitlab::SidekiqMiddleware::Metrics do
using RSpec::Parameterized::TableSyntax
let(:middleware) { described_class.new } let(:middleware) { described_class.new }
let(:concurrency_metric) { double('concurrency metric') } let(:concurrency_metric) { double('concurrency metric') }
...@@ -48,7 +45,7 @@ describe Gitlab::SidekiqMiddleware::Metrics do ...@@ -48,7 +45,7 @@ describe Gitlab::SidekiqMiddleware::Metrics do
let(:job) { {} } let(:job) { {} }
let(:job_status) { :done } let(:job_status) { :done }
let(:labels) { { queue: :test } } let(:labels) { { queue: :test } }
let(:labels_with_job_status) { labels.merge(job_status: job_status) } let(:labels_with_job_status) { { queue: :test, job_status: job_status } }
let(:thread_cputime_before) { 1 } let(:thread_cputime_before) { 1 }
let(:thread_cputime_after) { 2 } let(:thread_cputime_after) { 2 }
...@@ -60,31 +57,11 @@ describe Gitlab::SidekiqMiddleware::Metrics do ...@@ -60,31 +57,11 @@ describe Gitlab::SidekiqMiddleware::Metrics do
let(:queue_duration_for_job) { 0.01 } let(:queue_duration_for_job) { 0.01 }
where(:worker_has_attributes, :worker_is_latency_sensitive, :worker_has_external_dependencies, :worker_feature_category, :worker_resource_boundary, :labels) do
false | false | false | nil | nil | { queue: :test }
true | false | false | nil | nil | { queue: :test }
true | true | false | nil | nil | { queue: :test, latency_sensitive: true }
true | false | true | nil | nil | { queue: :test, external_deps: true }
true | false | false | :authentication | nil | { queue: :test, feat_cat: :authentication }
true | false | false | nil | :cpu | { queue: :test, boundary: :cpu }
true | false | false | nil | :memory | { queue: :test, boundary: :memory }
true | false | false | nil | :unknown | { queue: :test }
true | true | true | :authentication | :cpu | { queue: :test, latency_sensitive: true, external_deps: true, feat_cat: :authentication, boundary: :cpu }
end
with_them do
before do before do
allow(middleware).to receive(:get_thread_cputime).and_return(thread_cputime_before, thread_cputime_after) allow(middleware).to receive(:get_thread_cputime).and_return(thread_cputime_before, thread_cputime_after)
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(monotonic_time_before, monotonic_time_after) allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(monotonic_time_before, monotonic_time_after)
allow(Gitlab::InstrumentationHelper).to receive(:queue_duration_for_job).with(job).and_return(queue_duration_for_job) allow(Gitlab::InstrumentationHelper).to receive(:queue_duration_for_job).with(job).and_return(queue_duration_for_job)
# Attributes
allow(worker).to receive(:include?).with(WorkerAttributes).and_return(worker_has_attributes)
allow(worker).to receive(:latency_sensitive_worker?).and_return(worker_is_latency_sensitive)
allow(worker).to receive(:worker_has_external_dependencies?).and_return(worker_has_external_dependencies)
allow(worker).to receive(:get_worker_resource_boundary).and_return(worker_resource_boundary)
allow(worker).to receive(:get_feature_category).and_return(worker_feature_category)
expect(running_jobs_metric).to receive(:increment).with(labels, 1) expect(running_jobs_metric).to receive(:increment).with(labels, 1)
expect(running_jobs_metric).to receive(:increment).with(labels, -1) expect(running_jobs_metric).to receive(:increment).with(labels, -1)
...@@ -105,8 +82,6 @@ describe Gitlab::SidekiqMiddleware::Metrics do ...@@ -105,8 +82,6 @@ describe Gitlab::SidekiqMiddleware::Metrics do
let(:queue_duration_for_job) { nil } let(:queue_duration_for_job) { nil }
it 'does not set the queue_duration_seconds histogram' do it 'does not set the queue_duration_seconds histogram' do
expect(queue_duration_seconds).not_to receive(:observe)
middleware.call(worker, job, :test) { nil } middleware.call(worker, job, :test) { nil }
end end
end end
...@@ -131,5 +106,4 @@ describe Gitlab::SidekiqMiddleware::Metrics do ...@@ -131,5 +106,4 @@ describe Gitlab::SidekiqMiddleware::Metrics do
end end
end end
end end
end
end end
# frozen_string_literal: true
require 'spec_helper'
describe Groups::GroupLinks::DestroyService, '#execute' do
let(:user) { create(:user) }
let_it_be(:group) { create(:group, :private) }
let_it_be(:shared_group) { create(:group, :private) }
let_it_be(:project) { create(:project, group: shared_group) }
subject { described_class.new(nil, nil) }
context 'single link' do
let!(:link) { create(:group_group_link, shared_group: shared_group, shared_with_group: group) }
it 'destroys link' do
expect { subject.execute(link) }.to change { GroupGroupLink.count }.from(1).to(0)
end
it 'revokes project authorization' do
group.add_developer(user)
expect { subject.execute(link) }.to(
change { Ability.allowed?(user, :read_project, project) }.from(true).to(false))
end
end
context 'multiple links' do
let_it_be(:another_group) { create(:group, :private) }
let_it_be(:another_shared_group) { create(:group, :private) }
let!(:links) do
[
create(:group_group_link, shared_group: shared_group, shared_with_group: group),
create(:group_group_link, shared_group: shared_group, shared_with_group: another_group),
create(:group_group_link, shared_group: another_shared_group, shared_with_group: group),
create(:group_group_link, shared_group: another_shared_group, shared_with_group: another_group)
]
end
it 'updates project authorization once per group' do
expect(GroupGroupLink).to receive(:delete)
expect(group).to receive(:refresh_members_authorized_projects).once
expect(another_group).to receive(:refresh_members_authorized_projects).once
subject.execute(links)
end
it 'rolls back changes when error happens' do
group.add_developer(user)
expect(group).to receive(:refresh_members_authorized_projects).once.and_call_original
expect(another_group).to(
receive(:refresh_members_authorized_projects).and_raise('boom'))
expect { subject.execute(links) }.to raise_error('boom')
expect(GroupGroupLink.count).to eq(links.length)
expect(Ability.allowed?(user, :read_project, project)).to be_truthy
end
end
end
...@@ -4,6 +4,7 @@ require 'spec_helper' ...@@ -4,6 +4,7 @@ require 'spec_helper'
describe RemoveExpiredGroupLinksWorker do describe RemoveExpiredGroupLinksWorker do
describe '#perform' do describe '#perform' do
context 'ProjectGroupLinks' do
let!(:expired_project_group_link) { create(:project_group_link, expires_at: 1.hour.ago) } let!(:expired_project_group_link) { create(:project_group_link, expires_at: 1.hour.ago) }
let!(:project_group_link_expiring_in_future) { create(:project_group_link, expires_at: 10.days.from_now) } let!(:project_group_link_expiring_in_future) { create(:project_group_link, expires_at: 10.days.from_now) }
let!(:non_expiring_project_group_link) { create(:project_group_link, expires_at: nil) } let!(:non_expiring_project_group_link) { create(:project_group_link, expires_at: nil) }
...@@ -23,4 +24,34 @@ describe RemoveExpiredGroupLinksWorker do ...@@ -23,4 +24,34 @@ describe RemoveExpiredGroupLinksWorker do
expect(non_expiring_project_group_link.reload).to be_present expect(non_expiring_project_group_link.reload).to be_present
end end
end end
context 'GroupGroupLinks' do
let(:mock_destroy_service) { instance_double(Groups::GroupLinks::DestroyService) }
before do
allow(Groups::GroupLinks::DestroyService).to(
receive(:new).and_return(mock_destroy_service))
end
context 'expired GroupGroupLink exists' do
before do
create(:group_group_link, expires_at: 1.hour.ago)
end
it 'calls Groups::GroupLinks::DestroyService' do
expect(mock_destroy_service).to receive(:execute).once
subject.perform
end
end
context 'expired GroupGroupLink does not exist' do
it 'does not call Groups::GroupLinks::DestroyService' do
expect(mock_destroy_service).not_to receive(:execute)
subject.perform
end
end
end
end
end end
...@@ -2991,7 +2991,7 @@ collection-visit@^1.0.0: ...@@ -2991,7 +2991,7 @@ collection-visit@^1.0.0:
map-visit "^1.0.0" map-visit "^1.0.0"
object-visit "^1.0.0" object-visit "^1.0.0"
color-convert@^0.5.3: color-convert@^0.5.3, color-convert@~0.5.0:
version "0.5.3" version "0.5.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd"
integrity sha1-vbbGnOZg+t/+CwAHzER+G59ygr0= integrity sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=
...@@ -3468,6 +3468,11 @@ cssesc@^3.0.0: ...@@ -3468,6 +3468,11 @@ cssesc@^3.0.0:
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
cssfontparser@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/cssfontparser/-/cssfontparser-1.2.1.tgz#f4022fc8f9700c68029d542084afbaf425a3f3e3"
integrity sha1-9AIvyPlwDGgCnVQghK+69CWj8+M=
cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
version "0.3.4" version "0.3.4"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.4.tgz#8cd52e8a3acfd68d3aed38ee0a640177d2f9d797" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.4.tgz#8cd52e8a3acfd68d3aed38ee0a640177d2f9d797"
...@@ -6877,6 +6882,14 @@ jed@^1.1.1: ...@@ -6877,6 +6882,14 @@ jed@^1.1.1:
resolved "https://registry.yarnpkg.com/jed/-/jed-1.1.1.tgz#7a549bbd9ffe1585b0cd0a191e203055bee574b4" resolved "https://registry.yarnpkg.com/jed/-/jed-1.1.1.tgz#7a549bbd9ffe1585b0cd0a191e203055bee574b4"
integrity sha1-elSbvZ/+FYWwzQoZHiAwVb7ldLQ= integrity sha1-elSbvZ/+FYWwzQoZHiAwVb7ldLQ=
jest-canvas-mock@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/jest-canvas-mock/-/jest-canvas-mock-2.1.2.tgz#0d16c9f91534f773fd132fc289f2e6b6db8faa28"
integrity sha512-1VI4PK4/X70yrSjYScYVkYJYbXYlZLKJkUrAlyHjQsfolv64aoFyIrmMDtqCjpYrpVvWYEcAGUaYv5DVJj00oQ==
dependencies:
cssfontparser "^1.2.1"
parse-color "^1.0.0"
jest-changed-files@^24.8.0: jest-changed-files@^24.8.0:
version "24.8.0" version "24.8.0"
resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.8.0.tgz#7e7eb21cf687587a85e50f3d249d1327e15b157b" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.8.0.tgz#7e7eb21cf687587a85e50f3d249d1327e15b157b"
...@@ -9123,6 +9136,13 @@ parse-asn1@^5.0.0: ...@@ -9123,6 +9136,13 @@ parse-asn1@^5.0.0:
evp_bytestokey "^1.0.0" evp_bytestokey "^1.0.0"
pbkdf2 "^3.0.3" pbkdf2 "^3.0.3"
parse-color@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/parse-color/-/parse-color-1.0.0.tgz#7b748b95a83f03f16a94f535e52d7f3d94658619"
integrity sha1-e3SLlag/A/FqlPU15S1/PZRlhhk=
dependencies:
color-convert "~0.5.0"
parse-entities@^1.0.2, parse-entities@^1.1.0: parse-entities@^1.0.2, parse-entities@^1.1.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.2.0.tgz#9deac087661b2e36814153cb78d7e54a4c5fd6f4" resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.2.0.tgz#9deac087661b2e36814153cb78d7e54a4c5fd6f4"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment