Play Framework: Configuring system properties - scala

In play framework (2.2.1 & sbt 0.13) I have an IntegrationSpec that brings up a TestServer. I need to be able to set SSL specific System Properties for the TestServer. So far the only way I have been able to set it up correctly is passing them as command line properties like below
play -Djavax.net.ssl.keyStore=... -Djavax.net.ssl.keyStorePassword=.... -D... test
I want the tests to run simply using play test. For that in Build.scala I configured SBT javaOptions as follows
val main = play.Project(appName, appVersion, appDependencies).settings(
Keys.fork in Test := false,
javaOptions in Test += "-Dconfig.file=conf/application.test.conf")
And in application.test.conf I set all the system properties. With this the TestServer is not even using application.test.conf. I was not able to figure out why. So I thought I will try the following:
play -Dconfig.file=conf/application.test.conf test
The TestServer did use application.test.conf but none of the system properties (javax.net.ssl.keyStore="..." etc.) configured in the file were being used.
So I have two questions
How to have this running only using play test? . (I do not want to pass a long Map of properties to FakeApplication in TestServer).
When I run play -Dconfig.file=conf/application.test.conf test, why are the system properties configured in application.test.conf not being used?

I'm not sure if this works for setting a property for reading the configuration file, but you can set your individual properties with System.setProperty like this:
System.setProperty("application.secret","psssst!")

Related

How to use a separate configuration file for tests in CI, using Play and sbt?

For a project based on Play with sbt, I'd like to have multiple flavors for test runs, using different configuration files. Motivation is being able to run tests either against a local or a remote database.
There's already a custom config file speicified for general test runs (in build.sbt):
javaOptions in Test += "-Dconfig.file=conf/application.test.conf"
Now I would like to have another command where the same tests run against some configuration file conf/application.test-ci.conf.
Approaches tried so far
Adding a command alias
addCommandAlias("test-ci", ";test -Dconfig.file=conf/application.test-ci.conf")
This fails with an error message of a missing semicolon (;), indicating that sbt interprets the resulting command line as multiple commands, but I don't understand why.
Extend Test
lazy val CITest = config("ci") extend Test
lazy val config = (project in file(".")).enablePlugins(PlayScala)
.configs(CITest)
.settings(inConfig(CITest)(Defaults.testTasks): _*)
.settings(
javaOptions in CITest += "-Dconfig.file=conf/application.test-ci.conf"
)
javaOptions in CITest -= "-Dconfig.file=conf/application.test.conf"
I'm don't fully understand what this is doing, but it always seems to pick up the other test configuration file.
How can I specify multiple test setups picking up different configuration files?
Try first applying the setting via set command and then follow up with test task like so
addCommandAlias(
"test-ci",
""";set Test/javaOptions ++= Seq("-Dconfig.file=conf/application.test.con"); test"""
)
Notice how ; separates the set from test.
Another approach is to modify the setting based on the environment. Usually there is some environment variable set on CI like CI or BUILD, so you can modify javaOptions conditionally (without any custom configurations):
Test/javaOptions ++= {
if (sys.env.get("CI").isEmpty) Seq.empty
else Seq("-Dconfig.file=conf/application.test-ci.conf")
}
Note: Test/javaOptions is the new syntax for javaOptions in Test (since sbt 1)

Environment variable based runtime configuration with sbt native packager

I'm using the sbt native packager plugin to create a zip file of my application for deployment to elastic beanstalk. I would like to set environment variables in my beanstalk environment and have those be used to configure my application at runtime. I've attempted to reference the env variables in my Procfile like so:
web: ./bin/bridgeservice -Dhttp.port=$PORT
This does not work as $PORT is not interpolated by the start script generated by the packager.
I've also attempted to define the variables in my build.sbt like so:
import scala.util.Properties
javaOptions in Universal ++= Seq(
"-Dhttp.port=" + Properties.envOrElse("PORT", "9004"),
)
This also does not work as the packager expects the PORT env variable at the time of building the distributable zip and hardcodes the default value of 9004 in an application.ini file.
Is it possible to dynamically pass java options based on environment variables at application startup?
The settings in javaOptions in Universal are compiled into conf/application.ini file, but accordingly to sbt-native-packager docs application.ini currently does not support variable substitution:
The file will be installed to ${app_home}/conf/application.ini and
read from there by the startscript. You can use # for comments and new
lines as you like. This file currently doesn’t has any variable
substitution. We recommend using the build.sbt if you need any
information from your build.
So,the env var based runtime settings can be achieved in several ways:
Solution #1. Add extra definitions to generated start script
In build.sbt:
bashScriptExtraDefines += """addJava "-Dhttp.port=${PORT:-9004}""""
Check out Application and runtime configuration documentation for more info.
Solution #2: Set JAVA_OPTS env var in the target server
Just set JAVA_OPTS environment variable on the target server and make it available for the start script. This can be the easiest solution for environments like AWS ElasticBeanstalk where env vars can be set on the app's environment configuration page.
Not sure if this will help, but I had similar issue when building docker images of my multi module project.
I ended with this:
def sysPropOrDefault(propName: String, default: String): String = Option(System.getProperty(propName)).getOrElse(default)
val somePort = sysPropOrDefault("port", "9004")
and in the project definition:
lazy val someProject = project("some-project")
.enablePlugins(JavaServerAppPackaging)
.settings(
javaOptions in Universal ++= Seq(
s"-Dhttp.port=$somePort"
)
)
Adding javaOptions to project settings level was crucial in my case. And s before option is not a typo.
When running command from terminal I called:
sbt clean update -Dport=9005 docker:publishLocal

