diff options
20 files changed, 153 insertions, 87 deletions
diff --git a/app/assets/javascripts/vue_shared/components/members/action_buttons/user_action_buttons.vue b/app/assets/javascripts/vue_shared/components/members/action_buttons/user_action_buttons.vue index 8fa3d439fc1..484dbb8fef5 100644 --- a/app/assets/javascripts/vue_shared/components/members/action_buttons/user_action_buttons.vue +++ b/app/assets/javascripts/vue_shared/components/members/action_buttons/user_action_buttons.vue @@ -6,7 +6,13 @@ import { s__, sprintf } from '~/locale'; export default { name: 'UserActionButtons', - components: { ActionButtonGroup, RemoveMemberButton, LeaveButton }, + components: { + ActionButtonGroup, + RemoveMemberButton, + LeaveButton, + LdapOverrideButton: () => + import('ee_component/vue_shared/components/members/ldap/ldap_override_button.vue'), + }, props: { member: { type: Object, @@ -57,5 +63,8 @@ export default { :title="s__('Member|Remove member')" /> </div> + <div v-else-if="permissions.canOverride && !member.isOverridden" class="gl-px-1"> + <ldap-override-button :member="member" /> + </div> </action-button-group> </template> diff --git a/app/assets/javascripts/vue_shared/components/members/table/expiration_datepicker.vue b/app/assets/javascripts/vue_shared/components/members/table/expiration_datepicker.vue index e72093b6c9e..0a8af81c1d1 100644 --- a/app/assets/javascripts/vue_shared/components/members/table/expiration_datepicker.vue +++ b/app/assets/javascripts/vue_shared/components/members/table/expiration_datepicker.vue @@ -32,6 +32,13 @@ export default { return getDateInFuture(beginningOfToday, 1); }, + disabled() { + return ( + this.busy || + !this.permissions.canUpdate || + (this.permissions.canOverride && !this.member.isOverridden) + ); + }, }, mounted() { if (this.member.expiresAt) { @@ -85,7 +92,7 @@ export default { :container="null" :min-date="minDate" :placeholder="__('Expiration date')" - :disabled="!permissions.canUpdate || busy" + :disabled="disabled" @input="handleInput" @clear="handleClear" /> diff --git a/app/assets/javascripts/vue_shared/components/members/table/members_table.vue b/app/assets/javascripts/vue_shared/components/members/table/members_table.vue index 5a5b42e7952..7f6fe1ee250 100644 --- a/app/assets/javascripts/vue_shared/components/members/table/members_table.vue +++ b/app/assets/javascripts/vue_shared/components/members/table/members_table.vue @@ -1,6 +1,7 @@ <script> import { mapState } from 'vuex'; import { GlTable, GlBadge } from '@gitlab/ui'; +import MembersTableCell from 'ee_else_ce/vue_shared/components/members/table/members_table_cell.vue'; import { FIELDS } from '../constants'; import initUserPopovers from '~/user_popovers'; import MemberAvatar from './member_avatar.vue'; @@ -8,7 +9,6 @@ import MemberSource from './member_source.vue'; import CreatedAt from './created_at.vue'; import ExpiresAt from './expires_at.vue'; import MemberActionButtons from './member_action_buttons.vue'; -import MembersTableCell from './members_table_cell.vue'; import RoleDropdown from './role_dropdown.vue'; import RemoveGroupLinkModal from '../modals/remove_group_link_modal.vue'; import ExpirationDatepicker from './expiration_datepicker.vue'; @@ -91,7 +91,7 @@ export default { <template #cell(maxRole)="{ item: member }"> <members-table-cell #default="{ permissions }" :member="member"> - <role-dropdown v-if="permissions.canUpdate" :member="member" /> + <role-dropdown v-if="permissions.canUpdate" :permissions="permissions" :member="member" /> <gl-badge v-else>{{ member.accessLevel.stringValue }}</gl-badge> </members-table-cell> </template> diff --git a/app/assets/javascripts/vue_shared/components/members/table/role_dropdown.vue b/app/assets/javascripts/vue_shared/components/members/table/role_dropdown.vue index 2b40ccc3a9d..42a5ca1b3c9 100644 --- a/app/assets/javascripts/vue_shared/components/members/table/role_dropdown.vue +++ b/app/assets/javascripts/vue_shared/components/members/table/role_dropdown.vue @@ -9,12 +9,18 @@ export default { components: { GlDropdown, GlDropdownItem, + LdapDropdownItem: () => + import('ee_component/vue_shared/components/members/ldap/ldap_dropdown_item.vue'), }, props: { member: { type: Object, required: true, }, + permissions: { + type: Object, + required: true, + }, }, data() { return { @@ -22,6 +28,11 @@ export default { busy: false, }; }, + computed: { + disabled() { + return this.busy || (this.permissions.canOverride && !this.member.isOverridden); + }, + }, mounted() { this.isDesktop = bp.isDesktop(); }, @@ -55,7 +66,7 @@ export default { :right="!isDesktop" :text="member.accessLevel.stringValue" :header-text="__('Change permissions')" - :disabled="busy" + :disabled="disabled" > <gl-dropdown-item v-for="(value, name) in member.validRoles" @@ -66,5 +77,9 @@ export default { > {{ name }} </gl-dropdown-item> + <ldap-dropdown-item + v-if="permissions.canOverride && member.isOverridden" + :member-id="member.id" + /> </gl-dropdown> </template> diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index 135ab5ed7a1..a8b489f1273 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -225,7 +225,7 @@ } .col-actions { - width: px-to-rem(50px); + width: px-to-rem(65px); } } diff --git a/app/views/search/_filter.html.haml b/app/views/search/_filter.html.haml index e7febd4638b..6e8e7d8fd1f 100644 --- a/app/views/search/_filter.html.haml +++ b/app/views/search/_filter.html.haml @@ -17,10 +17,10 @@ = dropdown_content = dropdown_loading -.dropdown.project-filter.form-group.mb-lg-0.mx-lg-1 +.dropdown.form-group.mb-lg-0.mx-lg-1{ data: { testid: "project-filter" } } %label.d-block{ for: "dashboard_search_project" } = _("Project") - %button.dropdown-menu-toggle.gl-display-inline-flex.js-search-project-dropdown.gl-mt-0{ type: "button", id: "dashboard_search_project", data: { toggle: "dropdown", target: '.project-filter' } } + %button.dropdown-menu-toggle.gl-display-inline-flex.js-search-project-dropdown.gl-mt-0{ type: "button", id: "dashboard_search_project", data: { toggle: "dropdown" } } %span.dropdown-toggle-text.gl-flex-grow-1.str-truncated-100 = @project&.full_name || _("Any") - if @project.present? diff --git a/changelogs/unreleased/270412-license-error-in-template.yml b/changelogs/unreleased/270412-license-error-in-template.yml new file mode 100644 index 00000000000..e3a7b9daa36 --- /dev/null +++ b/changelogs/unreleased/270412-license-error-in-template.yml @@ -0,0 +1,5 @@ +--- +title: Show error in pipeline when API Fuzzing not licensed +merge_request: 46064 +author: +type: changed diff --git a/changelogs/unreleased/bjk-http_requests_total_status_matrix.yml b/changelogs/unreleased/bjk-http_requests_total_status_matrix.yml new file mode 100644 index 00000000000..979e6f507a0 --- /dev/null +++ b/changelogs/unreleased/bjk-http_requests_total_status_matrix.yml @@ -0,0 +1,5 @@ +--- +title: Make sure the http_requests_total and http_request_duration_seconds metrics are not empty on application start +merge_request: 45755 +author: +type: fixed diff --git a/config/initializers/7_prometheus_metrics.rb b/config/initializers/7_prometheus_metrics.rb index dbaebc83658..65ff6b656b9 100644 --- a/config/initializers/7_prometheus_metrics.rb +++ b/config/initializers/7_prometheus_metrics.rb @@ -70,7 +70,7 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled? Gitlab::Metrics.gauge(:deployments, 'GitLab Version', {}, :max).set({ version: Gitlab::VERSION }, 1) unless Gitlab::Runtime.sidekiq? - Gitlab::Metrics::RequestsRackMiddleware.initialize_http_request_duration_seconds + Gitlab::Metrics::RequestsRackMiddleware.initialize_metrics end rescue IOError => e Gitlab::ErrorTracking.track_exception(e) diff --git a/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml index 6eab3557024..68ea302261f 100644 --- a/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml @@ -26,6 +26,17 @@ variables: FUZZAPI_IMAGE: registry.gitlab.com/gitlab-org/security-products/analyzers/api-fuzzing:${FUZZAPI_VERSION}-engine # +apifuzzer_fuzz_unlicensed: + stage: fuzz + allow_failure: true + rules: + - if: '$GITLAB_FEATURES !~ /\bapi_fuzzing\b/ && $API_FUZZING_DISABLED == null' + - when: never + script: + - | + echo "Error: Your GitLab project is not licensed for API Fuzzing." + - exit 1 + apifuzzer_fuzz: stage: fuzz image: diff --git a/lib/gitlab/metrics/requests_rack_middleware.rb b/lib/gitlab/metrics/requests_rack_middleware.rb index bb7ecb6128c..8b2aa4d29d7 100644 --- a/lib/gitlab/metrics/requests_rack_middleware.rb +++ b/lib/gitlab/metrics/requests_rack_middleware.rb @@ -3,7 +3,15 @@ module Gitlab module Metrics class RequestsRackMiddleware - HTTP_METHODS = %w(delete get head options patch post put).to_set.freeze + HTTP_METHODS = { + "delete" => %w(200 202 204 303 400 401 403 404 500 503), + "get" => %w(200 204 301 302 303 304 307 400 401 403 404 410 422 429 500 503), + "head" => %w(200 204 301 302 303 401 403 404 410 500), + "options" => %w(200 404), + "patch" => %w(200 202 204 400 403 404 409 416 500), + "post" => %w(200 201 202 204 301 302 303 304 400 401 403 404 406 409 410 412 422 429 500 503), + "put" => %w(200 202 204 400 401 403 404 405 406 409 410 422 500) + }.freeze HEALTH_ENDPOINT = /^\/-\/(liveness|readiness|health|metrics)\/?$/.freeze @@ -14,8 +22,8 @@ module Gitlab @app = app end - def self.http_request_total - @http_request_total ||= ::Gitlab::Metrics.counter(:http_requests_total, 'Request count') + def self.http_requests_total + @http_requests_total ||= ::Gitlab::Metrics.counter(:http_requests_total, 'Request count') end def self.rack_uncaught_errors_count @@ -31,15 +39,31 @@ module Gitlab @http_health_requests_total ||= ::Gitlab::Metrics.counter(:http_health_requests_total, 'Health endpoint request count') end - def self.initialize_http_request_duration_seconds - HTTP_METHODS.each do |method| + def self.initialize_metrics + # This initialization is done to avoid gaps in scraped metrics after + # restarts. It makes sure all counters/histograms are available at + # process start. + # + # For example `rate(http_requests_total{status="500"}[1m])` would return + # no data until the first 500 error would occur. + + # The list of feature categories is currently not needed by the application + # anywhere else. So no need to keep these in memory forever. + # Doing it here, means we're only reading the file on boot. + feature_categories = YAML.load_file(Rails.root.join('config', 'feature_categories.yml')).map(&:strip).uniq << FEATURE_CATEGORY_DEFAULT + + HTTP_METHODS.each do |method, statuses| http_request_duration_seconds.get({ method: method }) + + statuses.product(feature_categories) do |status, feature_category| + http_requests_total.get({ method: method, status: status, feature_category: feature_category }) + end end end def call(env) method = env['REQUEST_METHOD'].downcase - method = 'INVALID' unless HTTP_METHODS.include?(method) + method = 'INVALID' unless HTTP_METHODS.key?(method) started = Time.now.to_f health_endpoint = health_endpoint?(env['PATH_INFO']) status = 'undefined' @@ -61,9 +85,13 @@ module Gitlab raise ensure if health_endpoint - RequestsRackMiddleware.http_health_requests_total.increment(status: status, method: method) + RequestsRackMiddleware.http_health_requests_total.increment(status: status.to_s, method: method) else - RequestsRackMiddleware.http_request_total.increment(status: status, method: method, feature_category: feature_category.presence || FEATURE_CATEGORY_DEFAULT) + RequestsRackMiddleware.http_requests_total.increment( + status: status.to_s, + method: method, + feature_category: feature_category.presence || FEATURE_CATEGORY_DEFAULT + ) end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 5b9d9ee4a3b..49955728c10 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -16414,6 +16414,12 @@ msgstr "" msgid "Members|Remove group" msgstr "" +msgid "Members|Revert to LDAP group sync settings" +msgstr "" + +msgid "Members|Reverted to LDAP group sync settings." +msgstr "" + msgid "Members|Role updated successfully." msgstr "" diff --git a/spec/features/search/user_searches_for_code_spec.rb b/spec/features/search/user_searches_for_code_spec.rb index a88043c98ac..f761bd30baf 100644 --- a/spec/features/search/user_searches_for_code_spec.rb +++ b/spec/features/search/user_searches_for_code_spec.rb @@ -28,10 +28,7 @@ RSpec.describe 'User searches for code' do before do visit(search_path) find('.js-search-project-dropdown').click - - page.within('.project-filter') do - click_link(project.full_name) - end + find('[data-testid="project-filter"]').click_link(project.full_name) end include_examples 'top right search form' diff --git a/spec/features/search/user_searches_for_issues_spec.rb b/spec/features/search/user_searches_for_issues_spec.rb index 35d5722f708..e2ae2738d2f 100644 --- a/spec/features/search/user_searches_for_issues_spec.rb +++ b/spec/features/search/user_searches_for_issues_spec.rb @@ -86,10 +86,7 @@ RSpec.describe 'User searches for issues', :js do context 'when on a project page' do it 'finds an issue' do find('.js-search-project-dropdown').click - - page.within('.project-filter') do - click_link(project.full_name) - end + find('[data-testid="project-filter"]').click_link(project.full_name) search_for_issue(issue1.title) diff --git a/spec/features/search/user_searches_for_merge_requests_spec.rb b/spec/features/search/user_searches_for_merge_requests_spec.rb index 40583664958..6f8f6303b66 100644 --- a/spec/features/search/user_searches_for_merge_requests_spec.rb +++ b/spec/features/search/user_searches_for_merge_requests_spec.rb @@ -31,10 +31,7 @@ RSpec.describe 'User searches for merge requests', :js do context 'when on a project page' do it 'finds a merge request' do find('.js-search-project-dropdown').click - - page.within('.project-filter') do - click_link(project.full_name) - end + find('[data-testid="project-filter"]').click_link(project.full_name) fill_in('dashboard_search', with: merge_request1.title) find('.btn-search').click diff --git a/spec/features/search/user_searches_for_milestones_spec.rb b/spec/features/search/user_searches_for_milestones_spec.rb index 64e756db180..1a2227db214 100644 --- a/spec/features/search/user_searches_for_milestones_spec.rb +++ b/spec/features/search/user_searches_for_milestones_spec.rb @@ -31,10 +31,7 @@ RSpec.describe 'User searches for milestones', :js do context 'when on a project page' do it 'finds a milestone' do find('.js-search-project-dropdown').click - - page.within('.project-filter') do - click_link(project.full_name) - end + find('[data-testid="project-filter"]').click_link(project.full_name) fill_in('dashboard_search', with: milestone1.title) find('.btn-search').click diff --git a/spec/features/search/user_searches_for_wiki_pages_spec.rb b/spec/features/search/user_searches_for_wiki_pages_spec.rb index fc60b6244d9..6bf1407fd4f 100644 --- a/spec/features/search/user_searches_for_wiki_pages_spec.rb +++ b/spec/features/search/user_searches_for_wiki_pages_spec.rb @@ -19,10 +19,7 @@ RSpec.describe 'User searches for wiki pages', :js do shared_examples 'search wiki blobs' do it 'finds a page' do find('.js-search-project-dropdown').click - - page.within('.project-filter') do - click_link(project.full_name) - end + find('[data-testid="project-filter"]').click_link(project.full_name) fill_in('dashboard_search', with: search_term) find('.btn-search').click diff --git a/spec/features/search/user_uses_search_filters_spec.rb b/spec/features/search/user_uses_search_filters_spec.rb index 080cced21c3..a1afba1a371 100644 --- a/spec/features/search/user_uses_search_filters_spec.rb +++ b/spec/features/search/user_uses_search_filters_spec.rb @@ -28,7 +28,7 @@ RSpec.describe 'User uses search filters', :js do expect(find('.js-search-group-dropdown')).to have_content(group.name) - page.within('.project-filter') do + page.within('[data-testid="project-filter"]') do find('.js-search-project-dropdown').click wait_for_requests @@ -57,7 +57,7 @@ RSpec.describe 'User uses search filters', :js do it 'shows a project' do visit search_path - page.within('.project-filter') do + page.within('[data-testid="project-filter"]') do find('.js-search-project-dropdown').click wait_for_requests @@ -77,7 +77,7 @@ RSpec.describe 'User uses search filters', :js do describe 'clear filter button' do it 'removes Project filters' do - link = find('.project-filter .js-search-clear') + link = find('[data-testid="project-filter"] .js-search-clear') params = CGI.parse(URI.parse(link[:href]).query) expect(params).not_to include(:project_id) diff --git a/spec/frontend/vue_shared/components/members/table/role_dropdown_spec.js b/spec/frontend/vue_shared/components/members/table/role_dropdown_spec.js index 1e47953a510..f53aec84c06 100644 --- a/spec/frontend/vue_shared/components/members/table/role_dropdown_spec.js +++ b/spec/frontend/vue_shared/components/members/table/role_dropdown_spec.js @@ -30,6 +30,7 @@ describe('RoleDropdown', () => { wrapper = mount(RoleDropdown, { propsData: { member, + permissions: {}, ...propsData, }, localVue, diff --git a/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb index 9c9ca7411bf..88c889b4055 100644 --- a/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb +++ b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::Metrics::RequestsRackMiddleware do +RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do let(:app) { double('app') } subject { described_class.new(app) } @@ -21,20 +21,15 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware do allow(app).to receive(:call).and_return([200, nil, nil]) end - it 'increments requests count' do - expect(described_class).to receive_message_chain(:http_request_total, :increment).with(method: 'get', status: 200, feature_category: 'unknown') - - subject.call(env) - end - RSpec::Matchers.define :a_positive_execution_time do match { |actual| actual > 0 } end - it 'measures execution time' do + it 'tracks request count and duration' do + expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: '200', feature_category: 'unknown') expect(described_class).to receive_message_chain(:http_request_duration_seconds, :observe).with({ method: 'get' }, a_positive_execution_time) - Timecop.scale(3600) { subject.call(env) } + subject.call(env) end context 'request is a health check endpoint' do @@ -44,15 +39,10 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware do env['PATH_INFO'] = path end - it 'increments health endpoint counter rather than overall counter' do - expect(described_class).to receive_message_chain(:http_health_requests_total, :increment).with(method: 'get', status: 200) - expect(described_class).not_to receive(:http_request_total) - - subject.call(env) - end - - it 'does not record the request duration' do + it 'increments health endpoint counter rather than overall counter and does not record duration' do expect(described_class).not_to receive(:http_request_duration_seconds) + expect(described_class).not_to receive(:http_requests_total) + expect(described_class).to receive_message_chain(:http_health_requests_total, :increment).with(method: 'get', status: '200') subject.call(env) end @@ -67,14 +57,9 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware do env['PATH_INFO'] = path end - it 'increments overall counter rather than health endpoint counter' do - expect(described_class).to receive_message_chain(:http_request_total, :increment).with(method: 'get', status: 200, feature_category: 'unknown') + it 'increments regular counters and tracks duration' do + expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: '200', feature_category: 'unknown') expect(described_class).not_to receive(:http_health_requests_total) - - subject.call(env) - end - - it 'records the request duration' do expect(described_class) .to receive_message_chain(:http_request_duration_seconds, :observe) .with({ method: 'get' }, a_positive_execution_time) @@ -88,26 +73,18 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware do context '@app.call throws exception' do let(:http_request_duration_seconds) { double('http_request_duration_seconds') } + let(:http_requests_total) { double('http_requests_total') } before do allow(app).to receive(:call).and_raise(StandardError) allow(described_class).to receive(:http_request_duration_seconds).and_return(http_request_duration_seconds) + allow(described_class).to receive(:http_requests_total).and_return(http_requests_total) end - it 'increments exceptions count' do + it 'tracks the correct metrics' do expect(described_class).to receive_message_chain(:rack_uncaught_errors_count, :increment) - - expect { subject.call(env) }.to raise_error(StandardError) - end - - it 'increments requests count' do - expect(described_class).to receive_message_chain(:http_request_total, :increment).with(method: 'get', status: 'undefined', feature_category: 'unknown') - - expect { subject.call(env) }.to raise_error(StandardError) - end - - it "does't measure request execution time" do - expect(described_class.http_request_duration_seconds).not_to receive(:increment) + expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: 'undefined', feature_category: 'unknown') + expect(described_class.http_request_duration_seconds).not_to receive(:observe) expect { subject.call(env) }.to raise_error(StandardError) end @@ -119,8 +96,9 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware do allow(app).to receive(:call).and_return([200, { described_class::FEATURE_CATEGORY_HEADER => 'issue_tracking' }, nil]) end - it 'adds the feature category to the labels for http_request_total' do - expect(described_class).to receive_message_chain(:http_request_total, :increment).with(method: 'get', status: 200, feature_category: 'issue_tracking') + it 'adds the feature category to the labels for http_requests_total' do + expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: '200', feature_category: 'issue_tracking') + expect(described_class).not_to receive(:http_health_requests_total) subject.call(env) end @@ -128,8 +106,8 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware do it 'does not record a feature category for health check endpoints' do env['PATH_INFO'] = '/-/liveness' - expect(described_class).to receive_message_chain(:http_health_requests_total, :increment).with(method: 'get', status: 200) - expect(described_class).not_to receive(:http_request_total) + expect(described_class).to receive_message_chain(:http_health_requests_total, :increment).with(method: 'get', status: '200') + expect(described_class).not_to receive(:http_requests_total) subject.call(env) end @@ -141,21 +119,37 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware do end it 'sets the feature category to unknown' do - expect(described_class).to receive_message_chain(:http_request_total, :increment).with(method: 'get', status: 200, feature_category: 'unknown') + expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: '200', feature_category: 'unknown') + expect(described_class).not_to receive(:http_health_requests_total) subject.call(env) end end end - describe '.initialize_http_request_duration_seconds' do - it "sets labels" do + describe '.initialize_metrics', :prometheus do + it "sets labels for http_requests_total" do + feature_categories = YAML.load_file(Rails.root.join('config', 'feature_categories.yml')).map(&:strip) << described_class::FEATURE_CATEGORY_DEFAULT expected_labels = [] - described_class::HTTP_METHODS.each do |method| - expected_labels << { method: method } + + described_class::HTTP_METHODS.each do |method, statuses| + statuses.each do |status| + feature_categories.each do |feature_category| + expected_labels << { method: method.to_s, status: status.to_s, feature_category: feature_category.to_s } + end + end end - described_class.initialize_http_request_duration_seconds + described_class.initialize_metrics + + expect(described_class.http_requests_total.values.keys).to contain_exactly(*expected_labels) + end + + it 'sets labels for http_request_duration_seconds' do + expected_labels = described_class::HTTP_METHODS.keys.map { |method| { method: method } } + + described_class.initialize_metrics + expect(described_class.http_request_duration_seconds.values.keys).to include(*expected_labels) end end |