aboutsummaryrefslogtreecommitdiff
path: root/src/test/scala/xyz/driver/restquery/rest/parsers/SortingParserSuite.scala
blob: 1813181c3b0da8ea0a22088dcd960b050b03826c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package xyz.driver.restquery.http.parsers

import xyz.driver.restquery.http.parsers.TestUtils._
import org.scalacheck.Arbitrary.arbitrary
import org.scalacheck.{Gen, Prop}
import org.scalatest.prop.Checkers
import org.scalatest.{FreeSpecLike, MustMatchers}

class SortingParserSuite extends FreeSpecLike with MustMatchers with Checkers {

  "parse" - {
    "single dimension" - commonTests(singleSortingQueryGen)
    "multiple dimensions in one query" - commonTests(multipleSortingQueryGen)
    "multiple queries" in {
      val r = SortingParser.parse(Set("foo", "bar"), Seq("sort" -> "foo", "sort" -> "bar"))
      r must failWith[ParseQueryArgException]
    }
  }

  private def commonTests(queryGen: Set[String] => Gen[String]): Unit = {
    "valid" in check {
      val inputGen: Gen[(Set[String], String)] = for {
        validDimensions <- dimensionsGen
        sorting         <- queryGen(validDimensions)
      } yield (validDimensions, sorting)

      Prop.forAllNoShrink(inputGen) {
        case (validDimensions, query) =>
          SortingParser.parse(validDimensions, Seq("sort" -> query)).successProp
      }
    }

    "invalid" in check {
      val inputGen: Gen[(Set[String], String)] = for {
        validDimensions <- dimensionsGen
        invalidDimensions <- dimensionsGen.filter { xs =>
                              xs.intersect(validDimensions).isEmpty
                            }
        sorting <- queryGen(invalidDimensions)
      } yield (validDimensions, sorting)

      Prop.forAllNoShrink(inputGen) {
        case (validDimensions, query) =>
          SortingParser.parse(validDimensions, Seq("sort" -> query)).failureProp
      }
    }
  }

  private val dimensionsGen: Gen[Set[String]] = for {
    unPrefixedSize <- Gen.choose(0, 3)
    prefixedSize   <- Gen.choose(0, 3)
    if (unPrefixedSize + prefixedSize) > 0

    unPrefixedDimensions <- Gen.containerOfN[Set, String](unPrefixedSize, Gen.identifier)

    prefixes   <- Gen.containerOfN[Set, String](prefixedSize, Gen.identifier)
    dimensions <- Gen.containerOfN[Set, String](prefixedSize, Gen.identifier)
  } yield {
    val prefixedDimensions = prefixes.zip(dimensions).map {
      case (prefix, dimension) => s"$prefix.$dimension"
    }
    unPrefixedDimensions ++ prefixedDimensions
  }

  private def multipleSortingQueryGen(validDimensions: Set[String]): Gen[String] = {
    val validDimensionsSeq = validDimensions.toSeq
    val indexGen           = Gen.oneOf(validDimensionsSeq.indices)
    val multipleDimensionsGen = Gen.nonEmptyContainerOf[Set, Int](indexGen).filter(_.size >= 2).map { indices =>
      indices.map(validDimensionsSeq.apply)
    }

    for {
      dimensions  <- multipleDimensionsGen
      isAscending <- Gen.containerOfN[Seq, Boolean](dimensions.size, arbitrary[Boolean])
    } yield {
      isAscending
        .zip(dimensions)
        .map {
          case (true, dimension)  => dimension
          case (false, dimension) => "-" + dimension
        }
        .mkString(",")
    }
  }

  private def singleSortingQueryGen(validDimensions: Set[String]): Gen[String] =
    for {
      isAscending <- arbitrary[Boolean]
      dimensions  <- Gen.oneOf(validDimensions.toSeq)
    } yield
      isAscending match {
        case true  => dimensions
        case false => "-" + dimensions
      }

}