1 (defpackage :postmodern-tests
2 (:use
:common-lisp
:fiveam
:postmodern
:simple-date
:cl-postgres-tests
))
4 (in-package :postmodern-tests
)
6 ;; Adjust the above to some db/user/pass/host combination that refers
7 ;; to a valid postgresql database in which no table named test_data
8 ;; currently exists. Then after loading the file, run the tests with
9 ;; (fiveam:run! :postmodern)
11 (def-suite :postmodern
)
12 (in-suite :postmodern
)
14 (defmacro with-test-connection
(&body body
)
15 `(with-connection *test-connection
* ,@body
))
17 (defmacro protect
(&body body
)
18 `(unwind-protect (progn ,@(butlast body
)) ,(car (last body
))))
22 (is (not (null *database
*)))))
25 (let ((pooled (apply 'connect
(append *test-connection
* '(:pooled-p t
)))))
27 (let ((pooled* (apply 'connect
(append *test-connection
* '(:pooled-p t
)))))
28 (is (eq pooled pooled
*))
30 (clear-connection-pool)
31 (let ((pooled* (apply 'connect
(append *test-connection
* '(:pooled-p t
)))))
32 (is (not (eq pooled pooled
*)))
34 (clear-connection-pool)))
38 (disconnect *database
*)
39 (is (not (connected-p *database
*)))
40 (reconnect *database
*)
41 (is (connected-p *database
*))))
45 (destructuring-bind (a b c d e f
)
46 (query (:select
22 (:type
44.5 double-precision
) "abcde" t
(:type
9/2 (numeric 5 2))
47 (:[] #("A" "B") 2)) :row
)
50 (is (string= c
"abcde"))
57 (is (= (query (:select
'* :from
(:as
(:select
(:as
1 'as
)) 'where
) :where
(:= 'where.as
1)) :single
!) 1))))
61 (is (time= (query (:select
(:type
(encode-date 1980 2 1) date
)) :single
)
62 (encode-date 1980 2 1)))
63 (is (time= (query (:select
(:type
(encode-timestamp 2040 3 19 12 15 0 2) timestamp
)) :single
)
64 (encode-timestamp 2040 3 19 12 15 0 2)))
65 (is (time= (query (:select
(:type
(encode-interval :month -
1 :hour
24) interval
)) :single
)
66 (encode-interval :month -
1 :hour
24)))))
70 (execute (:create-table test-data
((a :type integer
:primary-key t
) (b :type real
) (c :type
(or text db-null
))) (:unique c
)))
72 (is (table-exists-p 'test-data
))
73 (execute (:insert-into
'test-data
:set
'a
1 'b
5.4 'c
"foobar"))
74 (execute (:insert-into
'test-data
:set
'a
2 'b
88 'c
:null
))
75 (is (equal (query (:order-by
(:select
'* :from
'test-data
) 'a
))
78 (execute (:drop-table
'test-data
)))
79 (is (not (table-exists-p 'test-data
)))))
83 (execute (:create-sequence
'my-seq
:increment
4 :start
10))
85 (is (sequence-exists-p 'my-seq
))
86 (is (= (sequence-next 'my-seq
) 10))
87 (is (= (sequence-next 'my-seq
) 14))
88 (execute (:drop-sequence
'my-seq
)))
89 (is (not (sequence-exists-p 'my-seq
)))))
93 (let ((select-int (prepare (:select
(:type
'$
1 integer
)) :single
))
94 (byte-arr (make-array 10 :element-type
'(unsigned-byte 8) :initial-element
10))
95 (select-bytes (prepare (:select
(:type
'$
1 bytea
)) :single
)))
96 (is (= (funcall select-int
10) 10))
97 (is (= (funcall select-int -
40) -
40))
98 (is (eq (funcall select-int
:null
) :null
))
99 (is (equalp (funcall select-bytes byte-arr
) byte-arr
)))))
102 (with-test-connection
103 (doquery (:select
55 "foobar") (number string
)
105 (is (string= string
"foobar")))))
108 (with-test-connection
109 (doquery ("select $1::integer + 10" 20) (answer)
110 (is (= answer
30)))))
113 (with-test-connection
114 (execute (:create-table test-data
((value :type integer
))))
118 (execute (:insert-into
'test-data
:set
'value
2))
120 (is (= 0 (length (query (:select
'* :from
'test-data
)))))
122 (with-transaction (transaction)
123 (execute (:insert-into
'test-data
:set
'value
2))
124 (commit-transaction transaction
)
125 (error "no wait!!")))
126 (is (= 1 (length (query (:select
'* :from
'test-data
)))))
127 (with-transaction (transaction)
128 (execute (:insert-into
'test-data
:set
'value
44))
129 (abort-transaction transaction
))
130 (is (= 1 (length (query (:select
'* :from
'test-data
)))))
131 (execute (:drop-table
'test-data
)))))
133 (test logical-transaction
134 (with-test-connection
136 (execute (:create-table test-data
((value :type integer
))))
137 (with-logical-transaction ()
138 (execute (:insert-into
'test-data
:set
'value
1))
140 (with-logical-transaction ()
141 (execute (:insert-into
'test-data
:set
'value
2))
142 (error "fail here"))))
143 (is-true (query (:select
'* :from
'test-data
:where
(:= 'value
1))))
144 (is-false (query (:select
'* :from
'test-data
:where
(:= 'value
2))))
145 (execute (:drop-table
'test-data
)))))
147 (test transaction-commit-hooks
148 (with-test-connection
150 (execute (:create-table test-data
((value :type integer
))))
151 (with-logical-transaction (transaction-1)
152 (execute (:insert-into
'test-data
:set
'value
1))
153 (with-logical-transaction (transaction-2)
154 (push (lambda () (execute (:insert-into
'test-data
:set
'value
3))) (commit-hooks transaction-2
))
155 (push (lambda () (execute (:insert-into
'test-data
:set
'value
4))) (commit-hooks transaction-1
))
156 (execute (:insert-into
'test-data
:set
'value
2))))
157 (is (= 4 (length (query (:select
'* :from
'test-data
)))))
158 (execute (:drop-table
'test-data
)))))
160 (test transaction-abort-hooks
161 (with-test-connection
163 (execute (:create-table test-data
((value :type integer
))))
164 (with-logical-transaction (transaction-1)
165 (execute (:insert-into
'test-data
:set
'value
1))
167 (with-logical-transaction (transaction-2)
168 (push (lambda () (execute (:insert-into
'test-data
:set
'value
3))) (abort-hooks transaction-2
))
169 (push (lambda () (execute (:insert-into
'test-data
:set
'value
4))) (abort-hooks transaction-1
))
171 (execute (:insert-into
'test-data
:set
'value
2)))))
172 (is (= 2 (length (query (:select
'* :from
'test-data
)))))
173 (execute (:drop-table
'test-data
)))))
175 (test ensure-transaction
176 (with-test-connection
179 (is (eql postmodern
::*transaction-level
* 1))))
180 (is (eql postmodern
::*transaction-level
* 0))
182 (is (eql postmodern
::*transaction-level
* 1)))))
184 (defclass test-data
()
185 ((id :col-type serial
:initarg
:id
:accessor test-id
)
186 (a :col-type
(or (varchar 100) db-null
) :initarg
:a
:accessor test-a
)
187 (b :col-type boolean
:col-default nil
:initarg
:b
:accessor test-b
))
188 (:metaclass dao-class
)
189 (:table-name dao-test
)
193 (with-test-connection
194 (execute (dao-table-definition 'test-data
))
196 (is (member :dao-test
(list-tables)))
197 (is (null (get-dao 'test-data
1)))
198 (let ((dao (make-instance 'test-data
:a
"quux")))
200 (is (eql (test-id dao
) 1))
201 (is (dao-exists-p dao
)))
202 (let ((dao (get-dao 'test-data
1)))
203 (is (not (null dao
)))
204 (setf (test-b dao
) t
)
206 (let ((dao (get-dao 'test-data
1)))
207 (is (not (null dao
)))
208 (is (string= (test-a dao
) "quux"))
209 (is (eq (test-b dao
) t
))
211 (is (not (select-dao 'test-data
)))
212 (execute (:drop-table
'dao-test
)))))
215 (with-test-connection
216 (execute (dao-table-definition 'test-data
))
218 (let ((dao (make-instance 'test-data
:a
"quux")))
220 (setf (test-a dao
) "bar")
221 (is (not (save-dao dao
)))
222 (is (equal (test-a (get-dao 'test-data
(test-id dao
))) "bar"))
223 (signals database-error
224 (with-transaction () (save-dao dao
)))
226 (is (not (save-dao/transaction dao
)))))
227 (execute (:drop-table
'dao-test
)))))
229 (defclass test-oid
()
230 ((oid :col-type integer
:ghost t
:accessor test-oid
)
231 (a :col-type string
:initarg
:a
:accessor test-a
)
232 (b :col-type string
:initarg
:b
:accessor test-b
))
233 (:metaclass dao-class
)
237 (with-test-connection
238 (execute (concatenate 'string
(dao-table-definition 'test-oid
) "with (oids=true)"))
240 (let ((dao (make-instance 'test-oid
:a
"a" :b
"b")))
242 (is-true (integerp (test-oid dao
)))
243 (let ((back (get-dao 'test-oid
"a")))
244 (is (test-oid dao
) (test-oid back
))
245 (setf (test-b back
) "c")
247 (is (test-b (get-dao 'test-oid
"a")) "c"))
248 (execute (:drop-table
'test-oid
)))))
251 (with-test-connection
252 (execute (:listen
'foo
))
253 (with-test-connection
254 (execute (:notify
'foo
)))
255 (is (cl-postgres:wait-for-notification
*database
*) "foo")))
257 (defclass test-col-name
()
258 ((a :col-type string
:col-name aa
:initarg
:a
:accessor test-a
)
259 (b :col-type string
:col-name bb
:initarg
:b
:accessor test-b
)
260 (c :col-type string
:initarg
:c
:accessor test-c
))
261 (:metaclass dao-class
)
264 (test dao-class-col-name
265 (with-test-connection
266 (execute "CREATE TEMPORARY TABLE test_col_name (aa text primary key, bb text not null, c text not null)")
267 (let ((o (make-instance 'test-col-name
:a
"1" :b
"2" :c
"3")))
269 (let ((oo (get-dao 'test-col-name
"1")))
270 (is (string= "1" (test-a oo
)))
271 (is (string= "2" (test-b oo
)))
272 (is (string= "3" (test-c oo
)))))
273 (let ((o (get-dao 'test-col-name
"1")))
274 (setf (test-b o
) "b")
276 (is (string= "1" (test-a (get-dao 'test-col-name
"1"))))
277 (is (string= "b" (test-b (get-dao 'test-col-name
"1"))))
278 (is (string= "3" (test-c (get-dao 'test-col-name
"1"))))))
280 ;; create two tables with the same name in two different
283 (with-test-connection
284 (is (not (table-exists-p 'test-uniq
)))
285 (execute (:create-table test-uniq
((value :type integer
))))
286 (is (table-exists-p 'test-uniq
))
287 (is (not (schema-exist-p 'uniq
)))
288 (with-schema ('uniq
:if-not-exist
:create
)
289 (is (schema-exist-p 'uniq
))
290 (is (not (table-exists-p 'test-uniq
)))
291 (execute (:create-table test-uniq
((value :type integer
))))
292 (is (table-exists-p 'test-uniq
))
293 (execute (:drop-table
'test-uniq
)))
294 (is (schema-exist-p 'uniq
))
296 (is (not (schema-exist-p 'uniq
)))
297 (execute (:drop-table
'test-uniq
))))
300 (with-test-connection
301 (execute (:create-table test-data
((a :type integer
[]))))
303 (is (table-exists-p 'test-data
))
304 (execute (:insert-into
'test-data
:set
'a
(vector 3 4 5)))
305 (execute (:insert-into
'test-data
:set
'a
#()))
306 (execute (:drop-table
'test-data
)))
307 (is (not (table-exists-p 'test-data
)))))