diff --git a/gbs-downloader.cabal b/gbs-downloader.cabal
index a25ed0deaa377bfa1bef77a13d92ecd8c0ab9880..41af684fd92fc405db488869208e688dace67ac4 100644
--- a/gbs-downloader.cabal
+++ b/gbs-downloader.cabal
@@ -65,7 +65,7 @@ library
   import:           warnings
 
   -- Modules exported by the library.
-  exposed-modules:
+  exposed-modules:  Tahoe.Download
 
   -- Modules included in this library but not exported.
   -- other-modules:
@@ -76,8 +76,11 @@ library
   -- Other library packages from which modules are imported.
   build-depends:
     , base                     ^>=4.14.3.0
+    , bytestring
+    , containers
     , tahoe-chk
     , tahoe-great-black-swamp
+    , text
 
   -- Directories containing source files.
   hs-source-dirs:   src
@@ -111,27 +114,35 @@ executable gbs-downloader
 
 test-suite gbs-downloader-test
   -- Import common warning flags.
-  import:           warnings
+  import:             warnings
 
   -- Base language which the package is written in.
-  default-language: Haskell2010
+  default-language:   Haskell2010
 
   -- Modules included in this executable, other than Main.
   -- other-modules:
 
   -- LANGUAGE extensions used by modules in this package.
-  -- other-extensions:
+  default-extensions:
+    OverloadedStrings
+    RecordWildCards
 
   -- The interface type and version of the test suite.
-  type:             exitcode-stdio-1.0
+  type:               exitcode-stdio-1.0
 
   -- Directories containing source files.
-  hs-source-dirs:   test
+  hs-source-dirs:     test
 
   -- The entrypoint to the test suite.
-  main-is:          Main.hs
+  main-is:            Spec.hs
 
   -- Test dependencies.
   build-depends:
     , base            ^>=4.14.3.0
+    , bytestring
+    , crypto-api
     , gbs-downloader
+    , hedgehog
+    , tahoe-chk
+    , tasty
+    , tasty-hunit
diff --git a/src/Tahoe/Download.hs b/src/Tahoe/Download.hs
new file mode 100644
index 0000000000000000000000000000000000000000..25d438f60c65bae5f424b91e85c4735bd32504a7
--- /dev/null
+++ b/src/Tahoe/Download.hs
@@ -0,0 +1,21 @@
+module Tahoe.Download (DownloadError (..), download) where
+
+import Data.ByteString.Lazy (ByteString)
+import Data.Map.Strict (Map)
+import Tahoe.CHK.Capability (Reader)
+import Tahoe.CHK.Server (StorageServerAnnouncement, StorageServerID)
+
+{- | An unrecoverable problem arose while attempting to download and/or read
+ some application data.
+-}
+data DownloadError
+    = -- | The configuration included no candidate servers from which to download.
+      NoConfiguredServers
+    deriving (Eq, Ord, Show)
+
+download ::
+    Map StorageServerID StorageServerAnnouncement ->
+    Reader ->
+    IO (Either DownloadError ByteString)
+download servers cap
+    | mempty == servers = pure . Left $ NoConfiguredServers
diff --git a/test/Main.hs b/test/Main.hs
deleted file mode 100644
index 3e2059e31f5127521b263b051fc7247772685e1a..0000000000000000000000000000000000000000
--- a/test/Main.hs
+++ /dev/null
@@ -1,4 +0,0 @@
-module Main (main) where
-
-main :: IO ()
-main = putStrLn "Test suite not yet implemented."
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
index 0000000000000000000000000000000000000000..b3132b7347c79265ca90ca9b840fda67c6a5bfcb
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,45 @@
+module Main where
+
+import Control.Monad.IO.Class (liftIO)
+import Crypto.Classes (buildKey)
+import qualified Data.ByteString as B
+import System.IO (hSetEncoding, stderr, stdout, utf8)
+import Tahoe.CHK.Capability (Reader (..), Verifier (..))
+import Tahoe.Download (DownloadError (..))
+import Test.Tasty (TestTree, defaultMain, testGroup)
+import Test.Tasty.HUnit (assertEqual, testCase)
+
+import Tahoe.Download (download)
+
+tests :: TestTree
+tests =
+    testGroup
+        "All tests"
+        [ testCase "no configured servers" $ do
+            -- If there are no servers then we can't possibly get enough
+            -- shares to recover the application data.
+            let Just readKey = buildKey $ B.replicate 32 0x00
+                storageIndex = B.replicate 32 0x00
+                fingerprint = B.replicate 32 0x00
+                required = 1
+                total = 1
+                size = 1234
+                verifier = Verifier{..}
+                cap = Reader{..}
+            result <- liftIO $ download mempty cap
+            assertEqual
+                "download should fail with no servers"
+                (Left NoConfiguredServers)
+                result
+        ]
+
+main :: IO ()
+main = do
+    -- Hedgehog writes some non-ASCII and the whole test process will die if
+    -- it can't be encoded.  Increase the chances that all of the output can
+    -- be encoded by forcing the use of UTF-8 (overriding the LANG-based
+    -- choice normally made).
+    hSetEncoding stdout utf8
+    hSetEncoding stderr utf8
+
+    defaultMain tests