Skip to content
Snippets Groups Projects
Commit 6f0f893b authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 8b1228b0
No related branches found
No related tags found
No related merge requests found
Showing
with 660 additions and 78 deletions
Loading
Loading
@@ -133,6 +133,7 @@ describe 'Database schema' do
'Ci::BuildTraceChunk' => %w[data_store],
'Ci::JobArtifact' => %w[file_type],
'Ci::Pipeline' => %w[source config_source failure_reason],
'Ci::Processable' => %w[failure_reason],
'Ci::Runner' => %w[access_level],
'Ci::Stage' => %w[status],
'Clusters::Applications::Ingress' => %w[ingress_type],
Loading
Loading
Loading
Loading
@@ -34,6 +34,7 @@ FactoryBot.define do
last_release_last_commit { '9ad419c86' }
first_release_short_version { 'abc123' }
last_release_short_version { 'abc123' }
first_release_version { '123456' }
 
skip_create
end
Loading
Loading
Loading
Loading
@@ -20,5 +20,14 @@ FactoryBot.define do
create(:evidence, release: release)
end
end
trait :with_milestones do
transient do
milestones_count { 2 }
end
after(:create) do |release, evaluator|
create_list(:milestone, evaluator.milestones_count, project: evaluator.project, releases: [release])
end
end
end
end
{
"type": "object",
"required" : [
"required": [
"external_url",
"external_base_url",
"last_seen",
Loading
Loading
@@ -18,9 +18,10 @@
"first_release_last_commit",
"last_release_last_commit",
"first_release_short_version",
"last_release_short_version"
"last_release_short_version",
"gitlab_commit"
],
"properties" : {
"properties": {
"id": { "type": "string" },
"first_seen": { "type": "string", "format": "date-time" },
"last_seen": { "type": "string", "format": "date-time" },
Loading
Loading
@@ -30,13 +31,10 @@
"count": { "type": "integer" },
"external_url": { "type": "string" },
"external_base_url": { "type": "string" },
"user_count": { "type": "integer"},
"user_count": { "type": "integer" },
"tags": {
"type": "object",
"required" : [
"level",
"logger"
],
"required": ["level", "logger"],
"properties": {
"level": {
"type": "string"
Loading
Loading
@@ -57,7 +55,8 @@
"first_release_last_commit": { "type": ["string", "null"] },
"last_release_last_commit": { "type": ["string", "null"] },
"first_release_short_version": { "type": ["string", "null"] },
"last_release_short_version": { "type": ["string", "null"] }
"last_release_short_version": { "type": ["string", "null"] },
"gitlab_commit": { "type": ["string", "null"] }
},
"additionalProperties": false
}
Loading
Loading
@@ -80,6 +80,17 @@
"issue_id": 40
}
],
"award_emoji": [
{
"id": 1,
"name": "musical_keyboard",
"user_id": 1,
"awardable_type": "Issue",
"awardable_id": 40,
"created_at": "2020-01-07T11:55:22.234Z",
"updated_at": "2020-01-07T11:55:22.234Z"
}
],
"zoom_meetings": [
{
"id": 1,
Loading
Loading
@@ -188,7 +199,18 @@
"author": {
"name": "User 4"
},
"events": []
"events": [],
"award_emoji": [
{
"id": 1,
"name": "clapper",
"user_id": 1,
"awardable_type": "Note",
"awardable_id": 351,
"created_at": "2020-01-07T11:55:22.234Z",
"updated_at": "2020-01-07T11:55:22.234Z"
}
]
},
{
"id": 352,
Loading
Loading
@@ -2297,7 +2319,32 @@
"updated_at": "2019-11-05T15:37:24.645Z"
}
],
"notes": []
"notes": [
{
"id": 872,
"note": "This is a test note",
"noteable_type": "Snippet",
"author_id": 1,
"created_at": "2019-11-05T15:37:24.645Z",
"updated_at": "2019-11-05T15:37:24.645Z",
"noteable_id": 1,
"author": {
"name": "Random name"
},
"events": [],
"award_emoji": [
{
"id": 12,
"name": "thumbsup",
"user_id": 1,
"awardable_type": "Note",
"awardable_id": 872,
"created_at": "2019-11-05T15:37:21.287Z",
"updated_at": "2019-11-05T15:37:21.287Z"
}
]
}
]
}
],
"releases": [],
Loading
Loading
@@ -2434,7 +2481,18 @@
"author": {
"name": "User 4"
},
"events": []
"events": [],
"award_emoji": [
{
"id": 1,
"name": "tada",
"user_id": 1,
"awardable_type": "Note",
"awardable_id": 1,
"created_at": "2019-11-05T15:37:21.287Z",
"updated_at": "2019-11-05T15:37:21.287Z"
}
]
},
{
"id": 672,
Loading
Loading
@@ -2840,7 +2898,27 @@
"author_id": 1
}
],
"approvals_before_merge": 1
"approvals_before_merge": 1,
"award_emoji": [
{
"id": 1,
"name": "thumbsup",
"user_id": 1,
"awardable_type": "MergeRequest",
"awardable_id": 27,
"created_at": "2020-01-07T11:21:21.235Z",
"updated_at": "2020-01-07T11:21:21.235Z"
},
{
"id": 2,
"name": "drum",
"user_id": 1,
"awardable_type": "MergeRequest",
"awardable_id": 27,
"created_at": "2020-01-07T11:21:21.235Z",
"updated_at": "2020-01-07T11:21:21.235Z"
}
]
},
{
"id": 26,
Loading
Loading
Loading
Loading
@@ -6,7 +6,13 @@ import axios from '~/lib/utils/axios_utils';
import Issue from '~/issue';
import '~/lib/utils/text_utility';
 
describe('Issue', function() {
describe('Issue', () => {
let testContext;
beforeEach(() => {
testContext = {};
});
let $boxClosed, $boxOpen, $btn;
 
preloadFixtures('issues/closed-issue.html');
Loading
Loading
@@ -80,10 +86,18 @@ describe('Issue', function() {
}
 
[true, false].forEach(isIssueInitiallyOpen => {
describe(`with ${isIssueInitiallyOpen ? 'open' : 'closed'} issue`, function() {
describe(`with ${isIssueInitiallyOpen ? 'open' : 'closed'} issue`, () => {
const action = isIssueInitiallyOpen ? 'close' : 'reopen';
let mock;
 
function setup() {
testContext.issue = new Issue();
expectIssueState(isIssueInitiallyOpen);
testContext.$projectIssuesCounter = $('.issue_counter').first();
testContext.$projectIssuesCounter.text('1,001');
}
function mockCloseButtonResponseSuccess(url, response) {
mock.onPut(url).reply(() => {
expectNewBranchButtonState(true, false);
Loading
Loading
@@ -103,7 +117,7 @@ describe('Issue', function() {
});
}
 
beforeEach(function() {
beforeEach(() => {
if (isIssueInitiallyOpen) {
loadFixtures('issues/open-issue.html');
} else {
Loading
Loading
@@ -111,19 +125,11 @@ describe('Issue', function() {
}
 
mock = new MockAdapter(axios);
mock.onGet(/(.*)\/related_branches$/).reply(200, {});
jest.spyOn(axios, 'get');
 
findElements(isIssueInitiallyOpen);
this.issue = new Issue();
expectIssueState(isIssueInitiallyOpen);
this.$triggeredButton = $btn;
this.$projectIssuesCounter = $('.issue_counter').first();
this.$projectIssuesCounter.text('1,001');
spyOn(axios, 'get').and.callThrough();
testContext.$triggeredButton = $btn;
});
 
afterEach(() => {
Loading
Loading
@@ -131,82 +137,90 @@ describe('Issue', function() {
$('div.flash-alert').remove();
});
 
it(`${action}s the issue`, function(done) {
mockCloseButtonResponseSuccess(this.$triggeredButton.attr('href'), {
it(`${action}s the issue`, done => {
mockCloseButtonResponseSuccess(testContext.$triggeredButton.attr('href'), {
id: 34,
});
mockCanCreateBranch(!isIssueInitiallyOpen);
 
this.$triggeredButton.trigger('click');
setup();
testContext.$triggeredButton.trigger('click');
 
setTimeout(() => {
setImmediate(() => {
expectIssueState(!isIssueInitiallyOpen);
 
expect(this.$triggeredButton.get(0).getAttribute('disabled')).toBeNull();
expect(this.$projectIssuesCounter.text()).toBe(isIssueInitiallyOpen ? '1,000' : '1,002');
expect(testContext.$triggeredButton.get(0).getAttribute('disabled')).toBeNull();
expect(testContext.$projectIssuesCounter.text()).toBe(
isIssueInitiallyOpen ? '1,000' : '1,002',
);
expectNewBranchButtonState(false, !isIssueInitiallyOpen);
 
done();
});
});
 
it(`fails to ${action} the issue if saved:false`, function(done) {
mockCloseButtonResponseSuccess(this.$triggeredButton.attr('href'), {
it(`fails to ${action} the issue if saved:false`, done => {
mockCloseButtonResponseSuccess(testContext.$triggeredButton.attr('href'), {
saved: false,
});
mockCanCreateBranch(isIssueInitiallyOpen);
 
this.$triggeredButton.trigger('click');
setup();
testContext.$triggeredButton.trigger('click');
 
setTimeout(() => {
setImmediate(() => {
expectIssueState(isIssueInitiallyOpen);
 
expect(this.$triggeredButton.get(0).getAttribute('disabled')).toBeNull();
expect(testContext.$triggeredButton.get(0).getAttribute('disabled')).toBeNull();
expectErrorMessage();
 
expect(this.$projectIssuesCounter.text()).toBe('1,001');
expect(testContext.$projectIssuesCounter.text()).toBe('1,001');
expectNewBranchButtonState(false, isIssueInitiallyOpen);
 
done();
});
});
 
it(`fails to ${action} the issue if HTTP error occurs`, function(done) {
mockCloseButtonResponseError(this.$triggeredButton.attr('href'));
it(`fails to ${action} the issue if HTTP error occurs`, done => {
mockCloseButtonResponseError(testContext.$triggeredButton.attr('href'));
mockCanCreateBranch(isIssueInitiallyOpen);
 
this.$triggeredButton.trigger('click');
setup();
testContext.$triggeredButton.trigger('click');
 
setTimeout(() => {
setImmediate(() => {
expectIssueState(isIssueInitiallyOpen);
 
expect(this.$triggeredButton.get(0).getAttribute('disabled')).toBeNull();
expect(testContext.$triggeredButton.get(0).getAttribute('disabled')).toBeNull();
expectErrorMessage();
 
expect(this.$projectIssuesCounter.text()).toBe('1,001');
expect(testContext.$projectIssuesCounter.text()).toBe('1,001');
expectNewBranchButtonState(false, isIssueInitiallyOpen);
 
done();
});
});
 
it('disables the new branch button if Ajax call fails', function() {
mockCloseButtonResponseError(this.$triggeredButton.attr('href'));
it('disables the new branch button if Ajax call fails', () => {
mockCloseButtonResponseError(testContext.$triggeredButton.attr('href'));
mock.onGet(/(.*)\/can_create_branch$/).networkError();
 
this.$triggeredButton.trigger('click');
setup();
testContext.$triggeredButton.trigger('click');
 
expectNewBranchButtonState(false, false);
});
 
it('does not trigger Ajax call if new branch button is missing', function(done) {
mockCloseButtonResponseError(this.$triggeredButton.attr('href'));
Issue.$btnNewBranch = $();
this.canCreateBranchDeferred = null;
it('does not trigger Ajax call if new branch button is missing', done => {
mockCloseButtonResponseError(testContext.$triggeredButton.attr('href'));
document.querySelector('#related-branches').remove();
document.querySelector('.create-mr-dropdown-wrap').remove();
 
this.$triggeredButton.trigger('click');
setup();
testContext.$triggeredButton.trigger('click');
 
setTimeout(() => {
setImmediate(() => {
expect(axios.get).not.toHaveBeenCalled();
 
done();
Loading
Loading
import AxiosMockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import pollUntilComplete from '~/lib/utils/poll_until_complete';
import httpStatusCodes from '~/lib/utils/http_status';
import { TEST_HOST } from 'helpers/test_constants';
const endpoint = `${TEST_HOST}/foo`;
const mockData = 'mockData';
const pollInterval = 1234;
const pollIntervalHeader = {
'Poll-Interval': pollInterval,
};
describe('pollUntilComplete', () => {
let mock;
beforeEach(() => {
mock = new AxiosMockAdapter(axios);
});
afterEach(() => {
mock.restore();
});
describe('given an immediate success response', () => {
beforeEach(() => {
mock.onGet(endpoint).replyOnce(httpStatusCodes.OK, mockData);
});
it('resolves with the response', () =>
pollUntilComplete(endpoint).then(({ data }) => {
expect(data).toBe(mockData);
}));
});
describe(`given the endpoint returns NO_CONTENT with a Poll-Interval before succeeding`, () => {
beforeEach(() => {
mock
.onGet(endpoint)
.replyOnce(httpStatusCodes.NO_CONTENT, undefined, pollIntervalHeader)
.onGet(endpoint)
.replyOnce(httpStatusCodes.OK, mockData);
});
it('calls the endpoint until it succeeds, and resolves with the response', () =>
Promise.all([
pollUntilComplete(endpoint).then(({ data }) => {
expect(data).toBe(mockData);
expect(mock.history.get).toHaveLength(2);
}),
// To ensure the above pollUntilComplete() promise is actually
// fulfilled, we must explictly run the timers forward by the time
// indicated in the headers *after* each previous request has been
// fulfilled.
axios
// wait for initial NO_CONTENT response to be fulfilled
.waitForAll()
.then(() => {
jest.advanceTimersByTime(pollInterval);
}),
]));
});
describe('given the endpoint returns an error status', () => {
const errorMessage = 'error message';
beforeEach(() => {
mock.onGet(endpoint).replyOnce(httpStatusCodes.NOT_FOUND, errorMessage);
});
it('rejects with the error response', () =>
pollUntilComplete(endpoint).catch(error => {
expect(error.response.data).toBe(errorMessage);
}));
});
describe('given params', () => {
const params = { foo: 'bar' };
beforeEach(() => {
mock.onGet(endpoint, { params }).replyOnce(httpStatusCodes.OK, mockData);
});
it('requests the expected URL', () =>
pollUntilComplete(endpoint, { params }).then(({ data }) => {
expect(data).toBe(mockData);
}));
});
});
Loading
Loading
@@ -30,6 +30,7 @@ describe GitlabSchema.types['SentryDetailedError'] do
lastReleaseLastCommit
firstReleaseShortVersion
lastReleaseShortVersion
gitlabCommit
]
 
is_expected.to have_graphql_fields(*expected_fields)
Loading
Loading
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::BackgroundMigration::ActivatePrometheusServicesForSharedClusterApplications, :migration, schema: 2019_12_20_102807 do
include MigrationHelpers::PrometheusServiceHelpers
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:services) { table(:services) }
let(:namespace) { namespaces.create(name: 'user', path: 'user') }
let(:project) { projects.create(namespace_id: namespace.id) }
let(:columns) do
%w(project_id active properties type template push_events
issues_events merge_requests_events tag_push_events
note_events category default wiki_page_events pipeline_events
confidential_issues_events commit_events job_events
confidential_note_events deployment_events)
end
describe '#perform' do
it 'is idempotent' do
expect { subject.perform(project.id) }.to change { services.order(:id).map { |row| row.attributes } }
expect { subject.perform(project.id) }.not_to change { services.order(:id).map { |row| row.attributes } }
end
context 'non prometheus services' do
it 'does not change them' do
other_type = 'SomeOtherService'
services.create(service_params_for(project.id, active: true, type: other_type))
expect { subject.perform(project.id) }.not_to change { services.where(type: other_type).order(:id).map { |row| row.attributes } }
end
end
context 'prometheus services are configured manually ' do
it 'does not change them' do
properties = '{"api_url":"http://test.dev","manual_configuration":"1"}'
services.create(service_params_for(project.id, properties: properties, active: false))
expect { subject.perform(project.id) }.not_to change { services.order(:id).map { |row| row.attributes } }
end
end
context 'prometheus integration services do not exist' do
it 'creates missing services entries' do
subject.perform(project.id)
rows = services.order(:id).map { |row| row.attributes.slice(*columns).symbolize_keys }
expect([service_params_for(project.id, active: true)]).to eq rows
end
end
context 'prometheus integration services exist' do
context 'in active state' do
it 'does not change them' do
services.create(service_params_for(project.id, active: true))
expect { subject.perform(project.id) }.not_to change { services.order(:id).map { |row| row.attributes } }
end
end
context 'not in active state' do
it 'sets active attribute to true' do
service = services.create(service_params_for(project.id))
expect { subject.perform(project.id) }.to change { service.reload.active? }.from(false).to(true)
end
end
end
end
end
Loading
Loading
@@ -219,6 +219,16 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect(award_emoji.map(&:name)).to contain_exactly('thumbsup', 'coffee')
end
 
it 'snippet has notes' do
expect(@project.snippets.first.notes.count).to eq(1)
end
it 'snippet has award emojis on notes' do
award_emoji = @project.snippets.first.notes.first.award_emoji.first
expect(award_emoji.name).to eq('thumbsup')
end
it 'restores `ci_cd_settings` : `group_runners_enabled` setting' do
expect(@project.ci_cd_settings.group_runners_enabled?).to eq(false)
end
Loading
Loading
@@ -240,6 +250,18 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect(sentry_issue.sentry_issue_identifier).to eq(1234567891)
end
 
it 'has award emoji for an issue' do
award_emoji = @project.issues.first.award_emoji.first
expect(award_emoji.name).to eq('musical_keyboard')
end
it 'has award emoji for a note in an issue' do
award_emoji = @project.issues.first.notes.first.award_emoji.first
expect(award_emoji.name).to eq('clapper')
end
it 'restores container_expiration_policy' do
policy = Project.find_by_path('project').container_expiration_policy
 
Loading
Loading
@@ -266,6 +288,20 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
it 'has no source if source/target differ' do
expect(MergeRequest.find_by_title('MR2').source_project_id).to be_nil
end
it 'has award emoji' do
award_emoji = MergeRequest.find_by_title('MR1').award_emoji
expect(award_emoji.map(&:name)).to contain_exactly('thumbsup', 'drum')
end
context 'notes' do
it 'has award emoji' do
award_emoji = MergeRequest.find_by_title('MR1').notes.first.award_emoji.first
expect(award_emoji.name).to eq('tada')
end
end
end
 
context 'tokens are regenerated' do
Loading
Loading
Loading
Loading
@@ -263,6 +263,7 @@ describe Sentry::Client::Issue do
:last_release_last_commit | [:lastRelease, :lastCommit]
:first_release_short_version | [:firstRelease, :shortVersion]
:last_release_short_version | [:lastRelease, :shortVersion]
:first_release_version | [:firstRelease, :version]
end
 
with_them do
Loading
Loading
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'migrate', '20200107172020_add_timestamp_softwarelicensespolicy.rb')
describe AddTimestampSoftwarelicensespolicy, :migration do
let(:software_licenses_policy) { table(:software_license_policies) }
let(:projects) { table(:projects) }
let(:licenses) { table(:software_licenses) }
before do
projects.create!(name: 'gitlab', path: 'gitlab-org/gitlab-ce', namespace_id: 1)
licenses.create!(name: 'MIT')
software_licenses_policy.create!(project_id: projects.first.id, software_license_id: licenses.first.id)
end
it 'creates timestamps' do
migrate!
expect(software_licenses_policy.first.created_at).to be_nil
expect(software_licenses_policy.first.updated_at).to be_nil
end
end
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20191220102807_patch_prometheus_services_for_shared_cluster_applications.rb')
describe PatchPrometheusServicesForSharedClusterApplications, :migration, :sidekiq do
include MigrationHelpers::PrometheusServiceHelpers
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:services) { table(:services) }
let(:clusters) { table(:clusters) }
let(:cluster_groups) { table(:cluster_groups) }
let(:clusters_applications_prometheus) { table(:clusters_applications_prometheus) }
let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
let(:application_statuses) do
{
errored: -1,
installed: 3,
updated: 5
}
end
let(:cluster_types) do
{
instance_type: 1,
group_type: 2
}
end
describe '#up' do
let!(:project_with_missing_service) { projects.create!(name: 'gitlab', path: 'gitlab-ce', namespace_id: namespace.id) }
let(:project_with_inactive_service) { projects.create!(name: 'gitlab', path: 'gitlab-ee', namespace_id: namespace.id) }
let(:project_with_active_service) { projects.create!(name: 'gitlab', path: 'gitlab-ee', namespace_id: namespace.id) }
let(:project_with_manual_active_service) { projects.create!(name: 'gitlab', path: 'gitlab-ee', namespace_id: namespace.id) }
let(:project_with_manual_inactive_service) { projects.create!(name: 'gitlab', path: 'gitlab-ee', namespace_id: namespace.id) }
let(:project_with_active_not_prometheus_service) { projects.create!(name: 'gitlab', path: 'gitlab-ee', namespace_id: namespace.id) }
let(:project_with_inactive_not_prometheus_service) { projects.create!(name: 'gitlab', path: 'gitlab-ee', namespace_id: namespace.id) }
before do
services.create(service_params_for(project_with_inactive_service.id, active: false))
services.create(service_params_for(project_with_active_service.id, active: true))
services.create(service_params_for(project_with_active_not_prometheus_service.id, active: true, type: 'other'))
services.create(service_params_for(project_with_inactive_not_prometheus_service.id, active: false, type: 'other'))
services.create(service_params_for(project_with_manual_inactive_service.id, active: false, properties: { some: 'data' }.to_json))
services.create(service_params_for(project_with_manual_active_service.id, active: true, properties: { some: 'data' }.to_json))
end
shared_examples 'patch prometheus services post migration' do
context 'prometheus application is installed on the cluster' do
it 'schedules a background migration' do
clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:installed], version: '123')
Sidekiq::Testing.fake! do
Timecop.freeze do
background_migrations = [["ActivatePrometheusServicesForSharedClusterApplications", project_with_missing_service.id],
["ActivatePrometheusServicesForSharedClusterApplications", project_with_inactive_service.id],
["ActivatePrometheusServicesForSharedClusterApplications", project_with_active_not_prometheus_service.id],
["ActivatePrometheusServicesForSharedClusterApplications", project_with_inactive_not_prometheus_service.id]]
migrate!
enqueued_migrations = BackgroundMigrationWorker.jobs.map { |job| job['args'] }
expect(enqueued_migrations).to match_array(background_migrations)
end
end
end
end
context 'prometheus application was recently updated on the cluster' do
it 'schedules a background migration' do
clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:updated], version: '123')
Sidekiq::Testing.fake! do
Timecop.freeze do
background_migrations = [["ActivatePrometheusServicesForSharedClusterApplications", project_with_missing_service.id],
["ActivatePrometheusServicesForSharedClusterApplications", project_with_inactive_service.id],
["ActivatePrometheusServicesForSharedClusterApplications", project_with_active_not_prometheus_service.id],
["ActivatePrometheusServicesForSharedClusterApplications", project_with_inactive_not_prometheus_service.id]]
migrate!
enqueued_migrations = BackgroundMigrationWorker.jobs.map { |job| job['args'] }
expect(enqueued_migrations).to match_array(background_migrations)
end
end
end
end
context 'prometheus application failed to install on the cluster' do
it 'does not schedule a background migration' do
clusters_applications_prometheus.create(cluster_id: cluster.id, status: application_statuses[:errored], version: '123')
Sidekiq::Testing.fake! do
Timecop.freeze do
migrate!
expect(BackgroundMigrationWorker.jobs.size).to eq 0
end
end
end
end
context 'prometheus application is NOT installed on the cluster' do
it 'does not schedule a background migration' do
Sidekiq::Testing.fake! do
Timecop.freeze do
migrate!
expect(BackgroundMigrationWorker.jobs.size).to eq 0
end
end
end
end
end
context 'Cluster is group_type' do
let(:cluster) { clusters.create(name: 'cluster', cluster_type: cluster_types[:group_type]) }
before do
cluster_groups.create(group_id: namespace.id, cluster_id: cluster.id)
end
it_behaves_like 'patch prometheus services post migration'
end
context 'Cluster is instance_type' do
let(:cluster) { clusters.create(name: 'cluster', cluster_type: cluster_types[:instance_type]) }
it_behaves_like 'patch prometheus services post migration'
end
end
end
Loading
Loading
@@ -210,6 +210,53 @@ describe ErrorTracking::ProjectErrorTrackingSetting do
end
end
 
describe '#issue_details' do
let(:issue) { build(:detailed_error_tracking_error) }
let(:sentry_client) { double('sentry_client', issue_details: issue) }
let(:commit_id) { '123456' }
let(:result) do
subject.issue_details
end
context 'when cached' do
before do
stub_reactive_cache(subject, issue, {})
synchronous_reactive_cache(subject)
expect(subject).to receive(:sentry_client).and_return(sentry_client)
end
it { expect(result).to eq(issue: issue) }
it { expect(result[:issue].first_release_version).to eq(commit_id) }
it { expect(result[:issue].gitlab_commit).to eq(nil) }
context 'when release version is nil' do
before do
issue.first_release_version = nil
end
it { expect(result[:issue].gitlab_commit).to eq(nil) }
end
context 'when repo commit matches first relase version' do
let(:commit) { double('commit', id: commit_id) }
let(:repository) { double('repository', commit: commit) }
before do
expect(project).to receive(:repository).and_return(repository)
end
it { expect(result[:issue].gitlab_commit).to eq(commit_id) }
end
end
context 'when not cached' do
it { expect(subject).not_to receive(:sentry_client) }
it { expect(result).to be_nil }
end
end
describe '#update_issue' do
let(:opts) do
{ status: 'resolved' }
Loading
Loading
Loading
Loading
@@ -181,4 +181,10 @@ RSpec.describe Release do
it { is_expected.to eq(release.evidence.summary) }
end
end
describe '#milestone_titles' do
let(:release) { create(:release, :with_milestones) }
it { expect(release.milestone_titles).to eq(release.milestones.map {|m| m.title }.sort.join(", "))}
end
end
Loading
Loading
@@ -56,6 +56,7 @@ describe 'getting a detailed sentry error' do
expect(error_data['status']).to eql sentry_detailed_error.status.upcase
expect(error_data['firstSeen']).to eql sentry_detailed_error.first_seen
expect(error_data['lastSeen']).to eql sentry_detailed_error.last_seen
expect(error_data['gitlabCommit']).to be nil
end
 
it 'is expected to return the frequency correctly' do
Loading
Loading
Loading
Loading
@@ -21,6 +21,7 @@ describe Releases::UpdateService do
it 'raises an error' do
result = service.execute
expect(result[:status]).to eq(:error)
expect(result[:milestones_updated]).to be_falsy
end
end
 
Loading
Loading
@@ -50,21 +51,33 @@ describe Releases::UpdateService do
end
 
context 'when a milestone is passed in' do
let(:new_title) { 'v2.0' }
let(:milestone) { create(:milestone, project: project, title: 'v1.0') }
let(:new_milestone) { create(:milestone, project: project, title: new_title) }
let(:params_with_milestone) { params.merge!({ milestones: [new_title] }) }
let(:new_milestone) { create(:milestone, project: project, title: new_title) }
let(:service) { described_class.new(new_milestone.project, user, params_with_milestone) }
 
before do
release.milestones << milestone
end
 
service.execute
release.reload
context 'a different milestone' do
let(:new_title) { 'v2.0' }
it 'updates the related milestone accordingly' do
result = service.execute
release.reload
expect(release.milestones.first.title).to eq(new_title)
expect(result[:milestones_updated]).to be_truthy
end
end
 
it 'updates the related milestone accordingly' do
expect(release.milestones.first.title).to eq(new_title)
context 'an identical milestone' do
let(:new_title) { 'v1.0' }
it "raises an error" do
expect { service.execute }.to raise_error(ActiveRecord::RecordInvalid)
end
end
end
 
Loading
Loading
@@ -76,12 +89,14 @@ describe Releases::UpdateService do
release.milestones << milestone
 
service.params = params_with_empty_milestone
service.execute
release.reload
end
 
it 'removes the old milestone and does not associate any new milestone' do
result = service.execute
release.reload
expect(release.milestones).not_to be_present
expect(result[:milestones_updated]).to be_truthy
end
end
 
Loading
Loading
@@ -96,14 +111,15 @@ describe Releases::UpdateService do
create(:milestone, project: project, title: new_title_1)
create(:milestone, project: project, title: new_title_2)
release.milestones << milestone
service.execute
release.reload
end
 
it 'removes the old milestone and update the release with the new ones' do
result = service.execute
release.reload
milestone_titles = release.milestones.map(&:title)
expect(milestone_titles).to match_array([new_title_1, new_title_2])
expect(result[:milestones_updated]).to be_truthy
end
end
end
Loading
Loading
# frozen_string_literal: true
module MigrationHelpers
module PrometheusServiceHelpers
def service_params_for(project_id, params = {})
{
project_id: project_id,
active: false,
properties: '{}',
type: 'PrometheusService',
template: false,
push_events: true,
issues_events: true,
merge_requests_events: true,
tag_push_events: true,
note_events: true,
category: 'monitoring',
default: false,
wiki_page_events: true,
pipeline_events: true,
confidential_issues_events: true,
commit_events: true,
job_events: true,
confidential_note_events: true,
deployment_events: false
}.merge(params)
end
def row_attributes(entity)
entity.attributes.with_indifferent_access.tap do |hash|
hash.merge!(hash.slice(:created_at, :updated_at).transform_values { |v| v.to_s(:db) })
end
end
end
end
# frozen_string_literal: true
# This shared_example requires the following variables:
# let(:service_class) { Gitlab::DatabaseImporters::SelfMonitoring::Project::DeleteService }
# let(:service) { instance_double(service_class) }
RSpec.shared_examples 'executes service' do
before do
allow(service_class).to receive(:new) { service }
end
it 'runs the service' do
expect(service).to receive(:execute)
subject.perform
end
end
RSpec.shared_examples 'returns in_progress based on Sidekiq::Status' do
it 'returns true when job is enqueued' do
jid = described_class.perform_async
expect(described_class.in_progress?(jid)).to eq(true)
end
it 'returns false when job does not exist' do
expect(described_class.in_progress?('fake_jid')).to eq(false)
end
end
Loading
Loading
@@ -7,22 +7,10 @@ describe SelfMonitoringProjectCreateWorker do
let(:service_class) { Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService }
let(:service) { instance_double(service_class) }
 
before do
allow(service_class).to receive(:new) { service }
end
it 'runs the SelfMonitoring::Project::CreateService' do
expect(service).to receive(:execute)
subject.perform
end
it_behaves_like 'executes service'
end
 
describe '.in_progress?', :clean_gitlab_redis_shared_state do
it 'returns in_progress when job is enqueued' do
jid = described_class.perform_async
expect(described_class.in_progress?(jid)).to eq(true)
end
it_behaves_like 'returns in_progress based on Sidekiq::Status'
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment