main :: IO ()
main = hspec $ do
  describe "semver parsing" $ do
    it "should parse basic semvers" $ property $
      -- Pre-apply absolute value so we know they're positive integers
      \((abs -> maj, abs -> min, abs -> patch) :: (Int, Int, Int)) -> do
        let s = intercalate "." $ map tshow ([maj, min, patch] :: [Int])
        parseSemVer s `shouldBeR` semver maj min patch

    it "should parse a semver with only major version" $ property $
      \(abs -> maj :: Int) -> do
        parseSemVer (tshow maj) `shouldBeR` semver maj 0 0

    it "should parse a semver with only major and minor versions" $ property $
      \((abs -> maj, abs -> min) :: (Int, Int)) -> do
        let s = intercalate "." $ map tshow ([maj, min] :: [Int])
        parseSemVer s `shouldBeR` semver maj min 0

    it "pretty-printing is an injection" $ property $ \sv -> do
      parseSemVer (tshow sv) `shouldBeR` sv

    it "should parse a semver with metadata" $ do
      parseSemVer "1.2.3-pre+asdf" `shouldBeR` semver'' 1 2 3 ["pre"] ["asdf"]

    describe "with release tags" $ do
      it "should parse a semver with release tags" $ do
        parseSemVer "1.2.3-alpha" `shouldBeR` semver' 1 2 3 ["alpha"]
        parseSemVer "1.2.3alpha" `shouldBeR` semver' 1 2 3 ["alpha"]

      it "should parse a semver with multiple release tags" $ do
        parseSemVer "1.2.3-alpha.3" `shouldBeR` semver' 1 2 3 ["alpha", IntTag 3]
        parseSemVer "1.2.3alpha.3" `shouldBeR` semver' 1 2 3 ["alpha", IntTag 3]

  describe "prerelease tag comparison" $ do
    it "should treat empty lists as greater" $ property $
      \(tags::PrereleaseTags) -> case tags of
        PrereleaseTags [] -> return ()
        tags -> [] > tags `shouldBe` True

  describe "semver range parsing" $ do
    it "should parse a semver into an exact range" $ property $ \sv -> do
      -- This says that if we pretty-print a semver V and parse it as a
      -- semver range, we get the range "= V" back.
      parseSemVerRange (tshow sv) `shouldBeR` Eq sv

    it "pretty printing should be an injection" $ property $ \svr -> do
      -- This says that if we pretty-print a semver V and parse it as a
      -- semver range, we get the range "= V" back.
      parseSemVerRange (tshow svr) `shouldBeR` svr

    it "should parse a semver with partial version into a range" $ property $
      \(abs -> maj :: Int, abs -> min :: Int) -> do
        let expected = Geq (semver maj min 0) `And` Lt (semver maj (min + 1) 0)
            parseIt = parseSemVerRange . T.intercalate "."
        -- E.g. 1.2 =====> (>=1.2.0 <1.3)
        parseIt [tshow maj, tshow min] `shouldBeR` expected
        parseIt [tshow maj, tshow min, "X"] `shouldBeR` expected
        parseIt [tshow maj, tshow min, "x"] `shouldBeR` expected
        parseIt [tshow maj, tshow min, "*"] `shouldBeR` expected

    it "should parse a multi range" $ do
      parseSemVerRange "1.2.3-pre+asdf - 2.4.3-pre+asdf"
        `shouldBeR` Geq (semver'' 1 2 3 ["pre"] ["asdf"])
                     `And` Lt (semver'' 2 4 3 ["pre"] ["asdf"])

    it "should parse semvers with && instead of spaces" $ do
      let expected = Geq (semver 2 0 0) `And` Leq (semver 2 15 0)
      parseSemVerRange ">= 2 && <= 2.14" `shouldBeR` expected

    it "should fail when it's wrong" $ do
      shouldBeL (parseSemVerRange "xyz")

  rangeTests
  cleanTests

