rangeTests :: Spec
rangeTests = describe "range tests" $ do
  -- In each case, the range described in the first element of the
  -- tuple should be satisfied by the concrete version described in
  -- the second element of the tuple.
  let testCases :: [(Bool, Text, Text)] = [

        -- Range constraints with pre-release tags require that any
        -- version satisfying the constraint must be equivalent (in
        -- its semantic version tuple) to the minimum of all semantic
        -- versions within the range. In this case the minimum of the
        -- range is "1.2.3" and the version's semantic version tuple
        -- is "1.2.4", therefore it does not satisfy the constraints
        -- of the range given the presence of pre-release tags.
        (False, "1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.4-pre+asdf"),
        (False, "1.2.3-pre+asdf - 2.4.3-pre+asdf", "2.4.3-alpha"),
        (False, ">=0.0.1-alpha <0.2.0-alpha", "0.1.1-alpha"),
        (False, "^0.0.1-alpha", "0.0.4-alpha"),

        -- Range constraints without prerelease tags are very strict
        -- about not admitting versions *with* prerelease tags
        (False, "^0.1.2", "0.1.2-beta1"),
        (False, "^0.1.2", "0.1.4-beta1"),

        -- Despite the numeric quantity, these versions have
        -- prerelease tags and are therefore subjected to the same
        -- invariant checking.
        (False, "^1.2.3-1", "1.8.1-1"),
        (False, "^1.2.3-1", "1.8.1-4"),

        -- If we ever have an exact version tuple match at the top of
        -- a given range then it must satisfy the range constraint!
        --
        -- e.g. "^1.2.3-alpha" translates to ">=1.2.3-alpha
        -- <2.0.0-alpha" and the version to check is "2.0.0-alpha". In
        -- this case the version tuples are equivalent, sans
        -- prerelease tags, but it does not satisfy the upper-bound
        -- less-than relation.
        (False, "^1.2.3-alpha", "2.0.0-alpha"),

        (True,  "1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.3-pre+asdf"),
        (True, "", "1.0.0"),
        (True, "*", "1.2.3"),
        (True, "*", "1.2.3"),
        (True, "*", "v1.2.3"),
        (True, "0.1.20 || 1.2.4", "1.2.4"),
        (True, "1.0.0 - 2.0.0", "1.2.3"),
        (True, "1.0.0", "1.0.0"),
        (True, "1.2.* || 2.*", "1.2.3"),
        (True, "1.2.* || 2.*", "2.1.3"),
        (True, "1.2.*", "1.2.3"),
        (True, "1.2.3 - 2.4.3", "1.2.4"),
        (True, "1.2.3 >=1.2.1", "1.2.3"),
        (True, "1.2.3+asdf - 2.4.3+asdf", "1.2.3"),
        (True, "1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.3"),
        (True, "1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.3-pre.2"),
        (True, "1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.3-pred"),
        (True, "1.2.3-pre+asdf - 2.4.3pre+asdf", "1.2.3"),
        (True, "1.2.3pre+asdf - 2.4.3pre+asdf", "1.2.3"),
        (True, "1.2.x || 2.x", "1.2.3"),
        (True, "1.2.x || 2.x", "2.1.3"),
        (True, "1.2.x", "1.2.3"),
        (True, "2", "2.1.2"),
        (True, "2.*.*", "2.1.3"),
        (True, "2.3", "2.3.1"),
        (True, "2.x.x", "2.1.3"),
        (True, "<    2.0.0", "1.9999.9999"),
        (True, "< 1.2", "1.1.1"),
        (True, "<1.2", "1.1.1"),
        (True, "<2.0.0", "0.2.9"),
        (True, "<2.0.0", "1.9999.9999"),
        (True, "<=   2.0.0", "2.0.0"),
        (True, "<=  2.0.0", "0.2.9"),
        (True, "<= 2.0.0", "1.9999.9999"),
        (True, "<=0.7.x", "0.6.2"),
        (True, "<=0.7.x", "0.7.2"),
        (True, "<=2.0.0", "0.2.9"),
        (True, "<=2.0.0", "1.9999.9999"),
        (True, "<=2.0.0", "2.0.0"),
        (True, "<\t2.0.0", "0.2.9"),
        (True, "=0.7.x", "0.7.2"),
        (True, ">  1.0.0", "1.1.0"),
        (True, "> 1.0.0", "1.0.1"),
        (True, ">1.0.0", "1.0.1"),
        (True, ">1.0.0", "1.1.0"),
        (True, ">=   1.0.0", "1.1.0"),
        (True, ">=  1.0.0", "1.0.1"),
        (True, ">= 1", "1.0.0"),
        (True, ">= 1.0.0", "1.0.0"),
        (True, ">= 4.0.0 <4.1.0-0", "4.0.1"),
        (True, ">=*", "0.2.4"),
        (True, ">=0.1.97", "0.1.97"),
        (True, ">=0.1.97", "v0.1.97"),
        (True, ">=0.2.3 || <0.0.1", "0.0.0"),
        (True, ">=0.2.3 || <0.0.1", "0.2.3"),
        (True, ">=0.2.3 || <0.0.1", "0.2.4"),
        (True, ">=0.7.x", "0.7.2"),
        (True, ">=1", "1.0.0"),
        (True, ">=1.0.0", "1.0.0"),
        (True, ">=1.0.0", "1.0.1"),
        (True, ">=1.0.0", "1.1.0"),
        (True, ">=1.2", "1.2.8"),
        (True, ">=1.2.1 1.2.3", "1.2.3"),
        (True, ">=1.2.1 >=1.2.3", "1.2.3"),
        (True, ">=1.2.3 >=1.2.1", "1.2.3"),
        (True, "^0.0.1-alpha", "0.0.1-beta"),
        (True, "^0.0.1-alpha.1", "0.0.1-alpha.t"),
        (True, "^0.0.1-alpha.1", "0.0.1-alpha.tdff.dddddddddd"),
        (True, "^0.1", "0.1.2"),
        (True, "^0.1.2", "0.1.2"),
        (True, "^1.2 ^1", "1.4.2"),
        (True, "^1.2", "1.4.2"),
        (True, "^1.2.0-alpha", "1.2.0-pre"),
        (True, "^1.2.3", "1.8.1"),
        (True, "^1.2.3+build", "1.2.3"),
        (True, "^1.2.3+build", "1.3.0"),
        (True, "^1.2.3-alpha", "1.2.3-pre"),
        (True, "^1.2.3-alpha.1", "1.2.3-alpha.7"),
        (True, "^1.2.3-boop", "1.2.4"),
        (True, "x", "1.2.3"),
        (True, "||", "1.3.4"),
        (True, "~ 1.0", "1.0.2"),
        (True, "~ 1.0.3", "1.0.12"),
        (True, "~1", "1.2.3"),
        (True, "~1.0", "1.0.2"),
        (True, "~1.2.1 1.2.3 >=1.2.3", "1.2.3"),
        (True, "~1.2.1 1.2.3", "1.2.3"),
        (True, "~1.2.1 1.2.3", "1.2.3"),
        (True, "~1.2.1 =1.2.3", "1.2.3"),
        (True, "~1.2.1 >=1.2.3 1.2.3", "1.2.3"),
        (True, "~1.2.1 >=1.2.3", "1.2.3"),
        (True, "~2.4", "2.4.0"),
        (True, "~2.4", "2.4.5"),
        (True, "~> 1", "1.2.3"),
        (True, "~>1", "1.2.3"),
        (True, "~v0.5.4-pre", "0.5.4"),
        (True, "~v0.5.4-pre", "0.5.5"),
        (True, "~>3.2.1", "3.2.2")
        ]
  forM_ testCases $ \(expectedMatchBool, range, version) -> do
    let fail = expectationFailure
    it (show version <> " satisfies range " <> show range) $ do
      case (parseSemVerRange range, parseSemVer version) of
        (Left err, _) -> fail $ "Semver range parse failed: " <> show err
        (_, Left err) -> fail $ "Semver parse failed: " <> show err
        (Right range, Right version) -> matches range version `shouldBe` expectedMatchBool
