Repository: CSCfi/rems
Pull request #1488 (2019-08-14 09:56:00)

fix email sending


Patch:
diff --git a/src/clj/rems/application/search.clj b/src/clj/rems/application/search.clj
index 6500e2618..6e55bba0a 100644
--- a/src/clj/rems/application/search.clj
+++ b/src/clj/rems/application/search.clj
@@ -1,13 +1,10 @@
 (ns rems.application.search
   (:require [clojure.java.io :as io]
-            [clojure.string :as str]
             [clojure.tools.logging :as log]
             [mount.core :as mount]
-            [rems.application-util :as application-util]
             [rems.config :refer [env]]
             [rems.db.applications :as applications]
-            [rems.db.events :as events]
-            [rems.text :as text])
+            [rems.db.events :as events])
   (:import [org.apache.lucene.analysis.standard StandardAnalyzer]
            [org.apache.lucene.document Document StringField Field$Store TextField]
            [org.apache.lucene.index IndexWriter IndexWriterConfig IndexWriterConfig$OpenMode]
@@ -30,46 +27,11 @@
           (.close ^SearcherManager (::searcher-manager @search-index))
           (.close ^Directory (::directory @search-index))))
 
-(defn- index-terms-for-application [app]
-  {:id (->> [(:application/id app)
-             (:application/external-id app)]
-            (str/join " "))
-   :applicant (->> [(:application/applicant app)
-                    (application-util/get-applicant-name app)
-                    (:mail (:application/applicant-attributes app))]
-                   (str/join " "))
-   :member (->> (:application/members app)
-                (mapcat (fn [member]
-                          [(:userid member)
-                           (application-util/get-member-name member)
-                           (:mail member)]))
-                (str/join " "))
-   :title (:application/description app)
-   :resource (->> (:application/resources app)
-                  (mapcat (fn [resource]
-                            (vals (:catalogue-item/title resource))))
-                  (str/join " "))
-   :state (->> (:languages env)
-               (map (fn [lang]
-                      (text/with-language lang
-                        #(text/localize-state (:application/state app)))))
-               (str/join " "))
-   :form (->> (:form/fields (:application/form app))
-              (map (fn [field]
-                     ;; TODO: filter out checkboxes, attachments etc?
-                     (:field/value field)))
-              (str/join " "))})
-
 (defn- index-application! [^IndexWriter writer app]
   (log/info "Indexing application" (:application/id app))
-  (let [doc (Document.)
-        terms (index-terms-for-application app)]
-    ;; metadata
-    (.add doc (StringField. "app-id" (str (:application/id app)) Field$Store/YES))
-    ;; searchable fields
-    (doseq [[k v] terms]
-      (.add doc (TextField. (name k) v Field$Store/NO)))
-    (.add doc (TextField. "all" (str/join " " (vals terms)) Field$Store/NO))
+  (let [doc (Document.)]
+    (.add doc (StringField. "id" (str (:application/id app)) Field$Store/YES))
+    (.add doc (TextField. "applicant" (str (:application/applicant app)) Field$Store/NO))
     (.addDocument writer doc)))
 
 (def ^:private refresh-lock (Object.))
@@ -99,13 +61,13 @@
 (defn- get-application-ids [^IndexSearcher searcher ^TopDocs results]
   (doall (for [^ScoreDoc hit (.-scoreDocs results)]
            (let [doc (.doc searcher (.-doc hit))
-                 app-id (.get doc "app-id")]
-             (Long/parseLong app-id)))))
+                 id (.get doc "id")]
+             (Long/parseLong id)))))
 
 (defn- ^Query parse-query [^String query]
   (try
     (-> (StandardQueryParser. analyzer)
-        (.parse query "all"))
+        (.parse query "applicant")) ; TODO: change defaultField to full text search
     (catch QueryNodeException e
       (log/info (str "Failed to parse query '" query "', " e))
       nil)))
