[36238] Extract and fix user references in other objects (#9007)
* Move replacing invalid references into separate job for principals * Write migration to remove existing invalid custom values and responsible * Fix other specs * Fix other specs * rewrite replacing user in records * consolidate principal deletion * include placeholder users in spec Co-authored-by: ulferts <jens.ulferts@googlemail.com>pull/9015/head
parent
36e229a461
commit
f4dfd6c6c6
@ -1,33 +0,0 @@ |
|||||||
module Journals |
|
||||||
class UserReferenceUpdateService |
|
||||||
attr_accessor :original_user |
|
||||||
|
|
||||||
def initialize(original_user) |
|
||||||
self.original_user = original_user |
|
||||||
end |
|
||||||
|
|
||||||
def call(substitute_user) |
|
||||||
journal_classes.each do |klass| |
|
||||||
foreign_keys.each do |foreign_key| |
|
||||||
if klass.column_names.include? foreign_key |
|
||||||
klass |
|
||||||
.where(foreign_key => original_user.id) |
|
||||||
.update_all(foreign_key => substitute_user.id) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
ServiceResult.new success: true |
|
||||||
end |
|
||||||
|
|
||||||
private |
|
||||||
|
|
||||||
def journal_classes |
|
||||||
[Journal] + Journal::BaseJournal.subclasses |
|
||||||
end |
|
||||||
|
|
||||||
def foreign_keys |
|
||||||
%w[author_id user_id assigned_to_id responsible_id] |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
@ -0,0 +1,126 @@ |
|||||||
|
#-- encoding: UTF-8 |
||||||
|
|
||||||
|
#-- copyright |
||||||
|
# OpenProject is an open source project management software. |
||||||
|
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License version 3. |
||||||
|
# |
||||||
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||||
|
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||||
|
# Copyright (C) 2010-2013 the ChiliProject Team |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License |
||||||
|
# as published by the Free Software Foundation; either version 2 |
||||||
|
# of the License, or (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program; if not, write to the Free Software |
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||||
|
# |
||||||
|
# See docs/COPYRIGHT.rdoc for more details. |
||||||
|
#++ |
||||||
|
|
||||||
|
# Rewrites references to a principal from one principal to the other. |
||||||
|
# No data is to be removed. |
||||||
|
module Principals |
||||||
|
class ReplaceReferencesService |
||||||
|
def call(from:, to:) |
||||||
|
rewrite_active_models(from, to) |
||||||
|
rewrite_custom_value(from, to) |
||||||
|
rewrite_default_journals(from, to) |
||||||
|
rewrite_customizable_journals(from, to) |
||||||
|
|
||||||
|
ServiceResult.new success: true |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
# rubocop:disable Rails/SkipsModelValidations |
||||||
|
def rewrite_active_models(from, to) |
||||||
|
rewrite_author(from, to) |
||||||
|
rewrite_user(from, to) |
||||||
|
rewrite_assigned_to(from, to) |
||||||
|
rewrite_responsible(from, to) |
||||||
|
end |
||||||
|
|
||||||
|
def rewrite_custom_value(from, to) |
||||||
|
CustomValue |
||||||
|
.where(custom_field_id: CustomField.where(field_format: 'user')) |
||||||
|
.where(value: from.id.to_s) |
||||||
|
.update_all(value: to.id.to_s) |
||||||
|
end |
||||||
|
|
||||||
|
def rewrite_default_journals(from, to) |
||||||
|
journal_classes.each do |klass| |
||||||
|
foreign_keys.each do |foreign_key| |
||||||
|
if klass.column_names.include? foreign_key |
||||||
|
klass |
||||||
|
.where(foreign_key => from.id) |
||||||
|
.update_all(foreign_key => to.id) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def rewrite_customizable_journals(from, to) |
||||||
|
Journal::CustomizableJournal |
||||||
|
.joins(:custom_field) |
||||||
|
.where(custom_fields: { field_format: 'user' }) |
||||||
|
.where(value: from.id.to_s) |
||||||
|
.update_all(value: to.id.to_s) |
||||||
|
end |
||||||
|
|
||||||
|
def rewrite_author(from, to) |
||||||
|
[WorkPackage, |
||||||
|
Attachment, |
||||||
|
WikiContent, |
||||||
|
News, |
||||||
|
Comment, |
||||||
|
Message, |
||||||
|
Budget, |
||||||
|
MeetingAgenda, |
||||||
|
MeetingMinutes].each do |klass| |
||||||
|
klass.where(author_id: from.id).update_all(author_id: to.id) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def rewrite_user(from, to) |
||||||
|
[TimeEntry, |
||||||
|
::Query, |
||||||
|
Changeset, |
||||||
|
CostQuery, |
||||||
|
MeetingParticipant].each do |klass| |
||||||
|
klass.where(user_id: from.id).update_all(user_id: to.id) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def rewrite_assigned_to(from, to) |
||||||
|
[WorkPackage].each do |klass| |
||||||
|
klass.where(assigned_to_id: from.id).update_all(assigned_to_id: to.id) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def rewrite_responsible(from, to) |
||||||
|
[WorkPackage].each do |klass| |
||||||
|
klass.where(responsible_id: from.id).update_all(responsible_id: to.id) |
||||||
|
end |
||||||
|
end |
||||||
|
# rubocop:enable Rails/SkipsModelValidations |
||||||
|
|
||||||
|
def journal_classes |
||||||
|
[Journal] + Journal::BaseJournal.subclasses |
||||||
|
end |
||||||
|
|
||||||
|
def foreign_keys |
||||||
|
%w[author_id user_id assigned_to_id responsible_id] |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,85 @@ |
|||||||
|
#-- encoding: UTF-8 |
||||||
|
|
||||||
|
#-- copyright |
||||||
|
# OpenProject is an open source project management software. |
||||||
|
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License version 3. |
||||||
|
# |
||||||
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||||
|
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||||
|
# Copyright (C) 2010-2013 the ChiliProject Team |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License |
||||||
|
# as published by the Free Software Foundation; either version 2 |
||||||
|
# of the License, or (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program; if not, write to the Free Software |
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||||
|
# |
||||||
|
# See docs/COPYRIGHT.rdoc for more details. |
||||||
|
#++ |
||||||
|
|
||||||
|
class Principals::DeleteJob < ApplicationJob |
||||||
|
queue_with_priority :low |
||||||
|
|
||||||
|
def perform(principal) |
||||||
|
Principal.transaction do |
||||||
|
delete_associated(principal) |
||||||
|
replace_references(principal) |
||||||
|
update_cost_queries(principal) |
||||||
|
|
||||||
|
principal.destroy |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def replace_references(principal) |
||||||
|
Principals::ReplaceReferencesService |
||||||
|
.new |
||||||
|
.call(from: principal, to: DeletedUser.first) |
||||||
|
.tap do |call| |
||||||
|
raise ActiveRecord::Rollback if call.failure? |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def delete_associated(principal) |
||||||
|
delete_private_queries(principal) |
||||||
|
end |
||||||
|
|
||||||
|
def delete_private_queries(principal) |
||||||
|
::Query.where(user_id: principal.id, is_public: false).delete_all |
||||||
|
CostQuery.where(user_id: principal.id, is_public: false).delete_all |
||||||
|
end |
||||||
|
|
||||||
|
# rubocop:disable Rails/SkipsModelValidations |
||||||
|
def update_cost_queries(principal) |
||||||
|
CostQuery.in_batches.each_record do |query| |
||||||
|
serialized = query.serialized |
||||||
|
|
||||||
|
serialized[:filters] = serialized[:filters].map do |name, options| |
||||||
|
remove_cost_query_values(name, options, principal) |
||||||
|
end.compact |
||||||
|
|
||||||
|
CostQuery.where(id: query.id).update_all(serialized: serialized) |
||||||
|
end |
||||||
|
end |
||||||
|
# rubocop:enable Rails/SkipsModelValidations |
||||||
|
|
||||||
|
def remove_cost_query_values(name, options, principal) |
||||||
|
options[:values].delete(principal.id.to_s) if %w[UserId AuthorId AssignedToId ResponsibleId].include?(name) |
||||||
|
|
||||||
|
if options[:values].nil? || options[:values].any? |
||||||
|
[name, options] |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,22 @@ |
|||||||
|
class ReplaceInvalidPrincipalReferences < ActiveRecord::Migration[6.1] |
||||||
|
def up |
||||||
|
DeletedUser.reset_column_information |
||||||
|
deleted_user_id = DeletedUser.first.id |
||||||
|
|
||||||
|
say "Replacing invalid custom value user references" |
||||||
|
CustomValue |
||||||
|
.joins(:custom_field) |
||||||
|
.where("#{CustomField.table_name}.field_format" => 'user') |
||||||
|
.where("value NOT IN (SELECT id::text FROM users)") |
||||||
|
.update_all(value: deleted_user_id) |
||||||
|
|
||||||
|
say "Replacing invalid responsible user references in work packages" |
||||||
|
WorkPackage |
||||||
|
.where("responsible_id NOT IN (SELECT id FROM users)") |
||||||
|
.update_all(responsible_id: deleted_user_id) |
||||||
|
end |
||||||
|
|
||||||
|
def down |
||||||
|
# Nothing to do, as only invalid data is fixed |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,32 @@ |
|||||||
|
#-- copyright |
||||||
|
# OpenProject is an open source project management software. |
||||||
|
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License version 3. |
||||||
|
# |
||||||
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||||
|
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||||
|
# Copyright (C) 2010-2013 the ChiliProject Team |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License |
||||||
|
# as published by the Free Software Foundation; either version 2 |
||||||
|
# of the License, or (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program; if not, write to the Free Software |
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||||
|
# |
||||||
|
# See docs/COPYRIGHT.rdoc for more details. |
||||||
|
#++ |
||||||
|
|
||||||
|
FactoryBot.define do |
||||||
|
factory :journal_time_entry_journal, class: Journal::TimeEntryJournal do |
||||||
|
end |
||||||
|
end |
@ -1,208 +0,0 @@ |
|||||||
#-- copyright |
|
||||||
# OpenProject is an open source project management software. |
|
||||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
|
||||||
# |
|
||||||
# This program is free software; you can redistribute it and/or |
|
||||||
# modify it under the terms of the GNU General Public License version 3. |
|
||||||
# |
|
||||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
|
||||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
|
||||||
# Copyright (C) 2010-2013 the ChiliProject Team |
|
||||||
# |
|
||||||
# This program is free software; you can redistribute it and/or |
|
||||||
# modify it under the terms of the GNU General Public License |
|
||||||
# as published by the Free Software Foundation; either version 2 |
|
||||||
# of the License, or (at your option) any later version. |
|
||||||
# |
|
||||||
# This program is distributed in the hope that it will be useful, |
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
# GNU General Public License for more details. |
|
||||||
# |
|
||||||
# You should have received a copy of the GNU General Public License |
|
||||||
# along with this program; if not, write to the Free Software |
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
||||||
# |
|
||||||
# See docs/COPYRIGHT.rdoc for more details. |
|
||||||
#++ |
|
||||||
|
|
||||||
require File.dirname(__FILE__) + '/../spec_helper' |
|
||||||
|
|
||||||
describe User, '#destroy', type: :model do |
|
||||||
let(:user) { FactoryBot.create(:user) } |
|
||||||
let(:user2) { FactoryBot.create(:user) } |
|
||||||
let(:substitute_user) { DeletedUser.first } |
|
||||||
let(:project) { FactoryBot.create(:valid_project) } |
|
||||||
|
|
||||||
before do |
|
||||||
user |
|
||||||
user2 |
|
||||||
end |
|
||||||
|
|
||||||
after do |
|
||||||
User.current = nil |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples_for 'costs updated journalized associated object' do |
|
||||||
before do |
|
||||||
User.current = user2 |
|
||||||
associations.each do |association| |
|
||||||
associated_instance.send(association.to_s + '=', user2) |
|
||||||
end |
|
||||||
associated_instance.save! |
|
||||||
|
|
||||||
User.current = user # in order to have the content journal created by the user |
|
||||||
associated_instance.reload |
|
||||||
associations.each do |association| |
|
||||||
associated_instance.send(association.to_s + '=', user) |
|
||||||
end |
|
||||||
associated_instance.save! |
|
||||||
|
|
||||||
user.destroy |
|
||||||
associated_instance.reload |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(associated_class.find_by_id(associated_instance.id)).to eq(associated_instance) } |
|
||||||
it 'should replace the user on all associations' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.send(association)).to eq(substitute_user) |
|
||||||
end |
|
||||||
end |
|
||||||
it { expect(associated_instance.journals.first.user).to eq(user2) } |
|
||||||
it 'should update first journal details' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.journals.first.details["#{association}_id".to_sym].last).to eq(user2.id) |
|
||||||
end |
|
||||||
end |
|
||||||
it { expect(associated_instance.journals.last.user).to eq(substitute_user) } |
|
||||||
it 'should update second journal details' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.journals.last.details["#{association}_id".to_sym].last).to eq(substitute_user.id) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples_for 'costs created journalized associated object' do |
|
||||||
before do |
|
||||||
User.current = user # in order to have the content journal created by the user |
|
||||||
associations.each do |association| |
|
||||||
associated_instance.send(association.to_s + '=', user) |
|
||||||
end |
|
||||||
associated_instance.save! |
|
||||||
|
|
||||||
User.current = user2 |
|
||||||
associated_instance.reload |
|
||||||
associations.each do |association| |
|
||||||
associated_instance.send(association.to_s + '=', user2) |
|
||||||
end |
|
||||||
associated_instance.save! |
|
||||||
|
|
||||||
user.destroy |
|
||||||
associated_instance.reload |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(associated_class.find_by_id(associated_instance.id)).to eq(associated_instance) } |
|
||||||
it 'should keep the current user on all associations' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.send(association)).to eq(user2) |
|
||||||
end |
|
||||||
end |
|
||||||
it { expect(associated_instance.journals.first.user).to eq(substitute_user) } |
|
||||||
it 'should update the first journal' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.journals.first.details["#{association}_id".to_sym].last).to eq(substitute_user.id) |
|
||||||
end |
|
||||||
end |
|
||||||
it { expect(associated_instance.journals.last.user).to eq(user2) } |
|
||||||
it 'should update the last journal' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.journals.last.details["#{association}_id".to_sym].first).to eq(substitute_user.id) |
|
||||||
expect(associated_instance.journals.last.details["#{association}_id".to_sym].last).to eq(user2.id) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user updated a cost object' do |
|
||||||
let(:associations) { [:author] } |
|
||||||
let(:associated_instance) { FactoryBot.build(:budget) } |
|
||||||
let(:associated_class) { Budget } |
|
||||||
|
|
||||||
it_should_behave_like 'costs updated journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user created a cost object' do |
|
||||||
let(:associations) { [:author] } |
|
||||||
let(:associated_instance) { FactoryBot.build(:budget) } |
|
||||||
let(:associated_class) { Budget } |
|
||||||
|
|
||||||
it_should_behave_like 'costs created journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has a labor_budget_item associated' do |
|
||||||
let(:item) { FactoryBot.build(:labor_budget_item, user: user) } |
|
||||||
|
|
||||||
before do |
|
||||||
item.save! |
|
||||||
|
|
||||||
user.destroy |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(LaborBudgetItem.find_by_id(item.id)).to eq(item) } |
|
||||||
it { expect(item.user_id).to eq(user.id) } |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has a cost entry' do |
|
||||||
let(:work_package) { FactoryBot.create(:work_package) } |
|
||||||
let(:entry) do |
|
||||||
FactoryBot.create(:cost_entry, user: user, |
|
||||||
project: work_package.project, |
|
||||||
units: 100.0, |
|
||||||
spent_on: Date.today, |
|
||||||
work_package: work_package, |
|
||||||
comments: '') |
|
||||||
end |
|
||||||
|
|
||||||
before do |
|
||||||
FactoryBot.create(:member, project: work_package.project, |
|
||||||
user: user, |
|
||||||
roles: [FactoryBot.build(:role)]) |
|
||||||
entry |
|
||||||
|
|
||||||
user.destroy |
|
||||||
|
|
||||||
entry.reload |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(entry.user_id).to eq(user.id) } |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user is assigned an hourly rate' do |
|
||||||
let(:hourly_rate) do |
|
||||||
FactoryBot.build(:hourly_rate, user: user, |
|
||||||
project: project) |
|
||||||
end |
|
||||||
|
|
||||||
before do |
|
||||||
hourly_rate.save! |
|
||||||
user.destroy |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(HourlyRate.find_by_id(hourly_rate.id)).to eq(hourly_rate) } |
|
||||||
it { expect(hourly_rate.reload.user_id).to eq(user.id) } |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user is assigned a default hourly rate' do |
|
||||||
let(:default_hourly_rate) do |
|
||||||
FactoryBot.build(:default_hourly_rate, user: user, |
|
||||||
project: project) |
|
||||||
end |
|
||||||
|
|
||||||
before do |
|
||||||
default_hourly_rate.save! |
|
||||||
user.destroy |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(DefaultHourlyRate.find_by_id(default_hourly_rate.id)).to eq(default_hourly_rate) } |
|
||||||
it { expect(default_hourly_rate.reload.user_id).to eq(user.id) } |
|
||||||
end |
|
||||||
end |
|
@ -0,0 +1,32 @@ |
|||||||
|
#-- copyright |
||||||
|
# OpenProject is an open source project management software. |
||||||
|
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License version 3. |
||||||
|
# |
||||||
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||||
|
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||||
|
# Copyright (C) 2010-2013 the ChiliProject Team |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License |
||||||
|
# as published by the Free Software Foundation; either version 2 |
||||||
|
# of the License, or (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program; if not, write to the Free Software |
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||||
|
# |
||||||
|
# See docs/COPYRIGHT.rdoc for more details. |
||||||
|
#++ |
||||||
|
|
||||||
|
FactoryBot.define do |
||||||
|
factory :journal_document_journal, class: Journal::DocumentJournal do |
||||||
|
end |
||||||
|
end |
@ -1,207 +0,0 @@ |
|||||||
#-- copyright |
|
||||||
# OpenProject is an open source project management software. |
|
||||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
|
||||||
# |
|
||||||
# This program is free software; you can redistribute it and/or |
|
||||||
# modify it under the terms of the GNU General Public License version 3. |
|
||||||
# |
|
||||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
|
||||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
|
||||||
# Copyright (C) 2010-2013 the ChiliProject Team |
|
||||||
# |
|
||||||
# This program is free software; you can redistribute it and/or |
|
||||||
# modify it under the terms of the GNU General Public License |
|
||||||
# as published by the Free Software Foundation; either version 2 |
|
||||||
# of the License, or (at your option) any later version. |
|
||||||
# |
|
||||||
# This program is distributed in the hope that it will be useful, |
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
# GNU General Public License for more details. |
|
||||||
# |
|
||||||
# You should have received a copy of the GNU General Public License |
|
||||||
# along with this program; if not, write to the Free Software |
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
||||||
# |
|
||||||
# See docs/COPYRIGHT.rdoc for more details. |
|
||||||
#++ |
|
||||||
|
|
||||||
require File.dirname(__FILE__) + '/../spec_helper' |
|
||||||
|
|
||||||
describe User, '#destroy', type: :model do |
|
||||||
let!(:user) { FactoryBot.create(:user) } |
|
||||||
let!(:user2) { FactoryBot.create(:user) } |
|
||||||
let(:substitute_user) { DeletedUser.first } |
|
||||||
let(:project) do |
|
||||||
FactoryBot.create(:valid_project) |
|
||||||
end |
|
||||||
|
|
||||||
let(:meeting) do |
|
||||||
FactoryBot.create(:meeting, |
|
||||||
project: project, |
|
||||||
author: user2) |
|
||||||
end |
|
||||||
let(:participant) do |
|
||||||
FactoryBot.create(:meeting_participant, |
|
||||||
user: user, |
|
||||||
meeting: meeting, |
|
||||||
invited: true, |
|
||||||
attended: true) |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples_for 'updated journalized associated object' do |
|
||||||
before do |
|
||||||
allow(User).to receive(:current).and_return(user2) |
|
||||||
associations.each do |association| |
|
||||||
associated_instance.send(association.to_s + '=', user2) |
|
||||||
end |
|
||||||
associated_instance.save! |
|
||||||
|
|
||||||
allow(User).to receive(:current).and_return(user) # in order to have the content journal created by the user |
|
||||||
associated_instance.reload |
|
||||||
associations.each do |association| |
|
||||||
associated_instance.send(association.to_s + '=', user) |
|
||||||
end |
|
||||||
associated_instance.save! |
|
||||||
|
|
||||||
user.destroy |
|
||||||
associated_instance.reload |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(associated_class.find_by_id(associated_instance.id)).to eq(associated_instance) } |
|
||||||
it 'should replace the user on all associations' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.send(association)).to eq(substitute_user) |
|
||||||
end |
|
||||||
end |
|
||||||
it { expect(associated_instance.journals.first.user).to eq(user2) } |
|
||||||
it 'should update first journal changes' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.journals.first.details[(association.to_s + '_id').to_sym].last).to eq(user2.id) |
|
||||||
end |
|
||||||
end |
|
||||||
it { expect(associated_instance.journals.last.user).to eq(substitute_user) } |
|
||||||
it 'should update second journal changes' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.journals.last.details[(association.to_s + '_id').to_sym].last).to eq(substitute_user.id) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples_for 'created journalized associated object' do |
|
||||||
before do |
|
||||||
allow(User).to receive(:current).and_return(user) # in order to have the content journal created by the user |
|
||||||
associations.each do |association| |
|
||||||
associated_instance.send(association.to_s + '=', user) |
|
||||||
end |
|
||||||
associated_instance.save! |
|
||||||
|
|
||||||
allow(User).to receive(:current).and_return(user2) |
|
||||||
associated_instance.reload |
|
||||||
associations.each do |association| |
|
||||||
associated_instance.send(association.to_s + '=', user2) |
|
||||||
end |
|
||||||
associated_instance.save! |
|
||||||
|
|
||||||
user.destroy |
|
||||||
associated_instance.reload |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(associated_class.find_by_id(associated_instance.id)).to eq(associated_instance) } |
|
||||||
it 'should keep the current user on all associations' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.send(association)).to eq(user2) |
|
||||||
end |
|
||||||
end |
|
||||||
it { expect(associated_instance.journals.first.user).to eq(substitute_user) } |
|
||||||
it 'should update the first journal' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.journals.first.details[(association.to_s + '_id').to_sym].last).to eq(substitute_user.id) |
|
||||||
end |
|
||||||
end |
|
||||||
it { expect(associated_instance.journals.last.user).to eq(user2) } |
|
||||||
it 'should update the last journal' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.journals.last.details[(association.to_s + '_id').to_sym].first).to eq(substitute_user.id) |
|
||||||
expect(associated_instance.journals.last.details[(association.to_s + '_id').to_sym].last).to eq(user2.id) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user created a meeting' do |
|
||||||
let(:associations) { [:author] } |
|
||||||
let(:associated_instance) { FactoryBot.build(:meeting, project: project) } |
|
||||||
let(:associated_class) { Meeting } |
|
||||||
|
|
||||||
it_should_behave_like 'created journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user updated a meeting' do |
|
||||||
let(:associations) { [:author] } |
|
||||||
let(:associated_instance) { FactoryBot.build(:meeting, project: project) } |
|
||||||
let(:associated_class) { Meeting } |
|
||||||
|
|
||||||
it_should_behave_like 'updated journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user created a meeting agenda' do |
|
||||||
let(:associations) { [:author] } |
|
||||||
let(:associated_instance) do |
|
||||||
FactoryBot.build(:meeting_agenda, meeting: meeting, |
|
||||||
text: 'lorem') |
|
||||||
end |
|
||||||
let(:associated_class) { MeetingAgenda } |
|
||||||
|
|
||||||
it_should_behave_like 'created journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user updated a meeting agenda' do |
|
||||||
let(:associations) { [:author] } |
|
||||||
let(:associated_instance) do |
|
||||||
FactoryBot.build(:meeting_agenda, meeting: meeting, |
|
||||||
text: 'lorem') |
|
||||||
end |
|
||||||
let(:associated_class) { MeetingAgenda } |
|
||||||
|
|
||||||
it_should_behave_like 'updated journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user created a meeting minutes' do |
|
||||||
let(:associations) { [:author] } |
|
||||||
let(:associated_instance) do |
|
||||||
FactoryBot.build(:meeting_minutes, |
|
||||||
meeting: meeting, |
|
||||||
text: 'lorem') |
|
||||||
end |
|
||||||
let(:associated_class) { MeetingMinutes } |
|
||||||
|
|
||||||
it_should_behave_like 'created journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user updated a meeting minutes' do |
|
||||||
let(:associations) { [:author] } |
|
||||||
let(:associated_instance) do |
|
||||||
FactoryBot.build(:meeting_minutes, |
|
||||||
meeting: meeting, |
|
||||||
text: 'lorem') |
|
||||||
end |
|
||||||
let(:associated_class) { MeetingMinutes } |
|
||||||
|
|
||||||
it_should_behave_like 'updated journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user participated in a meeting' do |
|
||||||
before do |
|
||||||
participant |
|
||||||
# user2 added to participants by being the author |
|
||||||
|
|
||||||
user.destroy |
|
||||||
meeting.reload |
|
||||||
participant.reload |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(meeting.participants.map(&:user)).to match_array([DeletedUser.first, user2]) } |
|
||||||
it { expect(participant.invited).to be_truthy } |
|
||||||
it { expect(participant.attended).to be_truthy } |
|
||||||
end |
|
||||||
end |
|
@ -0,0 +1,41 @@ |
|||||||
|
#-- copyright |
||||||
|
# OpenProject is an open source project management software. |
||||||
|
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License version 3. |
||||||
|
# |
||||||
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||||
|
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||||
|
# Copyright (C) 2010-2013 the ChiliProject Team |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License |
||||||
|
# as published by the Free Software Foundation; either version 2 |
||||||
|
# of the License, or (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program; if not, write to the Free Software |
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||||
|
# |
||||||
|
# See docs/COPYRIGHT.rdoc for more details. |
||||||
|
#++ |
||||||
|
|
||||||
|
class CostQuery::Filter::ResponsibleId < CostQuery::Filter::UserId |
||||||
|
use :null_operators |
||||||
|
join_table WorkPackage |
||||||
|
applies_for :label_work_package_attributes |
||||||
|
|
||||||
|
def self.label |
||||||
|
WorkPackage.human_attribute_name(:responsible) |
||||||
|
end |
||||||
|
|
||||||
|
def self.available_values(*) |
||||||
|
CostQuery::Filter::UserId.available_values |
||||||
|
end |
||||||
|
end |
@ -1,122 +0,0 @@ |
|||||||
#-- copyright |
|
||||||
# OpenProject is an open source project management software. |
|
||||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
|
||||||
# |
|
||||||
# This program is free software; you can redistribute it and/or |
|
||||||
# modify it under the terms of the GNU General Public License version 3. |
|
||||||
# |
|
||||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
|
||||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
|
||||||
# Copyright (C) 2010-2013 the ChiliProject Team |
|
||||||
# |
|
||||||
# This program is free software; you can redistribute it and/or |
|
||||||
# modify it under the terms of the GNU General Public License |
|
||||||
# as published by the Free Software Foundation; either version 2 |
|
||||||
# of the License, or (at your option) any later version. |
|
||||||
# |
|
||||||
# This program is distributed in the hope that it will be useful, |
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
# GNU General Public License for more details. |
|
||||||
# |
|
||||||
# You should have received a copy of the GNU General Public License |
|
||||||
# along with this program; if not, write to the Free Software |
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
||||||
# |
|
||||||
# See docs/COPYRIGHT.rdoc for more details. |
|
||||||
#++ |
|
||||||
|
|
||||||
require File.dirname(__FILE__) + '/../../spec_helper' |
|
||||||
|
|
||||||
describe User, "#destroy", type: :model do |
|
||||||
let(:substitute_user) { DeletedUser.first } |
|
||||||
let(:private_query) { FactoryBot.create(:private_cost_query) } |
|
||||||
let(:public_query) { FactoryBot.create(:public_cost_query) } |
|
||||||
let(:user) { FactoryBot.create(:user) } |
|
||||||
let(:user2) { FactoryBot.create(:user) } |
|
||||||
|
|
||||||
describe "WHEN the user has saved private cost queries" do |
|
||||||
before do |
|
||||||
private_query.user.destroy |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(CostQuery.find_by_id(private_query.id)).to eq(nil) } |
|
||||||
end |
|
||||||
|
|
||||||
describe "WHEN the user has saved public cost queries" do |
|
||||||
before do |
|
||||||
public_query.user.destroy |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(CostQuery.find_by_id(public_query.id)).to eq(public_query) } |
|
||||||
it { expect(public_query.reload.user_id).to eq(substitute_user.id) } |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples_for "public query" do |
|
||||||
let(:filter_symbol) { filter.to_s.demodulize.underscore.to_sym } |
|
||||||
|
|
||||||
describe "WHEN the filter has the deleted user as it's value" do |
|
||||||
before do |
|
||||||
public_query.filter(filter_symbol, values: [user.id.to_s], operator: "=") |
|
||||||
public_query.save! |
|
||||||
|
|
||||||
user.destroy |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(CostQuery.find_by_id(public_query.id).deserialize.filters.any? { |f| f.is_a?(filter) }).to be_falsey } |
|
||||||
end |
|
||||||
|
|
||||||
describe "WHEN the filter has another user as it's value" do |
|
||||||
before do |
|
||||||
public_query.filter(filter_symbol, values: [user2.id.to_s], operator: "=") |
|
||||||
public_query.save! |
|
||||||
|
|
||||||
user.destroy |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(CostQuery.find_by_id(public_query.id).deserialize.filters.any? { |f| f.is_a?(filter) }).to be_truthy } |
|
||||||
it { |
|
||||||
expect(CostQuery.find_by_id(public_query.id).deserialize.filters.detect do |f| |
|
||||||
f.is_a?(filter) |
|
||||||
end.values).to eq([user2.id.to_s]) |
|
||||||
} |
|
||||||
end |
|
||||||
|
|
||||||
describe "WHEN the filter has the deleted user and another user as it's value" do |
|
||||||
before do |
|
||||||
public_query.filter(filter_symbol, values: [user.id.to_s, user2.id.to_s], operator: "=") |
|
||||||
public_query.save! |
|
||||||
|
|
||||||
user.destroy |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(CostQuery.find_by_id(public_query.id).deserialize.filters.any? { |f| f.is_a?(filter) }).to be_truthy } |
|
||||||
it { |
|
||||||
expect(CostQuery.find_by_id(public_query.id).deserialize.filters.detect do |f| |
|
||||||
f.is_a?(filter) |
|
||||||
end.values).to eq([user2.id.to_s]) |
|
||||||
} |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe "WHEN someone has saved a public cost query |
|
||||||
WHEN the query has a user_id filter" do |
|
||||||
let(:filter) { CostQuery::Filter::UserId } |
|
||||||
|
|
||||||
it_should_behave_like "public query" |
|
||||||
end |
|
||||||
|
|
||||||
describe "WHEN someone has saved a public cost query |
|
||||||
WHEN the query has a author_id filter" do |
|
||||||
let(:filter) { CostQuery::Filter::AuthorId } |
|
||||||
|
|
||||||
it_should_behave_like "public query" |
|
||||||
end |
|
||||||
|
|
||||||
describe "WHEN someone has saved a public cost query |
|
||||||
WHEN the query has a assigned_to_id filter" do |
|
||||||
let(:filter) { CostQuery::Filter::AssignedToId } |
|
||||||
|
|
||||||
it_should_behave_like "public query" |
|
||||||
end |
|
||||||
end |
|
@ -0,0 +1,32 @@ |
|||||||
|
#-- copyright |
||||||
|
# OpenProject is an open source project management software. |
||||||
|
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License version 3. |
||||||
|
# |
||||||
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||||
|
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||||
|
# Copyright (C) 2010-2013 the ChiliProject Team |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License |
||||||
|
# as published by the Free Software Foundation; either version 2 |
||||||
|
# of the License, or (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program; if not, write to the Free Software |
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||||
|
# |
||||||
|
# See docs/COPYRIGHT.rdoc for more details. |
||||||
|
#++ |
||||||
|
|
||||||
|
FactoryBot.define do |
||||||
|
factory :journal_attachment_journal, class: Journal::AttachmentJournal do |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,34 @@ |
|||||||
|
#-- copyright |
||||||
|
# OpenProject is an open source project management software. |
||||||
|
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License version 3. |
||||||
|
# |
||||||
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||||
|
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||||
|
# Copyright (C) 2010-2013 the ChiliProject Team |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License |
||||||
|
# as published by the Free Software Foundation; either version 2 |
||||||
|
# of the License, or (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program; if not, write to the Free Software |
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||||
|
# |
||||||
|
# See docs/COPYRIGHT.rdoc for more details. |
||||||
|
#++ |
||||||
|
|
||||||
|
FactoryBot.define do |
||||||
|
factory :journal_changeset_journal, class: Journal::ChangesetJournal do |
||||||
|
revision { 5 } |
||||||
|
committed_on { Time.zone.today } |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,32 @@ |
|||||||
|
#-- copyright |
||||||
|
# OpenProject is an open source project management software. |
||||||
|
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License version 3. |
||||||
|
# |
||||||
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||||
|
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||||
|
# Copyright (C) 2010-2013 the ChiliProject Team |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License |
||||||
|
# as published by the Free Software Foundation; either version 2 |
||||||
|
# of the License, or (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program; if not, write to the Free Software |
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||||
|
# |
||||||
|
# See docs/COPYRIGHT.rdoc for more details. |
||||||
|
#++ |
||||||
|
|
||||||
|
FactoryBot.define do |
||||||
|
factory :journal_customizable_journal, class: Journal::CustomizableJournal do |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,32 @@ |
|||||||
|
#-- copyright |
||||||
|
# OpenProject is an open source project management software. |
||||||
|
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License version 3. |
||||||
|
# |
||||||
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||||
|
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||||
|
# Copyright (C) 2010-2013 the ChiliProject Team |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License |
||||||
|
# as published by the Free Software Foundation; either version 2 |
||||||
|
# of the License, or (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program; if not, write to the Free Software |
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||||
|
# |
||||||
|
# See docs/COPYRIGHT.rdoc for more details. |
||||||
|
#++ |
||||||
|
|
||||||
|
FactoryBot.define do |
||||||
|
factory :journal_news_journal, class: Journal::NewsJournal do |
||||||
|
end |
||||||
|
end |
@ -1,464 +0,0 @@ |
|||||||
#-- copyright |
|
||||||
# OpenProject is an open source project management software. |
|
||||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
|
||||||
# |
|
||||||
# This program is free software; you can redistribute it and/or |
|
||||||
# modify it under the terms of the GNU General Public License version 3. |
|
||||||
# |
|
||||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
|
||||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
|
||||||
# Copyright (C) 2010-2013 the ChiliProject Team |
|
||||||
# |
|
||||||
# This program is free software; you can redistribute it and/or |
|
||||||
# modify it under the terms of the GNU General Public License |
|
||||||
# as published by the Free Software Foundation; either version 2 |
|
||||||
# of the License, or (at your option) any later version. |
|
||||||
# |
|
||||||
# This program is distributed in the hope that it will be useful, |
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
# GNU General Public License for more details. |
|
||||||
# |
|
||||||
# You should have received a copy of the GNU General Public License |
|
||||||
# along with this program; if not, write to the Free Software |
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
||||||
# |
|
||||||
# See docs/COPYRIGHT.rdoc for more details. |
|
||||||
#++ |
|
||||||
|
|
||||||
require 'spec_helper' |
|
||||||
|
|
||||||
describe User, 'deletion', type: :model do |
|
||||||
let(:project) { FactoryBot.create(:project_with_types) } |
|
||||||
let(:user) { FactoryBot.create(:user, member_in_project: project) } |
|
||||||
let(:user2) { FactoryBot.create(:user) } |
|
||||||
let(:member) { project.members.first } |
|
||||||
let(:role) { member.roles.first } |
|
||||||
let(:status) { FactoryBot.create(:status) } |
|
||||||
let(:issue) do |
|
||||||
FactoryBot.create(:work_package, type: project.types.first, |
|
||||||
author: user, |
|
||||||
project: project, |
|
||||||
status: status, |
|
||||||
assigned_to: user) |
|
||||||
end |
|
||||||
let(:issue2) do |
|
||||||
FactoryBot.create(:work_package, type: project.types.first, |
|
||||||
author: user2, |
|
||||||
project: project, |
|
||||||
status: status, |
|
||||||
assigned_to: user2) |
|
||||||
end |
|
||||||
|
|
||||||
let(:substitute_user) { DeletedUser.first } |
|
||||||
|
|
||||||
describe 'WHEN there is the user' do |
|
||||||
before do |
|
||||||
user.destroy |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(User.find_by(id: user.id)).to be_nil } |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples_for 'updated journalized associated object' do |
|
||||||
before do |
|
||||||
allow(User).to receive(:current).and_return user2 |
|
||||||
associations.each do |association| |
|
||||||
associated_instance.send(association.to_s + '=', user2) |
|
||||||
end |
|
||||||
associated_instance.save! |
|
||||||
|
|
||||||
allow(User).to receive(:current).and_return user # in order to have the content journal created by the user |
|
||||||
associated_instance.reload |
|
||||||
associations.each do |association| |
|
||||||
associated_instance.send(association.to_s + '=', user) |
|
||||||
end |
|
||||||
associated_instance.save! |
|
||||||
|
|
||||||
user.destroy |
|
||||||
associated_instance.reload |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(associated_class.find_by(id: associated_instance.id)).to eq(associated_instance) } |
|
||||||
it 'should replace the user on all associations' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.send(association)).to eq(substitute_user) |
|
||||||
end |
|
||||||
end |
|
||||||
it { expect(associated_instance.journals.first.user).to eq(user2) } |
|
||||||
it 'should update first journal changes' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.journals.first.details[association_key association].last).to eq(user2.id) |
|
||||||
end |
|
||||||
end |
|
||||||
it { expect(associated_instance.journals.last.user).to eq(substitute_user) } |
|
||||||
it 'should update second journal changes' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.journals.last.details[association_key association].last).to eq(substitute_user.id) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
def association_key(association) |
|
||||||
"#{association}_id".parameterize.underscore.to_sym |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples_for 'created associated object' do |
|
||||||
before do |
|
||||||
associations.each do |association| |
|
||||||
associated_instance.send(association.to_s + '=', user) |
|
||||||
end |
|
||||||
associated_instance.save! |
|
||||||
|
|
||||||
user.destroy |
|
||||||
associated_instance.reload |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(associated_class.find_by(id: associated_instance.id)).to eq(associated_instance) } |
|
||||||
it 'should replace the user on all associations' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.send(association)).to eq(substitute_user) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
shared_examples_for 'created journalized associated object' do |
|
||||||
before do |
|
||||||
allow(User).to receive(:current).and_return user # in order to have the content journal created by the user |
|
||||||
associations.each do |association| |
|
||||||
associated_instance.send(association.to_s + '=', user) |
|
||||||
end |
|
||||||
associated_instance.save! |
|
||||||
|
|
||||||
allow(User).to receive(:current).and_return user2 |
|
||||||
associated_instance.reload |
|
||||||
associations.each do |association| |
|
||||||
associated_instance.send(association.to_s + '=', user2) |
|
||||||
end |
|
||||||
associated_instance.save! |
|
||||||
|
|
||||||
user.destroy |
|
||||||
associated_instance.reload |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(associated_class.find_by(id: associated_instance.id)).to eq(associated_instance) } |
|
||||||
it 'should keep the current user on all associations' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.send(association)).to eq(user2) |
|
||||||
end |
|
||||||
end |
|
||||||
it { expect(associated_instance.journals.first.user).to eq(substitute_user) } |
|
||||||
it 'should update the first journal' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.journals.first.details[association_key association].last).to eq(substitute_user.id) |
|
||||||
end |
|
||||||
end |
|
||||||
it { expect(associated_instance.journals.last.user).to eq(user2) } |
|
||||||
it 'should update the last journal' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.journals.last.details[association_key association].first).to eq(substitute_user.id) |
|
||||||
expect(associated_instance.journals.last.details[association_key association].last).to eq(user2.id) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has created one attachment' do |
|
||||||
let(:associated_instance) { FactoryBot.build(:attachment) } |
|
||||||
let(:associated_class) { Attachment } |
|
||||||
let(:associations) { [:author] } |
|
||||||
|
|
||||||
it_should_behave_like 'created journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has updated one attachment' do |
|
||||||
let(:associated_instance) { FactoryBot.build(:attachment) } |
|
||||||
let(:associated_class) { Attachment } |
|
||||||
let(:associations) { [:author] } |
|
||||||
|
|
||||||
it_should_behave_like 'updated journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has an issue created and assigned' do |
|
||||||
let(:associated_instance) do |
|
||||||
FactoryBot.build(:work_package, type: project.types.first, |
|
||||||
project: project, |
|
||||||
status: status) |
|
||||||
end |
|
||||||
let(:associated_class) { WorkPackage } |
|
||||||
let(:associations) { %i[author assigned_to responsible] } |
|
||||||
|
|
||||||
it_should_behave_like 'created journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has an issue updated and assigned' do |
|
||||||
let(:associated_instance) do |
|
||||||
FactoryBot.build(:work_package, type: project.types.first, |
|
||||||
project: project, |
|
||||||
status: status) |
|
||||||
end |
|
||||||
let(:associated_class) { WorkPackage } |
|
||||||
let(:associations) { %i[author assigned_to responsible] } |
|
||||||
|
|
||||||
before do |
|
||||||
allow(User).to receive(:current).and_return user2 |
|
||||||
associated_instance.author = user2 |
|
||||||
associated_instance.assigned_to = user2 |
|
||||||
associated_instance.responsible = user2 |
|
||||||
associated_instance.save! |
|
||||||
|
|
||||||
allow(User).to receive(:current).and_return user # in order to have the content journal created by the user |
|
||||||
associated_instance.reload |
|
||||||
associated_instance.author = user |
|
||||||
associated_instance.assigned_to = user |
|
||||||
associated_instance.responsible = user |
|
||||||
associated_instance.save! |
|
||||||
|
|
||||||
user.destroy |
|
||||||
associated_instance.reload |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(associated_class.find_by(id: associated_instance.id)).to eq(associated_instance) } |
|
||||||
it 'should replace the user on all associations' do |
|
||||||
expect(associated_instance.author).to eq(substitute_user) |
|
||||||
expect(associated_instance.assigned_to).to be_nil |
|
||||||
expect(associated_instance.responsible).to be_nil |
|
||||||
end |
|
||||||
it { expect(associated_instance.journals.first.user).to eq(user2) } |
|
||||||
it 'should update first journal changes' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.journals.first.details[association_key association].last).to eq(user2.id) |
|
||||||
end |
|
||||||
end |
|
||||||
it { expect(associated_instance.journals.last.user).to eq(substitute_user) } |
|
||||||
it 'should update second journal changes' do |
|
||||||
associations.each do |association| |
|
||||||
expect(associated_instance.journals.last.details[association_key association].last).to eq(substitute_user.id) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has updated a wiki content' do |
|
||||||
let(:associated_instance) { FactoryBot.build(:wiki_content) } |
|
||||||
let(:associated_class) { WikiContent } |
|
||||||
let(:associations) { [:author] } |
|
||||||
|
|
||||||
it_should_behave_like 'updated journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has created a wiki content' do |
|
||||||
let(:associated_instance) { FactoryBot.build(:wiki_content) } |
|
||||||
let(:associated_class) { WikiContent } |
|
||||||
let(:associations) { [:author] } |
|
||||||
|
|
||||||
it_should_behave_like 'created journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has created a news' do |
|
||||||
let(:associated_instance) { FactoryBot.build(:news) } |
|
||||||
let(:associated_class) { News } |
|
||||||
let(:associations) { [:author] } |
|
||||||
|
|
||||||
it_should_behave_like 'created journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has worked on news' do |
|
||||||
let(:associated_instance) { FactoryBot.build(:news) } |
|
||||||
let(:associated_class) { News } |
|
||||||
let(:associations) { [:author] } |
|
||||||
|
|
||||||
it_should_behave_like 'updated journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has created a message' do |
|
||||||
let(:associated_instance) { FactoryBot.build(:message) } |
|
||||||
let(:associated_class) { Message } |
|
||||||
let(:associations) { [:author] } |
|
||||||
|
|
||||||
it_should_behave_like 'created journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has worked on message' do |
|
||||||
let(:associated_instance) { FactoryBot.build(:message) } |
|
||||||
let(:associated_class) { Message } |
|
||||||
let(:associations) { [:author] } |
|
||||||
|
|
||||||
it_should_behave_like 'updated journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has created a time entry' do |
|
||||||
let(:associated_instance) do |
|
||||||
FactoryBot.build(:time_entry, project: project, |
|
||||||
work_package: issue, |
|
||||||
hours: 2, |
|
||||||
activity: FactoryBot.create(:time_entry_activity)) |
|
||||||
end |
|
||||||
let(:associated_class) { TimeEntry } |
|
||||||
let(:associations) { [:user] } |
|
||||||
|
|
||||||
it_should_behave_like 'created journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has worked on time_entry' do |
|
||||||
let(:associated_instance) do |
|
||||||
FactoryBot.build(:time_entry, project: project, |
|
||||||
work_package: issue, |
|
||||||
hours: 2, |
|
||||||
activity: FactoryBot.create(:time_entry_activity)) |
|
||||||
end |
|
||||||
let(:associated_class) { TimeEntry } |
|
||||||
let(:associations) { [:user] } |
|
||||||
|
|
||||||
it_should_behave_like 'updated journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has commented' do |
|
||||||
let(:news) { FactoryBot.create(:news, author: user) } |
|
||||||
|
|
||||||
let(:associated_instance) do |
|
||||||
Comment.new(commented: news, |
|
||||||
comments: 'lorem') |
|
||||||
end |
|
||||||
|
|
||||||
let(:associated_class) { Comment } |
|
||||||
let(:associations) { [:author] } |
|
||||||
|
|
||||||
it_should_behave_like 'created associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user is a member of a project' do |
|
||||||
before do |
|
||||||
user |
|
||||||
member |
|
||||||
end |
|
||||||
|
|
||||||
it 'removes that member' do |
|
||||||
user.destroy |
|
||||||
|
|
||||||
expect(Member.find_by(id: member.id)).to be_nil |
|
||||||
expect(Role.find_by(id: role.id)).to eq(role) |
|
||||||
expect(Project.find_by(id: project.id)).to eq(project) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user is watching something' do |
|
||||||
let(:watched) { FactoryBot.create(:work_package, project: project) } |
|
||||||
let(:watch) do |
|
||||||
Watcher.new(user: user, |
|
||||||
watchable: watched) |
|
||||||
end |
|
||||||
|
|
||||||
before do |
|
||||||
watch.save! |
|
||||||
|
|
||||||
user.destroy |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(Watcher.find_by(id: watch.id)).to be_nil } |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has a token created' do |
|
||||||
let(:token) do |
|
||||||
Token::RSS.new(user: user, value: 'loremipsum') |
|
||||||
end |
|
||||||
|
|
||||||
before do |
|
||||||
token.save! |
|
||||||
|
|
||||||
user.destroy |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(Token::RSS.find_by(id: token.id)).to be_nil } |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has created a private query' do |
|
||||||
let(:query) { FactoryBot.build(:private_query, user: user) } |
|
||||||
|
|
||||||
before do |
|
||||||
query.save! |
|
||||||
|
|
||||||
user.destroy |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(Query.find_by(id: query.id)).to be_nil } |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has created a public query' do |
|
||||||
let(:associated_instance) { FactoryBot.build(:public_query) } |
|
||||||
|
|
||||||
let(:associated_class) { Query } |
|
||||||
let(:associations) { [:user] } |
|
||||||
|
|
||||||
it_should_behave_like 'created associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has created a changeset' do |
|
||||||
with_virtual_subversion_repository do |
|
||||||
let(:associated_instance) do |
|
||||||
FactoryBot.build(:changeset, |
|
||||||
repository_id: repository.id, |
|
||||||
committer: user.login) |
|
||||||
end |
|
||||||
|
|
||||||
let(:associated_class) { Changeset } |
|
||||||
let(:associations) { [:user] } |
|
||||||
end |
|
||||||
|
|
||||||
it_should_behave_like 'created journalized associated object' |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user has updated a changeset' do |
|
||||||
with_virtual_subversion_repository do |
|
||||||
let(:associated_instance) do |
|
||||||
FactoryBot.build(:changeset, |
|
||||||
repository_id: repository.id, |
|
||||||
committer: user2.login) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
let(:associated_class) { Changeset } |
|
||||||
let(:associations) { [:user] } |
|
||||||
|
|
||||||
before do |
|
||||||
allow(User).to receive(:current).and_return user2 |
|
||||||
associated_instance.user = user2 |
|
||||||
associated_instance.save! |
|
||||||
|
|
||||||
allow(User).to receive(:current).and_return user # in order to have the content journal created by the user |
|
||||||
associated_instance.reload |
|
||||||
associated_instance.user = user |
|
||||||
associated_instance.save! |
|
||||||
|
|
||||||
user.destroy |
|
||||||
associated_instance.reload |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(associated_class.find_by(id: associated_instance.id)).to eq(associated_instance) } |
|
||||||
it 'should replace the user on all associations' do |
|
||||||
expect(associated_instance.user).to be_nil |
|
||||||
end |
|
||||||
it { expect(associated_instance.journals.first.user).to eq(user2) } |
|
||||||
it 'should update first journal changes' do |
|
||||||
expect(associated_instance.journals.first.details[:user_id].last).to eq(user2.id) |
|
||||||
end |
|
||||||
it { expect(associated_instance.journals.last.user).to eq(substitute_user) } |
|
||||||
it 'should update second journal changes' do |
|
||||||
expect(associated_instance.journals.last.details[:user_id].last).to eq(substitute_user.id) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
describe 'WHEN the user is assigned an issue category' do |
|
||||||
let(:category) do |
|
||||||
FactoryBot.build(:category, assigned_to: user, |
|
||||||
project: project) |
|
||||||
end |
|
||||||
|
|
||||||
before do |
|
||||||
category.save! |
|
||||||
user.destroy |
|
||||||
category.reload |
|
||||||
end |
|
||||||
|
|
||||||
it { expect(Category.find_by(id: category.id)).to eq(category) } |
|
||||||
it { expect(category.assigned_to).to be_nil } |
|
||||||
end |
|
||||||
end |
|
@ -1,106 +0,0 @@ |
|||||||
#-- copyright |
|
||||||
# OpenProject is an open source project management software. |
|
||||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
|
||||||
# |
|
||||||
# This program is free software; you can redistribute it and/or |
|
||||||
# modify it under the terms of the GNU General Public License version 3. |
|
||||||
# |
|
||||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
|
||||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
|
||||||
# Copyright (C) 2010-2013 the ChiliProject Team |
|
||||||
# |
|
||||||
# This program is free software; you can redistribute it and/or |
|
||||||
# modify it under the terms of the GNU General Public License |
|
||||||
# as published by the Free Software Foundation; either version 2 |
|
||||||
# of the License, or (at your option) any later version. |
|
||||||
# |
|
||||||
# This program is distributed in the hope that it will be useful, |
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
# GNU General Public License for more details. |
|
||||||
# |
|
||||||
# You should have received a copy of the GNU General Public License |
|
||||||
# along with this program; if not, write to the Free Software |
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
||||||
# |
|
||||||
# See docs/COPYRIGHT.rdoc for more details. |
|
||||||
#++ |
|
||||||
|
|
||||||
require 'spec_helper' |
|
||||||
|
|
||||||
describe Journals::UserReferenceUpdateService, type: :model do |
|
||||||
let!(:work_package) { FactoryBot.create :work_package } |
|
||||||
let!(:doomed_user) { work_package.author } |
|
||||||
let!(:other_user) { FactoryBot.create(:user) } |
|
||||||
let!(:data1) do |
|
||||||
FactoryBot.build(:journal_work_package_journal, |
|
||||||
subject: work_package.subject, |
|
||||||
status_id: work_package.status_id, |
|
||||||
type_id: work_package.type_id, |
|
||||||
author_id: doomed_user.id, |
|
||||||
assigned_to_id: other_user.id, |
|
||||||
responsible_id: doomed_user.id, |
|
||||||
project_id: work_package.project_id) |
|
||||||
end |
|
||||||
let!(:data2) do |
|
||||||
FactoryBot.build(:journal_work_package_journal, |
|
||||||
subject: work_package.subject, |
|
||||||
status_id: work_package.status_id, |
|
||||||
type_id: work_package.type_id, |
|
||||||
author_id: doomed_user.id, |
|
||||||
assigned_to_id: doomed_user.id, |
|
||||||
responsible_id: other_user.id, |
|
||||||
project_id: work_package.project_id) |
|
||||||
end |
|
||||||
let!(:doomed_user_journal) do |
|
||||||
FactoryBot.create :work_package_journal, |
|
||||||
notes: '1', |
|
||||||
user: doomed_user, |
|
||||||
journable_id: work_package.id, |
|
||||||
data: data1 |
|
||||||
end |
|
||||||
let!(:some_other_journal) do |
|
||||||
FactoryBot.create :work_package_journal, |
|
||||||
notes: '2', |
|
||||||
journable_id: work_package.id, |
|
||||||
data: data2 |
|
||||||
end |
|
||||||
|
|
||||||
describe '.call' do |
|
||||||
subject do |
|
||||||
described_class |
|
||||||
.new(doomed_user) |
|
||||||
.call(DeletedUser.first) |
|
||||||
end |
|
||||||
|
|
||||||
before do |
|
||||||
subject |
|
||||||
end |
|
||||||
|
|
||||||
it "is success" do |
|
||||||
expect(subject) |
|
||||||
.to be_success |
|
||||||
end |
|
||||||
|
|
||||||
it "marks only the user's journal as deleted" do |
|
||||||
expect(doomed_user_journal.reload.user.is_a?(DeletedUser)).to be_truthy |
|
||||||
expect(some_other_journal.reload.user.is_a?(DeletedUser)).to be_falsey |
|
||||||
end |
|
||||||
|
|
||||||
it "marks the assignee stored in the WorkPackageJournal as deleted" do |
|
||||||
expect(data2.reload.assigned_to_id) |
|
||||||
.to eql(DeletedUser.first.id) |
|
||||||
|
|
||||||
expect(data1.reload.assigned_to_id) |
|
||||||
.to eql(other_user.id) |
|
||||||
end |
|
||||||
|
|
||||||
it "marks the responsible stored in the WorkPackageJournal as deleted" do |
|
||||||
expect(data1.reload.responsible_id) |
|
||||||
.to eql(DeletedUser.first.id) |
|
||||||
|
|
||||||
expect(data2.reload.responsible_id) |
|
||||||
.to eql(other_user.id) |
|
||||||
end |
|
||||||
end |
|
||||||
end |
|
@ -0,0 +1,408 @@ |
|||||||
|
#-- copyright |
||||||
|
# OpenProject is an open source project management software. |
||||||
|
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License version 3. |
||||||
|
# |
||||||
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||||
|
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||||
|
# Copyright (C) 2010-2013 the ChiliProject Team |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License |
||||||
|
# as published by the Free Software Foundation; either version 2 |
||||||
|
# of the License, or (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program; if not, write to the Free Software |
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||||
|
# |
||||||
|
# See docs/COPYRIGHT.rdoc for more details. |
||||||
|
#++ |
||||||
|
|
||||||
|
require 'spec_helper' |
||||||
|
|
||||||
|
describe Principals::ReplaceReferencesService, '#call', type: :model do |
||||||
|
subject(:service_call) { instance.call(from: principal, to: to_principal) } |
||||||
|
|
||||||
|
shared_let(:other_user) { FactoryBot.create(:user) } |
||||||
|
shared_let(:user) { FactoryBot.create(:user) } |
||||||
|
shared_let(:to_principal) { FactoryBot.create :user } |
||||||
|
|
||||||
|
let(:instance) do |
||||||
|
described_class.new |
||||||
|
end |
||||||
|
|
||||||
|
context 'with a user' do |
||||||
|
let(:principal) { user } |
||||||
|
|
||||||
|
it 'is successful' do |
||||||
|
expect(service_call) |
||||||
|
.to be_success |
||||||
|
end |
||||||
|
|
||||||
|
context 'with a Journal' do |
||||||
|
let!(:journal) do |
||||||
|
FactoryBot.create(:work_package_journal, |
||||||
|
user_id: user_id, |
||||||
|
data: instance_double(Journal::WorkPackageJournal, |
||||||
|
'journal=': nil, |
||||||
|
save: true)) |
||||||
|
end |
||||||
|
|
||||||
|
context 'with the replaced user' do |
||||||
|
let(:user_id) { principal.id } |
||||||
|
|
||||||
|
before do |
||||||
|
service_call |
||||||
|
journal.reload |
||||||
|
end |
||||||
|
|
||||||
|
it 'replaces user_id' do |
||||||
|
expect(journal.user_id) |
||||||
|
.to eql to_principal.id |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with a different user' do |
||||||
|
let(:user_id) { other_user.id } |
||||||
|
|
||||||
|
before do |
||||||
|
service_call |
||||||
|
journal.reload |
||||||
|
end |
||||||
|
|
||||||
|
it 'replaces user_id' do |
||||||
|
expect(journal.user_id) |
||||||
|
.to eql other_user.id |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples_for 'rewritten record' do |factory, attribute, format = Integer| |
||||||
|
let!(:model) do |
||||||
|
klass = FactoryBot.factories.find(factory).build_class |
||||||
|
all_attributes = other_attributes.merge(attribute => principal_id) |
||||||
|
|
||||||
|
inserted = ActiveRecord::Base.connection.select_one <<~SQL |
||||||
|
INSERT INTO #{klass.table_name} |
||||||
|
(#{all_attributes.keys.join(', ')}) |
||||||
|
VALUES |
||||||
|
(#{all_attributes.values.join(', ')}) |
||||||
|
RETURNING id |
||||||
|
SQL |
||||||
|
|
||||||
|
klass.find(inserted['id']) |
||||||
|
end |
||||||
|
|
||||||
|
let(:other_attributes) do |
||||||
|
defined?(attributes) ? attributes : {} |
||||||
|
end |
||||||
|
|
||||||
|
def expected(user, format) |
||||||
|
if format == String |
||||||
|
user.id.to_s |
||||||
|
else |
||||||
|
user.id |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context "for #{factory}" do |
||||||
|
context 'with the replaced user' do |
||||||
|
let(:principal_id) { principal.id } |
||||||
|
|
||||||
|
before do |
||||||
|
service_call |
||||||
|
model.reload |
||||||
|
end |
||||||
|
|
||||||
|
it "replaces #{attribute}" do |
||||||
|
expect(model.send(attribute)) |
||||||
|
.to eql expected(to_principal, format) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with a different user' do |
||||||
|
let(:principal_id) { other_user.id } |
||||||
|
|
||||||
|
before do |
||||||
|
service_call |
||||||
|
model.reload |
||||||
|
end |
||||||
|
|
||||||
|
it "keeps #{attribute}" do |
||||||
|
expect(model.send(attribute)) |
||||||
|
.to eql expected(other_user, format) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with Attachment' do |
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:attachment, |
||||||
|
:author_id |
||||||
|
|
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:journal_attachment_journal, |
||||||
|
:author_id do |
||||||
|
let(:attributes) do |
||||||
|
{ journal_id: 1 } |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with Comment' do |
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:comment, |
||||||
|
:author_id |
||||||
|
end |
||||||
|
|
||||||
|
context 'with CustomValue' do |
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:custom_value, |
||||||
|
:value, |
||||||
|
String do |
||||||
|
let(:user_cf) { FactoryBot.create(:user_wp_custom_field) } |
||||||
|
let(:attributes) do |
||||||
|
{ custom_field_id: user_cf.id } |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:journal_customizable_journal, |
||||||
|
:value, |
||||||
|
String do |
||||||
|
let(:user_cf) { FactoryBot.create(:user_wp_custom_field) } |
||||||
|
let(:attributes) do |
||||||
|
{ journal_id: 1, |
||||||
|
custom_field_id: user_cf.id } |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with Changeset' do |
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:changeset, |
||||||
|
:user_id do |
||||||
|
let(:attributes) do |
||||||
|
{ repository_id: 1, |
||||||
|
revision: 1, |
||||||
|
committed_on: "date '2012-02-02'" } |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:journal_changeset_journal, |
||||||
|
:user_id do |
||||||
|
let(:attributes) do |
||||||
|
{ journal_id: 1, |
||||||
|
repository_id: 1, |
||||||
|
revision: 1, |
||||||
|
committed_on: "date '2012-02-02'" } |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with Message' do |
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:message, |
||||||
|
:author_id do |
||||||
|
let(:attributes) do |
||||||
|
{ forum_id: 1 } |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:journal_message_journal, |
||||||
|
:author_id do |
||||||
|
let(:attributes) do |
||||||
|
{ journal_id: 1, |
||||||
|
forum_id: 1 } |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with MeetingContent' do |
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:meeting_agenda, |
||||||
|
:author_id do |
||||||
|
let(:attributes) do |
||||||
|
{ type: "'MeetingAgenda'", |
||||||
|
created_at: 'NOW()', |
||||||
|
updated_at: 'NOW()' } |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:meeting_minutes, |
||||||
|
:author_id do |
||||||
|
let(:attributes) do |
||||||
|
{ type: "'MeetingMinutes'", |
||||||
|
created_at: 'NOW()', |
||||||
|
updated_at: 'NOW()' } |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:journal_meeting_content_journal, |
||||||
|
:author_id do |
||||||
|
let(:attributes) do |
||||||
|
{ journal_id: 1 } |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with MeetingParticipant' do |
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:meeting_participant, |
||||||
|
:user_id do |
||||||
|
let(:attributes) do |
||||||
|
{ created_at: 'NOW()', |
||||||
|
updated_at: 'NOW()' } |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with News' do |
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:news, |
||||||
|
:author_id |
||||||
|
|
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:journal_news_journal, |
||||||
|
:author_id do |
||||||
|
let(:attributes) do |
||||||
|
{ journal_id: 1 } |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with WikiContent' do |
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:wiki_content, |
||||||
|
:author_id do |
||||||
|
let(:attributes) do |
||||||
|
{ page_id: 1, |
||||||
|
lock_version: 5 } |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:journal_wiki_content_journal, |
||||||
|
:author_id do |
||||||
|
let(:attributes) do |
||||||
|
{ journal_id: 1, |
||||||
|
page_id: 1 } |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with WorkPackage' do |
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:work_package, |
||||||
|
:assigned_to_id |
||||||
|
|
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:work_package, |
||||||
|
:responsible_id |
||||||
|
|
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:journal_work_package_journal, |
||||||
|
:assigned_to_id do |
||||||
|
let(:attributes) do |
||||||
|
{ journal_id: 1 } |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:journal_work_package_journal, |
||||||
|
:responsible_id do |
||||||
|
let(:attributes) do |
||||||
|
{ journal_id: 1 } |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with TimeEntry' do |
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:time_entry, |
||||||
|
:user_id do |
||||||
|
let(:attributes) do |
||||||
|
{ project_id: 1, |
||||||
|
hours: 5, |
||||||
|
activity_id: 1, |
||||||
|
spent_on: "date '2012-02-02'", |
||||||
|
tyear: 2021, |
||||||
|
tmonth: 12, |
||||||
|
tweek: 5 } |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:journal_time_entry_journal, |
||||||
|
:user_id do |
||||||
|
let(:attributes) do |
||||||
|
{ journal_id: 1, |
||||||
|
project_id: 1, |
||||||
|
hours: 5, |
||||||
|
activity_id: 1, |
||||||
|
spent_on: "date '2012-02-02'", |
||||||
|
tyear: 2021, |
||||||
|
tmonth: 12, |
||||||
|
tweek: 5 } |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with Budget' do |
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:budget, |
||||||
|
:author_id do |
||||||
|
let(:attributes) do |
||||||
|
{ project_id: 1, |
||||||
|
subject: "'abc'", |
||||||
|
description: "'cde'", |
||||||
|
fixed_date: "date '2012-02-02'" } |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:journal_budget_journal, |
||||||
|
:author_id do |
||||||
|
let(:attributes) do |
||||||
|
{ journal_id: 1, |
||||||
|
project_id: 1, |
||||||
|
subject: "'abc'", |
||||||
|
fixed_date: "date '2012-02-02'" } |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with Query' do |
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:query, |
||||||
|
:user_id |
||||||
|
end |
||||||
|
|
||||||
|
context 'with CostQuery' do |
||||||
|
let(:query) { FactoryBot.create(:cost_query, user: principal) } |
||||||
|
|
||||||
|
it_behaves_like 'rewritten record', |
||||||
|
:cost_query, |
||||||
|
:user_id do |
||||||
|
let(:attributes) do |
||||||
|
{ name: "'abc'", |
||||||
|
serialized: "'cde'" } |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,404 @@ |
|||||||
|
#-- copyright |
||||||
|
# OpenProject is an open source project management software. |
||||||
|
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License version 3. |
||||||
|
# |
||||||
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||||
|
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||||
|
# Copyright (C) 2010-2013 the ChiliProject Team |
||||||
|
# |
||||||
|
# This program is free software; you can redistribute it and/or |
||||||
|
# modify it under the terms of the GNU General Public License |
||||||
|
# as published by the Free Software Foundation; either version 2 |
||||||
|
# of the License, or (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program; if not, write to the Free Software |
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||||
|
# |
||||||
|
# See docs/COPYRIGHT.rdoc for more details. |
||||||
|
#++ |
||||||
|
|
||||||
|
require 'spec_helper' |
||||||
|
|
||||||
|
describe Principals::DeleteJob, type: :model do |
||||||
|
subject(:job) { described_class.perform_now(principal) } |
||||||
|
|
||||||
|
shared_let(:project) { FactoryBot.create(:project) } |
||||||
|
|
||||||
|
shared_let(:deleted_user) do |
||||||
|
FactoryBot.create(:deleted_user) |
||||||
|
end |
||||||
|
let(:principal) do |
||||||
|
FactoryBot.create(:user) |
||||||
|
end |
||||||
|
let(:member) do |
||||||
|
FactoryBot.create(:member, |
||||||
|
principal: principal, |
||||||
|
project: project, |
||||||
|
roles: [role]) |
||||||
|
end |
||||||
|
shared_let(:role) do |
||||||
|
FactoryBot.create(:role, permissions: %i[view_work_packages] ) |
||||||
|
end |
||||||
|
|
||||||
|
describe '#perform' do |
||||||
|
# These are the only tests that include testing |
||||||
|
# the ReplaceReferencesService. Most of the tests for this |
||||||
|
# Service are handled within the matching spec file. |
||||||
|
shared_examples_for 'work_package handling' do |
||||||
|
let(:work_package) do |
||||||
|
FactoryBot.create(:work_package, |
||||||
|
assigned_to: principal, |
||||||
|
responsible: principal) |
||||||
|
end |
||||||
|
|
||||||
|
before do |
||||||
|
work_package |
||||||
|
job |
||||||
|
end |
||||||
|
|
||||||
|
it 'resets assigned to to the deleted user' do |
||||||
|
expect(work_package.reload.assigned_to) |
||||||
|
.to eql(deleted_user) |
||||||
|
end |
||||||
|
|
||||||
|
it 'resets assigned to in all journals to the deleted user' do |
||||||
|
expect(Journal::WorkPackageJournal.pluck(:assigned_to_id)) |
||||||
|
.to eql([deleted_user.id]) |
||||||
|
end |
||||||
|
|
||||||
|
it 'resets responsible to to the deleted user' do |
||||||
|
expect(work_package.reload.responsible) |
||||||
|
.to eql(deleted_user) |
||||||
|
end |
||||||
|
|
||||||
|
it 'resets responsible to in all journals to the deleted user' do |
||||||
|
expect(Journal::WorkPackageJournal.pluck(:responsible_id)) |
||||||
|
.to eql([deleted_user.id]) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples_for 'labor_budget_item handling' do |
||||||
|
let(:item) { FactoryBot.build(:labor_budget_item, user: principal) } |
||||||
|
|
||||||
|
before do |
||||||
|
item.save! |
||||||
|
|
||||||
|
job |
||||||
|
end |
||||||
|
|
||||||
|
it { expect(LaborBudgetItem.find_by_id(item.id)).to eq(item) } |
||||||
|
it { expect(item.user_id).to eq(principal.id) } |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples_for 'cost_entry handling' do |
||||||
|
let(:work_package) { FactoryBot.create(:work_package) } |
||||||
|
let(:entry) do |
||||||
|
FactoryBot.create(:cost_entry, |
||||||
|
user: principal, |
||||||
|
project: work_package.project, |
||||||
|
units: 100.0, |
||||||
|
spent_on: Date.today, |
||||||
|
work_package: work_package, |
||||||
|
comments: '') |
||||||
|
end |
||||||
|
|
||||||
|
before do |
||||||
|
FactoryBot.create(:member, |
||||||
|
project: work_package.project, |
||||||
|
user: principal, |
||||||
|
roles: [FactoryBot.build(:role)]) |
||||||
|
entry |
||||||
|
|
||||||
|
job |
||||||
|
|
||||||
|
entry.reload |
||||||
|
end |
||||||
|
|
||||||
|
it { expect(entry.user_id).to eq(principal.id) } |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples_for 'member handling' do |
||||||
|
before do |
||||||
|
member |
||||||
|
|
||||||
|
job |
||||||
|
end |
||||||
|
|
||||||
|
it 'removes that member' do |
||||||
|
expect(Member.find_by(id: member.id)).to be_nil |
||||||
|
end |
||||||
|
|
||||||
|
it 'leaves the role' do |
||||||
|
expect(Role.find_by(id: role.id)).to eq(role) |
||||||
|
end |
||||||
|
|
||||||
|
it 'leaves the project' do |
||||||
|
expect(Project.find_by(id: project.id)).to eq(project) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples_for 'hourly_rate handling' do |
||||||
|
let(:hourly_rate) do |
||||||
|
FactoryBot.build(:hourly_rate, |
||||||
|
user: principal, |
||||||
|
project: project) |
||||||
|
end |
||||||
|
|
||||||
|
before do |
||||||
|
hourly_rate.save! |
||||||
|
job |
||||||
|
end |
||||||
|
|
||||||
|
it { expect(HourlyRate.find_by_id(hourly_rate.id)).to eq(hourly_rate) } |
||||||
|
it { expect(hourly_rate.reload.user_id).to eq(principal.id) } |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples_for 'watcher handling' do |
||||||
|
let(:watched) { FactoryBot.create(:news, project: project) } |
||||||
|
let(:watch) do |
||||||
|
Watcher.create(user: principal, |
||||||
|
watchable: watched) |
||||||
|
end |
||||||
|
|
||||||
|
before do |
||||||
|
member |
||||||
|
watch |
||||||
|
|
||||||
|
job |
||||||
|
end |
||||||
|
|
||||||
|
it { expect(Watcher.find_by(id: watch.id)).to be_nil } |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples_for 'token handling' do |
||||||
|
let(:token) do |
||||||
|
Token::RSS.new(user: principal, value: 'loremipsum') |
||||||
|
end |
||||||
|
|
||||||
|
before do |
||||||
|
token.save! |
||||||
|
|
||||||
|
job |
||||||
|
end |
||||||
|
|
||||||
|
it { expect(Token::RSS.find_by(id: token.id)).to be_nil } |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples_for 'private query handling' do |
||||||
|
let!(:query) do |
||||||
|
FactoryBot.create(:private_query, user: principal) |
||||||
|
end |
||||||
|
|
||||||
|
before do |
||||||
|
job |
||||||
|
end |
||||||
|
|
||||||
|
it { expect(Query.find_by(id: query.id)).to be_nil } |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples_for 'issue category handling' do |
||||||
|
let(:category) do |
||||||
|
FactoryBot.create(:category, |
||||||
|
assigned_to: principal, |
||||||
|
project: project) |
||||||
|
end |
||||||
|
|
||||||
|
before do |
||||||
|
member |
||||||
|
category |
||||||
|
job |
||||||
|
end |
||||||
|
|
||||||
|
it 'does not remove the category' do |
||||||
|
expect(Category.find_by(id: category.id)).to eq(category) |
||||||
|
end |
||||||
|
|
||||||
|
it 'removes the assigned_to association to the principal' do |
||||||
|
expect(category.reload.assigned_to).to be_nil |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples_for 'removes the principal' do |
||||||
|
it 'deletes the principal' do |
||||||
|
job |
||||||
|
|
||||||
|
expect(Principal.find_by(id: principal.id)) |
||||||
|
.to be_nil |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples_for 'private cost_query handling' do |
||||||
|
let!(:query) { FactoryBot.create(:private_cost_query, user: principal) } |
||||||
|
|
||||||
|
it 'removes the query' do |
||||||
|
job |
||||||
|
|
||||||
|
expect(CostQuery.find_by_id(query.id)).to eq(nil) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples_for 'public cost_query handling' do |
||||||
|
let!(:query) { FactoryBot.create(:public_cost_query, user: principal) } |
||||||
|
|
||||||
|
before do |
||||||
|
query |
||||||
|
|
||||||
|
job |
||||||
|
end |
||||||
|
|
||||||
|
it 'leaves the query' do |
||||||
|
expect(CostQuery.find_by_id(query.id)).to eq(query) |
||||||
|
end |
||||||
|
|
||||||
|
it 'rewrites the user reference' do |
||||||
|
expect(query.reload.user).to eq(deleted_user) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
shared_examples_for 'cost_query handling' do |
||||||
|
let(:query) { FactoryBot.create(:cost_query) } |
||||||
|
let(:other_user) { FactoryBot.create(:user) } |
||||||
|
|
||||||
|
shared_examples_for "public query rewriting" do |
||||||
|
let(:filter_symbol) { filter.to_s.demodulize.underscore.to_sym } |
||||||
|
|
||||||
|
describe "with the filter has the deleted user as it's value" do |
||||||
|
before do |
||||||
|
query.filter(filter_symbol, values: [principal.id.to_s], operator: "=") |
||||||
|
query.save! |
||||||
|
|
||||||
|
job |
||||||
|
end |
||||||
|
|
||||||
|
it 'removes the filter' do |
||||||
|
expect(CostQuery.find_by(id: query.id).deserialize.filters) |
||||||
|
.not_to(be_any { |f| f.is_a?(filter) }) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe "with the filter has another user as it's value" do |
||||||
|
before do |
||||||
|
query.filter(filter_symbol, values: [other_user.id.to_s], operator: "=") |
||||||
|
query.save! |
||||||
|
|
||||||
|
job |
||||||
|
end |
||||||
|
|
||||||
|
it 'keeps the filter' do |
||||||
|
expect(CostQuery.find_by(id: query.id).deserialize.filters) |
||||||
|
.to(be_any { |f| f.is_a?(filter) }) |
||||||
|
end |
||||||
|
|
||||||
|
it 'does not alter the filter values' do |
||||||
|
expect(CostQuery.find_by(id: query.id).deserialize.filters.detect do |f| |
||||||
|
f.is_a?(filter) |
||||||
|
end.values).to eq([other_user.id.to_s]) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe "with the filter has the deleted user and another user as it's value" do |
||||||
|
before do |
||||||
|
query.filter(filter_symbol, values: [principal.id.to_s, other_user.id.to_s], operator: "=") |
||||||
|
query.save! |
||||||
|
|
||||||
|
job |
||||||
|
end |
||||||
|
|
||||||
|
it 'keeps the filter' do |
||||||
|
expect(CostQuery.find_by(id: query.id).deserialize.filters) |
||||||
|
.to(be_any { |f| f.is_a?(filter) }) |
||||||
|
end |
||||||
|
|
||||||
|
it 'removes only the deleted user' do |
||||||
|
expect(CostQuery.find_by(id: query.id).deserialize.filters.detect do |f| |
||||||
|
f.is_a?(filter) |
||||||
|
end.values).to eq([other_user.id.to_s]) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
describe "with the query has a user_id filter" do |
||||||
|
let(:filter) { CostQuery::Filter::UserId } |
||||||
|
|
||||||
|
it_should_behave_like "public query rewriting" |
||||||
|
end |
||||||
|
|
||||||
|
describe "with the query has a author_id filter" do |
||||||
|
let(:filter) { CostQuery::Filter::AuthorId } |
||||||
|
|
||||||
|
it_should_behave_like "public query rewriting" |
||||||
|
end |
||||||
|
|
||||||
|
describe "with the query has a assigned_to_id filter" do |
||||||
|
let(:filter) { CostQuery::Filter::AssignedToId } |
||||||
|
|
||||||
|
it_should_behave_like "public query rewriting" |
||||||
|
end |
||||||
|
|
||||||
|
describe "with the query has an responsible_id filter" do |
||||||
|
let(:filter) { CostQuery::Filter::ResponsibleId } |
||||||
|
|
||||||
|
it_should_behave_like "public query rewriting" |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with a user' do |
||||||
|
it_behaves_like 'removes the principal' |
||||||
|
it_behaves_like 'work_package handling' |
||||||
|
it_behaves_like 'labor_budget_item handling' |
||||||
|
it_behaves_like 'cost_entry handling' |
||||||
|
it_behaves_like 'hourly_rate handling' |
||||||
|
it_behaves_like 'member handling' |
||||||
|
it_behaves_like 'watcher handling' |
||||||
|
it_behaves_like 'token handling' |
||||||
|
it_behaves_like 'private query handling' |
||||||
|
it_behaves_like 'issue category handling' |
||||||
|
it_behaves_like 'private cost_query handling' |
||||||
|
it_behaves_like 'public cost_query handling' |
||||||
|
it_behaves_like 'cost_query handling' |
||||||
|
end |
||||||
|
|
||||||
|
context 'with a group' do |
||||||
|
let(:principal) { FactoryBot.create(:group, members: group_members) } |
||||||
|
let(:group_members) { [] } |
||||||
|
|
||||||
|
it_behaves_like 'removes the principal' |
||||||
|
it_behaves_like 'work_package handling' |
||||||
|
it_behaves_like 'member handling' |
||||||
|
|
||||||
|
context 'with user only in project through group' do |
||||||
|
let(:user) do |
||||||
|
FactoryBot.create(:user) |
||||||
|
end |
||||||
|
let(:group_members) { [user] } |
||||||
|
let(:watched) { FactoryBot.create(:news, project: project) } |
||||||
|
let(:watch) do |
||||||
|
Watcher.create(user: user, |
||||||
|
watchable: watched) |
||||||
|
end |
||||||
|
|
||||||
|
it 'removes the watcher' do |
||||||
|
job |
||||||
|
|
||||||
|
expect(watched.watchers.reload).to be_empty |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
context 'with a placeholder user' do |
||||||
|
let(:principal) { FactoryBot.create(:placeholder_user) } |
||||||
|
|
||||||
|
it_behaves_like 'removes the principal' |
||||||
|
it_behaves_like 'work_package handling' |
||||||
|
end |
||||||
|
end |
||||||
|
end |
Loading…
Reference in new issue