rangeTests :: Spec
rangeTests = describe "range tests" $ do
  -- In each case, the range described in the first element of the
  -- tuple should be satisfied by the concrete version described in
  -- the second element of the tuple.
  let testCases :: [(Bool, Text, Text)] = [

        -- Range constraints with pre-release tags require that any
        -- version satisfying the constraint must be equivalent (in
        -- its semantic version tuple) to the minimum of all semantic
        -- versions within the range. In this case the minimum of the
        -- range is "1.2.3" and the version's semantic version tuple
        -- is "1.2.4", therefore it does not satisfy the constraints
        -- of the range given the presence of pre-release tags.
        (False, "1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.4-pre+asdf"),
        (False, "1.2.3-pre+asdf - 2.4.3-pre+asdf", "2.4.3-alpha"),
        (False, ">=0.0.1-alpha <0.2.0-alpha", "0.1.1-alpha"),
        (False, "^0.0.1-alpha", "0.0.4-alpha"),

        -- Range constraints without prerelease tags are very strict
        -- about not admitting versions *with* prerelease tags
        (False, "^0.1.2", "0.1.2-beta1"),
        (False, "^0.1.2", "0.1.4-beta1"),

        -- Despite the numeric quantity, these versions have
        -- prerelease tags and are therefore subjected to the same
        -- invariant checking.
        (False, "^1.2.3-1", "1.8.1-1"),
        (False, "^1.2.3-1", "1.8.1-4"),

        -- If we ever have an exact version tuple match at the top of
        -- a given range then it must satisfy the range constraint!
        --
        -- e.g. "^1.2.3-alpha" translates to ">=1.2.3-alpha
        -- <2.0.0-alpha" and the version to check is "2.0.0-alpha". In
        -- this case the version tuples are equivalent, sans
        -- prerelease tags, but it does not satisfy the upper-bound
        -- less-than relation.
        (False, "^1.2.3-alpha", "2.0.0-alpha"),

        (True,  "1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.3-pre+asdf"),
        (True, "", "1.0.0"),
        (True, "*", "1.2.3"),
        (True, "*", "1.2.3"),
        (True, "*", "v1.2.3"),
        (True, "0.1.20 || 1.2.4", "1.2.4"),
        (True, "1.0.0 - 2.0.0", "1.2.3"),
        (True, "1.0.0", "1.0.0"),
        (True, "1.2.* || 2.*", "1.2.3"),
        (True, "1.2.* || 2.*", "2.1.3"),
        (True, "1.2.*", "1.2.3"),
        (True, "1.2.3 - 2.4.3", "1.2.4"),
        (True, "1.2.3 >=1.2.1", "1.2.3"),
        (True, "1.2.3+asdf - 2.4.3+asdf", "1.2.3"),
        (True, "1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.3"),
        (True, "1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.3-pre.2"),
        (True, "1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.3-pred"),
        (True, "1.2.3-pre+asdf - 2.4.3pre+asdf", "1.2.3"),
        (True, "1.2.3pre+asdf - 2.4.3pre+asdf", "1.2.3"),
        (True, "1.2.x || 2.x", "1.2.3"),
        (True, "1.2.x || 2.x", "2.1.3"),
        (True, "1.2.x", "1.2.3"),
        (True, "2", "2.1.2"),
        (True, "2.*.*", "2.1.3"),
        (True, "2.3", "2.3.1"),
        (True, "2.x.x", "2.1.3"),
        (True, "<    2.0.0", "1.9999.9999"),
        (True, "< 1.2", "1.1.1"),
        (True, "<1.2", "1.1.1"),
        (True, "<2.0.0", "0.2.9"),
        (True, "<2.0.0", "1.9999.9999"),
        (True, "<=   2.0.0", "2.0.0"),
        (True, "<=  2.0.0", "0.2.9"),
        (True, "<= 2.0.0", "1.9999.9999"),
        (True, "<=0.7.x", "0.6.2"),
        (True, "<=0.7.x", "0.7.2"),
        (True, "<=2.0.0", "0.2.9"),
        (True, "<=2.0.0", "1.9999.9999"),
        (True, "<=2.0.0", "2.0.0"),
        (True, "<\t2.0.0", "0.2.9"),
        (True, "=0.7.x", "0.7.2"),
        (True, ">  1.0.0", "1.1.0"),
        (True, "> 1.0.0", "1.0.1"),
        (True, ">1.0.0", "1.0.1"),
        (True, ">1.0.0", "1.1.0"),
        (True, ">=   1.0.0", "1.1.0"),
        (True, ">=  1.0.0", "1.0.1"),
        (True, ">= 1", "1.0.0"),
        (True, ">= 1.0.0", "1.0.0"),
        (True, ">= 4.0.0 <4.1.0-0", "4.0.1"),
        (True, ">=*", "0.2.4"),
        (True, ">=0.1.97", "0.1.97"),
        (True, ">=0.1.97", "v0.1.97"),
        (True, ">=0.2.3 || <0.0.1", "0.0.0"),
        (True, ">=0.2.3 || <0.0.1", "0.2.3"),
        (True, ">=0.2.3 || <0.0.1", "0.2.4"),
        (True, ">=0.7.x", "0.7.2"),
        (True, ">=1", "1.0.0"),
        (True, ">=1.0.0", "1.0.0"),
        (True, ">=1.0.0", "1.0.1"),
        (True, ">=1.0.0", "1.1.0"),
        (True, ">=1.2", "1.2.8"),
        (True, ">=1.2.1 1.2.3", "1.2.3"),
        (True, ">=1.2.1 >=1.2.3", "1.2.3"),
        (True, ">=1.2.3 >=1.2.1", "1.2.3"),
        (True, "^0.0.1-alpha", "0.0.1-beta"),
        (True, "^0.0.1-alpha.1", "0.0.1-alpha.t"),
        (True, "^0.0.1-alpha.1", "0.0.1-alpha.tdff.dddddddddd"),
        (True, "^0.1", "0.1.2"),
        (True, "^0.1.2", "0.1.2"),
        (True, "^1.2 ^1", "1.4.2"),
        (True, "^1.2", "1.4.2"),
        (True, "^1.2.0-alpha", "1.2.0-pre"),
        (True, "^1.2.3", "1.8.1"),
        (True, "^1.2.3+build", "1.2.3"),
        (True, "^1.2.3+build", "1.3.0"),
        (True, "^1.2.3-alpha", "1.2.3-pre"),
        (True, "^1.2.3-alpha.1", "1.2.3-alpha.7"),
        (True, "^1.2.3-boop", "1.2.4"),
        (True, "x", "1.2.3"),
        (True, "||", "1.3.4"),
        (True, "~ 1.0", "1.0.2"),
        (True, "~ 1.0.3", "1.0.12"),
        (True, "~1", "1.2.3"),
        (True, "~1.0", "1.0.2"),
        (True, "~1.2.1 1.2.3 >=1.2.3", "1.2.3"),
        (True, "~1.2.1 1.2.3", "1.2.3"),
        (True, "~1.2.1 1.2.3", "1.2.3"),
        (True, "~1.2.1 =1.2.3", "1.2.3"),
        (True, "~1.2.1 >=1.2.3 1.2.3", "1.2.3"),
        (True, "~1.2.1 >=1.2.3", "1.2.3"),
        (True, "~2.4", "2.4.0"),
        (True, "~2.4", "2.4.5"),
        (True, "~> 1", "1.2.3"),
        (True, "~>1", "1.2.3"),
        (True, "~v0.5.4-pre", "0.5.4"),
        (True, "~v0.5.4-pre", "0.5.5"),
        (True, "~>3.2.1", "3.2.2")
        ]
  forM_ testCases $ \(expectedMatchBool, range, version) -> do
    let fail = expectationFailure
    it (show version <> " satisfies range " <> show range) $ do
      case (parseSemVerRange range, parseSemVer version) of
        (Left err, _) -> fail $ "Semver range parse failed: " <> show err
        (_, Left err) -> fail $ "Semver parse failed: " <> show err
        (Right range, Right version) -> matches range version `shouldBe` expectedMatchBool
