Repository: CSCfi/rems (clojure)

Issue:
Shouldn't be able to submit application if catalogue item is disabled/archived/expired
... but submitted applications can still be handled.

Pull request:
fixes #1524

Patch:
diff --git a/src/clj/rems/application/commands.clj b/src/clj/rems/application/commands.clj
index d585cf1eec..3d3bed8c67 100644
--- a/src/clj/rems/application/commands.clj
+++ b/src/clj/rems/application/commands.clj
@@ -198,6 +198,15 @@
     (not get-catalogue-item) {:errors [{:type :missing-injection :injection :get-catalogue-item}]}
     (not (get-catalogue-item catalogue-item-id)) {:errors [{:type :invalid-catalogue-item :catalogue-item-id catalogue-item-id}]}))
 
+(defn- disabled-catalogue-items-error [application {:keys [get-catalogue-item]}]
+  (let [errors (for [item (:application/resources application)
+                     :when (or (not (getx item :catalogue-item/enabled))
+                               (getx item :catalogue-item/archived)
+                               (getx item :catalogue-item/expired))]
+        {:type :disabled-catalogue-item :catalogue-item-id (getx item :catalogue-item/id)})]
+    (when (not (empty? errors))
+      {:errors (vec errors)})))
+
 (defn- invalid-catalogue-items
   "Checks the given catalogue items for validity and merges the errors"
   [catalogue-item-ids injections]
@@ -332,6 +341,7 @@
 (defmethod command-handler :application.command/submit
   [_cmd application injections]
   (or (validation-error application injections)
+      (disabled-catalogue-items-error application injections)
       (ok {:event/type :application.event/submitted})))
 
 (defmethod command-handler :application.command/approve
diff --git a/src/clj/rems/db/applications.clj b/src/clj/rems/db/applications.clj
index 92002f5cd3..eaf5695601 100644
--- a/src/clj/rems/db/applications.clj
+++ b/src/clj/rems/db/applications.clj
@@ -100,6 +100,10 @@
 (mount/defstate application-cache
   :start (atom (cache/ttl-cache-factory {} :ttl 10000)))
 
+;; TODO combine with reload-cache!?
+(defn- reset-application-cache! []
+  (swap! application-cache empty))
+
 (defn get-unrestricted-application
   "Returns the full application state without any user permission
    checks and filtering of sensitive information. Don't expose via APIs."
@@ -240,6 +244,7 @@
 
 (defn reload-cache! []
   ;; TODO: Here is a small chance that a user will experience a cache miss. Consider rebuilding the cache asynchronously and then `reset!` the cache.
+  (reset-application-cache!)
   (events-cache/empty! all-applications-cache)
   (refresh-all-applications-cache!))
 


Test patch:
diff --git a/test/clj/rems/api/test_applications.clj b/test/clj/rems/api/test_applications.clj
index 7f0a9f0a5c..6bb1b0d618 100644
--- a/test/clj/rems/api/test_applications.clj
+++ b/test/clj/rems/api/test_applications.clj
@@ -1,6 +1,8 @@
 (ns ^:integration rems.api.test-applications
   (:require [clojure.test :refer :all]
+            [rems.api.services.catalogue :as catalogue]
             [rems.api.testing :refer :all]
+            [rems.db.applications]
             [rems.db.form :as form]
             [rems.db.test-data :as test-data]
             [rems.handler :refer [handler]]
@@ -432,6 +434,38 @@
     (is (= "application.state/closed"
            (:application/state (get-application application-id user-id))))))
 
+(deftest test-application-submit
+  (let [user-id "alice"
+        form-id (test-data/create-form! {})
+        cat-id (test-data/create-catalogue-item! {:form-id form-id})
+        app-id (test-data/create-application! {:catalogue-item-ids [cat-id] :actor user-id})]
+    (testing "submit with disabled catalogue item fails"
+      (is (:success (catalogue/update-catalogue-item! {:id cat-id
+                                                       :enabled false
+                                                       :archived false})))
+      (rems.db.applications/reload-cache!)
+      (is (= {:success false
+              :errors [{:type "disabled-catalogue-item" :catalogue-item-id cat-id}]}
+             (send-command user-id {:type :application.command/submit
+                                    :application-id app-id}))))
+    (testing "submit with archived catalogue item fails"
+      (is (:success (catalogue/update-catalogue-item! {:id cat-id
+                                                       :enabled true
+                                                       :archived true})))
+      (rems.db.applications/reload-cache!)
+      (is (= {:success false
+              :errors [{:type "disabled-catalogue-item" :catalogue-item-id cat-id}]}
+             (send-command user-id {:type :application.command/submit
+                                    :application-id app-id}))))
+    (testing "submit with normal catalogue item succeeds"
+      (is (:success (catalogue/update-catalogue-item! {:id cat-id
+                                                       :enabled true
+                                                       :archived false})))
+      (rems.db.applications/reload-cache!)
+      (is (= {:success true}
+             (send-command user-id {:type :application.command/submit
+                                    :application-id app-id}))))))
+
 (deftest test-application-validation
   (let [user-id "alice"
         form-id (test-data/create-form! {:form/fields [{:field/title {:en "req"}
diff --git a/test/clj/rems/application/test_commands.clj b/test/clj/rems/application/test_commands.clj
index 0fbcb7b09f..9daafbd5b7 100644
--- a/test/clj/rems/application/test_commands.clj
+++ b/test/clj/rems/application/test_commands.clj
@@ -28,10 +28,14 @@
                                                           :email "user@example.com"}]}}})
 (def ^:private dummy-forms {1 {}})
 
+(defn- dummy-get-catalogue-item [id]
+  {:enabled true :archived false :expired false
+   :id id :wfid 1 :formid 1})
+
 (def ^:private injections
   {:get-workflow dummy-workflows
    :get-form-template dummy-forms
-   :get-catalogue-item (constantly nil)
+   :get-catalogue-item dummy-get-catalogue-item
    :get-license (constantly nil)
    :get-user (constantly nil)
    :get-users-with-role (constantly nil)
@@ -511,6 +515,11 @@
                  (apply-events [(assoc-in draft-saved-event [:application/field-values 41] "")])
                  (fail-command submit-command injections)))))
 
+    (testing "cannot submit if catalogue item is disabled"
+      (let [disabled (assoc-in application [:application/resources 1 :catalogue-item/enabled] false)]
+        (is (= {:errors [{:type :disabled-catalogue-item, :catalogue-item-id 20}]}
+               (fail-command disabled submit-command injections)))))
+
     (testing "non-applicant cannot submit"
       (is (= {:errors [{:type :forbidden}]}
              (fail-command application