Repository: CSCfi/rems
Pull request #1430 (2019-07-29 14:35:55)

Test helpers for generating test data


Patch:
diff --git a/resources/sql/queries.sql b/resources/sql/queries.sql
index 67ca10c3a..99a4d620b 100644
--- a/resources/sql/queries.sql
+++ b/resources/sql/queries.sql
@@ -503,3 +503,6 @@ WHERE prefix = :prefix
 -- :name add-external-id! :!
 INSERT INTO external_application_id (prefix, suffix)
 VALUES (:prefix, :suffix);
+
+-- :name get-database-time :? :1
+SELECT now();
diff --git a/resources/translations/en.edn b/resources/translations/en.edn
index cf8dba98b..052609514 100644
--- a/resources/translations/en.edn
+++ b/resources/translations/en.edn
@@ -82,6 +82,7 @@
                    :preview "Preview"
                    :resource "Resource"
                    :resources "Resources"
+                   :rounds "Rounds"
                    :save "Save"
                    :start "Start"
                    :title "Title"
@@ -195,11 +196,15 @@
   :create-resource {:licenses-selection "Licenses"
                     :resid "Resource identifier"
                     :resid-placeholder "urn:test:fi:some-uuid"}
-  :create-workflow {:auto-approve-workflow "Auto-approve"
+  :create-workflow {:approvers "Approvers"
+                    :auto-approve-workflow "Auto-approve"
                     :auto-approve-workflow-description "The application is approved immediately without human intervention."
                     :dynamic-workflow "Dynamic workflow"
                     :dynamic-workflow-description "The handler decides on a case-by-case basis who needs to review or approve the application."
                     :handlers "Handlers"
+                    :reviewers "Reviewers"
+                    :round-n "Round %1"
+                    :rounds-workflow "Round-based workflow"
                     :title "Title"}
   :email {:application-approved {:subject "Application approved (%3)"
                                  :message-to-applicant "Dear %1,\n\nYour application %3 has been approved.\n\nView application: %6"
diff --git a/resources/translations/fi.edn b/resources/translations/fi.edn
index 6855ee399..6b7ac22cf 100644
--- a/resources/translations/fi.edn
+++ b/resources/translations/fi.edn
@@ -82,6 +82,7 @@
                    :preview "Esikatselu"
                    :resource "Resurssi"
                    :resources "Resurssit"
+                   :rounds "Kierrokset"
                    :save "Tallenna"
                    :start "Alku"
                    :title "Nimi"
@@ -193,11 +194,15 @@
   :create-resource {:licenses-selection "Lisenssit"
                     :resid "Resurssin tunniste"
                     :resid-placeholder "urn:test:fi:some-uuid"}
-  :create-workflow {:auto-approve-workflow "Automaattinen hyväksyntä"
+  :create-workflow {:approvers "Hyväksyjät"
+                    :auto-approve-workflow "Automaattinen hyväksyntä"
                     :auto-approve-workflow-description "Hakemus hyväksytään välittömästi, keneltäkään erikseen kysymättä."
                     :dynamic-workflow "Dynaaminen työvuo"
                     :dynamic-workflow-description "Hakemuksen käsittelijä päättää tapauskohtaisesti, tarvitseeko hakemus katselmointia tai hyväksyntää."
                     :handlers "Käsittelijät"
+                    :reviewers "Katselmoijat"
+                    :round-n "Kierros %1"
+                    :rounds-workflow "Kierrospohjainen työvuo"
                     :title "Työvuon nimi"}
   :email {:application-approved {:subject "Hakemus hyväksytty (%3)"
                                  :message-to-applicant "Hyvä %1,\n\nHakemuksesi %3 on hyväksytty.\n\nTarkastele hakemusta: %5"
diff --git a/src/clj/rems/db/core.clj b/src/clj/rems/db/core.clj
index 39cf5e210..b0f304fa5 100644
--- a/src/clj/rems/db/core.clj
+++ b/src/clj/rems/db/core.clj
@@ -33,7 +33,7 @@
    (now-active? (time/now) start end))
   ([now start end]
    (and (or (nil? start)
-            (time/after? now start))
+            (not (time/before? now start)))
         (or (nil? end)
             (time/before? now end)))))
 
diff --git a/src/cljs/rems/administration/workflow.cljs b/src/cljs/rems/administration/workflow.cljs
index 8e4d6cee5..b1c611222 100644
--- a/src/cljs/rems/administration/workflow.cljs
+++ b/src/cljs/rems/administration/workflow.cljs
@@ -84,6 +84,28 @@
                 [[inline-info-field (text :t.administration/start) (localize-time (:start license))]
                  [inline-info-field (text :t.administration/end) (localize-time (:end license))]])))
 
+(defn round-view [actors]
+  [:div.form-item
+   [:h3 (text-format :t.create-workflow/round-n (inc (:round (first actors))))]
+   (let [{approvers "approver" reviewers "reviewer"} (group-by :role actors)]
+     [:div
+      (when (seq approvers)
+        [inline-info-field (text :t.create-workflow/approvers) (str/join ", " (map :actoruserid approvers))])
+      (when (seq reviewers)
+        [inline-info-field (text :t.create-workflow/reviewers) (str/join ", " (map :actoruserid reviewers))])])])
+
+(defn rounds-view [actors language]
+  (let [rounds (vals (group-by :round actors))]
+    (when (seq rounds)
+      [collapsible/component
+       {:id "rounds"
+        :title (text :t.administration/rounds)
+        :top-less-button? (> (count rounds) 5)
+        :open? (<= (count rounds 5))
+        :collapse (into [:div]
+                        (for [round rounds]
+                          [round-view round]))}])))
+
 (defn licenses-view [licenses language]
   [collapsible/component
    {:id "licenses"
@@ -106,17 +128,18 @@
               [inline-info-field (text :t.administration/organization) (:organization workflow)]
               [inline-info-field (text :t.administration/title) (:title workflow)]
               [inline-info-field (text :t.administration/type)
-               (if (:workflow workflow)
-                 (text :t.create-workflow/dynamic-workflow)
-                 ;; TODO: Not implemented.
-                 (text :t.create-workflow/auto-approve-workflow))]
-              [inline-info-field (text :t.create-workflow/handlers) (->> (get-in workflow [:workflow :handlers])
-                                                                         (map enrich-user)
-                                                                         (map :display)
-                                                                         (str/join ", "))]
+               (cond (:workflow workflow) (text :t.create-workflow/dynamic-workflow)
+                     (seq (:actors workflow)) (text :t.create-workflow/rounds-workflow)
+                     :else (text :t.create-workflow/auto-approve-workflow))]
+              (when (:workflow workflow)
+                [inline-info-field (text :t.create-workflow/handlers) (->> (get-in workflow [:workflow :handlers])
+                                                                           (map enrich-user)
+                                                                           (map :display)
+                                                                           (str/join ", "))])
               [inline-info-field (text :t.administration/start) (localize-time (:start workflow))]
               [inline-info-field (text :t.administration/end) (localize-time (:end workflow))]
               [inline-info-field (text :t.administration/active) [readonly-checkbox (not (:expired workflow))]]]}]
+   [rounds-view (:actors workflow) language]
    [licenses-view (:licenses workflow) language]
    [:div.col.commands [back-button] [edit-button (:id workflow)]]])
 


Test patch:
diff --git a/src/clj/rems/db/test_data.clj b/src/clj/rems/db/test_data.clj
index 955f80cd6..b0f33fd12 100644
--- a/src/clj/rems/db/test_data.clj
+++ b/src/clj/rems/db/test_data.clj
@@ -1,5 +1,6 @@
 (ns rems.db.test-data
   "Populating the database with nice test data."
+  (:refer-clojure :exclude [run!])
   (:require [clj-time.core :as time]
             [rems.db.applications :as applications]
             [rems.db.catalogue :as catalogue]
@@ -14,6 +15,124 @@
             [ring.util.http-response :refer [bad-request!]])
   (:import [java.util UUID]))
 
