Corda - Testing a Contract with 'at least' One Output - corda

I am trying to test a contract command where the rules are:
"Zero input states must be consumed" using (tx.inputs.isEmpty())
"At least one output must be created" using (tx.outputs.isNotEmpty())
How exactly do I test "At least one output must be created"?
If I test with an input, the test fails the first verification (inputs must be zero).
If I test with zero inputs/outputs, the test fails because a transaction must either consume an input, or produce an output.
If I test with a reference state, it seems not to work, suggesting an exception was expected but didn't get one.

If you run a test with no inputs and no outputs you will get an error before your contract code is run:
java.lang.AssertionError: Expected exception containing 'At least one output must be created' but raised exception was 'java.lang.IllegalStateException: A transaction must contain at least one input or output state'
Which is your second point in the question
If I test with zero inputs/outputs, the test fails because a transaction must either consume an input, or produce an output.
This means that you can't actually test this code in a way that checks for your specific message.
e.g. with
this `fails with` "At least one output must be created"
Essentially, your contract code checking for at least one output state is redundant, whilst you have the code that checks for zero input states.
However, you could write the test to expect the IllegalStateException.
This way, if your code changes and inputs subsequently become valid you will have a failing test that will alert you.
An alternative to checking for at least one output state in your contract code is to check for at least one state of a specific type.
e.g.
"Output states must be Demo States." using (tx.outputsOfType<DemoState>().isNotEmpty())
This way you will be able to write a meaningful test.
e.g.
Contract:
class DemoContract : Contract {
companion object {
#JvmStatic
val DEMO_CONTRACT_ID: String = DemoContract::class.java.name
}
interface Commands : CommandData {
class Issue : TypeOnlyCommandData(), Commands
}
override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<Commands>()
val signers = command.signers.toSet()
when (command.value) {
is Commands.Issue -> verifyIssue(tx, signers)
else -> throw IllegalArgumentException("Unrecognised command.")
}
}
private fun verifyIssue(tx: LedgerTransaction, signers: Set<PublicKey>) = requireThat {
"No inputs are required when creating a new demo." using (tx.inputStates.isEmpty())
"Output states must be Demo States." using (tx.outputsOfType<DemoState>().isNotEmpty())
}
}
Test:
class DemoContractTests {
private val owner = TestIdentity(CordaX500Name("Series", "London", "GB"))
private val borrower = TestIdentity(CordaX500Name("One", "London", "GB"))
private val mockServices = MockServices(owner)
#Test
fun `Demo issue must have no inputs`() {
mockServices.ledger {
transaction {
input(DEMO_CONTRACT_ID, DummyState())
command(listOf(owner.publicKey, borrower.publicKey), DemoContract.Commands.Issue())
this `fails with` "No inputs are required when creating a new Demo."
}
}
}
#Test
fun `There must only be DemoState outputs`() {
mockServices.ledger {
transaction {
output(DEMO_CONTRACT_ID, DummyState(0))
command(listOf(owner.publicKey, borrower.publicKey), DemoContract.Commands.Issue())
this `fails with` "Output states must be Demo States."
}
}
}
}

Related

picocli : List<> Option, used in #ArgGroup, duplicated in short usage string

