From b790075b3a62347c737de92a4945f2bb9f8778d2 Mon Sep 17 00:00:00 2001 From: ayato_p Date: Thu, 15 Jun 2017 18:03:24 +0900 Subject: [PATCH 1/2] Add migrator.ragtime component --- extra/.gitignore | 9 ++ extra/README.md | 6 + extra/dev-resources/migrations/.keep | 0 extra/project.clj | 9 ++ .../resources/zou/config/default/ragtime.edn | 7 + extra/src/zou/migrator/ragtime.clj | 134 ++++++++++++++++++ extra/test/zou/migrator/ragtime_test.clj | 97 +++++++++++++ project.clj | 8 +- 8 files changed, 267 insertions(+), 3 deletions(-) create mode 100644 extra/.gitignore create mode 100644 extra/README.md create mode 100644 extra/dev-resources/migrations/.keep create mode 100644 extra/project.clj create mode 100644 extra/resources/zou/config/default/ragtime.edn create mode 100644 extra/src/zou/migrator/ragtime.clj create mode 100644 extra/test/zou/migrator/ragtime_test.clj diff --git a/extra/.gitignore b/extra/.gitignore new file mode 100644 index 0000000..e04714b --- /dev/null +++ b/extra/.gitignore @@ -0,0 +1,9 @@ +/target +/classes +/checkouts +pom.xml +pom.xml.asc +*.jar +*.class +/.lein-* +/.nrepl-port diff --git a/extra/README.md b/extra/README.md new file mode 100644 index 0000000..c7ba7a7 --- /dev/null +++ b/extra/README.md @@ -0,0 +1,6 @@ +# Zou extra components + +## Ragtime component + +If you want to use this component, you need to add ragtime as a dependency to your project.clj. +https://github.com/weavejester/ragtime diff --git a/extra/dev-resources/migrations/.keep b/extra/dev-resources/migrations/.keep new file mode 100644 index 0000000..e69de29 diff --git a/extra/project.clj b/extra/project.clj new file mode 100644 index 0000000..17902c9 --- /dev/null +++ b/extra/project.clj @@ -0,0 +1,9 @@ +(defproject zou/extra "0.1.0-alpha5-SNAPSHOT" + :dependencies [[zou/common :version] + [zou/component :version] + [zou/lib :version] + [inflections "0.13.0"]] + :plugins [[lein-modules "0.3.11"]] + :profiles + {:provided {:dependencies [[ragtime "0.7.1"]]} + :dev {:resource-paths ["dev-resources"]}}) diff --git a/extra/resources/zou/config/default/ragtime.edn b/extra/resources/zou/config/default/ragtime.edn new file mode 100644 index 0000000..fac78b6 --- /dev/null +++ b/extra/resources/zou/config/default/ragtime.edn @@ -0,0 +1,7 @@ +{:ragtime + {:zou/constructor zou.migrator.ragtime/new-ragtime + :zou/dependencies {:db :hikari-cp} + :migrations-path "migrations" + :style :sql ;; `:sql` or `:edn` + :strategy #resolve ragtime.strategy/raise-error + :reporter #resolve ragtime.reporter/print}} diff --git a/extra/src/zou/migrator/ragtime.clj b/extra/src/zou/migrator/ragtime.clj new file mode 100644 index 0000000..b54e0fb --- /dev/null +++ b/extra/src/zou/migrator/ragtime.clj @@ -0,0 +1,134 @@ +(ns zou.migrator.ragtime + (:require [clojure.java.io :as io] + [clojure.string :as str] + [inflections.core :as inf] + [ragtime.core :as ragtime] + [ragtime.jdbc :as jdbc] + [zou.component :as c] + [zou.logging :as log] + [zou.task :as task]) + (:import java.nio.file.FileSystems + java.time.format.DateTimeFormatter + java.time.LocalDateTime)) + +(defprotocol IMigrator + (migrate [this]) + (remigrate [this]) + (rollback [this opts]) + (generate [this description])) + +(defn- gen-file-name [description] + (let [f (DateTimeFormatter/ofPattern "yyyyMMddHHmmssSSS") + d (LocalDateTime/now)] + (->> (str/replace description #"\s" "_") + inf/underscore + (str "v_" (.format d f) "_")))) + +(defn- gen-sql-files [path description] + (let [separator (.getSeparator (FileSystems/getDefault)) + prefix (str path separator (gen-file-name description)) + up-sql (io/file (str prefix ".up.sql")) + down-sql (io/file (str prefix ".down.sql"))] + (spit up-sql "") + (spit down-sql "") + [up-sql down-sql])) + +(defn- gen-edn-files [path description] + (let [separator (.getSeparator (FileSystems/getDefault)) + prefix (str path separator (gen-file-name description)) + edn (io/file (str prefix ".edn"))] + (spit edn (str "{:up []" (System/lineSeparator) " :down []}")) + [edn])) + +(defn generate* [migrator description] + (let [{:keys [migrations-path style]} migrator + path (some-> (io/resource migrations-path) + io/file + .getPath)] + (when-not path + (log/warnf "Migrations directory `%s` does not exist" migrations-path)) + (case style + :edn (gen-edn-files path description) + :sql (gen-sql-files path description) + (throw (ex-info "Invalid parameter" {:style style}))))) + +(defrecord Ragtime [db] + c/Lifecycle + (start [this] + (let [{:keys [migrations-path]} this + migrations (jdbc/load-resources migrations-path)] + (-> this + (assoc :migrations migrations) + (assoc :datastore (jdbc/sql-database db)) + (assoc :index (ragtime/into-index migrations))))) + (stop [this] + (dissoc this :migrations :datastore :index)) + + IMigrator + (migrate [this] + (let [{:keys [datastore index migrations]} this + applied (ragtime/applied-migrations datastore index) + opts (select-keys this [:strategy :reporter])] + (if (> (count index) (count applied)) + (do (ragtime/migrate-all datastore index migrations opts) + (log/info "Successfully migrated")) + (log/info "No migrations to apply")))) + + (remigrate [this] + (let [{:keys [datastore index migrations]} this + last-applied (last (ragtime/applied-migrations datastore index)) + opts (select-keys this [:reporter])] + (if last-applied + (do + (ragtime/rollback-last datastore index 1 opts) + (ragtime/migrate datastore last-applied) + (log/info "Successfully remigrated")) + (log/info "Applied migrations are nothing")))) + + (rollback [this options] + (let [{:keys [datastore index]} this + applied (ragtime/applied-migrations datastore index) + {:keys [n id] :or {n 1}} options + opts (select-keys this [:reporter])] + (if (seq applied) + (do + (if (seq id) + (ragtime/rollback-to datastore index id opts) + (ragtime/rollback-last datastore index n opts)) + (log/info "Successfully rolled back")) + (log/info "No migrations to roll back")))) + + (generate [this description] + (doseq [file-name (map #(.getName %) (generate* this description))] + (log/info "Generate" file-name))) + + task/Task + (task-name [this] :migration) + (spec [this] {:desc "Migration tasks"}) + + task/TaskContainer + (tasks [this] + [(task/task :migrate + (fn [_] + (migrate this)) + :desc "Runs migration") + + (task/task :rollback + (fn [{:keys [options]}] + (rollback this options)) + :desc "Rolls back the database" + :option-specs [["-n" "--n N" "How many changes to rollback" + :parse-fn #(Long/parseLong %) + :validate [#(re-matches #"^\d+$" %) "Must be integer"]] + ["-i" "--id ID" "Which id to rollback to" + :validate [seq "Must be non-empty string"]]]) + + (task/task :generate + (fn [env] + (generate this (get-in env [:arguments :description]))) + :desc "Generate migration files" + :argument-specs [["description" + :validate [seq "Must be non-empty string"]]])])) + +(defn new-ragtime [conf] + (map->Ragtime conf)) diff --git a/extra/test/zou/migrator/ragtime_test.clj b/extra/test/zou/migrator/ragtime_test.clj new file mode 100644 index 0000000..f2442e5 --- /dev/null +++ b/extra/test/zou/migrator/ragtime_test.clj @@ -0,0 +1,97 @@ +(ns zou.migrator.ragtime-test + (:require [clojure.java.io :as io] + [clojure.java.jdbc :as jdbc] + [clojure.string :as str] + [clojure.test :as t] + [zou.component :as c] + [zou.migrator.ragtime :as sut])) + +(def h2db-dir "/tmp/h2-zou.migrator.ragtime") +(def h2db-file (str h2db-dir "/h2db.sql")) + +(def test-conf + {:db (str "jdbc:h2:" h2db-file) + :ragtime {:zou/constructor 'zou.migrator.ragtime/new-ragtime + :zou/dependencies {:db :db} + :migrations-path "migrations" + :style :edn + :strategy #'ragtime.strategy/raise-error + :reporter #'ragtime.reporter/print}}) + +(defn setup [migrations] + (c/with-system [sys test-conf] + (dotimes [n 3] + (let [[edn] (sut/generate* (:ragtime sys) (str "migration_" n))] + (spit edn (format "{:up [\"create table test_%s(id integer);\"] :down [\"drop table test_%s;\"]}" n n)) + (swap! migrations conj edn))) + migrations)) + +(defn teardown [migrations] + (letfn [(delete-recur [func f] + (when (.isDirectory f) + (doseq [f' (.listFiles f)] + (func func f'))) + (io/delete-file f))] + (delete-recur delete-recur (io/file h2db-dir))) + (doseq [edn @migrations] + (io/delete-file edn))) + +(reset-meta! *ns* {}) +(t/use-fixtures :once + (fn [f] + (let [migrations (atom [])] + (setup migrations) + (f) + (teardown migrations)))) + +(defn find-test-tables [db] + (->> (jdbc/query db "select * from information_schema.tables") + (map :table_name) + (filter #(re-matches #"^TEST_\d+$" %)) + (into #{}))) + +(t/deftest ragtime-component-test + (t/testing "migrate" + (c/with-system [sys test-conf] + (t/is (= (find-test-tables (:db sys)) + #{})) + (let [output (-> (with-out-str (sut/migrate (:ragtime sys))) + str/split-lines)] + (doseq [o (take 3 output)] + (t/is (re-matches #"^Applying v_\d{17}_migration_\d+$" o)))) + + (t/is (= (find-test-tables (:db sys)) + #{"TEST_0" "TEST_1" "TEST_2"})))) + + (t/testing "rollback" + (t/testing "with n option" + (c/with-system [sys test-conf] + (sut/migrate (:ragtime sys)) + (t/is (= (find-test-tables (:db sys)) + #{"TEST_0" "TEST_1" "TEST_2"})) + + (let [output (-> (with-out-str (sut/rollback (:ragtime sys) {:n 2})) + str/split-lines)] + (doseq [o (take 2 output)] + (t/is (re-matches #"^Rolling back v_\d{17}_migration_\d+$" o)))) + + (t/is (= (find-test-tables (:db sys)) + #{"TEST_0"})))) + + + (t/testing "with id option" + (c/with-system [sys test-conf] + (sut/migrate (:ragtime sys)) + (t/is (= (find-test-tables (:db sys)) + #{"TEST_0" "TEST_1" "TEST_2"})) + + (let [id (-> (jdbc/query (:db sys) "select * from ragtime_migrations order by created_at") + second + :id) + output (-> (with-out-str (sut/rollback (:ragtime sys) {:id id})) + str/split-lines)] + (doseq [o (take 1 output)] + (t/is (re-matches #"^Rolling back v_\d{17}_migration_\d+$" o)))) + + (t/is (= (find-test-tables (:db sys)) + #{"TEST_0" "TEST_1"})))))) diff --git a/project.clj b/project.clj index 442d14a..89d1df6 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(def modules ["common" "component" "lib" "framework" "web" "db" "cljs-devel" "devel"]) +(def modules ["common" "component" "lib" "framework" "web" "db" "extra" "cljs-devel" "devel"]) (def modules+tpl (conj modules "lein-template")) (def modules+tpl+parent (conj modules+tpl ".")) @@ -10,7 +10,8 @@ [zou/lib :version] [zou/framework :version] [zou/web :version :exclusions [org.apache.commons/commons-compress]] - [zou/db :version]] + [zou/db :version] + [zou/extra :version]] :plugins [[lein-modules "0.3.11"]] :profiles {:coverage {:source-paths ~(subdir "src") :test-paths ~(subdir "test") @@ -49,7 +50,8 @@ [enlive "1.1.6"] [hiccup "1.0.5"] [stencil "0.5.0"] - [selmer "1.10.7"]] + [selmer "1.10.7"] + [ragtime "0.7.1"]] :plugins [[lein-midje "3.2.1"] [lein-file-replace "0.1.0"]] :env {:zou-env "dev"} From c5d84e8fbb50da47c168b349c28b542aaf26f559 Mon Sep 17 00:00:00 2001 From: ayato_p Date: Wed, 30 Aug 2017 15:11:40 +0900 Subject: [PATCH 2/2] fixup! Add migrator.ragtime component --- extra/project.clj | 4 +- extra/src/zou/migrator/ragtime.clj | 135 +++++++++++------------ extra/test/zou/migrator/ragtime_test.clj | 90 ++++++++++----- 3 files changed, 128 insertions(+), 101 deletions(-) diff --git a/extra/project.clj b/extra/project.clj index 17902c9..935b17f 100644 --- a/extra/project.clj +++ b/extra/project.clj @@ -5,5 +5,5 @@ [inflections "0.13.0"]] :plugins [[lein-modules "0.3.11"]] :profiles - {:provided {:dependencies [[ragtime "0.7.1"]]} - :dev {:resource-paths ["dev-resources"]}}) + {:dev {:dependencies [[zou/framework :version]]} + :provided {:dependencies [[ragtime "0.7.1"]]}}) diff --git a/extra/src/zou/migrator/ragtime.clj b/extra/src/zou/migrator/ragtime.clj index b54e0fb..93a8bf6 100644 --- a/extra/src/zou/migrator/ragtime.clj +++ b/extra/src/zou/migrator/ragtime.clj @@ -7,15 +7,38 @@ [zou.component :as c] [zou.logging :as log] [zou.task :as task]) - (:import java.nio.file.FileSystems - java.time.format.DateTimeFormatter + (:import java.time.format.DateTimeFormatter java.time.LocalDateTime)) -(defprotocol IMigrator - (migrate [this]) - (remigrate [this]) - (rollback [this opts]) - (generate [this description])) +(defn validate-component-state [component] + (let [{:keys [datastore index migrations]} component] + (when-not (and datastore index migrations) + (throw (ex-info "Ragtime component has not been initialized" {:keys [:datastore :index :migrations]}))) + component)) + +(defn migrate [ragtime-component] + (let [{:keys [datastore index migrations]} (validate-component-state ragtime-component) + applied (ragtime/applied-migrations datastore index) + opts (select-keys ragtime-component [:strategy :reporter])] + (if (> (count index) (count applied)) + (do (ragtime/migrate-all datastore index migrations opts) + (log/info "Successfully migrated")) + (log/info "No migrations to apply")))) + +(defn rollback [ragtime-component options] + (let [{:keys [datastore index]} (validate-component-state ragtime-component) + applied (ragtime/applied-migrations datastore index) + {:keys [n id]} options + n (if (nil? n) 1 n) + opts (select-keys ragtime-component [:reporter])] + (if (and (seq applied) + (or (seq id) (integer? n))) + (do + (if (seq id) + (ragtime/rollback-to datastore index id opts) + (ragtime/rollback-last datastore index n opts)) + (log/info "Successfully rolled back")) + (log/info "No migrations to roll back")))) (defn- gen-file-name [description] (let [f (DateTimeFormatter/ofPattern "yyyyMMddHHmmssSSS") @@ -24,87 +47,59 @@ inf/underscore (str "v_" (.format d f) "_")))) -(defn- gen-sql-files [path description] - (let [separator (.getSeparator (FileSystems/getDefault)) - prefix (str path separator (gen-file-name description)) - up-sql (io/file (str prefix ".up.sql")) - down-sql (io/file (str prefix ".down.sql"))] +(defmulti generate-files (fn [style path description] style)) + +(defmethod generate-files :default + [style _ _] + (throw (ex-info "Invalid parameter" {:style style}))) + +(defmethod generate-files :sql + [_ path description] + (let [prefix (gen-file-name description) + up-sql (io/file path (str prefix ".up.sql")) + down-sql (io/file path (str prefix ".down.sql"))] (spit up-sql "") (spit down-sql "") [up-sql down-sql])) -(defn- gen-edn-files [path description] - (let [separator (.getSeparator (FileSystems/getDefault)) - prefix (str path separator (gen-file-name description)) - edn (io/file (str prefix ".edn"))] +(defmethod generate-files :edn + [_ path description] + (let [prefix (gen-file-name description) + edn (io/file path (str prefix ".edn"))] (spit edn (str "{:up []" (System/lineSeparator) " :down []}")) [edn])) (defn generate* [migrator description] (let [{:keys [migrations-path style]} migrator - path (some-> (io/resource migrations-path) - io/file - .getPath)] - (when-not path - (log/warnf "Migrations directory `%s` does not exist" migrations-path)) - (case style - :edn (gen-edn-files path description) - :sql (gen-sql-files path description) - (throw (ex-info "Invalid parameter" {:style style}))))) + resource (io/resource migrations-path)] + (if (and resource (= (.getProtocol resource) "file")) + (generate-files style (-> resource io/file .getPath) description) + (throw (ex-info (format "Migrations directory `%s` %s" + migrations-path + (if resource "not a directory" "does not exist")) + {:migrations-path migrations-path}))))) + +(defn generate [ragtime-component description] + (doseq [file-name (map #(.getName %) (-> (validate-component-state ragtime-component) + (generate* description)))] + (log/info "Generate" file-name))) (defrecord Ragtime [db] c/Lifecycle (start [this] (let [{:keys [migrations-path]} this migrations (jdbc/load-resources migrations-path)] - (-> this - (assoc :migrations migrations) - (assoc :datastore (jdbc/sql-database db)) - (assoc :index (ragtime/into-index migrations))))) + (assoc this + :migrations migrations + :datastore (->> (select-keys this [:migrations-table]) + (jdbc/sql-database db)) + :index (ragtime/into-index migrations)))) (stop [this] (dissoc this :migrations :datastore :index)) - IMigrator - (migrate [this] - (let [{:keys [datastore index migrations]} this - applied (ragtime/applied-migrations datastore index) - opts (select-keys this [:strategy :reporter])] - (if (> (count index) (count applied)) - (do (ragtime/migrate-all datastore index migrations opts) - (log/info "Successfully migrated")) - (log/info "No migrations to apply")))) - - (remigrate [this] - (let [{:keys [datastore index migrations]} this - last-applied (last (ragtime/applied-migrations datastore index)) - opts (select-keys this [:reporter])] - (if last-applied - (do - (ragtime/rollback-last datastore index 1 opts) - (ragtime/migrate datastore last-applied) - (log/info "Successfully remigrated")) - (log/info "Applied migrations are nothing")))) - - (rollback [this options] - (let [{:keys [datastore index]} this - applied (ragtime/applied-migrations datastore index) - {:keys [n id] :or {n 1}} options - opts (select-keys this [:reporter])] - (if (seq applied) - (do - (if (seq id) - (ragtime/rollback-to datastore index id opts) - (ragtime/rollback-last datastore index n opts)) - (log/info "Successfully rolled back")) - (log/info "No migrations to roll back")))) - - (generate [this description] - (doseq [file-name (map #(.getName %) (generate* this description))] - (log/info "Generate" file-name))) - task/Task - (task-name [this] :migration) - (spec [this] {:desc "Migration tasks"}) + (task-name [this] :ragtime) + (spec [this] {:desc "Ragtime tasks"}) task/TaskContainer (tasks [this] @@ -119,7 +114,7 @@ :desc "Rolls back the database" :option-specs [["-n" "--n N" "How many changes to rollback" :parse-fn #(Long/parseLong %) - :validate [#(re-matches #"^\d+$" %) "Must be integer"]] + :validate [pos? "Must be integer"]] ["-i" "--id ID" "Which id to rollback to" :validate [seq "Must be non-empty string"]]]) diff --git a/extra/test/zou/migrator/ragtime_test.clj b/extra/test/zou/migrator/ragtime_test.clj index f2442e5..0540417 100644 --- a/extra/test/zou/migrator/ragtime_test.clj +++ b/extra/test/zou/migrator/ragtime_test.clj @@ -1,22 +1,31 @@ (ns zou.migrator.ragtime-test (:require [clojure.java.io :as io] [clojure.java.jdbc :as jdbc] - [clojure.string :as str] [clojure.test :as t] [zou.component :as c] - [zou.migrator.ragtime :as sut])) + [zou.framework.bootstrap :as boot] + [zou.framework.entrypoint.proto :as ep] + [zou.logging :as log] + [zou.migrator.ragtime :as sut]) + (:import java.nio.file.Files)) -(def h2db-dir "/tmp/h2-zou.migrator.ragtime") -(def h2db-file (str h2db-dir "/h2db.sql")) +(def empty-file-attrs (into-array java.nio.file.attribute.FileAttribute [])) + +(def h2db-file (Files/createTempFile "h2db-" ".sql" empty-file-attrs)) + +(defn log [_ op id] + (case op + :up (log/info "Applying" id) + :down (log/info "Rolling back" id))) (def test-conf - {:db (str "jdbc:h2:" h2db-file) + {:db (str "jdbc:h2:" (str h2db-file)) :ragtime {:zou/constructor 'zou.migrator.ragtime/new-ragtime :zou/dependencies {:db :db} :migrations-path "migrations" :style :edn :strategy #'ragtime.strategy/raise-error - :reporter #'ragtime.reporter/print}}) + :reporter #'log}}) (defn setup [migrations] (c/with-system [sys test-conf] @@ -27,17 +36,13 @@ migrations)) (defn teardown [migrations] - (letfn [(delete-recur [func f] - (when (.isDirectory f) - (doseq [f' (.listFiles f)] - (func func f'))) - (io/delete-file f))] - (delete-recur delete-recur (io/file h2db-dir))) + (c/with-system [sys test-conf] + (jdbc/execute! (:db sys) "drop all objects;")) (doseq [edn @migrations] (io/delete-file edn))) (reset-meta! *ns* {}) -(t/use-fixtures :once +(t/use-fixtures :each (fn [f] (let [migrations (atom [])] (setup migrations) @@ -50,19 +55,34 @@ (filter #(re-matches #"^TEST_\d+$" %)) (into #{}))) -(t/deftest ragtime-component-test +(t/deftest validate-component-state-test + (t/testing "validate-component-state" + (c/with-system [sys test-conf] + (t/is (= (sut/validate-component-state (:ragtime sys)) + (:ragtime sys))) + + (t/is (thrown? + clojure.lang.ExceptionInfo + (= (-> (:ragtime sys) + (dissoc :index) + sut/validate-component-state) + (:ragtime sys))))))) + +(t/deftest migrate-test (t/testing "migrate" (c/with-system [sys test-conf] (t/is (= (find-test-tables (:db sys)) #{})) - (let [output (-> (with-out-str (sut/migrate (:ragtime sys))) - str/split-lines)] - (doseq [o (take 3 output)] + + (log/with-test-logger + (sut/migrate (:ragtime sys)) + (doseq [o (map :msg (take 3 @log/*test-logger-entries*))] (t/is (re-matches #"^Applying v_\d{17}_migration_\d+$" o)))) (t/is (= (find-test-tables (:db sys)) - #{"TEST_0" "TEST_1" "TEST_2"})))) + #{"TEST_0" "TEST_1" "TEST_2"}))))) +(t/deftest rollback-test (t/testing "rollback" (t/testing "with n option" (c/with-system [sys test-conf] @@ -70,13 +90,25 @@ (t/is (= (find-test-tables (:db sys)) #{"TEST_0" "TEST_1" "TEST_2"})) - (let [output (-> (with-out-str (sut/rollback (:ragtime sys) {:n 2})) - str/split-lines)] - (doseq [o (take 2 output)] + (log/with-test-logger + (sut/rollback (:ragtime sys) {:n 2}) + (doseq [o (map :msg (take 2 @log/*test-logger-entries*))] (t/is (re-matches #"^Rolling back v_\d{17}_migration_\d+$" o)))) (t/is (= (find-test-tables (:db sys)) - #{"TEST_0"})))) + #{"TEST_0"}))) + + (c/with-system [sys test-conf] + (sut/migrate (:ragtime sys)) + (t/is (= (find-test-tables (:db sys)) + #{"TEST_0" "TEST_1" "TEST_2"})) + + (log/with-test-logger + (sut/rollback (:ragtime sys) {:n nil}) + (log/logged? #"^Rolling back v_\d{17}_migration_\d+$")) + + (t/is (= (find-test-tables (:db sys)) + #{"TEST_0" "TEST_1"})))) (t/testing "with id option" @@ -87,11 +119,11 @@ (let [id (-> (jdbc/query (:db sys) "select * from ragtime_migrations order by created_at") second - :id) - output (-> (with-out-str (sut/rollback (:ragtime sys) {:id id})) - str/split-lines)] - (doseq [o (take 1 output)] - (t/is (re-matches #"^Rolling back v_\d{17}_migration_\d+$" o)))) + :id)] + (log/with-test-logger + (sut/rollback (:ragtime sys) {:id id}) + (doseq [o (map :msg (take 1 @log/*test-logger-entries*))] + (t/is (re-matches #"^Rolling back v_\d{17}_migration_\d+$" o)))) - (t/is (= (find-test-tables (:db sys)) - #{"TEST_0" "TEST_1"})))))) + (t/is (= (find-test-tables (:db sys)) + #{"TEST_0" "TEST_1"})))))))