diff --git a/config/initializers/extends.rb b/config/initializers/extends.rb index 3d83ebbfd4..2694079f37 100644 --- a/config/initializers/extends.rb +++ b/config/initializers/extends.rb @@ -6,3 +6,4 @@ require "extends/cells/decidim/content_blocks/hero_cell_extends" require "extends/helpers/decidim/forms/application_helper_extends" require "extends/uploaders/decidim/application_uploader_extends" +require "extends/lib/decidim/proposals/imports/proposal_answer_creator_extends" diff --git a/lib/extends/lib/decidim/proposals/imports/proposal_answer_creator_extends.rb b/lib/extends/lib/decidim/proposals/imports/proposal_answer_creator_extends.rb new file mode 100644 index 0000000000..11724d815a --- /dev/null +++ b/lib/extends/lib/decidim/proposals/imports/proposal_answer_creator_extends.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module ProposalAnswerCreatorExtends + def notify + # If the initial state is set to nil, then it will be set to "" as it is a non-answered proposal. + # We still want to use further in the process and cannot read nil values. + state = initial_state || "" + ::Decidim::Proposals::Admin::NotifyProposalAnswer.call(resource, state) + end +end + +Decidim::Proposals::Import::ProposalAnswerCreator.class_eval do + prepend(ProposalAnswerCreatorExtends) +end diff --git a/spec/lib/decidim/proposals/import/proposal_answer_creator_spec.rb b/spec/lib/decidim/proposals/import/proposal_answer_creator_spec.rb new file mode 100644 index 0000000000..39a0bd01a9 --- /dev/null +++ b/spec/lib/decidim/proposals/import/proposal_answer_creator_spec.rb @@ -0,0 +1,143 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Decidim::Proposals::Import::ProposalAnswerCreator do + subject { described_class.new(data, context) } + + let(:proposal) { create(:proposal, state: state, component: component) } + let!(:moment) { Time.current } + let(:data) do + { + id: proposal.id, + state: state, + "answer/en": Faker::Lorem.paragraph + } + end + let(:organization) { create(:organization, available_locales: [:en]) } + let(:user) { create(:user, organization: organization) } + let(:context) do + { + current_organization: organization, + current_user: user, + current_component: component, + current_participatory_space: participatory_process + } + end + let(:participatory_process) { create :participatory_process, organization: organization } + let(:component) { create :component, manifest_name: :proposals, participatory_space: participatory_process } + let(:state) { %w(evaluating accepted rejected).sample } + + describe "#resource_klass" do + it "returns the correct class" do + expect(described_class.resource_klass).to be(Decidim::Proposals::Proposal) + end + end + + describe "#resource_attributes" do + it "returns the attributes hash" do + expect(subject.resource_attributes).to eq( + id: data[:id], + "answer/en": data[:"answer/en"], + state: data[:state] + ) + end + end + + describe "#produce" do + it "adds answer to proposal" do + record = subject.produce + + expect(record).to be_a(Decidim::Proposals::Proposal) + expect(record.id).to eq(data[:id]) + expect(record.answer["en"]).to eq(data[:"answer/en"]) + expect(record[:state]).to eq(data[:state]) + expect(record.answered_at).to be >= (moment) + end + + context "with an emendation" do + let!(:amendable) { create(:proposal, component: component) } + let!(:amendment) { create(:amendment, amendable: amendable, emendation: proposal, state: "evaluating") } + + it "does not produce a record" do + record = subject.produce + + expect(record).to be(nil) + end + end + end + + describe "#finish!" do + it "saves the proposal" do + record = subject.produce + subject.finish! + expect(record.new_record?).to be(false) + end + + it "creates an admin log record" do + record = subject.produce + subject.finish! + + log = Decidim::ActionLog.last + expect(log.resource).to eq(record) + expect(log.action).to eq("answer") + end + + shared_examples "it notifies follower" do + it "returns broadcast :ok" do + expect(subject.finish!).to eq({ ok: [] }) + end + + context "and notifies followers" do + before do + allow(::Decidim::Proposals::Admin::NotifyProposalAnswer).to receive(:call).with(proposal, expected_state) + end + + it "notifies followers" do + subject.finish! + expect(::Decidim::Proposals::Admin::NotifyProposalAnswer).to have_received(:call) + end + end + end + + context "when proposal state changes" do + context "when proposal had already a state" do + let!(:proposal) { create(:proposal, :evaluating, component: component) } + let(:state) { "accepted" } + let(:expected_state) { "evaluating" } + + include_examples "it notifies follower" + end + + context "when proposal had no state" do + let!(:proposal) { create(:proposal, :not_answered, component: component) } + let(:state) { "accepted" } + let(:expected_state) { "" } + + include_examples "it notifies follower" + end + + context "when proposal was just created and had a state set to nil" do + let!(:proposal) { create(:proposal, component: component, state: nil) } + let(:state) { "accepted" } + let(:expected_state) { "" } + + include_examples "it notifies follower" + end + end + + context "when proposal does not exist" do + let(:data) do + { + id: 99_999_999, + state: state, + "answer/en": Faker::Lorem.paragraph + } + end + + it "broadcasts invalid message" do + expect(subject.finish!).to eq({ invalid: [] }) + end + end + end +end