Skip to content

Commit f156440

Browse files
authored
Merge pull request #325 from librecaptcha/migrate-serverspec-scalatest-5720858097108799958
Migrate ServerSpec to ScalaTest and refactor LCFramework for tests
2 parents 0919b8b + fbacf17 commit f156440

4 files changed

Lines changed: 79 additions & 63 deletions

File tree

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ lazy val root = (project in file(".")).settings(
1515
libraryDependencies += "com.sksamuel.scrimage" % "scrimage-core" % "4.3.10",
1616
libraryDependencies += "com.sksamuel.scrimage" % "scrimage-filters" % "4.3.10",
1717
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.31",
18-
18+
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % Test,
1919
)
2020

2121
Compile / unmanagedResourceDirectories += { baseDirectory.value / "lib" }

src/main/scala/lc/Main.scala

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,54 @@ import lc.server.Server
55
import lc.background.BackgroundTask
66
import lc.database.Statements
77

8-
object LCFramework {
9-
def main(args: scala.Array[String]): Unit = {
10-
val configFilePath = if (args.length > 0) {
11-
args(0)
12-
} else {
13-
"data/config.json"
14-
}
8+
class LCFramework {
9+
private var backgroundTask: Option[BackgroundTask] = None
10+
private var server: Option[Server] = None
11+
12+
def start(configFilePath: String = "data/config.json"): Unit = {
1513
val config = new Config(configFilePath)
1614
Statements.maxAttempts = config.maxAttempts
1715
val captchaProviders = new CaptchaProviders(config = config)
1816
val captchaManager = new CaptchaManager(config = config, captchaProviders = captchaProviders)
19-
val backgroundTask = new BackgroundTask(config = config, captchaManager = captchaManager)
20-
backgroundTask.beginThread(delay = config.threadDelay)
21-
val server = new Server(
17+
val task = new BackgroundTask(config = config, captchaManager = captchaManager)
18+
task.beginThread(delay = config.threadDelay)
19+
backgroundTask = Some(task)
20+
21+
val srv = new Server(
2222
address = config.address,
2323
port = config.port,
2424
captchaManager = captchaManager,
2525
playgroundEnabled = config.playgroundEnabled,
2626
corsHeader = config.corsHeader
2727
)
28+
srv.start()
29+
server = Some(srv)
30+
}
31+
32+
def stop(): Unit = {
33+
println("Shutting down gracefully...")
34+
backgroundTask.foreach(_.shutdown())
35+
server.foreach(_.stop())
36+
}
37+
}
38+
39+
object LCFramework {
40+
def main(args: scala.Array[String]): Unit = {
41+
val configFilePath = if (args.length > 0) {
42+
args(0)
43+
} else {
44+
"data/config.json"
45+
}
46+
47+
val framework = new LCFramework()
2848

2949
Runtime.getRuntime.addShutdownHook(new Thread {
3050
override def run(): Unit = {
31-
println("Shutting down gracefully...")
32-
backgroundTask.shutdown()
51+
framework.stop()
3352
}
3453
})
3554

36-
server.start()
55+
framework.start(configFilePath)
3756
}
3857
}
3958

src/main/scala/lc/server/Server.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,9 @@ class Server(
116116
println("Starting server on " + address + ":" + port)
117117
server.start()
118118
}
119+
120+
def stop(): Unit = {
121+
println("Stopping server...")
122+
server.stop(0)
123+
}
119124
}

src/test/scala/lc/ServerSpec.scala

Lines changed: 41 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,50 @@
11
package lc.server
22

3+
import org.scalatest.funsuite.AnyFunSuite
4+
import org.scalatest.BeforeAndAfterAll
35
import java.net.{HttpURLConnection, URL}
46
import java.io.{BufferedReader, InputStreamReader, OutputStreamWriter}
57
import lc.LCFramework
68

7-
object ServerSpec {
8-
def main(args: Array[String]): Unit = {
9-
// Start server before tests in a thread
10-
val serverRunnable = new Runnable {
11-
override def run(): Unit = {
12-
try {
13-
LCFramework.main(Array.empty)
14-
} catch {
15-
case _: InterruptedException => // Expected on shutdown
16-
}
17-
}
18-
}
19-
val serverThread = new Thread(serverRunnable)
20-
serverThread.start()
21-
22-
// Give the server a few seconds to start
23-
Thread.sleep(5000)
24-
25-
try {
26-
println("Running ServerSpec Test...")
27-
val url = new URL("http://localhost:8888/v2/captcha")
28-
val connection = url.openConnection().asInstanceOf[HttpURLConnection]
29-
connection.setRequestMethod("POST")
30-
connection.setRequestProperty("Content-Type", "application/json")
31-
connection.setDoOutput(true)
32-
33-
val payload = """{"level":"easy","media":"image/png","input_type":"text","size":"350x100"}"""
34-
val out = new OutputStreamWriter(connection.getOutputStream)
35-
out.write(payload)
36-
out.close()
37-
38-
val responseCode = connection.getResponseCode
39-
assert(responseCode == 200, s"Expected 200 but got $responseCode")
40-
41-
val in = new BufferedReader(new InputStreamReader(connection.getInputStream))
42-
val response = new StringBuilder
43-
var line: String = in.readLine()
44-
while (line != null) {
45-
response.append(line)
46-
line = in.readLine()
47-
}
48-
in.close()
49-
50-
val responseString = response.toString()
51-
assert(responseString.contains("id"), "Response did not contain an id")
52-
println("Test Passed.")
53-
} finally {
54-
// Shutdown server without exit so SBT doesn't kill the VM
55-
System.exit(0)
9+
class ServerSpec extends AnyFunSuite with BeforeAndAfterAll {
10+
11+
val framework = new LCFramework()
12+
13+
override def beforeAll(): Unit = {
14+
framework.start("tests/debug-config.json")
15+
// Give the server a moment to start and generate some captchas
16+
Thread.sleep(2000)
17+
}
18+
19+
override def afterAll(): Unit = {
20+
framework.stop()
21+
}
22+
23+
test("Server should respond with an id for a valid captcha request") {
24+
val url = new URL("http://localhost:8888/v2/captcha")
25+
val connection = url.openConnection().asInstanceOf[HttpURLConnection]
26+
connection.setRequestMethod("POST")
27+
connection.setRequestProperty("Content-Type", "application/json")
28+
connection.setDoOutput(true)
29+
30+
val payload = """{"level":"debug","media":"image/png","input_type":"text","size":"350x100"}"""
31+
val out = new OutputStreamWriter(connection.getOutputStream)
32+
out.write(payload)
33+
out.close()
34+
35+
val responseCode = connection.getResponseCode
36+
assert(responseCode == 200, s"Expected 200 but got $responseCode")
37+
38+
val in = new BufferedReader(new InputStreamReader(connection.getInputStream))
39+
val response = new StringBuilder
40+
var line: String = in.readLine()
41+
while (line != null) {
42+
response.append(line)
43+
line = in.readLine()
5644
}
45+
in.close()
46+
47+
val responseString = response.toString()
48+
assert(responseString.contains("id"), "Response did not contain an id")
5749
}
5850
}

0 commit comments

Comments
 (0)