+;;; helpers for generating test data
+
+(defn command! [command]
+  (let [command (merge {:time (time/now)}
+                       command)
+        result (applications/command! command)]
+    (assert (nil? result) {:command command :result result})))
+
+(defn- transpose-localizations [m]
+  (->> m
+       (mapcat (fn [[k1 v]]
+                 (map (fn [[k2 v]]
+                        [k1 k2 v])
+                      v)))
+       (reduce (fn [m [k1 k2 v]]
+                 (assoc-in m [k2 k1] v))
+               {})))
+
+(defn create-license! [{:keys [actor]
+                        :license/keys [type title link text]
+                        :as command}]
+  (let [result (licenses/create-license! {:licensetype (name (or type :text))
+                                          :title ""
+                                          :textcontent ""
+                                          :localizations
+                                          (transpose-localizations {:title title
+                                                                    :textcontent (merge link text)})}
+                                         (or actor "owner"))]
+    (assert (:success result) {:command command :result result})
+    (:id result)))
+
+(defn create-form! [{:keys [actor]
+                     :form/keys [organization title fields]
+                     :as command}]
+  (let [result (form/create-form! (or actor "owner")
+                                  {:form/organization (or organization "abc")
+                                   :form/title (or title "")
+                                   :form/fields (or fields [])})]
+    (assert (:success result) {:command command :result result})
+    (:id result)))
+
+(defn create-resource! [{:keys [actor organization resource-ext-id license-ids]
+                         :as command}]
+  (let [result (resource/create-resource! {:resid (or resource-ext-id (str "urn:uuid:" (UUID/randomUUID)))
+                                           :organization (or organization "abc")
+                                           :licenses (or license-ids [])}
+                                          (or actor "owner"))]
+    (assert (:success result) {:command command :result result})
+    (:id result)))
+
+(defn create-dynamic-workflow! [{:keys [actor organization title handlers]
+                                 :as command}]
+  (let [result (workflow/create-workflow! {:user-id (or actor "owner")
+                                           :organization (or organization "abc")
+                                           :title (or title "")
+                                           :type :dynamic
+                                           :handlers (or handlers ["developer"])})]
+    (assert (:success result) {:command command :result result})
+    (:id result)))
+
+(defn create-catalogue-item! [{:keys [title resource-id form-id workflow-id]
+                               :as command}]
+  (let [result (catalogue/create-catalogue-item! {:title ""
+                                                  :resid (or resource-id (create-resource! {}))
+                                                  :form (or form-id (create-form! {}))
+                                                  :wfid (or workflow-id (create-dynamic-workflow! {}))})
+        _ (assert (:success result) {:command command :result result})
+        id (:id result)]
+    (when (map? title)
+      (doseq [[lang text] title]
+        (let [result (catalogue/create-catalogue-item-localization! {:id id
+                                                                     :langcode (name lang)
+                                                                     :title text})]
+          (assert (:success result) {:command [id lang text] :result result}))))
+    id))
+
+(defn create-application! [{:keys [catalogue-item-ids actor]
+                            :as command}]
+  (let [result (applications/create-application! actor
+                                                 (or catalogue-item-ids [(create-catalogue-item! {})]))]
+    (assert (:success result) {:command command :result result})
+    (:application-id result)))
+
+(defn- base-command [{:keys [application-id actor time]}]
+  (assert application-id)
+  (assert actor)
+  {:application-id application-id
+   :actor actor
+   :time (or time (time/now))})
+
+(defn fill-form! [{:keys [application-id actor field-value] :as command}]
+  (let [app (applications/get-application actor application-id)]
+    (command! (assoc (base-command command)
+                     :type :application.command/save-draft
+                     :field-values (->> (:form/fields (:application/form app))
+                                        (filter #(not (:field/optional %)))
+                                        (map (fn [field]
+                                               {:field (:field/id field)
+                                                :value (or field-value "x")})))))))
+
+(defn accept-licenses! [{:keys [application-id actor] :as command}]
+  (let [app (applications/get-application actor application-id)]
+    (command! (assoc (base-command command)
+                     :type :application.command/accept-licenses
+                     :accepted-licenses (map :license/id (:application/licenses app))))))
+
+(defn create-draft! [actor catalogue-item-ids description]
+  (let [app-id (create-application! {:catalogue-item-ids catalogue-item-ids
+                                     :actor actor})]
+    (fill-form! {:application-id app-id
+                 :actor actor
+                 :field-value description})
+    (accept-licenses! {:application-id app-id
+                       :actor actor})
+    app-id))
+
+;;; test data
+
 (def +fake-users+
   {:applicant1 "alice"
    :applicant2 "malice"
@@ -87,10 +206,9 @@
     (create-user! (users :reporter) :reporter)))
 
 (defn- create-archived-form! []
-  (let [id (:id (form/create-form! (+fake-users+ :owner)
-                                   {:form/organization "nbn"
-                                    :form/title "Archived form, should not be seen by applicants"
-                                    :form/fields []}))]
+  (let [id (create-form! {:actor (+fake-users+ :owner)
+                          :form/organization "nbn"
+                          :form/title "Archived form, should not be seen by applicants"})]
     (form/update-form! {:id id :enabled true :archived true})))
 
 (defn- create-expired-license! []
@@ -101,408 +219,359 @@
 (defn- create-basic-form!
   "Creates a bilingual form with all supported field types. Returns id of the form meta."
   [users]
-  (:id (form/create-form!
-        (users :owner)
-        {:form/organization "nbn"
-         :form/title "Basic form"
-         :form/fields [;; all form field types
-                       {:field/title {:en "Project name"
-                                      :fi "Projektin nimi"}
-                        :field/optional false
-                        :field/type :description
-                        :field/placeholder {:en "Project"
-                                            :fi "Projekti"}}
-
-                       {:field/title {:en "Here would be some helpful instructions."
-                                      :fi "Tässä olisi jotain täyttöohjeita."}
-                        :field/optional false
-                        :field/type :label}
-
-                       {:field/title {:en "Purpose of the project"
-                                      :fi "Projektin tarkoitus"}
-                        :field/optional false
-                        :field/type :texta
-                        :field/placeholder {:en "The purpose of the project is to..."
-                                            :fi "Projektin tarkoitus on..."}}
-
-                       {:field/title {:en "Start date of the project"
-                                      :fi "Projektin aloituspäivä"}
-                        :field/optional true
-                        :field/type :date}
-
-                       {:field/title {:en "Project plan"
-                                      :fi "Projektisuunnitelma"}
-                        :field/optional true
-                        :field/type :attachment}
-
-                       {:field/title {:en "Project team size"
-                                      :fi "Projektitiimin koko"}
-                        :field/optional true
-                        :field/type :option
-                        :field/options [{:key "1-5"
-                                         :label {:en "1-5 persons"
-                                                 :fi "1-5 henkilöä"}}
-                                        {:key "6-20"
-                                         :label {:en "6-20 persons"
-                                                 :fi "6-20 henkilöä"}}
-                                        {:key "20+"
-                                         :label {:en "over 20 persons"
-                                                 :fi "yli 20 henkilöä"}}]}
-
-                       {:field/title {:en "Where will the data be used?"
-                                      :fi "Missä dataa tullaan käyttämään?"}
-                        :field/optional true
-                        :field/type :multiselect
-                        :field/options [{:key "EU"
-                                         :label {:en "Inside EU"
-                                                 :fi "EU:n sisällä"}}
-                                        {:key "USA"
-                                         :label {:en "Inside USA"
-                                                 :fi "Yhdysvalloissa"}}
-                                        {:key "Other"
-                                         :label {:en "Elsewhere"
-                                                 :fi "Muualla"}}]}
-
-                       ;; fields which support maxlength
-                       {:field/title {:en "Project acronym"
-                                      :fi "Projektin lyhenne"}
-                        :field/optional true
-                        :field/type :text
-                        :field/max-length 10}
-
-                       {:field/title {:en "Research plan"
-                                      :fi "Tutkimussuunnitelma"}
-                        :field/optional true
-                        :field/type :texta
-                        :field/max-length 100}]})))
+  (create-form!
+   {:actor (users :owner)
+    :form/organization "nbn"
+    :form/title "Basic form"
+    :form/fields [;; all form field types
+                  {:field/title {:en "Project name"
+                                 :fi "Projektin nimi"}
+                   :field/optional false
+                   :field/type :description
+                   :field/placeholder {:en "Project"
+                                       :fi "Projekti"}}
+
+                  {:field/title {:en "Here would be some helpful instructions."
+                                 :fi "Tässä olisi jotain täyttöohjeita."}
+                   :field/optional false
+                   :field/type :label}
+
+                  {:field/title {:en "Purpose of the project"
+                                 :fi "Projektin tarkoitus"}
+                   :field/optional false
+                   :field/type :texta
+                   :field/placeholder {:en "The purpose of the project is to..."
+                                       :fi "Projektin tarkoitus on..."}}
+
+                  {:field/title {:en "Start date of the project"
+                                 :fi "Projektin aloituspäivä"}
+                   :field/optional true
+                   :field/type :date}
+
+                  {:field/title {:en "Project plan"
+                                 :fi "Projektisuunnitelma"}
+                   :field/optional true
+                   :field/type :attachment}
+
+                  {:field/title {:en "Project team size"
+                                 :fi "Projektitiimin koko"}
+                   :field/optional true
+                   :field/type :option
+                   :field/options [{:key "1-5"
+                                    :label {:en "1-5 persons"
+                                            :fi "1-5 henkilöä"}}
+                                   {:key "6-20"
+                                    :label {:en "6-20 persons"
+                                            :fi "6-20 henkilöä"}}
+                                   {:key "20+"
+                                    :label {:en "over 20 persons"
+                                            :fi "yli 20 henkilöä"}}]}
+
+                  {:field/title {:en "Where will the data be used?"
+                                 :fi "Missä dataa tullaan käyttämään?"}
+                   :field/optional true
+                   :field/type :multiselect
+                   :field/options [{:key "EU"
+                                    :label {:en "Inside EU"
+                                            :fi "EU:n sisällä"}}
+                                   {:key "USA"
+                                    :label {:en "Inside USA"
+                                            :fi "Yhdysvalloissa"}}
+                                   {:key "Other"
+                                    :label {:en "Elsewhere"
+                                            :fi "Muualla"}}]}
+
+                  ;; fields which support maxlength
+                  {:field/title {:en "Project acronym"
+                                 :fi "Projektin lyhenne"}
+                   :field/optional true
+                   :field/type :text
+                   :field/max-length 10}
+
+                  {:field/title {:en "Research plan"
+                                 :fi "Tutkimussuunnitelma"}
+                   :field/optional true
+                   :field/type :texta
+                   :field/max-length 100}]}))
 
 (defn create-thl-demo-form!
   [users]
-  (:id (form/create-form!
-        (users :owner)
-        {:form/organization "nbn"
-         :form/title "THL form"
-         :form/fields [{:field/title {:en "Application title"
-                                      :fi "Hakemuksen otsikko"}
-                        :field/optional true
-                        :field/type :description
-                        :field/placeholder {:en "Study of.."
-                                            :fi "Tutkimus aiheesta.."}}
-                       {:field/title {:en "1. Research project full title"
-                                      :fi "1. Tutkimusprojektin täysi nimi"}
-                        :field/optional false
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "2. This is an amendment of a previous approved application"
-                                      :fi "2. Hakemus täydentää edellistä hakemusta"}
-                        :field/optional false
-                        :field/type :option
-                        :field/options [{:key "false"
-                                         :label {:en "no"
-                                                 :fi "ei"}}
-                                        {:key "true"
-                                         :label {:en "yes"
-                                                 :fi "kyllä"}}]}
-                       {:field/title {:en "If yes, what were the previous project permit code/s?"
-                                      :fi "Jos kyllä, mitkä olivat edelliset projektin lupakoodit?"}
-                        :field/optional true
-                        :field/type :text}
-                       {:field/title {:en "3. Study PIs (name, titile, affiliation, email)"
-                                      :fi "3. Henkilöstö (nimi, titteli, yhteys projektiin, sähköposti)"}
-                        :field/optional false
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "4. Contact person for application if different than applicant (name, email)"
-                                      :fi "4. Yhteyshenkilö, jos ei sama kuin hakija (nimi, sähköposti)"}
-                        :field/optional true
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "5. Research project start date"
-                                      :fi "5. Projektin aloituspäivä"}
-                        :field/optional false
-                        :field/type :date}
-                       {:field/title {:en "6. Research project end date"
-                                      :fi "6. Projektin lopetuspäivä"}
-                        :field/optional false
-                        :field/type :date}
-                       {:field/title {:en "7. Describe in detail the aims of the study and analysis plan"
-                                      :fi "7. Kuvaile yksityiskohtaisesti tutkimussuunnitelma"}
-                        :field/optional false
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "8. If this is an amendment, please describe briefly what is new"
-                                      :fi "8. Jos tämä on täydennys edelliseen hakemukseen, kuvaile tiiviisti, mikä on muuttunut."}
-                        :field/optional true
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "9. Public description of the project (in Finnish, when possible), to be published in THL Biobank."
-                                      :fi "9. Kuvaile yksityiskohtaisesti tutkimussuunnitelma"}
-                        :field/placeholder {:en "Meant for sample donors and for anyone interested in the research done using THL Biobank's sample collections. This summary and the name of the Study PI will be published in THL Biobank's web pages."
-                                            :fi "Tarkoitettu aineistojen lahjoittajille ja kaikille, joita kiinnostaa THL:n Biopankkia käyttävät tutkimusprojektit. Tämä kuvaus sekä tutkijan nimi julkaistaan THL:n nettisivuilla, kun sopimus on allekirjoitettu."}
-                        :field/optional false
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "10. Place/plces of research, including place of sample and/or data analysis."
-                                      :fi "10. Tutkimuksen yysinen sijainti, mukaanlukien paikka, missä data-analyysi toteutetaan."}
-                        :field/placeholder {:en "List all research center involved in this study, and each center's role. Specify which centers will analyze which data and/or samples.."
-                                            :fi "Listaa kaikki tutkimuskeskukset, jotka osallistuvat tähän tutkimukseen, ml. niiden roolit tutkimuksessa. Erittele, missä analysoidaan mikäkin näyte."}
-                        :field/optional false
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "11. Description of other research group members and their role in the applied project."
-                                      :fi "11. Kuvaus muista tutkimukseen osallistuvista henkilöistä, ja heidän roolistaan projektissa."}
-                        :field/placeholder {:en "For every group member: name, title, affiliation, contact information. In addition describe earch member's role in the project (e.g. cohor representative, data analyst, etc.)"
-                                            :fi "Anna jokaisesta jäsenestä: nimi, titteli, yhteys projektiin, yhteystiedot. Kuvaile lisäki jokaisen henkilön rooli projektissa."}
-                        :field/optional false
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "12. Specify selection criteria of study participants (if applicable)"
-                                      :fi "12. Erottele tukimuksen osallistujien valintakriteerit (jos käytetty)"}
-                        :field/placeholder {:en "Describe any specific criteria by which study participans will be selected. For example, selection for specific age group, gender, area/locality, disease status etc."
-                                            :fi "Kuvaa tarkat valintakriteerit, joilla tutkimuksen osallistujat valitaan. Esimerkiksi ikäryhmä, sukupuoli, alue, taudin tila jne."}
-                        :field/optional false
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "13. Specify requested phenotype data (information on variables is found at https://kite.fimm.fi)"
-                                      :fi "13. Tarkenna pyydetty fenotyyppidatta (tietoa muuttujista on saatavilla osoitteesta https://kite.fimm.fi)"}
-                        :field/placeholder {:en "Desrcibe in detail the phenotype data needed for the study. Lists of variables are to be attached to the application (below)."
-                                            :fi "Kuvaile yksityiskohtaisesti tutkimukseen tarvittava fenotyyppidata. Lista muuttujista lisätään hakemukseen liitteenä."}
-                        :field/optional false
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "14. Specify requested genomics or other omics data (if applicable)"
-                                      :fi "14. Kuvaile tarvittava genomiikkadata."}
-                        :field/placeholder {:en "Specify in detail the requested data format for different genomics or other omics data types. Information of available omics data is found at THL Biobank web page (www.thl.fi/biobank/researchers)"
-                                            :fi "Kuvaile tarvitsemasi genomiikkadata. Lisätietoa saatavilla osoitteesta www.thl.fi/biobank/researchers"}
-                        :field/optional true
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "16. Are biological samples requested?"
-                                      :fi "16. Pyydetäänkö biologisia näytteitä?"}
-                        :field/optional false
-                        :field/type :option
-                        :field/options [{:key "false"
-                                         :label {:en "no"
-                                                 :fi "ei"}}
-                                        {:key "true"
-                                         :label {:en "yes"
-                                                 :fi "kyllä"}}]}
-                       {:field/title {:en "The type and amount of biological samples requested"
-                                      :fi "Biologisten näytteiden tyypit ja määrät."}
-                        :field/placeholder {:en "Type and amount of samples and any additional specific criteria."
-                                            :fi "Biologisten näytteiden määrät, tyypit, ja mahdolliset muut kriteerit."}
-                        :field/optional true
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "17. What study results will be returned to THL Biobank (if any)?"
-                                      :fi "17. Mitä tutkimustuloksia tullaan palauttamaan THL Biopankkiin?"}
-                        :field/placeholder {:en "Study results such as new laboratory measurements, produced omics data and other analysis data (\"raw data\")"
-                                            :fi "Tutkimustuloksia kuten mittaustuloksia, uutta biologista dataa, tai muita analyysien tuloksia (\"raaka-dataa\")"}
-                        :field/optional false
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "Expected date for return of study results"
-                                      :fi "Odotettu tutkimustuloksien palautuspäivämäärä"}
-                        :field/optional true
-                        :field/type :date}
-                       {:field/title {:en "18. Ethical aspects of the project"
-                                      :fi "18. Tutkimuksen eettiset puolet"}
-                        :field/placeholder {:en "If you have any documents from an ethical board, please provide them as an attachment."
-                                            :fi "Liitä mahdolliset eettisen toimikunnan lausunnot hakemuksen loppuun."}
-                        :field/optional false
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "19. Project keywords (max 5)"
-                                      :fi "19. Projektin avainsanat (maks. 5)"}
-                        :field/placeholder {:en "List a few keywords that are related to this research project (please separate with comma)"
-                                            :fi "Listaa muutama projektiin liittyvä avainsana, pilkuilla erotettuina."}
-                        :field/optional false
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "20. Planned publications (max 3)"
-                                      :fi "20. Suunnitellut julkaisut (maks. 3)"}
-                        :field/placeholder {:en "Planned publication titles / research topics"
-                                            :fi "Suunniteltujen julkaisujen otsikot / tutkimusaiheet"}
-                        :field/optional false
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "21. Funding information"
-                                      :fi "21. Rahoitus"}
-                        :field/placeholder {:en "List all funding sources which will be used for this research project."
-                                            :fi "Listaa kaikki rahoituslähteet joita tullaan käyttämään tähän tutkimusprojektiin"}
-                        :field/optional false
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "22. Invoice address (Service prices: www.thl.fi/biobank/researchers)"
-                                      :fi "22. Laskutusosoite (Palveluhinnasto: www.thl.fi/biobank/researchers)"}
-                        :field/placeholder {:en "Electronic invoice address when possible + invoicing reference"
-                                            :fi "Sähköinen laskutus, kun mahdollista. Lisäksi viitenumero."}
-                        :field/optional false
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "23. Other information"
-                                      :fi "23. Muuta"}
-                        :field/placeholder {:en "Any other relevant information for the application"
-                                            :fi "Muuta hakemukseen liittyvää oleellista tietoa"}
-                        :field/optional true
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "THL Biobank's registered area/s of operation to which the research project complies:"
-                                      :fi "THL Biobankin toimialueet, joihin tutkimusprojekti liittyy:"}
-                        :field/optional false
-                        :field/type :multiselect
-                        :field/options [{:key "population_health"
-                                         :label {:en "Promoting the population's health"
-                                                 :fi "Edistää kansanterveytttä"}}
-                                        {:key "disease_mechanisms"
-                                         :label {:en "Identifying factors involved in disease mechanisms"
-                                                 :fi "Tunnistaa tautien mekanismeja"}}
-                                        {:key "disease_prevention"
-                                         :label {:en "Disease prevention"
-                                                 :fi "Estää tautien leviämistä"}}
-                                        {:key "health_product_development"
-                                         :label {:en "Developing products that promote the welfare and health of the population"
-                                                 :fi "Kehittää tuotteita, jotka edistävät kansanterveyttä."}}
-                                        {:key "treatment_development"
-                                         :label {:en "Developing products and treatments for diseases"
-                                                 :fi "Kehittää tuotteita ja parannuskeinoja tautien varalle"}}
-                                        {:key "other"
-                                         :label {:en "Other"
-                                                 :fi "Muuta"}}]}
-                       {:field/title {:en "Other, specify"
-                                      :fi "Muuta, tarkenna"}
-                        :field/optional true
-                        :field/type :texta
-                        :field/max-length 100}
-                       {:field/title {:en "Data management plan (pdf)"
-                                      :fi "Datanhallintasuunnitelma (pdf)"}
-                        :field/optional true
-                        :field/type :attachment}]})))
+  (create-form!
+   {:actor (users :owner)
+    :form/organization "nbn"
+    :form/title "THL form"
+    :form/fields [{:field/title {:en "Application title"
+                                 :fi "Hakemuksen otsikko"}
+                   :field/optional true
+                   :field/type :description
+                   :field/placeholder {:en "Study of.."
+                                       :fi "Tutkimus aiheesta.."}}
+                  {:field/title {:en "1. Research project full title"
+                                 :fi "1. Tutkimusprojektin täysi nimi"}
+                   :field/optional false
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "2. This is an amendment of a previous approved application"
+                                 :fi "2. Hakemus täydentää edellistä hakemusta"}
+                   :field/optional false
+                   :field/type :option
+                   :field/options [{:key "false"
+                                    :label {:en "no"
+                                            :fi "ei"}}
+                                   {:key "true"
+                                    :label {:en "yes"
+                                            :fi "kyllä"}}]}
+                  {:field/title {:en "If yes, what were the previous project permit code/s?"
+                                 :fi "Jos kyllä, mitkä olivat edelliset projektin lupakoodit?"}
+                   :field/optional true
+                   :field/type :text}
+                  {:field/title {:en "3. Study PIs (name, titile, affiliation, email)"
+                                 :fi "3. Henkilöstö (nimi, titteli, yhteys projektiin, sähköposti)"}
+                   :field/optional false
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "4. Contact person for application if different than applicant (name, email)"
+                                 :fi "4. Yhteyshenkilö, jos ei sama kuin hakija (nimi, sähköposti)"}
+                   :field/optional true
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "5. Research project start date"
+                                 :fi "5. Projektin aloituspäivä"}
+                   :field/optional false
+                   :field/type :date}
+                  {:field/title {:en "6. Research project end date"
+                                 :fi "6. Projektin lopetuspäivä"}
+                   :field/optional false
+                   :field/type :date}
+                  {:field/title {:en "7. Describe in detail the aims of the study and analysis plan"
+                                 :fi "7. Kuvaile yksityiskohtaisesti tutkimussuunnitelma"}
+                   :field/optional false
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "8. If this is an amendment, please describe briefly what is new"
+                                 :fi "8. Jos tämä on täydennys edelliseen hakemukseen, kuvaile tiiviisti, mikä on muuttunut."}
+                   :field/optional true
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "9. Public description of the project (in Finnish, when possible), to be published in THL Biobank."
+                                 :fi "9. Kuvaile yksityiskohtaisesti tutkimussuunnitelma"}
+                   :field/placeholder {:en "Meant for sample donors and for anyone interested in the research done using THL Biobank's sample collections. This summary and the name of the Study PI will be published in THL Biobank's web pages."
+                                       :fi "Tarkoitettu aineistojen lahjoittajille ja kaikille, joita kiinnostaa THL:n Biopankkia käyttävät tutkimusprojektit. Tämä kuvaus sekä tutkijan nimi julkaistaan THL:n nettisivuilla, kun sopimus on allekirjoitettu."}
+                   :field/optional false
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "10. Place/plces of research, including place of sample and/or data analysis."
+                                 :fi "10. Tutkimuksen yysinen sijainti, mukaanlukien paikka, missä data-analyysi toteutetaan."}
+                   :field/placeholder {:en "List all research center involved in this study, and each center's role. Specify which centers will analyze which data and/or samples.."
+                                       :fi "Listaa kaikki tutkimuskeskukset, jotka osallistuvat tähän tutkimukseen, ml. niiden roolit tutkimuksessa. Erittele, missä analysoidaan mikäkin näyte."}
+                   :field/optional false
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "11. Description of other research group members and their role in the applied project."
+                                 :fi "11. Kuvaus muista tutkimukseen osallistuvista henkilöistä, ja heidän roolistaan projektissa."}
+                   :field/placeholder {:en "For every group member: name, title, affiliation, contact information. In addition describe earch member's role in the project (e.g. cohor representative, data analyst, etc.)"
+                                       :fi "Anna jokaisesta jäsenestä: nimi, titteli, yhteys projektiin, yhteystiedot. Kuvaile lisäki jokaisen henkilön rooli projektissa."}
+                   :field/optional false
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "12. Specify selection criteria of study participants (if applicable)"
+                                 :fi "12. Erottele tukimuksen osallistujien valintakriteerit (jos käytetty)"}
+                   :field/placeholder {:en "Describe any specific criteria by which study participans will be selected. For example, selection for specific age group, gender, area/locality, disease status etc."
+                                       :fi "Kuvaa tarkat valintakriteerit, joilla tutkimuksen osallistujat valitaan. Esimerkiksi ikäryhmä, sukupuoli, alue, taudin tila jne."}
+                   :field/optional false
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "13. Specify requested phenotype data (information on variables is found at https://kite.fimm.fi)"
+                                 :fi "13. Tarkenna pyydetty fenotyyppidatta (tietoa muuttujista on saatavilla osoitteesta https://kite.fimm.fi)"}
+                   :field/placeholder {:en "Desrcibe in detail the phenotype data needed for the study. Lists of variables are to be attached to the application (below)."
+                                       :fi "Kuvaile yksityiskohtaisesti tutkimukseen tarvittava fenotyyppidata. Lista muuttujista lisätään hakemukseen liitteenä."}
+                   :field/optional false
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "14. Specify requested genomics or other omics data (if applicable)"
+                                 :fi "14. Kuvaile tarvittava genomiikkadata."}
+                   :field/placeholder {:en "Specify in detail the requested data format for different genomics or other omics data types. Information of available omics data is found at THL Biobank web page (www.thl.fi/biobank/researchers)"
+                                       :fi "Kuvaile tarvitsemasi genomiikkadata. Lisätietoa saatavilla osoitteesta www.thl.fi/biobank/researchers"}
+                   :field/optional true
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "16. Are biological samples requested?"
+                                 :fi "16. Pyydetäänkö biologisia näytteitä?"}
+                   :field/optional false
+                   :field/type :option
+                   :field/options [{:key "false"
+                                    :label {:en "no"
+                                            :fi "ei"}}
+                                   {:key "true"
+                                    :label {:en "yes"
+                                            :fi "kyllä"}}]}
+                  {:field/title {:en "The type and amount of biological samples requested"
+                                 :fi "Biologisten näytteiden tyypit ja määrät."}
+                   :field/placeholder {:en "Type and amount of samples and any additional specific criteria."
+                                       :fi "Biologisten näytteiden määrät, tyypit, ja mahdolliset muut kriteerit."}
+                   :field/optional true
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "17. What study results will be returned to THL Biobank (if any)?"
+                                 :fi "17. Mitä tutkimustuloksia tullaan palauttamaan THL Biopankkiin?"}
+                   :field/placeholder {:en "Study results such as new laboratory measurements, produced omics data and other analysis data (\"raw data\")"
+                                       :fi "Tutkimustuloksia kuten mittaustuloksia, uutta biologista dataa, tai muita analyysien tuloksia (\"raaka-dataa\")"}
+                   :field/optional false
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "Expected date for return of study results"
+                                 :fi "Odotettu tutkimustuloksien palautuspäivämäärä"}
+                   :field/optional true
+                   :field/type :date}
+                  {:field/title {:en "18. Ethical aspects of the project"
+                                 :fi "18. Tutkimuksen eettiset puolet"}
+                   :field/placeholder {:en "If you have any documents from an ethical board, please provide them as an attachment."
+                                       :fi "Liitä mahdolliset eettisen toimikunnan lausunnot hakemuksen loppuun."}
+                   :field/optional false
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "19. Project keywords (max 5)"
+                                 :fi "19. Projektin avainsanat (maks. 5)"}
+                   :field/placeholder {:en "List a few keywords that are related to this research project (please separate with comma)"
+                                       :fi "Listaa muutama projektiin liittyvä avainsana, pilkuilla erotettuina."}
+                   :field/optional false
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "20. Planned publications (max 3)"
+                                 :fi "20. Suunnitellut julkaisut (maks. 3)"}
+                   :field/placeholder {:en "Planned publication titles / research topics"
+                                       :fi "Suunniteltujen julkaisujen otsikot / tutkimusaiheet"}
+                   :field/optional false
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "21. Funding information"
+                                 :fi "21. Rahoitus"}
+                   :field/placeholder {:en "List all funding sources which will be used for this research project."
+                                       :fi "Listaa kaikki rahoituslähteet joita tullaan käyttämään tähän tutkimusprojektiin"}
+                   :field/optional false
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "22. Invoice address (Service prices: www.thl.fi/biobank/researchers)"
+                                 :fi "22. Laskutusosoite (Palveluhinnasto: www.thl.fi/biobank/researchers)"}
+                   :field/placeholder {:en "Electronic invoice address when possible + invoicing reference"
+                                       :fi "Sähköinen laskutus, kun mahdollista. Lisäksi viitenumero."}
+                   :field/optional false
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "23. Other information"
+                                 :fi "23. Muuta"}
+                   :field/placeholder {:en "Any other relevant information for the application"
+                                       :fi "Muuta hakemukseen liittyvää oleellista tietoa"}
+                   :field/optional true
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "THL Biobank's registered area/s of operation to which the research project complies:"
+                                 :fi "THL Biobankin toimialueet, joihin tutkimusprojekti liittyy:"}
+                   :field/optional false
+                   :field/type :multiselect
+                   :field/options [{:key "population_health"
+                                    :label {:en "Promoting the population's health"
+                                            :fi "Edistää kansanterveytttä"}}
+                                   {:key "disease_mechanisms"
+                                    :label {:en "Identifying factors involved in disease mechanisms"
+                                            :fi "Tunnistaa tautien mekanismeja"}}
+                                   {:key "disease_prevention"
+                                    :label {:en "Disease prevention"
+                                            :fi "Estää tautien leviämistä"}}
+                                   {:key "health_product_development"
+                                    :label {:en "Developing products that promote the welfare and health of the population"
+                                            :fi "Kehittää tuotteita, jotka edistävät kansanterveyttä."}}
+                                   {:key "treatment_development"
+                                    :label {:en "Developing products and treatments for diseases"
+                                            :fi "Kehittää tuotteita ja parannuskeinoja tautien varalle"}}
+                                   {:key "other"
+                                    :label {:en "Other"
+                                            :fi "Muuta"}}]}
+                  {:field/title {:en "Other, specify"
+                                 :fi "Muuta, tarkenna"}
+                   :field/optional true
+                   :field/type :texta
+                   :field/max-length 100}
+                  {:field/title {:en "Data management plan (pdf)"
+                                 :fi "Datanhallintasuunnitelma (pdf)"}
+                   :field/optional true
+                   :field/type :attachment}]}))
 
 (defn- create-workflows! [users]
   (let [approver1 (users :approver1)
         owner (users :owner)
-        dynamic (:id (workflow/create-workflow! {:user-id owner
-                                                 :organization "nbn"
-                                                 :title "dynamic workflow"
-                                                 :type :dynamic
-                                                 :handlers [approver1]}))]
+        dynamic (create-dynamic-workflow! {:actor owner
+                                           :organization "nbn"
+                                           :title "dynamic workflow"
+                                           :handlers [approver1]})]
 
     ;; attach both kinds of licenses to all workflows
-    (let [link (:id (db/create-license!
-                     {:modifieruserid owner :owneruserid owner :title "non-localized link license"
-                      :type "link" :textcontent "http://invalid"}))
-          text (:id (db/create-license!
-                     {:modifieruserid owner :owneruserid owner :title "non-localized text license"
-                      :type "text" :textcontent "non-localized content"}))]
-      (db/create-license-localization!
-       {:licid link :langcode "en" :title "CC Attribution 4.0"
-        :textcontent "https://creativecommons.org/licenses/by/4.0/legalcode"})
-      (db/create-license-localization!
-       {:licid link :langcode "fi" :title "CC Nimeä 4.0"
-        :textcontent "https://creativecommons.org/licenses/by/4.0/legalcode.fi"})
-      (db/create-license-localization!
-       {:licid text :langcode "fi" :title "Yleiset käyttöehdot"
-        :textcontent (apply str (repeat 10 "Suomenkielinen lisenssiteksti. "))})
-      (db/create-license-localization!
-       {:licid text :langcode "en" :title "General Terms of Use"
-        :textcontent (apply str (repeat 10 "License text in English. "))})
-
-      (doseq [wfid [dynamic]]
-        (db/create-workflow-license! {:wfid wfid :licid link})
-        (db/create-workflow-license! {:wfid wfid :licid text})
-        (db/set-workflow-license-validity! {:licid link :start (time/minus (time/now) (time/years 1)) :end nil})
-        (db/set-workflow-license-validity! {:licid text :start (time/minus (time/now) (time/years 1)) :end nil})))
+    (let [link (create-license! {:actor owner
+                                 :license/type :link
+                                 :license/title {:en "CC Attribution 4.0"
+                                                 :fi "CC Nimeä 4.0"}
+                                 :license/link {:en "https://creativecommons.org/licenses/by/4.0/legalcode"
+                                                :fi "https://creativecommons.org/licenses/by/4.0/legalcode.fi"}})
+          text (create-license! {:actor owner
+                                 :license/type :text
+                                 :license/title {:en "General Terms of Use"
+                                                 :fi "Yleiset käyttöehdot"}
+                                 :license/text {:en (apply str (repeat 10 "License text in English. "))
+                                                :fi (apply str (repeat 10 "Suomenkielinen lisenssiteksti. "))}})]
+      (doseq [licid [link text]]
+        (doseq [wfid [dynamic]]
+          (db/create-workflow-license! {:wfid wfid :licid licid})
+          (db/set-workflow-license-validity! {:licid licid :start (time/minus (time/now) (time/years 1)) :end nil}))))
 
     {:dynamic dynamic}))
 
 (defn- create-resource-license! [resid text owner]
-  (let [licid (:id (db/create-license!
-                    {:modifieruserid owner :owneruserid owner :title "resource license"
-                     :type "link" :textcontent "http://invalid"}))]
-    (db/create-license-localization!
-     {:licid licid :langcode "en" :title (str text " (en)")
-      :textcontent "https://www.apache.org/licenses/LICENSE-2.0"})
-    (db/create-license-localization!
-     {:licid licid :langcode "fi" :title (str text " (fi)")
-      :textcontent "https://www.apache.org/licenses/LICENSE-2.0"})
+  (let [licid (create-license! {:actor owner
+                                :license/type :link
+                                :license/title {:en (str text " (en)")
+                                                :fi (str text " (fi)")}
+                                :license/link {:en "https://www.apache.org/licenses/LICENSE-2.0"
+                                               :fi "https://www.apache.org/licenses/LICENSE-2.0"}})]
     (db/create-resource-license! {:resid resid :licid licid})
     (db/set-resource-license-validity! {:licid licid :start (time/minus (time/now) (time/years 1)) :end nil})
     licid))
 
-(defn- create-catalogue-item! [resource workflow form localizations]
-  (let [id (:id (db/create-catalogue-item!
-                 {:title "non-localized title" :resid resource :wfid workflow :form form}))]
-    (doseq [[lang title] localizations]
-      (catalogue/create-catalogue-item-localization! {:id id :langcode lang :title title}))
-    id))
-
-(defn- run-and-check-dynamic-command! [cmd]
-  (let [result (applications/command! cmd)]
-    (assert (nil? result) {:actual result})
-    result))
-
-(defn- create-draft! [applicant cat-items description]
-  (let [app-id (:application-id (applications/create-application! applicant cat-items))
-        app (applications/get-application applicant app-id)
-        command (fn [cmd]
-                  (merge {:application-id app-id
-                          :actor applicant
-                          :time (time/now)}
-                         cmd))]
-    (run-and-check-dynamic-command! (command {:type :application.command/save-draft
-                                              :field-values (->> (:form/fields (:application/form app))
-                                                                 (filter #(not (:field/optional %)))
-                                                                 (map (fn [field]
-                                                                        {:field (:field/id field)
-                                                                         :value description})))}))
-    (run-and-check-dynamic-command! (command {:type :application.command/accept-licenses
-                                              :accepted-licenses (map :license/id (:application/licenses app))}))
-    app-id))
-
 (defn- create-disabled-applications! [catid applicant approver]
   (create-draft! applicant [catid] "draft with disabled item")
 
   (let [appid1 (create-draft! applicant [catid] "approved application with disabled item")]
-    (run-and-check-dynamic-command! {:application-id appid1
-                                     :actor applicant
-                                     :time (time/now)
-                                     :type :application.command/submit}))
+    (command! {:type :application.command/submit
+               :application-id appid1
+               :actor applicant}))
 
   (let [appid2 (create-draft! applicant [catid] "submitted application with disabled item")]
-    (run-and-check-dynamic-command! {:application-id appid2
-                                     :actor applicant
-                                     :time (time/now)
-                                     :type :application.command/submit})
-    (run-and-check-dynamic-command! {:application-id appid2
-                                     :actor approver
-                                     :time (time/now)
-                                     :type :application.command/approve
-                                     :comment "Looking good"})))
+    (command! {:type :application.command/submit
+               :application-id appid2
+               :actor applicant})
+    (command! {:type :application.command/approve
+               :application-id appid2
+               :actor approver
+               :comment "Looking good"})))
 
 (defn- create-member-applications! [catid applicant approver members]
   (let [appid1 (create-draft! applicant [catid] "draft with invited members")]
-    (run-and-check-dynamic-command! {:application-id appid1
-                                     :actor applicant
-                                     :time (time/now)
-                                     :type :application.command/invite-member
-                                     :member {:name "John Smith" :email "john.smith@example.org"}}))
+    (command! {:type :application.command/invite-member
+               :application-id appid1
+               :actor applicant
+               :member {:name "John Smith" :email "john.smith@example.org"}}))
   (let [appid2 (create-draft! applicant [catid] "submitted with members")]
-    (run-and-check-dynamic-command! {:application-id appid2
-                                     :actor applicant
-                                     :time (time/now)
-                                     :type :application.command/invite-member
-                                     :member {:name "John Smith" :email "john.smith@example.org"}})
-    (run-and-check-dynamic-command! {:application-id appid2
-                                     :actor applicant
-                                     :time (time/now)
-                                     :type :application.command/submit})
+    (command! {:type :application.command/invite-member
+               :application-id appid2
+               :actor applicant
+               :member {:name "John Smith" :email "john.smith@example.org"}})
+    (command! {:type :application.command/submit
+               :application-id appid2
+               :actor applicant})
     (doseq [member members]
-      (run-and-check-dynamic-command! {:application-id appid2
-                                       :actor approver
-                                       :time (time/now)
-                                       :type :application.command/add-member
-                                       :member member}))))
+      (command! {:type :application.command/add-member
+                 :application-id appid2
+                 :actor approver
+                 :member member}))))
 
 (defn- create-applications! [catid users]
   (let [applicant (users :applicant1)
@@ -512,40 +581,88 @@
     (create-draft! applicant [catid] "draft application")
 
     (let [app-id (create-draft! applicant [catid] "applied")]
-      (run-and-check-dynamic-command! {:application-id app-id :actor applicant :time (time/now) :type :application.command/submit}))
+      (command! {:type :application.command/submit
+                 :application-id app-id
+                 :actor applicant}))
 
     (let [app-id (create-draft! applicant [catid] "approved with comment")]
-      (run-and-check-dynamic-command! {:application-id app-id :actor applicant :time (time/now) :type :application.command/submit})
-      (run-and-check-dynamic-command! {:application-id app-id :actor approver :time (time/now) :type :application.command/request-comment :commenters [reviewer] :comment "please have a look"})
-      (run-and-check-dynamic-command! {:application-id app-id :actor reviewer :time (time/now) :type :application.command/comment :comment "looking good"})
-      (run-and-check-dynamic-command! {:application-id app-id :actor approver :time (time/now) :type :application.command/approve :comment "Thank you! Approved!"}))
+      (command! {:type :application.command/submit
+                 :application-id app-id
+                 :actor applicant})
+      (command! {:type :application.command/request-comment
+                 :application-id app-id
+                 :actor approver
+                 :commenters [reviewer]
+                 :comment "please have a look"})
+      (command! {:type :application.command/comment
+                 :application-id app-id
+                 :actor reviewer
+                 :comment "looking good"})
+      (command! {:type :application.command/approve
+                 :application-id app-id
+                 :actor approver
+                 :comment "Thank you! Approved!"}))
 
     (let [app-id (create-draft! applicant [catid] "rejected")]
-      (run-and-check-dynamic-command! {:application-id app-id :actor applicant :time (time/now) :type :application.command/submit})
-      (run-and-check-dynamic-command! {:application-id app-id :actor approver :time (time/now) :type :application.command/reject :comment "Never going to happen"}))
+      (command! {:type :application.command/submit
+                 :application-id app-id
+                 :actor applicant})
+      (command! {:type :application.command/reject
+                 :application-id app-id
+                 :actor approver
+                 :comment "Never going to happen"}))
 
     (let [app-id (create-draft! applicant [catid] "returned")]
-      (run-and-check-dynamic-command! {:application-id app-id :actor applicant :time (time/now) :type :application.command/submit})
-      (run-and-check-dynamic-command! {:application-id app-id :actor approver :time (time/now) :type :application.command/return :comment "Need more details"}))
+      (command! {:type :application.command/submit
+                 :application-id app-id
+                 :actor applicant})
+      (command! {:type :application.command/return
+                 :application-id app-id
+                 :actor approver
+                 :comment "Need more details"}))
 
     (let [app-id (create-draft! applicant [catid] "approved & closed")]
-      (run-and-check-dynamic-command! {:application-id app-id :actor applicant :time (time/now) :type :application.command/submit})
-      (run-and-check-dynamic-command! {:application-id app-id :actor approver :time (time/now) :type :application.command/request-comment :commenters [reviewer] :comment "please have a look"})
-      (run-and-check-dynamic-command! {:application-id app-id :actor reviewer :time (time/now) :type :application.command/comment :comment "looking good"})
-      (run-and-check-dynamic-command! {:application-id app-id :actor approver :time (time/now) :type :application.command/approve :comment "Thank you! Approved!"})
+      (command! {:type :application.command/submit
+                 :application-id app-id
+                 :actor applicant})
+      (command! {:type :application.command/request-comment
+                 :application-id app-id
+                 :actor approver
+                 :commenters [reviewer]
+                 :comment "please have a look"})
+      (command! {:type :application.command/comment
+                 :application-id app-id
+                 :actor reviewer
+                 :comment "looking good"})
+      (command! {:type :application.command/approve
+                 :application-id app-id
+                 :actor approver
+                 :comment "Thank you! Approved!"})
       (entitlements-poller/run)
-      (run-and-check-dynamic-command! {:application-id app-id :actor approver :time (time/now) :type :application.command/close :comment "Research project complete, closing."}))
+      (command! {:type :application.command/close
+                 :application-id app-id
+                 :actor approver
+                 :comment "Research project complete, closing."}))
 
     (let [app-id (create-draft! applicant [catid] "waiting for comment")]
-      (run-and-check-dynamic-command! {:application-id app-id :actor applicant :time (time/now) :type :application.command/submit})
-      (run-and-check-dynamic-command! {:application-id app-id :actor approver :time (time/now) :type :application.command/request-comment :commenters [reviewer] :comment ""}))
+      (command! {:type :application.command/submit
+                 :application-id app-id
+                 :actor applicant})
+      (command! {:type :application.command/request-comment
+                 :application-id app-id
+                 :actor approver
+                 :commenters [reviewer]
+                 :comment ""}))
 
     (let [app-id (create-draft! applicant [catid] "waiting for decision")]
-      (run-and-check-dynamic-command! {:application-id app-id :actor applicant :time (time/now) :type :application.command/submit})
-      (run-and-check-dynamic-command! {:application-id app-id :actor approver :time (time/now) :type :application.command/request-decision :deciders [reviewer] :comment ""}))))
-
-(defn- assert-no-error [error]
-  (assert (nil? error) {:error error}))
+      (command! {:type :application.command/submit
+                 :application-id app-id
+                 :actor applicant})
+      (command! {:type :application.command/request-decision
+                 :application-id app-id
+                 :actor approver
+                 :deciders [reviewer]
+                 :comment ""}))))
 
 (defn create-performance-test-data! []
   (let [resource-count 1000
@@ -554,51 +671,42 @@
         handlers [(+fake-users+ :approver1)
                   (+fake-users+ :approver2)]
         owner (+fake-users+ :owner)
-        workflow-id (:id (workflow/create-workflow! {:user-id owner
-                                                     :organization "perf"
-                                                     :title "Performance tests"
-                                                     :type :dynamic
-                                                     :handlers handlers}))
-        form-id (:id (form/create-form!
-                      owner
-                      {:form/organization "perf"
-                       :form/title "Performance tests"
-                       :form/fields [{:field/title {:en "Project name"
-                                                    :fi "Projektin nimi"}
-                                      :field/optional false
-                                      :field/type :description
-                                      :field/placeholder {:en "Project"
-                                                          :fi "Projekti"}}
-
-                                     {:field/title {:en "Project description"
-                                                    :fi "Projektin kuvaus"}
-                                      :field/optional false
-                                      :field/type :texta
-                                      :field/placeholder {:en "The purpose of the project is to..."
-                                                          :fi "Projektin tarkoitus on..."}}]}))
+        workflow-id (create-dynamic-workflow! {:actor owner
+                                               :organization "perf"
+                                               :title "Performance tests"
+                                               :handlers handlers})
+        form-id (create-form!
+                 {:actor owner
+                  :form/organization "perf"
+                  :form/title "Performance tests"
+                  :form/fields [{:field/title {:en "Project name"
+                                               :fi "Projektin nimi"}
+                                 :field/optional false
+                                 :field/type :description
+                                 :field/placeholder {:en "Project"
+                                                     :fi "Projekti"}}
+
+                                {:field/title {:en "Project description"
+                                               :fi "Projektin kuvaus"}
+                                 :field/optional false
+                                 :field/type :texta
+                                 :field/placeholder {:en "The purpose of the project is to..."
+                                                     :fi "Projektin tarkoitus on..."}}]})
         form (form/get-form-template form-id)
-        license-id (:id (licenses/create-license!
-                         {:licensetype "text"
-                          :title "Performance License"
-                          :textcontent "Be fast."
-                          :localizations {:en {:title "Performance license"
-                                               :textcontent "Be fast."}
-                                          :fi {:title "Suorituskykylisenssi"
-                                               :textcontent "Ole nopea."}}}
-                         owner))
-        cat-item-ids (vec (for [_ (range resource-count)]
-                            (let [uuid (UUID/randomUUID)
-                                  resource (resource/create-resource!
-                                            {:resid (str "urn:uuid:" uuid)
-                                             :organization "perf"
-                                             :licenses [license-id]}
-                                            owner)
-                                  _ (assert (:success resource))
-                                  cat-item (catalogue/create-catalogue-item! {:title (str "Performance test resource " uuid)
-                                                                              :form form-id
-                                                                              :resid (:id resource)
-                                                                              :wfid workflow-id})]
-                              (:id cat-item))))
+        license-id (create-license! {:actor owner
+                                     :license/type :text
+                                     :license/title {:en "Performance License"
+                                                     :fi "Suorituskykylisenssi"}
+                                     :license/text {:en "Be fast."
+                                                    :fi "Ole nopea."}})
+        cat-item-ids (vec (for [index (range resource-count)]
+                            (let [resource-id (create-resource! {:organization "perf"
+                                                                 :license-ids [license-id]})]
+                              (create-catalogue-item! {:title {:en (str "Performance test resource " (inc index))
+                                                               :fi (str "Suorituskykytestiresurssi " (inc index))}
+                                                       :resource-id resource-id
+                                                       :form-id form-id
+                                                       :workflow-id workflow-id}))))
         user-ids (vec (for [n (range 1 (inc user-count))]
                         (let [user-id (str "perftester" n)]
                           (users/add-user! user-id {:eppn user-id
@@ -609,86 +717,117 @@
       (let [cat-item-id (rand-nth cat-item-ids)
             user-id (rand-nth user-ids)
             handler (rand-nth handlers)
-            app-id (:application-id (applications/create-application! user-id [cat-item-id]))]
-        (assert-no-error
-         (applications/command!
-          {:type :application.command/save-draft
-           :actor user-id
-           :time (time/now)
-           :application-id app-id
-           :field-values [{:field (:field/id (first (:form/fields form)))
-                           :value (str "Performance test application " (UUID/randomUUID))}
-                          {:field (:field/id (second (:form/fields form)))
-                           ;; 5000 characters (10 KB) of lorem ipsum generated with www.lipsum.com
-                           ;; to increase the memory requirements of an application
-                           :value (str "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut non diam vel erat dapibus facilisis vel vitae nunc. Curabitur at fermentum lorem. Cras et bibendum ante. Etiam convallis erat justo. Phasellus cursus molestie vehicula. Etiam molestie tellus vitae consectetur dignissim. Pellentesque euismod hendrerit mi sed tincidunt. Integer quis lorem ut ipsum egestas hendrerit. Aenean est nunc, mattis euismod erat in, sodales rutrum mauris. Praesent sit amet risus quis felis congue ultricies. Nulla facilisi. Sed mollis justo id tristique volutpat.\n\nPhasellus augue mi, facilisis ac velit et, pharetra tristique nunc. Pellentesque eget arcu quam. Curabitur dictum nulla varius hendrerit varius. Proin vulputate, ex lacinia commodo varius, ipsum velit viverra est, eget molestie dui nisi non eros. Nulla lobortis odio a magna mollis placerat. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer consectetur libero ut gravida ullamcorper. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec aliquam feugiat mollis. Quisque massa lacus, efficitur vel justo vel, elementum mollis magna. Maecenas at sem sem. Praesent sed ex mattis, egestas dui non, volutpat lorem. Nulla tempor, nisi rutrum accumsan varius, tellus elit faucibus nulla, vel mattis lacus justo at ante. Sed ut mollis ex, sed tincidunt ex.\n\nMauris laoreet nibh eget erat tincidunt pharetra. Aenean sagittis maximus consectetur. Curabitur interdum nibh sed tincidunt finibus. Sed blandit nec lorem at iaculis. Morbi non augue nec tortor hendrerit mollis ut non arcu. Suspendisse maximus nec ligula a efficitur. Etiam ultrices rhoncus leo quis dapibus. Integer vel rhoncus est. Integer blandit varius auctor. Vestibulum suscipit suscipit risus, sit amet venenatis lacus iaculis a. Duis eu turpis sit amet nibh sagittis convallis at quis ligula. Sed eget justo quis risus iaculis lacinia vitae a justo. In hac habitasse platea dictumst. Maecenas euismod et lorem vel viverra.\n\nDonec bibendum nec ipsum in volutpat. Vivamus in elit venenatis, venenatis libero ac, ultrices dolor. Morbi quis odio in neque consequat rutrum. Suspendisse quis sapien id sapien fermentum dignissim. Nam eu est vel risus volutpat mollis sed quis eros. Proin leo nulla, dictum id hendrerit vitae, scelerisque in elit. Proin consectetur sodales arcu ac tristique. Suspendisse ut elementum ligula, at rhoncus mauris. Aliquam lacinia at diam eget mattis. Phasellus quam leo, hendrerit sit amet mi eget, porttitor aliquet velit. Proin turpis ante, consequat in enim nec, tempus consequat magna. Vestibulum fringilla ac turpis nec malesuada. Proin id lectus iaculis, suscipit erat at, volutpat turpis. In quis faucibus elit, ut maximus nibh. Sed egestas egestas dolor.\n\nNulla varius orci quam, id auctor enim ultrices nec. Morbi et tellus ac metus sodales convallis sed vehicula neque. Pellentesque rhoncus mattis massa a bibendum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce tincidunt nulla non aliquet facilisis. Praesent nisl nisi, finibus id odio sed, consectetur feugiat mauris. Suspendisse sed lacinia ligula. Duis vitae nisl leo. Donec erat arcu, feugiat sit amet sagittis ac, scelerisque nec est. Pellentesque finibus mauris nulla, in maximus sapien pharetra vitae. Sed leo elit, consequat eu aliquam vitae, feugiat ut eros. Pellentesque dictum feugiat odio sed commodo. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin neque quam, varius vel libero sit amet, rhoncus sollicitudin ex. In a dui non neque malesuada pellentesque.\n\nProin tincidunt nisl non commodo faucibus. Sed porttitor arcu neque, vitae bibendum sapien placerat nec. Integer eget tristique orci. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec eu molestie eros. Nunc iaculis rhoncus enim, vel mattis felis fringilla condimentum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean ac augue nulla. Phasellus vitae nulla lobortis, mattis magna ac, gravida ipsum. Aenean ornare non nunc non luctus. Aenean lacinia lectus nec velit finibus egestas vel ut ipsum. Cras hendrerit rhoncus erat, vel maximus nunc.\n\nPraesent quis imperdiet quam. Praesent ligula tellus, consectetur sed lacus eu, malesuada condimentum tellus. Donec et diam hendrerit, dictum diam quis, aliquet purus. Suspendisse pulvinar neque at efficitur iaculis. Nulla erat orci, euismod id velit sed, dictum hendrerit arcu. Nulla aliquam molestie aliquam. Duis et semper nisi, eget commodo arcu. Praesent rhoncus, nulla id sodales eleifend, ante ipsum pellentesque augue, id iaculis sem est vitae est. Phasellus cursus diam a lorem vestibulum sodales. Nullam lacinia tortor vel tellus commodo, sit amet sodales quam malesuada.\n\nNulla tempor lectus vel arcu feugiat, vel dapibus ex dapibus. Maecenas purus justo, aliquet et sem sit amet, tincidunt venenatis dui. Nulla eget purus id sapien elementum rutrum eu vel libero. Cras non accumsan justo posuere.\n\n"
-                                       ;; prevent string interning, just to be sure
-                                       (UUID/randomUUID))}]}))
-        (assert-no-error
-         (applications/command!
-          {:type :application.command/submit
-           :actor user-id
-           :time (time/now)
-           :application-id app-id}))
-        (assert-no-error
-         (applications/command!
-          {:type :application.command/approve
-           :actor handler
-           :time (time/now)
-           :application-id app-id
-           :comment "Looks fine."}))))))
+            app-id (create-application! {:catalogue-item-ids [cat-item-id]
+                                         :actor user-id})]
+        (command! {:type :application.command/save-draft
+                   :application-id app-id
+                   :actor user-id
+                   :field-values [{:field (:field/id (first (:form/fields form)))
+                                   :value (str "Performance test application " (UUID/randomUUID))}
+                                  {:field (:field/id (second (:form/fields form)))
+                                   ;; 5000 characters (10 KB) of lorem ipsum generated with www.lipsum.com
+                                   ;; to increase the memory requirements of an application
+                                   :value (str "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut non diam vel erat dapibus facilisis vel vitae nunc. Curabitur at fermentum lorem. Cras et bibendum ante. Etiam convallis erat justo. Phasellus cursus molestie vehicula. Etiam molestie tellus vitae consectetur dignissim. Pellentesque euismod hendrerit mi sed tincidunt. Integer quis lorem ut ipsum egestas hendrerit. Aenean est nunc, mattis euismod erat in, sodales rutrum mauris. Praesent sit amet risus quis felis congue ultricies. Nulla facilisi. Sed mollis justo id tristique volutpat.\n\nPhasellus augue mi, facilisis ac velit et, pharetra tristique nunc. Pellentesque eget arcu quam. Curabitur dictum nulla varius hendrerit varius. Proin vulputate, ex lacinia commodo varius, ipsum velit viverra est, eget molestie dui nisi non eros. Nulla lobortis odio a magna mollis placerat. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer consectetur libero ut gravida ullamcorper. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec aliquam feugiat mollis. Quisque massa lacus, efficitur vel justo vel, elementum mollis magna. Maecenas at sem sem. Praesent sed ex mattis, egestas dui non, volutpat lorem. Nulla tempor, nisi rutrum accumsan varius, tellus elit faucibus nulla, vel mattis lacus justo at ante. Sed ut mollis ex, sed tincidunt ex.\n\nMauris laoreet nibh eget erat tincidunt pharetra. Aenean sagittis maximus consectetur. Curabitur interdum nibh sed tincidunt finibus. Sed blandit nec lorem at iaculis. Morbi non augue nec tortor hendrerit mollis ut non arcu. Suspendisse maximus nec ligula a efficitur. Etiam ultrices rhoncus leo quis dapibus. Integer vel rhoncus est. Integer blandit varius auctor. Vestibulum suscipit suscipit risus, sit amet venenatis lacus iaculis a. Duis eu turpis sit amet nibh sagittis convallis at quis ligula. Sed eget justo quis risus iaculis lacinia vitae a justo. In hac habitasse platea dictumst. Maecenas euismod et lorem vel viverra.\n\nDonec bibendum nec ipsum in volutpat. Vivamus in elit venenatis, venenatis libero ac, ultrices dolor. Morbi quis odio in neque consequat rutrum. Suspendisse quis sapien id sapien fermentum dignissim. Nam eu est vel risus volutpat mollis sed quis eros. Proin leo nulla, dictum id hendrerit vitae, scelerisque in elit. Proin consectetur sodales arcu ac tristique. Suspendisse ut elementum ligula, at rhoncus mauris. Aliquam lacinia at diam eget mattis. Phasellus quam leo, hendrerit sit amet mi eget, porttitor aliquet velit. Proin turpis ante, consequat in enim nec, tempus consequat magna. Vestibulum fringilla ac turpis nec malesuada. Proin id lectus iaculis, suscipit erat at, volutpat turpis. In quis faucibus elit, ut maximus nibh. Sed egestas egestas dolor.\n\nNulla varius orci quam, id auctor enim ultrices nec. Morbi et tellus ac metus sodales convallis sed vehicula neque. Pellentesque rhoncus mattis massa a bibendum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce tincidunt nulla non aliquet facilisis. Praesent nisl nisi, finibus id odio sed, consectetur feugiat mauris. Suspendisse sed lacinia ligula. Duis vitae nisl leo. Donec erat arcu, feugiat sit amet sagittis ac, scelerisque nec est. Pellentesque finibus mauris nulla, in maximus sapien pharetra vitae. Sed leo elit, consequat eu aliquam vitae, feugiat ut eros. Pellentesque dictum feugiat odio sed commodo. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin neque quam, varius vel libero sit amet, rhoncus sollicitudin ex. In a dui non neque malesuada pellentesque.\n\nProin tincidunt nisl non commodo faucibus. Sed porttitor arcu neque, vitae bibendum sapien placerat nec. Integer eget tristique orci. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec eu molestie eros. Nunc iaculis rhoncus enim, vel mattis felis fringilla condimentum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean ac augue nulla. Phasellus vitae nulla lobortis, mattis magna ac, gravida ipsum. Aenean ornare non nunc non luctus. Aenean lacinia lectus nec velit finibus egestas vel ut ipsum. Cras hendrerit rhoncus erat, vel maximus nunc.\n\nPraesent quis imperdiet quam. Praesent ligula tellus, consectetur sed lacus eu, malesuada condimentum tellus. Donec et diam hendrerit, dictum diam quis, aliquet purus. Suspendisse pulvinar neque at efficitur iaculis. Nulla erat orci, euismod id velit sed, dictum hendrerit arcu. Nulla aliquam molestie aliquam. Duis et semper nisi, eget commodo arcu. Praesent rhoncus, nulla id sodales eleifend, ante ipsum pellentesque augue, id iaculis sem est vitae est. Phasellus cursus diam a lorem vestibulum sodales. Nullam lacinia tortor vel tellus commodo, sit amet sodales quam malesuada.\n\nNulla tempor lectus vel arcu feugiat, vel dapibus ex dapibus. Maecenas purus justo, aliquet et sem sit amet, tincidunt venenatis dui. Nulla eget purus id sapien elementum rutrum eu vel libero. Cras non accumsan justo posuere.\n\n"
+                                               ;; prevent string interning, just to be sure
+                                               (UUID/randomUUID))}]})
+        (command! {:type :application.command/submit
+                   :application-id app-id
+                   :actor user-id})
+        (command! {:type :application.command/approve
+                   :application-id app-id
+                   :actor handler
+                   :comment ""})))))
 
 (defn create-test-data! []
   (db/add-api-key! {:apikey 42 :comment "test data"})
   (create-users-and-roles!)
-  (let [res1 (:id (db/create-resource! {:resid "urn:nbn:fi:lb-201403262" :organization "nbn" :owneruserid (+fake-users+ :owner) :modifieruserid (+fake-users+ :owner)}))
-        res2 (:id (db/create-resource! {:resid "Extra Data" :organization "nbn" :owneruserid (+fake-users+ :owner) :modifieruserid (+fake-users+ :owner)}))
-        res-with-extra-license (:id (db/create-resource! {:resid "urn:nbn:fi:lb-201403263" :organization "nbn" :owneruserid (+fake-users+ :owner) :modifieruserid (+fake-users+ :owner)}))
-        _ (:id (db/create-resource! {:resid "Expired Resource, should not be seen" :organization "nbn" :owneruserid (+fake-users+ :owner) :modifieruserid (+fake-users+ :owner) :end (time/minus (time/now) (time/years 1))}))
+  (let [res1 (create-resource! {:resource-ext-id "urn:nbn:fi:lb-201403262"
+                                :organization "nbn"
+                                :actor (+fake-users+ :owner)
+                                :license-ids []})
+        res2 (create-resource! {:resource-ext-id "Extra Data"
+                                :organization "nbn"
+                                :actor (+fake-users+ :owner)
+                                :license-ids []})
+        _ (create-resource-license! res2 "Some test license" (+fake-users+ :owner))
+        res-with-extra-license (create-resource! {:resource-ext-id "urn:nbn:fi:lb-201403263"
+                                                  :organization "nbn"
+                                                  :actor (+fake-users+ :owner)})
+        _ (create-resource-license! res-with-extra-license "Extra license" (+fake-users+ :owner))
+        _ (db/create-resource! {:resid "Expired Resource, should not be seen"
+                                :organization "nbn"
+                                :owneruserid (+fake-users+ :owner)
+                                :modifieruserid (+fake-users+ :owner)
+                                :end (time/minus (time/now) (time/years 1))})
         form (create-basic-form! +fake-users+)
         _ (create-archived-form!)
         workflows (create-workflows! +fake-users+)]
-    (create-resource-license! res2 "Some test license" (+fake-users+ :owner))
-    (create-resource-license! res-with-extra-license "Extra license" (+fake-users+ :owner))
     (create-expired-license!)
-    (let [dynamic (create-catalogue-item! res1 (:dynamic workflows) form
-                                          {"en" "Dynamic workflow" "fi" "Dynaaminen työvuo"})]
+    (let [dynamic (create-catalogue-item! {:title {:en "Dynamic workflow"
+                                                   :fi "Dynaaminen työvuo"}
+                                           :resource-id res1
+                                           :form-id form
+                                           :workflow-id (:dynamic workflows)})]
       (create-applications! dynamic +fake-users+))
-    (create-catalogue-item! res-with-extra-license (:dynamic workflows) form
-                            {"en" "Dynamic workflow with extra license" "fi" "Dynaaminen työvuo ylimääräisellä lisenssillä"})
+    (create-catalogue-item! {:title {:en "Dynamic workflow with extra license"
+                                     :fi "Dynaaminen työvuo ylimääräisellä lisenssillä"}
+                             :resource-id res-with-extra-license
+                             :form-id form
+                             :workflow-id (:dynamic workflows)})
     (let [thlform (create-thl-demo-form! +fake-users+)
-          thl-catid (create-catalogue-item! res1 (:dynamic workflows) thlform {"en" "THL catalogue item" "fi" "THL katalogi-itemi"})]
+          thl-catid (create-catalogue-item! {:title {:en "THL catalogue item"
+                                                     :fi "THL katalogi-itemi"}
+                                             :resource-id res1
+                                             :form-id thlform
+                                             :workflow-id (:dynamic workflows)})]
       (create-member-applications! thl-catid (+fake-users+ :applicant1) (+fake-users+ :approver1) [{:userid (+fake-users+ :applicant2)}]))
-    (let [dynamic-disabled (create-catalogue-item! res1 (:dynamic workflows) form
-                                                   {"en" "Dynamic workflow (disabled)"
-                                                    "fi" "Dynaaminen työvuo (pois käytöstä)"})]
+    (let [dynamic-disabled (create-catalogue-item! {:title {:en "Dynamic workflow (disabled)"
+                                                            :fi "Dynaaminen työvuo (pois käytöstä)"}
+                                                    :resource-id res1
+                                                    :form-id form
+                                                    :workflow-id (:dynamic workflows)})]
       (create-disabled-applications! dynamic-disabled
                                      (+fake-users+ :approver1) ; TODO: this should probably be :applicant1
                                      (+fake-users+ :approver1))
       (db/set-catalogue-item-state! {:id dynamic-disabled :enabled false}))
-    (let [dynamic-expired (create-catalogue-item! res1 (:dynamic workflows) form
-                                                  {"en" "Dynamic workflow (expired)"
-                                                   "fi" "Dynaaminen työvuo (vanhentunut)"})]
+    (let [dynamic-expired (create-catalogue-item! {:title {:en "Dynamic workflow (expired)"
+                                                           :fi "Dynaaminen työvuo (vanhentunut)"}
+                                                   :resource-id res1
+                                                   :form-id form
+                                                   :workflow-id (:dynamic workflows)})]
       (db/set-catalogue-item-state! {:id dynamic-expired :end (time/now)}))))
 
 (defn create-demo-data! []
   (db/add-api-key! {:apikey 55 :comment "Finna"})
   (create-demo-users-and-roles!)
-  (let [res1 (:id (db/create-resource! {:resid "urn:nbn:fi:lb-201403262" :organization "nbn" :owneruserid (+demo-users+ :owner) :modifieruserid (+demo-users+ :owner)}))
-        res2 (:id (db/create-resource! {:resid "Extra Data" :organization "nbn" :owneruserid (+demo-users+ :owner) :modifieruserid (+demo-users+ :owner)}))
+  (let [res1 (create-resource! {:resource-ext-id "urn:nbn:fi:lb-201403262"
+                                :organization "nbn"
+                                :actor (+demo-users+ :owner)})
+        res2 (create-resource! {:resource-ext-id "Extra Data"
+                                :organization "nbn"
+                                :actor (+demo-users+ :owner)})
+        _ (create-resource-license! res2 "Some demo license" (+demo-users+ :owner))
         form (create-basic-form! +demo-users+)
         workflows (create-workflows! +demo-users+)]
-    (create-resource-license! res2 "Some demo license" (+demo-users+ :owner))
     (create-expired-license!)
-    (let [dynamic (create-catalogue-item! res1 (:dynamic workflows) form
-                                          {"en" "Dynamic workflow" "fi" "Dynaaminen työvuo"})]
+    (let [dynamic (create-catalogue-item! {:title {:en "Dynamic workflow"
+                                                   :fi "Dynaaminen työvuo"}
+                                           :resource-id res1
+                                           :form-id form
+                                           :workflow-id (:dynamic workflows)})]
       (create-applications! dynamic +demo-users+))
     (let [thlform (create-thl-demo-form! +demo-users+)
-          thl-catid (create-catalogue-item! res1 (:dynamic workflows) thlform {"en" "THL catalogue item" "fi" "THL katalogi-itemi"})]
+          thl-catid (create-catalogue-item! {:title {:en "THL catalogue item"
+                                                     :fi "THL katalogi-itemi"}
+                                             :resource-id res1
+                                             :form-id thlform
+                                             :workflow-id (:dynamic workflows)})]
       (create-member-applications! thl-catid (+demo-users+ :applicant1) (+demo-users+ :approver1) [{:userid (+demo-users+ :applicant2)}]))
-    (let [dynamic-disabled (create-catalogue-item! res1 (:dynamic workflows) form
-                                                   {"en" "Dynamic workflow (disabled)"
-                                                    "fi" "Dynaaminen työvuo (pois käytöstä)"})]
+    (let [dynamic-disabled (create-catalogue-item! {:title {:en "Dynamic workflow (disabled)"
+                                                            :fi "Dynaaminen työvuo (pois käytöstä)"}
+                                                    :resource-id res1
+                                                    :form-id form
+                                                    :workflow-id (:dynamic workflows)})]
       (create-disabled-applications! dynamic-disabled
                                      (+demo-users+ :approver1) ; TODO: this should probably be :applicant1
                                      (+demo-users+ :approver1))
diff --git a/test/clj/rems/api/test_applications.clj b/test/clj/rems/api/test_applications.clj
index 762676453..e53229b0a 100644
--- a/test/clj/rems/api/test_applications.clj
+++ b/test/clj/rems/api/test_applications.clj
@@ -2,10 +2,10 @@
   (:require [clojure.test :refer :all]
             [rems.api.testing :refer :all]
             [rems.db.form :as form]
+            [rems.db.test-data :as test-data]
             [rems.handler :refer [handler]]
             [rems.json]
-            [ring.mock.request :refer :all])
-  (:import [java.util UUID]))
+            [ring.mock.request :refer :all]))
 
 (use-fixtures
   :once
@@ -13,68 +13,6 @@
 
 ;;; shared helpers
 
-(defn- create-dynamic-workflow []
-  (-> (request :post "/api/workflows/create")
-      (json-body {:organization "abc"
-                  :title "dynamic workflow"
-                  :type :dynamic
-                  :handlers ["developer"]})
-      (authenticate "42" "owner")
-      handler
-      read-ok-body
-      :id))
-
-(defn- create-form-with-fields [form-fields]
-  (-> (request :post "/api/forms/create")
-      (authenticate "42" "owner")
-      (json-body {:form/organization "abc"
-                  :form/title ""
-                  :form/fields form-fields})
-      handler
-      read-ok-body
-      :id))
-
-(defn- create-empty-form []
-  (create-form-with-fields []))
-
-(defn- create-license []
-  (-> (request :post "/api/licenses/create")
-      (authenticate "42" "owner")
-      (json-body {:licensetype "text"
-                  :title (str (UUID/randomUUID))
-                  :textcontent ""
-                  :localizations {}})
-      handler
-      read-ok-body
-      :id))
-
-(defn- create-resource [& license-ids]
-  (-> (request :post "/api/resources/create")
-      (authenticate "42" "owner")
-      (json-body {:resid (str (UUID/randomUUID))
-                  :organization "abc"
-                  :licenses license-ids})
-      handler
-      read-ok-body
-      :id))
-
-(defn- create-catalogue-item [form-id workflow-id resource-id]
-  (-> (request :post "/api/catalogue-items/create")
-      (authenticate "42" "owner")
-      (json-body {:title ""
-                  :form form-id
-                  :resid resource-id
-                  :wfid workflow-id})
-      handler
-      read-ok-body
-      :id))
-
-(defn- create-dymmy-catalogue-item []
-  (let [form-id (create-empty-form)
-        workflow-id (create-dynamic-workflow)
-        resource-id (create-resource)]
-    (create-catalogue-item form-id workflow-id resource-id)))
-
 (defn- send-command [actor cmd]
   (-> (request :post (str "/api/applications/" (name (:type cmd))))
       (authenticate "42" actor)
@@ -82,17 +20,6 @@
       handler
       read-body))
 
-(defn- create-application [catalogue-item-ids user-id]
-  (-> (request :post "/api/applications/create")
-      (authenticate "42" user-id)
-      (json-body {:catalogue-item-ids catalogue-item-ids})
-      handler
-      read-ok-body
-      :application-id))
-
-(defn- create-dummy-application [user-id]
-  (create-application [(create-dymmy-catalogue-item)] user-id))
-
 (defn- get-ids [applications]
   (set (map :application/id applications)))
 
@@ -154,7 +81,7 @@
                  (header "Cookie" cookie)
                  handler
                  get-csrf-token)
-        cat-id (create-dymmy-catalogue-item)]
+        cat-id (test-data/create-catalogue-item! {})]
     (is cookie)
     (is csrf)
     (testing "save with session"
@@ -207,16 +134,27 @@
         handler-id "developer"
         commenter-id "carl"
         decider-id "bob"
-        license-id1 (create-license)
-        license-id2 (create-license)
-        license-id3 (create-license)
-        license-id4 (create-license)
-        form-id (create-empty-form)
-        workflow-id (create-dynamic-workflow)
-        cat-item-id1 (create-catalogue-item form-id workflow-id (create-resource license-id1 license-id2))
-        cat-item-id2 (create-catalogue-item form-id workflow-id (create-resource license-id1 license-id2))
-        cat-item-id3 (create-catalogue-item form-id workflow-id (create-resource license-id3))
-        application-id (create-application [cat-item-id1] user-id)]
+        license-id1 (test-data/create-license! {})
+        license-id2 (test-data/create-license! {})
+        license-id3 (test-data/create-license! {})
+        license-id4 (test-data/create-license! {})
+        form-id (test-data/create-form! {})
+        workflow-id (test-data/create-dynamic-workflow! {:handlers [handler-id]})
+        cat-item-id1 (test-data/create-catalogue-item! {:resource-id (test-data/create-resource!
+                                                                      {:license-ids [license-id1 license-id2]})
+                                                        :form-id form-id
+                                                        :workflow-id workflow-id})
+        cat-item-id2 (test-data/create-catalogue-item! {:resource-id (test-data/create-resource!
+                                                                      {:license-ids [license-id1 license-id2]})
+                                                        :form-id form-id
+                                                        :workflow-id workflow-id})
+
+        cat-item-id3 (test-data/create-catalogue-item! {:resource-id (test-data/create-resource!
+                                                                      {:license-ids [license-id3]})
+                                                        :form-id form-id
+                                                        :workflow-id workflow-id})
+        application-id (test-data/create-application! {:catalogue-item-ids [cat-item-id1]
+                                                       :actor user-id})]
 
     (testing "accept licenses"
       (is (= {:success true} (send-command user-id
@@ -438,7 +376,12 @@
 (deftest test-application-create
   (let [api-key "42"
         user-id "alice"
-        application-id (create-dummy-application user-id)]
+        application-id (-> (request :post "/api/applications/create")
+                           (authenticate "42" user-id)
+                           (json-body {:catalogue-item-ids [(test-data/create-catalogue-item! {})]})
+                           handler
+                           read-ok-body
+                           :application-id)]
 
     (testing "creating"
       (is (some? application-id))
@@ -469,7 +412,7 @@
 
 (deftest test-application-close
   (let [user-id "alice"
-        application-id (create-dummy-application user-id)]
+        application-id (test-data/create-application! {:actor user-id})]
     (is (= {:success true}
            (send-command user-id {:type :application.command/close
                                   :application-id application-id
@@ -479,18 +422,19 @@
 
 (deftest test-application-validation
   (let [user-id "alice"
-        workflow-id (create-dynamic-workflow)
-        form-id (create-form-with-fields [{:field/title {:en "req"}
-                                           :field/type :text
-                                           :field/optional false}
-                                          {:field/title {:en "opt"}
-                                           :field/type :text
-                                           :field/optional true}])
+        form-id (test-data/create-form! {:form/fields [{:field/title {:en "req"}
+                                                        :field/type :text
+                                                        :field/optional false}
+                                                       {:field/title {:en "opt"}
+                                                        :field/type :text
+                                                        :field/optional true}]})
         [req-id opt-id] (->> (form/get-form-template form-id)
                              :form/fields
                              (map :field/id))
-        cat-id (create-catalogue-item form-id workflow-id 1)
-        app-id (create-application [cat-id] user-id)]
+        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 "set value of optional field"
       (is (= {:success true}
              (send-command user-id {:type :application.command/save-draft
@@ -529,12 +473,12 @@
 (deftest test-application-api-attachments
   (let [api-key "42"
         user-id "alice"
-        workflow-id (create-dynamic-workflow)
-        form-id (create-form-with-fields [{:field/title {:en "some attachment"}
-                                           :field/type :attachment
-                                           :field/optional true}])
-        cat-id (create-catalogue-item form-id workflow-id 1)
-        app-id (create-application [cat-id] user-id)
+        form-id (test-data/create-form! {:form/fields [{:field/title {:en "some attachment"}
+                                                        :field/type :attachment
+                                                        :field/optional true}]})
+        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})
         upload-request (fn [file]
                          (-> (request :post (str "/api/applications/add-attachment?application-id=" app-id))
                              (assoc :params {"file" file})
@@ -602,8 +546,9 @@
 (deftest test-applications-api-security
   (let [api-key "42"
         applicant "alice"
-        cat-id (create-dymmy-catalogue-item)
-        app-id (create-application [cat-id] applicant)]
+        cat-id (test-data/create-catalogue-item! {})
+        app-id (test-data/create-application! {:catalogue-item-ids [cat-id]
+                                               :actor applicant})]
 
     (testing "fetch application without authentication"
       (let [req (request :get (str "/api/applications/" app-id))]
@@ -671,7 +616,7 @@
                                            handler)))))))
 
 (deftest test-application-listing
-  (let [app-id (create-dummy-application "alice")]
+  (let [app-id (test-data/create-application! {:actor "alice"})]
 
     (testing "list user applications"
       (is (contains? (get-ids (get-my-applications "alice"))
@@ -682,7 +627,7 @@
                      app-id)))))
 
 (deftest test-todos
-  (let [app-id (create-dummy-application "alice")]
+  (let [app-id (test-data/create-application! {:actor "alice"})]
 
     (testing "does not list drafts"
       (is (not (contains? (get-ids (get-todos "developer"))
diff --git a/test/clj/rems/api/test_catalogue_items.clj b/test/clj/rems/api/test_catalogue_items.clj
index 7297c5666..d11b56f4e 100644
--- a/test/clj/rems/api/test_catalogue_items.clj
+++ b/test/clj/rems/api/test_catalogue_items.clj
@@ -2,7 +2,7 @@
   (:require [clojure.string :as str]
             [clojure.test :refer :all]
             [rems.api.testing :refer :all]
-            [rems.db.form :as form]
+            [rems.db.test-data :as test-data]
             [rems.handler :refer [handler]]
             [ring.mock.request :refer :all]))
 
@@ -13,9 +13,7 @@
 (deftest catalogue-items-api-test
   (let [api-key "42"
         user-id "alice"
-        form-id (:id (form/create-form! "owner" {:form/organization "abc"
-                                                 :form/title "Basic form"
-                                                 :form/fields []}))]
+        form-id (test-data/create-form! {:form/title "form name"})]
     (let [data (-> (request :get "/api/catalogue-items/")
                    (authenticate api-key user-id)
                    handler
@@ -41,7 +39,7 @@
         (is (= {:id id
                 :title "test-item-title"
                 :workflow-name "dynamic workflow"
-                :form-name "Basic form"
+                :form-name "form name"
                 :resource-name "urn:nbn:fi:lb-201403262"}
                (select-keys data [:id :title :workflow-name :form-name :resource-name])))))
     (testing "not found"
diff --git a/test/clj/rems/api/test_workflows.clj b/test/clj/rems/api/test_workflows.clj
index 8f0a9ada1..96a5c1ae3 100644
--- a/test/clj/rems/api/test_workflows.clj
+++ b/test/clj/rems/api/test_workflows.clj
@@ -2,6 +2,8 @@
   (:require [clojure.test :refer :all]
             [rems.api.testing :refer :all]
             [rems.common-util :refer [index-by]]
+            [rems.db.test-data :as test-data]
+            [rems.db.testing :refer [sync-with-database-time]]
             [rems.db.workflow :as workflow]
             [rems.handler :refer [handler]]
             [ring.mock.request :refer :all]))
@@ -56,6 +58,7 @@
                    read-body)
           id (:id body)]
       (is (< 0 id))
+      (sync-with-database-time)
       (testing "and fetch"
         (let [workflows (-> (request :get "/api/workflows")
                             (authenticate "42" "owner")
@@ -76,16 +79,9 @@
 (deftest workflows-update-test
   (let [api-key "42"
         user-id "owner"
-        wf-spec {:organization "abc"
-                 :title "dynamic workflow"
-                 :type :dynamic
-                 :handlers ["bob" "carl"]}
-        wfid (-> (request :post "/api/workflows/create")
-                 (json-body wf-spec)
-                 (authenticate api-key user-id)
-                 handler
-                 read-ok-body
-                 :id)
+        wfid (test-data/create-dynamic-workflow! {:organization "abc"
+                                                  :title "dynamic workflow"
+                                                  :handlers ["bob" "carl"]})
         ;; this is a subset of what we expect to get from the api
         expected {:id wfid
                   :organization "abc"
@@ -109,6 +105,7 @@
                     (authenticate api-key user-id)
                     handler
                     read-ok-body)]
+    (sync-with-database-time)
     (testing "before changes"
       (is (= expected (fetch))))
     (testing "disable and archive"
@@ -141,16 +138,8 @@
              (fetch))))))
 
 (deftest workflows-api-filtering-test
-  (let [enabled-wf (:id (workflow/create-workflow! {:user-id "owner"
-                                                    :organization "abc"
-                                                    :title ""
-                                                    :type :dynamic
-                                                    :handlers []}))
-        disabled-wf (:id (workflow/create-workflow! {:user-id "owner"
-                                                     :organization "abc"
-                                                     :title ""
-                                                     :type :dynamic
-                                                     :handlers []}))
+  (let [enabled-wf (test-data/create-dynamic-workflow! {})
+        disabled-wf (test-data/create-dynamic-workflow! {})
         _ (workflow/update-workflow! {:id disabled-wf
                                       :enabled false})
         enabled-and-disabled-wfs (set (map :id (-> (request :get "/api/workflows" {:disabled true})
diff --git a/test/clj/rems/db/test_applications.clj b/test/clj/rems/db/test_applications.clj
index 8d7de128d..ea179e61e 100644
--- a/test/clj/rems/db/test_applications.clj
+++ b/test/clj/rems/db/test_applications.clj
@@ -4,14 +4,10 @@
             [clojure.test.check.generators :as generators]
             [rems.application.events :as events]
             [rems.db.applications :refer [application-created-event application-external-id!]]
-            [rems.db.catalogue :as catalogue]
             [rems.db.core :as db]
             [rems.db.events :as db-events]
-            [rems.db.form :as form]
-            [rems.db.licenses :as licenses]
-            [rems.db.resource :as resource]
+            [rems.db.test-data :as test-data]
             [rems.db.testing :refer [test-db-fixture rollback-db-fixture test-data-fixture]]
-            [rems.db.workflow :as workflow]
             [rems.util :refer [try-catch-ex]]
             [schema-generators.generators :as sg])
   (:import (org.joda.time DateTime DateTimeZone)
@@ -47,26 +43,12 @@
       (is (str/includes? json "\"event/time\":\"2020-01-01T10:00:00.000Z\"")))))
 
 (deftest test-application-created-event
-  (let [wf-id (:id (workflow/create-workflow! {:type :dynamic
-                                               :organization "abc"
-                                               :title ""
-                                               :handlers []
-                                               :user-id "owner"}))
-        _ (assert wf-id)
-        form-id (:id (form/create-form! "owner" {:form/organization "abc"
-                                                 :form/title ""
-                                                 :form/fields []}))
-        _ (assert form-id)
-        res-id (:id (resource/create-resource! {:resid "res1"
-                                                :organization "abc"
-                                                :licenses []}
-                                               "owner"))
-        _ (assert res-id)
-        cat-id (:id (catalogue/create-catalogue-item! {:title ""
-                                                       :resid res-id
-                                                       :form form-id
-                                                       :wfid wf-id}))
-        _ (assert cat-id)]
+  (let [wf-id (test-data/create-dynamic-workflow! {})
+        form-id (test-data/create-form! {})
+        res-id (test-data/create-resource! {:resource-ext-id "res1"})
+        cat-id (test-data/create-catalogue-item! {:resource-id res-id
+                                                  :form-id form-id
+                                                  :workflow-id wf-id})]
 
     (testing "minimal application"
       (is (= {:event/type :application.event/created
@@ -86,16 +68,10 @@
                                          :actor "alice"}))))
 
     (testing "multiple resources"
-      (let [res-id2 (:id (resource/create-resource! {:resid "res2"
-                                                     :organization "abc"
-                                                     :licenses []}
-                                                    "owner"))
-            _ (assert res-id2)
-            cat-id2 (:id (catalogue/create-catalogue-item! {:title ""
-                                                            :resid res-id2
-                                                            :form form-id
-                                                            :wfid wf-id}))
-            _ (assert cat-id2)]
+      (let [res-id2 (test-data/create-resource! {:resource-ext-id "res2"})
+            cat-id2 (test-data/create-catalogue-item! {:resource-id res-id2
+                                                       :form-id form-id
+                                                       :workflow-id wf-id})]
         (is (= {:event/type :application.event/created
                 :event/actor "alice"
                 :event/time (DateTime. 1000)
@@ -129,20 +105,11 @@
                                                         :actor "alice"}))))
 
     (testing "error: catalogue items with different forms"
-      (let [form-id2 (:id (form/create-form! "owner" {:form/organization "abc"
-                                                      :form/title ""
-                                                      :form/fields []}))
-            _ (assert form-id2)
-            res-id2 (:id (resource/create-resource! {:resid "res2+"
-                                                     :organization "abc"
-                                                     :licenses []}
-                                                    "owner"))
-            _ (assert res-id2)
-            cat-id2 (:id (catalogue/create-catalogue-item! {:title ""
-                                                            :resid res-id2
-                                                            :form form-id2
-                                                            :wfid wf-id}))
-            _ (assert cat-id2)]
+      (let [form-id2 (test-data/create-form! {})
+            res-id2 (test-data/create-resource! {:resource-ext-id "res2+"})
+            cat-id2 (test-data/create-catalogue-item! {:resource-id res-id2
+                                                       :form-id form-id2
+                                                       :workflow-id wf-id})]
         (is (thrown-with-msg? AssertionError #"catalogue items did not have the same form"
                               (application-created-event {:application-id 42
                                                           :catalogue-item-ids [cat-id cat-id2]
@@ -150,22 +117,11 @@
                                                           :actor "alice"})))))
 
     (testing "error: catalogue items with different workflows"
-      (let [wf-id2 (:id (workflow/create-workflow! {:type :dynamic
-                                                    :organization "abc"
-                                                    :title ""
-                                                    :handlers []
-                                                    :user-id "owner"}))
-            _ (assert wf-id2)
-            res-id2 (:id (resource/create-resource! {:resid "res2++"
-                                                     :organization "abc"
-                                                     :licenses []}
-                                                    "owner"))
-            _ (assert res-id2)
-            cat-id2 (:id (catalogue/create-catalogue-item! {:title ""
-                                                            :resid res-id2
-                                                            :form form-id
-                                                            :wfid wf-id2}))
-            _ (assert cat-id2)]
+      (let [wf-id2 (test-data/create-dynamic-workflow! {})
+            res-id2 (test-data/create-resource! {:resource-ext-id "res2++"})
+            cat-id2 (test-data/create-catalogue-item! {:resource-id res-id2
+                                                       :form-id form-id
+                                                       :workflow-id wf-id2})]
         (is (thrown-with-msg? AssertionError #"catalogue items did not have the same workflow"
                               (application-created-event {:application-id 42
                                                           :catalogue-item-ids [cat-id cat-id2]
@@ -173,22 +129,12 @@
                                                           :actor "alice"})))))
 
     (testing "resource licenses"
-      (let [lic-id (:id (licenses/create-license! {:licensetype "text"
-                                                   :title ""
-                                                   :textcontent ""
-                                                   :localizations {}}
-                                                  "owner"))
-            _ (assert lic-id)
-            res-id2 (:id (resource/create-resource! {:resid "res2+++"
-                                                     :organization "abc"
-                                                     :licenses [lic-id]}
-                                                    "owner"))
-            _ (assert res-id2)
-            cat-id2 (:id (catalogue/create-catalogue-item! {:title ""
-                                                            :resid res-id2
-                                                            :form form-id
-                                                            :wfid wf-id}))
-            _ (assert cat-id2)]
+      (let [lic-id (test-data/create-license! {})
+            res-id2 (test-data/create-resource! {:resource-ext-id "res2+++"
+                                                 :license-ids [lic-id]})
+            cat-id2 (test-data/create-catalogue-item! {:resource-id res-id2
+                                                       :form-id form-id
+                                                       :workflow-id wf-id})]
         (is (= {:event/type :application.event/created
                 :event/actor "alice"
                 :event/time (DateTime. 1000)
@@ -206,24 +152,12 @@
                                            :actor "alice"})))))
 
     (testing "workflow licenses"
-      (let [lic-id (:id (licenses/create-license! {:licensetype "text"
-                                                   :title ""
-                                                   :textcontent ""
-                                                   :localizations {}}
-                                                  "owner"))
-            _ (assert lic-id)
-            wf-id2 (:id (workflow/create-workflow! {:type :dynamic
-                                                    :organization "abc"
-                                                    :title ""
-                                                    :handlers []
-                                                    :user-id "owner"}))
-            _ (assert wf-id2)
+      (let [lic-id (test-data/create-license! {})
+            wf-id2 (test-data/create-dynamic-workflow! {})
             _ (db/create-workflow-license! {:wfid wf-id2 :licid lic-id})
-            cat-id2 (:id (catalogue/create-catalogue-item! {:title ""
-                                                            :resid res-id
-                                                            :form form-id
-                                                            :wfid wf-id2}))
-            _ (assert cat-id2)]
+            cat-id2 (test-data/create-catalogue-item! {:resource-id res-id
+                                                       :form-id form-id
+                                                       :workflow-id wf-id2})]
         (is (= {:event/type :application.event/created
                 :event/actor "alice"
                 :event/time (DateTime. 1000)
diff --git a/test/clj/rems/db/test_catalogue.clj b/test/clj/rems/db/test_catalogue.clj
index f60140987..db4c798b7 100644
--- a/test/clj/rems/db/test_catalogue.clj
+++ b/test/clj/rems/db/test_catalogue.clj
@@ -2,7 +2,7 @@
   (:require [clojure.test :refer :all]
             [rems.db.catalogue :as catalogue]
             [rems.db.core :as db]
-            [rems.db.form :as form]
+            [rems.db.test-data :as test-data]
             [rems.db.testing :refer [rollback-db-fixture test-db-fixture]]))
 
 (use-fixtures :once test-db-fixture)
@@ -13,11 +13,8 @@
       (select-keys [:enabled :archived])))
 
 (deftest test-update-catalogue-item!
-  (let [uid "test-user"
-        form-id (:id (form/create-form! uid {:form/organization "org" :form/title "" :form/fields []}))
-        wf-id (:id (db/create-workflow! {:organization "org" :modifieruserid uid :owneruserid uid :title "Test workflow"}))
-        item-id (:id (db/create-catalogue-item! {:title "item" :form form-id :resid nil :wfid wf-id}))
-        item-id2 (:id (db/create-catalogue-item! {:title "item" :form form-id :resid nil :wfid wf-id}))]
+  (let [item-id (test-data/create-catalogue-item! {})
+        item-id2 (test-data/create-catalogue-item! {})]
 
     (testing "new catalogue items are enabled and not archived"
       (is (= {:enabled true
@@ -72,10 +69,7 @@
              (status-flags item-id2))))))
 
 (deftest test-get-localized-catalogue-items
-  (let [uid "test-user"
-        form-id (:id (form/create-form! uid {:form/organization "org" :form/title "" :form/fields []}))
-        wf-id (:id (db/create-workflow! {:organization "org" :modifieruserid uid :owneruserid uid :title "Test workflow"}))
-        item-id (:id (db/create-catalogue-item! {:title "item" :form form-id :resid nil :wfid wf-id}))]
+  (let [item-id (test-data/create-catalogue-item! {})]
 
     (testing "find all"
       (is (= [item-id] (map :id (catalogue/get-localized-catalogue-items)))))
diff --git a/test/clj/rems/db/test_core.clj b/test/clj/rems/db/test_core.clj
index 3db2b2c39..cd5253c70 100644
--- a/test/clj/rems/db/test_core.clj
+++ b/test/clj/rems/db/test_core.clj
@@ -12,14 +12,32 @@
   (is (not (contains-all-kv-pairs? {:a 1 :b 2} {:c 3}))))
 
 (deftest test-now-active?
-  (let [today (time/now)
-        yesterday (time/minus today (time/days 1))
-        tomorrow (time/plus today (time/days 1))]
-    (is (not (now-active? tomorrow nil)) "not yet active")
-    (is (now-active? yesterday nil) "already active")
-    (is (now-active? nil nil) "always active")
-    (is (now-active? nil tomorrow) "still active")
-    (is (not (now-active? nil yesterday)) "already expired")))
+  (let [t0 (time/epoch)
+        t1 (time/plus t0 (time/millis 1))
+        t2 (time/plus t0 (time/millis 2))
+        t3 (time/plus t0 (time/millis 3))
+        t4 (time/plus t0 (time/millis 4))
+        t5 (time/plus t0 (time/millis 5))]
+    (testing "no start & no end"
+      (is (now-active? t1 nil nil) "always active"))
+    (testing "start defined"
+      (let [start t2]
+        (is (not (now-active? t1 start nil)) "before start")
+        (is (now-active? t2 start nil) "at start")
+        (is (now-active? t3 start nil) "after start")))
+    (testing "end defined"
+      (let [end t2]
+        (is (now-active? t1 nil end) "before end")
+        (is (not (now-active? t2 nil end)) "at end")
+        (is (not (now-active? t3 nil end)) "after end")))
+    (testing "start & end defined"
+      (let [start t2
+            end t4]
+        (is (not (now-active? t1 start end)) "before start")
+        (is (now-active? t2 start end) "at start")
+        (is (now-active? t3 start end) "between")
+        (is (not (now-active? t4 start end)) "at end")
+        (is (not (now-active? t5 start end)) "after end")))))
 
 (deftest test-assoc-expired
   (is (= {:expired false} (assoc-expired nil)))
diff --git a/test/clj/rems/db/test_entitlements.clj b/test/clj/rems/db/test_entitlements.clj
index 550e90f60..f3b918888 100644
--- a/test/clj/rems/db/test_entitlements.clj
+++ b/test/clj/rems/db/test_entitlements.clj
@@ -5,9 +5,7 @@
             [rems.db.applications :as applications]
             [rems.db.core :as db]
             [rems.db.entitlements :as entitlements]
-            [rems.db.form :as form]
-            [rems.db.licenses :as licenses]
-            [rems.db.resource :as resource]
+            [rems.db.test-data :as test-data]
             [rems.db.testing :refer [test-db-fixture rollback-db-fixture test-data-fixture]]
             [rems.poller.entitlements :as entitlements-poller]
             [rems.testing-util :refer [suppress-logging]]
@@ -80,45 +78,42 @@
 
 (deftest test-entitlement-granting
   (entitlements-poller/run) ; clear previous entitlements
-  (let [uid "bob"
-        memberid "elsa"
+  (let [applicant "bob"
+        member "elsa"
         admin "owner"
-        organization "foo"
-        workflow {:type :workflow/dynamic :handlers [admin]}
-        wfid (:id (db/create-workflow! {:organization "abc" :modifieruserid "owner" :owneruserid "owner" :title "dynamic" :workflow (cheshire/generate-string workflow)}))
-        form-id (:id (form/create-form! "owner" {:form/organization "abc" :form/title "" :form/fields []}))
-        lic-id1 (:id (licenses/create-license! {:licensetype "text"
-                                                :title "license1"
-                                                :textcontent "license1 text"
-                                                :localizations {}}
-                                               "owner"))
-        lic-id2 (:id (licenses/create-license! {:licensetype "text"
-                                                :title "license2"
-                                                :textcontent "license2 text"
-                                                :localizations {}}
-                                               "owner"))
-        res1 (:id (resource/create-resource! {:resid "resource1" :organization organization :licenses [lic-id1]} "owner"))
-        res2 (:id (resource/create-resource! {:resid "resource2" :organization organization :licenses [lic-id2]} "owner"))
-        res3 (:id (resource/create-resource! {:resid "resource3" :organization organization :licenses [lic-id1]} "owner"))
-        item1 (:id (db/create-catalogue-item! {:title "item1" :form form-id :resid res1 :wfid wfid}))
-        item2 (:id (db/create-catalogue-item! {:title "item2" :form form-id :resid res2 :wfid wfid}))
-        item3 (:id (db/create-catalogue-item! {:title "item3" :form form-id :resid res3 :wfid wfid}))]
-    (db/add-user! {:user uid :userattrs (cheshire/generate-string {"mail" "b@o.b"})})
-    (db/add-user! {:user memberid :userattrs (cheshire/generate-string {"mail" "e.l@s.a"})})
+        wfid (test-data/create-dynamic-workflow! {:handlers [admin]})
+        form-id (test-data/create-form! {})
+        lic-id1 (test-data/create-license! {})
+        lic-id2 (test-data/create-license! {})
+        item1 (test-data/create-catalogue-item!
+               {:resource-id (test-data/create-resource! {:resource-ext-id "resource1"
+                                                          :license-ids [lic-id1]})
+                :form-id form-id
+                :workflow-id wfid})
+        item2 (test-data/create-catalogue-item!
+               {:resource-id (test-data/create-resource! {:resource-ext-id "resource2"
+                                                          :license-ids [lic-id2]})
+                :form-id form-id
+                :workflow-id wfid})
+        item3 (test-data/create-catalogue-item!
+               {:resource-id (test-data/create-resource! {:resource-ext-id "resource3"
+                                                          :license-ids [lic-id1]})
+                :form-id form-id
+                :workflow-id wfid})]
+    (db/add-user! {:user applicant :userattrs (cheshire/generate-string {"mail" "b@o.b"})})
+    (db/add-user! {:user member :userattrs (cheshire/generate-string {"mail" "e.l@s.a"})})
     (db/add-user! {:user admin :userattrs nil})
 
-    (let [app-id (:application-id (applications/create-application! uid [item1 item2]))]
+    (let [app-id (test-data/create-application! {:actor applicant :catalogue-item-ids [item1 item2]})]
       (testing "submitted application should not yet cause entitlements"
         (with-stub-server server
-          (is (nil? (applications/command! {:type :application.command/submit
-                                            :actor uid
-                                            :application-id app-id
-                                            :time (time/now)})))
-          (is (nil? (applications/command! {:type :application.command/add-member
-                                            :actor admin
-                                            :application-id app-id
-                                            :member {:userid memberid}
-                                            :time (time/now)})))
+          (test-data/command! {:type :application.command/submit
+                               :application-id app-id
+                               :actor applicant})
+          (test-data/command! {:type :application.command/add-member
+                               :application-id app-id
+                               :actor admin
+                               :member {:userid member}})
           (entitlements-poller/run)
 
           (is (empty? (db/get-entitlements {:application app-id})))
@@ -126,11 +121,10 @@
 
         (testing "approved application should not yet cause entitlements"
           (with-stub-server server
-            (is (nil? (applications/command! {:type :application.command/approve
-                                              :actor admin
-                                              :application-id app-id
-                                              :comment ""
-                                              :time (time/now)})))
+            (test-data/command! {:type :application.command/approve
+                                 :application-id app-id
+                                 :actor admin
+                                 :comment ""})
             (entitlements-poller/run)
 
             (is (empty? (db/get-entitlements {:application app-id})))
@@ -138,27 +132,23 @@
 
         (testing "approved application, licenses accepted by one user generates entitlements for that user"
           (with-stub-server server
-            (is (nil? (applications/command! {:type :application.command/accept-licenses
-                                              :actor uid
-                                              :application-id app-id
-                                              :accepted-licenses [lic-id1 lic-id2]
-                                              :time (time/now)})))
-
-            (is (nil? (applications/command! {:type :application.command/accept-licenses
-                                              :actor memberid
-                                              :application-id app-id
-                                              :accepted-licenses [lic-id1] ; only accept some licenses
-                                              :time (time/now)})))
-
-            (is (= {uid #{lic-id1 lic-id2}
-                    memberid #{lic-id1}}
+            (test-data/command! {:type :application.command/accept-licenses
+                                 :application-id app-id
+                                 :actor applicant
+                                 :accepted-licenses [lic-id1 lic-id2]})
+            (test-data/command! {:type :application.command/accept-licenses
+                                 :application-id app-id
+                                 :actor member
+                                 :accepted-licenses [lic-id1]}) ; only accept some licenses
+            (is (= {applicant #{lic-id1 lic-id2}
+                    member #{lic-id1}}
                    (:application/accepted-licenses (applications/get-unrestricted-application app-id))))
 
             (entitlements-poller/run)
             (entitlements-poller/run) ;; run twice to check idempotence
             (is (= 2 (count (stub/recorded-requests server))))
             (testing "db"
-              (is (= [[uid "resource1"] [uid "resource2"]]
+              (is (= [[applicant "resource1"] [applicant "resource2"]]
                      (map (juxt :userid :resid) (db/get-entitlements {:application app-id})))))
             (testing "POST"
               (let [data (take 2 (stub/recorded-requests server))
@@ -174,17 +164,16 @@
 
       (testing "approved application, more accepted licenses generates more entitlements"
         (with-stub-server server
-          (is (nil? (applications/command! {:type :application.command/accept-licenses
-                                            :actor memberid
-                                            :application-id app-id
-                                            :accepted-licenses [lic-id1 lic-id2] ; now accept all licenses
-                                            :time (time/now)})))
+          (test-data/command! {:type :application.command/accept-licenses
+                               :application-id app-id
+                               :actor member
+                               :accepted-licenses [lic-id1 lic-id2]}) ; now accept all licenses
           (entitlements-poller/run)
 
           (is (= 2 (count (stub/recorded-requests server))))
           (testing "db"
-            (is (= [[uid "resource1"] [uid "resource2"]
-                    [memberid "resource1"] [memberid "resource2"]]
+            (is (= [[applicant "resource1"] [applicant "resource2"]
+                    [member "resource1"] [member "resource2"]]
                    (map (juxt :userid :resid) (db/get-entitlements {:application app-id})))))
           (testing "POST"
             (let [data (stub/recorded-requests server)
@@ -200,17 +189,16 @@
 
       (testing "removing a member ends entitlements"
         (with-stub-server server
-          (is (nil? (applications/command! {:type :application.command/remove-member
-                                            :actor admin
-                                            :application-id app-id
-                                            :member {:userid memberid}
-                                            :comment "Left team"
-                                            :time (time/now)})))
+          (test-data/command! {:type :application.command/remove-member
+                               :application-id app-id
+                               :actor admin
+                               :member {:userid member}
+                               :comment "Left team"})
           (entitlements-poller/run)
 
           (is (= 2 (count (stub/recorded-requests server))))
           (testing "db"
-            (is (= [[uid "resource1"] [uid "resource2"]]
+            (is (= [[applicant "resource1"] [applicant "resource2"]]
                    (map (juxt :userid :resid) (db/get-entitlements {:application app-id :is-active? true})))))
           (testing "POST"
             (let [data (stub/recorded-requests server)
@@ -226,17 +214,16 @@
 
       (testing "changing resources changes entitlements"
         (with-stub-server server
-          (is (nil? (applications/command! {:type :application.command/change-resources
-                                            :actor admin
-                                            :application-id app-id
-                                            :catalogue-item-ids [item1 item3]
-                                            :comment "Removed second resource, added third resource"
-                                            :time (time/now)})))
+          (test-data/command! {:type :application.command/change-resources
+                               :application-id app-id
+                               :actor admin
+                               :catalogue-item-ids [item1 item3]
+                               :comment "Removed second resource, added third resource"})
           (entitlements-poller/run)
 
           (is (= 2 (count (stub/recorded-requests server))))
           (testing "db"
-            (is (= [[uid "resource1"] [uid "resource3"]]
+            (is (= [[applicant "resource1"] [applicant "resource3"]]
                    (map (juxt :userid :resid) (db/get-entitlements {:application app-id :is-active? true})))))
           (testing "POST"
             (let [data (stub/recorded-requests server)
@@ -247,11 +234,10 @@
 
       (testing "closed application should end entitlements"
         (with-stub-server server
-          (is (nil? (applications/command! {:type :application.command/close
-                                            :actor admin
-                                            :application-id app-id
-                                            :comment "Finished"
-                                            :time (time/now)})))
+          (test-data/command! {:type :application.command/close
+                               :application-id app-id
+                               :actor admin
+                               :comment "Finished"})
           (entitlements-poller/run)
 
           (is (= 2 (count (stub/recorded-requests server))))
diff --git a/test/clj/rems/db/test_resources.clj b/test/clj/rems/db/test_resources.clj
index 46db8f3ee..b346df198 100644
--- a/test/clj/rems/db/test_resources.clj
+++ b/test/clj/rems/db/test_resources.clj
@@ -1,7 +1,7 @@
 (ns ^:integration rems.db.test-resources
   (:require [clojure.test :refer :all]
             [rems.db.resource :as resources]
-            [rems.db.core :as db]
+            [rems.db.test-data :as test-data]
             [rems.db.testing :refer [rollback-db-fixture test-db-fixture]]))
 
 (use-fixtures :once test-db-fixture)
@@ -12,9 +12,8 @@
       (select-keys [:enabled :archived])))
 
 (deftest test-update-resource!
-  (let [user-id "test-user"
-        res-id (:id (resources/create-resource! {:resid "res1" :organization "abc" :licenses []} user-id))
-        res-id2 (:id (resources/create-resource! {:resid "res2" :organization "abc" :licenses []} user-id))]
+  (let [res-id (test-data/create-resource! {})
+        res-id2 (test-data/create-resource! {})]
 
     (testing "new resources are enabled and not archived"
       (is (= {:enabled true
diff --git a/test/clj/rems/db/test_transactions.clj b/test/clj/rems/db/test_transactions.clj
index 99d6e6017..c0dfd479f 100644
--- a/test/clj/rems/db/test_transactions.clj
+++ b/test/clj/rems/db/test_transactions.clj
@@ -3,17 +3,12 @@
             [clojure.test :refer :all]
             [conman.core :as conman]
             [rems.application.commands :as commands]
-            [rems.db.applications :as applications]
-            [rems.db.catalogue :as catalogue]
             [rems.db.core :as db]
             [rems.db.events :as events]
-            [rems.db.form :as form]
-            [rems.db.resource :as resource]
+            [rems.db.test-data :as test-data]
             [rems.db.testing :refer [test-db-fixture rollback-db-fixture test-data-fixture]]
-            [rems.db.users :as users]
-            [rems.db.workflow :as workflow])
+            [rems.db.users :as users])
   (:import [java.sql SQLException]
-           [java.util UUID]
            [java.util.concurrent Executors Future TimeUnit ExecutorService]
            [org.postgresql.util PSQLException]))
 
@@ -26,28 +21,6 @@
     (users/add-user! user-id {:eppn user-id})
     user-id))
 
-(defn- create-dummy-application [user-id]
-  (let [workflow-id (:id (workflow/create-workflow! {:user-id user-id
-                                                     :organization ""
-                                                     :title ""
-                                                     :type :dynamic
-                                                     :handlers []}))
-        form-id (:id (form/create-form! user-id
-                                        {:form/organization ""
-                                         :form/title ""
-                                         :form/fields []}))
-        res-id (:id (resource/create-resource! {:resid (str "urn:uuid:" (UUID/randomUUID))
-                                                :organization ""
-                                                :licenses []}
-                                               user-id))
-        cat-id (:id (catalogue/create-catalogue-item! {:title ""
-                                                       :form form-id
-                                                       :resid res-id
-                                                       :wfid workflow-id}))
-        app-id (:application-id (applications/create-application! user-id [cat-id]))]
-    (assert app-id)
-    app-id))
-
 (defn- transaction-conflict? [^Exception e]
   (cond
     (nil? e) false
@@ -82,7 +55,7 @@
         concurrent-readers 5
         user-id (create-dummy-user)
         app-ids (vec (for [_ (range applications-count)]
-                       (create-dummy-application user-id)))
+                       (test-data/create-application! {:actor user-id})))
         ;; Currently we only test that commands are not executed concurrently
         ;; for a single application. To guarantee that, we could add an app version
         ;; column to the events table with constraint `UNIQUE (appId, appVersion)`.
@@ -97,11 +70,10 @@
                       (try
                         (conman/with-transaction [db/*db* {:isolation :serializable}]
                           (binding [commands/postprocess-command-result-for-tests mark-observed-app-version]
-                            (applications/command!
+                            (test-data/command!
                              {:type :application.command/save-draft
-                              :time (time/now)
-                              :actor user-id
                               :application-id app-id
+                              :actor user-id
                               :field-values []})))
                         (catch Exception e
                           (if (transaction-conflict? e)
diff --git a/test/clj/rems/db/testing.clj b/test/clj/rems/db/testing.clj
index fa1fcdc27..cafbd1df8 100644
--- a/test/clj/rems/db/testing.clj
+++ b/test/clj/rems/db/testing.clj
@@ -1,12 +1,15 @@
 (ns rems.db.testing
-  (:require [clojure.java.jdbc :as jdbc]
+  (:require [clj-time.core :as time]
+            [clojure.java.jdbc :as jdbc]
+            [clojure.tools.logging :as log]
             [conman.core :as conman]
             [luminus-migrations.core :as migrations]
             [mount.core :as mount]
             [rems.config :refer [env]]
             [rems.db.applications]
             [rems.db.core :as db]
-            [rems.db.test-data :as test-data]))
+            [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
@@ -35,3 +38,22 @@
   (conman/with-transaction [db/*db* {:isolation :serializable}]
     (jdbc/db-set-rollback-only! db/*db*)
     (f)))
+
+(defn get-database-time []
+  (:now (db/get-database-time)))
+
+(defn sync-with-database-time
+  "When the database runs on a different machine or VM than
+   the application (e.g. Docker for Mac), the database clock may be
+   ahead of the application clock, in which case newly created entities
+   may be incorrectly flagged as expired, because their creation time
+   seems to be in the future. This helper method can be called after
+   creating such entities in flaky tests to guarantee that the application
+   clock has caught up with the database clock."
+  []
+  (let [db-time (get-database-time)
+        app-time (time/now)
+        diff (.getMillis (Duration. ^ReadableInstant app-time ^ReadableInstant db-time))]
+    (when (pos? diff)
+      (log/info "The application clock is" diff "ms behind the database")
+      (Thread/sleep (+ 1 diff)))))
diff --git a/test/clj/rems/test_db.clj b/test/clj/rems/test_db.clj
index 691b40b89..57f8b65ec 100644
--- a/test/clj/rems/test_db.clj
+++ b/test/clj/rems/test_db.clj
@@ -27,42 +27,35 @@
     (is (empty? (db/get-catalogue-items))))
 
   (testing "with two items"
-    (let [resid (:id (db/create-resource! {:resid "urn:nbn:fi:lb-201403262" :organization "nbn" :owneruserid 1 :modifieruserid 1}))]
-      (db/create-catalogue-item! {:title "ELFA Corpus" :form nil :resid resid :wfid nil})
-      (db/create-catalogue-item! {:title "B" :form nil :resid nil :wfid nil})
-      (is (= ["B" "ELFA Corpus"] (sort (map :title (db/get-catalogue-items))))
+    (let [item1 (test-data/create-catalogue-item! {})
+          item2 (test-data/create-catalogue-item! {})]
+      (is (= (set [item1 item2]) (set (map :id (db/get-catalogue-items))))
           "should find the two items")
-      (let [item-from-list (second (db/get-catalogue-items))
-            item-by-id (first (db/get-catalogue-items {:ids [(:id item-from-list)]}))]
-        (is (= item-from-list (dissoc item-by-id
-                                      :resource-name
-                                      :form-name
-                                      :workflow-name))
-            "should find same catalogue item by id")))))
+      (is (= item1 (:id (first (db/get-catalogue-items {:ids [item1]}))))
+          "should find same catalogue item by id")
+      (is (= item2 (:id (first (db/get-catalogue-items {:ids [item2]}))))
+          "should find same catalogue item by id"))))
 
 (deftest test-multi-applications
   (db/add-user! {:user "test-user" :userattrs nil})
   (db/add-user! {:user "handler" :userattrs nil})
-  (let [uid "test-user"
-        workflow {:type :workflow/dynamic
-                  :handlers ["handler"]}
-        wfid (:id (db/create-workflow! {:organization "abc" :modifieruserid "owner" :owneruserid "owner" :title "dynamic" :workflow (cheshire/generate-string workflow)}))
-        res1 (:id (db/create-resource! {:resid "resid111" :organization "abc" :owneruserid uid :modifieruserid uid}))
-        res2 (:id (db/create-resource! {:resid "resid222" :organization "abc" :owneruserid uid :modifieruserid uid}))
-        form-id (:id (form/create-form! "owner" {:form/organization "abc" :form/title "" :form/fields []}))
-        item1 (:id (db/create-catalogue-item! {:title "item" :form form-id :resid res1 :wfid wfid}))
-        item2 (:id (db/create-catalogue-item! {:title "item" :form form-id :resid res2 :wfid wfid}))
-        app-id (:application-id (applications/create-application! uid [item1 item2]))]
-    (is (nil? (applications/command! {:type :application.command/submit
-                                      :actor uid
-                                      :application-id app-id
-                                      :time (time/now)})))
-    (is (nil? (applications/command! {:type :application.command/approve
-                                      :actor "handler"
-                                      :application-id app-id
-                                      :time (time/now)
-                                      :comment ""})))
-    (is (= :application.state/approved (:application/state (applications/get-application uid app-id))))
+  (let [applicant "test-user"
+        wfid (test-data/create-dynamic-workflow! {:handlers ["handler"]})
+        res1 (test-data/create-resource! {:resource-ext-id "resid111"})
+        res2 (test-data/create-resource! {:resource-ext-id "resid222"})
+        form-id (test-data/create-form! {})
+        item1 (test-data/create-catalogue-item! {:form-id form-id :resource-id res1 :workflow-id wfid})
+        item2 (test-data/create-catalogue-item! {:form-id form-id :resource-id res2 :workflow-id wfid})
+        app-id (test-data/create-application! {:catalogue-item-ids [item1 item2]
+                                               :actor applicant})]
+    (test-data/command! {:type :application.command/submit
+                         :application-id app-id
+                         :actor applicant})
+    (test-data/command! {:type :application.command/approve
+                         :application-id app-id
+                         :actor "handler"
+                         :comment ""})
+    (is (= :application.state/approved (:application/state (applications/get-application applicant app-id))))
 
     (entitlements-poller/run)
     (is (= ["resid111" "resid222"] (sort (map :resid (db/get-entitlements {:application app-id}))))
@@ -91,33 +84,28 @@
   (db/add-user! {:user "handler" :userattrs nil})
   (db/add-user! {:user "jack" :userattrs nil})
   (db/add-user! {:user "jill" :userattrs nil})
-  (let [workflow {:type :workflow/dynamic :handlers ["handler"]}
-        wf (:id (db/create-workflow! {:organization "abc" :modifieruserid "owner" :owneruserid "owner" :title "dynamic" :workflow (cheshire/generate-string workflow)}))
-        form-id (:id (form/create-form! "owner" {:form/organization "abc" :form/title "" :form/fields []}))
-        res1 (:id (db/create-resource! {:resid "resource1" :organization "pre" :owneruserid "owner" :modifieruserid "owner"}))
-        res2 (:id (db/create-resource! {:resid "resource2" :organization "pre" :owneruserid "owner" :modifieruserid "owner"}))
-        item1 (:id (db/create-catalogue-item! {:title "item1" :form form-id :resid res1 :wfid wf}))
-        item2 (:id (db/create-catalogue-item! {:title "item2" :form form-id :resid res2 :wfid wf}))
-        jack-app (:application-id (applications/create-application! "jack" [item1]))
-        jill-app (:application-id (applications/create-application! "jill" [item1 item2]))]
-    (is (nil? (applications/command! {:type :application.command/submit
-                                      :time (time/now)
-                                      :actor "jack"
-                                      :application-id jack-app})))
-    (is (nil? (applications/command! {:type :application.command/approve
-                                      :time (time/now)
-                                      :actor "handler"
-                                      :application-id jack-app
-                                      :comment ""})))
-    (is (nil? (applications/command! {:type :application.command/submit
-                                      :time (time/now)
-                                      :actor "jill"
-                                      :application-id jill-app})))
-    (is (nil? (applications/command! {:type :application.command/approve
-                                      :time (time/now)
-                                      :actor "handler"
-                                      :application-id jill-app
-                                      :comment ""})))
+  (let [wf (test-data/create-dynamic-workflow! {:handlers ["handler"]})
+        form-id (test-data/create-form! {})
+        res1 (test-data/create-resource! {:resource-ext-id "resource1"})
+        res2 (test-data/create-resource! {:resource-ext-id "resource2"})
+        item1 (test-data/create-catalogue-item! {:form-id form-id :resource-id res1 :workflow-id wf})
+        item2 (test-data/create-catalogue-item! {:form-id form-id :resource-id res2 :workflow-id wf})
+        jack-app (test-data/create-application! {:actor "jack" :catalogue-item-ids [item1]})
+        jill-app (test-data/create-application! {:actor "jill" :catalogue-item-ids [item1 item2]})]
+    (test-data/command! {:type :application.command/submit
+                         :application-id jack-app
+                         :actor "jack"})
+    (test-data/command! {:type :application.command/approve
+                         :application-id jack-app
+                         :actor "handler"
+                         :comment ""})
+    (test-data/command! {:type :application.command/submit
+                         :application-id jill-app
+                         :actor "jill"})
+    (test-data/command! {:type :application.command/approve
+                         :application-id jill-app
+                         :actor "handler"
+                         :comment ""})
     (entitlements-poller/run)
 
     (binding [context/*roles* #{:handler}]
diff --git a/test/clj/rems/test_redirects.clj b/test/clj/rems/test_redirects.clj
index 340ffd279..a534b19b0 100644
--- a/test/clj/rems/test_redirects.clj
+++ b/test/clj/rems/test_redirects.clj
@@ -1,9 +1,8 @@
 (ns ^:integration rems.test-redirects
   (:require [clojure.test :refer :all]
             [rems.api.testing :refer :all]
-            [rems.db.catalogue :as catalogue]
             [rems.db.core :as db]
-            [rems.db.resource :as resource]
+            [rems.db.test-data :as test-data]
             [rems.handler :refer [handler]]
             [ring.mock.request :refer :all]))
 
@@ -11,27 +10,13 @@
   :once
   api-fixture)
 
-(def test-user {:eppn "test-user"})
-
-(defn dummy-resource [resid]
-  (:id (resource/create-resource! {:resid resid
-                                   :organization "abc"
-                                   :licenses []}
-                                  (:eppn test-user))))
-
-(defn dummy-catalogue-item [resid]
-  (:id (catalogue/create-catalogue-item! {:title ""
-                                          :form 1
-                                          :resid resid
-                                          :wfid 1})))
-
 (defn disable-catalogue-item [catid]
   (db/set-catalogue-item-state! {:id catid :enabled false}))
 
 (deftest redirect-to-new-application-test
   (testing "redirects to new application page for catalogue item matching the resource ID"
-    (let [resid (dummy-resource "urn:one-matching-resource")
-          catid (dummy-catalogue-item resid)
+    (let [resid (test-data/create-resource! {:resource-ext-id "urn:one-matching-resource"})
+          catid (test-data/create-catalogue-item! {:resource-id resid})
           response (-> (request :get "/apply-for?resource=urn:one-matching-resource")
                        handler)]
       (is (= 302 (:status response)))
@@ -44,20 +29,20 @@
       (is (= "Resource not found" (read-body response)))))
 
   (testing "fails if more than one catalogue item is found"
-    (let [resid (dummy-resource "urn:two-matching-resources")
-          _ (dummy-catalogue-item resid)
-          _ (dummy-catalogue-item resid)
+    (let [resid (test-data/create-resource! {:resource-ext-id "urn:two-matching-resources"})
+          _ (test-data/create-catalogue-item! {:resource-id resid})
+          _ (test-data/create-catalogue-item! {:resource-id resid})
           response (-> (request :get "/apply-for?resource=urn:two-matching-resources")
                        handler)]
       (is (= 404 (:status response)))
       (is (= "Resource ID is not unique" (read-body response)))))
 
   (testing "redirects to active catalogue item, ignoring disabled items for the same resource ID"
-    (let [resid (dummy-resource "urn:enabed-and-disabled-items")
-          old-catid (dummy-catalogue-item resid)
+    (let [resid (test-data/create-resource! {:resource-ext-id "urn:enabled-and-disabled-items"})
+          old-catid (test-data/create-catalogue-item! {:resource-id resid})
           _ (disable-catalogue-item old-catid)
-          new-catid (dummy-catalogue-item resid)
-          response (-> (request :get "/apply-for?resource=urn:enabed-and-disabled-items")
+          new-catid (test-data/create-catalogue-item! {:resource-id resid})
+          response (-> (request :get "/apply-for?resource=urn:enabled-and-disabled-items")
                        handler)]
       (is (= 302 (:status response)))
       (is (= (str "http://localhost/#/application?items=" new-catid) (get-in response [:headers "Location"]))))))