(ns tbd.todo.db-test (:require [clojure.java.io :as io] [clojure.test :refer [deftest is testing]] [tbd.todo.db :as db]) (:import (java.nio.file Files) (java.nio.file.attribute FileAttribute))) (defn temp-db-path "A path for a database file inside a fresh temp directory (which `open` has to create a subdirectory of, as with the default data/todo.db)." [] (str (Files/createTempDirectory "tbd-todo" (make-array FileAttribute 0)) "/data/todo.db")) (defn todo [owner-id title] {:owner-id owner-id :title title :done false :created-at 1000 :updated-at 1000}) (deftest env-config-test (is (= {:db-path "data/todo.db"} (db/env-config {}))) (is (= {:db-path "data/todo.db"} (db/env-config {"TODO_DB_PATH" " "}))) (is (= {:db-path "/var/todo.db"} (db/env-config {"TODO_DB_PATH" " /var/todo.db "})))) (deftest open-test (testing "creates the file and its directory" (let [path (temp-db-path)] (db/open path) (is (.exists (io/file path))))) (testing "reopening keeps the data" (let [path (temp-db-path) saved (db/insert! (db/open path) (todo 7 "buy milk"))] (is (= [saved] (db/list-todos (db/open path) 7))))) (testing "a path is required" (doseq [path [nil "" " "]] (is (thrown? clojure.lang.ExceptionInfo (db/open path)))))) (deftest crud-test (let [ds (db/open (temp-db-path)) saved (db/insert! ds (todo 7 "buy milk")) id (:id saved)] (testing "insert returns the stored todo with an id" (is (= (assoc (todo 7 "buy milk") :id id) saved)) (is (not= id (:id (db/insert! ds (todo 7 "walk dog")))))) (testing "find and list" (is (= saved (db/find-todo ds 7 id))) (is (= ["buy milk" "walk dog"] (map :title (db/list-todos ds 7))))) (testing "update" (is (= (assoc saved :title "buy oat milk" :done true :updated-at 2000) (db/update! ds 7 (assoc saved :title "buy oat milk" :done true :updated-at 2000)))) (is (= true (:done (db/find-todo ds 7 id))))) (testing "missing ids" (is (nil? (db/find-todo ds 7 999))) (is (nil? (db/update! ds 7 (assoc saved :id 999)))) (is (false? (db/delete! ds 7 999)))) (testing "delete" (is (true? (db/delete! ds 7 id))) (is (nil? (db/find-todo ds 7 id))) (is (false? (db/delete! ds 7 id)))))) (deftest owner-scoping-test (let [ds (db/open (temp-db-path)) mine (db/insert! ds (todo 7 "mine")) theirs (db/insert! ds (todo 8 "theirs"))] (is (= [mine] (db/list-todos ds 7))) (is (= [theirs] (db/list-todos ds 8))) (is (= [] (db/list-todos ds 9))) (testing "another owner's todo can't be found, changed, or deleted by its id" (is (nil? (db/find-todo ds 8 (:id mine)))) (is (nil? (db/update! ds 8 (assoc mine :title "hijacked")))) (is (false? (db/delete! ds 8 (:id mine)))) (is (= mine (db/find-todo ds 7 (:id mine)))))))