Skip to content

Commit

Permalink
Merge pull request #1702 from dedis/work-simone-extend-decentralized-…
Browse files Browse the repository at this point in the history
…tests

Extend Karate tests for decentralized communication
  • Loading branch information
simone-kalbermatter authored Jan 4, 2024
2 parents 5d293b9 + 20710ee commit 482838b
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 137 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ Feature: Request messages by id from other servers
* call read(serverFeature)
* call read(mockClientFeature)
* def mockServer = call createMockClient
* def lao = mockServer.createValidLao()
* def mockFrontend = call createMockClient
* def lao = mockFrontend.createValidLao()

# Create the template for heartbeat message
# This is used in combination with 'eval' to dynamically resolve the channel keys in the heartbeat JSON
Expand All @@ -24,15 +25,15 @@ Feature: Request messages by id from other servers

# This call executes all the steps to create a valid lao on the server before every scenario
# (lao creation, subscribe, catchup)
* call read(createLaoScenario) { organizer: '#(mockServer)', lao: '#(lao)' }
* call read(createLaoScenario) { organizer: '#(mockFrontend)', lao: '#(lao)' }

# Check that after sending a heartbeat message with unknown message id, the server responds with a
# getMessagesByID requesting this message
Scenario: Server should request the missing message ids in a heartbeat
Given eval heartbeat.params[lao.channel] = messageIds

When mockServer.send(heartbeat)
And def getMessagesByIdMessages = mockServer.getMessagesByMethod('get_messages_by_id')
And def getMessagesByIdMessages = mockServer.getGetMessagesById()

Then assert getMessagesByIdMessages.length == 1
And match getMessagesByIdMessages[0] contains randomMessageId
Expand All @@ -43,7 +44,7 @@ Feature: Request messages by id from other servers
Given eval heartbeat.params[lao.id] = messageIds

When mockServer.send(heartbeat)
And def getMessagesByIdMessages = mockServer.getMessagesByMethod('get_messages_by_id')
And def getMessagesByIdMessages = mockServer.getGetMessagesById()

Then assert getMessagesByIdMessages.length == 0

Expand All @@ -54,7 +55,40 @@ Feature: Request messages by id from other servers
And eval heartbeat.params[lao.channel] = invalidMessageIds

When mockServer.send(heartbeat)
And def getMessagesByIdMessages = mockServer.getMessagesByMethod('get_messages_by_id')

Then assert getMessagesByIdMessages.length == 0
Then assert mockServer.getGetMessagesById().length == 0


# Check that after the server confirms it received a message, sending a heartbeat containing that message id does not
# trigger a getMessagesById anymore
Scenario: Server should not request messages that it already has
Given def validRollCall = mockFrontend.createValidRollCall(lao)
And def validCreateRollCall =
"""
{
"object": "roll_call",
"action": "create",
"id": '#(validRollCall.id)',
"name": '#(validRollCall.name)',
"creation": '#(validRollCall.creation)',
"proposed_start": '#(validRollCall.start)',
"proposed_end": '#(validRollCall.end)',
"location": '#(validRollCall.location)',
"description": '#(validRollCall.description)',
}
"""

And mockFrontend.publish(validCreateRollCall, lao.channel)
And json answer = mockFrontend.getBackendResponse(validCreateRollCall)
And def message_id = mockFrontend.getPublishMessageId(validCreateRollCall)
And def messageIds = []
And eval messageIds.push(message_id)
And eval heartbeat.params[lao.channel] = messageIds

When mockServer.send(heartbeat)

Then match answer contains VALID_MESSAGE
And assert mockServer.getGetMessagesById().length == 0



Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,25 @@ Feature: Send heartbeats to other servers
* call read(serverFeature)
* call read(mockClientFeature)
* def mockServer = call createMockClient
* def lao = mockServer.createValidLao()
* def validRollCall = mockServer.createValidRollCall(lao)
* def mockFrontend = call createMockClient
* def lao = mockFrontend.createValidLao()
* def validRollCall = mockFrontend.createValidRollCall(lao)

