How do I iterate over tests in Grails Spock testing when I wish to perform the same tests on many similar fields? - grails

I have a domain class with property pairs:
Boolean statement1
Boolean statement1Missing
These pairs number up to nine.
The following constraints apply to each pairing:
statement1 nullable: true
statement1Missing validator: {val, obj ->
if ( val == true &&
(obj.statement1 == true || obj.statement1 == false)) {
return ['statement.not.missing', 1]
}
if (obj.statement1 == null && val == false) {
return ['statement.is.missing', 1]
}
}
I have devised an integration test that can accommodate one pair of statements:
#Unroll
def "Validation of custom validation for statement1"() {
given: "New data"
def participant = new Data(studyId: "M00001",
formVersion: Version.get(7)
)
and: "an initial set of test values"
participant.statement1 = statement1
participant.statement1Missing = statement1Missing
when: "the validator is invoked"
def isValidConsentData = participant.validate()
then: "the appropriate fields are flagged as errors"
isValidConsentData == anticipatedValid
participant.errors.getFieldError(fieldInError)?.code == errorCode
where:
statement1 | statement1Missing | anticipatedValid | fieldInError | errorCode
true | false | true | null | null
false | false | true | null | null
null | true | true | null | null
null | false | false | "statement1Missing" | "statement.is.missing"
true | true | false | "statement1Missing" | "statement.not.missing"
false | true | false | "statement1Missing" | "statement.not.missing"
}
This handles all the combinations for statement1 that I wish to test.
I have been trying to work out how I can repeat this test for all nine statement pairs. I have tried putting in a loop like this:
(1..9).each { statementNo
...
and ...
participant.("statement" + statementNo) = ("statement" + statementNo)
...
where:
("statement" + StatementNo) | ("statement" + StatementNo + Missing) | ...
}
I have used this type of iteration before when wanting to iterate over properties, but it does not work in Spock. The test is just ignored completely. I really do not want to repeat this code for each statement pair.
I have investigated the use of this type of structure http://www.christianoestreich.com/2012/11/domain-constraints-grails-spock-updated/ but this only allows you to test one property value at a time, whereas I want to test one property value many times.
The other option is to explicitly include every property pair in the 'where' block and every possible outcome, but that would be very cumbersome.
Please provide some suggestions as to how I can use an iterative structure to perform these tests.

How about something like:
...
and: "an initial set of test values"
(1..9).each { statementNo ->
participant."statement$statementNo" = statement
participant."statement${statementNo}Missing" = statementMissing
}
when: "the validator is invoked"
def isValidConsentData = participant.validate()
then: "the appropriate fields are flagged as errors"
(1..9).each { statementNo ->
def fieldInError
if (anticipatedValid) {
fieldInError = null
} else {
fieldInError = "statement${statementNo}Missing"
}
assert isValidConsentData == anticipatedValid
assert participant.errors.getFieldError(fieldInError)?.code == errorCode
}
where:
statement | statementMissing | anticipatedValid | errorCode
true | false | true | null
false | false | true | null
null | true | true | null
null | false | false | "statement.is.missing"
true | true | false | "statement.not.missing"
false | true | false | "statement.not.missing"
Not sure how it behaves for getFieldError(fieldName) when there are is no error for a given field so you may need to add some condition for the second assert using !anticipatedValid if exception would be thrown.
Important is that implicit assert are necessary as each is void thus there would be nothing checked by the test at all otherwise.

Related

F# Bolero how to set the code apart to make the project structure clear?

