Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-06-16 12:12:12 +00:00
parent 5c93f783e8
commit d1d62516aa
30 changed files with 237 additions and 134 deletions

View File

@ -37,4 +37,5 @@ If you have questions about the patch release process, please:
[patch release runbook for engineers and maintainers]: https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/patch/engineers.md
[`#releases`]: https://gitlab.slack.com/archives/C0XM5UU6B
/label ~backport
/assign me

View File

@ -16,27 +16,6 @@ RSpec/ChangeByZero:
- 'ee/spec/requests/api/graphql/mutations/work_items/create_spec.rb'
- 'ee/spec/requests/api/graphql/mutations/work_items/linked_items/add_spec.rb'
- 'ee/spec/requests/api/merge_request_approvals_spec.rb'
- 'ee/spec/services/app_sec/dast/profiles/create_service_spec.rb'
- 'ee/spec/services/application_settings/update_service_spec.rb'
- 'ee/spec/services/approval_rules/user_rules_destroy_service_spec.rb'
- 'ee/spec/services/ci/delete_project_subscription_service_spec.rb'
- 'ee/spec/services/ci/minutes/update_project_and_namespace_usage_service_spec.rb'
- 'ee/spec/services/ee/ci/update_instance_variables_service_spec.rb'
- 'ee/spec/services/ee/issues/create_service_spec.rb'
- 'ee/spec/services/ee/members/destroy_service_spec.rb'
- 'ee/spec/services/geo/container_repository_registry_removal_service_spec.rb'
- 'ee/spec/services/geo/file_registry_removal_service_spec.rb'
- 'ee/spec/services/geo/metrics_update_service_spec.rb'
- 'ee/spec/services/gitlab_subscriptions/add_on_purchases/gitlab_com/provision_service_spec.rb'
- 'ee/spec/services/push_rules/create_or_update_service_spec.rb'
- 'ee/spec/services/registrations/import_namespace_create_service_spec.rb'
- 'ee/spec/services/registrations/standard_namespace_create_service_spec.rb'
- 'ee/spec/services/sbom/ingestion/tasks/ingest_sources_spec.rb'
- 'ee/spec/services/security/ingestion/tasks/ingest_cvs_security_scanners_spec.rb'
- 'ee/spec/services/security/orchestration/create_bot_service_spec.rb'
- 'ee/spec/services/vulnerabilities/manually_create_service_spec.rb'
- 'ee/spec/services/vulnerabilities/security_finding/create_merge_request_service_spec.rb'
- 'ee/spec/services/work_items/legacy_epics/related_epic_links/create_service_spec.rb'
- 'spec/controllers/admin/clusters_controller_spec.rb'
- 'spec/controllers/groups/boards_controller_spec.rb'
- 'spec/controllers/groups/clusters_controller_spec.rb'

View File

@ -1 +1 @@
55f4fb94e39f60e1d4c76deac5d6f20c6202c68f
3292fb4882db9208602f3cd2cc61ba56bce8e6a9

View File

