diff --git a/bddframework/features/deploy_travel_agency.feature b/bddframework/features/deploy_travel_agency.feature new file mode 100644 index 000000000..52c3dca73 --- /dev/null +++ b/bddframework/features/deploy_travel_agency.feature @@ -0,0 +1,44 @@ +@quarkus +@cr +Feature: Deploy Travel agency service and verify its functionality + + Background: + Given Namespace is created + And Kogito Operator is deployed with Infinispan and Kafka operators + And "CR" install Kogito Data Index with 1 replicas + And "CR" deploy service from example file "travelapp-kogito-travel-agency.yaml" + And Kogito application "kogito-travel-agency" has 1 pods running within 10 minutes + And HTTP GET request on service "kogito-travel-agency" with path "travels" is successful within 1 minutes + + Scenario: Travel application without required Visa + When Start "travels" process on service "kogito-travel-agency" with body: + """json + { + "traveller" : { + "firstName" : "John6", + "lastName" : "Doe", + "email" : "john.doe@example.com", + "nationality" : "American", + "address" : { + "street" : "main street", + "city" : "Boston", + "zipCode" : "10005", + "country" : "US" + } + }, + "trip" : { + "city" : "New York", + "country" : "US", + "begin" : "2019-12-10T00:00:00.000+02:00", + "end" : "2019-12-15T00:00:00.000+02:00" + } + } + """ + And Service "kogito-travel-agency" contains 1 instance of process with name "travels" + And Service "kogito-travel-agency" contains 1 task of process with name "travels" and task name "ConfirmTravel" + And Complete "ConfirmTravel" task on service "kogito-travel-agency" and process with name "travels" with body: + """json + {} + """ + + Then Service "kogito-travel-agency" contains 0 instances of process with name "travels" diff --git a/bddframework/framework/kogitoapp.go b/bddframework/framework/kogitoapp.go index 729c911f4..c91be908c 100644 --- a/bddframework/framework/kogitoapp.go +++ b/bddframework/framework/kogitoapp.go @@ -23,12 +23,15 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/yaml" + "github.com/gobuffalo/packr/v2" "github.com/kiegroup/kogito-cloud-operator/pkg/apis/app/v1alpha1" "github.com/kiegroup/kogito-cloud-operator/pkg/client/kubernetes" ) const ( + boxExamplesPath = "../../deploy/examples" mavenArgsAppendEnvVar = "MAVEN_ARGS_APPEND" mavenMirrorURLEnvVar = "MAVEN_MIRROR_URL" ) @@ -137,6 +140,32 @@ func cliDeployExample(namespace string, kogitoAppDeployment KogitoAppDeployment) return err } +// DeployServiceFromExampleFile deploy service from example YAML file (example is located in deploy/examples folder) +func DeployServiceFromExampleFile(namespace, exampleFile string) error { + box := packr.New("examples", boxExamplesPath) + yamlContent, err := box.FindString(exampleFile) + if err != nil { + return fmt.Errorf("Error reading file %s: %v ", exampleFile, err) + } + + // Create basic KogitoApp stub + kogitoApp := getKogitoAppStub(namespace, "name-should-be overwritten-from-yaml", nil) + + // Apply content from yaml file + if err := yaml.NewYAMLOrJSONDecoder(strings.NewReader(yamlContent), len([]byte(yamlContent))).Decode(kogitoApp); err != nil { + return fmt.Errorf("Error while unmarshalling file: %v ", err) + } + + // Apply custom image configuration + setupBuildImageStreams(kogitoApp) + + // Create application + if _, err := kubernetes.ResourceC(kubeClient).CreateIfNotExists(kogitoApp); err != nil { + return fmt.Errorf("Error creating service %s: %v", kogitoApp.Name, err) + } + return nil +} + // SetKogitoAppReplicas sets the number of replicas for a Kogito application func SetKogitoAppReplicas(namespace, name string, nbPods int) error { GetLogger(namespace).Infof("Set Kogito application %s replica number to %d", name, nbPods) diff --git a/bddframework/framework/process.go b/bddframework/framework/process.go new file mode 100644 index 000000000..87e6eb265 --- /dev/null +++ b/bddframework/framework/process.go @@ -0,0 +1,27 @@ +// Copyright 2020 Red Hat, Inc. and/or its affiliates +// +// 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. + +package framework + +// StartProcess starts new process instance +func StartProcess(namespace, routeURI, processName, bodyFormat, bodyContent string) (err error) { + _, err = ExecuteHTTPRequest(namespace, "POST", routeURI, processName, bodyFormat, bodyContent) + return +} + +// GetProcessInstances retrieves process instance of process name +func GetProcessInstances(namespace, routeURI, processName string) (foundProcessInstances []map[string]interface{}, err error) { + err = ExecuteHTTPRequestWithUnmarshalledResponse(namespace, "GET", routeURI, processName, "", "", &foundProcessInstances) + return +} diff --git a/bddframework/framework/task.go b/bddframework/framework/task.go new file mode 100644 index 000000000..44cf5c5cd --- /dev/null +++ b/bddframework/framework/task.go @@ -0,0 +1,27 @@ +// Copyright 2020 Red Hat, Inc. and/or its affiliates +// +// 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. + +package framework + +// GetTasks retrieves tasks of specific process instance +func GetTasks(namespace, routeURI, processName, processInstanceID string) (foundTasks map[string]string, err error) { + err = ExecuteHTTPRequestWithUnmarshalledResponse(namespace, "GET", routeURI, processName+"/"+processInstanceID+"/tasks", "", "", &foundTasks) + return +} + +// CompleteTask completes task +func CompleteTask(namespace, routeURI, processName, processInstanceID, taskName, taskID, bodyFormat, bodyContent string) (err error) { + _, err = ExecuteHTTPRequest(namespace, "POST", routeURI, processName+"/"+processInstanceID+"/"+taskName+"/"+taskID, bodyFormat, bodyContent) + return +} diff --git a/bddframework/steps/data.go b/bddframework/steps/data.go index 09eb46485..731707d73 100644 --- a/bddframework/steps/data.go +++ b/bddframework/steps/data.go @@ -41,6 +41,8 @@ func (data *Data) RegisterAllSteps(s *godog.Suite) { registerKubernetesSteps(s, data) registerOperatorSteps(s, data) registerPrometheusSteps(s, data) + registerProcessSteps(s, data) + registerTaskSteps(s, data) } // BeforeScenario configure the data before a scenario is launched diff --git a/bddframework/steps/kogitoapp.go b/bddframework/steps/kogitoapp.go index ccf40f378..8858226f3 100644 --- a/bddframework/steps/kogitoapp.go +++ b/bddframework/steps/kogitoapp.go @@ -36,6 +36,7 @@ func registerKogitoAppSteps(s *godog.Suite, data *Data) { s.Step(`^"([^"]*)" deploy quarkus example service "([^"]*)" with native "([^"]*)" and persistence and events$`, data.deployQuarkusExampleServiceWithNativeAndPersistenceAndEvents) s.Step(`^"([^"]*)" deploy spring boot example service "([^"]*)"$`, data.deploySpringBootExampleService) s.Step(`^"([^"]*)" deploy spring boot example service "([^"]*)" with persistence$`, data.deploySpringBootExampleServiceWithPersistence) + s.Step(`^"CR" deploy service from example file "([^"]*)"$`, data.deployServiceFromExampleFile) // Build steps s.Step(`^Build "([^"]*)" is complete after (\d+) minutes$`, data.buildIsCompleteAfterMinutes) @@ -133,6 +134,10 @@ func (data *Data) deploySpringBootExampleServiceWithPersistence(installerType, c }) } +func (data *Data) deployServiceFromExampleFile(exampleFile string) error { + return framework.DeployServiceFromExampleFile(data.Namespace, exampleFile) +} + // Build steps func (data *Data) buildIsCompleteAfterMinutes(buildName string, timeoutInMin int) error { return framework.WaitForBuildComplete(data.Namespace, buildName, timeoutInMin) diff --git a/bddframework/steps/process.go b/bddframework/steps/process.go new file mode 100644 index 000000000..331879185 --- /dev/null +++ b/bddframework/steps/process.go @@ -0,0 +1,58 @@ +// Copyright 2020 Red Hat, Inc. and/or its affiliates +// +// 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. + +package steps + +import ( + "fmt" + + "github.com/cucumber/godog" + "github.com/cucumber/godog/gherkin" + "github.com/kiegroup/kogito-cloud-operator/test/framework" +) + +// registerProcessSteps register all process steps +func registerProcessSteps(s *godog.Suite, data *Data) { + s.Step(`^Start "([^"]*)" process on service "([^"]*)" with body:$`, data.startProcessOnService) + s.Step(`^Service "([^"]*)" contains (\d+) (?:instance|instances) of process with name "([^"]*)"$`, data.serviceContainsInstancesOfProcess) +} + +func (data *Data) startProcessOnService(processName, serviceName string, body *gherkin.DocString) error { + routeURI, err := framework.WaitAndRetrieveRouteURI(data.Namespace, serviceName) + if err != nil { + return err + } + + err = framework.StartProcess(data.Namespace, routeURI, processName, body.ContentType, body.Content) + if err != nil { + return err + } + return nil +} + +func (data *Data) serviceContainsInstancesOfProcess(serviceName string, processInstances int, processName string) error { + routeURI, err := framework.WaitAndRetrieveRouteURI(data.Namespace, serviceName) + if err != nil { + return err + } + + foundProcessInstances, err := framework.GetProcessInstances(data.Namespace, routeURI, processName) + if err != nil { + return err + } + if len(foundProcessInstances) != processInstances { + return fmt.Errorf("expected %d of process instances, but got %d", processInstances, len(foundProcessInstances)) + } + return nil +} diff --git a/bddframework/steps/task.go b/bddframework/steps/task.go new file mode 100644 index 000000000..df9de1c8b --- /dev/null +++ b/bddframework/steps/task.go @@ -0,0 +1,111 @@ +// Copyright 2020 Red Hat, Inc. and/or its affiliates +// +// 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. + +package steps + +import ( + "fmt" + + "github.com/cucumber/godog" + "github.com/cucumber/godog/gherkin" + "github.com/kiegroup/kogito-cloud-operator/test/framework" +) + +// registerTaskSteps register all task steps +func registerTaskSteps(s *godog.Suite, data *Data) { + s.Step(`^Service "([^"]*)" contains (\d+) (?:task|tasks) of process with name "([^"]*)" and task name "([^"]*)"$`, data.serviceContainsTasksOfProcessWithNameAndTaskName) + s.Step(`^Complete "([^"]*)" task on service "([^"]*)" and process with name "([^"]*)" with body:$`, data.completeTaskOnServiceAndProcessWithName) +} + +func (data *Data) serviceContainsTasksOfProcessWithNameAndTaskName(serviceName string, numberOfTasks int, processName, taskName string) error { + routeURI, err := framework.WaitAndRetrieveRouteURI(data.Namespace, serviceName) + if err != nil { + return err + } + + // We assume that one process instance is available, can be refactored if more flexibility needed + foundProcessInstances, err := framework.GetProcessInstances(data.Namespace, routeURI, processName) + if err != nil { + return err + } + if len(foundProcessInstances) == 0 { + return fmt.Errorf("no process instance found, expected one instance") + } + if len(foundProcessInstances) > 1 { + return fmt.Errorf("too many process instances found, expected one instance, but found %d instances", len(foundProcessInstances)) + } + + // We assume that only tasks with specified name exist, can be refactored if more flexibility needed + processInstanceID := foundProcessInstances[0]["id"].(string) + foundTasks, err := framework.GetTasks(data.Namespace, routeURI, processName, processInstanceID) + if err != nil { + return err + } + for _, foundTaskName := range foundTasks { + if taskName != foundTaskName { + return fmt.Errorf("found unexpected task name %s", foundTaskName) + } + } + if len(foundTasks) < numberOfTasks { + return fmt.Errorf("not enough tasks found, expected at least %d tasks, but found just %d tasks", numberOfTasks, len(foundTasks)) + } + + return nil +} + +func (data *Data) completeTaskOnServiceAndProcessWithName(taskName, serviceName, processName string, body *gherkin.DocString) error { + routeURI, err := framework.WaitAndRetrieveRouteURI(data.Namespace, serviceName) + if err != nil { + return err + } + + // We assume that one process instance is available, can be refactored if more flexibility needed + foundProcessInstances, err := framework.GetProcessInstances(data.Namespace, routeURI, processName) + if err != nil { + return err + } + if len(foundProcessInstances) == 0 { + return fmt.Errorf("no process instance found, expected one instance") + } + if len(foundProcessInstances) > 1 { + return fmt.Errorf("too many process instances found, expected one instance, but found %d instances", len(foundProcessInstances)) + } + + processInstanceID := foundProcessInstances[0]["id"].(string) + foundTasks, err := framework.GetTasks(data.Namespace, routeURI, processName, processInstanceID) + if err != nil { + return err + } + + taskID, exists := getTaskID(foundTasks, taskName) + if !exists { + return fmt.Errorf("task with name %s not found", taskName) + } + + err = framework.CompleteTask(data.Namespace, routeURI, processName, processInstanceID, taskName, taskID, body.ContentType, body.Content) + if err != nil { + return err + } + return nil +} + +// getTaskID Returns task id of the task with name searchedTaskName and flag is the task with such name exists in tasks map +func getTaskID(tasks map[string]string, searchedTaskName string) (string, bool) { + for taskID, taskName := range tasks { + if taskName == searchedTaskName { + return taskID, true + } + } + return "", false +}