1module CookieTest (cookieTest) where
2
3import Prelude hiding (exp)
4import Test.Hspec
5import qualified Data.ByteString as BS
6import Test.HUnit hiding (path)
7import Network.HTTP.Client
8import qualified Network.HTTP.Conduit as HC
9import Data.ByteString.UTF8
10import Data.Monoid
11import Data.Time.Clock
12import Data.Time.Calendar
13import qualified Data.CaseInsensitive as CI
14import Web.Cookie
15
16default_request :: HC.Request
17default_request = HC.parseRequest_ "http://www.google.com/"
18
19default_cookie :: Cookie
20default_cookie = Cookie { cookie_name = fromString "name"
21                        , cookie_value = fromString "value"
22                        , cookie_expiry_time = default_time
23                        , cookie_domain = fromString "www.google.com"
24                        , cookie_path = fromString "/"
25                        , cookie_creation_time = default_time
26                        , cookie_last_access_time = default_time
27                        , cookie_persistent = False
28                        , cookie_host_only = False
29                        , cookie_secure_only = False
30                        , cookie_http_only = False
31                        }
32
33default_time :: UTCTime
34default_time = UTCTime (ModifiedJulianDay 56200) (secondsToDiffTime 0)
35
36default_diff_time :: DiffTime
37default_diff_time = secondsToDiffTime 1209600
38
39default_set_cookie :: SetCookie
40default_set_cookie = def       { setCookieName = fromString "name"
41                               , setCookieValue = fromString "value"
42                               , setCookiePath = Just $ fromString "/"
43                               , setCookieExpires = Just default_time
44                               , setCookieMaxAge = Just default_diff_time
45                               , setCookieDomain = Just $ fromString "www.google.com"
46                               , setCookieHttpOnly = False
47                               , setCookieSecure = False
48                               }
49
50testValidIp :: IO ()
51testValidIp = assertBool "Couldn't parse valid IP address" $
52  isIpAddress $ fromString "1.2.3.4"
53
54testIpNumTooHigh :: IO ()
55testIpNumTooHigh = assertBool "One of the digits in the IP address is too large" $
56  not $ isIpAddress $ fromString "501.2.3.4"
57
58testTooManySegmentsInIp :: IO ()
59testTooManySegmentsInIp = assertBool "Too many segments in the ip address" $
60  not $ isIpAddress $ fromString "1.2.3.4.5"
61
62testCharsInIp :: IO ()
63testCharsInIp = assertBool "Chars are not allowed in IP addresses" $
64  not $ isIpAddress $ fromString "1.2a3.4.5"
65
66testDomainMatchesSuccess :: IO ()
67testDomainMatchesSuccess = assertBool "Domains should match" $
68  domainMatches (fromString "www.google.com") (fromString "google.com")
69
70testSameDomain :: IO ()
71testSameDomain = assertBool "Same domain should match" $
72  domainMatches domain domain
73  where domain = fromString "www.google.com"
74
75testSiblingDomain :: IO ()
76testSiblingDomain = assertBool "Sibling domain should not match" $
77  not $ domainMatches (fromString "www.google.com") (fromString "secure.google.com")
78
79testParentDomain :: IO ()
80testParentDomain = assertBool "Parent domain should fail" $
81  not $ domainMatches (fromString "google.com") (fromString "www.google.com")
82
83testNaiveSuffixDomain :: IO ()
84testNaiveSuffixDomain = assertBool "Naively checking for suffix for domain matching should fail" $
85  not $ domainMatches (fromString "agoogle.com") (fromString "google.com")
86
87testDefaultPath :: IO ()
88testDefaultPath = assertEqual "Getting default path from a request"
89  (fromString "/") (defaultPath default_request)
90
91testShortDefaultPath :: IO ()
92testShortDefaultPath = assertEqual "Getting default path from a short path"
93  (fromString "/") (defaultPath $ default_request {HC.path = fromString "/search"})
94
95testPopulatedDefaultPath :: IO ()
96testPopulatedDefaultPath = assertEqual "Getting default path from a request with a path"
97  (fromString "/search") (defaultPath $ default_request {HC.path = fromString "/search/term"})
98
99testParamsDefaultPath :: IO ()
100testParamsDefaultPath = assertEqual "Getting default path from a request with a path and GET params"
101  (fromString "/search") (defaultPath $ default_request {HC.path = fromString "/search/term?var=val"})
102
103testDefaultPathEndingInSlash :: IO ()
104testDefaultPathEndingInSlash = assertEqual "Getting default path that ends in a slash"
105  (fromString "/search/term") (defaultPath $ default_request {HC.path = fromString "/search/term/"})
106
107testSamePathsMatch :: IO ()
108testSamePathsMatch = assertBool "The same path should match" $
109  pathMatches path' path'
110  where path' = fromString "/a/path"
111
112testPathSlashAtEnd :: IO ()
113testPathSlashAtEnd = assertBool "Putting the slash at the end should still match paths" $
114  pathMatches (fromString "/a/path/to/here") (fromString "/a/path/")
115
116testPathNoSlashAtEnd :: IO ()
117testPathNoSlashAtEnd = assertBool "Not putting the slash at the end should still match paths" $
118  pathMatches (fromString "/a/path/to/here") (fromString "/a/path")
119
120testDivergingPaths :: IO ()
121testDivergingPaths = assertBool "Diverging paths don't match" $
122  not $ pathMatches (fromString "/a/path/to/here") (fromString "/a/different/path")
123
124testCookieEqualitySuccess :: IO ()
125testCookieEqualitySuccess = assertEqual "The same cookies should be equal"
126  cookie cookie
127  where cookie = default_cookie
128
129testCookieEqualityResiliance :: IO ()
130testCookieEqualityResiliance = assertEqual "Cookies should still be equal if extra options are changed"
131  (default_cookie {cookie_persistent = True}) (default_cookie {cookie_host_only = True})
132
133testDomainChangesEquality :: IO ()
134testDomainChangesEquality = assertBool "Changing the domain should make cookies not equal" $
135  default_cookie /= (default_cookie {cookie_domain = fromString "/search"})
136
137testRemoveCookie :: IO ()
138testRemoveCookie = assertEqual "Removing a cookie works"
139  (Just default_cookie, createCookieJar []) (removeExistingCookieFromCookieJar default_cookie $ createCookieJar [default_cookie])
140
141testRemoveNonexistantCookie :: IO ()
142testRemoveNonexistantCookie = assertEqual "Removing a nonexistent cookie doesn't work"
143  (Nothing, createCookieJar [default_cookie]) (removeExistingCookieFromCookieJar (default_cookie {cookie_name = fromString "key2"}) $ createCookieJar [default_cookie])
144
145testRemoveCorrectCookie :: IO ()
146testRemoveCorrectCookie = assertEqual "Removing only the correct cookie"
147  (Just search_for, createCookieJar [red_herring]) (removeExistingCookieFromCookieJar search_for $ createCookieJar [red_herring, search_for])
148  where search_for = default_cookie {cookie_name = fromString "name1"}
149        red_herring = default_cookie {cookie_name = fromString "name2"}
150
151testEvictExpiredCookies :: IO ()
152testEvictExpiredCookies = assertEqual "Evicting expired cookies works"
153  (createCookieJar [a, c]) (evictExpiredCookies (createCookieJar [a, b, c, d]) middle)
154  where a = default_cookie { cookie_name = fromString "a"
155                           , cookie_expiry_time = UTCTime (ModifiedJulianDay 3) (secondsToDiffTime 0)
156                           }
157        b = default_cookie { cookie_name = fromString "b"
158                           , cookie_expiry_time = UTCTime (ModifiedJulianDay 1) (secondsToDiffTime 0)
159                           }
160        c = default_cookie { cookie_name = fromString "c"
161                           , cookie_expiry_time = UTCTime (ModifiedJulianDay 3) (secondsToDiffTime 0)
162                           }
163        d = default_cookie { cookie_name = fromString "d"
164                           , cookie_expiry_time = UTCTime (ModifiedJulianDay 1) (secondsToDiffTime 0)
165                           }
166        middle = UTCTime (ModifiedJulianDay 2) (secondsToDiffTime 0)
167
168testEvictNoCookies :: IO ()
169testEvictNoCookies = assertEqual "Evicting empty cookie jar"
170  (createCookieJar []) (evictExpiredCookies (createCookieJar []) middle)
171  where middle = UTCTime (ModifiedJulianDay 2) (secondsToDiffTime 0)
172
173testComputeCookieStringUpdateLastAccessTime :: IO ()
174testComputeCookieStringUpdateLastAccessTime = assertEqual "Updates last access time upon using cookies"
175  (fromString "name=value", out_cookie_jar) (computeCookieString request cookie_jar now True)
176  where request = default_request
177        cookie_jar = createCookieJar [default_cookie]
178        now = UTCTime (ModifiedJulianDay 0) (secondsToDiffTime 1)
179        out_cookie_jar = createCookieJar [default_cookie {cookie_last_access_time = now}]
180
181testComputeCookieStringHostOnly :: IO ()
182testComputeCookieStringHostOnly = assertEqual "Host only cookies should match host exactly"
183  (fromString "name=value", cookie_jar) (computeCookieString request cookie_jar default_time True)
184  where request = default_request
185        cookie_jar = createCookieJar [default_cookie {cookie_host_only = True}]
186
187testComputeCookieStringHostOnlyFilter :: IO ()
188testComputeCookieStringHostOnlyFilter = assertEqual "Host only cookies shouldn't match subdomain"
189  (fromString "", cookie_jar) (computeCookieString request cookie_jar default_time True)
190  where request = default_request {HC.host = fromString "sub1.sub2.google.com"}
191        cookie_jar = createCookieJar [default_cookie { cookie_host_only = True
192                                                     , cookie_domain = fromString "sub2.google.com"
193                                                     }
194                                     ]
195
196testComputeCookieStringDomainMatching :: IO ()
197testComputeCookieStringDomainMatching = assertEqual "Domain matching works for new requests"
198  (fromString "name=value", cookie_jar) (computeCookieString request cookie_jar default_time True)
199  where request = default_request {HC.host = fromString "sub1.sub2.google.com"}
200        cookie_jar = createCookieJar [default_cookie {cookie_domain = fromString "sub2.google.com"}]
201
202testComputeCookieStringPathMatching :: IO ()
203testComputeCookieStringPathMatching = assertEqual "Path matching works for new requests"
204  (fromString "name=value", cookie_jar) (computeCookieString request cookie_jar default_time True)
205  where request = default_request {HC.path = fromString "/a/path/to/nowhere"}
206        cookie_jar = createCookieJar [default_cookie {cookie_path = fromString "/a/path"}]
207
208testComputeCookieStringPathMatchingFails :: IO ()
209testComputeCookieStringPathMatchingFails = assertEqual "Path matching fails when it should"
210  (fromString "", cookie_jar) (computeCookieString request cookie_jar default_time True)
211  where request = default_request {HC.path = fromString "/a/different/path/to/nowhere"}
212        cookie_jar = createCookieJar [default_cookie {cookie_path = fromString "/a/path"}]
213
214testComputeCookieStringPathMatchingWithParms :: IO ()
215testComputeCookieStringPathMatchingWithParms = assertEqual "Path matching succeeds when request has GET params"
216  (fromString "name=value", cookie_jar) (computeCookieString request cookie_jar default_time True)
217  where request = default_request {HC.path = fromString "/a/path/to/nowhere?var=val"}
218        cookie_jar = createCookieJar [default_cookie {cookie_path = fromString "/a/path"}]
219
220testComputeCookieStringSecure :: IO ()
221testComputeCookieStringSecure = assertEqual "Secure flag filters properly"
222  (fromString "", cookie_jar) (computeCookieString default_request cookie_jar default_time True)
223  where cookie_jar = createCookieJar [default_cookie {cookie_secure_only = True}]
224
225testComputeCookieStringHttpOnly :: IO ()
226testComputeCookieStringHttpOnly = assertEqual "http-only flag filters properly"
227  (fromString "", cookie_jar) (computeCookieString default_request cookie_jar default_time False)
228  where cookie_jar = createCookieJar [default_cookie {cookie_http_only = True}]
229
230testComputeCookieStringSort :: IO ()
231testComputeCookieStringSort = assertEqual "Sorting works correctly"
232  (fromString "c1=v1;c3=v3;c4=v4;c2=v2", cookie_jar_out) format_output
233  where now = UTCTime (ModifiedJulianDay 10) (secondsToDiffTime 11)
234        cookie_jar = createCookieJar [ default_cookie { cookie_name = fromString "c1"
235                                                      , cookie_value = fromString "v1"
236                                                      , cookie_path = fromString "/all/encompassing/request"
237                                                      }
238                                     , default_cookie { cookie_name = fromString "c2"
239                                                      , cookie_value = fromString "v2"
240                                                      , cookie_path = fromString "/all"
241                                                      }
242                                     , default_cookie { cookie_name = fromString "c3"
243                                                      , cookie_value = fromString "v3"
244                                                      , cookie_path = fromString "/all/encompassing"
245                                                      , cookie_creation_time = UTCTime (ModifiedJulianDay 0) (secondsToDiffTime 1)
246                                                      }
247                                     , default_cookie { cookie_name = fromString "c4"
248                                                      , cookie_value = fromString "v4"
249                                                      , cookie_path = fromString "/all/encompassing"
250                                                      , cookie_creation_time = UTCTime (ModifiedJulianDay 0) (secondsToDiffTime 2)
251                                                      }
252                                     ]
253        cookie_jar_out = createCookieJar [ default_cookie { cookie_name = fromString "c1"
254                                                          , cookie_value = fromString "v1"
255                                                          , cookie_path = fromString "/all/encompassing/request"
256                                                          , cookie_last_access_time = now
257                                                          }
258                                         , default_cookie { cookie_name = fromString "c2"
259                                                          , cookie_value = fromString "v2"
260                                                          , cookie_path = fromString "/all"
261                                                          , cookie_last_access_time = now
262                                                          }
263                                         , default_cookie { cookie_name = fromString "c3"
264                                                          , cookie_value = fromString "v3"
265                                                          , cookie_path = fromString "/all/encompassing"
266                                                          , cookie_creation_time = UTCTime (ModifiedJulianDay 0) (secondsToDiffTime 1)
267                                                          , cookie_last_access_time = now
268                                                          }
269                                         , default_cookie { cookie_name = fromString "c4"
270                                                          , cookie_value = fromString "v4"
271                                                          , cookie_path = fromString "/all/encompassing"
272                                                          , cookie_creation_time = UTCTime (ModifiedJulianDay 0) (secondsToDiffTime 2)
273                                                          , cookie_last_access_time = now
274                                                          }
275                                         ]
276        request = default_request {HC.path = fromString "/all/encompassing/request/path"}
277        format_output = computeCookieString request cookie_jar default_time False
278
279testInsertCookiesIntoRequestWorks :: IO ()
280testInsertCookiesIntoRequestWorks = assertEqual "Inserting cookies works"
281  [(CI.mk $ fromString "Cookie", fromString "key=val")] out_headers
282  where out_headers = HC.requestHeaders req
283        (req, _) = insertCookiesIntoRequest req' cookie_jar default_time
284        cookie_jar = createCookieJar [ default_cookie { cookie_name = fromString "key"
285                                                      , cookie_value = fromString "val"
286                                                      }
287                                     ]
288        req' = default_request {HC.requestHeaders = [(CI.mk $ fromString "Cookie",
289                                                      fromString "otherkey=otherval")]}
290
291testReceiveSetCookie :: IO ()
292testReceiveSetCookie = assertEqual "Receiving a Set-Cookie"
293  (createCookieJar [default_cookie]) (receiveSetCookie default_set_cookie default_request default_time True $ createCookieJar [])
294
295testReceiveSetCookieTrailingDot :: IO ()
296testReceiveSetCookieTrailingDot = assertEqual "Receiving a Set-Cookie with a trailing domain dot"
297  (createCookieJar []) (receiveSetCookie set_cookie default_request default_time True $ createCookieJar [])
298  where set_cookie = default_set_cookie {setCookieDomain = Just $ fromString "www.google.com."}
299
300testReceiveSetCookieLeadingDot :: IO ()
301testReceiveSetCookieLeadingDot = assertEqual "Receiving a Set-Cookie with a leading domain dot"
302  (createCookieJar [default_cookie]) (receiveSetCookie set_cookie default_request default_time True $ createCookieJar [])
303  where set_cookie = default_set_cookie {setCookieDomain = Just $ fromString ".www.google.com"}
304
305testReceiveSetCookieNoDomain :: IO ()
306testReceiveSetCookieNoDomain = assertEqual "Receiving cookie without domain"
307  (createCookieJar [default_cookie]) (receiveSetCookie set_cookie default_request default_time True $ createCookieJar [])
308  where set_cookie = default_set_cookie {setCookieDomain = Nothing}
309
310testReceiveSetCookieEmptyDomain :: IO ()
311testReceiveSetCookieEmptyDomain = assertEqual "Receiving cookie with empty domain"
312  (createCookieJar [default_cookie]) (receiveSetCookie set_cookie default_request default_time True $ createCookieJar [])
313  where set_cookie = default_set_cookie {setCookieDomain = Just BS.empty}
314
315-- Can't test public suffixes until that module is written
316
317testReceiveSetCookieNonMatchingDomain :: IO ()
318testReceiveSetCookieNonMatchingDomain = assertEqual "Receiving cookie with non-matching domain"
319  (createCookieJar []) (receiveSetCookie set_cookie default_request default_time True $ createCookieJar [])
320  where set_cookie = default_set_cookie {setCookieDomain = Just $ fromString "www.wikipedia.org"}
321
322testReceiveSetCookieHostOnly :: IO ()
323testReceiveSetCookieHostOnly = assertBool "Checking host-only flag gets set" $
324  cookie_host_only $ head $ destroyCookieJar $ receiveSetCookie set_cookie default_request default_time True $ createCookieJar []
325  where set_cookie = default_set_cookie {setCookieDomain = Nothing}
326
327testReceiveSetCookieHostOnlyNotSet :: IO ()
328testReceiveSetCookieHostOnlyNotSet = assertBool "Checking host-only flag doesn't get set" $
329  not $ cookie_host_only $ head $ destroyCookieJar $ receiveSetCookie set_cookie default_request default_time True $ createCookieJar []
330  where set_cookie = default_set_cookie {setCookieDomain = Just $ fromString "google.com"}
331
332testReceiveSetCookieHttpOnly :: IO ()
333testReceiveSetCookieHttpOnly = assertBool "Checking http-only flag gets set" $
334  cookie_http_only $ head $ destroyCookieJar $ receiveSetCookie set_cookie default_request default_time True $ createCookieJar []
335  where set_cookie = default_set_cookie {setCookieHttpOnly = True}
336
337testReceiveSetCookieHttpOnlyNotSet :: IO ()
338testReceiveSetCookieHttpOnlyNotSet = assertBool "Checking http-only flag doesn't get set" $
339  not $ cookie_http_only $ head $ destroyCookieJar $ receiveSetCookie set_cookie default_request default_time True $ createCookieJar []
340  where set_cookie = default_set_cookie {setCookieHttpOnly = False}
341
342testReceiveSetCookieHttpOnlyDrop :: IO ()
343testReceiveSetCookieHttpOnlyDrop = assertEqual "Checking non http request gets dropped"
344  (createCookieJar []) (receiveSetCookie set_cookie default_request default_time False $ createCookieJar [])
345  where set_cookie = default_set_cookie {setCookieHttpOnly = True}
346
347testReceiveSetCookieName :: IO ()
348testReceiveSetCookieName = assertEqual "Name gets set correctly"
349  (fromString "name") (cookie_name $ head $ destroyCookieJar $ receiveSetCookie default_set_cookie default_request default_time True $ createCookieJar [])
350
351testReceiveSetCookieValue :: IO ()
352testReceiveSetCookieValue = assertEqual "Value gets set correctly"
353  (fromString "value") (cookie_value $ head $ destroyCookieJar $ receiveSetCookie default_set_cookie default_request default_time True $ createCookieJar [])
354
355testReceiveSetCookieExpiry :: IO ()
356testReceiveSetCookieExpiry = assertEqual "Expiry gets set correctly"
357  now_plus_diff_time (cookie_expiry_time $ head $ destroyCookieJar $ receiveSetCookie default_set_cookie default_request default_time True $ createCookieJar [])
358  where now_plus_diff_time = ((fromRational $ toRational default_diff_time) `addUTCTime` default_time)
359
360testReceiveSetCookieNoMaxAge :: IO ()
361testReceiveSetCookieNoMaxAge = assertEqual "Expiry is based on the given value"
362  default_time (cookie_expiry_time $ head $ destroyCookieJar $ receiveSetCookie cookie_without_max_age default_request default_time True $ createCookieJar [])
363  where cookie_without_max_age = default_set_cookie {setCookieMaxAge = Nothing}
364
365testReceiveSetCookieNoExpiry :: IO ()
366testReceiveSetCookieNoExpiry = assertEqual "Expiry is based on max age"
367  now_plus_diff_time (cookie_expiry_time $ head $ destroyCookieJar $ receiveSetCookie cookie_without_expiry default_request default_time True $ createCookieJar [])
368  where now_plus_diff_time = ((fromRational $ toRational default_diff_time) `addUTCTime` default_time)
369        cookie_without_expiry = default_set_cookie {setCookieExpires = Nothing}
370
371testReceiveSetCookieNoExpiryNoMaxAge :: IO ()
372testReceiveSetCookieNoExpiryNoMaxAge = assertBool "Expiry is set to a future date" $
373  default_time < (cookie_expiry_time $ head $ destroyCookieJar $ receiveSetCookie basic_cookie default_request default_time True $ createCookieJar [])
374  where basic_cookie = default_set_cookie { setCookieExpires = Nothing, setCookieMaxAge = Nothing }
375
376testReceiveSetCookiePath :: IO ()
377testReceiveSetCookiePath = assertEqual "Path gets set correctly"
378  (fromString "/a/path") (cookie_path $ head $ destroyCookieJar $ receiveSetCookie set_cookie default_request default_time True $ createCookieJar [])
379  where set_cookie = default_set_cookie {setCookiePath = Just $ fromString "/a/path"}
380
381testReceiveSetCookieNoPath :: IO ()
382testReceiveSetCookieNoPath = assertEqual "Path gets set correctly when nonexistent"
383  (fromString "/a/path/to") (cookie_path $ head $ destroyCookieJar $ receiveSetCookie set_cookie request default_time True $ createCookieJar [])
384  where set_cookie = default_set_cookie {setCookiePath = Nothing}
385        request = default_request {HC.path = fromString "/a/path/to/nowhere"}
386
387testReceiveSetCookieCreationTime :: IO ()
388testReceiveSetCookieCreationTime = assertEqual "Creation time gets set correctly"
389  now (cookie_creation_time $ head $ destroyCookieJar $ receiveSetCookie default_set_cookie default_request now True $ createCookieJar [])
390  where now = UTCTime (ModifiedJulianDay 0) (secondsToDiffTime 1)
391
392testReceiveSetCookieAccessTime :: IO ()
393testReceiveSetCookieAccessTime = assertEqual "Last access time gets set correctly"
394  now (cookie_last_access_time $ head $ destroyCookieJar $ receiveSetCookie default_set_cookie default_request now True $ createCookieJar [])
395  where now = UTCTime (ModifiedJulianDay 0) (secondsToDiffTime 1)
396
397testReceiveSetCookiePersistent :: IO ()
398testReceiveSetCookiePersistent = assertBool "Persistent flag gets set correctly" $
399  cookie_persistent $ head $ destroyCookieJar $ receiveSetCookie set_cookie default_request default_time True $ createCookieJar []
400  where set_cookie = default_set_cookie {setCookieExpires = Just default_time}
401
402testReceiveSetCookieSecure :: IO ()
403testReceiveSetCookieSecure = assertBool "Secure flag gets set correctly" $
404  cookie_secure_only $ head $ destroyCookieJar $ receiveSetCookie set_cookie default_request default_time True $ createCookieJar []
405  where set_cookie = default_set_cookie {setCookieSecure = True}
406
407testReceiveSetCookieMaxAge :: IO ()
408testReceiveSetCookieMaxAge = assertEqual "Max-Age gets set correctly"
409  total (cookie_expiry_time $ head $ destroyCookieJar $ receiveSetCookie set_cookie default_request now True $ createCookieJar [])
410  where set_cookie = default_set_cookie { setCookieExpires = Nothing
411                                        , setCookieMaxAge = Just $ secondsToDiffTime 10
412                                        }
413        now = UTCTime (ModifiedJulianDay 10) (secondsToDiffTime 12)
414        total = UTCTime (ModifiedJulianDay 10) (secondsToDiffTime 22)
415
416testReceiveSetCookiePreferMaxAge :: IO ()
417testReceiveSetCookiePreferMaxAge = assertEqual "Max-Age is preferred over Expires"
418  total (cookie_expiry_time $ head $ destroyCookieJar $ receiveSetCookie set_cookie default_request now True $ createCookieJar [])
419  where set_cookie = default_set_cookie { setCookieExpires = Just exp
420                                        , setCookieMaxAge = Just $ secondsToDiffTime 10
421                                        }
422        exp = UTCTime (ModifiedJulianDay 11) (secondsToDiffTime 5)
423        now = UTCTime (ModifiedJulianDay 10) (secondsToDiffTime 12)
424        total = UTCTime (ModifiedJulianDay 10) (secondsToDiffTime 22)
425
426testReceiveSetCookieExisting :: IO ()
427testReceiveSetCookieExisting = assertEqual "Existing cookie gets updated"
428  t (cookie_expiry_time $ head $ destroyCookieJar $ receiveSetCookie set_cookie default_request default_time True $ createCookieJar [default_cookie])
429  where set_cookie = default_set_cookie { setCookieExpires = Just t
430                                        , setCookieMaxAge = Nothing
431                                        }
432        t = UTCTime (ModifiedJulianDay 10) (secondsToDiffTime 12)
433
434testReceiveSetCookieExistingCreation :: IO ()
435testReceiveSetCookieExistingCreation = assertEqual "Creation time gets updated in existing cookie"
436  default_time (cookie_creation_time $ head $ destroyCookieJar $ receiveSetCookie default_set_cookie default_request now True $ createCookieJar [default_cookie])
437  where now = UTCTime (ModifiedJulianDay 10) (secondsToDiffTime 12)
438
439testReceiveSetCookieExistingHttpOnly :: IO ()
440testReceiveSetCookieExistingHttpOnly = assertEqual "Existing http-only cookie gets dropped"
441  default_time (cookie_expiry_time $ head $ destroyCookieJar $ receiveSetCookie default_set_cookie default_request default_time False $ createCookieJar [existing_cookie])
442  where existing_cookie = default_cookie {cookie_http_only = True}
443
444testMonoidPreferRecent :: IO ()
445testMonoidPreferRecent = assertEqual "Monoid prefers more recent cookies"
446  (cct $ createCookieJar [c2]) (cct $ createCookieJar [c1] `Data.Monoid.mappend` createCookieJar [c2])
447  where c1 = default_cookie {cookie_creation_time = UTCTime (ModifiedJulianDay 0) (secondsToDiffTime 1)}
448        c2 = default_cookie {cookie_creation_time = UTCTime (ModifiedJulianDay 0) (secondsToDiffTime 2)}
449        cct cj = cookie_creation_time $ head $ destroyCookieJar cj
450
451ipParseTests :: Spec
452ipParseTests = do
453    it "Valid IP" testValidIp
454    it "Digit Too High" testIpNumTooHigh
455    it "Too Many Segments" testTooManySegmentsInIp
456    it "Chars in IP" testCharsInIp
457
458domainMatchingTests :: Spec
459domainMatchingTests = do
460    it "Should Match" testDomainMatchesSuccess
461    it "Same Domain" testSameDomain
462    it "Sibling Domain" testSiblingDomain
463    it "Parent Domain" testParentDomain
464    it "Checking for Naive suffix-check" testNaiveSuffixDomain
465
466defaultPathTests :: Spec
467defaultPathTests = do
468    it "Basic default path test" testDefaultPath
469    it "Basic populated default path" testPopulatedDefaultPath
470    it "Default path from request with GET params works" testParamsDefaultPath
471    it "Getting a default path that ends in a slash" testDefaultPathEndingInSlash
472    it "Getting a short default path" testShortDefaultPath
473
474pathMatchingTests :: Spec
475pathMatchingTests = do
476    it "Same paths match" testSamePathsMatch
477    it "Putting slash at end" testPathSlashAtEnd
478    it "Not putting slash at end" testPathNoSlashAtEnd
479    it "Diverging paths don't match" testDivergingPaths
480
481equalityTests :: Spec
482equalityTests = do
483    it "The same cookie should be equal to itself" testCookieEqualitySuccess
484    it "Changing extra options shouldn't change equality" testCookieEqualityResiliance
485    it "Changing a cookie's domain should change its equality" testDomainChangesEquality
486
487removeTests :: Spec
488removeTests = do
489    it "Removing a cookie works" testRemoveCookie
490    it "Removing a nonexistent cookie doesn't work" testRemoveNonexistantCookie
491    it "Removing the correct cookie" testRemoveCorrectCookie
492
493evictionTests :: Spec
494evictionTests = do
495    it "Testing eviction" testEvictExpiredCookies
496    it "Evicting from empty cookie jar" testEvictNoCookies
497
498sendingTests :: Spec
499sendingTests = do
500    it "Updates last access time upon using cookies" testComputeCookieStringUpdateLastAccessTime
501    it "Host-only flag matches exact host" testComputeCookieStringHostOnly
502    it "Host-only flag doesn't match subdomain" testComputeCookieStringHostOnlyFilter
503    it "Domain matching works properly" testComputeCookieStringDomainMatching
504    it "Path matching works" testComputeCookieStringPathMatching
505    it "Path matching fails when it should" testComputeCookieStringPathMatchingFails
506    it "Path matching succeeds when request has GET params" testComputeCookieStringPathMatchingWithParms
507    it "Secure flag filters correctly" testComputeCookieStringSecure
508    it "Http-only flag filters correctly" testComputeCookieStringHttpOnly
509    it "Sorting works correctly" testComputeCookieStringSort
510    it "Inserting cookie header works" testInsertCookiesIntoRequestWorks
511
512receivingTests :: Spec
513receivingTests = do
514    it "Can receive set-cookie" testReceiveSetCookie
515    it "Receiving a Set-Cookie with a trailing dot on the domain" testReceiveSetCookieTrailingDot
516    it "Receiving a Set-Cookie with a leading dot on the domain" testReceiveSetCookieLeadingDot
517    it "Set-Cookie with no domain" testReceiveSetCookieNoDomain
518    it "Set-Cookie with empty domain" testReceiveSetCookieEmptyDomain
519    it "Set-Cookie with non-matching domain" testReceiveSetCookieNonMatchingDomain
520    it "Host-only flag gets set" testReceiveSetCookieHostOnly
521    it "Host-only flag doesn't get set" testReceiveSetCookieHostOnlyNotSet
522    it "Http-only flag gets set" testReceiveSetCookieHttpOnly
523    it "Http-only flag doesn't get set" testReceiveSetCookieHttpOnlyNotSet
524    it "Checking non http request gets dropped" testReceiveSetCookieHttpOnlyDrop
525    it "Name gets set correctly" testReceiveSetCookieName
526    it "Value gets set correctly" testReceiveSetCookieValue
527    it "Expiry gets set correctly" testReceiveSetCookieExpiry
528    it "Expiry gets set based on max age if no expiry is given" testReceiveSetCookieNoExpiry
529    it "Expiry gets set based on given value if no max age is given" testReceiveSetCookieNoMaxAge
530    it "Expiry gets set to a future date if no expiry and no max age are given" testReceiveSetCookieNoExpiryNoMaxAge
531    it "Path gets set correctly when nonexistent" testReceiveSetCookieNoPath
532    it "Path gets set correctly" testReceiveSetCookiePath
533    it "Creation time gets set correctly" testReceiveSetCookieCreationTime
534    it "Last access time gets set correctly" testReceiveSetCookieAccessTime
535    it "Persistent flag gets set correctly" testReceiveSetCookiePersistent
536    it "Existing cookie gets updated" testReceiveSetCookieExisting
537    it "Creation time gets updated in existing cookie" testReceiveSetCookieExistingCreation
538    it "Existing http-only cookie gets dropped" testReceiveSetCookieExistingHttpOnly
539    it "Secure flag gets set correctly" testReceiveSetCookieSecure
540    it "Max-Age flag gets set correctly" testReceiveSetCookieMaxAge
541    it "Max-Age is preferred over Expires" testReceiveSetCookiePreferMaxAge
542
543monoidTests :: Spec
544monoidTests = do
545    it "Monoid prefers more recent cookies" testMonoidPreferRecent
546
547cookieTest :: Spec
548cookieTest = do
549    describe "ipParseTests" ipParseTests
550    describe "domainMatchingTests" domainMatchingTests
551    describe "defaultPathTests" defaultPathTests
552    describe "pathMatchingTests" pathMatchingTests
553    describe "equalityTests" equalityTests
554    describe "removeTests" removeTests
555    describe "evictionTests" evictionTests
556    describe "sendingTests" sendingTests
557    describe "receivingTests" receivingTests
558    describe "monoidTest" monoidTests
559