I'm new to F# and Bolero. and after initialize the project as the docs shows, I find that sample application demonstrates 3 types of web applications but main codes is in one file called Main.fs .
here is part codes:
type Message =
| SetPage of Page
| Increment
| Decrement
| SetCounter of int
| GetBooks
| GotBooks of Book[]
| SetUsername of string
| SetPassword of string
| GetSignedInAs
| RecvSignedInAs of option<string>
| SendSignIn
| RecvSignIn of option<string>
| SendSignOut
| RecvSignOut
| Error of exn
| ClearError
let update remote message model =
let onSignIn = function
| Some _ -> Cmd.ofMsg GetBooks
| None -> Cmd.none
match message with
| SetPage page ->
{ model with page = page }, Cmd.none
| Increment ->
{ model with counter = model.counter + 1 }, Cmd.none
| Decrement ->
{ model with counter = model.counter - 1 }, Cmd.none
| SetCounter value ->
{ model with counter = value }, Cmd.none
| GetBooks ->
let cmd = Cmd.OfAsync.either remote.getBooks () GotBooks Error
{ model with books = None }, cmd
| GotBooks books ->
{ model with books = Some books }, Cmd.none
| SetUsername s ->
{ model with username = s }, Cmd.none
| SetPassword s ->
{ model with password = s }, Cmd.none
| GetSignedInAs ->
model, Cmd.OfAuthorized.either remote.getUsername () RecvSignedInAs Error
| RecvSignedInAs username ->
{ model with signedInAs = username }, onSignIn username
| SendSignIn ->
model, Cmd.OfAsync.either remote.signIn (model.username, model.password) RecvSignIn Error
| RecvSignIn username ->
{ model with signedInAs = username; signInFailed = Option.isNone username }, onSignIn username
| SendSignOut ->
model, Cmd.OfAsync.either remote.signOut () (fun () -> RecvSignOut) Error
| RecvSignOut ->
{ model with signedInAs = None; signInFailed = false }, Cmd.none
| Error RemoteUnauthorizedException ->
{ model with error = Some "You have been logged out."; signedInAs = None }, Cmd.none
| Error exn ->
{ model with error = Some exn.Message }, Cmd.none
| ClearError ->
{ model with error = None }, Cmd.none
As you can see, the code is too long to manage. if I add more functionality I can barely imagine what the file will be like.
so how can I split the code into different files ?
The most basic thing you could do is to split your Message type into multiple separate types and have one corresponding to different aspects of the logic. For example:
type CounterMessage =
| Increment
| Decrement
| SetCounter of int
type BooksMessage =
| GetBooks
| GotBooks of Book[]
type Message =
| SetPage of Page
| CounterMessage of CounterMessage
| BooksMessage of BooksMessage
Then you can similarly split your update function - and move each of the message-specific functions to a separate file:
let updateCounter remote message model =
match message with
| Increment ->
{ model with counter = model.counter + 1 }, Cmd.none
| Decrement ->
{ model with counter = model.counter - 1 }, Cmd.none
| SetCounter value ->
{ model with counter = value }, Cmd.none
let update remote message model =
match message with
| SetPage page ->
{ model with page = page }, Cmd.none
| CounterMessage msg ->
updateCounter remote message model
// (...)
This is something using just basic F# langauge mechanisms. Perhaps Bolero has some more sophisticated methods for structuring projects, but this is simple option will always work. The disadvantage is that you need to handle the "forwarding" of the messages yourself (and this is something that a more sophisticated option may alleviate) - but I think that for a moderately sized project, it is not such a bad things (and it helps making things explicit and clear).

Using KQL 'let' to combine two queries in the same table

