Files
openproject/spec/services/add_work_package_note_service_spec.rb
Kabiru Mwenja 34e136169e bug/65077 Inline comment attachments are not linked to the comment when submit is via API (#19304)
Relocate attachment claims in corresponding (journal) comment create and update services as
dependent services so that they are always invoked in both API & HTML contexts.

Follows:
 * https://github.com/opf/openproject/pull/18537
 * https://github.com/opf/openproject/pull/18648

https://community.openproject.org/work_packages/65077
2025-06-25 10:24:24 +03:00

217 lines
6.9 KiB
Ruby

# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 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 COPYRIGHT and LICENSE files for more details.
#++
require "spec_helper"
RSpec.describe AddWorkPackageNoteService, type: :model do
let(:user) { build_stubbed(:user) }
let(:work_package) { build_stubbed(:work_package) }
let(:instance) do
described_class.new(user:,
work_package:)
end
describe ".contract" do
it "uses the CreateNoteContract contract" do
expect(instance.contract_class).to eql WorkPackages::CreateNoteContract
end
end
describe "call" do
let(:mock_contract) do
class_double(WorkPackages::CreateNoteContract,
new: mock_contract_instance)
end
let(:mock_contract_instance) do
instance_double(WorkPackages::CreateNoteContract,
errors: contract_errors,
validate: valid_contract)
end
let(:valid_contract) { true }
let(:contract_errors) { instance_double(ActiveModel::Errors, full_messages: ["error message"]) }
let(:claims_service) { WorkPackages::ActivitiesTab::CommentAttachmentsClaims::ClaimsService }
let(:mock_claim_service_instance) do
instance_double(claims_service, call: ServiceResult.success(result: []))
end
let(:send_notifications) { false }
before do
allow(instance).to receive(:contract_class).and_return(mock_contract)
allow(work_package).to receive(:add_journal).and_call_original
allow(work_package).to receive(:save_journals).and_return(true)
allow(claims_service).to receive(:new).and_return(mock_claim_service_instance)
end
subject { instance.call("blubs", send_notifications:) }
it "persists the value" do
expect(subject).to be_success
expect(work_package).to have_received(:add_journal)
.with(user: user, notes: "blubs", internal: false)
expect(work_package).to have_received(:save_journals)
end
context "with internal note" do
subject { instance.call("blubs", send_notifications:, internal: true) }
it "persists the value" do
expect(subject).to be_success
expect(work_package).to have_received(:add_journal)
.with(user: user, notes: "blubs", internal: true)
expect(work_package).to have_received(:save_journals)
end
end
context "when the journal notes have attachments" do
let(:attachment1) { build_stubbed(:attachment) }
let(:attachment2) { build_stubbed(:attachment) }
let(:notes) do
<<~HTML
<img class="op-uc-image op-uc-image_inline" src="/api/v3/attachments/#{attachment1.id}/content">
Lorem ipsum dolor sit amet
<img class="op-uc-image op-uc-image_inline" src="/api/v3/attachments/#{attachment2.id}/content">
consectetur adipiscing elit
HTML
end
let(:mock_claim_service_instance) do
instance_double(claims_service, call: ServiceResult.success(result: [attachment1, attachment2]))
end
subject { instance.call(notes, send_notifications:) }
context "and note creation is successful" do
it "creates an attachment claim" do
expect(subject).to be_success
expect(WorkPackages::ActivitiesTab::CommentAttachmentsClaims::ClaimsService)
.to have_received(:new).with(user: user, model: work_package.journals.last)
dependent_results = subject.dependent_results
expect(dependent_results.size).to eq(1)
expect(dependent_results.first).to be_a_success
expect(dependent_results.first.result).to contain_exactly(attachment1, attachment2)
end
end
context "and note creation is unsuccessful" do
let(:valid_contract) { false }
it "does not create an attachment claim" do
expect(subject).to be_a_failure
expect(WorkPackages::ActivitiesTab::CommentAttachmentsClaims::ClaimsService)
.not_to have_received(:new)
expect(subject.dependent_results).to be_empty
end
end
end
it "creates an advisory lock" do
allow(OpenProject::Mutex)
.to receive(:with_advisory_lock_transaction)
.with(work_package)
.and_call_original
subject
expect(OpenProject::Mutex)
.to have_received(:with_advisory_lock_transaction)
end
it "sends notifications" do
allow(Journal::NotificationConfiguration)
.to receive(:with)
.with(send_notifications)
.and_yield
subject
expect(Journal::NotificationConfiguration)
.to have_received(:with)
end
it "has no errors" do
expect(subject.errors).to be_empty
end
context "when the contract does not validate" do
let(:valid_contract) { false }
it "is unsuccessful" do
expect(subject.success?).to be_falsey
end
it "does not persist the changes" do
expect(work_package).not_to receive(:save_journals)
subject
end
it "exposes the contract's errors" do
errors = double("errors")
allow(mock_contract_instance).to receive(:errors).and_return(errors)
subject
expect(subject.errors).to eql errors
end
end
context "when the saving is unsuccessful" do
before do
expect(work_package).to receive(:save_journals).and_return false
end
it "is unsuccessful" do
expect(subject).not_to be_success
end
it "leaves the value unchanged" do
subject
expect(work_package.journal_notes).to eql "blubs"
expect(work_package.journal_user).to eql user
end
it "exposes the work_packages's errors" do
errors = double("errors")
allow(work_package).to receive(:errors).and_return(errors)
subject
expect(subject.errors).to eql errors
end
end
end
end