From 16e9234ddd59da8bf01f3ef89ea16b429a94da69 Mon Sep 17 00:00:00 2001 From: Rolson Quadras Date: Tue, 18 Feb 2020 16:33:10 -0500 Subject: [PATCH] test: DIDComm Router - Rest Binding BDD tests Signed-off-by: Rolson Quadras --- test/bdd/bddtests_test.go | 1 + .../aries_router_e2e_controller.feature | 66 +++++++++ .../bdd/features/aries_router_e2e_sdk.feature | 4 +- test/bdd/fixtures/agent-rest/.env | 24 ++++ .../fixtures/agent-rest/docker-compose.yml | 56 ++++++++ .../didexchange_controller_steps.go | 20 +++ test/bdd/pkg/route/route_controller_steps.go | 134 ++++++++++++++++++ 7 files changed, 303 insertions(+), 2 deletions(-) create mode 100644 test/bdd/features/aries_router_e2e_controller.feature create mode 100644 test/bdd/pkg/route/route_controller_steps.go diff --git a/test/bdd/bddtests_test.go b/test/bdd/bddtests_test.go index 4a062042a..b0f54c4a2 100644 --- a/test/bdd/bddtests_test.go +++ b/test/bdd/bddtests_test.go @@ -167,4 +167,5 @@ func FeatureContext(s *godog.Suite) { // Register router tests route.NewRouteSDKSteps(bddContext).RegisterSteps(s) + route.NewRouteRESTSteps(bddContext).RegisterSteps(s) } diff --git a/test/bdd/features/aries_router_e2e_controller.feature b/test/bdd/features/aries_router_e2e_controller.feature new file mode 100644 index 000000000..56c5ff79e --- /dev/null +++ b/test/bdd/features/aries_router_e2e_controller.feature @@ -0,0 +1,66 @@ +# +# Copyright SecureKey Technologies Inc. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# + +@all +@aries_router_controller +Feature: DIDComm Transport between two Agents through DIDComm Routers [REST Binding] + + # https://wiki.hyperledger.org/display/ARIES/DIDComm+MediatorRouter + Scenario: Decentralized Identifier(DID) Exchange between two Edge Agents(without Inbound) through Routers + # DID Exchange between Carl and his Router + Given "Carl" agent is running with controller "http://localhost:10081" and webhook "http://localhost:10082" and "all" as the transport return route option + And "Carl-Router" agent is running on "http://localhost:10091,ws://localhost:10092" with controller "http://localhost:10093" and webhook "http://localhost:10094" + + When "Carl-Router" creates invitation through controller with label "carl-router-agent" + And "Carl" receives invitation from "Carl-Router" through controller + + Then "Carl" approves exchange invitation through controller + And "Carl-Router" approves exchange request through controller + + Then "Carl-Router" waits for post state event "completed" to webhook + And "Carl" waits for post state event "completed" to webhook + + Then "Carl-Router" retrieves connection record through controller and validates that connection state is "completed" + And "Carl" retrieves connection record through controller and validates that connection state is "completed" + And "Carl" saves the connectionID to variable "xyz" + + # DID Exchange between Dave and his Router + Given "Dave" agent is running with controller "http://localhost:10061" and webhook "http://localhost:10062" and "all" as the transport return route option + And "Dave-Router" agent is running on "http://localhost:10071,ws://localhost:10092" with controller "http://localhost:10073" and webhook "http://localhost:10074" + + When "Dave-Router" creates invitation through controller with label "Dave-router-agent" + And "Dave" receives invitation from "Dave-Router" through controller + + Then "Dave" approves exchange invitation through controller + And "Dave-Router" approves exchange request through controller + + Then "Dave-Router" waits for post state event "completed" to webhook + And "Dave" waits for post state event "completed" to webhook + + Then "Dave-Router" retrieves connection record through controller and validates that connection state is "completed" + And "Dave" retrieves connection record through controller and validates that connection state is "completed" + And "Dave" saves the connectionID to variable "abc" + + # Carl registers her Router + And "Carl" unregisters the router + And "Carl" sets connection "xyz" as the router + + # Dave registers his Router + And "Dave" unregisters the router + And "Dave" sets connection "abc" as the router + + # DIDExchange between Alice and Bob through routers + When "Carl" creates invitation through controller with label "carl-agent" + And "Dave" receives invitation from "Carl" through controller + + Then "Dave" approves exchange invitation through controller + And "Carl" approves exchange request through controller + + Then "Carl" waits for post state event "completed" to webhook + And "Dave" waits for post state event "completed" to webhook + + Then "Carl" retrieves connection record through controller and validates that connection state is "completed" + And "Dave" retrieves connection record through controller and validates that connection state is "completed" diff --git a/test/bdd/features/aries_router_e2e_sdk.feature b/test/bdd/features/aries_router_e2e_sdk.feature index aeb43dddf..6a7ffe06c 100644 --- a/test/bdd/features/aries_router_e2e_sdk.feature +++ b/test/bdd/features/aries_router_e2e_sdk.feature @@ -5,8 +5,8 @@ # @all -@aries_router -Feature: DIDComm Transport between two Agents through DIDComm Routers +@aries_router_sdk +Feature: DIDComm Transport between two Agents through DIDComm Routers [SDK] # https://wiki.hyperledger.org/display/ARIES/DIDComm+MediatorRouter Scenario: Decentralized Identifier(DID) Exchange between two Edge Agents(without Inbound) through Routers diff --git a/test/bdd/fixtures/agent-rest/.env b/test/bdd/fixtures/agent-rest/.env index 00517e794..c4a518740 100644 --- a/test/bdd/fixtures/agent-rest/.env +++ b/test/bdd/fixtures/agent-rest/.env @@ -74,3 +74,27 @@ CARL_WEBHOOK_PORT=10082 CARL_ROUTER_WEBHOOK_CONTAINER_NAME=carl.router.webhook.example.com CARL_ROUTER_WEBHOOK_HOST=0.0.0.0 CARL_ROUTER_WEBHOOK_PORT=10094 + +# Dave agent configurations +DAVE_HOST=0.0.0.0 +DAVE_API_PORT=10061 +DAVE_DB_PATH=/tmp/db/aries +DAVE_WEBHOOK_PORT=10062 + +# Dave router configurations +DAVE_ROUTER_HOST=0.0.0.0 +DAVE_ROUTER_HTTP_INBOUND_PORT=10071 +DAVE_ROUTER_WS_INBOUND_PORT=10072 +DAVE_ROUTER_API_PORT=10073 +DAVE_ROUTER_DB_PATH=/tmp/db/aries +DAVE_ROUTER_WEBHOOK_PORT=10074 + +# Dave webhook configurations +DAVE_WEBHOOK_CONTAINER_NAME=dave.webhook.example.com +DAVE_WEBHOOK_HOST=0.0.0.0 +DAVE_WEBHOOK_PORT=10062 + +# Dave Router webhook configurations +DAVE_ROUTER_WEBHOOK_CONTAINER_NAME=dave.router.webhook.example.com +DAVE_ROUTER_WEBHOOK_HOST=0.0.0.0 +DAVE_ROUTER_WEBHOOK_PORT=10074 diff --git a/test/bdd/fixtures/agent-rest/docker-compose.yml b/test/bdd/fixtures/agent-rest/docker-compose.yml index a4a60b697..8b6f00eac 100644 --- a/test/bdd/fixtures/agent-rest/docker-compose.yml +++ b/test/bdd/fixtures/agent-rest/docker-compose.yml @@ -119,6 +119,62 @@ services: networks: - bdd_net + dave.agent.example.com: + container_name: dave.aries.example.com + image: ${AGENT_REST_IMAGE}:${AGENT_REST_IMAGE_TAG} + environment: + - ARIESD_API_HOST=${DAVE_HOST}:${DAVE_API_PORT} + - ARIESD_WEBHOOK_URL=http://${DAVE_WEBHOOK_CONTAINER_NAME}:${DAVE_WEBHOOK_PORT} + - ARIESD_DEFAULT_LABEL=dave-agent + - ARIESD_DB_PATH=${DAVE_DB_PATH} + - ARIESD_OUTBOUND_TRANSPORT=${HTTP_SCHEME},${WS_SCHEME} + - ARIESD_TRANSPORT_RETURN_ROUTE=${TRANSPORT_RETURN_OPTION_ALL} + ports: + - ${DAVE_API_PORT}:${DAVE_API_PORT} + command: start + networks: + - bdd_net + + dave.webhook.example.com: + container_name: ${DAVE_WEBHOOK_CONTAINER_NAME} + image: ${SAMPLE_WEBHOOK_IMAGE}:${SAMPLE_WEBHOOK_IMAGE_TAG} + environment: + - WEBHOOK_PORT=${DAVE_WEBHOOK_PORT} + ports: + - ${DAVE_WEBHOOK_PORT}:${DAVE_WEBHOOK_PORT} + networks: + - bdd_net + + dave.router.agent.example.com: + container_name: dave.router.aries.example.com + image: ${AGENT_REST_IMAGE}:${AGENT_REST_IMAGE_TAG} + environment: + - ARIESD_API_HOST=${DAVE_ROUTER_HOST}:${DAVE_ROUTER_API_PORT} + - ARIESD_INBOUND_HOST=${HTTP_SCHEME}@${DAVE_ROUTER_HOST}:${DAVE_ROUTER_HTTP_INBOUND_PORT},${WS_SCHEME}@${DAVE_ROUTER_HOST}:${DAVE_ROUTER_WS_INBOUND_PORT} + - ARIESD_INBOUND_HOST_EXTERNAL=${HTTP_SCHEME}@http://dave.router.aries.example.com:${DAVE_ROUTER_HTTP_INBOUND_PORT},${WS_SCHEME}@ws://dave.router.aries.example.com:${DAVE_ROUTER_WS_INBOUND_PORT} + - ARIESD_WEBHOOK_URL=http://${DAVE_ROUTER_WEBHOOK_CONTAINER_NAME}:${DAVE_ROUTER_WEBHOOK_PORT} + - ARIESD_DB_PATH=${DAVE_ROUTER_DB_PATH} + - ARIESD_DEFAULT_LABEL=dave-router-agent + - ARIESD_OUTBOUND_TRANSPORT=${HTTP_SCHEME},${WS_SCHEME} + - ARIESD_HTTP_RESOLVER=${HTTP_DID_RESOLVER} + ports: + - ${DAVE_ROUTER_HTTP_INBOUND_PORT}:${DAVE_ROUTER_HTTP_INBOUND_PORT} + - ${DAVE_ROUTER_WS_INBOUND_PORT}:${DAVE_ROUTER_WS_INBOUND_PORT} + - ${DAVE_ROUTER_API_PORT}:${DAVE_ROUTER_API_PORT} + command: start + networks: + - bdd_net + + dave.router.webhook.example.com: + container_name: ${DAVE_ROUTER_WEBHOOK_CONTAINER_NAME} + image: ${SAMPLE_WEBHOOK_IMAGE}:${SAMPLE_WEBHOOK_IMAGE_TAG} + environment: + - WEBHOOK_PORT=${DAVE_ROUTER_WEBHOOK_PORT} + ports: + - ${DAVE_ROUTER_WEBHOOK_PORT}:${DAVE_ROUTER_WEBHOOK_PORT} + networks: + - bdd_net + networks: bdd_net: driver: bridge \ No newline at end of file diff --git a/test/bdd/pkg/didexchange/didexchange_controller_steps.go b/test/bdd/pkg/didexchange/didexchange_controller_steps.go index 25b510e41..88f18135d 100644 --- a/test/bdd/pkg/didexchange/didexchange_controller_steps.go +++ b/test/bdd/pkg/didexchange/didexchange_controller_steps.go @@ -9,6 +9,7 @@ package didexchange import ( "bytes" "encoding/json" + "errors" "fmt" "io" "io/ioutil" @@ -91,6 +92,9 @@ func (a *ControllerSteps) RegisterSteps(s *godog.Suite) { //nolint dupl a.createImplicitInvitationWithDID) s.Step(`^"([^"]*)" has established connection with "([^"]*)" through did exchange using controller$`, a.performDIDExchange) + s.Step(`^"([^"]*)" validates that the invitation service endpoint of type "([^"]*)"$`, + a.validateInvitationEndpointScheme) + s.Step(`^"([^"]*)" saves the connectionID to variable "([^"]*)"$`, a.saveConnectionID) } func (a *ControllerSteps) pullWebhookEvents(agentID, state string) (string, error) { @@ -246,6 +250,16 @@ func (a *ControllerSteps) verifyCreateInvitationResult(result *didexcmd.CreateIn return nil } +func (a *ControllerSteps) validateInvitationEndpointScheme(inviterAgentID, scheme string) error { + invitation := a.invitations[inviterAgentID] + + if !strings.HasPrefix(invitation.ServiceEndpoint, scheme) { + return errors.New("invitation service endpoint - invalid transport type") + } + + return nil +} + func (a *ControllerSteps) receiveInvitation(inviteeAgentID, inviterAgentID string) error { destination, ok := a.bddContext.GetControllerURL(inviteeAgentID) if !ok { @@ -283,6 +297,12 @@ func (a *ControllerSteps) receiveInvitation(inviteeAgentID, inviterAgentID strin return nil } +func (a *ControllerSteps) saveConnectionID(agentID, varName string) error { + a.bddContext.Args[varName] = a.connectionIDs[agentID] + + return nil +} + func (a *ControllerSteps) approveInvitation(agentID string) error { return a.performApproveInvitation(agentID, false) } diff --git a/test/bdd/pkg/route/route_controller_steps.go b/test/bdd/pkg/route/route_controller_steps.go new file mode 100644 index 000000000..7fcd3a888 --- /dev/null +++ b/test/bdd/pkg/route/route_controller_steps.go @@ -0,0 +1,134 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package route + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "strings" + + "github.com/cucumber/godog" + + "github.com/hyperledger/aries-framework-go/pkg/common/log" + "github.com/hyperledger/aries-framework-go/test/bdd/pkg/context" +) + +var logger = log.New("aries-framework/tests/messaging") + +type registerRouteReq struct { + ConnectionID string `json:"connectionID"` +} + +// RESTSteps is steps for route using REST APIs +type RESTSteps struct { + bddContext *context.BDDContext +} + +// NewRouteRESTSteps return steps for route using REST APIs +func NewRouteRESTSteps(ctx *context.BDDContext) *RESTSteps { + return &RESTSteps{ + bddContext: ctx, + } +} + +// RegisterRoute registers the router for the agent. +func (d *RESTSteps) RegisterRoute(agentID, varName string) error { + connectionID := d.bddContext.Args[varName] + + destination, ok := d.bddContext.GetControllerURL(agentID) + if !ok { + return fmt.Errorf(" unable to find controller URL registered for agent [%s]", agentID) + } + + err := sendHTTP(http.MethodPost, destination+"/route/register", registerRouteReq{ConnectionID: connectionID}, nil) + if err != nil { + return fmt.Errorf("router registration : %w", err) + } + + return nil +} + +// UnregisterRoute unregisters the router. +func (d *RESTSteps) UnregisterRoute(agentID string) error { + destination, ok := d.bddContext.GetControllerURL(agentID) + if !ok { + return fmt.Errorf(" unable to find controller URL registered for agent [%s]", agentID) + } + + err := sendHTTP(http.MethodDelete, destination+"/route/unregister", nil, nil) + if err != nil { + // ignore error if router is not registered (code=5003) + if strings.Contains(err.Error(), "\"code\":5003") { + logger.Infof("ignore unregister - router not registered") + + return nil + } + + return fmt.Errorf("router unregistration : %w", err) + } + + return nil +} + +// RegisterSteps registers router steps +func (d *RESTSteps) RegisterSteps(s *godog.Suite) { + s.Step(`^"([^"]*)" sets connection "([^"]*)" as the router$`, d.RegisterRoute) + s.Step(`^"([^"]*)" unregisters the router$`, d.UnregisterRoute) +} + +func sendHTTP(method, destination string, reqMsg, respMsg interface{}) error { + message, err := json.Marshal(reqMsg) + if err != nil { + return fmt.Errorf("failed to prepare params : %w", err) + } + + // create request + req, err := http.NewRequest(method, destination, bytes.NewBuffer(message)) + if err != nil { + return fmt.Errorf("failed to create new http '%s' request for '%s', cause: %s", method, destination, err) + } + + // set headers + req.Header.Set("Content-Type", "application/json") + + // send http request + resp, err := http.DefaultClient.Do(req) + if err != nil { + return fmt.Errorf("failed to get response from '%s', cause :%s", destination, err) + } + + defer closeResponse(resp.Body) + + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("unable to read response from '%s', cause :%s", destination, err) + } + + logger.Debugf("Got response from '%s' [method: %s], response payload: %s", destination, method, string(data)) + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("failed to get successful response from '%s', unexpected status code [%d], "+ + "and message [%s]", destination, resp.StatusCode, string(data)) + } + + if respMsg == nil { + return nil + } + + return json.Unmarshal(data, respMsg) +} + +func closeResponse(c io.Closer) { + err := c.Close() + if err != nil { + logger.Errorf("Failed to close response body : %s", err) + } +}