I am trying to learn KQL and had a query where I wanted to take 2 values from Windows Event codes 4624 (login) and 4634 (logout) and return them for different scenarios I'm still trying to build.
But primarily I would just like to be able to return the values in a table (print or project?)
let login = SecurityEvent
| where TimeGenerated > ago(1h)
| where EventID == '4624'
| project loginTime = TimeGenerated;
let logout = SecurityEvent
| where TimeGenerated > ago(1h)
| where EventID == '4634'
| project logoutTime = TimeGenerated;
print login
The error I am getting is "'project' operator: Failed to resolve scalar expression named 'login'"
What I would have hoped to see is:
loginTime | logoutTime
----------------------------------------------
01/02/2021 18:46:30 | 01/02/2021 18:45:45
01/02/2021 18:47:30 | 01/02/2021 18:47:45
01/02/2021 18:48:30 | 01/02/2021 18:48:45
Would a join be better? It is in the same table (SecurityEvent), so I thought it would be possible to do it this way?
The dataset is from the MS provided Azure portal: https://portal.azure.com/#blade/Microsoft_Azure_Monitoring_Logs/DemoLogsBlade
Thanks for the help!
The problem is that "login" is a table type but print is expecting a scalar type.
let login = SecurityEvent
| where TimeGenerated > ago(1h)
| where EventID == '4624'
| project loginTime = TimeGenerated;
let logout = SecurityEvent
| where TimeGenerated > ago(1h)
| where EventID == '4634'
| project logoutTime = TimeGenerated;
print toscalar (login)
As to the result you are trying to get, I think this might be what you need:
Updated to improve clarity/perf
let login = SecurityEvent
| where TimeGenerated > ago(1h)
| where EventID == '4624'
| project TargetLogonId, loginTime = TimeGenerated;
let logout = SecurityEvent
| where TimeGenerated > ago(1h)
| where EventID == '4634'
| project TargetLogonId, logoutTime = TimeGenerated;
login
| join kind=leftouter logout on TargetLogonId
| project loginTime, logoutTime
I added some changes that encompass #GenericUser and #Slavik-N suggested and brings out the information that I was looking to calculate:
let login = SecurityEvent
| where TimeGenerated > ago(1h)
| where EventID == '4624'
| where AccountType == 'User'
| project Computer,Account ,TargetLogonId, loginTime = TimeGenerated;
let logout = SecurityEvent
| where TimeGenerated > ago(1h)
| where EventID == '4634'
| where AccountType == 'User'
| project Computer,Account,TargetLogonId, logoutTime = TimeGenerated;
login
| join kind=inner logout on TargetLogonId
| project Computer,Account,loginTime, logoutTime, minute = datetime_diff('minute',logoutTime,loginTime)
| where minute >0
| sort by minute desc

String to Bool value f#

I'm trying to create a function that converts a string to a bool.
This is what I have for now
let iscomb=
Table.parseTable table
|>> fun row -> row.["IsComb"] |> Models.Bool //returns true or false from a table cell
let Bool (s:string) =
match (s) with
| "true" -> true
| "false" -> false
| _-> sprintf "Error: returns %s" s
This return the type bool does not match the type string
So what I have understod is that we cannot mix different types in a match.
Any suggestion on what I could do?
Thanks in advace.
Just for fun, I thought I would demonstrate using Active Patterns for this. Only really worthwhile if you are checking this often and wanting to do different things based on that and you want to expand what is accepted beyond "true" / "false". It also can remove typo errors like "fasle".
First, we create 2 partial Active Patterns that just cleanup checking for Int32 and bool from string.
Note: You could just use the Bool AP below and stop there.
// Partial active pattern that matches on Int32
let (|Int|_|) (value:string) =
match (System.Int32.TryParse(value)) with
| (true, i) -> Some i
| (false,_) -> None
// Partial active pattern that matches on Bool
let (|Bool|_|) (value:string) =
match (System.Boolean.TryParse(value)) with
| (true,b) -> Some b
| _ -> None
Next, we use those 2 partial Active Patterns to cleanly test our string in an active pattern with 2 choices.
// Active pattern that buckets a string into True or False (or throws)
let(|True|False|) (value:string) =
match value with
| null -> False
| Int i when i > 0 -> True
| Int i when i <= 0 -> False
| Bool true -> True
| Bool false -> False
| _ -> failwithf "Cannot convert %s to bool." value
So how would we use this? Here is a test function where we use the Active Pattern to test a few results.
// quick little function to test a string
let test s =
match s with
| True -> printfn "The value %s is true" s
| False -> printfn "The value %s is false" s
Resulting in the following:
// testing
test "true" // The value true is true
test "TRUE" // The value TRUE is true
test "false" // The value false is false
test "False" // The value False is false
test "-1" // The value -1 is false
test "42" // The value 42 is true
test null // The value is false
test "abc" // System.Exception: Cannot convert abc to bool.
For more info:
FSharp for fun and Profit
F# Online meetup talk
You could replace sprintf for failwith, throwing an exception:
let Bool s =
match s with
| "true" -> true
| "false" -> false
| _-> failwith("Error: returns " + s)

Spock does not execute when clause

