Commit 6959f7c4 authored by Rémy Coutable's avatar Rémy Coutable

Merge branch '212566-foss-design-management-services' into 'master'

Move Design Management services to FOSS

See merge request gitlab-org/gitlab!30961
parents 60049140 6e27e2f6
......@@ -146,6 +146,14 @@ module Noteable
target_id: id
)
end
def after_note_created(_note)
# no-op
end
def after_note_destroyed(_note)
# no-op
end
end
Noteable.extend(Noteable::ClassMethods)
......
......@@ -159,6 +159,8 @@ class Note < ApplicationRecord
after_save :touch_noteable, unless: :importing?
after_destroy :expire_etag_cache
after_save :store_mentions!, if: :any_mentionable_attributes_changed?
after_commit :notify_after_create, on: :create
after_commit :notify_after_destroy, on: :destroy
class << self
def model_name
......@@ -509,6 +511,14 @@ class Note < ApplicationRecord
noteable_object
end
def notify_after_create
noteable&.after_note_created(self)
end
def notify_after_destroy
noteable&.after_note_destroyed(self)
end
def banzai_render_context(field)
super.merge(noteable: noteable, system_note: system?)
end
......
......@@ -16,9 +16,6 @@ module DesignManagement
version = delete_designs!
# Create a Geo event so changes will be replicated to secondary node(s)
repository.log_geo_updated_event
success(version: version)
end
......@@ -65,3 +62,5 @@ module DesignManagement
end
end
end
DesignManagement::DeleteDesignsService.prepend_if_ee('EE::DesignManagement::DeleteDesignsService')
......@@ -20,9 +20,6 @@ module DesignManagement
uploaded_designs, version = upload_designs!
skipped_designs = designs - uploaded_designs
# Create a Geo event so changes will be replicated to secondary node(s)
repository.log_geo_updated_event
success({ designs: uploaded_designs, version: version, skipped_designs: skipped_designs })
rescue ::ActiveRecord::RecordInvalid => e
error(e.message)
......@@ -113,3 +110,5 @@ module DesignManagement
end
end
end
DesignManagement::SaveDesignsService.prepend_if_ee('EE::DesignManagement::SaveDesignsService')
......@@ -5,8 +5,7 @@ module Lfs
# return a transformed result with `content` and `encoding` to commit.
#
# The `repository` passed to the initializer can be a Repository or
# a DesignManagement::Repository (an EE-specific class that inherits
# from Repository).
# class that inherits from Repository.
#
# The `repository_type` property will be one of the types named in
# `Gitlab::GlRepository.types`, and is recorded on the `LfsObjectsProject`
......
......@@ -16,10 +16,18 @@ module Notes
return if @note.for_personal_snippet?
@note.create_cross_references!
::SystemNoteService.design_discussion_added(@note) if create_design_discussion_system_note?
execute_note_hooks
end
end
private
def create_design_discussion_system_note?
@note && @note.for_design? && @note.start_of_discussion?
end
def hook_data
Gitlab::DataBuilder::Note.build(@note, @note.author)
end
......
......@@ -8,13 +8,15 @@ module Projects
class BaseRepositoryService < BaseService
include Gitlab::ShellAdapter
attr_reader :old_disk_path, :new_disk_path, :old_storage_version, :logger, :move_wiki
attr_reader :old_disk_path, :new_disk_path, :old_storage_version,
:logger, :move_wiki, :move_design
def initialize(project:, old_disk_path:, logger: nil)
@project = project
@logger = logger || Gitlab::AppLogger
@old_disk_path = old_disk_path
@move_wiki = has_wiki?
@move_design = has_design?
end
protected
......@@ -23,6 +25,10 @@ module Projects
gitlab_shell.repository_exists?(project.repository_storage, "#{old_wiki_disk_path}.git")
end
def has_design?
gitlab_shell.repository_exists?(project.repository_storage, "#{old_design_disk_path}.git")
end
def move_repository(from_name, to_name)
from_exists = gitlab_shell.repository_exists?(project.repository_storage, "#{from_name}.git")
to_exists = gitlab_shell.repository_exists?(project.repository_storage, "#{to_name}.git")
......@@ -58,12 +64,18 @@ module Projects
project.clear_memoization(:wiki)
end
if move_design
result &&= move_repository(old_design_disk_path, new_design_disk_path)
project.clear_memoization(:design_repository)
end
result
end
def rollback_folder_move
move_repository(new_disk_path, old_disk_path)
move_repository(new_wiki_disk_path, old_wiki_disk_path)
move_repository(new_design_disk_path, old_design_disk_path) if move_design
end
def try_to_set_repository_read_only!
......@@ -87,8 +99,18 @@ module Projects
def new_wiki_disk_path
@new_wiki_disk_path ||= "#{new_disk_path}#{wiki_path_suffix}"
end
def design_path_suffix
@design_path_suffix ||= ::Gitlab::GlRepository::DESIGN.path_suffix
end
def old_design_disk_path
@old_design_disk_path ||= "#{old_disk_path}#{design_path_suffix}"
end
end
Projects::HashedStorage::BaseRepositoryService.prepend_if_ee('EE::Projects::HashedStorage::BaseRepositoryService')
def new_design_disk_path
@new_design_disk_path ||= "#{new_disk_path}#{design_path_suffix}"
end
end
end
end
......@@ -135,7 +135,8 @@ module Projects
return if project.hashed_storage?(:repository)
move_repo_folder(@new_path, @old_path)
move_repo_folder("#{@new_path}.wiki", "#{@old_path}.wiki")
move_repo_folder(new_wiki_repo_path, old_wiki_repo_path)
move_repo_folder(new_design_repo_path, old_design_repo_path)
end
def move_repo_folder(from_name, to_name)
......@@ -157,8 +158,9 @@ module Projects
# Disk path is changed; we need to ensure we reload it
project.reload_repository!
# Move wiki repo also if present
move_repo_folder("#{@old_path}.wiki", "#{@new_path}.wiki")
# Move wiki and design repos also if present
move_repo_folder(old_wiki_repo_path, new_wiki_repo_path)
move_repo_folder(old_design_repo_path, new_design_repo_path)
end
def move_project_uploads(project)
......@@ -170,6 +172,22 @@ module Projects
@new_namespace.full_path
)
end
def old_wiki_repo_path
"#{old_path}#{::Gitlab::GlRepository::WIKI.path_suffix}"
end
def new_wiki_repo_path
"#{new_path}#{::Gitlab::GlRepository::WIKI.path_suffix}"
end
def old_design_repo_path
"#{old_path}#{::Gitlab::GlRepository::DESIGN.path_suffix}"
end
def new_design_repo_path
"#{new_path}#{::Gitlab::GlRepository::DESIGN.path_suffix}"
end
end
end
......
......@@ -58,6 +58,10 @@ module Projects
if project.wiki.repository_exists?
mirror_repository(type: Gitlab::GlRepository::WIKI)
end
if project.design_repository.exists?
mirror_repository(type: ::Gitlab::GlRepository::DESIGN)
end
end
def mirror_repository(type: Gitlab::GlRepository::PROJECT)
......@@ -106,6 +110,13 @@ module Projects
wiki.disk_path,
"#{new_project_path}.wiki")
end
if design_repository.exists?
GitlabShellWorker.perform_async(:mv_repository,
old_repository_storage,
design_repository.disk_path,
"#{new_project_path}.design")
end
end
end
......@@ -140,5 +151,3 @@ module Projects
end
end
end
Projects::UpdateRepositoryStorageService.prepend_if_ee('EE::Projects::UpdateRepositoryStorageService')
......@@ -245,6 +245,34 @@ module SystemNoteService
def auto_resolve_prometheus_alert(noteable, project, author)
::SystemNotes::IssuablesService.new(noteable: noteable, project: project, author: author).auto_resolve_prometheus_alert
end
# Parameters:
# - version [DesignManagement::Version]
#
# Example Note text:
#
# "added [1 designs](link-to-version)"
# "changed [2 designs](link-to-version)"
#
# Returns [Array<Note>]: the created Note objects
def design_version_added(version)
::SystemNotes::DesignManagementService.new(noteable: version.issue, project: version.issue.project, author: version.author).design_version_added(version)
end
# Called when a new discussion is created on a design
#
# discussion_note - DiscussionNote
#
# Example Note text:
#
# "started a discussion on screen.png"
#
# Returns the created Note object
def design_discussion_added(discussion_note)
design = discussion_note.noteable
::SystemNotes::DesignManagementService.new(noteable: design.issue, project: design.project, author: discussion_note.author).design_discussion_added(discussion_note)
end
end
SystemNoteService.prepend_if_ee('EE::SystemNoteService')
# frozen_string_literal: true
module SystemNotes
class DesignManagementService < ::SystemNotes::BaseService
include ActionView::RecordIdentifier
# Parameters:
# - version [DesignManagement::Version]
#
# Example Note text:
#
# "added [1 designs](link-to-version)"
# "changed [2 designs](link-to-version)"
#
# Returns [Array<Note>]: the created Note objects
def design_version_added(version)
events = DesignManagement::Action.events
link_href = designs_path(version: version.id)
version.designs_by_event.map do |(event_name, designs)|
note_data = self.class.design_event_note_data(events[event_name])
icon_name = note_data[:icon]
n = designs.size
body = "%s [%d %s](%s)" % [note_data[:past_tense], n, 'design'.pluralize(n), link_href]
create_note(NoteSummary.new(noteable, project, author, body, action: icon_name))
end
end
# Called when a new discussion is created on a design
#
# discussion_note - DiscussionNote
#
# Example Note text:
#
# "started a discussion on screen.png"
#
# Returns the created Note object
def design_discussion_added(discussion_note)
design = discussion_note.noteable
body = _('started a discussion on %{design_link}') % {
design_link: '[%s](%s)' % [
design.filename,
designs_path(vueroute: design.filename, anchor: dom_id(discussion_note))
]
}
action = :designs_discussion_added
create_note(NoteSummary.new(noteable, project, author, body, action: action))
end
# Take one of the `DesignManagement::Action.events` and
# return:
# * an English past-tense verb.
# * the name of an icon used in renderin a system note
#
# We do not currently internationalize our system notes,
# instead we just produce English-language descriptions.
# See: https://gitlab.com/gitlab-org/gitlab/issues/30408
# See: https://gitlab.com/gitlab-org/gitlab/issues/14056
def self.design_event_note_data(event)
case event
when DesignManagement::Action.events[:creation]
{ icon: 'designs_added', past_tense: 'added' }
when DesignManagement::Action.events[:modification]
{ icon: 'designs_modified', past_tense: 'updated' }
when DesignManagement::Action.events[:deletion]
{ icon: 'designs_removed', past_tense: 'removed' }
else
raise "Unknown event: #{event}"
end
end
private
def designs_path(params = {})
url_helpers.designs_project_issue_path(project, noteable, params)
end
end
end
......@@ -24,13 +24,5 @@ module EE
super
end
end
def after_note_created(_note)
# no-op
end
def after_note_destroyed(_note)
# no-op
end
end
end
......@@ -15,9 +15,6 @@ module EE
scope :searchable, -> { where(system: false).includes(:noteable) }
scope :by_humans, -> { user.joins(:author).merge(::User.humans) }
scope :with_suggestions, -> { joins(:suggestions) }
after_commit :notify_after_create, on: :create
after_commit :notify_after_destroy, on: :destroy
end
# Original method in Elastic::ApplicationSearch
......@@ -62,14 +59,6 @@ module EE
for_epic? ? noteable.group : super
end
def notify_after_create
noteable&.after_note_created(self)
end
def notify_after_destroy
noteable&.after_note_destroyed(self)
end
override :system_note_with_references_visible_for?
def system_note_with_references_visible_for?(user)
return false unless super
......
# frozen_string_literal: true
module EE
module DesignManagement
module DeleteDesignsService
extend ::Gitlab::Utils::Override
override :execute
def execute
super.tap do |response|
# Create a Geo event so changes will be replicated to secondary node(s)
repository.log_geo_updated_event if response[:status] == :success
end
end
end
end
end
# frozen_string_literal: true
module EE
module DesignManagement
module SaveDesignsService
extend ::Gitlab::Utils::Override
override :execute
def execute
super.tap do |response|
# Create a Geo event so changes will be replicated to secondary node(s)
repository.log_geo_updated_event if response[:status] == :success
end
end
end
end
end
......@@ -11,14 +11,6 @@ module EE
super
::Analytics::RefreshCommentsData.for_note(note)&.execute
::SystemNoteService.design_discussion_added(note) if create_design_discussion_system_note?
end
private
def create_design_discussion_system_note?
note && note.for_design? && note.start_of_discussion?
end
end
end
......
# frozen_string_literal: true
module EE
module Projects
module HashedStorage
module BaseRepositoryService
extend ::Gitlab::Utils::Override
attr_reader :move_design
override :initialize
def initialize(project:, old_disk_path:, logger: nil)
super
@move_design = has_design?
end
protected
def has_design?
gitlab_shell.repository_exists?(project.repository_storage, "#{old_design_disk_path}.git")
end
override :move_repositories
def move_repositories
result = super
if move_design
result &&= move_repository(old_design_disk_path, new_design_disk_path)
project.clear_memoization(:design_repository)
end
result
end
override :rollback_folder_move
def rollback_folder_move
super
if move_design
move_repository(new_design_disk_path, old_design_disk_path)
end
end
def design_path_suffix
@design_path_suffix ||= ::Gitlab::GlRepository::DESIGN.path_suffix
end
def old_design_disk_path
@old_design_disk_path ||= "#{old_disk_path}#{design_path_suffix}"
end
def new_design_disk_path
@new_design_disk_path ||= "#{new_disk_path}#{design_path_suffix}"
end
end
end
end
end
......@@ -29,35 +29,9 @@ module EE
super
end
override :move_project_folders
def move_project_folders(project)
super
return if project.hashed_storage?(:repository)
move_repo_folder(old_design_repo_path, new_design_repo_path)
end
override :rollback_folder_move
def rollback_folder_move
super
return if project.hashed_storage?(:repository)
move_repo_folder(new_design_repo_path, old_design_repo_path)
end
def new_namespace_has_same_root?(project)
new_namespace.root_ancestor == project.namespace.root_ancestor
end
def old_design_repo_path
"#{old_path}#{::Gitlab::GlRepository::DESIGN.path_suffix}"
end
def new_design_repo_path
"#{new_path}#{::Gitlab::GlRepository::DESIGN.path_suffix}"
end
end
end
end
# frozen_string_literal: true
module EE
module Projects
module UpdateRepositoryStorageService
extend ::Gitlab::Utils::Override
override :mirror_repositories
def mirror_repositories
super
if project.design_repository.exists?
mirror_repository(type: ::Gitlab::GlRepository::DESIGN)
end
end
override :mark_old_paths_for_archive
def mark_old_paths_for_archive
super
old_repository_storage = project.repository_storage
new_project_path = moved_path(project.disk_path)
project.run_after_commit do
if design_repository.exists?
GitlabShellWorker.perform_async(:mv_repository,
old_repository_storage,
design_repository.disk_path,
"#{new_project_path}.design")
end
end
end
end
end
end
......@@ -24,37 +24,6 @@ module EE
issuables_service(noteable, noteable.project, user).unrelate_issue(noteable_ref)
end
# Parameters:
# - version [DesignManagement::Version]
#
# Example Note text:
#
# "added [1 designs](link-to-version)"
# "changed [2 designs](link-to-version)"
#
# Returns [Array<Note>]: the created Note objects
def design_version_added(version)
design_management_service(version.issue,
version.issue.project,
version.author).design_version_added(version)
end
# Called when a new discussion is created on a design
#
# discussion_note - DiscussionNote
#
# Example Note text:
#
# "started a discussion on screen.png"
#
# Returns the created Note object
def design_discussion_added(discussion_note)
design = discussion_note.noteable
design_management_service(design.issue,
design.project,
discussion_note.author).design_discussion_added(discussion_note)
end
def epic_issue(epic, issue, user, type)
epics_service(epic, user).epic_issue(issue, type)
end
......@@ -192,10 +161,6 @@ module EE
::SystemNotes::IssuablesService.new(noteable: noteable, project: project, author: author)
end
def design_management_service(noteable, project, author)
EE::SystemNotes::DesignManagementService.new(noteable: noteable, project: project, author: author)
end
def epics_service(noteable, author)
EE::SystemNotes::EpicsService.new(noteable: noteable, author: author)
end
......
# frozen_string_literal: true
module EE
module SystemNotes
class DesignManagementService < ::SystemNotes::BaseService
include ActionView::RecordIdentifier
# Parameters:
# - version [DesignManagement::Version]
#
# Example Note text:
#
# "added [1 designs](link-to-version)"
# "changed [2 designs](link-to-version)"
#
# Returns [Array<Note>]: the created Note objects
def design_version_added(version)
events = DesignManagement::Action.events
link_href = designs_path(version: version.id)
version.designs_by_event.map do |(event_name, designs)|
note_data = self.class.design_event_note_data(events[event_name])
icon_name = note_data[:icon]
n = designs.size
body = "%s [%d %s](%s)" % [note_data[:past_tense], n, 'design'.pluralize(n), link_href]
create_note(NoteSummary.new(noteable, project, author, body, action: icon_name))
end
end
# Called when a new discussion is created on a design
#
# discussion_note - DiscussionNote
#
# Example Note text:
#
# "started a discussion on screen.png"
#
# Returns the created Note object
def design_discussion_added(discussion_note)
design = discussion_note.noteable
body = _('started a discussion on %{design_link}') % {
design_link: '[%s](%s)' % [
design.filename,
designs_path(vueroute: design.filename, anchor: dom_id(discussion_note))
]
}
action = :designs_discussion_added
create_note(NoteSummary.new(noteable, project, author, body, action: action))
end
# Take one of the `DesignManagement::Action.events` and
# return:
# * an English past-tense verb.
# * the name of an icon used in renderin a system note
#
# We do not currently internationalize our system notes,
# instead we just produce English-language descriptions.
# See: https://gitlab.com/gitlab-org/gitlab/issues/30408
# See: https://gitlab.com/gitlab-org/gitlab/issues/14056
def self.design_event_note_data(event)
case event
when DesignManagement::Action.events[:creation]
{ icon: 'designs_added', past_tense: 'added' }
when DesignManagement::Action.events[:modification]
{ icon: 'designs_modified', past_tense: 'updated' }
when DesignManagement::Action.events[:deletion]
{ icon: 'designs_removed', past_tense: 'removed' }
else
raise "Unknown event: #{event}"
end
end
private
def designs_path(params = {})
url_helpers.designs_project_issue_path(project, noteable, params)
end
end
end
end
......@@ -107,38 +107,6 @@ describe Note do
end
end
describe 'callbacks' do
describe '#notify_after_create' do
it 'calls #after_note_created on the noteable' do
note = build(:note)
expect(note).to receive(:notify_after_create).and_call_original
expect(note.noteable).to receive(:after_note_created).with(note)
note.save!
end
end
describe '#notify_after_destroy' do
it 'calls #after_note_destroyed on the noteable' do
note = create(:note)
expect(note).to receive(:notify_after_destroy).and_call_original
expect(note.noteable).to receive(:after_note_destroyed).with(note)
note.destroy
end
it 'does not error if noteable is nil' do
note = create(:note)
expect(note).to receive(:notify_after_destroy).and_call_original
expect(note).to receive(:noteable).at_least(:once).and_return(nil)
expect { note.destroy }.not_to raise_error
end
end
end
context 'object storage with background upload' do
before do
stub_uploads_object_storage(AttachmentUploader, background_upload: true)
......
# frozen_string_literal: true
require 'spec_helper'
describe DesignManagement::DeleteDesignsService do
include DesignManagementTestHelpers
let_it_be(:project) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:user) { create(:user) }
let_it_be(:design_repository) { ::Gitlab::GlRepository::DESIGN.repository_resolver.call(project)}
let!(:design) { create(:design, :with_lfs_file, issue: issue) }
subject { described_class.new(project, user, issue: issue, designs: [design]) }
before do
enable_design_management
end
describe '#execute' do
before do
allow_next_instance_of(described_class) do |instance|
allow(instance).to receive(:repository).and_return(design_repository)
end
end
let(:response) { subject.execute }
context 'when service is successful' do
before do
project.add_developer(user)
end
it 'calls repository#log_geo_updated_event', :aggregate_failures do
expect(design_repository).to receive(:log_geo_updated_event)
expect(response).to include(status: :success)
end
end
context 'when service errors' do
it 'does not call repository#log_geo_updated_event', :aggregate_failures do
expect(design_repository).not_to receive(:log_geo_updated_event)
expect(response).to include(status: :error)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe DesignManagement::SaveDesignsService do
include DesignManagementTestHelpers
let_it_be(:project) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:user) { create(:user) }
let_it_be(:design_file) { fixture_file_upload('spec/fixtures/rails_sample.jpg') }
let_it_be(:design_repository) { ::Gitlab::GlRepository::DESIGN.repository_resolver.call(project)}
subject { described_class.new(project, user, issue: issue, files: [design_file]) }
before do
enable_design_management
end
describe '#execute' do
before do
allow_next_instance_of(described_class) do |instance|
allow(instance).to receive(:repository).and_return(design_repository)
end
end
let(:response) { subject.execute }
context 'when service is successful' do
before do
project.add_developer(user)
end
it 'calls repository#log_geo_updated_event', :aggregate_failures do
expect(design_repository).to receive(:log_geo_updated_event)
expect(response).to include(status: :success)
end
end
context 'when service errors' do
it 'does not call repository#log_geo_updated_event', :aggregate_failures do
expect(design_repository).not_to receive(:log_geo_updated_event)
expect(response).to include(status: :error)
end
end
end
end
......@@ -3,63 +3,9 @@
require 'spec_helper'
describe Notes::CreateService do
context 'notes for designs' do
let_it_be(:design) { create(:design, :with_file) }
let_it_be(:project) { design.project }
let_it_be(:user) { project.owner }
subject(:service) { described_class.new(project, user, opts) }
describe "#execute" do
let(:opts) do
{
type: 'DiffNote',
noteable: design,
note: "A message",
position: {
old_path: design.full_path,
new_path: design.full_path,
position_type: 'image',
width: '100',
height: '100',
x: '50',
y: '50',
base_sha: design.diff_refs.base_sha,
start_sha: design.diff_refs.base_sha,
head_sha: design.diff_refs.head_sha
}
}
end
it 'can create diff notes for designs' do
note = service.execute
expect(note).to be_a(DiffNote)
expect(note).to be_persisted
expect(note.noteable).to eq(design)
end
it 'sends a notification about this note', :sidekiq_might_not_need_inline do
notifier = double
allow(::NotificationService).to receive(:new).and_return(notifier)
expect(notifier)
.to receive(:new_note)
.with have_attributes(noteable: design)
service.execute
end
it 'correctly builds the position of the note' do
note = service.execute
expect(note.position.new_path).to eq(design.full_path)
expect(note.position.old_path).to eq(design.full_path)
expect(note.position.diff_refs).to eq(design.diff_refs)
end
context 'note with commands' do
context 'for issues' do
let(:project) { create(:project) }
let(:issuable) { create(:issue, project: project, weight: 10) }
let(:opts) { { noteable_type: 'Issue', noteable_id: issuable.id } }
let(:note_params) { opts }
......@@ -88,6 +34,4 @@ describe Notes::CreateService do
end
end
end
end
end
end
......@@ -4,33 +4,6 @@ require 'spec_helper'
describe Notes::PostProcessService do
describe '#execute' do
context 'when the noteable is a design' do
let_it_be(:noteable) { create(:design, :with_file) }
let_it_be(:discussion_note) { create_note }
subject { described_class.new(note).execute }
def create_note(in_reply_to: nil)
create(:diff_note_on_design, noteable: noteable, in_reply_to: in_reply_to)
end
context 'when the note is the start of a new discussion' do
let(:note) { discussion_note }
it 'creates a new system note' do
expect { subject }.to change { Note.system.count }.by(1)
end
end
context 'when the note is a reply within a discussion' do
let_it_be(:note) { create_note(in_reply_to: discussion_note) }
it 'does not create a new system note' do
expect { subject }.not_to change { Note.system.count }
end
end
end
context 'analytics' do
subject { described_class.new(note) }
......
......@@ -5,60 +5,11 @@ require 'spec_helper'
describe EE::NotificationService, :mailer do
include EmailSpec::Matchers
include NotificationHelpers
include DesignManagementTestHelpers
let(:subject) { NotificationService.new }
let(:mailer) { double(deliver_later: true) }
context 'when notified of a new design diff note' do
let_it_be(:design) { create(:design, :with_file) }
let_it_be(:project) { design.project }
let_it_be(:dev) { create(:user) }
let_it_be(:stranger) { create(:user) }
let_it_be(:note) do
create(:diff_note_on_design,
noteable: design,
note: "Hello #{dev.to_reference}, G'day #{stranger.to_reference}")
end
context 'design management is enabled' do
before do
enable_design_management
project.add_developer(dev)
allow(Notify).to receive(:note_design_email) { mailer }
end
it 'sends new note notifications' do
expect(subject).to receive(:send_new_note_notifications).with(note)
subject.new_note(note)
end
it 'sends a mail to the developer' do
expect(Notify)
.to receive(:note_design_email).with(dev.id, note.id, "mentioned")
subject.new_note(note)
end
it 'does not notify non-developers' do
expect(Notify)
.not_to receive(:note_design_email).with(stranger.id, note.id)
subject.new_note(note)
end
end
context 'design management is disabled' do
it 'does not notify the user' do
expect(Notify).not_to receive(:note_design_email)
subject.new_note(note)
end
end
end
context 'service desk issues' do
before do
allow(Notify).to receive(:service_desk_new_note_email)
......
# frozen_string_literal: true
require "spec_helper"
describe Lfs::FileTransformer do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:file_content) { 'Test file content' }
let(:branch_name) { 'lfs' }
subject { described_class.new(project, repository, branch_name) }
describe '#new_file' do
before do
allow(project).to receive(:lfs_enabled?).and_return(true)
file.write(file_content)
file.rewind
end
after do
file.unlink
end
context 'when repository is a design repository' do
let(:file_path) { "/#{DesignManagement.designs_directory}/test_file.lfs" }
let(:file) { Tempfile.new(file_path) }
let(:repository) { project.design_repository }
it "creates an LfsObject with the file's content" do
subject.new_file(file_path, file)
expect(LfsObject.last.file.read).to eq(file_content)
end
end
end
end
......@@ -15,64 +15,6 @@ describe Projects::HashedStorage::MigrateRepositoryService do
subject(:service) { described_class.new(project: project, old_disk_path: old_disk_path) }
describe '#execute' do
context 'when a project has a design repository' do
before do
allow(service).to receive(:gitlab_shell) { gitlab_shell }
end
context 'when succeeds' do
it 'renames project, wiki and design repositories' do
service.execute
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.git")).to be_truthy
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.wiki.git")).to be_truthy
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.design.git")).to be_truthy
end
it 'move operation is called for each repository' do
expect_move_repository(old_disk_path, new_disk_path)
expect_move_repository("#{old_disk_path}.wiki", "#{new_disk_path}.wiki")
expect_move_repository("#{old_disk_path}.design", "#{new_disk_path}.design")
service.execute
end
end
context 'when one move fails' do
it 'rollsback repositories to original name' do
allow(service).to receive(:move_repository).and_call_original
allow(service).to receive(:move_repository).with(old_disk_path, new_disk_path).once { false } # will disable first move only
expect(service).to receive(:rollback_folder_move).and_call_original
service.execute
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.git")).to be_falsey
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.wiki.git")).to be_falsey
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.design.git")).to be_falsey
expect(project.repository_read_only?).to be_falsey
end
context 'when rollback fails' do
before do
gitlab_shell.mv_repository(project.repository_storage, old_disk_path, new_disk_path)
end
it 'does not try to move nil repository over existing' do
expect(gitlab_shell).not_to receive(:mv_repository).with(project.repository_storage, old_disk_path, new_disk_path)
expect_move_repository("#{old_disk_path}.wiki", "#{new_disk_path}.wiki")
expect_move_repository("#{old_disk_path}.design", "#{new_disk_path}.design")
service.execute
end
end
end
def expect_move_repository(from_name, to_name)
expect(gitlab_shell).to receive(:mv_repository).with(project.repository_storage, from_name, to_name).and_call_original
end
end
context 'when running on a Geo primary node' do
let_it_be(:primary) { create(:geo_node, :primary) }
let_it_be(:secondary) { create(:geo_node) }
......
# frozen_string_literal: true
require 'spec_helper'
describe Projects::HashedStorage::RollbackRepositoryService, :clean_gitlab_redis_shared_state do
include GitHelpers
let(:gitlab_shell) { Gitlab::Shell.new }
let(:project) { create(:project, :repository, :wiki_repo, :design_repo, storage_version: ::Project::HASHED_STORAGE_FEATURES[:repository]) }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
let(:hashed_storage) { Storage::Hashed.new(project) }
let(:old_disk_path) { hashed_storage.disk_path }
let(:new_disk_path) { legacy_storage.disk_path }
subject(:service) { described_class.new(project: project, old_disk_path: project.disk_path) }
describe '#execute' do
before do
allow(service).to receive(:gitlab_shell) { gitlab_shell }
end
context 'when succeeds' do
it 'renames project, wiki and design repositories' do
service.execute
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.git")).to be_truthy
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.wiki.git")).to be_truthy
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.design.git")).to be_truthy
end
it 'updates project to be legacy and not read-only' do
service.execute
expect(project.legacy_storage?).to be_truthy
expect(project.repository_read_only).to be_falsey
end
it 'move operation is called for each repository' do
expect_move_repository(old_disk_path, new_disk_path)
expect_move_repository("#{old_disk_path}.wiki", "#{new_disk_path}.wiki")
expect_move_repository("#{old_disk_path}.design", "#{new_disk_path}.design")
service.execute
end
end
context 'when one move fails' do
it 'rolls repositories back to original name' do
allow(service).to receive(:move_repository).and_call_original
allow(service).to receive(:move_repository).with(old_disk_path, new_disk_path).once { false } # will disable first move only
expect(service).to receive(:rollback_folder_move).and_call_original
service.execute
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.git")).to be_falsey
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.wiki.git")).to be_falsey
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.design.git")).to be_falsey
expect(project.repository_read_only?).to be_falsey
end
context 'when rollback fails' do
before do
gitlab_shell.mv_repository(project.repository_storage, old_disk_path, new_disk_path)
end
it 'does not try to move nil repository over existing' do
expect(gitlab_shell).not_to receive(:mv_repository).with(project.repository_storage, old_disk_path, new_disk_path)
expect_move_repository("#{old_disk_path}.wiki", "#{new_disk_path}.wiki")
expect_move_repository("#{old_disk_path}.design", "#{new_disk_path}.design")
service.execute
end
end
end
def expect_move_repository(from_name, to_name)
expect(gitlab_shell).to receive(:mv_repository).with(project.repository_storage, from_name, to_name).and_call_original
end
end
end
......@@ -83,82 +83,4 @@ describe Projects::TransferService do
end
end
end
describe 'transferring a design repository' do
def design_repository
project.design_repository
end
it 'does not create a design repository' do
expect(subject.execute(group)).to be true
project.clear_memoization(:design_repository)
expect(design_repository.exists?).to be false
end
describe 'when the project has a design repository' do
let(:project_repo_path) { "#{project.path}#{::Gitlab::GlRepository::DESIGN.path_suffix}" }
let(:old_full_path) { "#{user.namespace.full_path}/#{project_repo_path}" }
let(:new_full_path) { "#{group.full_path}/#{project_repo_path}" }
context 'with legacy storage' do
let(:project) { create(:project, :repository, :legacy_storage, :design_repo, namespace: user.namespace) }
it 'moves the repository' do
expect(subject.execute(group)).to be true
project.clear_memoization(:design_repository)
expect(design_repository).to have_attributes(
disk_path: new_full_path,
full_path: new_full_path
)
end
it 'does not move the repository when an error occurs', :aggregate_failures do
allow(subject).to receive(:execute_system_hooks).and_raise('foo')
expect { subject.execute(group) }.to raise_error('foo')
project.clear_memoization(:design_repository)
expect(design_repository).to have_attributes(
disk_path: old_full_path,
full_path: old_full_path
)
end
end
context 'with hashed storage' do
let(:project) { create(:project, :repository, namespace: user.namespace) }
it 'does not move the repository' do
old_disk_path = design_repository.disk_path
expect(subject.execute(group)).to be true
project.clear_memoization(:design_repository)
expect(design_repository).to have_attributes(
disk_path: old_disk_path,
full_path: new_full_path
)
end
it 'does not move the repository when an error occurs' do
old_disk_path = design_repository.disk_path
allow(subject).to receive(:execute_system_hooks).and_raise('foo')
expect { subject.execute(group) }.to raise_error('foo')
project.clear_memoization(:design_repository)
expect(design_repository).to have_attributes(
disk_path: old_disk_path,
full_path: old_full_path
)
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Projects::UpdateRepositoryStorageService do
include Gitlab::ShellAdapter
subject { described_class.new(repository_storage_move) }
before do
allow(Gitlab.config.repositories.storages).to receive(:keys).and_return(%w[default test_second_storage])
end
describe "#execute" do
context 'with design repository' do
include_examples 'moves repository to another storage', 'design' do
let(:project) { create(:project, :repository, repository_read_only: true) }
let(:repository) { project.design_repository }
let(:destination) { 'test_second_storage' }
let(:repository_storage_move) { create(:project_repository_storage_move, :scheduled, project: project, destination_storage_name: destination) }
before do
project.design_repository.create_if_not_exists
end
end
end
end
end
......@@ -6,7 +6,6 @@ describe SystemNoteService do
include ProjectForksHelper
include Gitlab::Routing
include RepoHelpers
include DesignManagementTestHelpers
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, group: group) }
......@@ -49,30 +48,6 @@ describe SystemNoteService do
end
end
describe '.design_version_added' do
let(:version) { create(:design_version) }
it 'calls DesignManagementService' do
expect_next_instance_of(EE::SystemNotes::DesignManagementService) do |service|
expect(service).to receive(:design_version_added).with(version)
end
described_class.design_version_added(version)
end
end
describe '.design_discussion_added' do
let(:discussion_note) { create(:diff_note_on_design) }
it 'calls DesignManagementService' do
expect_next_instance_of(EE::SystemNotes::DesignManagementService) do |service|
expect(service).to receive(:design_discussion_added).with(discussion_note)
end
described_class.design_discussion_added(discussion_note)
end
end
describe '.approve_mr' do
it 'calls MergeRequestsService' do
expect_next_instance_of(::SystemNotes::MergeRequestsService) do |service|
......
......@@ -3,8 +3,6 @@
require 'spec_helper'
describe TodoService do
include DesignManagementTestHelpers
let(:author) { create(:user, username: 'author') }
let(:non_member) { create(:user, username: 'non_member') }
let(:member) { create(:user, username: 'member') }
......@@ -327,35 +325,6 @@ describe TodoService do
end
end
describe 'Designs' do
let(:project) { create(:project) }
let(:issue) { create(:issue, project: project) }
let(:design) { create(:design, issue: issue) }
before do
enable_design_management
project.add_guest(author)
project.add_developer(john_doe)
end
let(:note) do
build(:diff_note_on_design,
noteable: design,
author: author,
note: "Hey #{john_doe.to_reference}")
end
it 'creates a todo for mentioned user on new diff note' do
service.new_note(note, author)
should_create_todo(user: john_doe,
target: design,
action: Todo::MENTIONED,
note: note)
end
end
def should_create_todo(attributes = {})
attributes.reverse_merge!(
project: project,
......
......@@ -14,16 +14,6 @@ describe Projects::DesignManagement::Designs::ResizedImageController do
let(:sha) { design.versions.first.sha }
before do
# TODO these tests are being temporarily skipped unless run in EE,
# as we are in the process of moving Design Management to FOSS in 13.0
# in steps. In the current step the services have not yet been moved,
# and the `design` factory used in this test uses the `:with_smaller_image_versions`
# trait, which calls `GenerateImageVersionsService` to generate the
# smaller image versions.
#
# See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283.
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
enable_design_management
end
......
......@@ -105,6 +105,38 @@ describe Note do
end
end
describe 'callbacks' do
describe '#notify_after_create' do
it 'calls #after_note_created on the noteable' do
note = build(:note)
expect(note).to receive(:notify_after_create).and_call_original
expect(note.noteable).to receive(:after_note_created).with(note)
note.save!
end
end
describe '#notify_after_destroy' do
it 'calls #after_note_destroyed on the noteable' do
note = create(:note)
expect(note).to receive(:notify_after_destroy).and_call_original
expect(note.noteable).to receive(:after_note_destroyed).with(note)
note.destroy
end
it 'does not error if noteable is nil' do
note = create(:note)
expect(note).to receive(:notify_after_destroy).and_call_original
expect(note).to receive(:noteable).at_least(:once).and_return(nil)
expect { note.destroy }.not_to raise_error
end
end
end
describe "Commit notes" do
before do
allow(Gitlab::Git::KeepAround).to receive(:execute).and_call_original
......
......@@ -3,6 +3,7 @@ require 'spec_helper'
describe DesignManagement::DeleteDesignsService do
include DesignManagementTestHelpers
let_it_be(:project) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:user) { create(:user) }
......@@ -97,21 +98,10 @@ describe DesignManagement::DeleteDesignsService do
run_service
end
it 'creates a new verison' do
it 'creates a new version' do
expect { run_service }.to change { DesignManagement::Version.where(issue: issue).count }.by(1)
end
it 'calls repository#log_geo_updated_event' do
design_repository = ::Gitlab::GlRepository::DESIGN.repository_resolver.call(project)
allow_next_instance_of(described_class) do |instance|
allow(instance).to receive(:repository).and_return(design_repository)
end
expect(design_repository).to receive(:log_geo_updated_event)
run_service
end
it 'returns the new version' do
version = response[:version]
......
......@@ -23,15 +23,8 @@ describe DesignManagement::DesignUserNotesCountService, :use_clean_rails_memory_
end
end
# TODO These tests are being temporarily skipped unless run in EE,
# as we are in the process of moving Design Management to FOSS in 13.0
# in steps. In the current step the services have not yet been moved.
#
# See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283.
describe 'cache invalidation' do
it 'changes when a new note is created' do
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
new_note_attrs = attributes_for(:diff_note_on_design, noteable: design)
expect do
......@@ -40,8 +33,6 @@ describe DesignManagement::DesignUserNotesCountService, :use_clean_rails_memory_
end
it 'changes when a note is destroyed' do
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
note = create(:diff_note_on_design, noteable: design)
expect do
......
......@@ -182,16 +182,6 @@ describe DesignManagement::SaveDesignsService do
expect(updated_designs.first.versions.size).to eq(1)
end
end
it 'calls repository#log_geo_updated_event' do
expect(design_repository).to receive(:log_geo_updated_event)
allow_next_instance_of(described_class) do |instance|
allow(instance).to receive(:repository).and_return(design_repository)
end
run_service
end
end
context 'when a design has not changed since its previous version' do
......
......@@ -81,6 +81,23 @@ describe Lfs::FileTransformer do
expect(LfsObject.last.file.read).to eq file_content
end
context 'when repository is a design repository' do
let(:file_path) { "/#{DesignManagement.designs_directory}/test_file.lfs" }
let(:repository) { project.design_repository }
it "creates an LfsObject with the file's content" do
subject.new_file(file_path, file)
expect(LfsObject.last.file.read).to eq(file_content)
end
it 'saves the correct repository_type to LfsObjectsProject' do
subject.new_file(file_path, file)
expect(project.lfs_objects_projects.first.repository_type).to eq('design')
end
end
end
context "when doesn't use LFS" do
......
......@@ -342,6 +342,60 @@ describe Notes::CreateService do
end
end
context 'design note' do
subject(:service) { described_class.new(project, user, params) }
let_it_be(:design) { create(:design, :with_file) }
let_it_be(:project) { design.project }
let_it_be(:user) { project.owner }
let_it_be(:params) do
{
type: 'DiffNote',
noteable: design,
note: "A message",
position: {
old_path: design.full_path,
new_path: design.full_path,
position_type: 'image',
width: '100',
height: '100',
x: '50',
y: '50',
base_sha: design.diff_refs.base_sha,
start_sha: design.diff_refs.base_sha,
head_sha: design.diff_refs.head_sha
}
}
end
it 'can create diff notes for designs' do
note = service.execute
expect(note).to be_a(DiffNote)
expect(note).to be_persisted
expect(note.noteable).to eq(design)
end
it 'sends a notification about this note', :sidekiq_might_not_need_inline do
notifier = double
allow(::NotificationService).to receive(:new).and_return(notifier)
expect(notifier)
.to receive(:new_note)
.with have_attributes(noteable: design)
service.execute
end
it 'correctly builds the position of the note' do
note = service.execute
expect(note.position.new_path).to eq(design.full_path)
expect(note.position.old_path).to eq(design.full_path)
expect(note.position.diff_refs).to eq(design.diff_refs)
end
end
context 'note with emoji only' do
it 'creates regular note' do
opts = {
......
......@@ -43,5 +43,32 @@ describe Notes::PostProcessService do
described_class.new(@note).execute
end
end
context 'when the noteable is a design' do
let_it_be(:noteable) { create(:design, :with_file) }
let_it_be(:discussion_note) { create_note }
subject { described_class.new(note).execute }
def create_note(in_reply_to: nil)
create(:diff_note_on_design, noteable: noteable, in_reply_to: in_reply_to)
end
context 'when the note is the start of a new discussion' do
let(:note) { discussion_note }
it 'creates a new system note' do
expect { subject }.to change { Note.system.count }.by(1)
end
end
context 'when the note is a reply within a discussion' do
let_it_be(:note) { create_note(in_reply_to: discussion_note) }
it 'does not create a new system note' do
expect { subject }.not_to change { Note.system.count }
end
end
end
end
end
......@@ -709,6 +709,57 @@ describe NotificationService, :mailer do
end
end
end
context 'when notified of a new design diff note' do
include DesignManagementTestHelpers
let_it_be(:design) { create(:design, :with_file) }
let_it_be(:project) { design.project }
let_it_be(:dev) { create(:user) }
let_it_be(:stranger) { create(:user) }
let_it_be(:note) do
create(:diff_note_on_design,
noteable: design,
note: "Hello #{dev.to_reference}, G'day #{stranger.to_reference}")
end
let(:mailer) { double(deliver_later: true) }
context 'design management is enabled' do
before do
enable_design_management
project.add_developer(dev)
allow(Notify).to receive(:note_design_email) { mailer }
end
it 'sends new note notifications' do
expect(subject).to receive(:send_new_note_notifications).with(note)
subject.new_note(note)
end
it 'sends a mail to the developer' do
expect(Notify)
.to receive(:note_design_email).with(dev.id, note.id, 'mentioned')
subject.new_note(note)
end
it 'does not notify non-developers' do
expect(Notify)
.not_to receive(:note_design_email).with(stranger.id, note.id)
subject.new_note(note)
end
end
context 'design management is disabled' do
it 'does not notify the user' do
expect(Notify).not_to receive(:note_design_email)
subject.new_note(note)
end
end
end
end
describe '#send_new_release_notifications', :deliver_mails_inline, :sidekiq_inline do
......
......@@ -6,7 +6,7 @@ describe Projects::HashedStorage::MigrateRepositoryService do
include GitHelpers
let(:gitlab_shell) { Gitlab::Shell.new }
let(:project) { create(:project, :legacy_storage, :repository, :wiki_repo) }
let(:project) { create(:project, :legacy_storage, :repository, :wiki_repo, :design_repo) }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
let(:hashed_storage) { Storage::Hashed.new(project) }
......@@ -45,11 +45,12 @@ describe Projects::HashedStorage::MigrateRepositoryService do
end
context 'when succeeds' do
it 'renames project and wiki repositories' do
it 'renames project, wiki and design repositories' do
service.execute
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.git")).to be_truthy
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.wiki.git")).to be_truthy
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.design.git")).to be_truthy
end
it 'updates project to be hashed and not read-only' do
......@@ -59,9 +60,10 @@ describe Projects::HashedStorage::MigrateRepositoryService do
expect(project.repository_read_only).to be_falsey
end
it 'move operation is called for both repositories' do
it 'move operation is called for all repositories' do
expect_move_repository(old_disk_path, new_disk_path)
expect_move_repository("#{old_disk_path}.wiki", "#{new_disk_path}.wiki")
expect_move_repository("#{old_disk_path}.design", "#{new_disk_path}.design")
service.execute
end
......@@ -86,6 +88,7 @@ describe Projects::HashedStorage::MigrateRepositoryService do
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.git")).to be_falsey
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.wiki.git")).to be_falsey
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.design.git")).to be_falsey
expect(project.repository_read_only?).to be_falsey
end
......@@ -97,6 +100,7 @@ describe Projects::HashedStorage::MigrateRepositoryService do
it 'does not try to move nil repository over existing' do
expect(gitlab_shell).not_to receive(:mv_repository).with(project.repository_storage, old_disk_path, new_disk_path)
expect_move_repository("#{old_disk_path}.wiki", "#{new_disk_path}.wiki")
expect_move_repository("#{old_disk_path}.design", "#{new_disk_path}.design")
service.execute
end
......
......@@ -6,7 +6,7 @@ describe Projects::HashedStorage::RollbackRepositoryService, :clean_gitlab_redis
include GitHelpers
let(:gitlab_shell) { Gitlab::Shell.new }
let(:project) { create(:project, :repository, :wiki_repo, storage_version: ::Project::HASHED_STORAGE_FEATURES[:repository]) }
let(:project) { create(:project, :repository, :wiki_repo, :design_repo, storage_version: ::Project::HASHED_STORAGE_FEATURES[:repository]) }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
let(:hashed_storage) { Storage::Hashed.new(project) }
......@@ -45,11 +45,12 @@ describe Projects::HashedStorage::RollbackRepositoryService, :clean_gitlab_redis
end
context 'when succeeds' do
it 'renames project and wiki repositories' do
it 'renames project, wiki and design repositories' do
service.execute
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.git")).to be_truthy
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.wiki.git")).to be_truthy
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.design.git")).to be_truthy
end
it 'updates project to be legacy and not read-only' do
......@@ -62,6 +63,7 @@ describe Projects::HashedStorage::RollbackRepositoryService, :clean_gitlab_redis
it 'move operation is called for both repositories' do
expect_move_repository(old_disk_path, new_disk_path)
expect_move_repository("#{old_disk_path}.wiki", "#{new_disk_path}.wiki")
expect_move_repository("#{old_disk_path}.design", "#{new_disk_path}.design")
service.execute
end
......@@ -86,6 +88,7 @@ describe Projects::HashedStorage::RollbackRepositoryService, :clean_gitlab_redis
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.git")).to be_falsey
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.wiki.git")).to be_falsey
expect(gitlab_shell.repository_exists?(project.repository_storage, "#{new_disk_path}.design.git")).to be_falsey
expect(project.repository_read_only?).to be_falsey
end
......@@ -97,6 +100,7 @@ describe Projects::HashedStorage::RollbackRepositoryService, :clean_gitlab_redis
it 'does not try to move nil repository over existing' do
expect(gitlab_shell).not_to receive(:mv_repository).with(project.repository_storage, old_disk_path, new_disk_path)
expect_move_repository("#{old_disk_path}.wiki", "#{new_disk_path}.wiki")
expect_move_repository("#{old_disk_path}.design", "#{new_disk_path}.design")
service.execute
end
......
......@@ -359,6 +359,90 @@ describe Projects::TransferService do
end
end
describe 'transferring a design repository' do
subject { described_class.new(project, user) }
before do
group.add_owner(user)
end
def design_repository
project.design_repository
end
it 'does not create a design repository' do
expect(subject.execute(group)).to be true
project.clear_memoization(:design_repository)
expect(design_repository.exists?).to be false
end
describe 'when the project has a design repository' do
let(:project_repo_path) { "#{project.path}#{::Gitlab::GlRepository::DESIGN.path_suffix}" }
let(:old_full_path) { "#{user.namespace.full_path}/#{project_repo_path}" }
let(:new_full_path) { "#{group.full_path}/#{project_repo_path}" }
context 'with legacy storage' do
let(:project) { create(:project, :repository, :legacy_storage, :design_repo, namespace: user.namespace) }
it 'moves the repository' do
expect(subject.execute(group)).to be true
project.clear_memoization(:design_repository)
expect(design_repository).to have_attributes(
disk_path: new_full_path,
full_path: new_full_path
)
end
it 'does not move the repository when an error occurs', :aggregate_failures do
allow(subject).to receive(:execute_system_hooks).and_raise('foo')
expect { subject.execute(group) }.to raise_error('foo')
project.clear_memoization(:design_repository)
expect(design_repository).to have_attributes(
disk_path: old_full_path,
full_path: old_full_path
)
end
end
context 'with hashed storage' do
let(:project) { create(:project, :repository, namespace: user.namespace) }
it 'does not move the repository' do
old_disk_path = design_repository.disk_path
expect(subject.execute(group)).to be true
project.clear_memoization(:design_repository)
expect(design_repository).to have_attributes(
disk_path: old_disk_path,
full_path: new_full_path
)
end
it 'does not move the repository when an error occurs' do
old_disk_path = design_repository.disk_path
allow(subject).to receive(:execute_system_hooks).and_raise('foo')
expect { subject.execute(group) }.to raise_error('foo')
project.clear_memoization(:design_repository)
expect(design_repository).to have_attributes(
disk_path: old_disk_path,
full_path: old_full_path
)
end
end
end
end
def rugged_config
rugged_repo(project.repository).config
end
......
......@@ -141,5 +141,18 @@ describe Projects::UpdateRepositoryStorageService do
end
end
end
context 'with design repository' do
include_examples 'moves repository to another storage', 'design' do
let(:project) { create(:project, :repository, repository_read_only: true) }
let(:repository) { project.design_repository }
let(:destination) { 'test_second_storage' }
let(:repository_storage_move) { create(:project_repository_storage_move, :scheduled, project: project, destination_storage_name: destination) }
before do
project.design_repository.create_if_not_exists
end
end
end
end
end
......@@ -6,6 +6,7 @@ describe SystemNoteService do
include Gitlab::Routing
include RepoHelpers
include AssetsHelpers
include DesignManagementTestHelpers
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, group: group) }
......@@ -636,4 +637,28 @@ describe SystemNoteService do
described_class.auto_resolve_prometheus_alert(noteable, project, author)
end
end
describe '.design_version_added' do
let(:version) { create(:design_version) }
it 'calls DesignManagementService' do
expect_next_instance_of(SystemNotes::DesignManagementService) do |service|
expect(service).to receive(:design_version_added).with(version)
end
described_class.design_version_added(version)
end
end
describe '.design_discussion_added' do
let(:discussion_note) { create(:diff_note_on_design) }
it 'calls DesignManagementService' do
expect_next_instance_of(SystemNotes::DesignManagementService) do |service|
expect(service).to receive(:design_discussion_added).with(discussion_note)
end
described_class.design_discussion_added(discussion_note)
end
end
end
......@@ -2,7 +2,7 @@
require 'spec_helper'
describe EE::SystemNotes::DesignManagementService do
describe SystemNotes::DesignManagementService do
let(:project) { create(:project) }
let(:issue) { create(:issue, project: project) }
......
......@@ -895,6 +895,36 @@ describe TodoService do
end
end
describe 'Designs' do
include DesignManagementTestHelpers
let(:issue) { create(:issue, project: project) }
let(:design) { create(:design, issue: issue) }
before do
enable_design_management
project.add_guest(author)
project.add_developer(john_doe)
end
let(:note) do
build(:diff_note_on_design,
noteable: design,
author: author,
note: "Hey #{john_doe.to_reference}")
end
it 'creates a todo for mentioned user on new diff note' do
service.new_note(note, author)
should_create_todo(user: john_doe,
target: design,
action: Todo::MENTIONED,
note: note)
end
end
describe '#update_note' do
let(:noteable) { create(:issue, project: project) }
let(:note) { create(:note, project: project, note: mentions, noteable: noteable) }
......
......@@ -3,14 +3,6 @@
require 'spec_helper'
describe DesignManagement::NewVersionWorker do
# TODO a number of these tests are being temporarily skipped unless run in EE,
# as we are in the process of moving Design Management to FOSS in 13.0
# in steps. In the current step the services have not yet been moved, and
# certain services are called within these tests:
# - `SystemNoteService`
# - `DesignManagement::GenerateImageVersionsService`
#
# See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283.
describe '#perform' do
let(:worker) { described_class.new }
......@@ -18,16 +10,12 @@ describe DesignManagement::NewVersionWorker do
let(:version_id) { -1 }
it 'does not create system notes' do
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
expect(SystemNoteService).not_to receive(:design_version_added)
worker.perform(version_id)
end
it 'does not invoke GenerateImageVersionsService' do
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
expect(DesignManagement::GenerateImageVersionsService).not_to receive(:new)
worker.perform(version_id)
......@@ -45,14 +33,10 @@ describe DesignManagement::NewVersionWorker do
let_it_be(:version) { create(:design_version, :with_lfs_file, designs_count: 2) }
it 'creates a system note' do
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
expect { worker.perform(version.id) }.to change { Note.system.count }.by(1)
end
it 'invokes GenerateImageVersionsService' do
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
expect_next_instance_of(DesignManagement::GenerateImageVersionsService) do |service|
expect(service).to receive(:execute)
end
......@@ -61,8 +45,6 @@ describe DesignManagement::NewVersionWorker do
end
it 'does not log anything' do
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
expect(Sidekiq.logger).not_to receive(:warn)
worker.perform(version.id)
......@@ -77,14 +59,10 @@ describe DesignManagement::NewVersionWorker do
end
it 'creates two system notes' do
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
expect { worker.perform(version.id) }.to change { Note.system.count }.by(2)
end
it 'calls design_version_added' do
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
expect(SystemNoteService).to receive(:design_version_added).with(version)
worker.perform(version.id)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment