better testing documentation; adds basic subscription functionality back to base-subscribed-views; beginning of bulk update functionality; moved all database table creation into SQL script
This commit is contained in:
parent
d63fc95ff1
commit
e263ed10c6
15
README.md
15
README.md
|
@ -12,7 +12,20 @@ TODO
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
You can run all tests in the repl with
|
You will need to set up the test db to run the tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ psql -Upostgres < test/views/test_db.sql
|
||||||
|
CREATE ROLE
|
||||||
|
CREATE DATABASE
|
||||||
|
$
|
||||||
|
```
|
||||||
|
|
||||||
|
This will create a role `views_user` and a database owned by that user called `views_test`.
|
||||||
|
|
||||||
|
(You can change the database settings if you'd like by editing that file and checking the config in `test/views/fixtures.clj`.)
|
||||||
|
|
||||||
|
Then, to run all tests in the repl:
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
user=> (require '[views.all-tests :as at])
|
user=> (require '[views.all-tests :as at])
|
||||||
|
|
|
@ -11,7 +11,9 @@
|
||||||
[org.clojure/core.async "0.1.303.0-886421-alpha"]
|
[org.clojure/core.async "0.1.303.0-886421-alpha"]
|
||||||
[org.clojure/java.jdbc "0.3.3"]
|
[org.clojure/java.jdbc "0.3.3"]
|
||||||
[honeysql "0.4.3"]
|
[honeysql "0.4.3"]
|
||||||
[org.postgresql/postgresql "9.2-1003-jdbc4"]]
|
[edl "0.1.0"]
|
||||||
|
[org.postgresql/postgresql "9.2-1003-jdbc4"]
|
||||||
|
[clj-logging-config "1.9.10"]]
|
||||||
|
|
||||||
:profiles {:test {:dependencies [[org.clojure/tools.nrepl "0.2.3"]
|
:profiles {:test {:dependencies [[org.clojure/tools.nrepl "0.2.3"]
|
||||||
[environ "0.4.0"]
|
[environ "0.4.0"]
|
||||||
|
|
|
@ -1,36 +1,85 @@
|
||||||
(ns views.base-subscribed-views
|
(ns views.base-subscribed-views
|
||||||
(:require
|
(:require
|
||||||
[views.db.core :refer [initial-views]]
|
[views.db.load :refer [initial-views]]
|
||||||
[views.subscribed-views :refer [SubscribedViews subscriber-key-fn prefix-fn send-message]]
|
[views.subscribed-views :refer [SubscribedViews subscriber-key-fn prefix-fn]]
|
||||||
[views.subscriptions :as vs :refer [add-subscriptions!]]
|
[views.subscriptions :as vs :refer [add-subscriptions! remove-subscription! subscriptions-for]]
|
||||||
[clojure.tools.logging :refer [debug info warn error]]
|
[clojure.tools.logging :refer [debug info warn error]]
|
||||||
[clojure.core.async :refer [put! <! go thread]]))
|
[clojure.core.async :refer [put! <! go thread]]))
|
||||||
|
|
||||||
(defrecord BaseSubscribedViews [db templates delta-broadcast-chan]
|
(defn view-filter
|
||||||
|
"Takes a subscription request msg, a collection of view-sigs and
|
||||||
|
the config templates hash-map for an app. Checks if there is
|
||||||
|
a global filter-fn in the hash-map metadata and checks against
|
||||||
|
that if it exists, as well as against any existing filter
|
||||||
|
functions for individual template config entries. Template
|
||||||
|
config hash-map entries can specify a filter-fn using the key
|
||||||
|
:filter-fn, and the global filter-fn is the same, only on
|
||||||
|
the config meta-data (i.e. (with-meta templates {:filter-fn ...}))
|
||||||
|
|
||||||
|
By default throws an exception if no filters are present.
|
||||||
|
By passing in {:unsafe true} in opts, this can be overridden."
|
||||||
|
[msg view-sigs templates & opts]
|
||||||
|
(let [global-filter-fn (:filter-fn (meta templates))]
|
||||||
|
(filterv
|
||||||
|
#(let [filter-fn (:filter-fn (get templates (first %)))]
|
||||||
|
(cond
|
||||||
|
(and filter-fn global-filter-fn)
|
||||||
|
(and (global-filter-fn msg %) (filter-fn msg %))
|
||||||
|
|
||||||
|
filter-fn
|
||||||
|
(filter-fn msg %)
|
||||||
|
|
||||||
|
global-filter-fn
|
||||||
|
(global-filter-fn msg %)
|
||||||
|
|
||||||
|
:else
|
||||||
|
(if (-> opts first :unsafe)
|
||||||
|
(do (warn "YOU ARE RUNNING IN UNSAFE MODE, AND NO FILTERS ARE PRESENT FOR VIEW-SIG: " %)
|
||||||
|
true)
|
||||||
|
(throw (Exception. (str "No filter set for view " %))))))
|
||||||
|
view-sigs)))
|
||||||
|
|
||||||
|
(defn send-message
|
||||||
|
[this address msg]
|
||||||
|
(warn "IMPLEMENT ME. Got message " msg " sent to address " address))
|
||||||
|
|
||||||
|
(deftype BaseSubscribedViews [db templates send-fn broadcast-fn subscribed-views-fn opts]
|
||||||
SubscribedViews
|
SubscribedViews
|
||||||
(subscribe-views
|
(subscribe-views
|
||||||
[this sub-req]
|
[this sub-req]
|
||||||
;; (let [view-sigs (view-filter sub-req (:body sub-req) templates)] ; this is where security comes in.
|
|
||||||
(let [subscriber-key (subscriber-key-fn this sub-req)
|
(let [subscriber-key (subscriber-key-fn this sub-req)
|
||||||
view-sigs (:view-sigs sub-req)]
|
view-sigs (view-filter sub-req (:views sub-req) templates opts)] ; this is where security comes in.
|
||||||
(info "Subscribing views: " view-sigs)
|
(info "Subscribing views: " view-sigs " for subscriber " subscriber-key)
|
||||||
(when (seq view-sigs)
|
(when (seq view-sigs)
|
||||||
(add-subscriptions! subscriber-key view-sigs (prefix-fn this sub-req))
|
(let [subbed-views (if-let [prefix (prefix-fn this sub-req)]
|
||||||
|
(add-subscriptions! subscriber-key view-sigs templates prefix)
|
||||||
|
(add-subscriptions! subscriber-key view-sigs templates))]
|
||||||
(thread
|
(thread
|
||||||
(->> (initial-views db view-sigs templates @vs/compiled-views)
|
(->> (initial-views db view-sigs templates subbed-views)
|
||||||
(send-message this subscriber-key))))))
|
((if send-fn send-fn send-message) this subscriber-key)))))))
|
||||||
|
|
||||||
(unsubscribe-views [this unsub-req])
|
(unsubscribe-views
|
||||||
|
[this unsub-req]
|
||||||
|
(let [subscriber-key (subscriber-key-fn this unsub-req)
|
||||||
|
view-sigs (:views unsub-req)]
|
||||||
|
(info "Unsubscribing views: " view-sigs " for subscriber " subscriber-key)
|
||||||
|
(if-let [prefix (prefix-fn this unsub-req)]
|
||||||
|
(doseq [vs view-sigs] (remove-subscription! subscriber-key vs prefix))
|
||||||
|
(doseq [vs view-sigs] (remove-subscription! subscriber-key vs)))))
|
||||||
|
|
||||||
(disconnect [this disconnect-req])
|
(disconnect [this disconnect-req]
|
||||||
|
(let [subscriber-key (:subscriber-key disconnect-req)
|
||||||
(subscribed-views [this] @vs/compiled-views)
|
prefix (prefix-fn this disconnect-req)
|
||||||
|
view-sigs (if prefix (subscriptions-for subscriber-key prefix) (subscriptions-for subscriber-key))]
|
||||||
(broadcast-deltas [this fdb views-with-deltas])
|
(if prefix
|
||||||
|
(doseq [vs view-sigs] (remove-subscription! subscriber-key vs prefix))
|
||||||
(send-message [this address msg]
|
(doseq [vs view-sigs] (remove-subscription! subscriber-key vs)))))
|
||||||
(warn "IMPLEMENT ME. Got message " msg " sent to address " address))
|
|
||||||
|
|
||||||
(subscriber-key-fn [this msg] (:subscriber-key msg))
|
(subscriber-key-fn [this msg] (:subscriber-key msg))
|
||||||
|
|
||||||
(prefix-fn [this msg] nil))
|
(prefix-fn [this msg] nil)
|
||||||
|
|
||||||
|
;; DB interaction
|
||||||
|
(subscribed-views [this] @vs/compiled-views)
|
||||||
|
|
||||||
|
(broadcast-deltas [this fdb views-with-deltas]))
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
(merge {:args (rest view-sig)
|
(merge {:args (rest view-sig)
|
||||||
:view-sig view-sig
|
:view-sig view-sig
|
||||||
:view compiled-view
|
:view compiled-view
|
||||||
|
:bulk-update? (:bulk-update? (meta view-template))
|
||||||
:tables (set (vh/extract-tables compiled-view))}
|
:tables (set (vh/extract-tables compiled-view))}
|
||||||
(compile-dummy-view view-template (rest view-sig)))))
|
(compile-dummy-view view-template (rest view-sig)))))
|
||||||
|
|
||||||
|
@ -444,7 +445,7 @@
|
||||||
(broadcast-deltas ~subscribed-views ~(second binding) @deltas#)
|
(broadcast-deltas ~subscribed-views ~(second binding) @deltas#)
|
||||||
result#))))))
|
result#))))))
|
||||||
|
|
||||||
(defn vaction!
|
(defn vexec!
|
||||||
"Used to perform arbitrary insert/update/delete actions on the database,
|
"Used to perform arbitrary insert/update/delete actions on the database,
|
||||||
while ensuring that view deltas are appropriately checked and calculated
|
while ensuring that view deltas are appropriately checked and calculated
|
||||||
for the currently registered views as reported by a type implementing
|
for the currently registered views as reported by a type implementing
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
(reduce
|
(reduce
|
||||||
(fn [results nv]
|
(fn [results nv]
|
||||||
(->> (get subscribed-views nv)
|
(->> (get subscribed-views nv)
|
||||||
:view-map
|
:view
|
||||||
(view-query db)
|
(view-query db)
|
||||||
(into [])
|
(into [])
|
||||||
(post-process-result-set nv templates)
|
(post-process-result-set nv templates)
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
(ns views.filters)
|
|
||||||
|
|
||||||
(defn view-filter
|
|
||||||
"Takes a subscription request msg, a collection of view-sigs and
|
|
||||||
the config templates hash-map for an app. Checks if there is
|
|
||||||
a global filter-fn in the hash-map metadata and checks against
|
|
||||||
that if it exists, as well as against any existing filter
|
|
||||||
functions for individual template config entries. Template
|
|
||||||
config hash-map entries can specify a filter-fn using the key
|
|
||||||
:filter-fn, and the global filter-fn is the same, only on
|
|
||||||
the config meta-data (i.e. (with-meta templates {:filter-fn ...}))
|
|
||||||
|
|
||||||
By default throws an exception if no filters are present.
|
|
||||||
By passing in {:unsafe true} in opts, this can be overridden."
|
|
||||||
[msg view-sigs templates & opts]
|
|
||||||
(let [global-filter-fn (:filter-fn (meta templates))]
|
|
||||||
(filterv
|
|
||||||
#(let [filter-fn (:filter-fn (get templates (first %)))]
|
|
||||||
(cond
|
|
||||||
(and filter-fn global-filter-fn)
|
|
||||||
(and (global-filter-fn msg %) (filter-fn msg %))
|
|
||||||
|
|
||||||
filter-fn
|
|
||||||
(filter-fn msg %)
|
|
||||||
|
|
||||||
global-filter-fn
|
|
||||||
(global-filter-fn msg %)
|
|
||||||
|
|
||||||
:else
|
|
||||||
(if (-> opts first :unsafe)
|
|
||||||
(do (warn "YOU ARE RUNNING IN UNSAFE MODE, AND NO FILTERS ARE PRESENT FOR VIEW-SIG: " %)
|
|
||||||
true)
|
|
||||||
(throw (Exception. (str "No filter set for view " %))))))
|
|
||||||
view-sigs)))
|
|
|
@ -5,7 +5,6 @@
|
||||||
(subscribe-views [this sub-request])
|
(subscribe-views [this sub-request])
|
||||||
(unsubscribe-views [this unsub-request])
|
(unsubscribe-views [this unsub-request])
|
||||||
(disconnect [this disconnect-request])
|
(disconnect [this disconnect-request])
|
||||||
(send-message [this address msg])
|
|
||||||
(subscriber-key-fn [this msg])
|
(subscriber-key-fn [this msg])
|
||||||
(prefix-fn [this msg])
|
(prefix-fn [this msg])
|
||||||
|
|
||||||
|
|
|
@ -36,10 +36,17 @@
|
||||||
([subscriber-key view-sigs templates]
|
([subscriber-key view-sigs templates]
|
||||||
(add-subscriptions! subscriber-key view-sigs templates nil))
|
(add-subscriptions! subscriber-key view-sigs templates nil))
|
||||||
([subscriber-key view-sigs templates prefix]
|
([subscriber-key view-sigs templates prefix]
|
||||||
(doseq [vs view-sigs]
|
(last (mapv
|
||||||
(if prefix
|
#(if prefix
|
||||||
(add-subscription! subscriber-key vs templates prefix)
|
(add-subscription! subscriber-key % templates prefix)
|
||||||
(add-subscription! subscriber-key vs templates)))))
|
(add-subscription! subscriber-key % templates))
|
||||||
|
view-sigs))))
|
||||||
|
|
||||||
|
(defn subscriptions-for
|
||||||
|
([subscriber-key]
|
||||||
|
(reduce #(if (contains? (second %2) subscriber-key) (conj %1 (first %2)) %1) [] @subscribed-views))
|
||||||
|
([subscriber-key prefix]
|
||||||
|
(reduce #(if (contains? (second %2) subscriber-key) (conj %1 (first %2)) %1) [] (get @subscribed-views prefix))))
|
||||||
|
|
||||||
(defn subscribed-to
|
(defn subscribed-to
|
||||||
([view-sig]
|
([view-sig]
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
(:require
|
(:require
|
||||||
[clojure.test :refer [run-tests]]
|
[clojure.test :refer [run-tests]]
|
||||||
[views.subscriptions-test]
|
[views.subscriptions-test]
|
||||||
|
[views.base-subscribed-views-test]
|
||||||
[views.db.core-test]
|
[views.db.core-test]
|
||||||
[views.db.honeysql-test]
|
[views.db.honeysql-test]
|
||||||
[views.db.load-test]))
|
[views.db.load-test]))
|
||||||
|
@ -9,6 +10,7 @@
|
||||||
(defn run-all-tests
|
(defn run-all-tests
|
||||||
[]
|
[]
|
||||||
(run-tests 'views.subscriptions-test
|
(run-tests 'views.subscriptions-test
|
||||||
|
'views.base-subscribed-views-test
|
||||||
'views.db.core-test
|
'views.db.core-test
|
||||||
'views.db.honeysql-test
|
'views.db.honeysql-test
|
||||||
'views.db.load-test))
|
'views.db.load-test))
|
||||||
|
|
45
test/views/base_subscribed_views_test.clj
Normal file
45
test/views/base_subscribed_views_test.clj
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
(ns views.base-subscribed-views-test
|
||||||
|
(:require
|
||||||
|
[views.base-subscribed-views :as bsv] ; :refer [BaseSubscribedViews]]
|
||||||
|
[views.subscribed-views :refer [SubscribedViews subscriber-key-fn prefix-fn subscribe-views unsubscribe-views disconnect]]
|
||||||
|
[views.subscriptions :as vs :refer [subscribed-to?]]
|
||||||
|
[views.fixtures :as vf]
|
||||||
|
[clojure.test :refer [use-fixtures deftest is]]
|
||||||
|
[clojure.java.jdbc :as j]
|
||||||
|
[clj-logging-config.log4j :refer [set-logger! set-loggers!]])
|
||||||
|
(:import
|
||||||
|
[views.base_subscribed_views BaseSubscribedViews]))
|
||||||
|
|
||||||
|
(set-loggers! "views.base-subscribed-views" {:level :error})
|
||||||
|
|
||||||
|
(defn- subscription-fixtures!
|
||||||
|
[f]
|
||||||
|
(reset! vs/subscribed-views {})
|
||||||
|
(reset! vs/compiled-views {})
|
||||||
|
(f))
|
||||||
|
|
||||||
|
(use-fixtures :each vf/database-fixtures! subscription-fixtures!)
|
||||||
|
|
||||||
|
(deftest subscribes-and-dispatches-initial-view-result-set
|
||||||
|
(let [send-fn #(is (and (= %2 1) (= %3 {[:users] []})))
|
||||||
|
base-subbed-views (BaseSubscribedViews. vf/db vf/templates send-fn nil nil {:unsafe true})]
|
||||||
|
(subscribe-views base-subbed-views {:subscriber-key 1 :views [[:users]]})))
|
||||||
|
|
||||||
|
(deftest unsubscribes-view
|
||||||
|
(let [base-subbed-views (BaseSubscribedViews. vf/db vf/templates nil nil nil {:unsafe true})]
|
||||||
|
(subscribe-views base-subbed-views {:subscriber-key 1 :views [[:users]]})
|
||||||
|
(unsubscribe-views base-subbed-views {:subscriber-key 1 :views [[:users]]})
|
||||||
|
(is (not (subscribed-to? 1 [:users])))))
|
||||||
|
|
||||||
|
(deftest filters-subscription-requests
|
||||||
|
(let [templates (assoc-in vf/templates [:users :filter-fn] (fn [msg _] (:authorized? msg)))
|
||||||
|
base-subbed-views (BaseSubscribedViews. vf/db templates nil nil nil nil)]
|
||||||
|
(subscribe-views base-subbed-views {:subscriber-key 1 :views [[:users]]})
|
||||||
|
(is (not (subscribed-to? 1 [:users])))))
|
||||||
|
|
||||||
|
(deftest removes-all-subscriptions-on-disconnect
|
||||||
|
(let [base-subbed-views (BaseSubscribedViews. vf/db vf/templates nil nil nil {:unsafe true})]
|
||||||
|
(subscribe-views base-subbed-views {:subscriber-key 1 :views [[:users][:user-posts 1]]})
|
||||||
|
(disconnect base-subbed-views {:subscriber-key 1})
|
||||||
|
(is (not (subscribed-to? 1 [:user-posts 1])))
|
||||||
|
(is (not (subscribed-to? 1 [:users])))))
|
|
@ -1,9 +1,14 @@
|
||||||
(ns views.db.core-test
|
(ns views.db.core-test
|
||||||
(:require
|
(:require
|
||||||
[clojure.test :refer [deftest is run-tests]]
|
[clojure.test :refer [deftest is run-tests]]
|
||||||
|
[edl.core :refer [defschema]]
|
||||||
[honeysql.core :as hsql]
|
[honeysql.core :as hsql]
|
||||||
[honeysql.helpers :as hh]
|
[honeysql.helpers :as hh]
|
||||||
[views.db.core :as vdb]))
|
[views.fixtures :as vf]
|
||||||
|
[views.db.core :as vdb]
|
||||||
|
[views.base-subscribed-views :as bsv])
|
||||||
|
(:import
|
||||||
|
[views.base_subscribed_views BaseSubscribedViews]))
|
||||||
|
|
||||||
(defn join-test-template
|
(defn join-test-template
|
||||||
[id val3]
|
[id val3]
|
||||||
|
@ -53,14 +58,6 @@
|
||||||
(is (= (hsql/format check-template)
|
(is (= (hsql/format check-template)
|
||||||
["SELECT f.id, f.val3 FROM foo f INNER JOIN bar b ON b.id = f.b_id LEFT JOIN baz ba ON ba.id = b.ba_id RIGHT JOIN qux q ON q.id = ba.q_id WHERE (b.id = 123 AND f.val2 = ?)" "constant"]))))
|
["SELECT f.id, f.val3 FROM foo f INNER JOIN bar b ON b.id = f.b_id LEFT JOIN baz ba ON ba.id = b.ba_id RIGHT JOIN qux q ON q.id = ba.q_id WHERE (b.id = 123 AND f.val2 = ?)" "constant"]))))
|
||||||
|
|
||||||
;; ;; Not meaningful at this point perhaps...view-check-template shouldn't
|
|
||||||
;; ;; get handed an action that doesn't have a related table in the first place...?
|
|
||||||
;; (deftest removes-non-related-tables
|
|
||||||
;; (let [update-bar (update-bar-template "foo" [:= :id 123])
|
|
||||||
;; vm (vdb/view-map no-where-view-template [:no-where])
|
|
||||||
;; check-template (:view-check (vdb/view-check-template vm update-bar))]
|
|
||||||
;; (is (nil? check-template))))
|
|
||||||
|
|
||||||
(deftest creates-collection-of-views-to-check
|
(deftest creates-collection-of-views-to-check
|
||||||
(let [views [(vdb/view-map no-where-view-template [:no-where]) ; no :bar
|
(let [views [(vdb/view-map no-where-view-template [:no-where]) ; no :bar
|
||||||
(vdb/view-map no-where-view-template [:no-where]) ; no :bar
|
(vdb/view-map no-where-view-template [:no-where]) ; no :bar
|
||||||
|
@ -75,4 +72,17 @@
|
||||||
;; and 1 for *both* the joint-test-templates.
|
;; and 1 for *both* the joint-test-templates.
|
||||||
(is (= (count checked-views) 2))))
|
(is (= (count checked-views) 2))))
|
||||||
|
|
||||||
|
;; What is this for?
|
||||||
(def left-join-example (hsql/build :select [:R.a :S.C] :from :R :left-join [:S [:= :R.B :S.B]] :where [:!= :S.C 20]))
|
(def left-join-example (hsql/build :select [:R.a :S.C] :from :R :left-join [:S [:= :R.B :S.B]] :where [:!= :S.C 20]))
|
||||||
|
|
||||||
|
(deftest notes-view-map-as-no-delta-calc
|
||||||
|
(let [tmpl (with-meta vf/users-tmpl {:bulk-update? true})]
|
||||||
|
(is (:bulk-update? (vdb/view-map tmpl [:users])))))
|
||||||
|
|
||||||
|
(defschema schema vf/db "public")
|
||||||
|
|
||||||
|
;; (deftest sends-entire-view-on-every-update-with-bulk-update
|
||||||
|
;; (let [tmpl (with-meta vf/users-tmpl {:bulk-update? true})
|
||||||
|
;; vm (vdb/view-map tmpl [:users])
|
||||||
|
;; bsv (BaseSubscribedViews. vf/db
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
(defn subscribed-views
|
(defn subscribed-views
|
||||||
[]
|
[]
|
||||||
{[:users] {:view-map ((get-in templates [:users :fn]))}})
|
{[:users] {:view ((get-in templates [:users :fn]))}})
|
||||||
|
|
||||||
(deftest initializes-views
|
(deftest initializes-views
|
||||||
(let [users (gen-n-users! 2)]
|
(let [users (gen-n-users! 2)]
|
||||||
|
|
|
@ -5,9 +5,6 @@
|
||||||
[honeysql.core :as hsql]
|
[honeysql.core :as hsql]
|
||||||
[clojure.data.generators :as dg]))
|
[clojure.data.generators :as dg]))
|
||||||
|
|
||||||
;; CREATE ROLE views_user LOGIN PASSWORD 'password';
|
|
||||||
;; CREATE DATABASE views_test OWNER views_user;
|
|
||||||
|
|
||||||
(defn sql-ts
|
(defn sql-ts
|
||||||
([ts] (java.sql.Timestamp. ts))
|
([ts] (java.sql.Timestamp. ts))
|
||||||
([] (java.sql.Timestamp. (.getTime (java.util.Date.)))))
|
([] (java.sql.Timestamp. (.getTime (java.util.Date.)))))
|
||||||
|
@ -18,30 +15,15 @@
|
||||||
:user (get :views-test-user e/env "views_user")
|
:user (get :views-test-user e/env "views_user")
|
||||||
:password (get :views-test-ppassword e/env "password")})
|
:password (get :views-test-ppassword e/env "password")})
|
||||||
|
|
||||||
(defn users-table-fixture!
|
(defn clean-tables!
|
||||||
[]
|
|
||||||
(j/execute! db ["CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL, created_on DATE NOT NULL)"]))
|
|
||||||
|
|
||||||
(defn posts-table-fixture!
|
|
||||||
[]
|
|
||||||
(j/execute! db ["CREATE TABLE posts (id SERIAL PRIMARY KEY,
|
|
||||||
title TEXT NOT NULL,
|
|
||||||
body TEXT NOT NULL,
|
|
||||||
created_on DATE NOT NULL,
|
|
||||||
user_id INTEGER NOT NULL,
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id))"]))
|
|
||||||
|
|
||||||
(defn drop-tables!
|
|
||||||
[tables]
|
[tables]
|
||||||
(doseq [t tables]
|
(doseq [t (map name tables)]
|
||||||
(j/execute! db [(str "DROP TABLE " (name t))])))
|
(j/execute! db [(str "DELETE FROM " t)])))
|
||||||
|
|
||||||
(defn database-fixtures!
|
(defn database-fixtures!
|
||||||
[f]
|
[f]
|
||||||
(users-table-fixture!)
|
(clean-tables! [:posts :users])
|
||||||
(posts-table-fixture!)
|
(f))
|
||||||
(f)
|
|
||||||
(drop-tables! [:posts :users]))
|
|
||||||
|
|
||||||
(defn user-fixture!
|
(defn user-fixture!
|
||||||
[name]
|
[name]
|
||||||
|
@ -59,9 +41,10 @@
|
||||||
|
|
||||||
(defn user-posts-tmpl
|
(defn user-posts-tmpl
|
||||||
[user_id]
|
[user_id]
|
||||||
(hsql/build :select [:u.user_id :u.name :p.title :p.body :p.created_on]
|
(hsql/build :select [:u.id :u.name :p.title :p.body :p.created_on]
|
||||||
:from {:posts :p}
|
:from {:posts :p}
|
||||||
:join [[:users :u][:= :user_id user_id]]))
|
:join [[:users :u][:= :u.id :p.user_id]]
|
||||||
|
:where [:= :p.user_id user_id]))
|
||||||
|
|
||||||
(def templates
|
(def templates
|
||||||
{:users {:fn #'users-tmpl}
|
{:users {:fn #'users-tmpl}
|
||||||
|
|
|
@ -64,3 +64,13 @@
|
||||||
(vs/add-subscription! key view-sig templates)
|
(vs/add-subscription! key view-sig templates)
|
||||||
(vs/remove-subscription! key view-sig)
|
(vs/remove-subscription! key view-sig)
|
||||||
(is (nil? (vs/compiled-view-for [:user-posts 1])))))
|
(is (nil? (vs/compiled-view-for [:user-posts 1])))))
|
||||||
|
|
||||||
|
(deftest retrieves-subscriptions-for-subscriber
|
||||||
|
(let [key 1, view-sigs [[:users][:user-posts 1]]]
|
||||||
|
(vs/add-subscriptions! key view-sigs templates)
|
||||||
|
(is (= (set (vs/subscriptions-for 1)) (set view-sigs)))))
|
||||||
|
|
||||||
|
(deftest retrieves-subscriptions-for-subscriber-with-prefix
|
||||||
|
(let [key 1, view-sigs [[:users][:user-posts 1]] prefix 1]
|
||||||
|
(vs/add-subscriptions! key view-sigs templates prefix)
|
||||||
|
(is (= (set (vs/subscriptions-for 1 prefix)) (set view-sigs)))))
|
||||||
|
|
12
test/views/test_db.sql
Normal file
12
test/views/test_db.sql
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
CREATE ROLE views_user LOGIN PASSWORD 'password';
|
||||||
|
CREATE DATABASE views_test OWNER views_user;
|
||||||
|
\c postgresql://localhost/views_test;
|
||||||
|
CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL, created_on DATE NOT NULL);
|
||||||
|
CREATE TABLE posts (id SERIAL PRIMARY KEY,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
body TEXT NOT NULL,
|
||||||
|
created_on DATE NOT NULL,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id));
|
||||||
|
ALTER TABLE users OWNER TO views_user;
|
||||||
|
ALTER TABLE posts OWNER TO views_user;
|
Loading…
Reference in a new issue