How to run sbt tests for debugging when debug is disabled by default?

I find it incredibly awkward to have to restart sbt with special flags if I want to run the tests (or a main) with debug enabled. It's also a pain if the main or test is usually in a forked JVM:
How to set fork in Test when -jvm-debug given on command line?
Is there any simple way to conditionally do a run, test, test-quick or test-only and ask for debugging to be enabled in the forked process? e.g. with syntax like test-only -jdb
I don't really want to have to write my own Tasks to do this... maintaining them is going to be a nightmare. But I guess that would allow syntax like module/jdb:test-only
While Eugene accurately mentions that we could provide debug:testOnly out of the box, the following should help you out:
val DebugTest = config("dtest") extend Test
lazy val myproject =
project.configs(DebugTest).
settings(inConfig(DebugTest)(Defaults.testSettings):_*).
settings(
fork in DebugTest := true,
javaOptions in DebugTest += "debugging options",
definedTests in DebugTest := (definedTests in Test).value
)
This should allow you to do dtest:testOnly *, dtest:run and dtest:test on myproject. The forked JVM (note fork in DebugTest := true) will use whatever debugging options you've provided.
I don't think there's any simple feature that enables debugging out of the box.
As you mentioned on sbt-dev list, making a custom configuration like debug:testOnly sounds like a good strategy.

How to create a custom package task to jar a subset of classes in SBT

I am trying to define a separate package task without modifying the original task in compile configuration. This new task will package only a subset of classes conforming an API which we need to be able to share with other teams so they can write plugins for our application. So the end result will be two jars, one with the full application and a second one with a subset of the classes.
I approached this problem by creating a different configuration which I called pluginApi and would redefine the packageBin task within this new configuration so it does not change the original definition of packageBin. This idea was taken from here:
How to create custom "package" task to jar up only specific package in SBT?
In my build.stb I have:
lazy val PluginApi = config("pluginApi") extend(Compile) describedAs("Custom plugin api configuration")
lazy val root = project in file(".") overrideConfigs (PluginApi)
This effectively creates my new configuration and I can call
sbt pluginApi:packageBin
Which generates the complete jar in the same way as compile:packageBin would do. I then try to modify the mappings in the new packageBin task with:
mappings in (PluginApi, packageBin) ~= { (ms: Seq[(File, String)]) =>
ms filter { case (file, toPath) =>
toPath.startsWith("some/path/defining/api")
}
}
but this has no effect. I think the reason is because the call to pluginApi:packageBin is delegated to compile:packageBin rather than it being a cloned task.
I can redefine a new packageBin within the new scope like:
packageBin in PluginApi := {
}
However I would have to rewrite all packageBin functionality instead of reusing existing code. Also, in case that rewriting is unavoidable I am not sure how that implementation would be.
Could somebody provide an example about how to achieve this?
You could have it done as follows
lazy val PluginApi = config("pluginApi").extend(Compile)
inConfig(PluginApi)(Defaults.compileSettings) // you have to have standard
mappings in (PluginApi, packageBin) := {
val original = (mappings in (PluginApi, packageBin)).value
original.filter { case (file, toPath) => toPath.startsWith("some/path/defining/api") }
}
unmanagedSourceDirectories in PluginApi := (unmanagedSourceDirectories in Compile).value
Note that, if you keep your sources in src/main/scala you'll have to override unmanagedSourceDirectories in the newly created configuration.
Normally the unmanagedSourceDirectories contains the configuration name. E.g. src/pluginApi/scala or src/pluginApi/java.
I have had similar problems (with more than one jar per project). Our project uses ant - here you can do it, you just will repeat yourself a lot.
However, I have come to the conclusion that this scenario (2 JARs for one project) actually can be simplified by splitting the project - i.e. making 2 modules out of it.
This way, I don't have to "fight" tools which assume project==artifact (like sbt, maybe maven?, IDEA's default setting,...).
As a bonus point the compiler helps me to verify that my dependencies are correct, i.e. that I did not accidentally make my API package depend on the implementation package - when compiling everything together and only splitting classes apart in the JAR step, you do run the risk of getting an invalid dependency in your setup which you would only see when testing, because during compile time everything is compiled together.