diff --git a/src/clj/rems/locales.clj b/src/clj/rems/locales.clj
index 3e420047a..953e67d64 100644
--- a/src/clj/rems/locales.clj
+++ b/src/clj/rems/locales.clj
@@ -16,9 +16,9 @@
                         (and file (.exists file)) file
                         resource resource
                         :else (throw (FileNotFoundException.
-                                      (if file
-                                        (str "translations could not be found in " file " file or " resource-path " resource")
-                                        (str "translations could not be found in " resource-path " resource and " :translations-directory " was not set")))))]
+                                       (if file
+                                         (str "translations could not be found in " file " file or " resource-path " resource")
+                                         (str "translations could not be found in " resource-path " resource and " :translations-directory " was not set")))))]
     (read-string (slurp file-contents))))
 
 (defn- extra-translations-path [theme-path]
@@ -43,5 +43,4 @@
 (defstate translations :start (load-translations env))
 
 (defn tempura-config []
-  (assert (map? translations) {:translations translations})
   {:dict translations})
diff --git a/src/clj/rems/poller/email.clj b/src/clj/rems/poller/email.clj
index 335edb2ff..eb8a901e6 100644
--- a/src/clj/rems/poller/email.clj
+++ b/src/clj/rems/poller/email.clj
@@ -71,13 +71,15 @@
                                 (:application/applicant application)
                                 (resources-for-email application)
                                 (link-to-application (:application/id event)))
-          :body (text-format body-text
-                             recipient
-                             (:event/actor event)
-                             (application-id-for-email application)
-                             (:application/applicant application)
-                             (resources-for-email application)
-                             (link-to-application (:application/id event)))})))))
+          :body (str
+                 (text-format body-text
+                              recipient
+                              (:event/actor event)
+                              (application-id-for-email application)
+                              (:application/applicant application)
+                              (resources-for-email application)
+                              (link-to-application (:application/id event)))
+                 (text :t.email/footer))})))))
 
 (defmethod event-to-emails-impl :application.event/approved [event application]
   (concat (emails-to-recipients (applicant-and-members application)
@@ -173,9 +175,11 @@
         :subject (text-format :t.email.member-invited/subject
                               (:email (:application/member event))
                               (invitation-link (:invitation/token event)))
-        :body (text-format :t.email.member-invited/message
-                           (:email (:application/member event))
-                           (invitation-link (:invitation/token event)))}])))
+        :body (str
+               (text-format :t.email.member-invited/message
+                            (:email (:application/member event))
+                            (invitation-link (:invitation/token event)))
+               (text :t.email/footer))}])))
 
 ;; TODO member-joined?
 
