diff --git a/liblastfm.cabal b/liblastfm.cabal
--- a/liblastfm.cabal
+++ b/liblastfm.cabal
@@ -1,5 +1,5 @@
 name: liblastfm
-version: 0.1.1.1
+version: 0.1.1.2
 synopsis: Lastfm API interface
 license: MIT
 license-file: LICENSE
@@ -59,8 +59,22 @@
                  text,
                  liblastfm
   type: exitcode-stdio-1.0
-  main-is: json.hs
   hs-source-dirs: tests
+  main-is: json.hs
+  other-modules: Common
+                 Venue
+                 User
+                 Track
+                 Tasteometer
+                 Tag
+                 Playlist
+                 Library
+                 Group
+                 Geo
+                 Event
+                 Chart
+                 Artist
+                 Album
   ghc-options: -Wall
                -fno-warn-unused-do-bind
                -fno-warn-orphans
diff --git a/tests/Album.hs b/tests/Album.hs
new file mode 100644
--- /dev/null
+++ b/tests/Album.hs
@@ -0,0 +1,90 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+module Album (auth, noauth) where
+
+import Data.Aeson.Types
+import Network.Lastfm
+import Network.Lastfm.Album
+import Test.Framework
+import Test.Framework.Providers.HUnit
+
+import Common
+
+
+auth ∷ Request JSON Sign APIKey → Request JSON Sign SessionKey → Secret → [Test]
+auth ak sk s =
+  [ testCase "Album.addTags" testAddTags
+  , testCase "Album.getTags-authenticated" testGetTagsAuth
+  , testCase "Album.removeTag" testRemoveTag
+  , testCase "Album.share" testShare
+  ]
+ where
+  testAddTags = check ok . sign s $
+    addTags <*> artist "Pink Floyd" <*> album "The Wall" <*> tags ["70s", "awesome", "classic"]
+      <*> ak <*> sk
+
+  testGetTagsAuth = check gt . sign s $
+    getTags <*> artist "Pink Floyd" <*> album "The Wall"
+      <*> ak <* sk
+
+  testRemoveTag = check ok . sign s $
+    removeTag <*> artist "Pink Floyd" <*> album "The Wall" <*> tag "awesome"
+      <*> ak <*> sk
+
+  testShare = check ok . sign s $
+    share <*> album "Jerusalem" <*> artist "Sleep" <*> recipient "liblastfm" <* message "Just listen!"
+      <*> ak <*> sk
+
+
+noauth ∷ Request JSON Send APIKey → [Test]
+noauth ak =
+  [ testCase "Album.getBuyLinks" testGetBuylinks
+  , testCase "Album.getBuyLinks_mbid" testGetBuylinks_mbid
+  , testCase "Album.getInfo" testGetInfo
+  , testCase "Album.getInfo_mbid" testGetInfo_mbid
+  , testCase "Album.getShouts" testGetShouts
+  , testCase "Album.getShouts_mbid" testGetShouts_mbid
+  , testCase "Album.getTags" testGetTags
+  , testCase "Album.getTags_mbid" testGetTags_mbid
+  , testCase "Album.getTopTags" testGetTopTags
+  , testCase "Album.getTopTags_mbid" testGetTopTags_mbid
+  , testCase "Album.search" testSearch
+  ]
+ where
+  testGetBuylinks = check gbl $
+    getBuyLinks <*> country "United Kingdom" <*> artist "Pink Floyd" <*> album "The Wall" <*> ak
+  testGetBuylinks_mbid = check gbl $
+    getBuyLinks <*> country "United Kingdom" <*> mbid "3a16c04b-922b-35c5-a29b-cbe9111fbe79" <*> ak
+
+  testGetInfo = check gi $
+    getInfo <*> artist "Pink Floyd" <*> album "The Wall" <*> ak
+  testGetInfo_mbid = check gi $
+    getInfo <*> mbid "3a16c04b-922b-35c5-a29b-cbe9111fbe79" <*> ak
+
+  testGetShouts = check gs $
+    getShouts <*> artist "Pink Floyd" <*> album "The Wall" <* limit 7 <*> ak
+  testGetShouts_mbid = check gs $
+    getShouts <*> mbid "3a16c04b-922b-35c5-a29b-cbe9111fbe79" <* limit 7 <*> ak
+
+  testGetTags = check gt $
+    getTags <*> artist "Pink Floyd" <*> album "The Wall" <* user "liblastfm" <*> ak
+  testGetTags_mbid = check gt $
+    getTags <*> mbid "3a16c04b-922b-35c5-a29b-cbe9111fbe79" <* user "liblastfm" <*> ak
+
+  testGetTopTags = check gtt $
+    getTopTags <*> artist "Pink Floyd" <*> album "The Wall" <*> ak
+  testGetTopTags_mbid = check gtt $
+    getTopTags <*> mbid "3a16c04b-922b-35c5-a29b-cbe9111fbe79" <*> ak
+
+  testSearch = check se $
+    search <*> album "wall" <* limit 5 <*> ak
+
+
+gbl, gi, gs, gt, gtt, se ∷ Value → Parser [String]
+gbl o = parseJSON o >>= (.: "affiliations") >>= (.: "physicals") >>= (.: "affiliation") >>= mapM (.: "supplierName")
+gi o = parseJSON o >>= (.: "album") >>= (.: "toptags") >>= (.: "tag") >>= mapM (.: "name")
+gs o = parseJSON o >>= (.: "shouts") >>= (.: "shout") >>= mapM (.: "body")
+gt o = parseJSON o >>= (.: "tags") >>= (.: "tag") >>= mapM (.: "name")
+gtt o = parseJSON o >>= (.: "toptags") >>= (.: "tag") >>= mapM (.: "count")
+se o = parseJSON o >>= (.: "results") >>= (.: "albummatches") >>= (.: "album") >>= mapM (.: "name")
diff --git a/tests/Artist.hs b/tests/Artist.hs
new file mode 100644
--- /dev/null
+++ b/tests/Artist.hs
@@ -0,0 +1,140 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+module Artist (auth, noauth) where
+
+import Data.Aeson.Types
+import Network.Lastfm
+import Network.Lastfm.Artist
+import Test.Framework
+import Test.Framework.Providers.HUnit
+
+import Common
+
+
+auth ∷ Request JSON Sign APIKey → Request JSON Sign SessionKey → Secret → [Test]
+auth ak sk s =
+  [ testCase "Artist.addTags" testAddTags
+  , testCase "Artist.getTags-authenticated" testGetTagsAuth
+  , testCase "Artist.removeTag" testRemoveTag
+  , testCase "Artist.share" testShare
+  ]
+ where
+  testAddTags = check ok . sign s $
+    addTags <*> artist "Егор Летов" <*> tags ["russian", "black metal"] <*> ak <*> sk
+
+  testGetTagsAuth = check gt . sign s $
+    getTags <*> artist "Егор Летов" <*> ak <* sk
+
+  testRemoveTag = check ok . sign s $
+    removeTag <*> artist "Егор Летов" <*> tag "russian" <*> ak <*> sk
+
+  testShare = check ok . sign s $
+    share <*> artist "Sleep" <*> recipient "liblastfm" <* message "Just listen!" <*> ak <*> sk
+
+
+noauth ∷ Request JSON Send APIKey → [Test]
+noauth ak =
+  [ testCase "Artist.getCorrection" testGetCorrection
+  , testCase "Artist.getEvents" testGetEvents
+  , testCase "Artist.getEvents_mbid" testGetEvents_mbid
+  , testCase "Artist.getInfo" testGetInfo
+  , testCase "Artist.getInfo_mbid" testGetInfo_mbid
+  , testCase "Artist.getPastEvents" testGetPastEvents
+  , testCase "Artist.getPastEvents_mbid" testGetPastEvents_mbid
+  , testCase "Artist.getPodcast" testGetPodcast
+  , testCase "Artist.getPodcast_mbid" testGetPodcast_mbid
+  , testCase "Artist.getShouts" testGetShouts
+  , testCase "Artist.getShouts_mbid" testGetShouts_mbid
+  , testCase "Artist.getSimilar" testGetSimilar
+  , testCase "Artist.getSimilar_mbid" testGetSimilar_mbid
+  , testCase "Artist.getTags" testGetTags
+  , testCase "Artist.getTags_mbid" testGetTags_mbid
+  , testCase "Artist.getTopAlbums" testGetTopAlbums
+  , testCase "Artist.getTopAlbums_mbid" testGetTopAlbums_mbid
+  , testCase "Artist.getTopFans" testGetTopFans
+  , testCase "Artist.getTopFans_mbid" testGetTopFans_mbid
+  , testCase "Artist.getTopTags" testGetTopTags
+  , testCase "Artist.getTopTags_mbid" testGetTopTags_mbid
+  , testCase "Artist.getTopTracks" testGetTopTracks
+  , testCase "Artist.getTopTracks_mbid" testGetTopTracks_mbid
+  , testCase "Artist.search" testSearch
+  ]
+ where
+  testGetCorrection = check gc $
+    getCorrection <*> artist "Meshugah" <*> ak
+
+  testGetEvents = check ge $
+    getEvents <*> artist "Meshuggah" <* limit 2 <*> ak
+  testGetEvents_mbid = check ge $
+    getEvents <*> mbid "cf8b3b8c-118e-4136-8d1d-c37091173413" <* limit 2 <*> ak
+
+  testGetInfo = check gin $
+    getInfo <*> artist "Meshuggah" <*> ak
+  testGetInfo_mbid = check gin $
+    getInfo <*> mbid "cf8b3b8c-118e-4136-8d1d-c37091173413" <*> ak
+
+  testGetPastEvents = check gpe $
+    getPastEvents <*> artist "Meshuggah" <* autocorrect True <*> ak
+  testGetPastEvents_mbid = check gpe $
+    getPastEvents <*> mbid "cf8b3b8c-118e-4136-8d1d-c37091173413" <* autocorrect True <*> ak
+
+  testGetPodcast = check gp $
+    getPodcast <*> artist "Meshuggah" <*> ak
+  testGetPodcast_mbid = check gp $
+    getPodcast <*> mbid "cf8b3b8c-118e-4136-8d1d-c37091173413" <*> ak
+
+  testGetShouts = check gs $
+    getShouts <*> artist "Meshuggah" <* limit 5 <*> ak
+  testGetShouts_mbid = check gs $
+    getShouts <*> mbid "cf8b3b8c-118e-4136-8d1d-c37091173413" <* limit 5 <*> ak
+
+  testGetSimilar = check gsi $
+    getSimilar <*> artist "Meshuggah" <* limit 3 <*> ak
+  testGetSimilar_mbid = check gsi $
+    getSimilar <*> mbid "cf8b3b8c-118e-4136-8d1d-c37091173413" <* limit 3 <*> ak
+
+  testGetTags = check gt $
+    getTags <*> artist "Егор Летов" <* user "liblastfm" <*> ak
+  testGetTags_mbid = check gt $
+    getTags <*> mbid "cfb3d32e-d095-4d63-946d-9daf06932180" <* user "liblastfm" <*> ak
+
+  testGetTopAlbums = check gta $
+    getTopAlbums <*> artist "Meshuggah" <* limit 3 <*> ak
+  testGetTopAlbums_mbid = check gta $
+    getTopAlbums <*> mbid "cf8b3b8c-118e-4136-8d1d-c37091173413" <* limit 3 <*> ak
+
+  testGetTopFans = check gtf $
+    getTopFans <*> artist "Meshuggah" <*> ak
+  testGetTopFans_mbid = check gtf $
+    getTopFans <*> mbid "cf8b3b8c-118e-4136-8d1d-c37091173413" <*> ak
+
+  testGetTopTags = check gtt $
+    getTopTags <*> artist "Meshuggah" <*> ak
+  testGetTopTags_mbid = check gtt $
+    getTopTags <*> mbid "cf8b3b8c-118e-4136-8d1d-c37091173413" <*> ak
+
+  testGetTopTracks = check gttr $
+    getTopTracks <*> artist "Meshuggah" <* limit 3 <*> ak
+  testGetTopTracks_mbid = check gttr $
+    getTopTracks <*> mbid "cf8b3b8c-118e-4136-8d1d-c37091173413" <* limit 3 <*> ak
+
+  testSearch = check se $
+    search <*> artist "Mesh" <* limit 3 <*> ak
+
+
+gc, gin, gp ∷ Value → Parser String
+ge, gpe, gs, gsi, gt, gta, gtf, gtt, gttr, se ∷ Value → Parser [String]
+gc o = parseJSON o >>= (.: "corrections") >>= (.: "correction") >>= (.: "artist") >>= (.: "name")
+ge o = parseJSON o >>= (.: "events") >>= (.: "event") >>= mapM (\o' → (o' .: "venue") >>= (.: "name"))
+gin o = parseJSON o >>= (.: "artist") >>= (.: "stats") >>= (.: "listeners")
+gp o = parseJSON o >>= (.: "rss") >>= (.: "channel") >>= (.: "description")
+gpe o = parseJSON o >>= (.: "events") >>= (.: "event") >>= mapM (.: "title")
+gs o = parseJSON o >>= (.: "shouts") >>= (.: "shout") >>= mapM (.: "author")
+gsi o = parseJSON o >>= (.: "similarartists") >>= (.: "artist") >>= mapM (.: "name")
+gt o = parseJSON o >>= (.: "tags") >>= (.: "tag") >>= mapM (.: "name")
+gta o = parseJSON o >>= (.: "topalbums") >>= (.: "album") >>= mapM (.: "name")
+gtf o = parseJSON o >>= (.: "topfans") >>= (.: "user") >>= mapM (.: "name")
+gtt o = parseJSON o >>= (.: "toptags") >>= (.: "tag") >>= mapM (.: "name")
+gttr o = parseJSON o >>= (.: "toptracks") >>= (.: "track") >>= mapM (.: "name")
+se o = parseJSON o >>= (.: "results") >>= (.: "artistmatches") >>= (.: "artist") >>= mapM (.: "name")
diff --git a/tests/Chart.hs b/tests/Chart.hs
new file mode 100644
--- /dev/null
+++ b/tests/Chart.hs
@@ -0,0 +1,47 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+module Chart (noauth) where
+
+import Data.Aeson.Types
+import Network.Lastfm
+import Network.Lastfm.Chart
+import Test.Framework
+import Test.Framework.Providers.HUnit
+
+import Common
+
+
+noauth ∷ Request JSON Send APIKey → [Test]
+noauth ak =
+  [ testCase "Chart.getHypedArtists" testGetHypedArtists
+  , testCase "Chart.getHypedTracks" testGetHypedTracks
+  , testCase "Chart.getLovedTracks" testGetLovedTracks
+  , testCase "Chart.getTopArtists" testGetTopArtists
+  , testCase "Chart.getTopTags" testGetTopTags
+  , testCase "Chart.getTopTracks" testGetTopTracks
+  ]
+ where
+  testGetHypedArtists = check ga $
+    getHypedArtists <* limit 3 <*> ak
+
+  testGetHypedTracks = check gt $
+    getHypedTracks <* limit 2 <*> ak
+
+  testGetLovedTracks = check gt $
+    getLovedTracks <* limit 3 <*> ak
+
+  testGetTopArtists = check ga $
+    getTopArtists <* limit 4 <*> ak
+
+  testGetTopTags = check gta $
+    getTopTags <* limit 5 <*> ak
+
+  testGetTopTracks = check gt $
+    getTopTracks <* limit 2 <*> ak
+
+
+ga, gt, gta ∷ Value → Parser [String]
+ga o = parseJSON o >>= (.: "artists") >>= (.: "artist") >>= mapM (.: "name")
+gt o = parseJSON o >>= (.: "tracks") >>= (.: "track") >>= mapM (.: "name")
+gta o = parseJSON o >>= (.: "tags") >>= (.: "tag") >>= mapM (.: "name")
diff --git a/tests/Common.hs b/tests/Common.hs
new file mode 100644
--- /dev/null
+++ b/tests/Common.hs
@@ -0,0 +1,20 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+module Common where
+
+import Data.Aeson.Types
+import Network.Lastfm
+import Test.HUnit
+
+
+check ∷ (Value → Parser a) → Request JSON Send Ready → Assertion
+check p q = do
+  r ← lastfm q
+  case parse p `fmap` r of
+    Just (Success _) → assertBool "success" True
+    _                → assertFailure $ "Got: " ++ show r
+
+
+ok ∷ Value → Parser String
+ok o = parseJSON o >>= (.: "status")
diff --git a/tests/Event.hs b/tests/Event.hs
new file mode 100644
--- /dev/null
+++ b/tests/Event.hs
@@ -0,0 +1,49 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+module Event (auth, noauth) where
+
+import Data.Aeson.Types
+import Network.Lastfm
+import Network.Lastfm.Event
+import Test.Framework
+import Test.Framework.Providers.HUnit
+
+import Common
+
+
+auth ∷ Request JSON Sign APIKey → Request JSON Sign SessionKey → Secret → [Test]
+auth ak sk s =
+  [ testCase "Event.attend" testAttend
+  , testCase "Event.share" testShare
+  ]
+ where
+  testAttend = check ok . sign s $
+    attend <*> event 3142549 <*> status Attending <*> ak <*> sk
+
+  testShare = check ok . sign s $
+    share <*> event 3142549 <*> recipient "liblastfm" <* message "Just listen!" <*> ak <*> sk
+
+
+noauth ∷ Request JSON Send APIKey → [Test]
+noauth ak =
+  [ testCase "Event.getAttendees" testGetAttendees
+  , testCase "Event.getInfo" testGetInfo
+  , testCase "Event.getShouts" testGetShouts
+  ]
+ where
+  testGetAttendees = check ga $
+    getAttendees <*> event 3142549 <* limit 2 <*> ak
+
+  testGetInfo = check gi $
+    getInfo <*> event 3142549 <*> ak
+
+  testGetShouts = check gs $
+    getShouts <*> event 3142549 <* limit 1 <*> ak
+
+
+gi, gs ∷ Value → Parser String
+ga ∷ Value → Parser [String]
+ga o = parseJSON o >>= (.: "attendees") >>= (.: "user") >>= mapM (.: "name")
+gi o = parseJSON o >>= (.: "event") >>= (.: "venue") >>= (.: "location") >>= (.: "city")
+gs o = parseJSON o >>= (.: "shouts") >>= (.: "shout") >>= (.: "body")
diff --git a/tests/Geo.hs b/tests/Geo.hs
new file mode 100644
--- /dev/null
+++ b/tests/Geo.hs
@@ -0,0 +1,70 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+module Geo (noauth) where
+
+import Data.Aeson.Types
+import Network.Lastfm
+import Network.Lastfm.Geo
+import Test.Framework
+import Test.Framework.Providers.HUnit
+
+import Common
+
+
+noauth ∷ Request JSON Send APIKey → [Test]
+noauth ak =
+  [ testCase "Geo.getEvents" testGetEvents
+  , testCase "Geo.getMetroArtistChart" testGetMetroArtistChart
+  , testCase "Geo.getMetroHypeArtistChart" testGetMetroHypeArtistChart
+  , testCase "Geo.getMetroHypeTrackChart" testGetMetroHypeTrackChart
+  , testCase "Geo.getMetroTrackChart" testGetMetroTrackChart
+  , testCase "Geo.getMetroUniqueArtistChart" testGetMetroUniqueArtistChart
+  , testCase "Geo.getMetroUniqueTrackChart" testGetMetroUniqueTrackChart
+  , testCase "Geo.getMetroWeeklyChartlist" testGetMetroWeeklyChartlist
+  , testCase "Geo.getMetros" testGetMetros
+  , testCase "Geo.getTopArtists" testGetTopArtists
+  , testCase "Geo.getTopTracks" testGetTopTracks
+  ]
+ where
+  testGetEvents = check ge $
+    getEvents <* location "Moscow" <* limit 5 <*> ak
+
+  testGetMetroArtistChart = check ga $
+    getMetroArtistChart <*> metro "Saint Petersburg" <*> country "Russia" <*> ak
+
+  testGetMetroHypeArtistChart = check ga $
+    getMetroHypeArtistChart <*> metro "New York" <*> country "United States" <*> ak
+
+  testGetMetroHypeTrackChart = check gt $
+    getMetroHypeTrackChart <*> metro "Ufa" <*> country "Russia" <*> ak
+
+  testGetMetroTrackChart = check gt $
+    getMetroTrackChart <*> metro "Boston" <*> country "United States" <*> ak
+
+  testGetMetroUniqueArtistChart = check ga $
+    getMetroUniqueArtistChart <*> metro "Minsk" <*> country "Belarus" <*> ak
+
+  testGetMetroUniqueTrackChart = check gt $
+    getMetroUniqueTrackChart <*> metro "Moscow" <*> country "Russia" <*> ak
+
+  testGetMetroWeeklyChartlist = check gc $
+    getMetroWeeklyChartlist <*> metro "Moscow" <*> ak
+
+  testGetMetros = check gm $
+    getMetros <* country "Russia" <*> ak
+
+  testGetTopArtists = check ga $
+    getTopArtists <*> country "Belarus" <* limit 3 <*> ak
+
+  testGetTopTracks = check gt $
+    getTopTracks <*> country "Ukraine" <* limit 2 <*> ak
+
+
+ga, ge, gm, gt ∷ Value → Parser [String]
+gc ∷ Value → Parser [(String, String)]
+ga o = parseJSON o >>= (.: "topartists") >>= (.: "artist") >>= mapM (.: "name")
+gc o = parseJSON o >>= (.: "weeklychartlist") >>= (.: "chart") >>= mapM (\t → (,) <$> (t .: "from") <*> (t .: "to"))
+ge o = parseJSON o >>= (.: "events") >>= (.: "event") >>= mapM (.: "id")
+gm o = parseJSON o >>= (.: "metros") >>= (.: "metro") >>= mapM (.: "name")
+gt o = parseJSON o >>= (.: "toptracks") >>= (.: "track") >>= mapM (.: "name")
diff --git a/tests/Group.hs b/tests/Group.hs
new file mode 100644
--- /dev/null
+++ b/tests/Group.hs
@@ -0,0 +1,53 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+module Group (noauth) where
+
+import Data.Aeson.Types
+import Network.Lastfm
+import Network.Lastfm.Group
+import Test.Framework
+import Test.Framework.Providers.HUnit
+
+import Common
+
+
+noauth ∷ Request JSON Send APIKey → [Test]
+noauth ak =
+  [ testCase "Group.getHype" testGetHype
+  , testCase "Group.getMembers" testGetMembers
+  , testCase "Group.getWeeklyAlbumChart" testGetWeeklyAlbumChart
+  , testCase "Group.getWeeklyArtistChart" testGetWeeklyArtistChart
+  , testCase "Group.getWeeklyChartList" testGetWeeklyChartList
+  , testCase "Group.getWeeklyTrackChart" testGetWeeklyTrackChart
+  ]
+ where
+  g = "People with no social lives that listen to more music than is healthy who are slightly scared of spiders and can never seem to find a pen"
+
+  testGetHype = check gh $
+    getHype <*> group g <*> ak
+
+  testGetMembers = check gm $
+    getMembers <*> group g <* limit 10 <*> ak
+
+  testGetWeeklyAlbumChart = check ga $
+    getWeeklyAlbumChart <*> group g <*> ak
+
+  testGetWeeklyArtistChart = check gar $
+    getWeeklyArtistChart <*> group g <*> ak
+
+  testGetWeeklyChartList = check gc $
+    getWeeklyChartList <*> group g <*> ak
+
+  testGetWeeklyTrackChart = check gt $
+    getWeeklyTrackChart <*> group g <*> ak
+
+
+ga, gar, gh, gm, gt ∷ Value → Parser [String]
+gc ∷ Value → Parser [(String, String)]
+ga o = parseJSON o >>= (.: "weeklyalbumchart") >>= (.: "album") >>= mapM (.: "playcount")
+gar o = parseJSON o >>= (.: "weeklyartistchart") >>= (.: "artist") >>= mapM (.: "name")
+gc o = parseJSON o >>= (.: "weeklychartlist") >>= (.: "chart") >>= mapM (\t → (,) <$> (t .: "from") <*> (t .: "to"))
+gh o = parseJSON o >>= (.: "weeklyartistchart") >>= (.: "artist") >>= mapM (.: "mbid")
+gm o = parseJSON o >>= (.: "members") >>= (.: "user") >>= mapM (.: "name")
+gt o = parseJSON o >>= (.: "weeklytrackchart") >>= (.: "track") >>= mapM (.: "url")
diff --git a/tests/Library.hs b/tests/Library.hs
new file mode 100644
--- /dev/null
+++ b/tests/Library.hs
@@ -0,0 +1,68 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+module Library (auth, noauth) where
+
+import Data.Aeson.Types
+import Network.Lastfm
+import Network.Lastfm.Library
+import Test.Framework
+import Test.Framework.Providers.HUnit
+
+import Common
+
+
+auth ∷ Request JSON Sign APIKey → Request JSON Sign SessionKey → Secret → [Test]
+auth ak sk s =
+  [ testCase "Library.addAlbum" testAddAlbum
+  , testCase "Library.addArtist" testAddArtist
+  , testCase "Library.addTrack" testAddTrack
+  , testCase "Library.removeAlbum" testRemoveAlbum
+  , testCase "Library.removeArtist" testRemoveArtist
+  , testCase "Library.removeTrack" testRemoveTrack
+  , testCase "Library.removeScrobble" testRemoveScrobble
+  ]
+ where
+  testAddAlbum = check ok . sign s $
+    addAlbum <*> artist "Franz Ferdinand" <*> album "Franz Ferdinand" <*> ak <*> sk
+
+  testAddArtist = check ok . sign s $
+    addArtist <*> artist "Mobthrow" <*> ak <*> sk
+
+  testAddTrack = check ok . sign s $
+    addTrack <*> artist "Eminem" <*> track "Kim" <*> ak <*> sk
+
+  testRemoveAlbum = check ok . sign s $
+    removeAlbum <*> artist "Franz Ferdinand" <*> album "Franz Ferdinand" <*> ak <*> sk
+
+  testRemoveArtist = check ok . sign s $
+    removeArtist <*> artist "Burzum" <*> ak <*> sk
+
+  testRemoveTrack = check ok . sign s $
+    removeTrack <*> artist "Eminem" <*> track "Kim" <*> ak <*> sk
+
+  testRemoveScrobble = check ok . sign s $
+    removeScrobble <*> artist "Gojira" <*> track "Ocean" <*> timestamp 1328905590 <*> ak <*> sk
+
+
+noauth ∷ Request JSON Send APIKey → [Test]
+noauth ak =
+  [ testCase "Library.getAlbums" testGetAlbums
+  , testCase "Library.getArtists" testGetArtists
+  , testCase "Library.getTracks" testGetTracks
+  ]
+ where
+  testGetAlbums = check ga $
+    getAlbums <*> user "smpcln" <* artist "Burzum" <* limit 5 <*> ak
+
+  testGetArtists = check gar $
+    getArtists <*> user "smpcln" <* limit 7 <*> ak
+
+  testGetTracks = check gt $
+    getTracks <*> user "smpcln" <* artist "Burzum" <* limit 4 <*> ak
+
+
+ga, gar, gt ∷ Value → Parser [String]
+ga o = parseJSON o >>= (.: "albums") >>= (.: "album") >>= mapM (.: "name")
+gar o = parseJSON o >>= (.: "artists") >>= (.: "artist") >>= mapM (.: "name")
+gt o = parseJSON o >>= (.: "tracks") >>= (.: "track") >>= mapM (.: "name")
diff --git a/tests/Playlist.hs b/tests/Playlist.hs
new file mode 100644
--- /dev/null
+++ b/tests/Playlist.hs
@@ -0,0 +1,39 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+module Playlist (auth) where
+
+import Data.Aeson.Types
+import Network.Lastfm
+import Network.Lastfm.Playlist
+import Network.Lastfm.User
+import Test.Framework
+import Test.Framework.Providers.HUnit
+
+import Common
+
+
+auth ∷ Request JSON Sign APIKey → Request JSON Sign SessionKey → Secret → [Test]
+auth ak sk s =
+  [ testCase "Playlist.create"   testCreate -- Order matters.
+  , testCase "Playlist.addTrack" testAddTrack
+  ]
+ where
+  ak' = "29effec263316a1f8a97f753caaa83e0"
+
+  testAddTrack =
+    do r ← lastfm $ getPlaylists <*> user "liblastfm" <*> apiKey ak' <* json
+       case parseMaybe pl =<< r of
+         Nothing → error "M"
+         Just r' → let pid = read $ head r' in check ok . sign s $
+           addTrack <*> playlist pid <*> artist "Ruby my dear" <*> track "Chazz" <*> ak <*> sk
+
+  testCreate = check pn . sign s $
+    create <* title "Awesome playlist" <*> ak <*> sk
+
+
+pl ∷ Value → Parser [String]
+pl o = parseJSON o >>= (.: "playlists") >>= (.: "playlist") >>= mapM (.: "id")
+
+pn ∷ Value → Parser String
+pn o = parseJSON o >>= (.: "playlists") >>= (.: "playlist") >>= (.: "title")
diff --git a/tests/Tag.hs b/tests/Tag.hs
new file mode 100644
--- /dev/null
+++ b/tests/Tag.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+module Tag (noauth) where
+
+import Data.Aeson.Types
+import Network.Lastfm
+import Network.Lastfm.Tag
+import Test.Framework
+import Test.Framework.Providers.HUnit
+
+import Common
+
+
+noauth ∷ Request JSON Send APIKey → [Test]
+noauth ak =
+  [ testCase "Tag.getInfo" testGetInfo
+  , testCase "Tag.getSimilar" testGetSimilar
+  , testCase "Tag.getTopAlbums" testGetTopAlbums
+  , testCase "Tag.getTopArtists" testGetTopArtists
+  , testCase "Tag.getTopTags" testGetTopTags
+  , testCase "Tag.getTopTracks" testGetTopTracks
+  , testCase "Tag.getWeeklyArtistChart" testGetWeeklyArtistChart
+  , testCase "Tag.getWeeklyChartList" testGetWeeklyChartList
+  , testCase "Tag.search" testSearch
+  ]
+ where
+  testGetInfo = check gi $
+    getInfo <*> tag "depressive" <*> ak
+
+  testGetSimilar = check gs $
+    getSimilar <*> tag "depressive" <*> ak
+
+  testGetTopAlbums = check gta $
+    getTopAlbums <*> tag "depressive" <* limit 2 <*> ak
+
+  testGetTopArtists = check gtar $
+    getTopArtists <*> tag "depressive" <* limit 3 <*> ak
+
+  testGetTopTags = check gtt $
+    getTopTags <*> ak
+
+  testGetTopTracks = check gttr $
+    getTopTracks <*> tag "depressive" <* limit 2 <*> ak
+
+  testGetWeeklyArtistChart = check gwac $
+    getWeeklyArtistChart <*> tag "depressive" <* limit 3 <*> ak
+
+  testGetWeeklyChartList = check gc $
+    getWeeklyChartList <*> tag "depressive" <*> ak
+
+  testSearch = check se $
+    search <*> tag "depressive" <* limit 3 <*> ak
+
+
+gi ∷ Value → Parser String
+gs, gta, gtar, gtt, gttr, gwac, se ∷ Value → Parser [String]
+gc ∷ Value → Parser [(String, String)]
+gi o = parseJSON o >>= (.: "tag") >>= (.: "taggings")
+gc o = parseJSON o >>= (.: "weeklychartlist") >>= (.: "chart") >>= mapM (\t → (,) <$> (t .: "from") <*> (t .: "to"))
+gs o = parseJSON o >>= (.: "similartags") >>= (.: "tag") >>= mapM (.: "name")
+gta o = parseJSON o >>= (.: "topalbums") >>= (.: "album") >>= mapM (.: "url")
+gtar o = parseJSON o >>= (.: "topartists") >>= (.: "artist") >>= mapM (.: "url")
+gtt o = parseJSON o >>= (.: "toptags") >>= (.: "tag") >>= mapM (.: "name")
+gttr o = parseJSON o >>= (.: "toptracks") >>= (.: "track") >>= mapM (.: "url")
+gwac o = parseJSON o >>= (.: "weeklyartistchart") >>= (.: "artist") >>= mapM (.: "name")
+se o = parseJSON o >>= (.: "results") >>= (.: "tagmatches") >>= (.: "tag") >>= mapM (.: "name")
diff --git a/tests/Tasteometer.hs b/tests/Tasteometer.hs
new file mode 100644
--- /dev/null
+++ b/tests/Tasteometer.hs
@@ -0,0 +1,34 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+module Tasteometer (noauth) where
+
+import           Data.Aeson.Types
+import           Network.Lastfm
+import qualified Network.Lastfm.Tasteometer as T
+import           Test.Framework
+import           Test.Framework.Providers.HUnit
+
+import Common
+
+
+noauth ∷ Request JSON Send APIKey → [Test]
+noauth ak =
+  [ testCase "Tasteometer.compare"    testCompare
+  , testCase "Tasteometer.compare'"   testCompare'
+  , testCase "Tasteometer.compare''"  testCompare''
+  , testCase "Tasteometer.compare'''" testCompare'''
+  ]
+ where
+  testCompare = check cs $
+    T.compare (user "smpcln") (user "MCDOOMDESTROYER") <*> ak
+  testCompare' = check cs $
+    T.compare (user "smpcln") (artists ["enduser", "venetian snares"]) <*> ak
+  testCompare'' = check cs $
+    T.compare (artists ["enduser", "venetian snares"]) (user "smpcln") <*> ak
+  testCompare''' = check cs $
+    T.compare (artists ["enduser", "venetian snares"]) (artists ["enduser", "venetian snares"]) <*> ak
+
+
+cs ∷ Value → Parser String
+cs o = parseJSON o >>= (.: "comparison") >>= (.: "result") >>= (.: "score")
diff --git a/tests/Track.hs b/tests/Track.hs
new file mode 100644
--- /dev/null
+++ b/tests/Track.hs
@@ -0,0 +1,132 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+module Track (auth, noauth) where
+
+import Data.Aeson.Types
+import Network.Lastfm
+import Network.Lastfm.Track
+import Test.Framework
+import Test.Framework.Providers.HUnit
+
+import Common
+
+
+auth ∷ Request JSON Sign APIKey → Request JSON Sign SessionKey → Secret → [Test]
+auth ak sk s =
+  [ testCase "Track.addTags" testAddTags
+  , testCase "Track.ban" testBan
+  , testCase "Track.love" testLove
+  , testCase "Track.removeTag" testRemoveTag
+  , testCase "Track.share" testShare
+  , testCase "Track.unban" testUnban
+  , testCase "Track.unlove" testUnlove
+  , testCase "Track.scrobble" testScrobble
+  , testCase "Track.updateNowPlaying" testUpdateNowPlaying
+  ]
+ where
+  testAddTags = check ok . sign s $
+    addTags <*> artist "Jefferson Airplane" <*> track "White rabbit" <*> tags ["60s", "awesome"] <*> ak <*> sk
+
+  testBan = check ok . sign s $
+    ban <*> artist "Eminem" <*> track "Kim" <*> ak <*> sk
+
+  testLove = check ok . sign s $
+    love <*> artist "Gojira" <*> track "Ocean" <*> ak <*> sk
+
+  testRemoveTag = check ok . sign s $
+    removeTag <*> artist "Jefferson Airplane" <*> track "White rabbit" <*> tag "awesome" <*> ak <*> sk
+
+  testShare = check ok . sign s $
+    share <*> artist "Led Zeppelin" <*> track "When the Levee Breaks" <*> recipient "liblastfm" <* message "Just listen!" <*> ak <*> sk
+
+  testUnban = check ok . sign s $
+    unban <*> artist "Eminem" <*> track "Kim" <*> ak <*> sk
+
+  testUnlove = check ok . sign s $
+    unlove <*> artist "Gojira" <*> track "Ocean" <*> ak <*> sk
+
+  testScrobble = check ss . sign s $
+    scrobble <*> artist "Gojira" <*> track "Ocean" <*> timestamp 1300000000 <*> ak <*> sk
+
+  testUpdateNowPlaying = check np . sign s $
+    updateNowPlaying <*> artist "Gojira" <*> track "Ocean" <*> ak <*> sk
+
+
+noauth ∷ Request JSON Send APIKey → [Test]
+noauth ak =
+  [ testCase "Track.getBuylinks" testGetBuylinks
+  , testCase "Track.getCorrection" testGetCorrection
+  , testCase "Track.getFingerprintMetadata" testGetFingerprintMetadata
+  , testCase "Track.getInfo" testGetInfo
+  , testCase "Track.getInfo_mbid" testGetInfo_mbid
+  , testCase "Track.getShouts" testGetShouts
+  , testCase "Track.getShouts_mbid" testGetShouts_mbid
+  , testCase "Track.getSimilar" testGetSimilar
+  , testCase "Track.getSimilar_mbid" testGetSimilar_mbid
+  , testCase "Track.getTags" testGetTags
+  , testCase "Track.getTags_mbid" testGetTags_mbid
+  , testCase "Track.getTopFans" testGetTopFans
+  , testCase "Track.getTopFans_mbid" testGetTopFans_mbid
+  , testCase "Track.getTopTags" testGetTopTags
+  , testCase "Track.getTopTags_mbid" testGetTopTags_mbid
+  , testCase "Track.search" testSearch
+  ]
+ where
+  testGetBuylinks = check gbl $
+    getBuyLinks <*> country "United Kingdom" <*> artist "Pink Floyd" <*> track "Brain Damage" <*> ak
+
+  testGetCorrection = check gc $
+    getCorrection <*> artist "Pink Ployd" <*> track "Brain Damage" <*> ak
+
+  testGetFingerprintMetadata = check gfm $
+    getFingerprintMetadata <*> fingerprint 1234 <*> ak
+
+  testGetInfo = check gi $
+    getInfo <*> artist "Pink Floyd" <*> track "Comfortably Numb" <* username "aswalrus" <*> ak
+  testGetInfo_mbid = check gi $
+    getInfo <*> mbid "b6581c74-0b8c-4981-ab92-7eed0298a4bb" <* username "aswalrus" <*> ak
+
+  testGetShouts = check gsh $
+    getShouts <*> artist "Pink Floyd" <*> track "Comfortably Numb" <* limit 7 <*> ak
+  testGetShouts_mbid = check gsh $
+    getShouts <*> mbid "b6581c74-0b8c-4981-ab92-7eed0298a4bb" <* limit 7 <*> ak
+
+  testGetSimilar = check gsi $
+    getSimilar <*> artist "Pink Floyd" <*> track "Comfortably Numb" <* limit 4 <*> ak
+  testGetSimilar_mbid = check gsi $
+    getSimilar <*> mbid "b6581c74-0b8c-4981-ab92-7eed0298a4bb" <* limit 4 <*> ak
+
+  testGetTags = check gt $
+    getTags <*> artist "Jefferson Airplane" <*> track "White Rabbit" <* user "liblastfm" <*> ak
+  testGetTags_mbid = check gt $
+    getTags <*> mbid "1fc619ee-c612-4b2a-a8dc-bb8f1b8b2d6d"  <* user "liblastfm" <*> ak
+
+  testGetTopFans = check gtf $
+    getTopFans <*> artist "Pink Floyd" <*> track "Comfortably Numb" <*> ak
+  testGetTopFans_mbid = check gtf $
+    getTopFans <*> mbid "b6581c74-0b8c-4981-ab92-7eed0298a4bb" <*> ak
+
+  testGetTopTags = check gtt $
+    getTopTags <*> artist "Pink Floyd" <*> track "Comfortably Numb" <*> ak
+  testGetTopTags_mbid = check gtt $
+    getTopTags <*> mbid "b6581c74-0b8c-4981-ab92-7eed0298a4bb" <*> ak
+
+  testSearch = check s' $
+    search <*> track "Believe" <* limit 12 <*> ak
+
+
+gc, gi, gt, ss, np ∷ Value → Parser String
+gbl, gfm, gsh, gsi{-, gta-}, gtf, gtt, s' ∷ Value → Parser [String]
+gbl o = parseJSON o >>= (.: "affiliations") >>= (.: "downloads") >>= (.: "affiliation") >>= mapM (.: "supplierName")
+gc o = parseJSON o >>= (.: "corrections") >>= (.: "correction") >>= (.: "track") >>= (.: "artist") >>= (.: "name")
+gfm o = parseJSON o >>= (.: "tracks") >>= (.: "track") >>= mapM (.: "name")
+gi o = parseJSON o >>= (.: "track") >>= (.: "userplaycount")
+gsh o = parseJSON o >>= (.: "shouts") >>= (.: "shout") >>= mapM (.: "author")
+gsi o = parseJSON o >>= (.: "similartracks") >>= (.: "track") >>= mapM (.: "name")
+gt o = parseJSON o >>= (.: "tags") >>= (.: "@attr") >>= (.: "track")
+gtf o = parseJSON o >>= (.: "topfans") >>= (.: "user") >>= mapM (.: "name")
+gtt o = parseJSON o >>= (.: "toptags") >>= (.: "tag") >>= mapM (.: "name")
+s' o = parseJSON o >>= (.: "results") >>= (.: "trackmatches") >>= (.: "track") >>= mapM (.: "name")
+ss o = parseJSON o >>= (.: "scrobbles") >>= (.: "scrobble") >>= (.: "track") >>= (.: "#text")
+np o = parseJSON o >>= (.: "nowplaying") >>= (.: "track") >>= (.: "#text")
diff --git a/tests/User.hs b/tests/User.hs
new file mode 100644
--- /dev/null
+++ b/tests/User.hs
@@ -0,0 +1,150 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+module User (auth, noauth) where
+
+import Data.Aeson.Types
+import Network.Lastfm
+import Network.Lastfm.User
+import Test.Framework
+import Test.Framework.Providers.HUnit
+
+import Common
+
+
+auth ∷ Request JSON Sign APIKey → Request JSON Sign SessionKey → Secret → [Test]
+auth ak sk s =
+  [ testCase "User.getRecentStations" testGetRecentStations
+  , testCase "User.getRecommendedArtists" testGetRecommendedArtists
+  , testCase "User.getRecommendedEvents" testGetRecommendedEvents
+  , testCase "User.shout" testShout
+  ]
+ where
+  testGetRecentStations = check grs . sign s $
+    getRecentStations <*> user "liblastfm" <* limit 10 <*> ak <*> sk
+
+  testGetRecommendedArtists = check gra . sign s $
+    getRecommendedArtists <* limit 10 <*> ak <*> sk
+
+  testGetRecommendedEvents = check gre . sign s $
+    getRecommendedEvents <* limit 10 <*> ak <*> sk
+
+  testShout = check ok . sign s $
+    shout <*> user "liblastfm" <*> message "test message" <*> ak <*> sk
+
+
+noauth ∷ Request JSON Send APIKey → [Test]
+noauth ak =
+  [ testCase "User.getArtistTracks" testGetArtistTracks
+  , testCase "User.getBannedTracks" testGetBannedTracks
+  , testCase "User.getEvents" testGetEvents
+  , testCase "User.getFriends" testGetFriends
+  , testCase "User.getPlayCount" testGetPlayCount
+  , testCase "User.getGetLovedTracks" testGetLovedTracks
+  , testCase "User.getNeighbours" testGetNeighbours
+  , testCase "User.getNewReleases" testGetNewReleases
+  , testCase "User.getPastEvents" testGetPastEvents
+  , testCase "User.getPersonalTags" testGetPersonalTags
+  , testCase "User.getPlaylists" testGetPlaylists
+  , testCase "User.getRecentTracks" testGetRecentTracks
+  , testCase "User.getShouts" testGetShouts
+  , testCase "User.getTopAlbums" testGetTopAlbums
+  , testCase "User.getTopArtists" testGetTopArtists
+  , testCase "User.getTopTags" testGetTopTags
+  , testCase "User.getTopTracks" testGetTopTracks
+  , testCase "User.getWeeklyAlbumChart" testGetWeeklyAlbumChart
+  , testCase "User.getWeeklyArtistChart" testGetWeeklyArtistChart
+  , testCase "User.getWeeklyChartList" testGetWeeklyChartList
+  , testCase "User.getWeeklyTrackChart" testGetWeeklyTrackChart
+  ]
+ where
+  testGetArtistTracks = check gat $
+    getArtistTracks <*> user "smpcln" <*> artist "Dvar" <*> ak
+
+  testGetBannedTracks = check gbt $
+    getBannedTracks <*> user "smpcln" <* limit 10 <*> ak
+
+  testGetEvents = check ge $
+    getEvents <*> user "chansonnier" <* limit 5 <*> ak
+
+  testGetFriends = check gf $
+    getFriends <*> user "smpcln" <* limit 10 <*> ak
+
+  testGetPlayCount = check gpc $
+    getInfo <*> user "smpcln" <*> ak
+
+  testGetLovedTracks = check glt $
+    getLovedTracks <*> user "smpcln" <* limit 10 <*> ak
+
+  testGetNeighbours = check gn $
+    getNeighbours <*> user "smpcln" <* limit 10 <*> ak
+
+  testGetNewReleases = check gnr $
+    getNewReleases <*> user "smpcln" <*> ak
+
+  testGetPastEvents = check gpe $
+    getPastEvents <*> user "mokele" <* limit 5 <*> ak
+
+  testGetPersonalTags = check gpt $
+    getPersonalTags <*> user "crackedcore" <*> tag "rhythmic noise" <*> taggingType "artist" <* limit 10 <*> ak
+
+  testGetPlaylists = check gp $
+    getPlaylists <*> user "mokele" <*> ak
+
+  testGetRecentTracks = check grt $
+    getRecentTracks <*> user "smpcln" <* limit 10 <*> ak
+
+  testGetShouts = check gs $
+    getShouts <*> user "smpcln" <* limit 2 <*> ak
+
+  testGetTopAlbums = check gtal $
+    getTopAlbums <*> user "smpcln" <* limit 5 <*> ak
+
+  testGetTopArtists = check gtar $
+    getTopArtists <*> user "smpcln" <* limit 5 <*> ak
+
+  testGetTopTags = check gtta $
+    getTopTags <*> user "smpcln" <* limit 10 <*> ak
+
+  testGetTopTracks = check gttr $
+    getTopTracks <*> user "smpcln" <* limit 10 <*> ak
+
+  testGetWeeklyAlbumChart = check gwalc $
+    getWeeklyAlbumChart <*> user "smpcln" <*> ak
+
+  testGetWeeklyArtistChart = check gwarc $
+    getWeeklyArtistChart <*> user "smpcln" <*> ak
+
+  testGetWeeklyChartList = check gwcl $
+    getWeeklyChartList <*> user "smpcln" <*> ak
+
+  testGetWeeklyTrackChart = check gwtc $
+    getWeeklyTrackChart <*> user "smpcln" <*> ak
+
+
+gpc ∷ Value → Parser String
+gat, gbt, ge, gf, glt, gn, gnr, gp, gpe, gpt, gra, gre, grs, grt, gs, gtal, gtar, gtta, gttr, gwalc, gwarc, gwcl, gwtc ∷ Value → Parser [String]
+gat o = parseJSON o >>= (.: "artisttracks") >>= (.: "track") >>= mapM (.: "name")
+gbt o = parseJSON o >>= (.: "bannedtracks") >>= (.: "track") >>= mapM (.: "name")
+ge o = parseJSON o >>= (.: "events") >>= (.: "event") >>= mapM (.: "venue") >>= mapM (.: "url")
+gf o = parseJSON o >>= (.: "friends") >>= (.: "user") >>= mapM (.: "name")
+glt o = parseJSON o >>= (.: "lovedtracks") >>= (.: "track") >>= mapM (.: "name")
+gn o = parseJSON o >>= (.: "neighbours") >>= (.: "user") >>= mapM (.: "name")
+gnr o = parseJSON o >>= (.: "albums") >>= (.: "album") >>= mapM (.: "url")
+gp o = parseJSON o >>= (.: "playlists") >>= (.: "playlist") >>= mapM (.: "title")
+gpc o = parseJSON o >>= (.: "user") >>= (.: "playcount")
+gpe o = parseJSON o >>= (.: "events") >>= (.: "event") >>= mapM (.: "url")
+gpt o = parseJSON o >>= (.: "taggings") >>= (.: "artists") >>= (.: "artist") >>= mapM (.: "name")
+gra o = parseJSON o >>= (.: "recommendations") >>= (.: "artist") >>= mapM (.: "name")
+gre o = parseJSON o >>= (.: "events") >>= (.: "event") >>= mapM (.: "url")
+grs o = parseJSON o >>= (.: "recentstations") >>= (.: "station") >>= mapM (.: "name")
+grt o = parseJSON o >>= (.: "recenttracks") >>= (.: "track") >>= mapM (.: "name")
+gs o = parseJSON o >>= (.: "shouts") >>= (.: "shout") >>= mapM (.: "body")
+gtal o = parseJSON o >>= (.: "topalbums") >>= (.: "album") >>= mapM (.: "artist") >>= mapM (.: "name")
+gtar o = parseJSON o >>= (.: "topartists") >>= (.: "artist") >>= mapM (.: "name")
+gtta o = parseJSON o >>= (.: "toptags") >>= (.: "tag") >>= mapM (.: "name")
+gttr o = parseJSON o >>= (.: "toptracks") >>= (.: "track") >>= mapM (.: "url")
+gwalc o = parseJSON o >>= (.: "weeklyalbumchart") >>= (.: "album") >>= mapM (.: "url")
+gwarc o = parseJSON o >>= (.: "weeklyartistchart") >>= (.: "artist") >>= mapM (.: "url")
+gwcl o = take 5 `fmap` (parseJSON o >>= (.: "weeklychartlist") >>= (.: "chart") >>= mapM (.: "from"))
+gwtc o = parseJSON o >>= (.: "weeklytrackchart") >>= (.: "track") >>= mapM (.: "url")
diff --git a/tests/Venue.hs b/tests/Venue.hs
new file mode 100644
--- /dev/null
+++ b/tests/Venue.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE UnicodeSyntax #-}
+module Venue (noauth) where
+
+import Data.Aeson.Types
+import Network.Lastfm
+import Network.Lastfm.Venue
+import Test.Framework
+import Test.Framework.Providers.HUnit
+
+import Common
+
+
+noauth ∷ Request JSON Send APIKey → [Test]
+noauth ak =
+  [ testCase "Venue.getEvents" testGetEvents
+  , testCase "Venue.getPastEvents" testGetPastEvents
+  , testCase "Venue.search" testSearch
+  ]
+ where
+  testGetEvents = check ge $
+    getEvents <*> venue 9163107 <*> ak
+
+  testGetPastEvents = check gpe $
+    getPastEvents <*> venue 9163107 <* limit 2 <*> ak
+
+  testSearch = check se $
+    search <*> venueName "Arena" <*> ak
+
+
+ge, gpe, se ∷ Value → Parser [String]
+ge o = parseJSON o >>= (.: "events") >>= (.: "event") >>= mapM (\t → (t .: "venue") >>= (.: "name"))
+gpe o = parseJSON o >>= (.: "events") >>= (.: "event") >>= mapM (.: "title")
+se o = parseJSON o >>= (.: "results") >>= (.: "venuematches") >>= (.: "venue") >>= mapM (.: "id")
