From 66df9c662d51ffc365c952fdf00902f80f0706f4 Mon Sep 17 00:00:00 2001 From: Guillaume MORET <90462045+AyakorK@users.noreply.github.com> Date: Thu, 11 Jan 2024 12:16:17 +0100 Subject: [PATCH] fix: Compatibility between committee promotion and initiatives creation (#470) * fix: Change the behavior of the committee requests to make it intuitive * fix: test addition * fix: Redirect to login page if logged out on the page * fix: Improve code coverage * fix: Add some more tests to try to improve code coverage --- config/application.rb | 1 + .../committee_requests_controller_extends.rb | 26 ++ .../committee_requests_controller_spec.rb | 226 ++++++++++++++++++ 3 files changed, 253 insertions(+) create mode 100644 lib/extends/controllers/decidim/initiatives/committee_requests_controller_extends.rb create mode 100644 spec/controllers/decidim/initiatives/committee_requests_controller_spec.rb diff --git a/config/application.rb b/config/application.rb index 8e5d323de9..312fa370d7 100644 --- a/config/application.rb +++ b/config/application.rb @@ -49,6 +49,7 @@ class Application < Rails::Application require "extends/services/decidim/iframe_disabler_extends" require "extends/helpers/decidim/icon_helper_extends" require "extends/commands/decidim/initiatives/admin/update_initiative_answer_extends" + require "extends/controllers/decidim/initiatives/committee_requests_controller_extends" Decidim::GraphiQL::Rails.config.tap do |config| config.initial_query = "{\n deployment {\n version\n branch\n remote\n upToDate\n currentCommit\n latestCommit\n locallyModified\n }\n}".html_safe diff --git a/lib/extends/controllers/decidim/initiatives/committee_requests_controller_extends.rb b/lib/extends/controllers/decidim/initiatives/committee_requests_controller_extends.rb new file mode 100644 index 0000000000..960295a967 --- /dev/null +++ b/lib/extends/controllers/decidim/initiatives/committee_requests_controller_extends.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module CommitteeRequestsControllerExtends + def new + return if authorized?(current_user) + + if current_user.nil? + redirect_to decidim.new_user_session_path + else + authorization_method = Decidim::Verifications::Adapter.from_element(current_initiative.document_number_authorization_handler) + redirect_url = new_initiative_committee_request_path(current_initiative) + redirect_to authorization_method.root_path(redirect_url: redirect_url) + end + end + + private + + def authorized?(user) + authorization = current_initiative.document_number_authorization_handler + Decidim::Authorization.exists?(user: user, name: authorization) + end +end + +Decidim::Initiatives::CommitteeRequestsController.class_eval do + prepend(CommitteeRequestsControllerExtends) +end diff --git a/spec/controllers/decidim/initiatives/committee_requests_controller_spec.rb b/spec/controllers/decidim/initiatives/committee_requests_controller_spec.rb new file mode 100644 index 0000000000..2ef1af7b69 --- /dev/null +++ b/spec/controllers/decidim/initiatives/committee_requests_controller_spec.rb @@ -0,0 +1,226 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Initiatives + describe CommitteeRequestsController, type: :controller do + routes { Decidim::Initiatives::Engine.routes } + + let(:organization) { create(:organization) } + let!(:initiative) { create(:initiative, :created, organization: organization) } + let(:admin_user) { create(:user, :admin, :confirmed, organization: organization) } + let(:user) { create(:user, :confirmed, organization: organization) } + + before do + request.env["decidim.current_organization"] = organization + end + + context "when GET new" do + let(:current_user) { create(:user, :confirmed, organization: organization) } + let(:authorization_handler) { "dummy_authorization_handler" } + let(:committee_request_path) { "/initiatives/#{initiative.id}/committee_requests/new" } + + before do + allow(controller).to receive(:current_initiative).and_return(initiative) + allow(controller).to receive(:current_user).and_return(current_user) + allow(controller).to receive(:authorized?).and_return(authorized) + allow(initiative).to receive(:document_number_authorization_handler).and_return(authorization_handler) + end + + context "when not authorized" do + let(:authorized) { false } + + it "redirects to authorization root path" do + allow(controller).to receive(:authorized?).with(current_user).and_return(false) + allow(controller).to receive(:new_initiative_committee_request_path).with(initiative).and_return(committee_request_path) + + get :new, params: { initiative_slug: initiative.slug } + + expect(response).to have_http_status(:found) + end + end + + context "when not logged in" do + let(:current_user) { nil } + let(:authorized) { false } + + it "redirects to login page" do + allow(controller).to receive(:new_initiative_committee_request_path).with(initiative).and_return(committee_request_path) + + get :new, params: { initiative_slug: initiative.slug } + + expect(response).to have_http_status(:found) + expect(URI.parse(response.location).path).to eq("/users/sign_in") + end + end + + context "when authorized" do + let(:authorized) { true } + + it "does not redirect" do + allow(controller).to receive(:authorized?).with(current_user).and_return(true) + + get :new, params: { initiative_slug: initiative.slug } + + expect(response).to have_http_status(:ok) + end + end + end + + context "when authorized? is called" do + let(:current_user) { create(:user, :confirmed, organization: organization) } + let(:authorization_handler) { "dummy_authorization_handler" } + + before do + allow(controller).to receive(:current_initiative).and_return(initiative) + allow(controller).to receive(:current_user).and_return(current_user) + allow(initiative).to receive(:document_number_authorization_handler).and_return(authorization_handler) + end + + context "when authorized" do + it "returns true" do + allow(controller).to receive(:authorized?).with(current_user).and_return(true) + + result = controller.send(:authorized?, current_user) + + expect(result).to be(true) + end + end + + context "when not authorized" do + it "returns false" do + allow(controller).to receive(:authorized?).with(current_user).and_return(false) + + result = controller.send(:authorized?, current_user) + + expect(result).to be(false) + end + end + end + + context "when GET spawn" do + let(:user) { create(:user, :confirmed, organization: organization) } + + before do + create(:authorization, user: user) + sign_in user, scope: :user + end + + context "and created initiative" do + it "Membership request is created" do + expect do + get :spawn, params: { initiative_slug: initiative.slug } + end.to change(InitiativesCommitteeMember, :count).by(1) + end + + it "Duplicated requests finish with an error" do + expect do + get :spawn, params: { initiative_slug: initiative.slug } + end.to change(InitiativesCommitteeMember, :count).by(1) + + expect do + get :spawn, params: { initiative_slug: initiative.slug } + end.not_to change(InitiativesCommitteeMember, :count) + end + end + + context "and published initiative" do + let!(:published_initiative) { create(:initiative, :published, organization: organization) } + + it "Membership request is not created" do + expect do + get :spawn, params: { initiative_slug: published_initiative.slug } + end.not_to change(InitiativesCommitteeMember, :count) + end + end + end + + context "when GET approve" do + let(:membership_request) { create(:initiatives_committee_member, initiative: initiative, state: "requested") } + + context "and Owner" do + before do + sign_in initiative.author, scope: :user + end + + it "request gets approved" do + get :approve, params: { initiative_slug: membership_request.initiative.to_param, id: membership_request.to_param } + membership_request.reload + expect(membership_request).to be_accepted + end + end + + context "and other users" do + let(:user) { create(:user, :confirmed, organization: organization) } + + before do + create(:authorization, user: user) + sign_in user, scope: :user + end + + it "Action is denied" do + get :approve, params: { initiative_slug: membership_request.initiative.to_param, id: membership_request.to_param } + expect(flash[:alert]).not_to be_empty + expect(response).to have_http_status(:found) + end + end + + context "and Admin" do + before do + sign_in admin_user, scope: :user + end + + it "request gets approved" do + get :approve, params: { initiative_slug: membership_request.initiative.to_param, id: membership_request.to_param } + membership_request.reload + expect(membership_request).to be_accepted + end + end + end + + context "when DELETE revoke" do + let(:membership_request) { create(:initiatives_committee_member, initiative: initiative, state: "requested") } + + context "and Owner" do + before do + sign_in initiative.author, scope: :user + end + + it "request gets approved" do + delete :revoke, params: { initiative_slug: membership_request.initiative.to_param, id: membership_request.to_param } + membership_request.reload + expect(membership_request).to be_rejected + end + end + + context "and Other users" do + let(:user) { create(:user, :confirmed, organization: organization) } + + before do + create(:authorization, user: user) + sign_in user, scope: :user + end + + it "Action is denied" do + delete :revoke, params: { initiative_slug: membership_request.initiative.to_param, id: membership_request.to_param } + expect(flash[:alert]).not_to be_empty + expect(response).to have_http_status(:found) + end + end + + context "and Admin" do + before do + sign_in admin_user, scope: :user + end + + it "request gets approved" do + delete :revoke, params: { initiative_slug: membership_request.initiative.to_param, id: membership_request.to_param } + membership_request.reload + expect(membership_request).to be_rejected + end + end + end + end + end +end