packages feed

gitson-0.5.0: test-suite/GitsonSpec.hs

{-# LANGUAGE TemplateHaskell #-}

module GitsonSpec (spec) where

import           Test.Hspec
import           System.Directory
import           Data.Aeson.TH
import           Data.List (sort)
import           Control.Exception
import           Control.Monad.IO.Class
import           Control.Monad (void)
import           Gitson
import           Gitson.Util (insideDirectory, lastCommitText)

data Thing = Thing { val :: Int } deriving (Eq, Show, Ord)
$(deriveJSON defaultOptions ''Thing)

spec :: Spec
spec = before setup $ after cleanup $ do
  context "transaction" $ do
    describe "saveDocument" $ do
      it "saves entries only after it's done" $ do
        transaction "tmp/repo" $ do
          saveDocument "things" "first-thing" Thing {val = 1}
          saveDocument "things" "second-thing" Thing {val = 2}
          liftIO $ (readFile "things/first-thing.json") `shouldThrow` anyIOException
          liftIO $ (readFile "things/second-thing.json") `shouldThrow` anyIOException
        insideDirectory "tmp/repo" $ do
          first <- readFile "things/first-thing.json"
          first `shouldBe` "{\n  \"val\": 1\n}"
          second <- readFile "things/second-thing.json"
          second `shouldBe` "{\n  \"val\": 2\n}"
          commitMsg <- lastCommitText
          commitMsg `shouldBe` "Gitson transaction"

    describe "saveNextDocument" $ do
      it "saves entries with next numeric ids" $ do
        transaction "tmp/repo" $ do
          saveNextDocument "things" "hello" Thing {val = 1}
          saveNextDocument "things" "world" Thing {val = 2}
        insideDirectory "tmp/repo" $ do
          first <- readFile "things/000001-hello.json"
          first `shouldBe` "{\n  \"val\": 1\n}"
          second <- readFile "things/000002-world.json"
          second `shouldBe` "{\n  \"val\": 2\n}"

    describe "saveDocumentById" $ do
      it "updates an entry by specified id" $ do
        createDirectoryIfMissing True "tmp/repo/things"
        _ <- liftIO $ writeFile "tmp/repo/things/000004-second-thing.json" "{\"val\":1}"
        transaction "tmp/repo" $ do
          saveDocumentById "things" 4 Thing {val = 2}
        insideDirectory "tmp/repo" $ do
          second <- readFile "things/000004-second-thing.json"
          second `shouldBe` "{\n  \"val\": 2\n}"

    describe "saveDocumentByName" $ do
      it "updates an entry by specified name" $ do
        createDirectoryIfMissing True "tmp/repo/things"
        _ <- liftIO $ writeFile "tmp/repo/things/000004-second-thing.json" "{\"val\":1}"
        transaction "tmp/repo" $ do
          saveDocumentByName "things" "second-thing" Thing {val = 2}
        insideDirectory "tmp/repo" $ do
          second <- readFile "things/000004-second-thing.json"
          second `shouldBe` "{\n  \"val\": 2\n}"

  describe "readDocument" $ do
    it "returns Just the document when reading an document by key" $ do
      createDirectoryIfMissing True "tmp/repo/things"
      _ <- writeFile "tmp/repo/things/second-thing.json" "{\"val\":2}"
      content <- readDocument "tmp/repo/things" "second-thing" :: IO (Maybe Thing)
      content `shouldBe` Just Thing {val = 2}

    it "returns Nothing when reading by a nonexistent key" $ do
      content <- readDocument "tmp/repo/things" "totally-not-a-thing" :: IO (Maybe Thing)
      content `shouldBe` Nothing

  describe "readDocumentById" $ do
    it "returns Just the document when reading an document by id" $ do
      createDirectoryIfMissing True "tmp/repo/things"
      _ <- writeFile "tmp/repo/things/000004-second-thing.json" "{\"val\":1}"
      content <- readDocumentById "tmp/repo/things" 4 :: IO (Maybe Thing)
      content `shouldBe` Just Thing {val = 1}

    it "returns Nothing when reading by a nonexistent id" $ do
      content <- readDocumentById "tmp/repo/things" 1 :: IO (Maybe Thing)
      content `shouldBe` Nothing

  describe "readDocumentByName" $ do
    it "returns Just the document when reading an document by name" $ do
      createDirectoryIfMissing True "tmp/repo/things"
      _ <- writeFile "tmp/repo/things/000098-second-thing.json" "{\"val\":1}"
      content <- readDocumentByName "tmp/repo/things" "second-thing" :: IO (Maybe Thing)
      content `shouldBe` Just Thing {val = 1}

    it "returns Nothing when reading by a nonexistent name" $ do
      content <- readDocumentByName "tmp/repo/things" "yolo" :: IO (Maybe Thing)
      content `shouldBe` Nothing

  describe "documentIdFromName" $ do
    it "returns Just the document id from the document's name" $ do
      createDirectoryIfMissing True "tmp/repo/things"
      _ <- writeFile "tmp/repo/things/000098-some-thing.json" "THIS SHOULD NOT BE READ"
      i <- documentIdFromName "tmp/repo/things" "some-thing"
      i `shouldBe` Just 98

    it "returns Nothing when run with a nonexistent name" $ do
      i <- documentIdFromName "tmp/repo/things" "yolo"
      i `shouldBe` Nothing

  describe "documentNameFromId" $ do
    it "returns Just the document name from the document's id" $ do
      createDirectoryIfMissing True "tmp/repo/things"
      _ <- writeFile "tmp/repo/things/000098-some-thing.json" "THIS SHOULD NOT BE READ"
      i <- documentNameFromId "tmp/repo/things" 98
      i `shouldBe` Just "some-thing"

    it "returns Nothing when run with a nonexistent id" $ do
      i <- documentNameFromId "tmp/repo/things" 123
      i `shouldBe` Nothing

  describe "listDocumentKeys" $ do
    it "returns a list of document keys when listing a collection" $ do
      createDirectoryIfMissing True "tmp/repo/things"
      _ <- writeFile "tmp/repo/things/first-thing.json" "{}"
      _ <- writeFile "tmp/repo/things/second-thing.json" "{}"
      list <- listDocumentKeys "tmp/repo/things"
      sort list `shouldBe` ["first-thing", "second-thing"]

    it "returns an empty when listing a nonexistent collection" $ do
      list <- listDocumentKeys "nonsense"
      list `shouldBe` []

  describe "listEntries" $ do
    it "returns a list of entries when listing a collection" $ do
      createDirectoryIfMissing True "tmp/repo/things"
      _ <- writeFile "tmp/repo/things/first-thing.json" "{\"val\":1}"
      _ <- writeFile "tmp/repo/things/second-thing.json" "{\"val\":2}"
      list <- listEntries "tmp/repo/things" :: IO [Thing]
      sort list `shouldBe` [Thing {val = 1}, Thing {val = 2}]

    it "returns an empty list when listing a nonexistent collection" $ do
      list <- listEntries "nonsense" :: IO [Thing]
      list `shouldBe` []

  describe "listCollections" $ do
    it "returns a list of collections" $ do
      createDirectoryIfMissing True "tmp/repo/bits"
      createDirectoryIfMissing True "tmp/repo/pieces"
      insideDirectory "tmp/repo" $ do
        list <- listCollections
        sort list `shouldBe` ["bits", "pieces"]

    it "returns an empty list when listing an empty repo" $ do
      createDirectoryIfMissing True "tmp/repo"
      insideDirectory "tmp/repo" $ do
        list <- listCollections
        sort list `shouldBe` []

setup :: IO ()
setup = createRepo "tmp/repo"

cleanup :: IO ()
cleanup = void (try (removeDirectoryRecursive "tmp/repo") :: IO (Either IOException ()))