One of my spock tests is skipping the when clause and moving right to the then clause.
#Unroll
void "test refresh metadata with #consumerNames"() {
setup:
consumerNames.each {
new CredentialConsumer(name: it).save(validate: false)
}
when:
service.refreshMetadata()
then:
consumerNames.each { consumerName ->
Metadata metadata = new Metadata(content: "Metadata for $consumerName")
ConsumerDefinition consumerDefinition = new ConsumerDefinition(name: consumerName, metadata: metadata)
1 * mockApplicationLibraryService.fetchConsumerDefinition(consumerName) >> consumerDefinition
assert CredentialConsumer.findByName(consumerName)?.metadata?.content == metadata.content
}
where:
consumerNames | _
["bacon"] | _
["bacon", "eggs"] | _
}
I get the following results...
Condition not satisfied:
CredentialConsumer.findByName(consumerName)?.metadata?.content == metadata.content
| | | | | | |
| bacon null null | | Metadata for bacon
| | com.datapriviasoftware.completesso.configuration.Metadata#487cd177
| false
com.datapriviasoftware.completesso.CredentialConsumer : 1
Expected :Metadata for bacon
Actual :null
When I step through it with the debugger, it executes the setup clause, skips the when clause, and immediately executes the then clause.
So, I knew that Spock uses AST transformations to process mocks in the then clause before it executes the when clause. When I thought through it, I realized that processing the mocks in the then clause required the entire each loop to execute, so I needed to separate the explicit assertion from the mock definition.
This works...
#Unroll
void "test refresh metadata with #consumerNames"() {
setup:
consumerNames.each {
new CredentialConsumer(name: it).save(validate: false)
}
when:
service.refreshMetadata()
then:
consumerNames.each { consumerName ->
Metadata metadata = new Metadata(content: "Metadata for $consumerName")
ConsumerDefinition consumerDefinition = new ConsumerDefinition(name: consumerName, metadata: metadata)
1 * mockApplicationLibraryService.fetchConsumerDefinition(consumerName) >> consumerDefinition
}
and:
consumerNames.each { consumerName ->
assert CredentialConsumer.findByName(consumerName)?.metadata?.content == "Metadata for $consumerName"
}
where:
consumerNames | _
["bacon"] | _
["bacon", "eggs"] | _
}

How to get a null value when using the head function with an empty list

I have a cypher query like this.
START dep=node:cities(city_code = "JGS"),
arr=node:cities(city_code = "XMN")
MATCH dep-[way:BRANCH2BRANCH_AIRWAY*0..1]->()-->arr
RETURN length(way), transfer.city_code,
extract(w in way: w.min_consume_time) AS consumeTime
The relationship named "way" is a optional one, so the property named "consumeTime" will be a empty list when the relationship "way" not exsit.
The query result is:
| 0 | "JGS" | [] |
| 1 | "SZX" | [3600] |
When I want to use the head function with the property "consumeTime", it return a error "Invalid query: head of empty list".
How can I get a result like this?
| 0 | "JGS" | null |
| 1 | "SZX" | 3600 |
This would be trivial with conditional expressions, which is something I think is important to add to Cypher: https://github.com/neo4j/community/issues/899
Here's a somewhat hacky query using reduce for you that requires 1.9-SNAPSHOT:
START dep=node:cities(city_code = "JGS"),
arr=node:cities(city_code = "XMN")
MATCH dep-[way:BRANCH2BRANCH_AIRWAY*0..1]->()-->arr
WITH length(way) as wayLength,
transfer.city_code as transferCityCode,
extract(w in way: w.min_consume_time) AS consumeTime
WITH wayLength,
transferCityCode,
consumeTime,
// reverse the consumeTime list, so that we can get the head from the end
reduce(acc=[], x in consumeTime: x + acc) reverseConsumeTime
RETURN wayLength,
transferCityCode,
consumeTime,
// return null if empty, else the end of the list
reduce(acc=null, x in reverseConsumeTime: x) as headOrNull;
This query could be improved a fair amount with the following syntax to reverse a collection:
reverse(coll) or conditional expressions to check for an empty list.

Resources