# This call executes all the steps to create a valid lao on the server before every scenario
# (lao creation, subscribe, catchup)
* call read(createLaoScenario) { organizer: '#(mockServer)', lao: '#(lao)' }
* call read(createLaoScenario) { organizer: '#(mockFrontend)', lao: '#(lao)' }

# After lao creation, wait and do nothing (30 seconds for now) and check that a heartbeat message was received
# After lao creation, wait and do nothing (40 seconds for now) and check that more than one heartbeat message was received.
# (The initial one would be a response to publishing lao creation)
Scenario: Server should send heartbeat messages automatically after a time interval
Given wait(30)
Given wait(40)

When def heartbeatMessages = mockServer.getMessagesByMethod('heartbeat')
When def heartbeatMessages = mockServer.getHeartbeats()

Then assert heartbeatMessages.length > 0
Then assert heartbeatMessages.length == 2

# Check that after receiving a publish message (in this case a create roll call), the server sends a heartbeat
# Check that after receiving a publish message (in this case a create roll call), the server sends a heartbeat containing
# the message id of that publish message.
Scenario: Server should send heartbeat messages after receiving a publish
Given def validCreateRollCall =
"""
Expand All @@ -42,7 +45,6 @@ Feature: Send heartbeats to other servers
}
"""

When mockServer.publish(validCreateRollCall, lao.channel)
And def heartbeatMessages = mockServer.getMessagesByMethod('heartbeat')

Then assert heartbeatMessages.length == 1
When mockFrontend.publish(validCreateRollCall, lao.channel)
And def message_id = mockFrontend.getPublishMessageId(validCreateRollCall)
Then assert mockServer.receivedHeartbeatWithSubstring(message_id)
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Feature: Terminate an election
* def electionEnd = election.close()

# After a successful election setup and cast vote sending a valid election end
# message should succeed
# message should succeed (message should be accepted and a broadcast with election results received in return)
Scenario: Sending a valid election end should succeed
Given def validElectionEnd =
"""
Expand All @@ -33,8 +33,10 @@ Feature: Terminate an election
}
"""
When organizer.publish(validElectionEnd, election.channel)
And json answer = organizer.getBackendResponseWithElectionResults(validElectionEnd)
Then match answer contains ELECTION_RESULTS
And json answer = organizer.getBackendResponse(validElectionEnd)
And json results = organizer.getElectionResults()
And match answer contains VALID_MESSAGE
And match results contains ELECTION_RESULTS
And match organizer.receiveNoMoreResponses() == true

# After having a successful election setup and vote casts, sending an election end
Expand Down
68 changes: 38 additions & 30 deletions tests/karate/src/test/java/be/utils/JsonConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,54 +25,62 @@ public JsonConverter(String publicKey, String privateKey){
}

