Skip to content

Commit 1fa4668

Browse files
authored
Merge pull request #40 from scala-exercises/nm-39-third-party-tests
Tests ensuring the server integrity when running third-party code
2 parents 4deed2d + 6f1d3d2 commit 1fa4668

File tree

4 files changed

+98
-1
lines changed

4 files changed

+98
-1
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ project/plugins/project/
1515
# ensime
1616
.ensime
1717
.ensime_cache/
18+
ensime.sbt
1819

1920
# Scala-IDE specific
2021
.scala_dependencies

.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ after_success:
2626
echo "Deploying to Heroku";
2727
docker login --username=noel.m@47deg.com --password=$heroku_token registry.heroku.com;
2828
sbt dockerBuildAndPush;
29+
sbt smoketests/test;
2930
fi
3031
- if [ "$TRAVIS_PULL_REQUEST" = "true" ]; then
3132
echo "Not in master branch, skipping deploy and release";

build.sbt

+17-1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,22 @@ lazy val `evaluator-server` = (project in file("server"))
8282
.settings(dockerSettings)
8383
.settings(compilerDependencySettings: _*)
8484

85+
lazy val `smoketests` = (project in file("smoketests"))
86+
.dependsOn(`evaluator-server`)
87+
.settings(
88+
name := "evaluator-server-smoke-tests",
89+
libraryDependencies ++= Seq(
90+
"org.scalatest" %% "scalatest" % v('scalaTest) % "test",
91+
"org.http4s" %% "http4s-blaze-client" % v('http4s),
92+
"org.http4s" %% "http4s-circe" % v('http4s),
93+
"io.circe" %% "circe-core" % v('circe),
94+
"io.circe" %% "circe-generic" % v('circe),
95+
"io.circe" %% "circe-parser" % v('circe),
96+
"com.pauldijou" %% "jwt-core" % v('jwtcore)
97+
)
98+
99+
)
100+
85101
onLoad in Global := (Command.process("project evaluator-server", _: State)) compose (onLoad in Global).value
86102
addCommandAlias("publishSignedAll", ";evaluator-sharedJS/publishSigned;evaluator-sharedJVM/publishSigned;evaluator-clientJS/publishSigned;evaluator-clientJVM/publishSigned")
87103

@@ -99,7 +115,7 @@ lazy val dockerSettings = Seq(
99115
.run("useradd", "-m", "evaluator")
100116
.user("evaluator")
101117
.add(artifact, artifactTargetPath)
102-
.cmdRaw(s"java -Dhttp.port=$$PORT -jar $artifactTargetPath")
118+
.cmdRaw(s"java -Dhttp.port=$$PORT -Deval.auth.secretKey=$$EVAL_SECRET_KEY -jar $artifactTargetPath")
103119
},
104120
imageNames in docker := Seq(ImageName(repository = "registry.heroku.com/scala-evaluator-sandbox/web"))
105121
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package org.scalaexercises.evaluator
2+
3+
import org.scalatest._
4+
import org.http4s._
5+
import org.http4s.client.blaze._
6+
import org.http4s.circe._
7+
import io.circe.generic.auto._
8+
import scalaz.concurrent.Task
9+
import scala.concurrent.duration._
10+
11+
import pdi.jwt.{Jwt, JwtAlgorithm}
12+
13+
class Smoketests extends FunSpec with Matchers with CirceInstances {
14+
15+
case class EvaluatorResponse(msg: String,
16+
value: String,
17+
valueType: String,
18+
compilationInfos: Map[String, String])
19+
20+
implicit val decoder: EntityDecoder[EvaluatorResponse] =
21+
jsonOf[EvaluatorResponse]
22+
23+
val validToken = Jwt.encode(
24+
"""{"user": "scala-exercises"}""",
25+
auth.secretKey,
26+
JwtAlgorithm.HS256)
27+
28+
def makeRequest(code: String)(
29+
expectation: EvaluatorResponse => Unit,
30+
failExpectation: Throwable => Unit = fail(_)): Unit = {
31+
32+
val request = new Request(
33+
method = Method.POST,
34+
uri = Uri.uri("http://scala-evaluator-sandbox.herokuapp.com/eval"),
35+
headers = Headers(headers)
36+
).withBody(
37+
s"""{"resolvers" : [], "dependencies" : [], "code" : "$code"}""")
38+
39+
val task = client.expect[EvaluatorResponse](request)
40+
41+
val response = task.unsafePerformSyncAttemptFor(60.seconds)
42+
response.fold(failExpectation, expectation)
43+
}
44+
45+
val headers = List(
46+
Header("Content-Type", "application/json").parsed,
47+
Header("x-scala-eval-api-token", validToken).parsed
48+
)
49+
50+
val client = PooledHttp1Client()
51+
52+
describe("Querying the /eval endpoint") {
53+
it("should succeed for a simple request") {
54+
makeRequest("1 + 1") { evaluatorResponse =>
55+
evaluatorResponse.value shouldBe "2"
56+
}
57+
}
58+
59+
it("should continue to work after calling System.exit") {
60+
makeRequest("System.exit(1)")(
61+
expectation = _ => fail("Request should not succeed"),
62+
failExpectation = _ => ()
63+
)
64+
65+
makeRequest("1 + 1") { evaluatorResponse =>
66+
evaluatorResponse.value shouldBe "2"
67+
}
68+
}
69+
70+
it("should not expose sensitive details by calling sys.env") {
71+
val keywords = List("password", "key", "api")
72+
makeRequest("sys.env") { evaluatorResponse =>
73+
keywords.foreach(kw =>
74+
evaluatorResponse.value.contains(kw) shouldBe false)
75+
}
76+
77+
}
78+
}
79+
}

0 commit comments

Comments
 (0)