diff --git a/bddtests/java_shim.feature b/bddtests/java_shim.feature index ee1ee0fdcea..439730d1731 100644 --- a/bddtests/java_shim.feature +++ b/bddtests/java_shim.feature @@ -35,7 +35,7 @@ Feature: Java chaincode example | arg1 | arg2 | arg3 | arg4 | | a | 100 | b | 200 | Then I should have received a chaincode name - Then I wait up to "300" seconds for transaction to be committed to all peers + Then I wait up to "60" seconds for transaction to be committed to all peers When requesting "/chain" from "vp0" Then I should get a JSON response with "height" = "2" @@ -72,7 +72,7 @@ Scenario: java RangeExample chaincode single peer || || Then I should have received a chaincode name - Then I wait up to "300" seconds for transaction to be committed to all peers + Then I wait up to "60" seconds for transaction to be committed to all peers When requesting "/chain" from "vp0" Then I should get a JSON response with "height" = "2" diff --git a/bddtests/peer_basic.feature b/bddtests/peer_basic.feature index d0b0108944a..7fa29e23158 100644 --- a/bddtests/peer_basic.feature +++ b/bddtests/peer_basic.feature @@ -1077,7 +1077,6 @@ Feature: Network of Peers @issue_1942 -# @doNotDecompose Scenario: chaincode example02 with 4 peers, stop and start alternates, reverse Given we compose "docker-compose-4-consensus-batch.yml" And I register with CA supplying username "binhn" and secret "7avZQLwcUe9q" on peers: @@ -1113,7 +1112,7 @@ Scenario: chaincode example02 with 4 peers, stop and start alternates, reverse |arg1|arg2|arg3| | a | b | 1 | Then I should have received a transactionID - Then I wait up to "180" seconds for transaction to be committed to peers: + Then I wait up to "60" seconds for transaction to be committed to peers: | vp0 | vp1 | vp3 | When I query chaincode "example2" function name "query" with value "a" on peers: @@ -1130,7 +1129,7 @@ Scenario: chaincode example02 with 4 peers, stop and start alternates, reverse When I invoke chaincode "example2" function name "invoke" on "vp3" "20" times |arg1|arg2|arg3| | a | b | 1 | - Then I wait up to "300" seconds for transactions to be committed to peers: + Then I wait up to "60" seconds for transactions to be committed to peers: | vp0 | vp2 | vp3 | When I query chaincode "example2" function name "query" with value "a" on peers: diff --git a/bddtests/steps/bdd_compose_util.py b/bddtests/steps/bdd_compose_util.py index a558b644eca..a7916c89efa 100644 --- a/bddtests/steps/bdd_compose_util.py +++ b/bddtests/steps/bdd_compose_util.py @@ -16,7 +16,7 @@ import os, time, re, requests -from bdd_rest_util import buildUrl, CORE_REST_PORT +from bdd_request_util import httpGetToContainer, CORE_REST_PORT from bdd_json_util import getAttributeFromJSON from bdd_test_util import cli_call, bdd_log @@ -229,13 +229,12 @@ def peerIsReady(context, thisPeer, allPeers): return numPeers == numConnectedPeers def getConnectedPeersFromPeer(context, thisPeer): - url = buildUrl(context, thisPeer.ipAddress, "/network/peers") - response = requests.get(url, headers={'Accept': 'application/json'}, verify=False) + response = httpGetToContainer(context, thisPeer, "/network/peers") if response.status_code != 200: return None - return getAttributeFromJSON("peers", response.json(), "There should be a peer json attribute") + return getAttributeFromJSON("peers", response.json()) def mapAliasesToContainers(context): aliasToContainerMap = {} @@ -258,4 +257,4 @@ def mapContainerNamesToContainers(context): name = container.name nameToContainerMap[name] = container - return nameToContainerMap \ No newline at end of file + return nameToContainerMap diff --git a/bddtests/steps/bdd_json_util.py b/bddtests/steps/bdd_json_util.py index 44fc4cdd097..7e09f7f6732 100644 --- a/bddtests/steps/bdd_json_util.py +++ b/bddtests/steps/bdd_json_util.py @@ -14,11 +14,21 @@ # limitations under the License. # -def getAttributeFromJSON(attribute, jsonObject, msg): - return getHierarchyAttributesFromJSON(attribute.split("."), jsonObject, msg) +def getAttributeFromJSON(attribute, json): + foundJson = getHierarchyAttributesFromJSON(attribute.split("."), json) + assert foundJson is not None, "Unable to locate {} in JSON".format(attribute) -def getHierarchyAttributesFromJSON(attributes, jsonObject, msg): - if len(attributes) > 0: - assert attributes[0] in jsonObject, msg - return getHierarchyAttributesFromJSON(attributes[1:], jsonObject[attributes[0]], msg) - return jsonObject \ No newline at end of file + return foundJson + +def getHierarchyAttributesFromJSON(attributes, json): + foundJson = None + + currentAttribute = attributes[0] + if currentAttribute in json: + foundJson = json[currentAttribute] + + attributesToGo = attributes[1:] + if len(attributesToGo) > 0: + foundJson = getHierarchyAttributesFromJSON(attributesToGo, foundJson) + + return foundJson \ No newline at end of file diff --git a/bddtests/steps/bdd_request_util.py b/bddtests/steps/bdd_request_util.py new file mode 100644 index 00000000000..cf39f4c5313 --- /dev/null +++ b/bddtests/steps/bdd_request_util.py @@ -0,0 +1,98 @@ +# +# Copyright IBM Corp. 2016 All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import requests, json +from bdd_test_util import bdd_log + +CORE_REST_PORT = "7050" +ACCEPT_JSON_HEADER = { + 'Accept': 'application/json' +} + +def httpGetToContainerAlias(context, containerAlias, endpoint, \ + port=CORE_REST_PORT, expectSuccess=True): + """ Performs a GET to the given container doing a lookup by alias first. + Optional args are port (defaults to the default peer rest port) and the + expectSuccess which validates the response returned a 200 """ + container = context.containerAliasMap[containerAlias] + return httpGetToContainer(context, container, endpoint, port, expectSuccess) + +def httpGetToContainer(context, container, endpoint, \ + port=CORE_REST_PORT, expectSuccess=True): + """ Performs a GET to the given container. Optional args are port (defaults + to the default peer rest port) and the expectSuccess which validates the + response returned a 200 """ + request_url = buildContainerUrl(context, container, endpoint, port) + return httpGet(request_url, expectSuccess) + +def httpPostToContainerAlias(context, containerAlias, endpoint, body, \ + port=CORE_REST_PORT, expectSuccess=True): + """ Performs a POST to the given container doing a lookup by alias first. + Optional args are port (defaults to the default peer rest port) and the + expectSuccess which validates the response returned a 200 """ + container = context.containerAliasMap[containerAlias] + return httpPostToContainer(context, container, endpoint, body, port, expectSuccess) + +def httpPostToContainer(context, container, endpoint, body, \ + port=CORE_REST_PORT, expectSuccess=True): + """ Performs a GET to the given container. Optional args are port (defaults + to the default peer rest port) and the expectSuccess which validates the + response returned a 200 """ + request_url = buildContainerUrl(context, container, endpoint, port) + return httpPost(request_url, body, expectSuccess) + +def buildContainerAliasUrl(context, containerAlias, endpoint, port=CORE_REST_PORT): + """ Build a URL to do a HTTP request to the given container looking up the + alias first. Optionally provide a port too which defaults to the peer + rest port """ + container = context.containerAliasMap[containerAlias] + return buildContainerUrl(context, container, endpoint, port=port) + +def buildContainerUrl(context, container, endpoint, port=CORE_REST_PORT): + """ Build a URL to do a HTTP request to the given container. Optionally + provide a port too which defaults to the peer rest port """ + return buildUrl(context, container.ipAddress, port, endpoint) + +def buildUrl(context, ipAddress, port, endpoint): + schema = "http" + if 'TLS' in context.tags: + schema = "https" + return "{0}://{1}:{2}{3}".format(schema, ipAddress, port, endpoint) + +def httpGet(url, expectSuccess=True): + return _request("GET", url, expectSuccess=expectSuccess) + +def httpPost(url, body, expectSuccess=True): + return _request("POST", url, json=body, expectSuccess=expectSuccess) + +def _request(method, url, expectSuccess=True, **kwargs): + bdd_log("HTTP {} to url = {}".format(method, url)) + + response = requests.request(method, url, \ + headers=ACCEPT_JSON_HEADER, verify=False, **kwargs) + + if expectSuccess: + assert response.status_code == 200, \ + "Failed to {} to {}: {}".format(method, url, response.text) + + bdd_log("Response from {}:".format(url)) + bdd_log(formatResponseText(response)) + + return response + +def formatResponseText(response): + # Limit to 300 chars because of the size of the Base64 encoded Certs + return json.dumps(response.json(), indent = 4)[:300] diff --git a/bddtests/steps/bdd_rest_util.py b/bddtests/steps/bdd_rest_util.py deleted file mode 100644 index 0bde0de8e26..00000000000 --- a/bddtests/steps/bdd_rest_util.py +++ /dev/null @@ -1,23 +0,0 @@ -# -# Copyright IBM Corp. 2016 All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -CORE_REST_PORT = "7050" - -def buildUrl(context, ipAddress, path): - schema = "http" - if 'TLS' in context.tags: - schema = "https" - return "{0}://{1}:{2}{3}".format(schema, ipAddress, CORE_REST_PORT, path) \ No newline at end of file diff --git a/bddtests/steps/peer_basic_impl.py b/bddtests/steps/peer_basic_impl.py index f1eb3133228..768f7b3e501 100644 --- a/bddtests/steps/peer_basic_impl.py +++ b/bddtests/steps/peer_basic_impl.py @@ -14,21 +14,19 @@ # limitations under the License. # -import os -import os.path +import os, os.path +import re import time import copy from behave import * from datetime import datetime, timedelta import base64 -import requests, json +import json -import bdd_compose_util -import bdd_test_util -from bdd_test_util import bdd_log -from bdd_rest_util import buildUrl +import bdd_compose_util, bdd_test_util, bdd_request_util from bdd_json_util import getAttributeFromJSON +from bdd_test_util import bdd_log JSONRPC_VERSION = "2.0" @@ -51,22 +49,17 @@ def step_impl(context, composeYamlFile): @when(u'requesting "{path}" from "{containerAlias}"') def step_impl(context, path, containerAlias): - ipAddress = context.containerAliasMap[containerAlias].ipAddress - request_url = buildUrl(context, ipAddress, path) - bdd_log("Requesting path = {0}".format(request_url)) - resp = requests.get(request_url, headers={'Accept': 'application/json'}, verify=False) - assert resp.status_code == 200, "Failed to GET url %s: %s" % (request_url,resp.text) - context.response = resp - bdd_log("") + context.response = bdd_request_util.httpGetToContainerAlias(context, \ + containerAlias, path) @then(u'I should get a JSON response containing "{attribute}" attribute') def step_impl(context, attribute): - getAttributeFromJSON(attribute, context.response.json(), "Attribute not found in response (%s)" %(attribute)) + getAttributeFromJSON(attribute, context.response.json()) @then(u'I should get a JSON response containing no "{attribute}" attribute') def step_impl(context, attribute): try: - getAttributeFromJSON(attribute, context.response.json(), "") + getAttributeFromJSON(attribute, context.response.json()) assert None, "Attribute found in response (%s)" %(attribute) except AssertionError: bdd_log("Attribute not found as was expected.") @@ -77,12 +70,12 @@ def formatStringToCompare(value): @then(u'I should get a JSON response with "{attribute}" = "{expectedValue}"') def step_impl(context, attribute, expectedValue): - foundValue = getAttributeFromJSON(attribute, context.response.json(), "Attribute not found in response (%s)" %(attribute)) + foundValue = getAttributeFromJSON(attribute, context.response.json()) assert (formatStringToCompare(foundValue) == expectedValue), "For attribute %s, expected (%s), instead found (%s)" % (attribute, expectedValue, foundValue) @then(u'I should get a JSON response with array "{attribute}" contains "{expectedValue}" elements') def step_impl(context, attribute, expectedValue): - foundValue = getAttributeFromJSON(attribute, context.response.json(), "Attribute not found in response (%s)" %(attribute)) + foundValue = getAttributeFromJSON(attribute, context.response.json()) assert (len(foundValue) == int(expectedValue)), "For attribute %s, expected array of size (%s), instead found (%s)" % (attribute, expectedValue, len(foundValue)) @given(u'I wait "{seconds}" seconds') @@ -108,7 +101,8 @@ def step_impl(context, chaincodePath, chainLang, ctor, containerAlias): "args": getArgsFromContext(context), } - deployChainCodeToContainer(context, chaincode, containerAlias) + container = context.containerAliasMap[containerAlias] + deployChainCodeToContainer(context, chaincode, container) def getArgsFromContext(context): args = [] @@ -126,7 +120,8 @@ def step_impl(context, chaincodePath, ctor, containerAlias): "args": getArgsFromContext(context), } - deployChainCodeToContainer(context, chaincode, containerAlias) + container = context.containerAliasMap[containerAlias] + deployChainCodeToContainer(context, chaincode, container) @when(u'I deploy chaincode with name "{chaincodeName}" and with ctor "{ctor}" to "{containerAlias}"') def step_impl(context, chaincodeName, ctor, containerAlias): @@ -137,21 +132,17 @@ def step_impl(context, chaincodeName, ctor, containerAlias): "args": getArgsFromContext(context), } - deployChainCodeToContainer(context, chaincode, containerAlias) + container = context.containerAliasMap[containerAlias] + deployChainCodeToContainer(context, chaincode, container) time.sleep(2.0) # After #2068 implemented change this to only apply after a successful ping -def deployChainCodeToContainer(context, chaincode, containerAlias): - ipAddress = context.containerAliasMap[containerAlias].ipAddress - request_url = buildUrl(context, ipAddress, "/chaincode") - bdd_log("Requesting path = {0}".format(request_url)) - +def deployChainCodeToContainer(context, chaincode, container): chaincodeSpec = createChaincodeSpec(context, chaincode) chaincodeOpPayload = createChaincodeOpPayload("deploy", chaincodeSpec) + context.response = bdd_request_util.httpPostToContainer(context, \ + container, "/chaincode", chaincodeOpPayload) - resp = requests.post(request_url, headers={'Content-type': 'application/json'}, data=json.dumps(chaincodeOpPayload), verify=False) - assert resp.status_code == 200, "Failed to POST to %s: %s" %(request_url, resp.text) - context.response = resp - chaincodeName = resp.json()['result']['message'] + chaincodeName = context.response.json()['result']['message'] chaincodeSpec['chaincodeID']['name'] = chaincodeName context.chaincodeSpec = chaincodeSpec bdd_log(json.dumps(chaincodeSpec, indent=4)) @@ -231,11 +222,9 @@ def step_impl(context, chaincodeName, functionName, containerAlias, idGenAlg): @when(u'I invoke chaincode "{chaincodeName}" function name "{functionName}" on "{containerAlias}" "{times}" times') def step_impl(context, chaincodeName, functionName, containerAlias, times): assert 'chaincodeSpec' in context, "chaincodeSpec not found in context" - ipAddress = context.containerAliasMap[containerAlias].ipAddress - request_url = buildUrl(context, ipAddress, "/chain") - resp = requests.get(request_url, headers={'Accept': 'application/json'}, verify=False) - assert resp.status_code == 200, "Failed to get chain height %s: %s" % (request_url,resp.text) - context.chainheight = getAttributeFromJSON("height", resp.json(), "Height not found in response.") + + resp = bdd_request_util.httpGetToContainerAlias(context, containerAlias, "/chain") + context.chainheight = getAttributeFromJSON("height", resp.json()) context.txcount = times for i in range(int(times)): invokeChaincode(context, "invoke", functionName, containerAlias) @@ -253,7 +242,8 @@ def step_impl(context, chaincodeName, functionName, containerAlias): @when(u'I invoke master chaincode "{chaincodeName}" function name "{functionName}" on "{containerAlias}"') def step_impl(context, chaincodeName, functionName, containerAlias): - invokeMasterChaincode(context, "invoke", chaincodeName, functionName, containerAlias) + container = context.containerAliasMap[containerAlias] + invokeMasterChaincode(context, "invoke", chaincodeName, functionName, container) @then(u'I should have received a transactionID') def step_impl(context): @@ -291,56 +281,45 @@ def invokeChaincode(context, devopsFunc, functionName, containerAlias, idGenAlg= context.chaincodeSpec['attributes'] = attributes + container = context.containerAliasMap[containerAlias] #If idGenAlg is passed then, we still using the deprecated devops API because this parameter can't be passed in the new API. if idGenAlg != None: context.chaincodeSpec['ctorMsg']['args'] = to_bytes(args) - invokeUsingDevopsService(context, devopsFunc, functionName, containerAlias, idGenAlg) + invokeUsingDevopsService(context, devopsFunc, functionName, container, idGenAlg) else: context.chaincodeSpec['ctorMsg']['args'] = args - invokeUsingChaincodeService(context, devopsFunc, functionName, containerAlias) + invokeUsingChaincodeService(context, devopsFunc, functionName, container) -def invokeUsingChaincodeService(context, devopsFunc, functionName, containerAlias): +def invokeUsingChaincodeService(context, devopsFunc, functionName, container): # Invoke the POST chaincodeOpPayload = createChaincodeOpPayload(devopsFunc, context.chaincodeSpec) + context.response = bdd_request_util.httpPostToContainer(context, \ + container, "/chaincode", chaincodeOpPayload) - ipAddress = context.containerAliasMap[containerAlias].ipAddress - - request_url = buildUrl(context, ipAddress, "/chaincode") - bdd_log("POSTing path = {}".format(request_url)) - bdd_log("Using attributes {0}".format(context.chaincodeSpec['attributes'])) - - resp = requests.post(request_url, headers={'Content-type': 'application/json'}, data=json.dumps(chaincodeOpPayload), verify=False) - assert resp.status_code == 200, "Failed to POST to %s: %s" %(request_url, resp.text) - context.response = resp - bdd_log("RESULT from {0} of chaincode from peer {1}".format(functionName, containerAlias)) - bdd_log(json.dumps(context.response.json(), indent = 4)) - if 'result' in resp.json(): - result = resp.json()['result'] + if 'result' in context.response.json(): + result = context.response.json()['result'] if 'message' in result: transactionID = result['message'] context.transactionID = transactionID -def invokeUsingDevopsService(context, devopsFunc, functionName, containerAlias, idGenAlg): +def invokeUsingDevopsService(context, devopsFunc, functionName, container, idGenAlg): # Invoke the POST chaincodeInvocationSpec = { "chaincodeSpec" : context.chaincodeSpec } - ipAddress = context.containerAliasMap[containerAlias].ipAddress + if idGenAlg is not None: chaincodeInvocationSpec['idGenerationAlg'] = idGenAlg - request_url = buildUrl(context, ipAddress, "/devops/{0}".format(devopsFunc)) - bdd_log("POSTing path = {}".format(request_url)) - - resp = requests.post(request_url, headers={'Content-type': 'application/json'}, data=json.dumps(chaincodeInvocationSpec), verify=False) - assert resp.status_code == 200, "Failed to POST to %s: %s" %(request_url, resp.text) - context.response = resp - bdd_log("RESULT from {0} of chaincode from peer {1}".format(functionName, containerAlias)) - bdd_log(json.dumps(context.response.json(), indent = 4)) - if 'message' in resp.json(): + + endpoint = "/devops/{0}".format(devopsFunc) + context.response = bdd_request_util.httpPostToContainer(context, \ + container, endpoint, chaincodeInvocationSpec) + + if 'message' in context.response.json(): transactionID = context.response.json()['message'] context.transactionID = transactionID -def invokeMasterChaincode(context, devopsFunc, chaincodeName, functionName, containerAlias): +def invokeMasterChaincode(context, devopsFunc, chaincodeName, functionName, container): args = [] if 'table' in context: args = context.table[0].cells @@ -359,18 +338,11 @@ def invokeMasterChaincode(context, devopsFunc, chaincodeName, functionName, cont chaincodeSpec["secureContext"] = context.userName chaincodeOpPayload = createChaincodeOpPayload(devopsFunc, chaincodeSpec) + context.response = bdd_request_util.httpPostToContainer(context, \ + container, "/chaincode", chaincodeOpPayload) - ipAddress = context.containerAliasMap[containerAlias].ipAddress - request_url = buildUrl(context, ipAddress, "/chaincode") - bdd_log("POSTing path = {}".format(request_url)) - - resp = requests.post(request_url, headers={'Content-type': 'application/json'}, data=json.dumps(chaincodeOpPayload), verify=False) - assert resp.status_code == 200, "Failed to POST to %s: %s" %(request_url, resp.text) - context.response = resp - bdd_log("RESULT from {0} of chaincode from peer {1}".format(functionName, containerAlias)) - bdd_log(json.dumps(context.response.json(), indent = 4)) - if 'result' in resp.json(): - result = resp.json()['result'] + if 'result' in context.response.json(): + result = context.response.json()['result'] if 'message' in result: transactionID = result['message'] context.transactionID = transactionID @@ -385,73 +357,18 @@ def step_impl(context, seconds): else: time.sleep(float(seconds)) -@then(u'I wait "{seconds}" seconds for transaction to be committed to block on "{containerAlias}"') -def step_impl(context, seconds, containerAlias): +@then(u'I check the transaction ID if it is "{tUUID}"') +def step_impl(context, tUUID): assert 'transactionID' in context, "transactionID not found in context" - ipAddress = context.containerAliasMap[containerAlias].ipAddress - request_url = buildUrl(context, ipAddress, "/transactions/{0}".format(context.transactionID)) - bdd_log("GETing path = {}".format(request_url)) - - resp = requests.get(request_url, headers={'Accept': 'application/json'}, verify=False) - assert resp.status_code == 200, "Failed to POST to %s: %s" %(request_url, resp.text) - context.response = resp - -def multiRequest(context, seconds, containerDataList, pathBuilderFunc): - """Perform a multi request against the system""" - # Build map of "containerName" : response - respMap = {container.name:None for container in containerDataList} - # Set the max time before stopping attempts - maxTime = datetime.now() + timedelta(seconds = int(seconds)) - for container in containerDataList: - ipAddress = container.ipAddress - request_url = buildUrl(context, ipAddress, pathBuilderFunc(context, container)) - - # Loop unless failure or time exceeded - while (datetime.now() < maxTime): - bdd_log("GETing path = {}".format(request_url)) - resp = requests.get(request_url, headers={'Accept': 'application/json'}, verify=False) - respMap[container.name] = resp - else: - raise Exception("Max time exceeded waiting for multiRequest with current response map = {0}".format(respMap)) + assert context.transactionID == tUUID, "transactionID is not tUUID" @then(u'I wait up to "{seconds}" seconds for transaction to be committed to all peers') def step_impl(context, seconds): assert 'transactionID' in context, "transactionID not found in context" assert 'compose_containers' in context, "compose_containers not found in context" - # Build map of "containerName" : resp.statusCode - respMap = {container.name:0 for container in context.compose_containers} - - # Set the max time before stopping attempts - maxTime = datetime.now() + timedelta(seconds = int(seconds)) - for container in context.compose_containers: - ipAddress = container.ipAddress - request_url = buildUrl(context, ipAddress, "/transactions/{0}".format(context.transactionID)) - - # Loop unless failure or time exceeded - while (datetime.now() < maxTime): - bdd_log("GETing path = {}".format(request_url)) - resp = requests.get(request_url, headers={'Accept': 'application/json'}, verify=False) - if resp.status_code == 404: - # Pause then try again - respMap[container.name] = 404 - time.sleep(1) - continue - elif resp.status_code == 200: - # Success, continue - respMap[container.name] = 200 - break - else: - raise Exception("Error requesting {0}, returned result code = {1}".format(request_url, resp.status_code)) - else: - raise Exception("Max time exceeded waiting for transactions with current response map = {0}".format(respMap)) - bdd_log("Result of request to all peers = {0}".format(respMap)) - bdd_log("") - -@then(u'I check the transaction ID if it is "{tUUID}"') -def step_impl(context, tUUID): - assert 'transactionID' in context, "transactionID not found in context" - assert context.transactionID == tUUID, "transactionID is not tUUID" + containers = context.compose_containers + transactionCommittedToContainersWithinTimeout(context, containers, int(seconds)) @then(u'I wait up to "{seconds}" seconds for transaction to be committed to peers') def step_impl(context, seconds): @@ -459,37 +376,51 @@ def step_impl(context, seconds): assert 'compose_containers' in context, "compose_containers not found in context" assert 'table' in context, "table (of peers) not found in context" + aliases = context.table.headings + containers = [context.containerAliasMap[alias] for alias in aliases] + transactionCommittedToContainersWithinTimeout(context, containers, int(seconds)) + +def transactionCommittedToContainersWithinTimeout(context, containers, timeout): # Set the max time before stopping attempts - maxTime = datetime.now() + timedelta(seconds = int(seconds)) - respMap = {} - - aliases = context.table.headings - for containerAlias in aliases: - container = context.containerAliasMap[containerAlias] - respMap[container.name] = 0 - - ipAddress = container.ipAddress - request_url = buildUrl(context, ipAddress, "/transactions/{0}".format(context.transactionID)) - - # Loop unless failure or time exceeded - while (datetime.now() < maxTime): - bdd_log("GETing path = {}".format(request_url)) - resp = requests.get(request_url, headers={'Accept': 'application/json'}, verify=False) - if resp.status_code == 404: - # Pause then try again - respMap[container.name] = 404 - time.sleep(1) - continue - elif resp.status_code == 200: - # Success, continue - respMap[container.name] = 200 - break - else: - raise Exception("Error requesting {0}, returned result code = {1}".format(request_url, resp.status_code)) - else: - raise Exception("Max time exceeded waiting for transactions with current response map = {0}".format(respMap)) - bdd_log("Result of request to all peers = {0}".format(respMap)) - bdd_log("") + maxTime = datetime.now() + timedelta(seconds=timeout) + endpoint = "/transactions/{0}".format(context.transactionID) + + for container in containers: + request_url = bdd_request_util.buildContainerUrl(context, container, endpoint) + urlFound = httpGetUntilSuccessfulOrTimeout(request_url, maxTime, responseIsOk) + + assert urlFound, "Timed out waiting for transaction to be committed to {}" \ + .format(container.name) + +def responseIsOk(response): + isResponseOk = False; + + status_code = response.status_code + assert status_code == 200 or status_code == 404, \ + "Error requesting {}, returned result code = {}, expected {} or {}" \ + .format(url, status_code, 200, 404) + + if status_code == 200: + isResponseOk = True + + return isResponseOk + +def httpGetUntilSuccessfulOrTimeout(url, timeoutTimestamp, isSuccessFunction): + """ Keep attempting to HTTP GET the given URL until either the given + timestamp is exceeded or the given callback function passes. + isSuccessFunction should accept a requests.response and return a boolean + """ + successful = False + + while timeNowIsWithinTimestamp(timeoutTimestamp) and not successful: + response = bdd_request_util.httpGet(url, expectSuccess=False) + successful = isSuccessFunction(response) + time.sleep(1) + + return successful + +def timeNowIsWithinTimestamp(timestamp): + return datetime.now() < timestamp @then(u'I wait up to "{seconds}" seconds for transactions to be committed to peers') def step_impl(context, seconds): @@ -498,40 +429,24 @@ def step_impl(context, seconds): assert 'compose_containers' in context, "compose_containers not found in context" assert 'table' in context, "table (of peers) not found in context" - # Set the max time before stopping attempts - maxTime = datetime.now() + timedelta(seconds = int(seconds)) - responseStatusCodeMap = {} - - aliases = context.table.headings - for containerAlias in aliases: - container = context.containerAliasMap[containerAlias] - ipAddress = container.ipAddress - request_url = buildUrl(context, ipAddress, "/chain") - - # Loop unless failure or time exceeded - while (datetime.now() < maxTime): - bdd_log("GETing path = {}".format(request_url)) - resp = requests.get(request_url, headers={'Accept': 'application/json'}, verify=False) - if resp.status_code == 404: - # Pause then try again - responseStatusCodeMap[container.name] = 404 - time.sleep(1) - continue - elif resp.status_code == 200: - height = getAttributeFromJSON("height", resp.json(), "Height not found in response.") - if height >= int(context.chainheight) + int(context.txcount): - # Success, continue - responseStatusCodeMap[container.name] = 200 - break - else: - continue - else: - raise Exception("Error requesting {0}, returned result code = {1}".format(request_url, resp.status_code)) - else: - raise Exception("Max time exceeded waiting for transactions with current response map = {0}".format(respMap)) - bdd_log("Result of request to all peers = {0}".format(responseStatusCodeMap)) - bdd_log("") + aliases = context.table.headings + containers = [context.containerAliasMap[alias] for alias in aliases] + allTransactionsCommittedToContainersWithinTimeout(context, containers, int(seconds)) + +def allTransactionsCommittedToContainersWithinTimeout(context, containers, timeout): + maxTime = datetime.now() + timedelta(seconds=timeout) + endpoint = "/chain" + expectedMinHeight = int(context.chainheight) + int(context.txcount) + + allTransactionsCommitted = lambda (response): \ + getAttributeFromJSON("height", response.json()) >= expectedMinHeight + for container in containers: + request_url = bdd_request_util.buildContainerUrl(context, container, endpoint) + urlFound = httpGetUntilSuccessfulOrTimeout(request_url, maxTime, allTransactionsCommitted) + + assert urlFound, "Timed out waiting for transaction to be committed to {}" \ + .format(container.name) @then(u'I should get a rejection message in the listener after stopping it') def step_impl(context): @@ -553,16 +468,14 @@ def step_impl(context, chaincodeName, functionName): # There is ctor arguments args = context.table[0].cells args = prepend(functionName, args) + context.chaincodeSpec['ctorMsg']['args'] = args #context.table[0].cells if ('table' in context) else [] - # Invoke the POST chaincodeOpPayload = createChaincodeOpPayload("query", context.chaincodeSpec) responses = [] for container in context.compose_containers: - request_url = buildUrl(context, container.ipAddress, "/chaincode") - bdd_log("POSTing path = {}".format(request_url)) - resp = requests.post(request_url, headers={'Content-type': 'application/json'}, data=json.dumps(chaincodeOpPayload), verify=False) - assert resp.status_code == 200, "Failed to POST to %s: %s" %(request_url, resp.text) + resp = bdd_request_util.httpPostToContainer(context, \ + container, "/chaincode", chaincodeOpPayload) responses.append(resp) context.responses = responses @@ -582,33 +495,29 @@ def query_common(context, chaincodeName, functionName, value, failOnError): # Update the chaincodeSpec ctorMsg for invoke context.chaincodeSpec['ctorMsg']['args'] = [functionName, value] - # Invoke the POST # Make deep copy of chaincodeSpec as we will be changing the SecurityContext per call. chaincodeOpPayload = createChaincodeOpPayload("query", copy.deepcopy(context.chaincodeSpec)) responses = [] - aliases = context.table.headings - for containerAlias in aliases: - container = context.containerAliasMap[containerAlias] - + aliases = context.table.headings + containers = [context.containerAliasMap[alias] for alias in aliases] + for container in containers: # Change the SecurityContext per call - chaincodeOpPayload['params']["secureContext"] = context.peerToSecretMessage[container.composeService]['enrollId'] + chaincodeOpPayload['params']["secureContext"] = \ + context.peerToSecretMessage[container.composeService]['enrollId'] + bdd_log("Container {0} enrollID = {1}".format(container.name, container.getEnv("CORE_SECURITY_ENROLLID"))) - request_url = buildUrl(context, container.ipAddress, "/chaincode") - bdd_log("POSTing path = {}".format(request_url)) - resp = requests.post(request_url, headers={'Content-type': 'application/json'}, data=json.dumps(chaincodeOpPayload), timeout=30, verify=False) - if failOnError: - assert resp.status_code == 200, "Failed to POST to %s: %s" %(request_url, resp.text) - bdd_log("RESULT from {0} of chaincode from peer {1}".format(functionName, container.name)) - bdd_log(json.dumps(resp.json(), indent = 4)) + resp = bdd_request_util.httpPostToContainer(context, \ + container, "/chaincode", chaincodeOpPayload, expectSuccess=failOnError) responses.append(resp) + context.responses = responses @then(u'I should get a JSON response from all peers with "{attribute}" = "{expectedValue}"') def step_impl(context, attribute, expectedValue): assert 'responses' in context, "responses not found in context" for resp in context.responses: - foundValue = getAttributeFromJSON(attribute, resp.json(), "Attribute not found in response (%s)" %(attribute)) + foundValue = getAttributeFromJSON(attribute, resp.json()) assert (formatStringToCompare(foundValue) == expectedValue), "For attribute %s, expected (%s), instead found (%s)" % (attribute, expectedValue, foundValue) @then(u'I should get a JSON response from peers with "{attribute}" = "{expectedValue}"') @@ -618,7 +527,7 @@ def step_impl(context, attribute, expectedValue): assert 'table' in context, "table (of peers) not found in context" for resp in context.responses: - foundValue = getAttributeFromJSON(attribute, resp.json(), "Attribute not found in response (%s)" %(attribute)) + foundValue = getAttributeFromJSON(attribute, resp.json()) assert (formatStringToCompare(foundValue) == expectedValue), "For attribute %s, expected (%s), instead found (%s)" % (attribute, expectedValue, foundValue) @given(u'I register with CA supplying username "{userName}" and secret "{secret}" on peers') @@ -632,19 +541,14 @@ def step_impl(context, userName, secret): } # Login to each container specified - aliases = context.table.headings - for containerAlias in aliases: - containerData = context.containerAliasMap[containerAlias] - request_url = buildUrl(context, containerData.ipAddress, "/registrar") - bdd_log("POSTing path = {}".format(request_url)) - - resp = requests.post(request_url, headers={'Content-type': 'application/json'}, data=json.dumps(secretMsg), verify=False) - assert resp.status_code == 200, "Failed to POST to %s: %s" %(request_url, resp.text) - context.response = resp - bdd_log("message = {0}".format(resp.json())) + aliases = context.table.headings + containers = [context.containerAliasMap[alias] for alias in aliases] + for container in containers: + context.response = bdd_request_util.httpPostToContainer(context, \ + container, "/registrar", secretMsg) # Create new User entry - bdd_test_util.registerUser(context, secretMsg, containerData.composeService) + bdd_test_util.registerUser(context, secretMsg, container.composeService) # Store the username in the context context.userName = userName @@ -663,20 +567,15 @@ def step_impl(context): # Login to each container specified using username and secret for row in context.table.rows: peer, userName, secret = row['peer'], row['username'], row['secret'] - secretMsg = { + peerToSecretMessage[peer] = { "enrollId": userName, "enrollSecret" : secret } - ipAddress = context.containerAliasMap[peer].ipAddress - request_url = buildUrl(context, ipAddress, "/registrar") - bdd_log("POSTing to service = {0}, path = {1}".format(peer, request_url)) + container = context.containerAliasMap[peer] + context.response = bdd_request_util.httpPostToContainer(context, \ + container, "/registrar", peerToSecretMessage[peer]) - resp = requests.post(request_url, headers={'Content-type': 'application/json'}, data=json.dumps(secretMsg), verify=False) - assert resp.status_code == 200, "Failed to POST to %s: %s" %(request_url, resp.text) - context.response = resp - bdd_log("message = {0}".format(resp.json())) - peerToSecretMessage[peer] = secretMsg context.peerToSecretMessage = peerToSecretMessage diff --git a/bddtests/steps/peer_cli_impl.py b/bddtests/steps/peer_cli_impl.py index fae3ac54761..53adca0f838 100644 --- a/bddtests/steps/peer_cli_impl.py +++ b/bddtests/steps/peer_cli_impl.py @@ -67,7 +67,7 @@ def step_impl(context, stream, attribute, length): assertIsJson(data) json = decodeJson(data) - array = getAttribute(attribute, json) + array = getAttributeFromJSON(attribute, json) assertLength(array, int(length)) @then(u'I should get result with "{expectResult}"') @@ -88,10 +88,6 @@ def isJson(data): def decodeJson(data): return json.loads(data) -def getAttribute(attribute, json): - return getAttributeFromJSON(attribute, json, - "Attribute '{}' missing from JSON".format(attribute)) - def assertLength(array, length): arrayLength = len(array) assert arrayLength == length, "Unexpected array length. Expected {}, got {}".format(length, arrayLength) diff --git a/bddtests/steps/peer_rest_impl.py b/bddtests/steps/peer_rest_impl.py index 7e0f943514b..b4bd8544bf9 100644 --- a/bddtests/steps/peer_rest_impl.py +++ b/bddtests/steps/peer_rest_impl.py @@ -16,7 +16,8 @@ import requests from behave import * -from peer_basic_impl import buildUrl, getAttributeFromJSON +from peer_basic_impl import getAttributeFromJSON +from bdd_request_util import httpGetToContainerAlias from bdd_test_util import bdd_log @@ -26,24 +27,17 @@ def step_impl(context, containerAlias): assert 'userName' in context, "userName not found in context" assert 'compose_containers' in context, "compose_containers not found in context" - ipAddress = context.containerAliasMap[containerAlias].ipAddress - request_url = buildUrl(context, ipAddress, "/registrar/{0}/tcert".format(context.userName)) - bdd_log("Requesting path = {0}".format(request_url)) queryParams = {} for row in context.table.rows: key, value = row['key'], row['value'] queryParams[key] = value - bdd_log("Query parameters = {0}".format(queryParams)) - resp = requests.get(request_url, params=queryParams, headers={'Accept': 'application/json'}, verify=False) - - assert resp.status_code == 200, "Failed to GET to %s: %s" % (request_url, resp.text) - context.response = resp - bdd_log("") + endpoint = "/registrar/{0}/tcert".format(context.userName) + context.response = httpGetToContainerAlias(context, containerAlias, endpoint) @then(u'I should get a JSON response with "{expectedValue}" different transaction certs') def step_impl(context, expectedValue): bdd_log(context.response.json()) - foundValue = getAttributeFromJSON("OK", context.response.json(), "Attribute not found in response (OK)") + foundValue = getAttributeFromJSON("OK", context.response.json()) bdd_log(len(set(foundValue))) - assert (len(set(foundValue)) == int(expectedValue)), "For attribute OK, expected different transaction cert of size (%s), instead found (%s)" % (expectedValue, len(set(foundValue))) \ No newline at end of file + assert (len(set(foundValue)) == int(expectedValue)), "For attribute OK, expected different transaction cert of size (%s), instead found (%s)" % (expectedValue, len(set(foundValue))) diff --git a/bddtests/steps/sdk_impl.py b/bddtests/steps/sdk_impl.py index 8af95450512..ab0dbc223f1 100644 --- a/bddtests/steps/sdk_impl.py +++ b/bddtests/steps/sdk_impl.py @@ -17,29 +17,18 @@ import requests import bdd_test_util from bdd_test_util import bdd_log +from bdd_request_util import buildContainerAliasUrl, httpGet SDK_NODE_APP_REST_PORT = 8080 -def buildUrl(context, ipAddress, path): - schema = "http" - if 'TLS' in context.tags: - schema = "https" - return "{0}://{1}:{2}{3}".format(schema, ipAddress, SDK_NODE_APP_REST_PORT, path) - - @given(u'I register thru the sample SDK app supplying username "{enrollId}" and secret "{enrollSecret}" on "{composeService}"') def step_impl(context, enrollId, enrollSecret, composeService): assert 'compose_containers' in context, "compose_containers not found in context" - # Get the sampleApp IP Address - sampleAppIpAddress = context.containerAliasMap[composeService].ipAddress secretMsg = { "enrollId": enrollId, "enrollSecret" : enrollSecret } - request_url = buildUrl(context, sampleAppIpAddress, "/") - resp = requests.get(request_url, headers={'Accept': 'application/json'}, verify=False) - assert resp.status_code == 200, "Failed to GET url %s: %s" % (request_url,resp.text) - context.response = resp - bdd_log("") + url = buildContainerAliasUrl(context, composeService, "/", port=SDK_NODE_APP_REST_PORT) + context.response = httpGet(url)