/**
* Produces a valid Json representation of a message given the message data, the id of the message
* Produces a valid Json representation of a publish message given the high level message data, the id of the message
* and the channel where the message is supposed to be sent
*/
public Json publishMessageFromData(String stringData, int id, String channel) {
Json messageData = Json.object();
messageData.set("method", "publish");
messageData.set("id", id);
Map<String, Object> paramsPart = new LinkedHashMap<>();
public Json constructPublishMessage(String highLevelMessageData, int messageId, String channel) {
Json publishMessage = Json.object();
publishMessage.set("jsonrpc", "2.0");
publishMessage.set("id", messageId);
publishMessage.set("method", "publish");

Map<String, Object> paramsField = new LinkedHashMap<>();
try{
paramsPart = constructParamsField(channel, stringData);
messageData.set("params", paramsPart);
paramsField = constructParamsField(channel, highLevelMessageData);
publishMessage.set("params", paramsField);
}catch (GeneralSecurityException e){
e.printStackTrace();
}
messageData.set("jsonrpc", "2.0");
return publishMessage;
}

/**
* Creates a JSON map of the params field of a publish message.
* Consists of sub-fields channel and message.
*/
public Map<String, Object> constructParamsField(String channel, String highLevelMessageData) throws GeneralSecurityException {
Map<String, Object> paramsField = new LinkedHashMap<>();
Map<String, Object> messageField = constructMessageField(highLevelMessageData);

paramsField.put("channel", channel);
paramsField.put("message", messageField);

return messageData;
return paramsField;
}

public Map<String, Object> constructMessageField(String stringData) throws GeneralSecurityException{
Map<String, Object> messagePart = new LinkedHashMap<>();
Json messageData = Json.of(stringData);
/**
* Creates a JSON map of the message field of a publish message.
* Consists of sub-fields data, sender, signature, message id and witness signatures.
*/
public Map<String, Object> constructMessageField(String highLevelMessageData) throws GeneralSecurityException{
Map<String, Object> messageField = new LinkedHashMap<>();
Json messageData = Json.of(highLevelMessageData);
String messageDataBase64 = Base64Utils.convertJsonToBase64(messageData);
String signature = constructSignature(stringData);
String signature = constructSignature(highLevelMessageData);
if(isSignatureForced){
signature = this.signatureForced;
isSignatureForced = false;
}
String messageId = Hash.hash(messageDataBase64.getBytes(), signature.getBytes());
String[] witness = new String[0];

messagePart.put("data", messageDataBase64);
messagePart.put("sender", publicKey);
messagePart.put("signature", signature);
messagePart.put("message_id", messageId);
messagePart.put("witness_signatures", witness);

return messagePart;
}

public Map<String, Object> constructParamsField(String channel, String messageDataBase64) throws GeneralSecurityException {
Map<String, Object> paramsPart = new LinkedHashMap<>();
Map<String, Object> messagePart = constructMessageField(messageDataBase64);

paramsPart.put("channel", channel);
paramsPart.put("message", messagePart);
messageField.put("data", messageDataBase64);
messageField.put("sender", publicKey);
messageField.put("signature", signature);
messageField.put("message_id", messageId);
messageField.put("witness_signatures", witness);

return paramsPart;
return messageField;
}

/** Constructs a valid signature on given data */
Expand Down
4 changes: 2 additions & 2 deletions tests/karate/src/test/java/be/utils/JsonConverterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public void testJsonDataToBase64PrintsInDesiredFormat() {
Map<String, Object> testMap = new LinkedHashMap<>();
testMap.put("test1", "test2");
Json testJson = Json.of(testMap);
Json testConverter = jsonConverter.publishMessageFromData(testJson.toString(), 2, "/root");
Json testConverter = jsonConverter.constructPublishMessage(testJson.toString(), 2, "/root");
String jsonString = testConverter.toString();
String result = "{\"method\":\"publish\",\"id\":2,\"params\":{\"channel\":\"/root\",\"message\":{\"data\":\"eyJ0ZXN0MSI6InRlc3QyIn0=\",\"sender\":\"J9fBzJV70Jk5c-i3277Uq4CmeL4t53WDfUghaK0HpeM=\",\"signature\":\"-waobQoP4TyXbTSXG0A8hZ2EPRB--p8G_F_NDerSoOhcBA1BE1JZux98ihvmP8-lG8WifZTx9gSVfWuN2dx2Bw==\",\"message_id\":\"Oj7kLJCLMvQrvBZmW0YyRUDbRX10p4mIg2gw0AuIu3E=\",\"witness_signatures\":[]}},\"jsonrpc\":\"2.0\"}";
assert jsonString.equals(result);
Expand All @@ -52,7 +52,7 @@ public void testIfMessageFromDataCorrespondsToTrueMessageForValidLao() {
@Test
public void constructJsonMessageFromDataCorrespondsToTrueJsonMessage() {
String laoDataJsonString = constructJsonDataForValidLao().toString();
Json jsonValidLaoMessage = jsonConverter.publishMessageFromData(laoDataJsonString, 1, "/root");
Json jsonValidLaoMessage = jsonConverter.constructPublishMessage(laoDataJsonString, 1, "/root");
Map<String, Object> validStringMessage = new LinkedHashMap<>();
validStringMessage.put("method", "publish");
validStringMessage.put("id", 1);
Expand Down
Loading

0 comments on commit 482838b

Please sign in to comment.