When a List<> option is used in an #ArgGroup, it is duplicated in the short usage help. Consider the following code:
import picocli.CommandLine;
import picocli.CommandLine.*;
import picocli.CommandLine.Model.CommandSpec;
#Command(name = "MyApp")
public class App implements Runnable {
#ArgGroup(exclusive=true) // or false
MyGroup myGroup;
static class MyGroup {
#Option(names="-A", paramLabel="N", split=",") List<Long> A;
}
#Spec CommandSpec spec;
#Override
public void run() {
System.out.printf("OK: %s%n", spec.commandLine().getParseResult().originalArgs());
}
public static void main(String[] args) {
new CommandLine(new App()).execute("-h");
}
}
Shows the following output
Usage: MyApp [[-A=N[,N...]] [-A=N[,N...]]...]
I was expecting the output
Usage: MyApp [-A=N[,N...]]
#ArgGroup is needed in the code for other reasons, it may seem futile in this toy example.
You may have found a bug in picocli.
Would you mind raising this on the picocli issue tracker?
Update:
The short story
This was a bug. In the next version of picocli, the expected synopsis can be achieved by setting the argument group to exclusive = false.
The long story
This synopsis stuff can get quite complex... Let's break it down.
Option Synopsis
Before we go into argument groups, let's first look at simple options. Picocli shows a different synopsis for required and non-required options, and for single-value and multi-value options.
The below table illustrates. Note especially the notation for required multi-value options. Such options must be specified at least once, but possibly multiple times, and the synopsis reflects this:
Required Non-Required
--------- ------------
Single value -x=N [-x=N]
Multi-value -x=N [-x=N]... [-x=N]...
Argument Group Synopsis
Now, let's look at groups. In exclusive groups (the default), all arguments are automatically made required. (There is some history behind this, but basically anything else did not make sense.) In non-exclusive groups, options can be required or optional.
Groups have a multiplicity. The default is multiplicity = "0..1" meaning the group is optional, and this is shown in the synopsis by surrounding the group with [ and ] square brackets.
Now, let's put these together. The table below shows the synopsis for groups with two options, -x and -y:
Exclusive Group Non-Exclusive Group
--------------------------------- -------------------
Single value [-x=N | -y=M] [[-x=N] [-y=M]]
Multi-value [-x=N [-x=N]... | -y=M [-y=M]...] [[-x=N]... [-y=M]...]
Split Regex Synopsis
The final element: when the option accepts a split="," regex, the N parameter label becomes N[,N...] in the synopsis.
Problem: synopsis too long
When I execute your example with picocli 4.3.2, I get the following synopsis:
Usage: MyApp [[-A=N[,N...]] [-A=N[,N...]]...]
This is incorrect, and does not follow the specifications above.
With picocli 4.3.3-SNAPSHOT, I get the correct synopsis:
Usage: MyApp [-A=N[,N...] [-A=N[,N...]]...]
Given the above, we now know why: this is the synopsis for a multi-value option on an exclusive group. The option became a required option because the group is exclusive.
Getting a shorter synopsis
With picocli 4.3.3, one idea is to make the group non-exclusive (after all, with only one option, exclusive or non-exclusive does not matter). The program is almost unchanged (exclusive = false instead of true):
#Command(name = "MyApp")
public class App implements Runnable {
#ArgGroup(exclusive = false) // was: exclusive=true
MyGroup myGroup;
static class MyGroup {
#Option(names="-A", paramLabel="N", split=",")
List<Long> A;
}
// ...
}
The synopsis of the usage help message now looks like this:
Usage: MyApp [[-A=N[,N...]]...]
I hope this explains things.

PlaySpec test passing even though the result doesn't match

I have written the following spec. Surprisingly it passes even though the result doesn't match
code snippet
val controller = new UserController(mockUserRepository,mockControllerComponents,mockSilhouette)
//val request = FakeRequest[AnyContentAsJson]("POST", "/ws/users/signup").withJsonBody(Json.parse("""{"bad": "field"}"""))//FakeRequest(POST,"/ws/users/signup").withJsonBody(Json.parse("""{"bad":"field"}"""));
val request = FakeRequest("POST","ws/users/signup")
println("sending request",request)
//val result = controller.someMethod()
val result = controller.signupUser(request)
Await.result(result,Duration("10 secs"))
result.map(response => {
println("response from controller:"+response)
response mustBe play.api.mvc.Results.BadRequest
})
console prints
(sending request,POST ws/users/signup)
print in controller. (received request,POST ws/users/signup)
Controller returns 200OK but I am matching it with BadRequest. Still the test passes!
response from controller:Result(200, Map())
I suspect that I am not matching the result correctly. I am doing response mustBe play.api.mvc.Results.BadRequest. I know that response is Results but BadRequest is Status. But I don'w know how else to match and also why the test doesn't fail. I also tried following and can see the the values are different but even then the test passes.
println("response from controller:"+response.header.status+" compared with "+play.api.mvc.Results.BadRequest.header.status)
response.header.status mustBe play.api.mvc.Results.BadRequest.header.status
console print - response from controller:200 compared with 400
Importing members of Helpers object like so
import play.api.test.Helpers._
gives access to various utility methods to extract results from Future[Result] in tests. For example, Helpers.status can be used to extract status code like so:
val result: Future[Result] = controller.signupUser(request)
status(result) mustBe BAD_REQUEST
Another option is to use whenReady from ScalaFutures:
val result: Future[Result] = controller.signupUser(request)
whenReady(result) { _.header.status mustBe BAD_REQUEST }
Another option is to extend AsyncWordSpec and then you can map over the Future like so:
val result: Future[Result] = controller.signupUser(request)
result.map(_.header.status mustBe BAD_REQUEST)

