aboutsummaryrefslogtreecommitdiff
path: root/src/test/scala/xyz/driver/core/rest/RestTest.scala
blob: 19e4ed17efb19320f92a912325924789b017e0f6 (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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package xyz.driver.core.rest

import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.{Directives, Route, ValidationRejection}
import akka.http.scaladsl.testkit.ScalatestRouteTest
import akka.util.ByteString
import org.scalatest.{Matchers, WordSpec}
import xyz.driver.core.rest

import scala.concurrent.Future
import scala.util.Random

class RestTest extends WordSpec with Matchers with ScalatestRouteTest with Directives {
  "`escapeScriptTags` function" should {
    "escape script tags properly" in {
      val dirtyString = "</sc----</sc----</sc"
      val cleanString = "--------------------"

      (escapeScriptTags(ByteString(dirtyString)).utf8String) should be(dirtyString.replace("</sc", "< /sc"))

      (escapeScriptTags(ByteString(cleanString)).utf8String) should be(cleanString)
    }
  }

  "paginated directive" should {
    val route: Route = rest.paginated { paginated =>
      complete(StatusCodes.OK -> s"${paginated.pageNumber},${paginated.pageSize}")
    }
    "accept a pagination" in {
      Get("/?pageNumber=2&pageSize=42") ~> route ~> check {
        assert(status == StatusCodes.OK)
        assert(entityAs[String] == "2,42")
      }
    }
    "provide a default pagination" in {
      Get("/") ~> route ~> check {
        assert(status == StatusCodes.OK)
        assert(entityAs[String] == "1,100")
      }
    }
    "provide default values for a partial pagination" in {
      Get("/?pageSize=2") ~> route ~> check {
        assert(status == StatusCodes.OK)
        assert(entityAs[String] == "1,2")
      }
    }
    "reject an invalid pagination" in {
      Get("/?pageNumber=-1") ~> route ~> check {
        assert(rejection.isInstanceOf[ValidationRejection])
      }
    }
  }

  "optional paginated directive" should {
    val route: Route = rest.optionalPagination { paginated =>
      complete(StatusCodes.OK -> paginated.map(p => s"${p.pageNumber},${p.pageSize}").getOrElse("no pagination"))
    }
    "accept a pagination" in {
      Get("/?pageNumber=2&pageSize=42") ~> route ~> check {
        assert(status == StatusCodes.OK)
        assert(entityAs[String] == "2,42")
      }
    }
    "without pagination" in {
      Get("/") ~> route ~> check {
        assert(status == StatusCodes.OK)
        assert(entityAs[String] == "no pagination")
      }
    }
    "reject an invalid pagination" in {
      Get("/?pageNumber=1") ~> route ~> check {
        assert(rejection.isInstanceOf[ValidationRejection])
      }
    }
  }

  "completeWithPagination directive" when {
    import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
    import spray.json.DefaultJsonProtocol._

    val data = Seq.fill(103)(Random.alphanumeric.take(10).mkString)
    val route: Route =
      parameter('empty.as[Boolean] ? false) { isEmpty =>
        completeWithPagination[String] {
          case Some(pagination) if isEmpty =>
            Future.successful(ListResponse(Seq(), 0, Some(pagination)))
          case Some(pagination) =>
            val filtered = data.slice(pagination.offset, pagination.offset + pagination.pageSize)
            Future.successful(ListResponse(filtered, data.size, Some(pagination)))
          case None if isEmpty => Future.successful(ListResponse(Seq(), 0, None))
          case None            => Future.successful(ListResponse(data, data.size, None))
        }
      }

    "pagination is defined" should {
      "return a response with pagination headers" in {
        Get("/?pageNumber=2&pageSize=10") ~> route ~> check {
          responseAs[Seq[String]] shouldBe data.slice(10, 20)
          header(ContextHeaders.ResourceCount).map(_.value) should contain("103")
          header(ContextHeaders.PageCount).map(_.value) should contain("11")
        }
      }

      "disallow pageSize <= 0" in {
        Get("/?pageNumber=2&pageSize=0") ~> route ~> check {
          rejection shouldBe a[ValidationRejection]
        }

        Get("/?pageNumber=2&pageSize=-1") ~> route ~> check {
          rejection shouldBe a[ValidationRejection]
        }
      }

      "disallow pageNumber <= 0" in {
        Get("/?pageNumber=0&pageSize=10") ~> route ~> check {
          rejection shouldBe a[ValidationRejection]
        }

        Get("/?pageNumber=-1&pageSize=10") ~> route ~> check {
          rejection shouldBe a[ValidationRejection]
        }
      }

      "return PageCount == 0 if returning an empty list" in {
        Get("/?empty=true&pageNumber=2&pageSize=10") ~> route ~> check {
          responseAs[Seq[String]] shouldBe empty
          header(ContextHeaders.ResourceCount).map(_.value) should contain("0")
          header(ContextHeaders.PageCount).map(_.value) should contain("0")
        }
      }
    }

    "pagination is not defined" should {
      "return a response with pagination headers and PageCount == 1" in {
        Get("/") ~> route ~> check {
          responseAs[Seq[String]] shouldBe data
          header(ContextHeaders.ResourceCount).map(_.value) should contain("103")
          header(ContextHeaders.PageCount).map(_.value) should contain("1")
        }
      }

      "return PageCount == 0 if returning an empty list" in {
        Get("/?empty=true") ~> route ~> check {
          responseAs[Seq[String]] shouldBe empty
          header(ContextHeaders.ResourceCount).map(_.value) should contain("0")
          header(ContextHeaders.PageCount).map(_.value) should contain("0")
        }
      }
    }
  }
}