@@ -207,8 +211,7 @@
       (log/info "pretending to send email:" (pr-str email-spec))
       (let [email (assoc email-spec
                          :from (:mail-from env)
-                         :body (str (:body email-spec)
-                                    (text :t.email/footer))
+                         :body (:body email-spec)
                          :to (or (:to email-spec)
                                  (util/get-user-mail
                                   (users/get-user-attributes


Test patch:
diff --git a/src/clj/rems/db/test_data.clj b/src/clj/rems/db/test_data.clj
index 1720236d4..95e0d9186 100644
--- a/src/clj/rems/db/test_data.clj
+++ b/src/clj/rems/db/test_data.clj
@@ -178,7 +178,7 @@
     (doseq [role roles]
       (roles/add-role! user role))))
 
-(defn create-users-and-roles! []
+(defn- create-users-and-roles! []
   ;; users provided by the fake login
   (let [users (comp +fake-user-data+ +fake-users+)]
     (create-user! (users :applicant1))
diff --git a/test/clj/rems/api/testing.clj b/test/clj/rems/api/testing.clj
index c59e8b381..22fc99b81 100644
--- a/test/clj/rems/api/testing.clj
+++ b/test/clj/rems/api/testing.clj
@@ -3,7 +3,7 @@
   (:require [clojure.test :refer :all]
             [cheshire.core :refer [parse-stream]]
             [mount.core :as mount]
-            [rems.db.testing :refer [test-data-fixture test-db-fixture caches-fixture search-index-fixture]]
+            [rems.db.testing :refer [test-data-fixture test-db-fixture caches-fixture]]
             [rems.handler :refer :all]
             [ring.mock.request :refer :all]))
 
@@ -17,7 +17,6 @@
 
 (def api-fixture
   (join-fixtures [test-db-fixture
-                  search-index-fixture
                   caches-fixture
                   test-data-fixture
                   handler-fixture]))
diff --git a/test/clj/rems/application/test_search.clj b/test/clj/rems/application/test_search.clj
index 4b9757ba8..b4dfd8f19 100644
--- a/test/clj/rems/application/test_search.clj
+++ b/test/clj/rems/application/test_search.clj
@@ -1,104 +1,30 @@
 (ns ^:integration rems.application.test-search
   (:require [clojure.test :refer :all]
             [rems.application.search :as search]
-            [rems.db.applications :as applications]
             [rems.db.test-data :as test-data]
-            [rems.db.testing :refer [test-db-fixture search-index-fixture]]))
+            [rems.db.testing :refer [test-db-fixture rollback-db-fixture]]))
 
-(use-fixtures :once (join-fixtures [test-db-fixture
-                                    search-index-fixture]))
+(use-fixtures :once test-db-fixture)
+(use-fixtures :each rollback-db-fixture)
 
 (deftest test-application-search
-  ;; generate users with full names and emails
-  (test-data/create-users-and-roles!)
   ;; unrelated application - it's an error if any of the tests finds this
   (test-data/create-application! {:actor "developer"})
+  (search/refresh!)
 
   (testing "find by applicant"
     (let [app-id (test-data/create-application! {:actor "alice"})]
-      (is (= #{app-id} (search/find-applications "alice")) "user ID, any field")
-      (is (= #{app-id} (search/find-applications "applicant:alice")) "user ID")
-      (is (= #{app-id} (search/find-applications "applicant:\"Alice Applicant\"")) "name")
-      (is (= #{app-id} (search/find-applications "applicant:\"alice@example.com\"")) "email")))
+      (is (= #{app-id} (search/find-applications "alice")))))
 
-  (testing "find by member"
-    (let [app-id (test-data/create-application! {:actor "alice"})]
-      (test-data/command! {:type :application.command/submit
-                           :application-id app-id
-                           :actor "alice"})
-      (test-data/command! {:type :application.command/add-member
-                           :application-id app-id
-                           :actor "developer"
-                           :member {:userid "bob"}})
-      (is (= #{app-id} (search/find-applications "bob")) "user ID, any field")
-      (is (= #{app-id} (search/find-applications "member:bob")) "user ID")
-      (is (= #{app-id} (search/find-applications "member:\"Bob Approver\"")) "name")
-      (is (= #{app-id} (search/find-applications "member:\"bob@example.com\"")) "email")))
-
-  (testing "find by ID"
-    (let [app-id (test-data/create-application! {:actor "alice"})
-          app (applications/get-unrestricted-application app-id)]
-      (is (= #{app-id} (search/find-applications (str app-id))) "app ID, any field")
-      (is (= #{app-id} (search/find-applications (str "id:" app-id))) "app ID")
-      (is (= #{app-id} (search/find-applications (str "id:\"" (:application/external-id app) "\""))) "external ID")))
+  (testing "find by member")
 
-  (testing "find by title"
-    (let [form-id (test-data/create-form! {:form/fields [{:field/type :description
-                                                          :field/title {:en "Title"}
-                                                          :field/optional false}]})
-          cat-id (test-data/create-catalogue-item! {:form-id form-id})
-          app-id (test-data/create-application! {:catalogue-item-ids [cat-id]
-                                                 :actor "alice"})]
-      (test-data/command! {:type :application.command/save-draft
-                           :application-id app-id
-                           :actor "alice"
-                           :field-values [{:field 1
-                                           :value "Supercalifragilisticexpialidocious"}]})
-      (test-data/command! {:type :application.command/submit ; make sure that the required field was filled in
-                           :application-id app-id
-                           :actor "alice"})
-      (is (= #{app-id} (search/find-applications "Supercalifragilisticexpialidocious")) "any field")
-      (is (= #{app-id} (search/find-applications "title:Supercalifragilisticexpialidocious")) "title field")))
+  (testing "find by title")
 
-  (testing "find by resource"
-    (let [cat-id (test-data/create-catalogue-item! {:title {:en "Spam"
-                                                            :fi "Nötkötti"}})
-          app-id (test-data/create-application! {:catalogue-item-ids [cat-id]
-                                                 :actor "alice"})]
-      (is (= #{app-id} (search/find-applications "Spam")) "en title, any field")
-      (is (= #{app-id} (search/find-applications "resource:Spam")) "en title")
-      (is (= #{app-id} (search/find-applications "resource:Nötkötti")) "fi title")))
+  (testing "find by resource")
 
-  (testing "find by state"
-    (let [app-id (test-data/create-application! {:actor "alice"})]
-      (test-data/command! {:type :application.command/submit
-                           :application-id app-id
-                           :actor "alice"})
-      (test-data/command! {:type :application.command/approve
-                           :application-id app-id
-                           :actor "developer"
-                           :comment ""})
-      (is (= #{app-id} (search/find-applications "Approved")) "en status, any field")
-      (is (= #{app-id} (search/find-applications "state:Approved")) "en status")
-      (is (= #{app-id} (search/find-applications "state:Hyväksytty")) "fi status")))
+  (testing "find by state")
 
-  (testing "find by form content"
-    (let [form-id (test-data/create-form! {:form/fields [{:field/type :text
-                                                          :field/title {:en "Text field"}
-                                                          :field/optional false}]})
-          cat-id (test-data/create-catalogue-item! {:form-id form-id})
-          app-id (test-data/create-application! {:catalogue-item-ids [cat-id]
-                                                 :actor "alice"})]
-      (test-data/command! {:type :application.command/save-draft
-                           :application-id app-id
-                           :actor "alice"
-                           :field-values [{:field 1
-                                           :value "Tis but a scratch."}]})
-      (test-data/command! {:type :application.command/submit ; make sure that the required field was filled in
-                           :application-id app-id
-                           :actor "alice"})
-      (is (= #{app-id} (search/find-applications "scratch")) "any field")
-      (is (= #{app-id} (search/find-applications "form:scratch")) "form field")))
+  (testing "find by form content")
 
   (testing "invalid query"
     (is (= nil (search/find-applications "+")))))
diff --git a/test/clj/rems/db/testing.clj b/test/clj/rems/db/testing.clj
index 79413e851..c52d0cd10 100644
--- a/test/clj/rems/db/testing.clj
+++ b/test/clj/rems/db/testing.clj
@@ -5,30 +5,23 @@
             [conman.core :as conman]
             [luminus-migrations.core :as migrations]
             [mount.core :as mount]
-            [rems.application.search]
             [rems.config :refer [env]]
             [rems.db.applications]
             [rems.db.core :as db]
-            [rems.db.test-data :as test-data]
-            [rems.locales])
+            [rems.db.test-data :as test-data])
   (:import [org.joda.time Duration ReadableInstant]))
 
 (defn test-db-fixture [f]
   (mount/stop) ;; during interactive development, app might be running when tests start. we need to tear it down
   (mount/start-with-args {:test true}
                          #'rems.config/env
-                         #'rems.db.core/*db*)
+                         #'rems.db.core/*db*
+                         #'rems.application.search/search-index)
   (db/assert-test-database!)
   (migrations/migrate ["reset"] {:database-url (:test-database-url env)})
   (f)
   (mount/stop))
 
-(defn search-index-fixture [f]
-  ;; no specific teardown. relies on the teardown of test-db-fixture.
-  (mount/start #'rems.application.search/search-index
-               #'rems.locales/translations)
-  (f))
-
 (defn caches-fixture [f]
   ;; no specific teardown. relies on the teardown of test-db-fixture.
   (mount/start #'rems.db.applications/application-cache
diff --git a/test/clj/rems/poller/test_email.clj b/test/clj/rems/poller/test_email.clj
index d56fd9b4e..dcb22a07a 100644
--- a/test/clj/rems/poller/test_email.clj
+++ b/test/clj/rems/poller/test_email.clj
@@ -16,6 +16,29 @@
     (f)
     (mount/stop)))
 
+(deftest test-send-email!
+  ;; Just for a bit of coverage in code that doesn't get run in other tests or the dev profile
+  (let [message-atom (atom nil)]
+    (with-redefs [rems.config/env (assoc rems.config/env
+                                         :smtp-host "localhost"
+                                         :smtp-port 25
+                                         :mail-from "rems@rems.rems")
+                  postal.core/send-message (fn [_host message] (reset! message-atom message))
+                  rems.db.users/get-user-attributes (constantly {:mail "user@example.com"})]
+      (send-email! {:to "foo@example.com" :subject "ding" :body "boing"})
+      (is (= {:to "foo@example.com"
+              :subject "ding"
+              :body "boing"
+              :from "rems@rems.rems"}
+             @message-atom))
+      (send-email! {:to-user "user" :subject "ding" :body "boing"})
+      (is (= {:to "user@example.com"
+              :to-user "user"
+              :subject "ding"
+              :body "boing"
+              :from "rems@rems.rems"}
+             @message-atom)))))
+
 (defn sort-emails [emails]
   (sort-by #(or (:to %) (:to-user %)) emails))
 
@@ -104,62 +127,62 @@
       (is (= [[]
               [{:to-user "assistant",
                 :subject "Application submitted (applicant: 2001/3)",
-                :body "Dear assistant,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7"}
+                :body "Dear assistant,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "handler",
                 :subject "Application submitted (applicant: 2001/3)",
-                :body "Dear handler,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7"}]
+                :body "Dear handler,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
               [{:to "somebody@example.com",
                 :subject "Invitation to participate in an application",
-                :body "Hello,\n\nThis email address (somebody@example.com) has been invited to participate in an application.\n\nParticipate: http://example.com/accept-invitation?token=abc"}]
+                :body "Hello,\n\nThis email address (somebody@example.com) has been invited to participate in an application.\n\nParticipate: http://example.com/accept-invitation?token=abc\n\nPlease don't reply to this automatically generated message."}]
               [{:to-user "commenter1",
                 :subject "Review requested (handler: 2001/3)",
-                :body "Dear commenter1,\n\nhandler has requested a review on application 2001/3.\n\nComment: http://example.com/#/application/7"}
+                :body "Dear commenter1,\n\nhandler has requested a review on application 2001/3.\n\nComment: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "commenter2",
                 :subject "Review requested (handler: 2001/3)",
-                :body "Dear commenter2,\n\nhandler has requested a review on application 2001/3.\n\nComment: http://example.com/#/application/7"}]
+                :body "Dear commenter2,\n\nhandler has requested a review on application 2001/3.\n\nComment: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
               []
               [{:to-user "assistant",
                 :subject "Review added (commenter2: 2001/3)",
-                :body "Dear assistant,\n\ncommenter2 has reviewed application 2001/3.\n\nView application: http://example.com/#/application/7"}
+                :body "Dear assistant,\n\ncommenter2 has reviewed application 2001/3.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "handler",
                 :subject "Review added (commenter2: 2001/3)",
-                :body "Dear handler,\n\ncommenter2 has reviewed application 2001/3.\n\nView application: http://example.com/#/application/7"}]
+                :body "Dear handler,\n\ncommenter2 has reviewed application 2001/3.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
               [{:to-user "member",
                 :subject "Added as a member of an application (2001/3)",
-                :body "Dear member,\n\nYou've been added as a member of application 2001/3.\n\nView application: http://example.com/#/application/7"}]
+                :body "Dear member,\n\nYou've been added as a member of application 2001/3.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
               [{:to-user "decider",
                 :subject "Decision requested (assistant: 2001/3)",
-                :body "Dear decider,\n\nassistant has requested your decision on application 2001/3.\n\nView application: http://example.com/#/application/7"}]
+                :body "Dear decider,\n\nassistant has requested your decision on application 2001/3.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
               [{:to-user "assistant",
                 :subject "Decision made (decider: 2001/3)",
-                :body "Dear assistant,\n\ndecider has made a decision on application 2001/3.\n\nView application: http://example.com/#/application/7"}
+                :body "Dear assistant,\n\ndecider has made a decision on application 2001/3.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "handler",
                 :subject "Decision made (decider: 2001/3)",
-                :body "Dear handler,\n\ndecider has made a decision on application 2001/3.\n\nView application: http://example.com/#/application/7"}]
+                :body "Dear handler,\n\ndecider has made a decision on application 2001/3.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
               [{:to-user "applicant",
                 :subject "Application approved (2001/3)",
-                :body "Dear applicant,\n\nYour application 2001/3 has been approved.\n\nView application: http://example.com/#/application/7"}
+                :body "Dear applicant,\n\nYour application 2001/3 has been approved.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "assistant"
                 :subject "Application approved (2001/3)"
-                :body "Dear assistant,\n\nhandler has approved the application 2001/3 from applicant.\n\nView application: http://example.com/#/application/7"}
+                :body "Dear assistant,\n\nhandler has approved the application 2001/3 from applicant.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "member",
                 :subject "Application approved (2001/3)",
-                :body "Dear member,\n\nYour application 2001/3 has been approved.\n\nView application: http://example.com/#/application/7"}
+                :body "Dear member,\n\nYour application 2001/3 has been approved.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "somebody",
                 :subject "Application approved (2001/3)",
-                :body "Dear somebody,\n\nYour application 2001/3 has been approved.\n\nView application: http://example.com/#/application/7"}]
+                :body "Dear somebody,\n\nYour application 2001/3 has been approved.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
               [{:to-user "applicant",
                 :subject "Application closed (2001/3)",
-                :body "Dear applicant,\n\nYour application 2001/3 has been closed.\n\nView application: http://example.com/#/application/7"}
+                :body "Dear applicant,\n\nYour application 2001/3 has been closed.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "handler"
                 :subject "Application closed (2001/3)"
-                :body "Dear handler,\n\nassistant has closed the application 2001/3 from applicant.\n\nView application: http://example.com/#/application/7"}
+                :body "Dear handler,\n\nassistant has closed the application 2001/3 from applicant.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "member",
                 :subject "Application closed (2001/3)",
-                :body "Dear member,\n\nYour application 2001/3 has been closed.\n\nView application: http://example.com/#/application/7"}
+                :body "Dear member,\n\nYour application 2001/3 has been closed.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "somebody",
                 :subject "Application closed (2001/3)",
-                :body "Dear somebody,\n\nYour application 2001/3 has been closed.\n\nView application: http://example.com/#/application/7"}]]
+                :body "Dear somebody,\n\nYour application 2001/3 has been closed.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]]
              (events-to-emails events))))
     (testing "application rejected"
       (let [events (conj base-events
@@ -169,15 +192,15 @@
         (is (= [[]
                 [{:to-user "assistant",
                   :subject "Application submitted (applicant: 2001/3)",
-                  :body "Dear assistant,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7"}
+                  :body "Dear assistant,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                  {:to-user "handler",
                   :subject "Application submitted (applicant: 2001/3)",
-                  :body "Dear handler,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7"}]
+                  :body "Dear handler,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
                 [{:subject "Application rejected (2001/3)",
-                  :body "Dear applicant,\n\nYour application 2001/3 has been rejected.\n\nView application: http://example.com/#/application/7",
+                  :body "Dear applicant,\n\nYour application 2001/3 has been rejected.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message.",
                   :to-user "applicant"}
                  {:subject "Application rejected (2001/3)"
-                  :body "Dear assistant,\n\nhandler has rejected the application 2001/3 from applicant.\n\nView application: http://example.com/#/application/7"
+                  :body "Dear assistant,\n\nhandler has rejected the application 2001/3 from applicant.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."
                   :to-user "assistant"}]]
                (events-to-emails events)))))
     (testing "id field can be overrided"
@@ -185,10 +208,10 @@
         (is (= [[]
                 [{:to-user "assistant"
                   :subject "Application submitted (applicant: 7)"
-                  :body "Dear assistant,\n\napplicant has submitted an application (7): en title 11, en title 21.\n\nView application: http://example.com/#/application/7"}
+                  :body "Dear assistant,\n\napplicant has submitted an application (7): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                  {:to-user "handler"
                   :subject "Application submitted (applicant: 7)"
-                  :body "Dear handler,\n\napplicant has submitted an application (7): en title 11, en title 21.\n\nView application: http://example.com/#/application/7"}]]
+                  :body "Dear handler,\n\napplicant has submitted an application (7): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]]
                (events-to-emails base-events)))))
     (testing "returning application to applicant and re-submitting"
       (let [events (conj base-events
@@ -202,20 +225,20 @@
         (is (= [[]
                 [{:to-user "assistant",
                   :subject "Application submitted (applicant: 2001/3)",
-                  :body "Dear assistant,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7"}
+                  :body "Dear assistant,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                  {:to-user "handler",
                   :subject "Application submitted (applicant: 2001/3)",
-                  :body "Dear handler,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7"}]
+                  :body "Dear handler,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
                 [{:to-user "applicant",
                   :subject "Application returned (2001/3)",
-                  :body "Dear applicant,\n\nYour application 2001/3 has been returned.\n\nView application: http://example.com/#/application/7"}
+                  :body "Dear applicant,\n\nYour application 2001/3 has been returned.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                  {:to-user "assistant",
                   :subject "Application returned (2001/3)",
-                  :body "Dear assistant,\n\nhandler has returned the application 2001/3 from applicant.\n\nView application: http://example.com/#/application/7"}]
+                  :body "Dear assistant,\n\nhandler has returned the application 2001/3 from applicant.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
                 [{:to-user "assistant",
                   :subject "Application submitted (applicant: 2001/3)",
-                  :body "Dear assistant,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7"}
+                  :body "Dear assistant,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                  {:to-user "handler",
                   :subject "Application submitted (applicant: 2001/3)",
-                  :body "Dear handler,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7"}]]
+                  :body "Dear handler,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]]
                (events-to-emails events)))))))