Filtering tab completion in input task implementation

I'm currently implementing a SBT plugin for Gatling.
One of its features will be to open the last generated report in a new browser tab from SBT.
As each run can have a different "simulation ID" (basically a simple string), I'd like to offer tab completion on simulation ids.
An example :
Running the Gatling SBT plugin will produce several folders (named from simulationId + date of report generaation) in target/gatling, for example mysim-20140204234534, myothersim-20140203124534 and yetanothersim-20140204234534.
Let's call the task lastReport.
If someone start typing lastReport my, I'd like to filter out tab-completion to only suggest mysim and myothersim.
Getting the simulation ID is a breeze, but how can help the parser and filter out suggestions so that it only suggest an existing simulation ID ?
To sum up, I'd like to do what testOnly do, in a way : I only want to suggest things that make sense in my context.
Thanks in advance for your answers,
Pierre
Edit : As I got a bit stuck after my latest tries, here is the code of my inputTask, in it's current state :
package io.gatling.sbt
import sbt._
import sbt.complete.{ DefaultParsers, Parser }
import io.gatling.sbt.Utils._
object GatlingTasks {
val lastReport = inputKey[Unit]("Open last report in browser")
val allSimulationIds = taskKey[Set[String]]("List of simulation ids found in reports folder")
val allReports = taskKey[List[Report]]("List of all reports by simulation id and timestamp")
def findAllReports(reportsFolder: File): List[Report] = {
val allDirectories = (reportsFolder ** DirectoryFilter.&&(new PatternFilter(reportFolderRegex.pattern))).get
allDirectories.map(file => (file, reportFolderRegex.findFirstMatchIn(file.getPath).get)).map {
case (file, regexMatch) => Report(file, regexMatch.group(1), regexMatch.group(2))
}.toList
}
def findAllSimulationIds(allReports: Seq[Report]): Set[String] = allReports.map(_.simulationId).distinct.toSet
def openLastReport(allReports: List[Report], allSimulationIds: Set[String]): Unit = {
def simulationIdParser(allSimulationIds: Set[String]): Parser[Option[String]] =
DefaultParsers.ID.examples(allSimulationIds, check = true).?
def filterReportsIfSimulationIdSelected(allReports: List[Report], simulationId: Option[String]): List[Report] =
simulationId match {
case Some(id) => allReports.filter(_.simulationId == id)
case None => allReports
}
Def.inputTaskDyn {
val selectedSimulationId = simulationIdParser(allSimulationIds).parsed
val filteredReports = filterReportsIfSimulationIdSelected(allReports, selectedSimulationId)
val reportsSortedByDate = filteredReports.sorted.map(_.path)
Def.task(reportsSortedByDate.headOption.foreach(file => openInBrowser((file / "index.html").toURI)))
}
}
}
Of course, openReport is called using the results of allReports and allSimulationIds tasks.
I think I'm close to a functioning input task but I'm still missing something...
Def.inputTaskDyn returns a value of type InputTask[T] and doesn't perform any side effects. The result needs to be bound to an InputKey, like lastReport. The return type of openLastReport is Unit, which means that openLastReport will construct a value that will be discarded, effectively doing nothing useful. Instead, have:
def openLastReport(...): InputTask[...] = ...
lastReport := openLastReport(...).evaluated
(Or, the implementation of openLastReport can be inlined into the right hand side of :=)
You probably don't need inputTaskDyn, but just inputTask. You only need inputTaskDyn if you need to return a task. Otherwise, use inputTask and drop the Def.task.