Play! framework: customize which tests are run

I have a Play! 2 for Scala application, and I am using Specs2 for tests. I can run all tests with the test command, or a particular specification with test-only MyParticularSpec.
What I would like to do is mark some particular specifications, or even single methods inside a specification, in order to do things like:
running all tests that are not integration (that is, that do not access external resources)
running all tests that do not access external resources in write mode (but still running the reading tests)
running all tests but a given one
and so on.
I guess something like that should be doable, perhaps by adding some annotations, but I am not sure how to go for it.
Does there exist a mechanism to selectively run some tests and not other ones?
EDIT I have answered myself when using test-only. Still the command line option does not work for the test task. Following the sbt guide I have tried to create an additional sbt configuration, like
object ApplicationBuild extends Build {
// more settings
lazy val UnitTest = config("unit") extend(Test)
lazy val specs = "org.scala-tools.testing" %% "specs" % "1.6.9" % "unit"
val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA)
.configs(UnitTest)
.settings(inConfig(UnitTest)(Defaults.testTasks) : _*)
.settings(
testOptions in UnitTest += Tests.Argument("exclude integration"),
libraryDependencies += specs
)
}
This works when I pass arguments without options, for instance when I put Test.Argument("plan"). But I was not able to find how to pass a more complex argument. I have tried
Tests.Argument("exclude integration")
Tests.Argument("exclude=integration")
Tests.Argument("-exclude integration")
Tests.Argument("-exclude=integration")
Tests.Argument("exclude", "integration")
Tests.Argument("exclude \"integration\"")
and probably more. Still not any clue what is the correct syntax.
Does anyone know how to pass arguments with options to specs2 from sbt?
First, following the specs2 guide one must add tags to the specifications, like this
class MySpec extends Specification with Tags {
"My spec" should {
"exclude this test" in {
true
} tag ("foo")
"include this one" in {
true
}
}
}
The command line arguments to include are documented here
Then one can selectively include or exclude test with
test-only MySpec -- exclude foo
test-only MySpec -- include foo
You can also use without any change to your build
test-only * -- exclude integration
Tested in Play 2.1-RC3
If you want to pass several arguments you can add several strings to one Test.Argument
testOptions in Test += Tests.Argument("include", "unit")
There are examples of this in the specs2 User Guide here and in the Play documentation there.
I'm using Play2.2, and there are 2 ways to do this depending on whether or not you are in the play console.
From the console type: test-only full.namespace.to.TestSpec
From the terminal type: test-only "full.namespace.to.TestSpec"
I came across this question while trying to figure out how to do something similar for ScalaTest with Play. SBT has detailed documentation on how to configure additional test configurations but these could use a bit of tweaking for Play.
Apart from the subtly different Project configuration I found that I wanted to crib a bunch of the test settings from PlaySettings. The following is running and generating an Intellij project with integration test sources in the "/it" directory. I may still be missing reporting and lifecycle hooks,
object BuildSettings {
def testSettings = {
// required for ScalaTest. See http://stackoverflow.com/questions/10362388/using-scalatest-in-a-playframework-project
testOptions in Test := Nil
}
def itSettings = {
// variously cribbed from https://github.com/playframework/Play20/blob/master/framework/src/sbt-plugin/src/main/scala/PlaySettings.scala
sourceDirectory in IntegrationTest <<= baseDirectory / "it"
scalaSource in Test <<= baseDirectory / "it"
libraryDependencies += "play" %% "play-test" % play.core.PlayVersion.current % "it"
}
}
object ApplicationBuild extends Build {
val main = play.Project(
appName,
appVersion,
Dependencies.dependencies)
.configs( IntegrationTest )
.settings(Dependencies.resolutionRepos)
.settings(BuildSettings.testSettings)
.settings(Defaults.itSettings : _*)
.settings(BuildSettings.itSettings)
}