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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-01-29 15:09:08 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-01-29 15:09:08 +0300
commit7cc6872401eb487ed20dbb9d455f8bb9c97d9e39 (patch)
tree63f6ed5d4e6c5cec31c43363626d9f5b178eddf8 /spec
parent46b10c0fc884400941c17e2777b242ac54d111e5 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/factories/error_tracking/detailed_error.rb35
-rw-r--r--spec/factories/error_tracking/error.rb12
-rw-r--r--spec/features/issues/move_spec.rb2
-rw-r--r--spec/fixtures/lib/gitlab/import_export/with_duplicates.json43
-rw-r--r--spec/frontend/registry/settings/components/settings_form_spec.js35
-rw-r--r--spec/graphql/resolvers/error_tracking/sentry_error_collection_resolver_spec.rb47
-rw-r--r--spec/graphql/resolvers/error_tracking/sentry_errors_resolver_spec.rb103
-rw-r--r--spec/graphql/types/error_tracking/sentry_error_collection_type_spec.rb31
-rw-r--r--spec/graphql/types/error_tracking/sentry_error_type_spec.rb31
-rw-r--r--spec/lib/banzai/filter/commit_range_reference_filter_spec.rb4
-rw-r--r--spec/lib/banzai/filter/label_reference_filter_spec.rb4
-rw-r--r--spec/lib/banzai/filter/milestone_reference_filter_spec.rb8
-rw-r--r--spec/lib/banzai/filter/project_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/gfm/reference_rewriter_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/project_tree_loader_spec.rb49
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb8
-rw-r--r--spec/models/project_spec.rb153
-rw-r--r--spec/presenters/sentry_error_presenter_spec.rb (renamed from spec/presenters/sentry_detailed_error_presenter_spec.rb)4
-rw-r--r--spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb191
-rw-r--r--spec/requests/self_monitoring_project_spec.rb17
-rw-r--r--spec/support/matchers/graphql_matchers.rb6
-rw-r--r--spec/support/shared_examples/error_tracking_shared_examples.rb13
22 files changed, 663 insertions, 137 deletions
diff --git a/spec/factories/error_tracking/detailed_error.rb b/spec/factories/error_tracking/detailed_error.rb
index 07b6c53e3cd..83004ffae38 100644
--- a/spec/factories/error_tracking/detailed_error.rb
+++ b/spec/factories/error_tracking/detailed_error.rb
@@ -1,41 +1,20 @@
# frozen_string_literal: true
FactoryBot.define do
- factory :detailed_error_tracking_error, class: 'Gitlab::ErrorTracking::DetailedError' do
- id { '1' }
- title { 'title' }
- type { 'error' }
- user_count { 1 }
- count { 2 }
- first_seen { Time.now.iso8601 }
- last_seen { Time.now.iso8601 }
- message { 'message' }
- culprit { 'culprit' }
- external_url { 'http://example.com/id' }
+ factory :detailed_error_tracking_error, parent: :error_tracking_error, class: 'Gitlab::ErrorTracking::DetailedError' do
+ gitlab_issue { 'http://gitlab.example.com/issues/1' }
external_base_url { 'http://example.com' }
- project_id { 'project1' }
- project_name { 'project name' }
- project_slug { 'project_name' }
- short_id { 'ID' }
- status { 'unresolved' }
+ first_release_last_commit { '68c914da9' }
+ last_release_last_commit { '9ad419c86' }
+ first_release_short_version { 'abc123' }
+ last_release_short_version { 'abc123' }
+ first_release_version { '12345678' }
tags do
{
level: 'error',
logger: 'rails'
}
end
- frequency do
- [
- [Time.now.to_i, 10]
- ]
- end
- gitlab_issue { 'http://gitlab.example.com/issues/1' }
- first_release_last_commit { '68c914da9' }
- last_release_last_commit { '9ad419c86' }
- first_release_short_version { 'abc123' }
- last_release_short_version { 'abc123' }
- first_release_version { '12345678' }
-
skip_create
end
end
diff --git a/spec/factories/error_tracking/error.rb b/spec/factories/error_tracking/error.rb
index 5be1f074555..e5f2e2ca9a7 100644
--- a/spec/factories/error_tracking/error.rb
+++ b/spec/factories/error_tracking/error.rb
@@ -2,13 +2,13 @@
FactoryBot.define do
factory :error_tracking_error, class: 'Gitlab::ErrorTracking::Error' do
- id { 'id' }
+ id { '1' }
title { 'title' }
type { 'error' }
user_count { 1 }
count { 2 }
- first_seen { Time.now }
- last_seen { Time.now }
+ first_seen { Time.now.iso8601 }
+ last_seen { Time.now.iso8601 }
message { 'message' }
culprit { 'culprit' }
external_url { 'http://example.com/id' }
@@ -17,7 +17,11 @@ FactoryBot.define do
project_slug { 'project_name' }
short_id { 'ID' }
status { 'unresolved' }
- frequency { [] }
+ frequency do
+ [
+ [Time.now.to_i, 10]
+ ]
+ end
skip_create
end
diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb
index 7126707affd..831bcf8931e 100644
--- a/spec/features/issues/move_spec.rb
+++ b/spec/features/issues/move_spec.rb
@@ -32,7 +32,7 @@ describe 'issue move to another project' do
let(:new_project) { create(:project) }
let(:new_project_search) { create(:project) }
let(:text) { "Text with #{mr.to_reference}" }
- let(:cross_reference) { old_project.to_reference(new_project) }
+ let(:cross_reference) { old_project.to_reference_base(new_project) }
before do
old_project.add_reporter(user)
diff --git a/spec/fixtures/lib/gitlab/import_export/with_duplicates.json b/spec/fixtures/lib/gitlab/import_export/with_duplicates.json
new file mode 100644
index 00000000000..ed2e1821dd3
--- /dev/null
+++ b/spec/fixtures/lib/gitlab/import_export/with_duplicates.json
@@ -0,0 +1,43 @@
+{
+ "simple": 42,
+ "duped_hash_with_id": {
+ "id": 0,
+ "v1": 1
+ },
+ "duped_hash_no_id": {
+ "v1": 1
+ },
+ "duped_array": [
+ "v2"
+ ],
+ "array": [
+ {
+ "duped_hash_with_id": {
+ "id": 0,
+ "v1": 1
+ }
+ },
+ {
+ "duped_array": [
+ "v2"
+ ]
+ },
+ {
+ "duped_hash_no_id": {
+ "v1": 1
+ }
+ }
+ ],
+ "nested": {
+ "duped_hash_with_id": {
+ "id": 0,
+ "v1": 1
+ },
+ "duped_array": [
+ "v2"
+ ],
+ "array": [
+ "don't touch"
+ ]
+ }
+} \ No newline at end of file
diff --git a/spec/frontend/registry/settings/components/settings_form_spec.js b/spec/frontend/registry/settings/components/settings_form_spec.js
index 996804f6d08..5b81d034e14 100644
--- a/spec/frontend/registry/settings/components/settings_form_spec.js
+++ b/spec/frontend/registry/settings/components/settings_form_spec.js
@@ -1,4 +1,5 @@
import { mount } from '@vue/test-utils';
+import Tracking from '~/tracking';
import stubChildren from 'helpers/stub_children';
import component from '~/registry/settings/components/settings_form.vue';
import { createStore } from '~/registry/settings/store/';
@@ -15,6 +16,9 @@ describe('Settings Form', () => {
let dispatchSpy;
const FORM_ELEMENTS_ID_PREFIX = '#expiration-policy';
+ const trackingPayload = {
+ label: 'docker_container_retention_and_expiration_policies',
+ };
const GlLoadingIcon = { name: 'gl-loading-icon-stub', template: '<svg></svg>' };
@@ -48,6 +52,7 @@ describe('Settings Form', () => {
store.dispatch('setInitialState', stringifiedFormOptions);
dispatchSpy = jest.spyOn(store, 'dispatch');
mountComponent();
+ jest.spyOn(Tracking, 'event');
});
afterEach(() => {
@@ -118,15 +123,23 @@ describe('Settings Form', () => {
beforeEach(() => {
form = findForm();
});
- it('cancel has type reset', () => {
- expect(findCancelButton().attributes('type')).toBe('reset');
- });
- it('form reset event call the appropriate function', () => {
- dispatchSpy.mockReturnValue();
- form.trigger('reset');
- // expect.any(Object) is necessary because the event payload is passed to the function
- expect(dispatchSpy).toHaveBeenCalledWith('resetSettings', expect.any(Object));
+ describe('form cancel event', () => {
+ it('has type reset', () => {
+ expect(findCancelButton().attributes('type')).toBe('reset');
+ });
+
+ it('calls the appropriate function', () => {
+ dispatchSpy.mockReturnValue();
+ form.trigger('reset');
+ expect(dispatchSpy).toHaveBeenCalledWith('resetSettings');
+ });
+
+ it('tracks the reset event', () => {
+ dispatchSpy.mockReturnValue();
+ form.trigger('reset');
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, 'reset_form', trackingPayload);
+ });
});
it('save has type submit', () => {
@@ -177,6 +190,12 @@ describe('Settings Form', () => {
expect(dispatchSpy).toHaveBeenCalledWith('saveSettings');
});
+ it('tracks the submit event', () => {
+ dispatchSpy.mockResolvedValue();
+ form.trigger('submit');
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, 'submit_form', trackingPayload);
+ });
+
it('show a success toast when submit succeed', () => {
dispatchSpy.mockResolvedValue();
form.trigger('submit');
diff --git a/spec/graphql/resolvers/error_tracking/sentry_error_collection_resolver_spec.rb b/spec/graphql/resolvers/error_tracking/sentry_error_collection_resolver_spec.rb
new file mode 100644
index 00000000000..3bb8a5c389d
--- /dev/null
+++ b/spec/graphql/resolvers/error_tracking/sentry_error_collection_resolver_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Resolvers::ErrorTracking::SentryErrorCollectionResolver do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:current_user) { create(:user) }
+
+ let(:list_issues_service) { spy('ErrorTracking::ListIssuesService') }
+
+ before do
+ project.add_developer(current_user)
+
+ allow(ErrorTracking::ListIssuesService)
+ .to receive(:new)
+ .and_return list_issues_service
+ end
+
+ describe '#resolve' do
+ it 'returns an error collection object' do
+ expect(resolve_error_collection).to be_a Gitlab::ErrorTracking::ErrorCollection
+ end
+
+ it 'provides the service url' do
+ fake_url = 'http://test.com'
+
+ expect(list_issues_service)
+ .to receive(:external_url)
+ .and_return(fake_url)
+
+ result = resolve_error_collection
+ expect(result.external_url).to eq fake_url
+ end
+
+ it 'provides the project' do
+ expect(resolve_error_collection.project).to eq project
+ end
+ end
+
+ private
+
+ def resolve_error_collection(context = { current_user: current_user })
+ resolve(described_class, obj: project, args: {}, ctx: context)
+ end
+end
diff --git a/spec/graphql/resolvers/error_tracking/sentry_errors_resolver_spec.rb b/spec/graphql/resolvers/error_tracking/sentry_errors_resolver_spec.rb
new file mode 100644
index 00000000000..93f89d077d7
--- /dev/null
+++ b/spec/graphql/resolvers/error_tracking/sentry_errors_resolver_spec.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Resolvers::ErrorTracking::SentryErrorsResolver do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:error_collection) { Gitlab::ErrorTracking::ErrorCollection.new(project: project) }
+
+ let(:list_issues_service) { spy('ErrorTracking::ListIssuesService') }
+
+ let(:issues) { nil }
+ let(:pagination) { nil }
+
+ describe '#resolve' do
+ context 'insufficient user permission' do
+ let(:user) { create(:user) }
+
+ it 'returns nil' do
+ context = { current_user: user }
+
+ expect(resolve_errors({}, context)).to eq nil
+ end
+ end
+
+ context 'user with permission' do
+ before do
+ project.add_developer(current_user)
+
+ allow(ErrorTracking::ListIssuesService)
+ .to receive(:new)
+ .and_return list_issues_service
+ end
+
+ context 'when after arg given' do
+ let(:after) { "1576029072000:0:0" }
+
+ it 'gives the cursor arg' do
+ expect(ErrorTracking::ListIssuesService)
+ .to receive(:new)
+ .with(project, current_user, { cursor: after })
+ .and_return list_issues_service
+
+ resolve_errors({ after: after })
+ end
+ end
+
+ context 'when no issues fetched' do
+ before do
+ allow(list_issues_service)
+ .to receive(:execute)
+ .and_return(
+ issues: nil
+ )
+ end
+ it 'returns nil' do
+ expect(resolve_errors).to eq nil
+ end
+ end
+
+ context 'when issues returned' do
+ let(:issues) { [:issue_1, :issue_2] }
+ let(:pagination) do
+ {
+ 'next' => { 'cursor' => 'next' },
+ 'previous' => { 'cursor' => 'prev' }
+ }
+ end
+
+ before do
+ allow(list_issues_service)
+ .to receive(:execute)
+ .and_return(
+ issues: issues,
+ pagination: pagination
+ )
+ end
+
+ it 'sets the issues' do
+ expect(resolve_errors).to contain_exactly(*issues)
+ end
+
+ it 'sets the pagination variables' do
+ result = resolve_errors
+ expect(result.next_cursor).to eq 'next'
+ expect(result.previous_cursor).to eq 'prev'
+ end
+
+ it 'returns an externally paginated array' do
+ expect(resolve_errors).to be_a Gitlab::Graphql::ExternallyPaginatedArray
+ end
+ end
+ end
+ end
+
+ private
+
+ def resolve_errors(args = {}, context = { current_user: current_user })
+ resolve(described_class, obj: error_collection, args: args, ctx: context)
+ end
+end
diff --git a/spec/graphql/types/error_tracking/sentry_error_collection_type_spec.rb b/spec/graphql/types/error_tracking/sentry_error_collection_type_spec.rb
new file mode 100644
index 00000000000..1e6b7f89c08
--- /dev/null
+++ b/spec/graphql/types/error_tracking/sentry_error_collection_type_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe GitlabSchema.types['SentryErrorCollection'] do
+ it { expect(described_class.graphql_name).to eq('SentryErrorCollection') }
+
+ it { expect(described_class).to require_graphql_authorizations(:read_sentry_issue) }
+
+ it 'exposes the expected fields' do
+ expected_fields = %i[
+ errors
+ detailed_error
+ external_url
+ ]
+
+ is_expected.to have_graphql_fields(*expected_fields)
+ end
+
+ describe 'errors field' do
+ subject { described_class.fields['errors'] }
+
+ it 'returns errors' do
+ aggregate_failures 'testing the correct types are returned' do
+ is_expected.to have_graphql_type(Types::ErrorTracking::SentryErrorType.connection_type)
+ is_expected.to have_graphql_extension(Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension)
+ is_expected.to have_graphql_resolver(Resolvers::ErrorTracking::SentryErrorsResolver)
+ end
+ end
+ end
+end
diff --git a/spec/graphql/types/error_tracking/sentry_error_type_spec.rb b/spec/graphql/types/error_tracking/sentry_error_type_spec.rb
new file mode 100644
index 00000000000..51acd035024
--- /dev/null
+++ b/spec/graphql/types/error_tracking/sentry_error_type_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe GitlabSchema.types['SentryError'] do
+ it { expect(described_class.graphql_name).to eq('SentryError') }
+
+ it 'exposes the expected fields' do
+ expected_fields = %i[
+ id
+ sentryId
+ title
+ type
+ userCount
+ count
+ firstSeen
+ lastSeen
+ message
+ culprit
+ externalUrl
+ sentryProjectId
+ sentryProjectName
+ sentryProjectSlug
+ shortId
+ status
+ frequency
+ ]
+
+ is_expected.to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
index a82b890be42..5cfb0e6e6f7 100644
--- a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
@@ -229,10 +229,10 @@ describe Banzai::Filter::CommitRangeReferenceFilter do
end
it 'ignores invalid commit IDs on the referenced project' do
- exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}"
+ exp = act = "Fixed #{project2.to_reference_base}@#{commit1.id.reverse}...#{commit2.id}"
expect(reference_filter(act).to_html).to eq exp
- exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}"
+ exp = act = "Fixed #{project2.to_reference_base}@#{commit1.id}...#{commit2.id.reverse}"
expect(reference_filter(act).to_html).to eq exp
end
end
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index 66af26bc51c..82df5064896 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -369,7 +369,7 @@ describe Banzai::Filter::LabelReferenceFilter do
end
context 'with project reference' do
- let(:reference) { "#{project.to_reference}#{group_label.to_reference(format: :name)}" }
+ let(:reference) { "#{project.to_reference_base}#{group_label.to_reference(format: :name)}" }
it 'links to a valid reference' do
doc = reference_filter("See #{reference}", project: project)
@@ -385,7 +385,7 @@ describe Banzai::Filter::LabelReferenceFilter do
end
it 'ignores invalid label names' do
- exp = act = %(Label #{project.to_reference}#{Label.reference_prefix}"#{group_label.name.reverse}")
+ exp = act = %(Label #{project.to_reference_base}#{Label.reference_prefix}"#{group_label.name.reverse}")
expect(reference_filter(act).to_html).to eq exp
end
diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
index 2fe8c9074df..0c8413adcba 100644
--- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
@@ -367,15 +367,17 @@ describe Banzai::Filter::MilestoneReferenceFilter do
expect(doc.css('a').first.text).to eq(urls.milestone_url(milestone))
end
- it 'does not support cross-project references' do
+ it 'does not support cross-project references', :aggregate_failures do
another_group = create(:group)
another_project = create(:project, :public, group: group)
- project_reference = another_project.to_reference(project)
+ project_reference = another_project.to_reference_base(project)
+ input_text = "See #{project_reference}#{reference}"
milestone.update!(group: another_group)
- doc = reference_filter("See #{project_reference}#{reference}")
+ doc = reference_filter(input_text)
+ expect(input_text).to match(Milestone.reference_pattern)
expect(doc.css('a')).to be_empty
end
diff --git a/spec/lib/banzai/filter/project_reference_filter_spec.rb b/spec/lib/banzai/filter/project_reference_filter_spec.rb
index d0b4542d503..a054b79ec03 100644
--- a/spec/lib/banzai/filter/project_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/project_reference_filter_spec.rb
@@ -10,7 +10,7 @@ describe Banzai::Filter::ProjectReferenceFilter do
end
def get_reference(project)
- project.to_reference_with_postfix
+ project.to_reference
end
let(:project) { create(:project, :public) }
diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
index b0d2e049777..a3904f4a97c 100644
--- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::Gfm::ReferenceRewriter do
let(:new_project) { create(:project, name: 'new-project', group: group) }
let(:user) { create(:user) }
- let(:old_project_ref) { old_project.to_reference(new_project) }
+ let(:old_project_ref) { old_project.to_reference_base(new_project) }
let(:text) { 'some text' }
before do
diff --git a/spec/lib/gitlab/import_export/project_tree_loader_spec.rb b/spec/lib/gitlab/import_export/project_tree_loader_spec.rb
new file mode 100644
index 00000000000..b22de5a3f7b
--- /dev/null
+++ b/spec/lib/gitlab/import_export/project_tree_loader_spec.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::ImportExport::ProjectTreeLoader do
+ let(:fixture) { 'spec/fixtures/lib/gitlab/import_export/with_duplicates.json' }
+ let(:project_tree) { JSON.parse(File.read(fixture)) }
+
+ context 'without de-duplicating entries' do
+ let(:parsed_tree) do
+ subject.load(fixture)
+ end
+
+ it 'parses the JSON into the expected tree' do
+ expect(parsed_tree).to eq(project_tree)
+ end
+
+ it 'does not de-duplicate entries' do
+ expect(parsed_tree['duped_hash_with_id']).not_to be(parsed_tree['array'][0]['duped_hash_with_id'])
+ end
+ end
+
+ context 'with de-duplicating entries' do
+ let(:parsed_tree) do
+ subject.load(fixture, dedup_entries: true)
+ end
+
+ it 'parses the JSON into the expected tree' do
+ expect(parsed_tree).to eq(project_tree)
+ end
+
+ it 'de-duplicates equal values' do
+ expect(parsed_tree['duped_hash_with_id']).to be(parsed_tree['array'][0]['duped_hash_with_id'])
+ expect(parsed_tree['duped_hash_with_id']).to be(parsed_tree['nested']['duped_hash_with_id'])
+ expect(parsed_tree['duped_array']).to be(parsed_tree['array'][1]['duped_array'])
+ expect(parsed_tree['duped_array']).to be(parsed_tree['nested']['duped_array'])
+ end
+
+ it 'does not de-duplicate hashes without IDs' do
+ expect(parsed_tree['duped_hash_no_id']).to eq(parsed_tree['array'][2]['duped_hash_no_id'])
+ expect(parsed_tree['duped_hash_no_id']).not_to be(parsed_tree['array'][2]['duped_hash_no_id'])
+ end
+
+ it 'keeps single entries intact' do
+ expect(parsed_tree['simple']).to eq(42)
+ expect(parsed_tree['nested']['array']).to eq(["don't touch"])
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index 25f70420cda..129f119e148 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -450,7 +450,9 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
context 'project.json file access check' do
let(:user) { create(:user) }
let!(:project) { create(:project, :builds_disabled, :issues_disabled, name: 'project', path: 'project') }
- let(:project_tree_restorer) { described_class.new(user: user, shared: shared, project: project) }
+ let(:project_tree_restorer) do
+ described_class.new(user: user, shared: shared, project: project)
+ end
let(:restored_project_json) { project_tree_restorer.restore }
it 'does not read a symlink' do
@@ -725,7 +727,9 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:tree_hash) { { 'visibility_level' => visibility } }
- let(:restorer) { described_class.new(user: user, shared: shared, project: project) }
+ let(:restorer) do
+ described_class.new(user: user, shared: shared, project: project)
+ end
before do
expect(restorer).to receive(:read_tree_hash) { tree_hash }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index a1c38a3e668..df32545b90b 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -131,23 +131,19 @@ describe Project do
end
context 'when creating a new project' do
- it 'automatically creates a CI/CD settings row' do
- project = create(:project)
+ let_it_be(:project) { create(:project) }
+ it 'automatically creates a CI/CD settings row' do
expect(project.ci_cd_settings).to be_an_instance_of(ProjectCiCdSetting)
expect(project.ci_cd_settings).to be_persisted
end
it 'automatically creates a container expiration policy row' do
- project = create(:project)
-
expect(project.container_expiration_policy).to be_an_instance_of(ContainerExpirationPolicy)
expect(project.container_expiration_policy).to be_persisted
end
it 'automatically creates a Pages metadata row' do
- project = create(:project)
-
expect(project.pages_metadatum).to be_an_instance_of(ProjectPagesMetadatum)
expect(project.pages_metadatum).to be_persisted
end
@@ -532,111 +528,114 @@ describe Project do
it { is_expected.to delegate_method(:last_pipeline).to(:commit).with_arguments(allow_nil: true) }
end
- describe '#to_reference_with_postfix' do
- it 'returns the full path with reference_postfix' do
- namespace = create(:namespace, path: 'sample-namespace')
- project = create(:project, path: 'sample-project', namespace: namespace)
-
- expect(project.to_reference_with_postfix).to eq 'sample-namespace/sample-project>'
- end
- end
+ describe 'reference methods' do
+ let_it_be(:owner) { create(:user, name: 'Gitlab') }
+ let_it_be(:namespace) { create(:namespace, name: 'Sample namespace', path: 'sample-namespace', owner: owner) }
+ let_it_be(:project) { create(:project, name: 'Sample project', path: 'sample-project', namespace: namespace) }
+ let_it_be(:group) { create(:group, name: 'Group', path: 'sample-group') }
+ let_it_be(:another_project) { create(:project, namespace: namespace) }
+ let_it_be(:another_namespace_project) { create(:project, name: 'another-project') }
- describe '#to_reference' do
- let(:owner) { create(:user, name: 'Gitlab') }
- let(:namespace) { create(:namespace, path: 'sample-namespace', owner: owner) }
- let(:project) { create(:project, path: 'sample-project', namespace: namespace) }
- let(:group) { create(:group, name: 'Group', path: 'sample-group') }
+ describe '#to_reference' do
+ it 'returns the path with reference_postfix' do
+ expect(project.to_reference).to eq("#{project.full_path}>")
+ end
- context 'when nil argument' do
- it 'returns nil' do
- expect(project.to_reference).to be_nil
+ it 'returns the path with reference_postfix when arg is self' do
+ expect(project.to_reference(project)).to eq("#{project.full_path}>")
end
- end
- context 'when full is true' do
- it 'returns complete path to the project' do
- expect(project.to_reference(full: true)).to eq 'sample-namespace/sample-project'
- expect(project.to_reference(project, full: true)).to eq 'sample-namespace/sample-project'
- expect(project.to_reference(group, full: true)).to eq 'sample-namespace/sample-project'
+ it 'returns the full_path with reference_postfix when full' do
+ expect(project.to_reference(full: true)).to eq("#{project.full_path}>")
end
- end
- context 'when same project argument' do
- it 'returns nil' do
- expect(project.to_reference(project)).to be_nil
+ it 'returns the full_path with reference_postfix when cross-project' do
+ expect(project.to_reference(build_stubbed(:project))).to eq("#{project.full_path}>")
end
end
- context 'when cross namespace project argument' do
- let(:another_namespace_project) { create(:project, name: 'another-project') }
-
- it 'returns complete path to the project' do
- expect(project.to_reference(another_namespace_project)).to eq 'sample-namespace/sample-project'
+ describe '#to_reference_base' do
+ context 'when nil argument' do
+ it 'returns nil' do
+ expect(project.to_reference_base).to be_nil
+ end
end
- end
- context 'when same namespace / cross-project argument' do
- let(:another_project) { create(:project, namespace: namespace) }
+ context 'when full is true' do
+ it 'returns complete path to the project', :aggregate_failures do
+ be_full_path = eq('sample-namespace/sample-project')
- it 'returns path to the project' do
- expect(project.to_reference(another_project)).to eq 'sample-project'
+ expect(project.to_reference_base(full: true)).to be_full_path
+ expect(project.to_reference_base(project, full: true)).to be_full_path
+ expect(project.to_reference_base(group, full: true)).to be_full_path
+ end
end
- end
- context 'when different namespace / cross-project argument' do
- let(:another_namespace) { create(:namespace, path: 'another-namespace', owner: owner) }
- let(:another_project) { create(:project, path: 'another-project', namespace: another_namespace) }
+ context 'when same project argument' do
+ it 'returns nil' do
+ expect(project.to_reference_base(project)).to be_nil
+ end
+ end
- it 'returns full path to the project' do
- expect(project.to_reference(another_project)).to eq 'sample-namespace/sample-project'
+ context 'when cross namespace project argument' do
+ it 'returns complete path to the project' do
+ expect(project.to_reference_base(another_namespace_project)).to eq 'sample-namespace/sample-project'
+ end
end
- end
- context 'when argument is a namespace' do
- context 'with same project path' do
+ context 'when same namespace / cross-project argument' do
it 'returns path to the project' do
- expect(project.to_reference(namespace)).to eq 'sample-project'
+ expect(project.to_reference_base(another_project)).to eq 'sample-project'
end
end
- context 'with different project path' do
+ context 'when different namespace / cross-project argument with same owner' do
+ let(:another_namespace_same_owner) { create(:namespace, path: 'another-namespace', owner: owner) }
+ let(:another_project_same_owner) { create(:project, path: 'another-project', namespace: another_namespace_same_owner) }
+
it 'returns full path to the project' do
- expect(project.to_reference(group)).to eq 'sample-namespace/sample-project'
+ expect(project.to_reference_base(another_project_same_owner)).to eq 'sample-namespace/sample-project'
end
end
- end
- end
- describe '#to_human_reference' do
- let(:owner) { create(:user, name: 'Gitlab') }
- let(:namespace) { create(:namespace, name: 'Sample namespace', owner: owner) }
- let(:project) { create(:project, name: 'Sample project', namespace: namespace) }
+ context 'when argument is a namespace' do
+ context 'with same project path' do
+ it 'returns path to the project' do
+ expect(project.to_reference_base(namespace)).to eq 'sample-project'
+ end
+ end
- context 'when nil argument' do
- it 'returns nil' do
- expect(project.to_human_reference).to be_nil
+ context 'with different project path' do
+ it 'returns full path to the project' do
+ expect(project.to_reference_base(group)).to eq 'sample-namespace/sample-project'
+ end
+ end
end
end
- context 'when same project argument' do
- it 'returns nil' do
- expect(project.to_human_reference(project)).to be_nil
+ describe '#to_human_reference' do
+ context 'when nil argument' do
+ it 'returns nil' do
+ expect(project.to_human_reference).to be_nil
+ end
end
- end
-
- context 'when cross namespace project argument' do
- let(:another_namespace_project) { create(:project, name: 'another-project') }
- it 'returns complete name with namespace of the project' do
- expect(project.to_human_reference(another_namespace_project)).to eq 'Gitlab / Sample project'
+ context 'when same project argument' do
+ it 'returns nil' do
+ expect(project.to_human_reference(project)).to be_nil
+ end
end
- end
- context 'when same namespace / cross-project argument' do
- let(:another_project) { create(:project, namespace: namespace) }
+ context 'when cross namespace project argument' do
+ it 'returns complete name with namespace of the project' do
+ expect(project.to_human_reference(another_namespace_project)).to eq 'Gitlab / Sample project'
+ end
+ end
- it 'returns name of the project' do
- expect(project.to_human_reference(another_project)).to eq 'Sample project'
+ context 'when same namespace / cross-project argument' do
+ it 'returns name of the project' do
+ expect(project.to_human_reference(another_project)).to eq 'Sample project'
+ end
end
end
end
diff --git a/spec/presenters/sentry_detailed_error_presenter_spec.rb b/spec/presenters/sentry_error_presenter_spec.rb
index e483b6d41a1..5f3f1d33b86 100644
--- a/spec/presenters/sentry_detailed_error_presenter_spec.rb
+++ b/spec/presenters/sentry_error_presenter_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe SentryDetailedErrorPresenter do
+describe SentryErrorPresenter do
let(:error) { build(:detailed_error_tracking_error) }
let(:presenter) { described_class.new(error) }
@@ -10,7 +10,7 @@ describe SentryDetailedErrorPresenter do
subject { presenter.frequency }
it 'returns an array of frequency structs' do
- expect(subject).to include(a_kind_of(SentryDetailedErrorPresenter::FrequencyStruct))
+ expect(subject).to include(a_kind_of(SentryErrorPresenter::FrequencyStruct))
end
it 'converts the times into UTC time objects' do
diff --git a/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb b/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb
new file mode 100644
index 00000000000..e68025bf01b
--- /dev/null
+++ b/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb
@@ -0,0 +1,191 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe 'sentry errors requests' do
+ include GraphqlHelpers
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:project_setting) { create(:project_error_tracking_setting, project: project) }
+ let_it_be(:current_user) { project.owner }
+
+ let(:query) do
+ graphql_query_for(
+ 'project',
+ { 'fullPath' => project.full_path },
+ query_graphql_field('sentryErrors', {}, fields)
+ )
+ end
+
+ describe 'getting a detailed sentry error' do
+ let_it_be(:sentry_detailed_error) { build(:detailed_error_tracking_error) }
+ let(:sentry_gid) { sentry_detailed_error.to_global_id.to_s }
+
+ let(:detailed_fields) do
+ all_graphql_fields_for('SentryDetailedError'.classify)
+ end
+
+ let(:fields) do
+ query_graphql_field('detailedError', { id: sentry_gid }, detailed_fields)
+ end
+
+ let(:error_data) { graphql_data.dig('project', 'sentryErrors', 'detailedError') }
+
+ it_behaves_like 'a working graphql query' do
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+ end
+
+ context 'when data is loading via reactive cache' do
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ it "is expected to return an empty error" do
+ expect(error_data).to eq nil
+ end
+ end
+
+ context 'reactive cache returns data' do
+ before do
+ allow_any_instance_of(ErrorTracking::ProjectErrorTrackingSetting)
+ .to receive(:issue_details)
+ .and_return({ issue: sentry_detailed_error })
+
+ post_graphql(query, current_user: current_user)
+ end
+
+ let(:sentry_error) { sentry_detailed_error }
+ let(:error) { error_data }
+
+ it_behaves_like 'setting sentry error data'
+
+ it 'is expected to return the frequency correctly' do
+ aggregate_failures 'it returns the frequency correctly' do
+ expect(error_data['frequency'].count).to eql sentry_detailed_error.frequency.count
+
+ first_frequency = error_data['frequency'].first
+ expect(Time.parse(first_frequency['time'])).to eql Time.at(sentry_detailed_error.frequency[0][0], in: 0)
+ expect(first_frequency['count']).to eql sentry_detailed_error.frequency[0][1]
+ end
+ end
+
+ context 'user does not have permission' do
+ let(:current_user) { create(:user) }
+
+ it "is expected to return an empty error" do
+ expect(error_data).to eq nil
+ end
+ end
+ end
+
+ context 'sentry api returns an error' do
+ before do
+ expect_any_instance_of(ErrorTracking::ProjectErrorTrackingSetting)
+ .to receive(:issue_details)
+ .and_return({ error: 'error message' })
+
+ post_graphql(query, current_user: current_user)
+ end
+
+ it 'is expected to handle the error and return nil' do
+ expect(error_data).to eq nil
+ end
+ end
+ end
+
+ describe 'getting an errors list' do
+ let_it_be(:sentry_error) { build(:error_tracking_error) }
+ let_it_be(:pagination) do
+ {
+ 'next' => { 'cursor' => '2222' },
+ 'previous' => { 'cursor' => '1111' }
+ }
+ end
+
+ let(:fields) do
+ <<~QUERY
+ errors {
+ nodes {
+ #{all_graphql_fields_for('SentryError'.classify)}
+ }
+ pageInfo {
+ hasNextPage
+ hasPreviousPage
+ startCursor
+ endCursor
+ }
+ }
+ QUERY
+ end
+
+ let(:error_data) { graphql_data.dig('project', 'sentryErrors', 'errors', 'nodes') }
+ let(:pagination_data) { graphql_data.dig('project', 'sentryErrors', 'errors', 'pageInfo') }
+
+ it_behaves_like 'a working graphql query' do
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+ end
+
+ context 'when data is loading via reactive cache' do
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ it "is expected to return nil" do
+ expect(error_data).to eq nil
+ end
+ end
+
+ context 'reactive cache returns data' do
+ before do
+ expect_any_instance_of(ErrorTracking::ProjectErrorTrackingSetting)
+ .to receive(:list_sentry_issues)
+ .and_return({ issues: [sentry_error], pagination: pagination })
+
+ post_graphql(query, current_user: current_user)
+ end
+
+ let(:error) { error_data.first }
+
+ it 'is expected to return an array of data' do
+ expect(error_data).to be_a Array
+ expect(error_data.count).to eq 1
+ end
+
+ it_behaves_like 'setting sentry error data'
+
+ it 'sets the pagination correctly' do
+ expect(pagination_data['startCursor']).to eq(pagination['previous']['cursor'])
+ expect(pagination_data['endCursor']).to eq(pagination['next']['cursor'])
+ end
+
+ it 'is expected to return the frequency correctly' do
+ aggregate_failures 'it returns the frequency correctly' do
+ error = error_data.first
+
+ expect(error['frequency'].count).to eql sentry_error.frequency.count
+
+ first_frequency = error['frequency'].first
+
+ expect(Time.parse(first_frequency['time'])).to eql Time.at(sentry_error.frequency[0][0], in: 0)
+ expect(first_frequency['count']).to eql sentry_error.frequency[0][1]
+ end
+ end
+ end
+
+ context "sentry api itself errors out" do
+ before do
+ expect_any_instance_of(ErrorTracking::ProjectErrorTrackingSetting)
+ .to receive(:list_sentry_issues)
+ .and_return({ error: 'error message' })
+
+ post_graphql(query, current_user: current_user)
+ end
+
+ it 'is expected to handle the error and return nil' do
+ expect(error_data).to eq nil
+ end
+ end
+ end
+end
diff --git a/spec/requests/self_monitoring_project_spec.rb b/spec/requests/self_monitoring_project_spec.rb
index 1da0be882d0..5e46645e7a0 100644
--- a/spec/requests/self_monitoring_project_spec.rb
+++ b/spec/requests/self_monitoring_project_spec.rb
@@ -68,6 +68,8 @@ describe 'Self-Monitoring project requests' do
let(:job_id) { nil }
it 'returns bad_request' do
+ create(:application_setting)
+
subject
aggregate_failures do
@@ -81,11 +83,10 @@ describe 'Self-Monitoring project requests' do
end
context 'when self-monitoring project exists' do
- let(:project) { build(:project) }
+ let(:project) { create(:project) }
before do
- stub_application_setting(self_monitoring_project_id: 1)
- stub_application_setting(self_monitoring_project: project)
+ create(:application_setting, self_monitoring_project_id: project.id)
end
it 'does not need job_id' do
@@ -94,7 +95,7 @@ describe 'Self-Monitoring project requests' do
aggregate_failures do
expect(response).to have_gitlab_http_status(:success)
expect(json_response).to eq(
- 'project_id' => 1,
+ 'project_id' => project.id,
'project_full_path' => project.full_path
)
end
@@ -106,7 +107,7 @@ describe 'Self-Monitoring project requests' do
aggregate_failures do
expect(response).to have_gitlab_http_status(:success)
expect(json_response).to eq(
- 'project_id' => 1,
+ 'project_id' => project.id,
'project_full_path' => project.full_path
)
end
@@ -179,7 +180,7 @@ describe 'Self-Monitoring project requests' do
context 'when self-monitoring project exists and job does not exist' do
before do
- stub_application_setting(self_monitoring_project_id: 1)
+ create(:application_setting, self_monitoring_project_id: create(:project).id)
end
it 'returns bad_request' do
@@ -196,6 +197,10 @@ describe 'Self-Monitoring project requests' do
end
context 'when self-monitoring project does not exist' do
+ before do
+ create(:application_setting)
+ end
+
it 'does not need job_id' do
get status_delete_self_monitoring_project_admin_application_settings_path
diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb
index e151a934591..31b0290bb15 100644
--- a/spec/support/matchers/graphql_matchers.rb
+++ b/spec/support/matchers/graphql_matchers.rb
@@ -108,6 +108,12 @@ RSpec::Matchers.define :have_graphql_resolver do |expected|
end
end
+RSpec::Matchers.define :have_graphql_extension do |expected|
+ match do |field|
+ expect(field.metadata[:type_class].extensions).to include(expected)
+ end
+end
+
RSpec::Matchers.define :expose_permissions_using do |expected|
match do |type|
permission_field = type.fields['userPermissions']
diff --git a/spec/support/shared_examples/error_tracking_shared_examples.rb b/spec/support/shared_examples/error_tracking_shared_examples.rb
new file mode 100644
index 00000000000..86134fa7fd1
--- /dev/null
+++ b/spec/support/shared_examples/error_tracking_shared_examples.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'setting sentry error data' do
+ it 'sets the sentry error data correctly' do
+ aggregate_failures 'testing the sentry error is correct' do
+ expect(error['id']).to eql sentry_error.to_global_id.to_s
+ expect(error['sentryId']).to eql sentry_error.id.to_s
+ expect(error['status']).to eql sentry_error.status.upcase
+ expect(error['firstSeen']).to eql sentry_error.first_seen
+ expect(error['lastSeen']).to eql sentry_error.last_seen
+ end
+ end
+end