Geb: Waiting/sleeping between tests

Is there a way to wait a set amount of time between tests? I need a solution to compensate for server lag. When creating a record, it takes a little bit of time before the record is searchable in my environment.
In the following code example, how would I wait 30 seconds between the first test and the second test and have no wait time between second test and third test?
class MySpec extends GebReportingSpec {
// First Test
def "should create a record named myRecord"() {
given:
to CreateRecordsPage
when:
name_field = "myRecord"
and:
saveButton.click()
then:
at IndexPage
}
// Second Test
def "should find record named myRecord"() {
given:
to SearchPage
when:
search_query = "myRecord"
and:
searchButton.click()
then:
// haven't figured this part out yet, but would look for "myRecord" on the results page
}
// Third Test
def "should delete the record named myRecord"() {
// do the delete
}
}
You probably don't want to wait a set amount of time - it will make your tests slow. You would ideally want to continue as soon as the record is added. You can use Geb's waitFor {} to poll for a condition to be fulfilled.
// Second Test
def "should find record named myRecord"() {
when:
to SearchPage
then:
waitFor(30) {
search_query = "myRecord"
searchButton.click()
//verify that the record was found
}
}
This will poll every half a second for 30 seconds for the condition to be fulfilled passing as soon as it is and failing if it's still not fulfilled after 30 seconds.
To see what options you have for setting waiting time and interval have look at section on waiting in The Book of Geb. You might also want to check out the section on implicit assertions in waitFor blocks.
If your second feature method depends on success of the first one then you should probably consider annotating this specification with #Stepwise.
You should always try to use waitFor and check conditions wherever possible. However if you find there isn't a specific element you can check for, or any other condition to check, you can use this to wait for a specified amount of time:
def sleepForNSeconds(int n) {
def originalMilliseconds = System.currentTimeMillis()
waitFor(n + 1, 0.5) {
(System.currentTimeMillis() - originalMilliseconds) > (n * 1000)
}
}
I had to use this while waiting for some chart library animations to complete before capturing a screenshot in a report.
Thread.sleep(30000)
also does the trick. Of course still agree to "use waitFor whenever possible".

How to get debug output from Codeception Unit Tests?

I might be being thick here, but I can't figure out how to get a little info about why a test failed.
E.g. I have this test which is there to check that we haven't got any new methods with no test...
//TEST ALL METHODS TESTED
public function testAllMethodsTested()
{
$temp = $this->class_namespace.substr(__CLASS__, 0, -5);
$class_methods = get_class_methods($temp);
$test_methods = get_class_methods(__CLASS__);
foreach($class_methods as $class_method)
{
$this->assertEquals(true, in_array('test_'.$class_method, $test_methods));
}
}
When this fails, I get something like...
1) c_functions_core_ext_Test::testAllMethodsTested
Failed asserting that false matches expected true.
To make it easier to see where to go and fix some code, I would like to get some kind of helpful debug output in console and report.html (like you get for acceptance tests) along the lines of...
1) some_class_Test::testAllMethodsTested
Checking test exists for some_class::someMethod
Failed asserting that false matches expected true.
Is this possible for unit tests?
Yes, I am thick. It seems the assert functions allow for specifying your own message...
//TEST ALL METHODS TESTED
public function testAllMethodsTested()
{
$target_class = $this->class_namespace.substr(__CLASS__, 0, -5);
$class_methods = get_class_methods($target_class);
$test_methods = get_class_methods(__CLASS__);
$result = in_array('test_'.$class_method, $test_methods);
//FAIL WITH A USEFUL ERROR
$this->assertTrue($result, 'There is no test for '.$target_class.'::'.$class_method);
}
When this fails, I now get something like...
1) c_functions_core_ext_Test::testAllMethodsTested
There is no test for Namespace\target_class::someMethodName
Failed asserting that false matches expected true.

Resources