-- | These test cases were adapted from
-- https://github.com/npm/node-semver/blob/master/test/clean.js
main :: IO ()
main = hspec $ do
  describe "semver parsing" $ do
    it "should parse basic semvers" $ property $
      -- Pre-apply absolute value so we know they're positive integers
      \((abs -> maj, abs -> min, abs -> patch) :: (Int, Int, Int)) -> do
        let s = intercalate "." $ map tshow ([maj, min, patch] :: [Int])
        parseSemVer s `shouldBeR` semver maj min patch

    it "should parse a semver with only major version" $ property $
      \(abs -> maj :: Int) -> do
        parseSemVer (tshow maj) `shouldBeR` semver maj 0 0

    it "should parse a semver with only major and minor versions" $ property $
      \((abs -> maj, abs -> min) :: (Int, Int)) -> do
        let s = intercalate "." $ map tshow ([maj, min] :: [Int])
        parseSemVer s `shouldBeR` semver maj min 0

    it "pretty-printing is an injection" $ property $ \sv -> do
      parseSemVer (tshow sv) `shouldBeR` sv

    it "should parse a semver with metadata" $ do
      parseSemVer "1.2.3-pre+asdf" `shouldBeR` semver'' 1 2 3 ["pre"] ["asdf"]

    describe "with release tags" $ do
      it "should parse a semver with release tags" $ do
        parseSemVer "1.2.3-alpha" `shouldBeR` semver' 1 2 3 ["alpha"]
        parseSemVer "1.2.3alpha" `shouldBeR` semver' 1 2 3 ["alpha"]

      it "should parse a semver with multiple release tags" $ do
        parseSemVer "1.2.3-alpha.3" `shouldBeR` semver' 1 2 3 ["alpha", IntTag 3]
        parseSemVer "1.2.3alpha.3" `shouldBeR` semver' 1 2 3 ["alpha", IntTag 3]

  describe "prerelease tag comparison" $ do
    it "should treat empty lists as greater" $ property $
      \(tags::PrereleaseTags) -> case tags of
        PrereleaseTags [] -> return ()
        tags -> [] > tags `shouldBe` True

  describe "semver range parsing" $ do
    it "should parse a semver into an exact range" $ property $ \sv -> do
      -- This says that if we pretty-print a semver V and parse it as a
      -- semver range, we get the range "= V" back.
      parseSemVerRange (tshow sv) `shouldBeR` Eq sv

    it "pretty printing should be an injection" $ property $ \svr -> do
      -- This says that if we pretty-print a semver V and parse it as a
      -- semver range, we get the range "= V" back.
      parseSemVerRange (tshow svr) `shouldBeR` svr

    it "should parse a semver with partial version into a range" $ property $
      \(abs -> maj :: Int, abs -> min :: Int) -> do
        let expected = Geq (semver maj min 0) `And` Lt (semver maj (min + 1) 0)
            parseIt = parseSemVerRange . T.intercalate "."
        -- E.g. 1.2 =====> (>=1.2.0 <1.3)
        parseIt [tshow maj, tshow min] `shouldBeR` expected
        parseIt [tshow maj, tshow min, "X"] `shouldBeR` expected
        parseIt [tshow maj, tshow min, "x"] `shouldBeR` expected
        parseIt [tshow maj, tshow min, "*"] `shouldBeR` expected

    it "should parse a multi range" $ do
      parseSemVerRange "1.2.3-pre+asdf - 2.4.3-pre+asdf"
        `shouldBeR` Geq (semver'' 1 2 3 ["pre"] ["asdf"])
                     `And` Lt (semver'' 2 4 3 ["pre"] ["asdf"])

    it "should parse semvers with && instead of spaces" $ do
      let expected = Geq (semver 2 0 0) `And` Leq (semver 2 15 0)
      parseSemVerRange ">= 2 && <= 2.14" `shouldBeR` expected

    it "should fail when it's wrong" $ do
      shouldBeL (parseSemVerRange "xyz")

  rangeTests
  cleanTests