rangeTests = describe "range tests" $ do
  -- In each case, the range described in the first element of the
  -- tuple should be satisfied by the concrete version described in
  -- the second element of the tuple.
  let testCases :: [(Bool, Text, Text)] = [

        -- Range constraints with pre-release tags require that any
        -- version satisfying the constraint must be equivalent (in
        -- its semantic version tuple) to the minimum of all semantic
        -- versions within the range. In this case the minimum of the
        -- range is "1.2.3" and the version's semantic version tuple
        -- is "1.2.4", therefore it does not satisfy the constraints
        -- of the range given the presence of pre-release tags.
        (False, "1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.4-pre+asdf"),
        (False, "1.2.3-pre+asdf - 2.4.3-pre+asdf", "2.4.3-alpha"),
        (False, ">=0.0.1-alpha <0.2.0-alpha", "0.1.1-alpha"),
        (False, "^0.0.1-alpha", "0.0.4-alpha"),

        -- Range constraints without prerelease tags are very strict
        -- about not admitting versions *with* prerelease tags
        (False, "^0.1.2", "0.1.2-beta1"),
        (False, "^0.1.2", "0.1.4-beta1"),

        -- Despite the numeric quantity, these versions have
        -- prerelease tags and are therefore subjected to the same
        -- invariant checking.
        (False, "^1.2.3-1", "1.8.1-1"),
        (False, "^1.2.3-1", "1.8.1-4"),

        -- If we ever have an exact version tuple match at the top of
        -- a given range then it must satisfy the range constraint!
        --
        -- e.g. "^1.2.3-alpha" translates to ">=1.2.3-alpha
        -- <2.0.0-alpha" and the version to check is "2.0.0-alpha". In
        -- this case the version tuples are equivalent, sans
        -- prerelease tags, but it does not satisfy the upper-bound
        -- less-than relation.
        (False, "^1.2.3-alpha", "2.0.0-alpha"),

        (True,  "1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.3-pre+asdf"),
        (True, "", "1.0.0"),
        (True, "*", "1.2.3"),
        (True, "*", "1.2.3"),
        (True, "*", "v1.2.3"),
        (True, "0.1.20 || 1.2.4", "1.2.4"),
        (True, "1.0.0 - 2.0.0", "1.2.3"),
        (True, "1.0.0", "1.0.0"),
        (True, "1.2.* || 2.*", "1.2.3"),
        (True, "1.2.* || 2.*", "2.1.3"),
        (True, "1.2.*", "1.2.3"),
        (True, "1.2.3 - 2.4.3", "1.2.4"),
        (True, "1.2.3 >=1.2.1", "1.2.3"),
        (True, "1.2.3+asdf - 2.4.3+asdf", "1.2.3"),
        (True, "1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.3"),
        (True, "1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.3-pre.2"),
        (True, "1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.3-pred"),
        (True, "1.2.3-pre+asdf - 2.4.3pre+asdf", "1.2.3"),
        (True, "1.2.3pre+asdf - 2.4.3pre+asdf", "1.2.3"),
        (True, "1.2.x || 2.x", "1.2.3"),
        (True, "1.2.x || 2.x", "2.1.3"),
        (True, "1.2.x", "1.2.3"),
        (True, "2", "2.1.2"),
        (True, "2.*.*", "2.1.3"),
        (True, "2.3", "2.3.1"),
        (True, "2.x.x", "2.1.3"),
        (True, "<    2.0.0", "1.9999.9999"),
        (True, "< 1.2", "1.1.1"),
        (True, "<1.2", "1.1.1"),
        (True, "<2.0.0", "0.2.9"),
        (True, "<2.0.0", "1.9999.9999"),
        (True, "<=   2.0.0", "2.0.0"),
        (True, "<=  2.0.0", "0.2.9"),
        (True, "<= 2.0.0", "1.9999.9999"),
        (True, "<=0.7.x", "0.6.2"),
        (True, "<=0.7.x", "0.7.2"),
        (True, "<=2.0.0", "0.2.9"),
        (True, "<=2.0.0", "1.9999.9999"),
        (True, "<=2.0.0", "2.0.0"),
        (True, "<\t2.0.0", "0.2.9"),
        (True, "=0.7.x", "0.7.2"),
        (True, ">  1.0.0", "1.1.0"),
        (True, "> 1.0.0", "1.0.1"),
        (True, ">1.0.0", "1.0.1"),
        (True, ">1.0.0", "1.1.0"),
        (True, ">=   1.0.0", "1.1.0"),
        (True, ">=  1.0.0", "1.0.1"),
        (True, ">= 1", "1.0.0"),
        (True, ">= 1.0.0", "1.0.0"),
        (True, ">= 4.0.0 <4.1.0-0", "4.0.1"),
        (True, ">=*", "0.2.4"),
        (True, ">=0.1.97", "0.1.97"),
        (True, ">=0.1.97", "v0.1.97"),
        (True, ">=0.2.3 || <0.0.1", "0.0.0"),
        (True, ">=0.2.3 || <0.0.1", "0.2.3"),
        (True, ">=0.2.3 || <0.0.1", "0.2.4"),
        (True, ">=0.7.x", "0.7.2"),
        (True, ">=1", "1.0.0"),
        (True, ">=1.0.0", "1.0.0"),
        (True, ">=1.0.0", "1.0.1"),
        (True, ">=1.0.0", "1.1.0"),
        (True, ">=1.2", "1.2.8"),
        (True, ">=1.2.1 1.2.3", "1.2.3"),
        (True, ">=1.2.1 >=1.2.3", "1.2.3"),
        (True, ">=1.2.3 >=1.2.1", "1.2.3"),
        (True, "^0.0.1-alpha", "0.0.1-beta"),
        (True, "^0.0.1-alpha.1", "0.0.1-alpha.t"),
        (True, "^0.0.1-alpha.1", "0.0.1-alpha.tdff.dddddddddd"),
        (True, "^0.1", "0.1.2"),
        (True, "^0.1.2", "0.1.2"),
        (True, "^1.2 ^1", "1.4.2"),
        (True, "^1.2", "1.4.2"),
        (True, "^1.2.0-alpha", "1.2.0-pre"),
        (True, "^1.2.3", "1.8.1"),
        (True, "^1.2.3+build", "1.2.3"),
        (True, "^1.2.3+build", "1.3.0"),
        (True, "^1.2.3-alpha", "1.2.3-pre"),
        (True, "^1.2.3-alpha.1", "1.2.3-alpha.7"),
        (True, "^1.2.3-boop", "1.2.4"),
        (True, "x", "1.2.3"),
        (True, "||", "1.3.4"),
        (True, "~ 1.0", "1.0.2"),
        (True, "~ 1.0.3", "1.0.12"),
        (True, "~1", "1.2.3"),
        (True, "~1.0", "1.0.2"),
        (True, "~1.2.1 1.2.3 >=1.2.3", "1.2.3"),
        (True, "~1.2.1 1.2.3", "1.2.3"),
        (True, "~1.2.1 1.2.3", "1.2.3"),
        (True, "~1.2.1 =1.2.3", "1.2.3"),
        (True, "~1.2.1 >=1.2.3 1.2.3", "1.2.3"),
        (True, "~1.2.1 >=1.2.3", "1.2.3"),
        (True, "~2.4", "2.4.0"),
        (True, "~2.4", "2.4.5"),
        (True, "~> 1", "1.2.3"),
        (True, "~>1", "1.2.3"),
        (True, "~v0.5.4-pre", "0.5.4"),
        (True, "~v0.5.4-pre", "0.5.5"),
        (True, "~>3.2.1", "3.2.2")
        ]
  forM_ testCases $ \(expectedMatchBool, range, version) -> do
    let fail = expectationFailure
    it (show version <> " satisfies range " <> show range) $ do
      case (parseSemVerRange range, parseSemVer version) of
        (Left err, _) -> fail $ "Semver range parse failed: " <> show err
        (_, Left err) -> fail $ "Semver parse failed: " <> show err
        (Right range, Right version) -> matches range version `shouldBe` expectedMatchBool