Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
27c6c4bf06
commit
1691cbe307
|
@ -15,4 +15,8 @@ gitlab_danger.rule_names.each do |file|
|
|||
danger.import_dangerfile(path: File.join('danger', file))
|
||||
end
|
||||
|
||||
markdown("**If needed, you can retry the [`danger-review` job](#{ENV['CI_JOB_URL']}) that generated this comment.**") if gitlab_danger.ci?
|
||||
anything_to_post = status_report.values.any? { |data| data.any? }
|
||||
|
||||
if gitlab_danger.ci? && anything_to_post
|
||||
markdown("**If needed, you can retry the [`danger-review` job](#{ENV['CI_JOB_URL']}) that generated this comment.**")
|
||||
end
|
||||
|
|
|
@ -30,6 +30,11 @@ export default {
|
|||
required: false,
|
||||
default: SIMPLE_BLOB_VIEWER,
|
||||
},
|
||||
hasRenderError: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -75,6 +80,7 @@ export default {
|
|||
v-if="showDefaultActions"
|
||||
:raw-path="blob.rawPath"
|
||||
:active-viewer="viewer"
|
||||
:has-render-error="hasRenderError"
|
||||
@copy="proxyCopyRequest"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -27,6 +27,11 @@ export default {
|
|||
default: SIMPLE_BLOB_VIEWER,
|
||||
required: false,
|
||||
},
|
||||
hasRenderError: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
downloadUrl() {
|
||||
|
@ -44,11 +49,13 @@ export default {
|
|||
<template>
|
||||
<gl-button-group>
|
||||
<gl-deprecated-button
|
||||
v-if="!hasRenderError"
|
||||
v-gl-tooltip.hover
|
||||
:aria-label="$options.BTN_COPY_CONTENTS_TITLE"
|
||||
:title="$options.BTN_COPY_CONTENTS_TITLE"
|
||||
:disabled="copyDisabled"
|
||||
data-clipboard-target="#blob-code-content"
|
||||
data-testid="copyContentsButton"
|
||||
>
|
||||
<gl-icon name="copy-to-clipboard" :size="14" />
|
||||
</gl-deprecated-button>
|
||||
|
|
|
@ -74,6 +74,9 @@ export default {
|
|||
canBeCloned() {
|
||||
return this.snippet.sshUrlToRepo || this.snippet.httpUrlToRepo;
|
||||
},
|
||||
hasRenderError() {
|
||||
return Boolean(this.viewer.renderError);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
switchViewer(newViewer) {
|
||||
|
@ -92,7 +95,12 @@ export default {
|
|||
<div>
|
||||
<blob-embeddable v-if="embeddable" class="mb-3" :url="snippet.webUrl" />
|
||||
<article class="file-holder snippet-file-content">
|
||||
<blob-header :blob="blob" :active-viewer-type="viewer.type" @viewer-changed="switchViewer">
|
||||
<blob-header
|
||||
:blob="blob"
|
||||
:active-viewer-type="viewer.type"
|
||||
:has-render-error="hasRenderError"
|
||||
@viewer-changed="switchViewer"
|
||||
>
|
||||
<template #actions>
|
||||
<clone-dropdown-button
|
||||
v-if="canBeCloned"
|
||||
|
|
|
@ -127,7 +127,7 @@ export default {
|
|||
</button>
|
||||
<span v-if="!rebasingError" class="bold">{{
|
||||
__(
|
||||
'Fast-forward merge is not possible. Rebase the source branch onto the target branch or merge target branch into source branch to allow this merge request to be merged.',
|
||||
'Fast-forward merge is not possible. Rebase the source branch onto the target branch.',
|
||||
)
|
||||
}}</span>
|
||||
<span v-else class="bold danger">{{ rebasingError }}</span>
|
||||
|
|
|
@ -425,7 +425,6 @@ img.emoji {
|
|||
.append-right-32 { margin-right: 32px; }
|
||||
.append-right-48 { margin-right: 48px; }
|
||||
.prepend-right-32 { margin-right: 32px; }
|
||||
.append-bottom-2 { margin-bottom: 2px; }
|
||||
.append-bottom-4 { margin-bottom: $gl-padding-4; }
|
||||
.append-bottom-5 { margin-bottom: 5px; }
|
||||
.append-bottom-8 { margin-bottom: $grid-size; }
|
||||
|
|
|
@ -1,22 +1,4 @@
|
|||
.alert-management-list {
|
||||
// consider adding these stateful variants to @gitlab-ui
|
||||
// https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/1178
|
||||
.hover-bg-blue-50:hover {
|
||||
background-color: $blue-50;
|
||||
}
|
||||
|
||||
.hover-gl-cursor-pointer:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hover-gl-border-b-solid:hover {
|
||||
@include gl-border-b-solid;
|
||||
}
|
||||
|
||||
.hover-gl-border-blue-200:hover {
|
||||
border-color: $blue-200;
|
||||
}
|
||||
|
||||
// these styles need to be deleted once GlTable component looks in GitLab same as in @gitlab/ui
|
||||
table {
|
||||
color: $gray-700;
|
||||
|
|
|
@ -6,7 +6,6 @@ module Projects
|
|||
before_action :authenticate_user!
|
||||
before_action :check_issues_available!
|
||||
before_action :authorize_read_project!
|
||||
before_action :jira_import_enabled?
|
||||
before_action :jira_integration_configured?
|
||||
before_action :authorize_admin_project!, only: [:import]
|
||||
|
||||
|
@ -44,12 +43,6 @@ module Projects
|
|||
|
||||
private
|
||||
|
||||
def jira_import_enabled?
|
||||
return if @project.jira_issues_import_feature_flag_enabled?
|
||||
|
||||
redirect_to project_issues_path(@project)
|
||||
end
|
||||
|
||||
def jira_integration_configured?
|
||||
return if Feature.enabled?(:jira_issue_import_vue, @project, default_enabled: true)
|
||||
return if @project.jira_service
|
||||
|
|
|
@ -14,8 +14,6 @@ module Resolvers
|
|||
end
|
||||
|
||||
def authorized_resource?(project)
|
||||
return false unless project.jira_issues_import_feature_flag_enabled?
|
||||
|
||||
context[:current_user].present? && Ability.allowed?(context[:current_user], :read_project, project)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,7 +20,7 @@ module Resolvers
|
|||
end
|
||||
|
||||
def authorized_resource?(project)
|
||||
Feature.enabled?(:jira_issue_import, project) && Ability.allowed?(context[:current_user], :admin_project, project)
|
||||
Ability.allowed?(context[:current_user], :admin_project, project)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -3,6 +3,13 @@
|
|||
module NotesHelper
|
||||
MAX_PRERENDERED_NOTES = 10
|
||||
|
||||
def note_target_title(note)
|
||||
# The design title is already present in `Event#note_target_reference`.
|
||||
return if note.nil? || note.for_design?
|
||||
|
||||
note.title
|
||||
end
|
||||
|
||||
def note_target_fields(note)
|
||||
if note.noteable
|
||||
hidden_field_tag(:target_type, note.noteable.class.name.underscore) +
|
||||
|
|
|
@ -808,10 +808,6 @@ class Project < ApplicationRecord
|
|||
Feature.enabled?(:context_commits, default_enabled: true)
|
||||
end
|
||||
|
||||
def jira_issues_import_feature_flag_enabled?
|
||||
Feature.enabled?(:jira_issue_import, self, default_enabled: true)
|
||||
end
|
||||
|
||||
# LFS and hashed repository storage are required for using Design Management.
|
||||
def design_management_enabled?
|
||||
lfs_enabled? && hashed_storage?(:repository)
|
||||
|
@ -900,7 +896,6 @@ class Project < ApplicationRecord
|
|||
end
|
||||
|
||||
def validate_jira_import_settings!(user: nil)
|
||||
raise Projects::ImportService::Error, _('Jira import feature is disabled.') unless jira_issues_import_feature_flag_enabled?
|
||||
raise Projects::ImportService::Error, _('Jira integration not configured.') unless jira_service&.active?
|
||||
|
||||
if user
|
||||
|
@ -1015,7 +1010,7 @@ class Project < ApplicationRecord
|
|||
end
|
||||
|
||||
def jira_import?
|
||||
import_type == 'jira' && latest_jira_import.present? && jira_issues_import_feature_flag_enabled?
|
||||
import_type == 'jira' && latest_jira_import.present?
|
||||
end
|
||||
|
||||
def gitlab_project_import?
|
||||
|
|
|
@ -1818,7 +1818,7 @@ class User < ApplicationRecord
|
|||
def update_highest_role?
|
||||
return false unless persisted?
|
||||
|
||||
(previous_changes.keys & %w(state user_type ghost)).any?
|
||||
(previous_changes.keys & %w(state user_type)).any?
|
||||
end
|
||||
|
||||
def update_highest_role_attribute
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
%span.event-type.d-inline-block.append-right-4{ class: event.action_name }
|
||||
= event.action_name
|
||||
= event_note_title_html(event)
|
||||
%span.event-target-title.append-right-4{ dir: "auto" }
|
||||
= """.html_safe + event.target.title + """.html_safe
|
||||
- title = note_target_title(event.target)
|
||||
- if title.present?
|
||||
%span.event-target-title.append-right-4{ dir: "auto" }
|
||||
= """.html_safe + title + """.html_safe
|
||||
|
||||
= render "events/event_scope", event: event
|
||||
|
||||
|
|
|
@ -1,22 +1,14 @@
|
|||
- type = local_assigns.fetch(:type, :icon)
|
||||
|
||||
- if @project.jira_issues_import_feature_flag_enabled?
|
||||
.dropdown.btn-group
|
||||
%button.btn.rounded-right.text-center{ class: ('has-tooltip' if type == :icon), title: (_('Import issues') if type == :icon),
|
||||
data: { toggle: 'dropdown' }, 'aria-label' => _('Import issues'), 'aria-haspopup' => 'true', 'aria-expanded' => 'false' }
|
||||
- if type == :icon
|
||||
= sprite_icon('import')
|
||||
- else
|
||||
= _('Import issues')
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
%button.btn{ data: { toggle: 'modal', target: '.issues-import-modal' } }
|
||||
= _('Import CSV')
|
||||
%li= link_to _('Import from Jira'), project_import_jira_path(@project)
|
||||
- else
|
||||
%button.csv-import-button.btn{ title: _('Import CSV'), class: ('has-tooltip' if type == :icon),
|
||||
data: { toggle: 'modal', target: '.issues-import-modal' } }
|
||||
.dropdown.btn-group
|
||||
%button.btn.rounded-right.text-center{ class: ('has-tooltip' if type == :icon), title: (_('Import issues') if type == :icon),
|
||||
data: { toggle: 'dropdown' }, 'aria-label' => _('Import issues'), 'aria-haspopup' => 'true', 'aria-expanded' => 'false' }
|
||||
- if type == :icon
|
||||
= sprite_icon('import')
|
||||
- else
|
||||
= _('Import CSV')
|
||||
= _('Import issues')
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
%button.btn{ data: { toggle: 'modal', target: '.issues-import-modal' } }
|
||||
= _('Import CSV')
|
||||
%li= link_to _('Import from Jira'), project_import_jira_path(@project)
|
||||
|
|
|
@ -6,11 +6,10 @@
|
|||
= content_for :meta_tags do
|
||||
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{@project.name} issues")
|
||||
|
||||
- if @project.jira_issues_import_feature_flag_enabled?
|
||||
.js-projects-issues-root{ data: { can_edit: can?(current_user, :admin_project, @project).to_s,
|
||||
is_jira_configured: @project.jira_service.present?.to_s,
|
||||
issues_path: project_issues_path(@project),
|
||||
project_path: @project.full_path } }
|
||||
.js-projects-issues-root{ data: { can_edit: can?(current_user, :admin_project, @project).to_s,
|
||||
is_jira_configured: @project.jira_service.present?.to_s,
|
||||
issues_path: project_issues_path(@project),
|
||||
project_path: @project.full_path } }
|
||||
|
||||
- if project_issues(@project).exists?
|
||||
.top-area
|
||||
|
|
|
@ -27,7 +27,6 @@ module Gitlab
|
|||
|
||||
def can_import?(project)
|
||||
return false unless project
|
||||
return false unless project.jira_issues_import_feature_flag_enabled?
|
||||
|
||||
project.latest_jira_import&.started?
|
||||
end
|
||||
|
|
|
@ -25,7 +25,6 @@ module Gitlab
|
|||
|
||||
def start_import
|
||||
return false unless project
|
||||
return false unless project.jira_issues_import_feature_flag_enabled?
|
||||
return true if start(project.latest_jira_import)
|
||||
|
||||
Gitlab::Import::Logger.info(
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Hid copy contents button when blob has rendering error
|
||||
merge_request: 32632
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove obsolete users.ghost column
|
||||
merge_request: 32957
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Improve fast-forward merge is not possible message
|
||||
merge_request: 22834
|
||||
author: Ben Bodenmiller
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix filename duplication in design notes in activity feeds
|
||||
merge_request: 32823
|
||||
author: Arun Kumar Mohan
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add btree_gist PGSQL extension and add DB constraints for Iteration date ranges
|
||||
merge_request: 32335
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Use build_stubbed to avoid interacting with the DB in todos helper specs
|
||||
merge_request: 32906
|
||||
author: Arun Kumar Mohan
|
||||
type: performance
|
|
@ -465,7 +465,6 @@ tables:
|
|||
- auditor
|
||||
- require_two_factor_authentication_from_group
|
||||
- two_factor_grace_period
|
||||
- ghost
|
||||
- last_activity_on
|
||||
- notified_of_own_activity
|
||||
- user_type
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class EnableBtreeGistExtension < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
execute 'CREATE EXTENSION IF NOT EXISTS btree_gist'
|
||||
end
|
||||
|
||||
def down
|
||||
execute 'DROP EXTENSION IF EXISTS btree_gist'
|
||||
end
|
||||
end
|
|
@ -0,0 +1,39 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class IterationDateRangeConstraint < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
execute <<~SQL
|
||||
ALTER TABLE sprints
|
||||
ADD CONSTRAINT iteration_start_and_due_daterange_project_id_constraint
|
||||
EXCLUDE USING gist
|
||||
( project_id WITH =,
|
||||
daterange(start_date, due_date, '[]') WITH &&
|
||||
)
|
||||
WHERE (project_id IS NOT NULL)
|
||||
SQL
|
||||
|
||||
execute <<~SQL
|
||||
ALTER TABLE sprints
|
||||
ADD CONSTRAINT iteration_start_and_due_daterange_group_id_constraint
|
||||
EXCLUDE USING gist
|
||||
( group_id WITH =,
|
||||
daterange(start_date, due_date, '[]') WITH &&
|
||||
)
|
||||
WHERE (group_id IS NOT NULL)
|
||||
SQL
|
||||
end
|
||||
|
||||
def down
|
||||
execute <<~SQL
|
||||
ALTER TABLE sprints
|
||||
DROP CONSTRAINT IF EXISTS iteration_start_and_due_daterange_project_id_constraint
|
||||
SQL
|
||||
|
||||
execute <<~SQL
|
||||
ALTER TABLE sprints
|
||||
DROP CONSTRAINT IF EXISTS iteration_start_and_due_daterange_group_id_constraint
|
||||
SQL
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RenameUserTypeIndex < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_index :users, [:state, :user_type], name: 'index_users_on_state_and_user_type'
|
||||
remove_concurrent_index_by_name :users, 'index_users_on_state_and_user_type_internal'
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index :users, [:state, :user_type], where: 'ghost IS NOT TRUE', name: 'index_users_on_state_and_user_type_internal'
|
||||
remove_concurrent_index_by_name :users, 'index_users_on_state_and_user_type'
|
||||
end
|
||||
end
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DropUsersGhostColumn < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
remove_concurrent_index_by_name :users, 'index_users_on_ghost'
|
||||
|
||||
with_lock_retries do
|
||||
remove_column :users, :ghost
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
unless column_exists?(:users, :ghost)
|
||||
with_lock_retries do
|
||||
add_column :users, :ghost, :boolean # rubocop:disable Migration/AddColumnsToWideTables
|
||||
end
|
||||
end
|
||||
|
||||
execute 'UPDATE users set ghost = TRUE WHERE user_type = 5'
|
||||
|
||||
add_concurrent_index :users, :ghost
|
||||
end
|
||||
end
|
|
@ -2,6 +2,8 @@ SET search_path=public;
|
|||
|
||||
CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS btree_gist WITH SCHEMA public;
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA public;
|
||||
|
||||
CREATE TABLE public.abuse_reports (
|
||||
|
@ -6806,7 +6808,6 @@ CREATE TABLE public.users (
|
|||
auditor boolean DEFAULT false NOT NULL,
|
||||
require_two_factor_authentication_from_group boolean DEFAULT false NOT NULL,
|
||||
two_factor_grace_period integer DEFAULT 48 NOT NULL,
|
||||
ghost boolean,
|
||||
last_activity_on date,
|
||||
notified_of_own_activity boolean,
|
||||
preferred_language character varying,
|
||||
|
@ -8410,6 +8411,12 @@ ALTER TABLE ONLY public.issue_user_mentions
|
|||
ALTER TABLE ONLY public.issues
|
||||
ADD CONSTRAINT issues_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY public.sprints
|
||||
ADD CONSTRAINT iteration_start_and_due_daterange_group_id_constraint EXCLUDE USING gist (group_id WITH =, daterange(start_date, due_date, '[]'::text) WITH &&) WHERE ((group_id IS NOT NULL));
|
||||
|
||||
ALTER TABLE ONLY public.sprints
|
||||
ADD CONSTRAINT iteration_start_and_due_daterange_project_id_constraint EXCLUDE USING gist (project_id WITH =, daterange(start_date, due_date, '[]'::text) WITH &&) WHERE ((project_id IS NOT NULL));
|
||||
|
||||
ALTER TABLE ONLY public.jira_connect_installations
|
||||
ADD CONSTRAINT jira_connect_installations_pkey PRIMARY KEY (id);
|
||||
|
||||
|
@ -10826,8 +10833,6 @@ CREATE INDEX index_users_on_email_trigram ON public.users USING gin (email publi
|
|||
|
||||
CREATE INDEX index_users_on_feed_token ON public.users USING btree (feed_token);
|
||||
|
||||
CREATE INDEX index_users_on_ghost ON public.users USING btree (ghost);
|
||||
|
||||
CREATE INDEX index_users_on_group_view ON public.users USING btree (group_view);
|
||||
|
||||
CREATE INDEX index_users_on_incoming_email_token ON public.users USING btree (incoming_email_token);
|
||||
|
@ -10844,7 +10849,7 @@ CREATE UNIQUE INDEX index_users_on_reset_password_token ON public.users USING bt
|
|||
|
||||
CREATE INDEX index_users_on_state ON public.users USING btree (state);
|
||||
|
||||
CREATE INDEX index_users_on_state_and_user_type_internal ON public.users USING btree (state, user_type) WHERE (ghost IS NOT TRUE);
|
||||
CREATE INDEX index_users_on_state_and_user_type ON public.users USING btree (state, user_type);
|
||||
|
||||
CREATE UNIQUE INDEX index_users_on_static_object_token ON public.users USING btree (static_object_token);
|
||||
|
||||
|
@ -13922,8 +13927,12 @@ COPY "schema_migrations" (version) FROM STDIN;
|
|||
20200514000009
|
||||
20200514000132
|
||||
20200514000340
|
||||
20200515152649
|
||||
20200515153633
|
||||
20200515155620
|
||||
20200519115908
|
||||
20200519171058
|
||||
20200525114553
|
||||
20200525121014
|
||||
\.
|
||||
|
||||
|
|
|
@ -199,6 +199,7 @@ authentication](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/configurati
|
|||
sidekiq['enable'] = false
|
||||
gitlab_workhorse['enable'] = false
|
||||
grafana['enable'] = false
|
||||
gitlab_exporter['enable'] = false
|
||||
|
||||
# If you run a separate monitoring node you can disable these services
|
||||
alertmanager['enable'] = false
|
||||
|
@ -211,7 +212,6 @@ authentication](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/configurati
|
|||
# prometheus['monitor_kubernetes'] = false
|
||||
|
||||
# If you don't want to run monitoring services uncomment the following (not recommended)
|
||||
# gitlab_exporter['enable'] = false
|
||||
# node_exporter['enable'] = false
|
||||
|
||||
# Prevent database connections during 'gitlab-ctl reconfigure'
|
||||
|
|
|
@ -310,7 +310,6 @@ include:
|
|||
- template: 'Workflows/Branch-Pipelines.gitlab-ci.yml'
|
||||
```
|
||||
|
||||
The [`MergeRequest-Pipelines` include](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates/Workflows/MergeRequest-Pipelines.gitlab-ci.yml) sets your pipelines to run for the default branch (usually `master`), tags, and
|
||||
The [`MergeRequest-Pipelines` template](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates/Workflows/MergeRequest-Pipelines.gitlab-ci.yml)
|
||||
makes your pipelines run for the default branch (usually `master`), tags, and
|
||||
all types of merge request pipelines. Use this template if you use any of the
|
||||
|
|
|
@ -257,11 +257,11 @@ Your Rails console will return the generated SQL queries.
|
|||
Example:
|
||||
|
||||
```ruby
|
||||
pry(main)> Gitlab::UsageData.count(User.active)
|
||||
(0.4ms) SELECT "features"."key" FROM "features"
|
||||
(0.7ms) SELECT MIN("users"."id") FROM "users" WHERE ("users"."state" IN ('active')) AND (ghost IS NOT TRUE) AND ("users"."user_type" IS NULL OR "users"."user_type" NOT IN (2, 1, 3))
|
||||
(0.6ms) SELECT MAX("users"."id") FROM "users" WHERE ("users"."state" IN ('active')) AND (ghost IS NOT TRUE) AND ("users"."user_type" IS NULL OR "users"."user_type" NOT IN (2, 1, 3))
|
||||
(0.5ms) SELECT COUNT("users"."id") FROM "users" WHERE ("users"."state" IN ('active')) AND (ghost IS NOT TRUE) AND ("users"."user_type" IS NULL OR "users"."user_type" NOT IN (2, 1, 3)) AND "users"."id" BETWEEN 0 AND 99999
|
||||
pry(main)> Gitlab::UsageData.count(User.active)
|
||||
(2.6ms) SELECT "features"."key" FROM "features"
|
||||
(15.3ms) SELECT MIN("users"."id") FROM "users" WHERE ("users"."state" IN ('active')) AND ("users"."user_type" IS NULL OR "users"."user_type" IN (6, 4))
|
||||
(2.4ms) SELECT MAX("users"."id") FROM "users" WHERE ("users"."state" IN ('active')) AND ("users"."user_type" IS NULL OR "users"."user_type" IN (6, 4))
|
||||
(1.9ms) SELECT COUNT("users"."id") FROM "users" WHERE ("users"."state" IN ('active')) AND ("users"."user_type" IS NULL OR "users"."user_type" IN (6, 4)) AND "users"."id" BETWEEN 1 AND 100000
|
||||
```
|
||||
|
||||
### 3. Optimize queries with #database-lab
|
||||
|
|
|
@ -315,25 +315,30 @@ the `Indexes:` section:
|
|||
```sql
|
||||
Indexes:
|
||||
"users_pkey" PRIMARY KEY, btree (id)
|
||||
"users_confirmation_token_key" UNIQUE CONSTRAINT, btree (confirmation_token)
|
||||
"users_email_key" UNIQUE CONSTRAINT, btree (email)
|
||||
"users_reset_password_token_key" UNIQUE CONSTRAINT, btree (reset_password_token)
|
||||
"index_on_users_lower_email" btree (lower(email::text))
|
||||
"index_on_users_lower_username" btree (lower(username::text))
|
||||
"index_users_on_confirmation_token" UNIQUE, btree (confirmation_token)
|
||||
"index_users_on_email" UNIQUE, btree (email)
|
||||
"index_users_on_reset_password_token" UNIQUE, btree (reset_password_token)
|
||||
"index_users_on_static_object_token" UNIQUE, btree (static_object_token)
|
||||
"index_users_on_unlock_token" UNIQUE, btree (unlock_token)
|
||||
"index_on_users_name_lower" btree (lower(name::text))
|
||||
"index_users_on_accepted_term_id" btree (accepted_term_id)
|
||||
"index_users_on_admin" btree (admin)
|
||||
"index_users_on_created_at" btree (created_at)
|
||||
"index_users_on_email_trigram" gin (email gin_trgm_ops)
|
||||
"index_users_on_feed_token" btree (feed_token)
|
||||
"index_users_on_ghost" btree (ghost)
|
||||
"index_users_on_group_view" btree (group_view)
|
||||
"index_users_on_incoming_email_token" btree (incoming_email_token)
|
||||
"index_users_on_managing_group_id" btree (managing_group_id)
|
||||
"index_users_on_name" btree (name)
|
||||
"index_users_on_name_trigram" gin (name gin_trgm_ops)
|
||||
"index_users_on_public_email" btree (public_email) WHERE public_email::text <> ''::text
|
||||
"index_users_on_state" btree (state)
|
||||
"index_users_on_state_and_internal_attrs" btree (state) WHERE ghost <> true AND support_bot <> true
|
||||
"index_users_on_support_bot" btree (support_bot)
|
||||
"index_users_on_state_and_user_type" btree (state, user_type)
|
||||
"index_users_on_unconfirmed_email" btree (unconfirmed_email) WHERE unconfirmed_email IS NOT NULL
|
||||
"index_users_on_user_type" btree (user_type)
|
||||
"index_users_on_username" btree (username)
|
||||
"index_users_on_username_trigram" gin (username gin_trgm_ops)
|
||||
"tmp_idx_on_user_id_where_bio_is_filled" btree (id) WHERE COALESCE(bio, ''::character varying)::text IS DISTINCT FROM ''::text
|
||||
```
|
||||
|
||||
Here we can see there is no index on the `twitter` column, which means
|
||||
|
|
|
@ -237,7 +237,9 @@ On the EC2 dashboard, look for Load Balancer in the left navigation bar:
|
|||
1. Click **Assign Security Groups** and select **Create a new security group**, give it a name
|
||||
(we'll use `gitlab-loadbalancer-sec-group`) and description, and allow both HTTP and HTTPS traffic
|
||||
from anywhere (`0.0.0.0/0, ::/0`). Also allow SSH traffic from a single IP address or an IP address range in CIDR notation.
|
||||
1. Click **Configure Security Settings** and select an SSL/TLS certificate from ACM or upload a certificate to IAM.
|
||||
1. Click **Configure Security Settings** and set the following:
|
||||
1. Select an SSL/TLS certificate from ACM or upload a certificate to IAM.
|
||||
1. Under **Select a Cipher**, pick a predefined security policy from the dropdown. You can see a breakdown of [Predefined SSL Security Policies for Classic Load Balancers](https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-policy-table.html) in the AWS docs. Check the GitLab codebase for a list of [supported SSL ciphers and protocols](https://gitlab.com/gitlab-org/gitlab/-/blob/9ee7ad433269b37251e0dd5b5e00a0f00d8126b4/lib/support/nginx/gitlab-ssl#L97-99).
|
||||
1. Click **Configure Health Check** and set up a health check for your EC2 instances.
|
||||
1. For **Ping Protocol**, select HTTP.
|
||||
1. For **Ping Port**, enter 80.
|
||||
|
|
|
@ -1,42 +1,36 @@
|
|||
# Akismet
|
||||
|
||||
> *Note:* Before 8.11 only issues submitted via the API and for non-project
|
||||
members were submitted to Akismet.
|
||||
|
||||
GitLab leverages [Akismet](https://akismet.com/) to protect against spam. Currently
|
||||
GitLab uses Akismet to prevent the creation of spam issues on public projects. Issues
|
||||
created via the WebUI or the API can be submitted to Akismet for review.
|
||||
created via the web UI or the API can be submitted to Akismet for review.
|
||||
|
||||
Detected spam will be rejected, and an entry in the "Spam Log" section in the
|
||||
Admin page will be created.
|
||||
|
||||
Privacy note: GitLab submits the user's IP and user agent to Akismet. Note that
|
||||
adding a user to a project will disable the Akismet check and prevent this
|
||||
from happening.
|
||||
Privacy note: GitLab submits the user's IP and user agent to Akismet.
|
||||
|
||||
NOTE: **Note:**
|
||||
In GitLab 8.11 and later, all issues are submitted to Akismet.
|
||||
In earlier GitLab versions, this only applied to API and non-project members.
|
||||
|
||||
## Configuration
|
||||
|
||||
To use Akismet:
|
||||
|
||||
1. Go to the URL: <https://akismet.com/account/>
|
||||
|
||||
1. Sign-in or create a new account.
|
||||
|
||||
1. Click on **Show** to reveal the API key.
|
||||
|
||||
1. Go to **Admin Area > Settings > Reporting** (`/admin/application_settings/reporting`).
|
||||
|
||||
1. Check the **Enable Akismet** checkbox.
|
||||
|
||||
1. Fill in the API key from step 3.
|
||||
|
||||
1. Save the configuration.
|
||||
|
||||
![Screenshot of Akismet settings](img/akismet_settings.png)
|
||||
|
||||
## Training
|
||||
|
||||
> *Note:* Training the Akismet filter is only available in 8.11 and above.
|
||||
NOTE: **Note:**
|
||||
Training the Akismet filter is only available in GitLab 8.11 and later.
|
||||
|
||||
As a way to better recognize between spam and ham, you can train the Akismet
|
||||
filter whenever there is a false positive or false negative.
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 195 KiB |
|
@ -5699,9 +5699,6 @@ msgstr ""
|
|||
msgid "Configure Prometheus"
|
||||
msgstr ""
|
||||
|
||||
msgid "Configure Security %{wordBreakOpportunity}and Compliance"
|
||||
msgstr ""
|
||||
|
||||
msgid "Configure Tracing"
|
||||
msgstr ""
|
||||
|
||||
|
@ -9291,7 +9288,7 @@ msgstr ""
|
|||
msgid "Fast-forward merge is not possible. Rebase the source branch onto %{targetBranch} to allow this merge request to be merged."
|
||||
msgstr ""
|
||||
|
||||
msgid "Fast-forward merge is not possible. Rebase the source branch onto the target branch or merge target branch into source branch to allow this merge request to be merged."
|
||||
msgid "Fast-forward merge is not possible. Rebase the source branch onto the target branch."
|
||||
msgstr ""
|
||||
|
||||
msgid "Fast-forward merge without a merge commit"
|
||||
|
@ -12204,9 +12201,6 @@ msgstr ""
|
|||
msgid "Jira Issue Import"
|
||||
msgstr ""
|
||||
|
||||
msgid "Jira import feature is disabled."
|
||||
msgstr ""
|
||||
|
||||
msgid "Jira import is already running."
|
||||
msgstr ""
|
||||
|
||||
|
@ -18994,9 +18988,6 @@ msgstr ""
|
|||
msgid "Security Dashboard"
|
||||
msgstr ""
|
||||
|
||||
msgid "Security configuration help link"
|
||||
msgstr ""
|
||||
|
||||
msgid "Security dashboard"
|
||||
msgstr ""
|
||||
|
||||
|
@ -19009,21 +19000,21 @@ msgstr ""
|
|||
msgid "SecurityConfiguration|Enabled"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Feature"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Feature documentation for %{featureName}"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Not yet enabled"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Secure features"
|
||||
msgid "SecurityConfiguration|Security Control"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Status"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityConfiguration|Testing & Compliance"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityReports|%{firstProject} and %{secondProject}"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
"@babel/preset-env": "^7.8.4",
|
||||
"@gitlab/at.js": "1.5.5",
|
||||
"@gitlab/svgs": "1.130.0",
|
||||
"@gitlab/ui": "15.5.0",
|
||||
"@gitlab/ui": "15.6.0",
|
||||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "^6.0.3",
|
||||
"@sentry/browser": "^5.10.2",
|
||||
|
|
|
@ -10,10 +10,6 @@ describe Projects::Import::JiraController do
|
|||
let_it_be(:jira_project_key) { 'Test' }
|
||||
|
||||
context 'with anonymous user' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: true)
|
||||
end
|
||||
|
||||
context 'get show' do
|
||||
it 'redirects to issues page' do
|
||||
get :show, params: { namespace_id: project.namespace, project_id: project }
|
||||
|
@ -35,195 +31,167 @@ describe Projects::Import::JiraController do
|
|||
before do
|
||||
sign_in(user)
|
||||
project.add_maintainer(user)
|
||||
stub_feature_flags(jira_issue_import_vue: false)
|
||||
stub_jira_service_test
|
||||
end
|
||||
|
||||
context 'when feature flag not enabled' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: false)
|
||||
end
|
||||
context 'when Jira service is enabled for the project' do
|
||||
let_it_be(:jira_service) { create(:jira_service, project: project) }
|
||||
|
||||
context 'get show' do
|
||||
it 'redirects to issues page' do
|
||||
get :show, params: { namespace_id: project.namespace, project_id: project }
|
||||
context 'when user is developer' do
|
||||
let_it_be(:dev) { create(:user) }
|
||||
|
||||
expect(response).to redirect_to(project_issues_path(project))
|
||||
before do
|
||||
sign_in(dev)
|
||||
project.add_developer(dev)
|
||||
end
|
||||
end
|
||||
|
||||
context 'post import' do
|
||||
it 'redirects to issues page' do
|
||||
post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: jira_project_key }
|
||||
|
||||
expect(response).to redirect_to(project_issues_path(project))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature flag enabled' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: true)
|
||||
stub_feature_flags(jira_issue_import_vue: false)
|
||||
stub_jira_service_test
|
||||
end
|
||||
|
||||
context 'when Jira service is enabled for the project' do
|
||||
let_it_be(:jira_service) { create(:jira_service, project: project) }
|
||||
|
||||
context 'when user is developer' do
|
||||
let_it_be(:dev) { create(:user) }
|
||||
|
||||
context 'get show' do
|
||||
before do
|
||||
sign_in(dev)
|
||||
project.add_developer(dev)
|
||||
get :show, params: { namespace_id: project.namespace.to_param, project_id: project }
|
||||
end
|
||||
|
||||
context 'get show' do
|
||||
before do
|
||||
get :show, params: { namespace_id: project.namespace.to_param, project_id: project }
|
||||
end
|
||||
it 'does not query Jira service' do
|
||||
expect(project).not_to receive(:jira_service)
|
||||
end
|
||||
|
||||
it 'does not query Jira service' do
|
||||
expect(project).not_to receive(:jira_service)
|
||||
it 'renders show template' do
|
||||
expect(response).to render_template(:show)
|
||||
expect(assigns(:jira_projects)).not_to be_present
|
||||
end
|
||||
end
|
||||
|
||||
context 'post import' do
|
||||
it 'returns 404' do
|
||||
post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: jira_project_key }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when issues disabled' do
|
||||
let_it_be(:disabled_issues_project) { create(:project, :public, :issues_disabled) }
|
||||
|
||||
context 'get show' do
|
||||
it 'returs 404' do
|
||||
get :show, params: { namespace_id: project.namespace.to_param, project_id: disabled_issues_project }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
context 'post import' do
|
||||
it 'returs 404' do
|
||||
post :import, params: { namespace_id: disabled_issues_project.namespace, project_id: disabled_issues_project, jira_project_key: jira_project_key }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when running Jira import first time' do
|
||||
context 'get show' do
|
||||
before do
|
||||
allow(JIRA::Resource::Project).to receive(:all).and_return(jira_projects)
|
||||
|
||||
expect(project.jira_imports).to be_empty
|
||||
|
||||
get :show, params: { namespace_id: project.namespace.to_param, project_id: project }
|
||||
end
|
||||
|
||||
context 'when no projects have been retrieved from Jira' do
|
||||
let(:jira_projects) { [] }
|
||||
|
||||
it 'render an error message' do
|
||||
expect(flash[:alert]).to eq('No projects have been returned from Jira. Please check your Jira configuration.')
|
||||
expect(response).to render_template(:show)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when projects retrieved from Jira' do
|
||||
let(:jira_projects) { [double(name: 'FOO project', key: 'FOO')] }
|
||||
|
||||
it 'renders show template' do
|
||||
expect(response).to render_template(:show)
|
||||
expect(assigns(:jira_projects)).not_to be_present
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'post import' do
|
||||
context 'when Jira project key is empty' do
|
||||
it 'redirects back to show with an error' do
|
||||
post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: '' }
|
||||
|
||||
expect(response).to redirect_to(project_import_jira_path(project))
|
||||
expect(flash[:alert]).to eq('No Jira project key has been provided.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'post import' do
|
||||
it 'returns 404' do
|
||||
context 'when everything is ok' do
|
||||
it 'creates import state' do
|
||||
expect(project.latest_jira_import).to be_nil
|
||||
|
||||
post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: jira_project_key }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when issues disabled' do
|
||||
let_it_be(:disabled_issues_project) { create(:project, :public, :issues_disabled) }
|
||||
|
||||
context 'get show' do
|
||||
it 'returs 404' do
|
||||
get :show, params: { namespace_id: project.namespace.to_param, project_id: disabled_issues_project }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
context 'post import' do
|
||||
it 'returs 404' do
|
||||
post :import, params: { namespace_id: disabled_issues_project.namespace, project_id: disabled_issues_project, jira_project_key: jira_project_key }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when running Jira import first time' do
|
||||
context 'get show' do
|
||||
before do
|
||||
allow(JIRA::Resource::Project).to receive(:all).and_return(jira_projects)
|
||||
|
||||
expect(project.jira_imports).to be_empty
|
||||
|
||||
get :show, params: { namespace_id: project.namespace.to_param, project_id: project }
|
||||
end
|
||||
|
||||
context 'when no projects have been retrieved from Jira' do
|
||||
let(:jira_projects) { [] }
|
||||
|
||||
it 'render an error message' do
|
||||
expect(flash[:alert]).to eq('No projects have been returned from Jira. Please check your Jira configuration.')
|
||||
expect(response).to render_template(:show)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when projects retrieved from Jira' do
|
||||
let(:jira_projects) { [double(name: 'FOO project', key: 'FOO')] }
|
||||
|
||||
it 'renders show template' do
|
||||
expect(response).to render_template(:show)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'post import' do
|
||||
context 'when Jira project key is empty' do
|
||||
it 'redirects back to show with an error' do
|
||||
post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: '' }
|
||||
|
||||
expect(response).to redirect_to(project_import_jira_path(project))
|
||||
expect(flash[:alert]).to eq('No Jira project key has been provided.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when everything is ok' do
|
||||
it 'creates import state' do
|
||||
expect(project.latest_jira_import).to be_nil
|
||||
|
||||
post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: jira_project_key }
|
||||
|
||||
project.reload
|
||||
|
||||
jira_import = project.latest_jira_import
|
||||
expect(project.import_type).to eq 'jira'
|
||||
expect(jira_import.status).to eq 'scheduled'
|
||||
expect(jira_import.jira_project_key).to eq jira_project_key
|
||||
expect(response).to redirect_to(project_import_jira_path(project))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when import state is scheduled' do
|
||||
let_it_be(:jira_import_state) { create(:jira_import_state, :scheduled, project: project) }
|
||||
|
||||
context 'get show' do
|
||||
it 'renders import status' do
|
||||
get :show, params: { namespace_id: project.namespace.to_param, project_id: project }
|
||||
project.reload
|
||||
|
||||
jira_import = project.latest_jira_import
|
||||
expect(project.import_type).to eq 'jira'
|
||||
expect(jira_import.status).to eq 'scheduled'
|
||||
expect(flash.now[:notice]).to eq 'Import scheduled'
|
||||
end
|
||||
end
|
||||
|
||||
context 'post import' do
|
||||
it 'uses the existing import data' do
|
||||
post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: 'New Project' }
|
||||
|
||||
expect(flash[:notice]).to eq('Jira import is already running.')
|
||||
expect(jira_import.jira_project_key).to eq jira_project_key
|
||||
expect(response).to redirect_to(project_import_jira_path(project))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when Jira import ran before' do
|
||||
let_it_be(:jira_import_state) { create(:jira_import_state, :finished, project: project, jira_project_key: jira_project_key) }
|
||||
context 'when import state is scheduled' do
|
||||
let_it_be(:jira_import_state) { create(:jira_import_state, :scheduled, project: project) }
|
||||
|
||||
context 'get show' do
|
||||
it 'renders import status' do
|
||||
allow(JIRA::Resource::Project).to receive(:all).and_return([])
|
||||
get :show, params: { namespace_id: project.namespace.to_param, project_id: project }
|
||||
context 'get show' do
|
||||
it 'renders import status' do
|
||||
get :show, params: { namespace_id: project.namespace.to_param, project_id: project }
|
||||
|
||||
expect(project.latest_jira_import.status).to eq 'finished'
|
||||
expect(flash.now[:notice]).to eq 'Import finished'
|
||||
end
|
||||
jira_import = project.latest_jira_import
|
||||
expect(jira_import.status).to eq 'scheduled'
|
||||
expect(flash.now[:notice]).to eq 'Import scheduled'
|
||||
end
|
||||
end
|
||||
|
||||
context 'post import' do
|
||||
it 'uses the existing import data' do
|
||||
post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: 'New Project' }
|
||||
context 'post import' do
|
||||
it 'uses the existing import data' do
|
||||
post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: 'New Project' }
|
||||
|
||||
project.reload
|
||||
expect(project.latest_jira_import.status).to eq 'scheduled'
|
||||
expect(project.jira_imports.size).to eq 2
|
||||
expect(project.jira_imports.first.jira_project_key).to eq jira_project_key
|
||||
expect(project.jira_imports.last.jira_project_key).to eq 'New Project'
|
||||
expect(response).to redirect_to(project_import_jira_path(project))
|
||||
end
|
||||
expect(flash[:notice]).to eq('Jira import is already running.')
|
||||
expect(response).to redirect_to(project_import_jira_path(project))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when Jira import ran before' do
|
||||
let_it_be(:jira_import_state) { create(:jira_import_state, :finished, project: project, jira_project_key: jira_project_key) }
|
||||
|
||||
context 'get show' do
|
||||
it 'renders import status' do
|
||||
allow(JIRA::Resource::Project).to receive(:all).and_return([])
|
||||
get :show, params: { namespace_id: project.namespace.to_param, project_id: project }
|
||||
|
||||
expect(project.latest_jira_import.status).to eq 'finished'
|
||||
expect(flash.now[:notice]).to eq 'Import finished'
|
||||
end
|
||||
end
|
||||
|
||||
context 'post import' do
|
||||
it 'uses the existing import data' do
|
||||
post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: 'New Project' }
|
||||
|
||||
project.reload
|
||||
expect(project.latest_jira_import.status).to eq 'scheduled'
|
||||
expect(project.jira_imports.size).to eq 2
|
||||
expect(project.jira_imports.first.jira_project_key).to eq jira_project_key
|
||||
expect(project.jira_imports.last.jira_project_key).to eq 'New Project'
|
||||
expect(response).to redirect_to(project_import_jira_path(project))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -118,10 +118,7 @@ describe 'Mermaid rendering', :js do
|
|||
|
||||
visit project_issue_path(project, issue)
|
||||
|
||||
svg = page.find('svg.mermaid')
|
||||
expect(svg[:style]).to match(/max-width/)
|
||||
expect(svg[:width].to_i).to eq(100)
|
||||
expect(svg[:height].to_i).to eq(0)
|
||||
expect(page).to have_css('svg.mermaid[style*="max-width"][width="100%"]')
|
||||
end
|
||||
|
||||
it 'display button when diagram exceeds length', :js do
|
||||
|
|
|
@ -66,5 +66,13 @@ describe('Blob Header Default Actions', () => {
|
|||
|
||||
expect(buttons.at(0).attributes('disabled')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('does not render the copy button if a rendering error is set', () => {
|
||||
createComponent({
|
||||
hasRenderError: true,
|
||||
});
|
||||
|
||||
expect(wrapper.find('[data-testid="copyContentsButton"]').exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -87,6 +87,17 @@ describe('Blob Header Default Actions', () => {
|
|||
expect(wrapper.text()).toContain(slotContent);
|
||||
});
|
||||
});
|
||||
|
||||
it('passes information about render error down to default actions', () => {
|
||||
createComponent(
|
||||
{},
|
||||
{},
|
||||
{
|
||||
hasRenderError: true,
|
||||
},
|
||||
);
|
||||
expect(wrapper.find(DefaultActions).props('hasRenderError')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('functionality', () => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { range as rge } from 'lodash';
|
||||
import Vue from 'vue';
|
||||
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
|
||||
import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
|
||||
import app from '~/releases/components/app_index.vue';
|
||||
import createStore from '~/releases/stores';
|
||||
import listModule from '~/releases/stores/modules/list';
|
||||
|
@ -9,11 +9,11 @@ import { resetStore } from '../stores/modules/list/helpers';
|
|||
import {
|
||||
pageInfoHeadersWithoutPagination,
|
||||
pageInfoHeadersWithPagination,
|
||||
release,
|
||||
release2 as release,
|
||||
releases,
|
||||
} from '../mock_data';
|
||||
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
import waitForPromises from 'spec/helpers/wait_for_promises';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
|
||||
describe('Releases App ', () => {
|
||||
const Component = Vue.extend(app);
|
||||
|
@ -42,83 +42,67 @@ describe('Releases App ', () => {
|
|||
|
||||
describe('while loading', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(api, 'releases').and.returnValue(Promise.resolve({ data: [], headers: {} }));
|
||||
jest
|
||||
.spyOn(api, 'releases')
|
||||
// Need to defer the return value here to the next stack,
|
||||
// otherwise the loading state disappears before our test even starts.
|
||||
.mockImplementation(() => waitForPromises().then(() => ({ data: [], headers: {} })));
|
||||
vm = mountComponentWithStore(Component, { props, store });
|
||||
});
|
||||
|
||||
it('renders loading icon', done => {
|
||||
it('renders loading icon', () => {
|
||||
expect(vm.$el.querySelector('.js-loading')).not.toBeNull();
|
||||
expect(vm.$el.querySelector('.js-empty-state')).toBeNull();
|
||||
expect(vm.$el.querySelector('.js-success-state')).toBeNull();
|
||||
expect(vm.$el.querySelector('.gl-pagination')).toBeNull();
|
||||
|
||||
waitForPromises()
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
return waitForPromises();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with successful request', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(api, 'releases').and.returnValue(
|
||||
Promise.resolve({ data: releases, headers: pageInfoHeadersWithoutPagination }),
|
||||
);
|
||||
jest
|
||||
.spyOn(api, 'releases')
|
||||
.mockResolvedValue({ data: releases, headers: pageInfoHeadersWithoutPagination });
|
||||
vm = mountComponentWithStore(Component, { props, store });
|
||||
});
|
||||
|
||||
it('renders success state', done => {
|
||||
waitForPromises()
|
||||
.then(() => {
|
||||
expect(vm.$el.querySelector('.js-loading')).toBeNull();
|
||||
expect(vm.$el.querySelector('.js-empty-state')).toBeNull();
|
||||
expect(vm.$el.querySelector('.js-success-state')).not.toBeNull();
|
||||
expect(vm.$el.querySelector('.gl-pagination')).toBeNull();
|
||||
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
it('renders success state', () => {
|
||||
expect(vm.$el.querySelector('.js-loading')).toBeNull();
|
||||
expect(vm.$el.querySelector('.js-empty-state')).toBeNull();
|
||||
expect(vm.$el.querySelector('.js-success-state')).not.toBeNull();
|
||||
expect(vm.$el.querySelector('.gl-pagination')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with successful request and pagination', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(api, 'releases').and.returnValue(
|
||||
Promise.resolve({ data: releasesPagination, headers: pageInfoHeadersWithPagination }),
|
||||
);
|
||||
jest
|
||||
.spyOn(api, 'releases')
|
||||
.mockResolvedValue({ data: releasesPagination, headers: pageInfoHeadersWithPagination });
|
||||
vm = mountComponentWithStore(Component, { props, store });
|
||||
});
|
||||
|
||||
it('renders success state', done => {
|
||||
waitForPromises()
|
||||
.then(() => {
|
||||
expect(vm.$el.querySelector('.js-loading')).toBeNull();
|
||||
expect(vm.$el.querySelector('.js-empty-state')).toBeNull();
|
||||
expect(vm.$el.querySelector('.js-success-state')).not.toBeNull();
|
||||
expect(vm.$el.querySelector('.gl-pagination')).not.toBeNull();
|
||||
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
it('renders success state', () => {
|
||||
expect(vm.$el.querySelector('.js-loading')).toBeNull();
|
||||
expect(vm.$el.querySelector('.js-empty-state')).toBeNull();
|
||||
expect(vm.$el.querySelector('.js-success-state')).not.toBeNull();
|
||||
expect(vm.$el.querySelector('.gl-pagination')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with empty request', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(api, 'releases').and.returnValue(Promise.resolve({ data: [], headers: {} }));
|
||||
jest.spyOn(api, 'releases').mockResolvedValue({ data: [], headers: {} });
|
||||
vm = mountComponentWithStore(Component, { props, store });
|
||||
});
|
||||
|
||||
it('renders empty state', done => {
|
||||
waitForPromises()
|
||||
.then(() => {
|
||||
expect(vm.$el.querySelector('.js-loading')).toBeNull();
|
||||
expect(vm.$el.querySelector('.js-empty-state')).not.toBeNull();
|
||||
expect(vm.$el.querySelector('.js-success-state')).toBeNull();
|
||||
expect(vm.$el.querySelector('.gl-pagination')).toBeNull();
|
||||
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
it('renders empty state', () => {
|
||||
expect(vm.$el.querySelector('.js-loading')).toBeNull();
|
||||
expect(vm.$el.querySelector('.js-empty-state')).not.toBeNull();
|
||||
expect(vm.$el.querySelector('.js-success-state')).toBeNull();
|
||||
expect(vm.$el.querySelector('.gl-pagination')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -126,7 +110,7 @@ describe('Releases App ', () => {
|
|||
const findNewReleaseButton = () => vm.$el.querySelector('.js-new-release-btn');
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(api, 'releases').and.returnValue(Promise.resolve({ data: [], headers: {} }));
|
||||
jest.spyOn(api, 'releases').mockResolvedValue({ data: [], headers: {} });
|
||||
});
|
||||
|
||||
const factory = additionalProps => {
|
||||
|
@ -146,38 +130,20 @@ describe('Releases App ', () => {
|
|||
factory({ newReleasePath });
|
||||
});
|
||||
|
||||
it('renders the "New release" button', done => {
|
||||
waitForPromises()
|
||||
.then(() => {
|
||||
expect(findNewReleaseButton()).not.toBeNull();
|
||||
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
it('renders the "New release" button', () => {
|
||||
expect(findNewReleaseButton()).not.toBeNull();
|
||||
});
|
||||
|
||||
it('renders the "New release" button with the correct href', done => {
|
||||
waitForPromises()
|
||||
.then(() => {
|
||||
expect(findNewReleaseButton().getAttribute('href')).toBe(newReleasePath);
|
||||
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
it('renders the "New release" button with the correct href', () => {
|
||||
expect(findNewReleaseButton().getAttribute('href')).toBe(newReleasePath);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user is not allowed to create a new Release', () => {
|
||||
beforeEach(() => factory());
|
||||
|
||||
it('does not render the "New release" button', done => {
|
||||
waitForPromises()
|
||||
.then(() => {
|
||||
expect(findNewReleaseButton()).toBeNull();
|
||||
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
it('does not render the "New release" button', () => {
|
||||
expect(findNewReleaseButton()).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -131,3 +131,91 @@ export const release = {
|
|||
edit_url: 'http://0.0.0.0:3001/root/release-test/-/releases/v0.3/edit',
|
||||
},
|
||||
};
|
||||
|
||||
export const pageInfoHeadersWithoutPagination = {
|
||||
'X-NEXT-PAGE': '',
|
||||
'X-PAGE': '1',
|
||||
'X-PER-PAGE': '20',
|
||||
'X-PREV-PAGE': '',
|
||||
'X-TOTAL': '19',
|
||||
'X-TOTAL-PAGES': '1',
|
||||
};
|
||||
|
||||
export const pageInfoHeadersWithPagination = {
|
||||
'X-NEXT-PAGE': '2',
|
||||
'X-PAGE': '1',
|
||||
'X-PER-PAGE': '20',
|
||||
'X-PREV-PAGE': '',
|
||||
'X-TOTAL': '21',
|
||||
'X-TOTAL-PAGES': '2',
|
||||
};
|
||||
|
||||
export const release2 = {
|
||||
name: 'Bionic Beaver',
|
||||
tag_name: '18.04',
|
||||
description: '## changelog\n\n* line 1\n* line2',
|
||||
description_html: '<div><h2>changelog</h2><ul><li>line1</li<li>line 2</li></ul></div>',
|
||||
author_name: 'Release bot',
|
||||
author_email: 'release-bot@example.com',
|
||||
created_at: '2012-05-28T05:00:00-07:00',
|
||||
commit: {
|
||||
id: '2695effb5807a22ff3d138d593fd856244e155e7',
|
||||
short_id: '2695effb',
|
||||
title: 'Initial commit',
|
||||
created_at: '2017-07-26T11:08:53.000+02:00',
|
||||
parent_ids: ['2a4b78934375d7f53875269ffd4f45fd83a84ebe'],
|
||||
message: 'Initial commit',
|
||||
author: {
|
||||
avatar_url: 'uploads/-/system/user/avatar/johndoe/avatar.png',
|
||||
id: 482476,
|
||||
name: 'John Doe',
|
||||
path: '/johndoe',
|
||||
state: 'active',
|
||||
status_tooltip_html: null,
|
||||
username: 'johndoe',
|
||||
web_url: 'https://gitlab.com/johndoe',
|
||||
},
|
||||
authored_date: '2012-05-28T04:42:42-07:00',
|
||||
committer_name: 'Jack Smith',
|
||||
committer_email: 'jack@example.com',
|
||||
committed_date: '2012-05-28T04:42:42-07:00',
|
||||
},
|
||||
assets: {
|
||||
count: 6,
|
||||
sources: [
|
||||
{
|
||||
format: 'zip',
|
||||
url: 'https://gitlab.com/gitlab-org/gitlab-foss/-/archive/v11.3.12/gitlab-ce-v11.3.12.zip',
|
||||
},
|
||||
{
|
||||
format: 'tar.gz',
|
||||
url:
|
||||
'https://gitlab.com/gitlab-org/gitlab-foss/-/archive/v11.3.12/gitlab-ce-v11.3.12.tar.gz',
|
||||
},
|
||||
{
|
||||
format: 'tar.bz2',
|
||||
url:
|
||||
'https://gitlab.com/gitlab-org/gitlab-foss/-/archive/v11.3.12/gitlab-ce-v11.3.12.tar.bz2',
|
||||
},
|
||||
{
|
||||
format: 'tar',
|
||||
url: 'https://gitlab.com/gitlab-org/gitlab-foss/-/archive/v11.3.12/gitlab-ce-v11.3.12.tar',
|
||||
},
|
||||
],
|
||||
links: [
|
||||
{
|
||||
name: 'release-18.04.dmg',
|
||||
url: 'https://my-external-hosting.example.com/scrambled-url/',
|
||||
external: true,
|
||||
},
|
||||
{
|
||||
name: 'binary-linux-amd64',
|
||||
url:
|
||||
'https://gitlab.com/gitlab-org/gitlab-foss/-/jobs/artifacts/v11.6.0-rc4/download?job=rspec-mysql+41%2F50',
|
||||
external: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const releases = [release, release2];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import testAction from 'spec/helpers/vuex_action_helper';
|
||||
import testAction from 'helpers/vuex_action_helper';
|
||||
import {
|
||||
requestReleases,
|
||||
fetchReleases,
|
||||
|
@ -31,7 +31,7 @@ describe('Releases State actions', () => {
|
|||
describe('fetchReleases', () => {
|
||||
describe('success', () => {
|
||||
it('dispatches requestReleases and receiveReleasesSuccess', done => {
|
||||
spyOn(api, 'releases').and.callFake((id, options) => {
|
||||
jest.spyOn(api, 'releases').mockImplementation((id, options) => {
|
||||
expect(id).toEqual(1);
|
||||
expect(options.page).toEqual('1');
|
||||
return Promise.resolve({ data: releases, headers: pageInfoHeadersWithoutPagination });
|
||||
|
@ -56,7 +56,7 @@ describe('Releases State actions', () => {
|
|||
});
|
||||
|
||||
it('dispatches requestReleases and receiveReleasesSuccess on page two', done => {
|
||||
spyOn(api, 'releases').and.callFake((_, options) => {
|
||||
jest.spyOn(api, 'releases').mockImplementation((_, options) => {
|
||||
expect(options.page).toEqual('2');
|
||||
return Promise.resolve({ data: releases, headers: pageInfoHeadersWithoutPagination });
|
||||
});
|
||||
|
@ -82,7 +82,7 @@ describe('Releases State actions', () => {
|
|||
|
||||
describe('error', () => {
|
||||
it('dispatches requestReleases and receiveReleasesError', done => {
|
||||
spyOn(api, 'releases').and.returnValue(Promise.reject());
|
||||
jest.spyOn(api, 'releases').mockReturnValue(Promise.reject());
|
||||
|
||||
testAction(
|
||||
fetchReleases,
|
|
@ -3,7 +3,11 @@ import SnippetBlobView from '~/snippets/components/snippet_blob_view.vue';
|
|||
import BlobHeader from '~/blob/components/blob_header.vue';
|
||||
import BlobEmbeddable from '~/blob/components/blob_embeddable.vue';
|
||||
import BlobContent from '~/blob/components/blob_content.vue';
|
||||
import { BLOB_RENDER_EVENT_LOAD, BLOB_RENDER_EVENT_SHOW_SOURCE } from '~/blob/components/constants';
|
||||
import {
|
||||
BLOB_RENDER_EVENT_LOAD,
|
||||
BLOB_RENDER_EVENT_SHOW_SOURCE,
|
||||
BLOB_RENDER_ERRORS,
|
||||
} from '~/blob/components/constants';
|
||||
import { RichViewer, SimpleViewer } from '~/vue_shared/components/blob_viewers';
|
||||
import {
|
||||
SNIPPET_VISIBILITY_PRIVATE,
|
||||
|
@ -109,6 +113,20 @@ describe('Blob Embeddable', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('passes information about render error down to blob header', () => {
|
||||
createComponent({
|
||||
blob: {
|
||||
...BlobMock,
|
||||
simpleViewer: {
|
||||
...SimpleViewerMock,
|
||||
renderError: BLOB_RENDER_ERRORS.REASONS.COLLAPSED.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.find(BlobHeader).props('hasRenderError')).toBe(true);
|
||||
});
|
||||
|
||||
describe('URLS with hash', () => {
|
||||
beforeEach(() => {
|
||||
window.location.hash = '#LC2';
|
||||
|
|
|
@ -45,10 +45,8 @@ describe('Merge request widget rebase component', () => {
|
|||
|
||||
expect(text).toContain('Fast-forward merge is not possible.');
|
||||
expect(text.replace(/\s\s+/g, ' ')).toContain(
|
||||
'Rebase the source branch onto the target branch or merge target',
|
||||
'Rebase the source branch onto the target branch.',
|
||||
);
|
||||
|
||||
expect(text).toContain('branch into source branch to allow this merge request to be merged.');
|
||||
});
|
||||
|
||||
it('it should render error message when it fails', done => {
|
||||
|
|
|
@ -40,16 +40,6 @@ describe Resolvers::Projects::JiraImportsResolver do
|
|||
let_it_be(:jira_import1) { create(:jira_import_state, :finished, project: project, jira_project_key: 'AA', created_at: 2.days.ago) }
|
||||
let_it_be(:jira_import2) { create(:jira_import_state, :finished, project: project, jira_project_key: 'BB', created_at: 5.days.ago) }
|
||||
|
||||
context 'when feature flag disabled' do
|
||||
let(:current_user) { user }
|
||||
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'no Jira import access'
|
||||
end
|
||||
|
||||
context 'when user cannot read Jira imports' do
|
||||
context 'when anonymous user' do
|
||||
let(:current_user) { nil }
|
||||
|
|
|
@ -24,6 +24,36 @@ describe NotesHelper do
|
|||
project.add_guest(guest)
|
||||
end
|
||||
|
||||
describe '#note_target_title' do
|
||||
context 'note does not exist' do
|
||||
it 'returns nil' do
|
||||
expect(helper.note_target_title(nil)).to be_blank
|
||||
end
|
||||
end
|
||||
|
||||
context 'target does not exist' do
|
||||
it 'returns nil' do
|
||||
note = Note.new
|
||||
expect(helper.note_target_title(note)).to be_blank
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given a design target' do
|
||||
it 'returns nil' do
|
||||
note = build_stubbed(:note_on_design)
|
||||
expect(helper.note_target_title(note)).to be_blank
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given a non-design target' do
|
||||
it 'returns the issue title' do
|
||||
issue = build_stubbed(:issue, title: 'Issue 1')
|
||||
note = build_stubbed(:note, noteable: issue)
|
||||
expect(helper.note_target_title(note)).to eq('Issue 1')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#notes_max_access_for_users" do
|
||||
it 'returns access levels' do
|
||||
expect(helper.note_max_access_for_user(owner_note)).to eq(Gitlab::Access::OWNER)
|
||||
|
|
|
@ -89,7 +89,7 @@ describe TodosHelper do
|
|||
|
||||
context 'when given a non-design todo' do
|
||||
let(:todo) do
|
||||
create(:todo, :assigned,
|
||||
build_stubbed(:todo, :assigned,
|
||||
user: user,
|
||||
project: issue.project,
|
||||
target: issue,
|
||||
|
|
|
@ -1,148 +0,0 @@
|
|||
export const pageInfoHeadersWithoutPagination = {
|
||||
'X-NEXT-PAGE': '',
|
||||
'X-PAGE': '1',
|
||||
'X-PER-PAGE': '20',
|
||||
'X-PREV-PAGE': '',
|
||||
'X-TOTAL': '19',
|
||||
'X-TOTAL-PAGES': '1',
|
||||
};
|
||||
|
||||
export const pageInfoHeadersWithPagination = {
|
||||
'X-NEXT-PAGE': '2',
|
||||
'X-PAGE': '1',
|
||||
'X-PER-PAGE': '20',
|
||||
'X-PREV-PAGE': '',
|
||||
'X-TOTAL': '21',
|
||||
'X-TOTAL-PAGES': '2',
|
||||
};
|
||||
|
||||
export const release = {
|
||||
name: 'Bionic Beaver',
|
||||
tag_name: '18.04',
|
||||
description: '## changelog\n\n* line 1\n* line2',
|
||||
description_html: '<div><h2>changelog</h2><ul><li>line1</li<li>line 2</li></ul></div>',
|
||||
author_name: 'Release bot',
|
||||
author_email: 'release-bot@example.com',
|
||||
created_at: '2012-05-28T05:00:00-07:00',
|
||||
commit: {
|
||||
id: '2695effb5807a22ff3d138d593fd856244e155e7',
|
||||
short_id: '2695effb',
|
||||
title: 'Initial commit',
|
||||
created_at: '2017-07-26T11:08:53.000+02:00',
|
||||
parent_ids: ['2a4b78934375d7f53875269ffd4f45fd83a84ebe'],
|
||||
message: 'Initial commit',
|
||||
author: {
|
||||
avatar_url: 'uploads/-/system/user/avatar/johndoe/avatar.png',
|
||||
id: 482476,
|
||||
name: 'John Doe',
|
||||
path: '/johndoe',
|
||||
state: 'active',
|
||||
status_tooltip_html: null,
|
||||
username: 'johndoe',
|
||||
web_url: 'https://gitlab.com/johndoe',
|
||||
},
|
||||
authored_date: '2012-05-28T04:42:42-07:00',
|
||||
committer_name: 'Jack Smith',
|
||||
committer_email: 'jack@example.com',
|
||||
committed_date: '2012-05-28T04:42:42-07:00',
|
||||
},
|
||||
assets: {
|
||||
count: 6,
|
||||
sources: [
|
||||
{
|
||||
format: 'zip',
|
||||
url: 'https://gitlab.com/gitlab-org/gitlab-foss/-/archive/v11.3.12/gitlab-ce-v11.3.12.zip',
|
||||
},
|
||||
{
|
||||
format: 'tar.gz',
|
||||
url:
|
||||
'https://gitlab.com/gitlab-org/gitlab-foss/-/archive/v11.3.12/gitlab-ce-v11.3.12.tar.gz',
|
||||
},
|
||||
{
|
||||
format: 'tar.bz2',
|
||||
url:
|
||||
'https://gitlab.com/gitlab-org/gitlab-foss/-/archive/v11.3.12/gitlab-ce-v11.3.12.tar.bz2',
|
||||
},
|
||||
{
|
||||
format: 'tar',
|
||||
url: 'https://gitlab.com/gitlab-org/gitlab-foss/-/archive/v11.3.12/gitlab-ce-v11.3.12.tar',
|
||||
},
|
||||
],
|
||||
links: [
|
||||
{
|
||||
name: 'release-18.04.dmg',
|
||||
url: 'https://my-external-hosting.example.com/scrambled-url/',
|
||||
external: true,
|
||||
},
|
||||
{
|
||||
name: 'binary-linux-amd64',
|
||||
url:
|
||||
'https://gitlab.com/gitlab-org/gitlab-foss/-/jobs/artifacts/v11.6.0-rc4/download?job=rspec-mysql+41%2F50',
|
||||
external: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const releases = [
|
||||
release,
|
||||
{
|
||||
name: 'JoJos Bizarre Adventure',
|
||||
tag_name: '19.00',
|
||||
description: '## changelog\n\n* line 1\n* line2',
|
||||
description_html: '<div><h2>changelog</h2><ul><li>line1</li<li>line 2</li></ul></div>',
|
||||
author_name: 'Release bot',
|
||||
author_email: 'release-bot@example.com',
|
||||
created_at: '2012-05-28T05:00:00-07:00',
|
||||
commit: {
|
||||
id: '2695effb5807a22ff3d138d593fd856244e155e7',
|
||||
short_id: '2695effb',
|
||||
title: 'Initial commit',
|
||||
created_at: '2017-07-26T11:08:53.000+02:00',
|
||||
parent_ids: ['2a4b78934375d7f53875269ffd4f45fd83a84ebe'],
|
||||
message: 'Initial commit',
|
||||
author: {
|
||||
avatar_url: 'uploads/-/system/user/avatar/johndoe/avatar.png',
|
||||
id: 482476,
|
||||
name: 'John Doe',
|
||||
path: '/johndoe',
|
||||
state: 'active',
|
||||
status_tooltip_html: null,
|
||||
username: 'johndoe',
|
||||
web_url: 'https://gitlab.com/johndoe',
|
||||
},
|
||||
authored_date: '2012-05-28T04:42:42-07:00',
|
||||
committer_name: 'Jack Smith',
|
||||
committer_email: 'jack@example.com',
|
||||
committed_date: '2012-05-28T04:42:42-07:00',
|
||||
},
|
||||
assets: {
|
||||
count: 4,
|
||||
sources: [
|
||||
{
|
||||
format: 'tar.gz',
|
||||
url:
|
||||
'https://gitlab.com/gitlab-org/gitlab-foss/-/archive/v11.3.12/gitlab-ce-v11.3.12.tar.gz',
|
||||
},
|
||||
{
|
||||
format: 'tar.bz2',
|
||||
url:
|
||||
'https://gitlab.com/gitlab-org/gitlab-foss/-/archive/v11.3.12/gitlab-ce-v11.3.12.tar.bz2',
|
||||
},
|
||||
{
|
||||
format: 'tar',
|
||||
url:
|
||||
'https://gitlab.com/gitlab-org/gitlab-foss/-/archive/v11.3.12/gitlab-ce-v11.3.12.tar',
|
||||
},
|
||||
],
|
||||
links: [
|
||||
{
|
||||
name: 'binary-linux-amd64',
|
||||
url:
|
||||
'https://gitlab.com/gitlab-org/gitlab-foss/-/jobs/artifacts/v11.6.0-rc4/download?job=rspec-mysql+41%2F50',
|
||||
external: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
|
@ -8,24 +8,10 @@ describe Gitlab::JiraImport::BaseImporter do
|
|||
let(:project) { create(:project) }
|
||||
|
||||
describe 'with any inheriting class' do
|
||||
context 'when an error is returned from the project validation' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: false)
|
||||
|
||||
allow(project).to receive(:validate_jira_import_settings!)
|
||||
.and_raise(Projects::ImportService::Error, 'Jira import feature is disabled.')
|
||||
end
|
||||
|
||||
it 'raises exception' do
|
||||
expect { described_class.new(project) }.to raise_error(Projects::ImportService::Error, 'Jira import feature is disabled.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project validation is ok' do
|
||||
let!(:jira_service) { create(:jira_service, project: project) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: true)
|
||||
stub_jira_service_test
|
||||
|
||||
allow(project).to receive(:validate_jira_import_settings!)
|
||||
|
|
|
@ -14,7 +14,6 @@ describe Gitlab::JiraImport::IssuesImporter do
|
|||
subject { described_class.new(project) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: true)
|
||||
stub_jira_service_test
|
||||
end
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ describe Gitlab::JiraImport::LabelsImporter do
|
|||
subject { importer.execute }
|
||||
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: true)
|
||||
stub_const('Gitlab::JiraImport::LabelsImporter::MAX_LABELS', 2)
|
||||
end
|
||||
|
||||
|
|
|
@ -50,7 +50,10 @@ describe Iteration do
|
|||
end
|
||||
|
||||
context 'when dates overlap' do
|
||||
context 'same group' do
|
||||
let(:start_date) { 5.days.from_now }
|
||||
let(:due_date) { 6.days.from_now }
|
||||
|
||||
shared_examples_for 'overlapping dates' do
|
||||
context 'when start_date is in range' do
|
||||
let(:start_date) { 5.days.from_now }
|
||||
let(:due_date) { 3.weeks.from_now }
|
||||
|
@ -59,6 +62,11 @@ describe Iteration do
|
|||
expect(subject).not_to be_valid
|
||||
expect(subject.errors[:base]).to include('Dates cannot overlap with other existing Iterations')
|
||||
end
|
||||
|
||||
it 'is not valid even if forced' do
|
||||
subject.validate # to generate iid/etc
|
||||
expect { subject.save(validate: false) }.to raise_exception(ActiveRecord::StatementInvalid, /#{constraint_name}/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when end_date is in range' do
|
||||
|
@ -69,25 +77,84 @@ describe Iteration do
|
|||
expect(subject).not_to be_valid
|
||||
expect(subject.errors[:base]).to include('Dates cannot overlap with other existing Iterations')
|
||||
end
|
||||
|
||||
it 'is not valid even if forced' do
|
||||
subject.validate # to generate iid/etc
|
||||
expect { subject.save(validate: false) }.to raise_exception(ActiveRecord::StatementInvalid, /#{constraint_name}/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when both overlap' do
|
||||
let(:start_date) { 5.days.from_now }
|
||||
let(:due_date) { 6.days.from_now }
|
||||
|
||||
it 'is not valid' do
|
||||
expect(subject).not_to be_valid
|
||||
expect(subject.errors[:base]).to include('Dates cannot overlap with other existing Iterations')
|
||||
end
|
||||
|
||||
it 'is not valid even if forced' do
|
||||
subject.validate # to generate iid/etc
|
||||
expect { subject.save(validate: false) }.to raise_exception(ActiveRecord::StatementInvalid, /#{constraint_name}/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'different group' do
|
||||
let(:start_date) { 5.days.from_now }
|
||||
let(:due_date) { 6.days.from_now }
|
||||
let(:group) { create(:group) }
|
||||
context 'group' do
|
||||
it_behaves_like 'overlapping dates' do
|
||||
let(:constraint_name) { 'iteration_start_and_due_daterange_group_id_constraint' }
|
||||
end
|
||||
|
||||
it { is_expected.to be_valid }
|
||||
context 'different group' do
|
||||
let(:group) { create(:group) }
|
||||
|
||||
it { is_expected.to be_valid }
|
||||
|
||||
it 'does not trigger exclusion constraints' do
|
||||
expect { subject.save }.not_to raise_exception
|
||||
end
|
||||
end
|
||||
|
||||
context 'in a project' do
|
||||
let(:project) { create(:project) }
|
||||
|
||||
subject { build(:iteration, project: project, start_date: start_date, due_date: due_date) }
|
||||
|
||||
it { is_expected.to be_valid }
|
||||
|
||||
it 'does not trigger exclusion constraints' do
|
||||
expect { subject.save }.not_to raise_exception
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'project' do
|
||||
let_it_be(:existing_iteration) { create(:iteration, project: project, start_date: 4.days.from_now, due_date: 1.week.from_now) }
|
||||
|
||||
subject { build(:iteration, project: project, start_date: start_date, due_date: due_date) }
|
||||
|
||||
it_behaves_like 'overlapping dates' do
|
||||
let(:constraint_name) { 'iteration_start_and_due_daterange_project_id_constraint' }
|
||||
end
|
||||
|
||||
context 'different project' do
|
||||
let(:project) { create(:project) }
|
||||
|
||||
it { is_expected.to be_valid }
|
||||
|
||||
it 'does not trigger exclusion constraints' do
|
||||
expect { subject.save }.not_to raise_exception
|
||||
end
|
||||
end
|
||||
|
||||
context 'in a group' do
|
||||
let(:group) { create(:group) }
|
||||
|
||||
subject { build(:iteration, group: group, start_date: start_date, due_date: due_date) }
|
||||
|
||||
it { is_expected.to be_valid }
|
||||
|
||||
it 'does not trigger exclusion constraints' do
|
||||
expect { subject.save }.not_to raise_exception
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6053,34 +6053,20 @@ describe Project do
|
|||
end
|
||||
|
||||
shared_examples 'jira configuration base checks' do
|
||||
context 'when feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'raise Jira import error', 'Jira import feature is disabled.'
|
||||
context 'when Jira service was not setup' do
|
||||
it_behaves_like 'raise Jira import error', 'Jira integration not configured.'
|
||||
end
|
||||
|
||||
context 'when feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: true)
|
||||
end
|
||||
context 'when Jira service exists' do
|
||||
let!(:jira_service) { create(:jira_service, project: project, active: true) }
|
||||
|
||||
context 'when Jira service was not setup' do
|
||||
it_behaves_like 'raise Jira import error', 'Jira integration not configured.'
|
||||
end
|
||||
|
||||
context 'when Jira service exists' do
|
||||
let!(:jira_service) { create(:jira_service, project: project, active: true) }
|
||||
|
||||
context 'when Jira connection is not valid' do
|
||||
before do
|
||||
WebMock.stub_request(:get, 'https://jira.example.com/rest/api/2/serverInfo')
|
||||
.to_raise(JIRA::HTTPError.new(double(message: 'Some failure.')))
|
||||
end
|
||||
|
||||
it_behaves_like 'raise Jira import error', 'Unable to connect to the Jira instance. Please check your Jira integration configuration.'
|
||||
context 'when Jira connection is not valid' do
|
||||
before do
|
||||
WebMock.stub_request(:get, 'https://jira.example.com/rest/api/2/serverInfo')
|
||||
.to_raise(JIRA::HTTPError.new(double(message: 'Some failure.')))
|
||||
end
|
||||
|
||||
it_behaves_like 'raise Jira import error', 'Unable to connect to the Jira instance. Please check your Jira integration configuration.'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -6116,38 +6102,32 @@ describe Project do
|
|||
it_behaves_like 'jira configuration base checks'
|
||||
end
|
||||
|
||||
context 'when feature flag is enabled' do
|
||||
context 'when user does not have permissions to run the import' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: true)
|
||||
create(:jira_service, project: project, active: true)
|
||||
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
context 'when user does not have permissions to run the import' do
|
||||
before do
|
||||
create(:jira_service, project: project, active: true)
|
||||
it_behaves_like 'raise Jira import error', 'You do not have permissions to run the import.'
|
||||
end
|
||||
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it_behaves_like 'raise Jira import error', 'You do not have permissions to run the import.'
|
||||
context 'when user has permission to run import' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
context 'when user has permission to run import' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
let!(:jira_service) { create(:jira_service, project: project, active: true) }
|
||||
|
||||
let!(:jira_service) { create(:jira_service, project: project, active: true) }
|
||||
context 'when issues feature is disabled' do
|
||||
let_it_be(:project, reload: true) { create(:project, :issues_disabled) }
|
||||
|
||||
context 'when issues feature is disabled' do
|
||||
let_it_be(:project, reload: true) { create(:project, :issues_disabled) }
|
||||
it_behaves_like 'raise Jira import error', 'Cannot import because issues are not available in this project.'
|
||||
end
|
||||
|
||||
it_behaves_like 'raise Jira import error', 'Cannot import because issues are not available in this project.'
|
||||
end
|
||||
|
||||
context 'when everything is ok' do
|
||||
it 'does not return any error' do
|
||||
expect { subject }.not_to raise_error
|
||||
end
|
||||
context 'when everything is ok' do
|
||||
it 'does not return any error' do
|
||||
expect { subject }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,10 +29,6 @@ describe 'Starting a Jira Import' do
|
|||
end
|
||||
|
||||
context 'when the user does not have permission' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: true)
|
||||
end
|
||||
|
||||
shared_examples 'Jira import does not start' do
|
||||
it 'does not start the Jira import' do
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
|
@ -83,53 +79,39 @@ describe 'Starting a Jira Import' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when feature jira_issue_import feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'a mutation that returns errors in the response', errors: ['Jira import feature is disabled.']
|
||||
context 'when project has no Jira service' do
|
||||
it_behaves_like 'a mutation that returns errors in the response', errors: ['Jira integration not configured.']
|
||||
end
|
||||
|
||||
context 'when feature jira_issue_import feature flag is enabled' do
|
||||
context 'when when project has Jira service' do
|
||||
let!(:service) { create(:jira_service, project: project) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: true)
|
||||
project.reload
|
||||
|
||||
stub_jira_service_test
|
||||
end
|
||||
|
||||
context 'when project has no Jira service' do
|
||||
it_behaves_like 'a mutation that returns errors in the response', errors: ['Jira integration not configured.']
|
||||
context 'when issues feature are disabled' do
|
||||
let_it_be(:project, reload: true) { create(:project, :issues_disabled) }
|
||||
|
||||
it_behaves_like 'a mutation that returns errors in the response', errors: ['Cannot import because issues are not available in this project.']
|
||||
end
|
||||
|
||||
context 'when when project has Jira service' do
|
||||
let!(:service) { create(:jira_service, project: project) }
|
||||
context 'when jira_project_key not provided' do
|
||||
let(:jira_project_key) { '' }
|
||||
|
||||
before do
|
||||
project.reload
|
||||
it_behaves_like 'a mutation that returns errors in the response', errors: ['Unable to find Jira project to import data from.']
|
||||
end
|
||||
|
||||
stub_jira_service_test
|
||||
end
|
||||
context 'when Jira import successfully scheduled' do
|
||||
it 'schedules a Jira import' do
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
|
||||
context 'when issues feature are disabled' do
|
||||
let_it_be(:project, reload: true) { create(:project, :issues_disabled) }
|
||||
|
||||
it_behaves_like 'a mutation that returns errors in the response', errors: ['Cannot import because issues are not available in this project.']
|
||||
end
|
||||
|
||||
context 'when jira_project_key not provided' do
|
||||
let(:jira_project_key) { '' }
|
||||
|
||||
it_behaves_like 'a mutation that returns errors in the response', errors: ['Unable to find Jira project to import data from.']
|
||||
end
|
||||
|
||||
context 'when Jira import successfully scheduled' do
|
||||
it 'schedules a Jira import' do
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
|
||||
expect(jira_import['jiraProjectKey']).to eq 'AA'
|
||||
expect(jira_import['scheduledBy']['username']).to eq current_user.username
|
||||
expect(project.latest_jira_import).not_to be_nil
|
||||
expect(project.latest_jira_import).to be_scheduled
|
||||
end
|
||||
expect(jira_import['jiraProjectKey']).to eq 'AA'
|
||||
expect(jira_import['scheduledBy']['username']).to eq current_user.username
|
||||
expect(project.latest_jira_import).not_to be_nil
|
||||
expect(project.latest_jira_import).to be_scheduled
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,47 +11,33 @@ describe Gitlab::JiraImport::Stage::FinishImportWorker do
|
|||
end
|
||||
|
||||
describe '#perform' do
|
||||
context 'when feature flag enabled' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: false)
|
||||
end
|
||||
let_it_be(:jira_import, reload: true) { create(:jira_import_state, :scheduled, project: project) }
|
||||
|
||||
context 'when import did not start' do
|
||||
it_behaves_like 'cannot do Jira import'
|
||||
end
|
||||
|
||||
context 'when feature flag enabled' do
|
||||
let_it_be(:jira_import, reload: true) { create(:jira_import_state, :scheduled, project: project) }
|
||||
context 'when import started' do
|
||||
let_it_be(:import_label) { create(:label, project: project, title: 'jira-import') }
|
||||
let_it_be(:imported_issues) { create_list(:labeled_issue, 3, project: project, labels: [import_label]) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: true)
|
||||
expect(Gitlab::JiraImport).to receive(:get_import_label_id).and_return(import_label.id)
|
||||
expect(Gitlab::JiraImport).to receive(:issue_failures).and_return(2)
|
||||
|
||||
jira_import.start!
|
||||
worker.perform(project.id)
|
||||
end
|
||||
|
||||
context 'when import did not start' do
|
||||
it_behaves_like 'cannot do Jira import'
|
||||
it 'changes import state to finished' do
|
||||
expect(project.jira_import_status).to eq('finished')
|
||||
end
|
||||
|
||||
context 'when import started' do
|
||||
let_it_be(:import_label) { create(:label, project: project, title: 'jira-import') }
|
||||
let_it_be(:imported_issues) { create_list(:labeled_issue, 3, project: project, labels: [import_label]) }
|
||||
|
||||
before do
|
||||
expect(Gitlab::JiraImport).to receive(:get_import_label_id).and_return(import_label.id)
|
||||
expect(Gitlab::JiraImport).to receive(:issue_failures).and_return(2)
|
||||
|
||||
jira_import.start!
|
||||
worker.perform(project.id)
|
||||
end
|
||||
|
||||
it 'changes import state to finished' do
|
||||
expect(project.jira_import_status).to eq('finished')
|
||||
end
|
||||
|
||||
it 'saves imported issues counts' do
|
||||
latest_jira_import = project.latest_jira_import
|
||||
expect(latest_jira_import.total_issue_count).to eq(5)
|
||||
expect(latest_jira_import.failed_to_import_count).to eq(2)
|
||||
expect(latest_jira_import.imported_issues_count).to eq(3)
|
||||
end
|
||||
it 'saves imported issues counts' do
|
||||
latest_jira_import = project.latest_jira_import
|
||||
expect(latest_jira_import.total_issue_count).to eq(5)
|
||||
expect(latest_jira_import.failed_to_import_count).to eq(2)
|
||||
expect(latest_jira_import.imported_issues_count).to eq(3)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,34 +10,19 @@ describe Gitlab::JiraImport::Stage::ImportAttachmentsWorker do
|
|||
end
|
||||
|
||||
describe '#perform' do
|
||||
context 'when feature flag disabled' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: false)
|
||||
end
|
||||
let_it_be(:jira_import) { create(:jira_import_state, :scheduled, project: project) }
|
||||
|
||||
context 'when import did not start' do
|
||||
it_behaves_like 'cannot do Jira import'
|
||||
it_behaves_like 'does not advance to next stage'
|
||||
end
|
||||
|
||||
context 'when feature flag enabled' do
|
||||
let_it_be(:jira_import) { create(:jira_import_state, :scheduled, project: project) }
|
||||
|
||||
context 'when import started' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: true)
|
||||
jira_import.start!
|
||||
end
|
||||
|
||||
context 'when import did not start' do
|
||||
it_behaves_like 'cannot do Jira import'
|
||||
it_behaves_like 'does not advance to next stage'
|
||||
end
|
||||
|
||||
context 'when import started' do
|
||||
before do
|
||||
jira_import.start!
|
||||
end
|
||||
|
||||
it_behaves_like 'advance to next stage', :notes
|
||||
end
|
||||
it_behaves_like 'advance to next stage', :notes
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,65 +13,53 @@ describe Gitlab::JiraImport::Stage::ImportIssuesWorker do
|
|||
end
|
||||
|
||||
describe '#perform' do
|
||||
context 'when feature flag disabled' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: false)
|
||||
end
|
||||
let_it_be(:jira_import, reload: true) { create(:jira_import_state, :scheduled, project: project) }
|
||||
|
||||
before do
|
||||
stub_jira_service_test
|
||||
end
|
||||
|
||||
context 'when import did not start' do
|
||||
it_behaves_like 'cannot do Jira import'
|
||||
it_behaves_like 'does not advance to next stage'
|
||||
end
|
||||
|
||||
context 'when feature flag enabled' do
|
||||
let_it_be(:jira_import, reload: true) { create(:jira_import_state, :scheduled, project: project) }
|
||||
context 'when import started', :clean_gitlab_redis_cache do
|
||||
let_it_be(:jira_service) { create(:jira_service, project: project) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: true)
|
||||
stub_jira_service_test
|
||||
jira_import.start!
|
||||
allow_next_instance_of(Gitlab::JiraImport::IssuesImporter) do |instance|
|
||||
allow(instance).to receive(:fetch_issues).and_return([])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when import did not start' do
|
||||
it_behaves_like 'cannot do Jira import'
|
||||
it_behaves_like 'does not advance to next stage'
|
||||
context 'when start_at is nil' do
|
||||
it_behaves_like 'advance to next stage', :attachments
|
||||
end
|
||||
|
||||
context 'when import started', :clean_gitlab_redis_cache do
|
||||
let_it_be(:jira_service) { create(:jira_service, project: project) }
|
||||
|
||||
context 'when start_at is zero' do
|
||||
before do
|
||||
jira_import.start!
|
||||
allow_next_instance_of(Gitlab::JiraImport::IssuesImporter) do |instance|
|
||||
allow(instance).to receive(:fetch_issues).and_return([])
|
||||
end
|
||||
allow(Gitlab::Cache::Import::Caching).to receive(:read).and_return(0)
|
||||
end
|
||||
|
||||
context 'when start_at is nil' do
|
||||
it_behaves_like 'advance to next stage', :attachments
|
||||
it_behaves_like 'advance to next stage', :issues
|
||||
end
|
||||
|
||||
context 'when start_at is greater than zero' do
|
||||
before do
|
||||
allow(Gitlab::Cache::Import::Caching).to receive(:read).and_return(25)
|
||||
end
|
||||
|
||||
context 'when start_at is zero' do
|
||||
before do
|
||||
allow(Gitlab::Cache::Import::Caching).to receive(:read).and_return(0)
|
||||
end
|
||||
it_behaves_like 'advance to next stage', :issues
|
||||
end
|
||||
|
||||
it_behaves_like 'advance to next stage', :issues
|
||||
context 'when start_at is below zero' do
|
||||
before do
|
||||
allow(Gitlab::Cache::Import::Caching).to receive(:read).and_return(-1)
|
||||
end
|
||||
|
||||
context 'when start_at is greater than zero' do
|
||||
before do
|
||||
allow(Gitlab::Cache::Import::Caching).to receive(:read).and_return(25)
|
||||
end
|
||||
|
||||
it_behaves_like 'advance to next stage', :issues
|
||||
end
|
||||
|
||||
context 'when start_at is below zero' do
|
||||
before do
|
||||
allow(Gitlab::Cache::Import::Caching).to receive(:read).and_return(-1)
|
||||
end
|
||||
|
||||
it_behaves_like 'advance to next stage', :attachments
|
||||
end
|
||||
it_behaves_like 'advance to next stage', :attachments
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,48 +13,33 @@ describe Gitlab::JiraImport::Stage::ImportLabelsWorker do
|
|||
end
|
||||
|
||||
describe '#perform' do
|
||||
context 'when feature flag disabled' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: false)
|
||||
end
|
||||
let_it_be(:jira_import, reload: true) { create(:jira_import_state, :scheduled, project: project) }
|
||||
|
||||
context 'when import did not start' do
|
||||
it_behaves_like 'cannot do Jira import'
|
||||
it_behaves_like 'does not advance to next stage'
|
||||
end
|
||||
|
||||
context 'when feature flag enabled' do
|
||||
let_it_be(:jira_import, reload: true) { create(:jira_import_state, :scheduled, project: project) }
|
||||
context 'when import started' do
|
||||
let!(:jira_service) { create(:jira_service, project: project) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: true)
|
||||
stub_jira_service_test
|
||||
|
||||
jira_import.start!
|
||||
|
||||
WebMock.stub_request(:get, 'https://jira.example.com/rest/api/2/label?maxResults=500&startAt=0')
|
||||
.to_return(body: {}.to_json )
|
||||
end
|
||||
|
||||
context 'when import did not start' do
|
||||
it_behaves_like 'cannot do Jira import'
|
||||
it_behaves_like 'does not advance to next stage'
|
||||
end
|
||||
it_behaves_like 'advance to next stage', :issues
|
||||
|
||||
context 'when import started' do
|
||||
let!(:jira_service) { create(:jira_service, project: project) }
|
||||
|
||||
before do
|
||||
stub_jira_service_test
|
||||
|
||||
jira_import.start!
|
||||
|
||||
WebMock.stub_request(:get, 'https://jira.example.com/rest/api/2/label?maxResults=500&startAt=0')
|
||||
.to_return(body: {}.to_json )
|
||||
it 'executes labels importer' do
|
||||
expect_next_instance_of(Gitlab::JiraImport::LabelsImporter) do |instance|
|
||||
expect(instance).to receive(:execute).and_return(Gitlab::JobWaiter.new)
|
||||
end
|
||||
|
||||
it_behaves_like 'advance to next stage', :issues
|
||||
|
||||
it 'executes labels importer' do
|
||||
expect_next_instance_of(Gitlab::JiraImport::LabelsImporter) do |instance|
|
||||
expect(instance).to receive(:execute).and_return(Gitlab::JobWaiter.new)
|
||||
end
|
||||
|
||||
described_class.new.perform(project.id)
|
||||
end
|
||||
described_class.new.perform(project.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,34 +10,19 @@ describe Gitlab::JiraImport::Stage::ImportNotesWorker do
|
|||
end
|
||||
|
||||
describe '#perform' do
|
||||
context 'when feature flag disabled' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: false)
|
||||
end
|
||||
let_it_be(:jira_import) { create(:jira_import_state, :scheduled, project: project) }
|
||||
|
||||
context 'when import did not start' do
|
||||
it_behaves_like 'cannot do Jira import'
|
||||
it_behaves_like 'does not advance to next stage'
|
||||
end
|
||||
|
||||
context 'when feature flag enabled' do
|
||||
let_it_be(:jira_import) { create(:jira_import_state, :scheduled, project: project) }
|
||||
|
||||
context 'when import started' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: true)
|
||||
jira_import.start!
|
||||
end
|
||||
|
||||
context 'when import did not start' do
|
||||
it_behaves_like 'cannot do Jira import'
|
||||
it_behaves_like 'does not advance to next stage'
|
||||
end
|
||||
|
||||
context 'when import started' do
|
||||
before do
|
||||
jira_import.start!
|
||||
end
|
||||
|
||||
it_behaves_like 'advance to next stage', :finish
|
||||
end
|
||||
it_behaves_like 'advance to next stage', :finish
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,81 +12,63 @@ describe Gitlab::JiraImport::Stage::StartImportWorker do
|
|||
end
|
||||
|
||||
describe '#perform' do
|
||||
context 'when feature flag not disabled' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: false)
|
||||
end
|
||||
let_it_be(:jira_import, reload: true) { create(:jira_import_state, project: project, jid: jid) }
|
||||
|
||||
it 'exits because import not allowed' do
|
||||
context 'when import is not scheduled' do
|
||||
it 'exits because import not started' do
|
||||
expect(Gitlab::JiraImport::Stage::ImportLabelsWorker).not_to receive(:perform_async)
|
||||
|
||||
worker.perform(project.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature flag enabled' do
|
||||
let_it_be(:jira_import, reload: true) { create(:jira_import_state, project: project, jid: jid) }
|
||||
|
||||
context 'when import is scheduled' do
|
||||
before do
|
||||
stub_feature_flags(jira_issue_import: true)
|
||||
jira_import.schedule!
|
||||
end
|
||||
|
||||
context 'when import is not scheduled' do
|
||||
it 'exits because import not started' do
|
||||
expect(Gitlab::JiraImport::Stage::ImportLabelsWorker).not_to receive(:perform_async)
|
||||
it 'advances to importing labels' do
|
||||
expect(Gitlab::JiraImport::Stage::ImportLabelsWorker).to receive(:perform_async)
|
||||
|
||||
worker.perform(project.id)
|
||||
end
|
||||
worker.perform(project.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when import is started' do
|
||||
before do
|
||||
jira_import.update!(status: :started)
|
||||
end
|
||||
|
||||
context 'when import is scheduled' do
|
||||
before do
|
||||
jira_import.schedule!
|
||||
end
|
||||
|
||||
context 'when this is the same worker that stated import' do
|
||||
it 'advances to importing labels' do
|
||||
allow(worker).to receive(:jid).and_return(jid)
|
||||
expect(Gitlab::JiraImport::Stage::ImportLabelsWorker).to receive(:perform_async)
|
||||
|
||||
worker.perform(project.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when import is started' do
|
||||
before do
|
||||
jira_import.update!(status: :started)
|
||||
end
|
||||
|
||||
context 'when this is the same worker that stated import' do
|
||||
it 'advances to importing labels' do
|
||||
allow(worker).to receive(:jid).and_return(jid)
|
||||
expect(Gitlab::JiraImport::Stage::ImportLabelsWorker).to receive(:perform_async)
|
||||
|
||||
worker.perform(project.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when this is a different worker that stated import' do
|
||||
it 'advances to importing labels' do
|
||||
allow(worker).to receive(:jid).and_return('87654321')
|
||||
expect(Gitlab::JiraImport::Stage::ImportLabelsWorker).not_to receive(:perform_async)
|
||||
|
||||
worker.perform(project.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when import is finished' do
|
||||
before do
|
||||
jira_import.update!(status: :finished)
|
||||
end
|
||||
|
||||
context 'when this is a different worker that stated import' do
|
||||
it 'advances to importing labels' do
|
||||
allow(worker).to receive(:jid).and_return(jid)
|
||||
allow(worker).to receive(:jid).and_return('87654321')
|
||||
expect(Gitlab::JiraImport::Stage::ImportLabelsWorker).not_to receive(:perform_async)
|
||||
|
||||
worker.perform(project.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when import is finished' do
|
||||
before do
|
||||
jira_import.update!(status: :finished)
|
||||
end
|
||||
|
||||
it 'advances to importing labels' do
|
||||
allow(worker).to receive(:jid).and_return(jid)
|
||||
expect(Gitlab::JiraImport::Stage::ImportLabelsWorker).not_to receive(:perform_async)
|
||||
|
||||
worker.perform(project.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -787,10 +787,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.130.0.tgz#0c2f3cdc0a4b0f54c47b2861c8fa31b2a58c570a"
|
||||
integrity sha512-azJ1E9PBk6fGOaP6816BSr8oYrQu3m3BbYZwWOCUp8AfbZuf0ZOZVYmlR9i/eAOhoqqqmwF8hYCK2VjAklbpPA==
|
||||
|
||||
"@gitlab/ui@15.5.0":
|
||||
version "15.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-15.5.0.tgz#96b563875ab8f587ee0e55d16c14c98f1d698957"
|
||||
integrity sha512-AmYPR5X2UtipfkrZd8e8VvOo26YKHfXT+2XRd+Hysjdg+DBaGcUDTbO67cMb9dKkFPSgPdN67SgV+C8CBuBONQ==
|
||||
"@gitlab/ui@15.6.0":
|
||||
version "15.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-15.6.0.tgz#0fc4ca7fd6ecc3c1be7e127dce3dde78fb4faa06"
|
||||
integrity sha512-drb+f+xcAOtov2mAE3FX49845Fe4o41/jTKDxONDlsescKggZBXaSl1PQtL4EkspllIFb3JYdvL0sXbmp/dz3w==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.0.0"
|
||||
"@gitlab/vue-toasted" "^1.3.0"
|
||||
|
|
Loading…
Reference in New Issue