-- | These test cases were adapted from
-- https://github.com/npm/node-semver/blob/master/test/clean.js
main = hspec $ do
  describe "semver parsing" $ do
    it "should parse basic semvers" $ property $
      -- Pre-apply absolute value so we know they're positive integers
      \((abs -> maj, abs -> min, abs -> patch) :: (Int, Int, Int)) -> do
        let s = intercalate "." $ map tshow ([maj, min, patch] :: [Int])
        parseSemVer s `shouldBeR` semver maj min patch

    it "should parse a semver with only major version" $ property $
      \(abs -> maj :: Int) -> do
        parseSemVer (tshow maj) `shouldBeR` semver maj 0 0

    it "should parse a semver with only major and minor versions" $ property $
      \((abs -> maj, abs -> min) :: (Int, Int)) -> do
        let s = intercalate "." $ map tshow ([maj, min] :: [Int])
        parseSemVer s `shouldBeR` semver maj min 0

    it "pretty-printing is an injection" $ property $ \sv -> do
      parseSemVer (tshow sv) `shouldBeR` sv

    it "should parse a semver with metadata" $ do
      parseSemVer "1.2.3-pre+asdf" `shouldBeR` semver'' 1 2 3 ["pre"] ["asdf"]

    describe "with release tags" $ do
      it "should parse a semver with release tags" $ do
        parseSemVer "1.2.3-alpha" `shouldBeR` semver' 1 2 3 ["alpha"]
        parseSemVer "1.2.3alpha" `shouldBeR` semver' 1 2 3 ["alpha"]

      it "should parse a semver with multiple release tags" $ do
        parseSemVer "1.2.3-alpha.3" `shouldBeR` semver' 1 2 3 ["alpha", IntTag 3]
        parseSemVer "1.2.3alpha.3" `shouldBeR` semver' 1 2 3 ["alpha", IntTag 3]

  describe "prerelease tag comparison" $ do
    it "should treat empty lists as greater" $ property $
      \(tags::PrereleaseTags) -> case tags of
        PrereleaseTags [] -> return ()
        tags -> [] > tags `shouldBe` True

  describe "semver range parsing" $ do
    it "should parse a semver into an exact range" $ property $ \sv -> do
      -- This says that if we pretty-print a semver V and parse it as a
      -- semver range, we get the range "= V" back.
      parseSemVerRange (tshow sv) `shouldBeR` Eq sv

    it "pretty printing should be an injection" $ property $ \svr -> do
      -- This says that if we pretty-print a semver V and parse it as a
      -- semver range, we get the range "= V" back.
      parseSemVerRange (tshow svr) `shouldBeR` svr

    it "should parse a semver with partial version into a range" $ property $
      \(abs -> maj :: Int, abs -> min :: Int) -> do
        let expected = Geq (semver maj min 0) `And` Lt (semver maj (min + 1) 0)
            parseIt = parseSemVerRange . T.intercalate "."
        -- E.g. 1.2 =====> (>=1.2.0 <1.3)
        parseIt [tshow maj, tshow min] `shouldBeR` expected
        parseIt [tshow maj, tshow min, "X"] `shouldBeR` expected
        parseIt [tshow maj, tshow min, "x"] `shouldBeR` expected
        parseIt [tshow maj, tshow min, "*"] `shouldBeR` expected

    it "should parse a multi range" $ do
      parseSemVerRange "1.2.3-pre+asdf - 2.4.3-pre+asdf"
        `shouldBeR` Geq (semver'' 1 2 3 ["pre"] ["asdf"])
                     `And` Lt (semver'' 2 4 3 ["pre"] ["asdf"])

    it "should parse semvers with && instead of spaces" $ do
      let expected = Geq (semver 2 0 0) `And` Leq (semver 2 15 0)
      parseSemVerRange ">= 2 && <= 2.14" `shouldBeR` expected

    it "should fail when it's wrong" $ do
      shouldBeL (parseSemVerRange "xyz")

  rangeTests
  cleanTests

-- | These test cases were adapted from
-- https://github.com/npm/node-semver/blob/master/test/clean.js