@ -2,6 +2,7 @@
import { GlPopover, GlFormInputGroup } from '@gitlab/ui';
import { __ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import { helpPagePath } from '~/helpers/help_page_helper';
export default {
components: {
@ -9,13 +10,14 @@ export default {
GlFormInputGroup,
ClipboardButton,
},
inject: ['pushToCreateProjectCommand', 'projectHelpPath'],
inject: ['pushToCreateProjectCommand'],
props: {
target: {
type: [Function, HTMLElement],
required: true,
},
},
helpPath: helpPagePath('topics/git/project.md'),
i18n: {
clipboardButtonTitle: __('Copy command'),
commandInputAriaLabel: __('Push project from command line'),
@ -55,12 +57,9 @@ export default {
</gl-form-input-group>
</p>
<p>
<a
:href="`${projectHelpPath}#create-a-new-project-with-git-push`"
class="gl-text-sm"
target="_blank"
>{{ $options.i18n.helpLinkText }}</a
>
<a :href="$options.helpPath" class="gl-text-sm" target="_blank">{{
$options.i18n.helpLinkText
}}</a>
</p>
</gl-popover>
</template>

View File

@ -16,7 +16,6 @@ export function initNewProjectCreation() {
const {
pushToCreateProjectCommand,
projectHelpPath,
newProjectGuidelines,
hasErrors,
isCiCdAvailable,
@ -39,7 +38,6 @@ export function initNewProjectCreation() {
};
const provide = {
projectHelpPath,
pushToCreateProjectCommand,
};

View File

@ -2,6 +2,7 @@
import { GlFormGroup, GlLink, GlFormInputGroup } from '@gitlab/ui';
import SafeHtml from '~/vue_shared/directives/safe_html';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import { helpPagePath } from '~/helpers/help_page_helper';
export default {
components: {
@ -13,7 +14,8 @@ export default {
directives: {
SafeHtml,
},
inject: ['projectHelpPath', 'pushToCreateProjectCommand'],
helpPath: helpPagePath('topics/git/project.md'),
inject: ['pushToCreateProjectCommand'],
};
</script>
@ -30,7 +32,7 @@ export default {
data-testid="new-project-with-command"
>
<template #label-description>
<gl-link :href="`${projectHelpPath}#create-a-new-project-with-git-push`" target="_blank">
<gl-link :href="$options.helpPath" target="_blank">
{{ s__('ProjectNew|What does this command do?') }}
</gl-link>
</template>

View File

@ -22,7 +22,6 @@ export function initNewProjectForm() {
projectsUrl,
parentGroupUrl,
parentGroupName,
projectHelpPath,
isCiCdAvailable,
canImportProjects,
importSourcesEnabled,
@ -64,7 +63,6 @@ export function initNewProjectForm() {
projectsUrl,
parentGroupUrl,
parentGroupName,
projectHelpPath,
trackLabel,
isCiCdAvailable: parseBoolean(isCiCdAvailable),
importSourcesEnabled: parseBoolean(importSourcesEnabled),

View File

@ -23,9 +23,7 @@ export const formatListBoxItems = ({ branches, tags, commits, selectedRef }) =>
const addToFinalResult = (items, header, shouldFilter = true) => {
if (!items) return;
const filteredItems =
shouldFilter && selectedRef
? items.filter((item) => item.value !== selectedRef.value)
: items; // Filter out the selected
shouldFilter && selectedRef ? items.filter((item) => item.name !== selectedRef.name) : items; // Filter out the selected
if (!filteredItems?.length) return;
listBoxItems.push({

View File

@ -98,8 +98,7 @@ module Ci
next unless result
if result.valid?
@metrics.register_success(result.build_presented)
@metrics.observe_queue_depth(:found, depth)
track_success(result, depth)
return result # rubocop:disable Cop/AvoidReturnFromBlocks
else
@ -109,10 +108,7 @@ module Ci
end
end
@metrics.increment_queue_operation(:queue_conflict) unless valid
@metrics.observe_queue_depth(:conflict, depth) unless valid
@metrics.observe_queue_depth(:not_found, depth) if valid
@metrics.register_failure
track_conflict(depth, valid)
Result.new(nil, nil, nil, valid)
end
@ -308,14 +304,40 @@ module Ci
track_exception_for_build(ex, build)
end
def track_success(result, depth)
@metrics.register_success(result.build_presented)
@metrics.observe_queue_depth(:found, depth)
rescue StandardError => ex
# We should know about any errors during tracking but we don't want them to prevent
# reporting a result to the runner
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(
ex, **build_tracking_data(result.build)
)
end
def track_conflict(depth, valid)
@metrics.increment_queue_operation(:queue_conflict) unless valid
@metrics.observe_queue_depth(:conflict, depth) unless valid
@metrics.observe_queue_depth(:not_found, depth) if valid
@metrics.register_failure
rescue StandardError => ex
# We should know about any errors during tracking but we don't want them to prevent
# reporting a result to the runner
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(ex)
end
def track_exception_for_build(ex, build)
Gitlab::ErrorTracking.track_exception(ex,
Gitlab::ErrorTracking.track_exception(ex, **build_tracking_data(build))
end
def build_tracking_data(build)
{
build_id: build.id,
build_name: build.name,
build_stage: build.stage_name,
pipeline_id: build.pipeline_id,
project_id: build.project_id
)
}
end
def pre_assign_runner_checks

View File

@ -0,0 +1,12 @@
= content_for :meta_tags do
%meta{ 'http-equiv': 'refresh', content: "3; url=#{@redirect_url}" }
.gl-text-center.gl-max-w-62.gl-mx-auto{ data: local_assigns.fetch(:data, {}) }
.svg-content.svg-80
= image_tag 'illustrations/success-sm.svg'
%h2
= s_('IdentityVerification|Verification successful')
%p.gl-pt-2
- redirect_url_start = '<a href="%{url}">'.html_safe % { url: @redirect_url }
- redirect_url_end = '</a>'.html_safe
= html_escape(s_("IdentityVerification|Your account has been successfully verified. You'll be redirected to your account in just a moment. You can also %{redirect_url_start}refresh the page%{redirect_url_end}.")) % { redirect_url_start: redirect_url_start, redirect_url_end: redirect_url_end }

View File

@ -0,0 +1,13 @@
- @html_class = 'gl-dark'
= content_for :meta_tags do
%meta{ 'http-equiv': 'refresh', content: "3; url=#{@redirect_url}" }
.gl-text-center.gl-max-w-xl.gl-mx-auto.gl-bg-subtle.gl-rounded-lg.gl-py-4{ data: local_assigns.fetch(:data, {}) }
.svg-content.svg-80
= image_tag 'illustrations/secure-sm.svg'
%h2
= s_('IdentityVerification|Verification successful')
%p.gl-pt-2.gl-px-6
- redirect_url_start = '<a href="%{url}">'.html_safe % { url: @redirect_url }
- redirect_url_end = '</a>'.html_safe
= html_escape(s_("IdentityVerification|Your account has been successfully verified. You'll be redirected to your account in just a moment. You can also %{redirect_url_start}refresh the page%{redirect_url_end}.")) % { redirect_url_start: redirect_url_start, redirect_url_end: redirect_url_end }

View File

@ -1,12 +1,8 @@
= content_for :meta_tags do
%meta{ 'http-equiv': 'refresh', content: "3; url=#{@redirect_url}" }
.gl-text-center.gl-max-w-62.gl-mx-auto{ data: local_assigns.fetch(:data, {}) }
.svg-content.svg-80
= image_tag 'illustrations/success-sm.svg'
%h2
= s_('IdentityVerification|Verification successful')
%p.gl-pt-2
- redirect_url_start = '<a href="%{url}"">'.html_safe % { url: @redirect_url }
- redirect_url_end = '</a>'.html_safe
= html_escape(s_("IdentityVerification|Your account has been successfully verified. You'll be redirected to your account in just a moment. You can also %{redirect_url_start}refresh the page%{redirect_url_end}.")) % { redirect_url_start: redirect_url_start, redirect_url_end: redirect_url_end }
- if current_user.try(:onboarding_status_initial_registration_type) != "trial"
= render 'devise/sessions/successful_verification', local_assigns
- else
- experiment(:lightweight_trial_registration_redesign, actor: current_user) do |e|
- e.control do
= render 'devise/sessions/successful_verification', local_assigns
- e.candidate do
= render 'devise/sessions/successful_verification_lightweight', local_assigns

View File

@ -17,7 +17,6 @@
projects_url: dashboard_projects_url,
parent_group_url: @project.parent && group_url(@project.parent),
parent_group_name: @project.parent&.name,
project_help_path: help_page_path("user/project/_index.md"),
is_ci_cd_available: remote_mirror_setting_enabled?.to_s,
can_import_projects: params[:namespace_id].presence ? current_user.can?(:import_projects, @namespace).to_s : 'true',
import_sources_enabled: import_sources_enabled?.to_s,
@ -56,7 +55,6 @@
has_errors: @project.errors.any?.to_s,
new_project_guidelines: brand_new_project_guidelines,
push_to_create_project_command: push_to_create_project_command,
project_help_path: help_page_path("user/project/_index.md"),
root_path: root_path,
parent_group_url: @project.parent && group_url(@project.parent),
parent_group_name: @project.parent&.name,

View File

@ -28,17 +28,24 @@ module Gitlab
feature_category :continuous_integration
urgency :throttled
idempotent!
deduplicate :until_executed
deduplicate :until_executing
sidekiq_options retry: true
def perform_work
return unless ::Gitlab.com_except_jh? # rubocop:disable Gitlab/AvoidGitlabInstanceChecks -- we need to check on which instance this happens
ID_RANGES.each do |model, attributes|
min_id = start_id(model)
last_id = flush_stale_for_model(model, min_id, attributes[:end_id])
# we need to add +1 here, because otherwise, we'd process the last record twice.
update_start_id(model, last_id + 1)
# Process up to 100 batches per job execution as a compromise between
# performance (avoiding too many small jobs) and efficiency (preventing
# jobs from running too long and potentially timing out)
100.times do
ID_RANGES.each do |model, attributes|
min_id = start_id(model)
remaining_work = [(attributes[:end_id] - min_id), 0].max
last_id = flush_stale_for_model(model, min_id, attributes[:end_id])
# we need to add +1 here, because otherwise, we'd process the last record twice.
update_start_id(model, last_id + 1)
return if remaining_work == 0 # rubocop:disable Lint/NonLocalExitFromIterator -- return if we don't have anymore work to do
end
end
end

View File

@ -7,4 +7,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/538388
milestone: '18.0'
group: group::authentication
type: beta
default_enabled: false
default_enabled: true

View File

@ -173,7 +173,9 @@ Doorkeeper.configure do
expires_in: configuration.device_code_expires_in,
scopes: scopes.to_s,
user_code: generate_user_code,
organization_id: Organizations::Organization::DEFAULT_ORGANIZATION_ID
# This will result in a fallback to Default Organizzation
# OAuth device grant flow doesn't support other organizations yet
organization_id: Gitlab::Current::Organization.new.organization.id
}
end
end

View File

@ -1,25 +0,0 @@
# frozen_string_literal: true
module Gitlab
module Tracking
class AiTracking
def self.track_event(*args, **kwargs)
new.track_event(*args, **kwargs)
end
def self.track_user_activity(*args)
new.track_user_activity(*args)
end
def track_event(_event_name, **_context_hash)
# no-op for CE
end
def track_user_activity(_user)
# no-op for CE
end
end
end
end
Gitlab::Tracking::AiTracking.prepend_mod

View File

@ -8,7 +8,6 @@ describe('New project push tip popover', () => {
let wrapper;
const targetId = 'target';
const pushToCreateProjectCommand = 'command';
const projectHelpPath = 'path';
const findPopover = () => wrapper.findComponent(GlPopover);
const findClipboardButton = () => wrapper.findComponent(ClipboardButton);
@ -27,7 +26,6 @@ describe('New project push tip popover', () => {
},
provide: {
pushToCreateProjectCommand,
projectHelpPath,
},
});
};
@ -74,8 +72,6 @@ describe('New project push tip popover', () => {
});
it('displays a link to open the push command help page reference', () => {
expect(findHelpLink().attributes().href).toBe(
`${projectHelpPath}#create-a-new-project-with-git-push`,
);
expect(findHelpLink().attributes().href).toBe('/help/topics/git/project.md');
});
});

View File

@ -13,9 +13,9 @@ import {
describe('formatListBoxItems', () => {
const FORMATTED_SELECTED = {
text: DEFAULT_I18N.selected,
options: [{ text: 'main', value: 'main', default: undefined, protected: undefined }],
options: [{ text: 'selected', value: 'selected', default: undefined, protected: undefined }],
};
const selected = { name: 'main', value: 'main' };
const selected = { name: 'selected', value: 'selected' };
it.each`
branches | tags | commits | selectedRef | expectedResult

View File

@ -144,6 +144,12 @@ RSpec.describe Gitlab::Tracking::EventDefinition, feature_category: :service_pin
end
describe '.extra_trackers' do
let(:dummy_tracking_class) { Class.new }
before do
stub_const('Gitlab::Tracking::DummyTracking', dummy_tracking_class)
end
it 'returns an empty hash when no extra tracking classes are set' do
expect(described_class.new(nil, {}).extra_trackers).to eq([])
end
@ -153,7 +159,7 @@ RSpec.describe Gitlab::Tracking::EventDefinition, feature_category: :service_pin
extra_trackers:
[
{
tracking_class: 'Gitlab::Tracking::AiTracking',
tracking_class: 'Gitlab::Tracking::DummyTracking',
protected_properties: { prop: { description: 'description' } }
}
]
@ -161,7 +167,7 @@ RSpec.describe Gitlab::Tracking::EventDefinition, feature_category: :service_pin
config = attributes.merge(extra_trackers)
expect(described_class.new(nil, config).extra_trackers)
.to eq({ Gitlab::Tracking::AiTracking => { protected_properties: [:prop] } })
.to eq({ dummy_tracking_class => { protected_properties: [:prop] } })
end
it 'returns the hash with extra tracking class with empty array when props are not set' do
@ -169,14 +175,14 @@ RSpec.describe Gitlab::Tracking::EventDefinition, feature_category: :service_pin
extra_trackers:
[
{
tracking_class: 'Gitlab::Tracking::AiTracking'
tracking_class: 'Gitlab::Tracking::DummyTracking'
}
]
}
config = attributes.merge(extra_trackers)
expect(described_class.new(nil, config).extra_trackers)
.to eq({ Gitlab::Tracking::AiTracking => { protected_properties: [] } })
.to eq({ dummy_tracking_class => { protected_properties: [] } })
end
end

View File

@ -13,8 +13,10 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
let_it_be(:project, reload: true) { create(:project) }
let_it_be(:personal_access_token) { create(:personal_access_token) }
let_it_be(:job) { create(:ci_build, :running, user: personal_access_token.user, project: project) }
let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) }
let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) }
let_it_be(:deploy_token) do
create(:deploy_token, read_package_registry: true, write_package_registry: true, projects: [project])
end
let_it_be(:another_project, reload: true) { create(:project) }
let_it_be(:model) { create(:ml_models, user: project.owner, project: project) }
let_it_be(:model_version) { create(:ml_model_versions, :with_package, model: model, version: '0.1.0') }

View File

@ -5,7 +5,11 @@ require 'spec_helper'
RSpec.describe 'OAuth tokens', feature_category: :system_access do
include HttpBasicAuthHelpers
let_it_be(:organization) { create(:organization, :default) }
let_it_be(:organization) { create(:organization) }
before do
stub_current_organization(organization)
end
context 'Resource Owner Password Credentials' do
def request_oauth_token(user, headers = {}, password = user.password)

View File

@ -2,8 +2,8 @@
require 'spec_helper'
RSpec.describe 'Gitlab OAuth2 Device Authorization Grant', feature_category: :system_access do
let_it_be(:organization) { create(:organization, :default) }
RSpec.describe 'Gitlab OAuth2 Device Authorization Grant', :with_current_organization, feature_category: :system_access do
let_it_be(:organization) { current_organization }
let_it_be(:application) { create(:oauth_application, redirect_uri: 'urn:ietf:wg:oauth:2.0:oob', confidential: false) }
let_it_be(:user) { create(:user, :with_namespace, organizations: [organization]) }
let_it_be(:client_id) { application.uid }

View File

@ -9,8 +9,8 @@ module Ci
let_it_be_with_reload(:pipeline) { create(:ci_pipeline, project: project) }
let_it_be(:shared_runner) { create(:ci_runner, :instance) }
let!(:project_runner) { create(:ci_runner, :project, projects: [project]) }
let!(:group_runner) { create(:ci_runner, :group, groups: [group]) }
let_it_be_with_reload(:project_runner) { create(:ci_runner, :project, projects: [project]) }
let_it_be_with_reload(:group_runner) { create(:ci_runner, :group, groups: [group]) }
let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
describe '#execute' do
@ -463,9 +463,11 @@ module Ci
context 'when first build is stalled' do
before do
allow_any_instance_of(described_class).to receive(:assign_runner!).and_call_original
allow_any_instance_of(described_class).to receive(:assign_runner!)
.with(pending_job, anything).and_raise(ActiveRecord::StaleObjectError)
allow_next_instance_of(described_class) do |instance|
allow(instance).to receive(:assign_runner!).and_call_original
allow(instance).to receive(:assign_runner!)
.with(pending_job, anything).and_raise(ActiveRecord::StaleObjectError)
end
end
subject { described_class.new(project_runner, nil).execute }
@ -474,9 +476,10 @@ module Ci
let!(:other_build) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
before do
allow_any_instance_of(::Ci::Queue::BuildQueueService)
.to receive(:execute)
.and_return(Ci::Build.where(id: [pending_job, other_build]).pluck(:id, :partition_id))
allow_next_instance_of(::Ci::Queue::BuildQueueService) do |instance|
allow(instance).to receive(:execute)
.and_return(Ci::Build.where(id: [pending_job, other_build]).pluck(:id, :partition_id))
end
end
it "receives second build from the queue" do
@ -487,9 +490,10 @@ module Ci
context 'when single build is in queue' do
before do
allow_any_instance_of(::Ci::Queue::BuildQueueService)
.to receive(:execute)
.and_return(Ci::Build.where(id: pending_job).pluck(:id, :partition_id))
allow_next_instance_of(::Ci::Queue::BuildQueueService) do |instance|
allow(instance).to receive(:execute)
.and_return(Ci::Build.where(id: pending_job).pluck(:id, :partition_id))
end
end
it "does not receive any valid result" do
@ -499,9 +503,10 @@ module Ci
context 'when there is no build in queue' do
before do
allow_any_instance_of(::Ci::Queue::BuildQueueService)
.to receive(:execute)
.and_return([])
allow_next_instance_of(::Ci::Queue::BuildQueueService) do |instance|
allow(instance).to receive(:execute)
.and_return([])
end
end
it "does not receive builds but result is valid" do
@ -893,6 +898,66 @@ module Ci
end
end
end
context 'when metrics recording fails' do
before do
project.update!(shared_runners_enabled: true)
pending_job.reload.create_queuing_entry!
allow_next_instance_of(::Gitlab::Ci::Queue::Metrics) do |metric|
allow(metric).to receive(:register_success).and_raise(StandardError, 'metrics failure')
end
end
it 'continues assignment when register_success fails' do
expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
.with(
instance_of(StandardError),
hash_including(
build_id: pending_job.id,
build_name: pending_job.name,
build_stage: pending_job.stage_name,
pipeline_id: pending_job.pipeline_id,
project_id: pending_job.project_id
)
)
result = described_class.new(shared_runner, nil).execute
expect(result).to be_valid
expect(result.build).to eq(pending_job)
expect(result.build_json).to be_present
expect(pending_job.reload).to be_running
end
it 'continues assignment when observe_queue_depth fails' do
allow_next_instance_of(::Gitlab::Ci::Queue::Metrics) do |metric|
allow(metric).to receive(:register_success).and_call_original
allow(metric).to receive(:observe_queue_depth)
.with(:found, anything)
.and_raise(StandardError, 'queue depth metrics failure')
end
expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
.with(
instance_of(StandardError),
hash_including(
build_id: pending_job.id,
build_name: pending_job.name,
build_stage: pending_job.stage_name,
pipeline_id: pending_job.pipeline_id,
project_id: pending_job.project_id
)
)
result = described_class.new(shared_runner, nil).execute
expect(result).to be_valid
expect(result.build).to eq(pending_job)
expect(result.build_json).to be_present
expect(pending_job.reload).to be_running
end
end
end
context 'when using pending builds table' do
@ -1137,6 +1202,25 @@ module Ci
expect(result.build).to be_nil
expect(result.build_json).to be_nil
end
context 'when metrics tracking raises an error during conflict' do
let(:error) { StandardError.new('Metrics tracking failed') }
before do
allow_next_instance_of(Gitlab::Ci::Queue::Metrics) do |metric|
allow(metric).to receive(:observe_queue_depth).with(:conflict, any_args).and_raise(error)
end
end
it 'tracks and raises the exception for dev without affecting the result' do
expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception).with(error)
result = described_class.new(project_runner, nil).execute
expect(result).not_to be_valid
expect(result.build).to be_nil
end
end
end
def build_on(runner, runner_manager: nil, params: {})

View File

@ -224,6 +224,7 @@ RSpec.configure do |config|
config.include_context 'when rendered has no HTML escapes', type: :view
config.include_context 'with STI disabled', type: :model
include StubCurrentOrganization
include StubFeatureFlags
include StubSnowplow
include StubMember

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
module StubCurrentOrganization
def stub_current_organization(organization)
allow_next_instance_of(Gitlab::Current::Organization) do |instance|
allow(instance).to receive(:organization).and_return(organization)
end
end
end

View File

@ -8,9 +8,7 @@ RSpec.shared_context 'with current_organization setting' do
end
before do
allow_next_instance_of(::Gitlab::Current::Organization) do |organization|
allow(organization).to receive(:organization).and_return(current_organization)
end
stub_current_organization(current_organization)
end
end

View File

@ -8,11 +8,7 @@ RSpec.shared_context 'with conan api setup' do
let_it_be(:user) { create(:user, developer_of: [project]) }
let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
let_it_be(:deploy_token) do
create(:deploy_token, read_package_registry: true, write_package_registry: true)
end
let_it_be(:project_deploy_token, freeze: true) do
create(:project_deploy_token, deploy_token: deploy_token, project: project)
create(:deploy_token, read_package_registry: true, write_package_registry: true, projects: [project])
end
let_it_be(:job, freeze: true) { create(:ci_build, :running, user: user, project: project) }

View File

@ -9,7 +9,6 @@ RSpec.describe ::Gitlab::Counters::FlushStaleCounterIncrementsCronWorker, featur
context 'when we are on gitlab.com' do
before do
allow(Gitlab).to receive(:com_except_jh?).and_return(true)
allow(::Gitlab::Counters::FlushStaleCounterIncrementsWorker).to receive(:perform_with_capacity)
end
it 'calls FlushStaleCounterIncrementsWorker.perform_with_capacity' do

View File

@ -78,18 +78,26 @@ RSpec.describe ::Gitlab::Counters::FlushStaleCounterIncrementsWorker, :saas, :cl
before do
Gitlab::Redis::SharedState.with do |redis|
redis.set(redis_key, project_daily_statistic.id)
stub_const("#{described_class}::ID_RANGES", { ProjectDailyStatistic => {
end_id: project_daily_statistic_three.id
} })
stub_const("#{described_class}::BATCH_LIMIT", 1)
end
allow(Gitlab::Saas).to receive(:feature_available?).with(:purchases_additional_minutes).and_return(true)
end
it "flushes stale counters and updates the redis start id" do
Gitlab::Redis::SharedState.with do |redis|
expect(redis.get(redis_key).to_i).to eq(project_daily_statistic.id)
end
# Test that the service is called multiple times (batching behavior)
# without running the full 100 iterations, and verify early termination
# when no more work is available
expect_next_instance_of(Gitlab::Counters::FlushStaleCounterIncrements) do |service|
expect(service).to receive(:execute)
.and_call_original
end
end.thrice
expect_initial_counts
worker.perform_work