diff --git a/README.md b/README.md index ae85f21..0944783 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Prepare testing environment: Run integration test from path `{project-root}/testApi/target/scala-2.13` ``` -scala testApi-assembly-0.1.0-SNAPSHOT.jar --env ./../../src/test/resources/test_project/localhost.env.json --test-root-path ./../../src/test/resources/test_project/ +java -jar testApi-assembly-0.1.0-SNAPSHOT.jar --env ./../../src/test/resources/test_project/localhost.env.json --test-root-path ./../../src/test/resources/test_project/ ``` Check report printed into console. All tests should be passed. @@ -65,7 +65,7 @@ Jar files should be available on path `{project-root}/testApi/target/scala-2.13` ## How to run tests from jar file Expect that you are in folder with test json files. ``` -scala /testApi-assembly-0.1.0-SNAPSHOT.jar --env pc_1.json --test-root-path "." +java -jar /testApi-assembly-0.1.0-SNAPSHOT.jar --env pc_1.json --test-root-path "." ``` ``` scala testapi_2.13-assembly.jar --help ─╯ diff --git a/testApi/src/main/resources/schema/after.schema.json b/testApi/src/main/resources/schema/afterAll.schema.json similarity index 96% rename from testApi/src/main/resources/schema/after.schema.json rename to testApi/src/main/resources/schema/afterAll.schema.json index 3a81a10..d7965ea 100644 --- a/testApi/src/main/resources/schema/after.schema.json +++ b/testApi/src/main/resources/schema/afterAll.schema.json @@ -1,9 +1,9 @@ { "$schema": "http://json-schema.org/draft-06/schema#", - "$ref": "#/definitions/suiteAfter", + "$ref": "#/definitions/afterAll", "definitions": { - "suiteAfter": { + "afterAll": { "type": "object", "additionalProperties": true, "properties": { @@ -23,8 +23,8 @@ "name", "methods" ], - "title": "SuiteAfter", - "description": "Defines a suite with its associated methods to be executed after the main tests." + "title": "AfterAll", + "description": "Defines a set of methods to be executed after all connected suites." }, "Method": { "type": "object", diff --git a/testApi/src/main/resources/schema/afterSuite.schema.json b/testApi/src/main/resources/schema/afterSuite.schema.json new file mode 100644 index 0000000..91cbd88 --- /dev/null +++ b/testApi/src/main/resources/schema/afterSuite.schema.json @@ -0,0 +1,160 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$ref": "#/definitions/afterSuite", + + "definitions": { + "afterSuite": { + "type": "object", + "additionalProperties": true, + "properties": { + "name": { + "type": "string", + "description": "The name of the suite." + }, + "methods": { + "type": "array", + "items": { + "$ref": "#/definitions/Method" + }, + "description": "An array of method objects associated with the suite." + } + }, + "required": [ + "name", + "methods" + ], + "title": "AfterSuite", + "description": "Defines a set of methods to be executed after the connected Suite." + }, + "Method": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "The name of the method." + }, + "headers": { + "type": "array", + "items": { + "$ref": "#/definitions/Header" + }, + "description": "Headers to be sent with the method request." + }, + "action": { + "$ref": "#/definitions/Action", + "description": "Actions to be performed during the method execution." + }, + "responseActions": { + "type": "array", + "items": { + "$ref": "#/definitions/ResponseAction" + }, + "description": "Actions to be performed on the response of the method." + } + }, + "required": [ + "name", + "headers", + "action", + "responseActions" + ], + "title": "Method", + "description": "Defines a single method within a suite." + }, + "Header": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "enum": ["content-type", "authorization"], + "description": "The name of the header. Restricted to specific values." + }, + "value": { + "type": "string", + "description": "The value of the header." + } + }, + "required": [ + "name", + "value" + ], + "title": "Header", + "description": "Defines a header to be sent with a method request." + }, + "Action": { + "type": "object", + "additionalProperties": false, + "properties": { + "method": { + "type": "string", + "enum": ["get", "post", "put", "delete"], + "description": "The HTTP method name for the action. Restricted to specific values." + }, + "url": { + "type": "string", + "description": "The URL for the action." + }, + "body": { + "type": ["string", "null"], + "description": "The body content for the action." + }, + "params": { + "type": ["array", "null"], + "items": { + "$ref": "#/definitions/Param" + }, + "description": "Parameters for the action." + } + }, + "required": [ + "method", + "url" + ], + "title": "Action", + "description": "Defines an action to be performed during a method." + }, + "ResponseAction": { + "type": "object", + "additionalProperties": false, + "properties": { + "method": { + "type": "string", + "enum": ["assert.response-time-is-below", "assert.response-time-is-above", "assert.status-code-equals", "assert.status-code-is-success", "assert.status-code-is-client-error", "assert.status-code-is-server-error", "assert.header-exists", "assert.header-value-equals", "assert.content-type-is-json", "assert.content-type-is-xml", "assert.content-type-is-html", "assert.cookie-exists", "assert.cookie-value-equals", "assert.cookie-is-secured", "assert.cookie-is-not-secured", "assert.body-equals", "assert.body-contains-text", "assert.body-is-empty", "assert.body-is-not-empty", "assert.body-length-equals", "assert.body-starts-with", "assert.body-ends-with", "assert.body-matches-regex", "assert.body-json-is-json-array", "assert.body-json-is-json-object", "assert.body-json-path-exists", "log.error", "log.warn", "log.info", "log.debug", "log.log-info-response", "extractJson.string-from-list", "extractJson.string-from-json-path"], + "description": "The method to be used for the response action. Restricted to specific values." + } + }, + "patternProperties": { + "^[a-zA-Z_][a-zA-Z0-9_]*$": { + "type": "string" + } + }, + "required": [ + "method" + ], + "title": "ResponseAction", + "description": "Defines an action to be performed on the response of a method." + }, + "Param": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "value": { + "type": "string", + "description": "The value of the parameter." + } + }, + "required": [ + "name", + "value" + ], + "title": "Param", + "description": "Defines a parameter for an action." + } + } +} diff --git a/testApi/src/main/resources/schema/before.schema.json b/testApi/src/main/resources/schema/beforeAll.schema.json similarity index 96% rename from testApi/src/main/resources/schema/before.schema.json rename to testApi/src/main/resources/schema/beforeAll.schema.json index 949e87f..c8b4023 100644 --- a/testApi/src/main/resources/schema/before.schema.json +++ b/testApi/src/main/resources/schema/beforeAll.schema.json @@ -1,9 +1,9 @@ { "$schema": "http://json-schema.org/draft-06/schema#", - "$ref": "#/definitions/suiteBefore", + "$ref": "#/definitions/beforeAll", "definitions": { - "suiteBefore": { + "beforeAll": { "type": "object", "additionalProperties": true, "properties": { @@ -23,8 +23,8 @@ "name", "methods" ], - "title": "SuiteBefore", - "description": "Defines a suite with its associated methods." + "title": "BeforeAll", + "description": "Defines a set of methods to be executed before all suites." }, "Method": { "type": "object", diff --git a/testApi/src/main/resources/schema/beforeSuite.schema.json b/testApi/src/main/resources/schema/beforeSuite.schema.json new file mode 100644 index 0000000..fbb14e2 --- /dev/null +++ b/testApi/src/main/resources/schema/beforeSuite.schema.json @@ -0,0 +1,158 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$ref": "#/definitions/beforeSuite", + + "definitions": { + "beforeSuite": { + "type": "object", + "additionalProperties": true, + "properties": { + "name": { + "type": "string", + "description": "The name of the suite." + }, + "methods": { + "type": "array", + "items": { + "$ref": "#/definitions/Method" + }, + "description": "An array of method objects associated with the suite." + } + }, + "required": [ + "name", + "methods" + ], + "title": "BeforeSuite", + "description": "Defines a set of methods to be executed before the connected Suite." + }, + "Method": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "The name of the method." + }, + "headers": { + "type": "array", + "items": { + "$ref": "#/definitions/Header" + }, + "description": "Headers to be sent with the method request." + }, + "action": { + "$ref": "#/definitions/Action", + "description": "Actions to be performed during the method execution." + }, + "responseActions": { + "type": "array", + "items": { + "$ref": "#/definitions/ResponseAction" + }, + "description": "Actions to be performed on the response of the method." + } + }, + "required": [ + "name", + "headers", + "action", + "responseActions" + ], + "title": "Method", + "description": "Defines a single method within a suite." + }, + "Header": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "The name of the header." + }, + "value": { + "type": "string", + "description": "The value of the header." + } + }, + "required": [ + "name", + "value" + ], + "title": "Header", + "description": "Defines a header to be sent with a method request." + }, + "Action": { + "type": "object", + "additionalProperties": false, + "properties": { + "method": { + "type": "string", + "description": "The HTTP method name for the action." + }, + "url": { + "type": "string", + "description": "The URL for the action." + }, + "body": { + "type": ["string", "null"], + "description": "The body content for the action." + }, + "params": { + "type": ["array", "null"], + "items": { + "$ref": "#/definitions/Param" + }, + "description": "Parameters for the action." + } + }, + "required": [ + "method", + "url" + ], + "title": "Action", + "description": "Defines an action to be performed during a method." + }, + "ResponseAction": { + "type": "object", + "additionalProperties": false, + "properties": { + "method": { + "type": "string", + "enum": ["assert.response-time-is-below", "assert.response-time-is-above", "assert.status-code-equals", "assert.status-code-is-success", "assert.status-code-is-client-error", "assert.status-code-is-server-error", "assert.header-exists", "assert.header-value-equals", "assert.content-type-is-json", "assert.content-type-is-xml", "assert.content-type-is-html", "assert.cookie-exists", "assert.cookie-value-equals", "assert.cookie-is-secured", "assert.cookie-is-not-secured", "assert.body-equals", "assert.body-contains-text", "assert.body-is-empty", "assert.body-is-not-empty", "assert.body-length-equals", "assert.body-starts-with", "assert.body-ends-with", "assert.body-matches-regex", "assert.body-json-is-json-array", "assert.body-json-is-json-object", "assert.body-json-path-exists", "log.error", "log.warn", "log.info", "log.debug", "log.log-info-response", "extractJson.string-from-list", "extractJson.string-from-json-path"], + "description": "The method to be used for the response action." + } + }, + "patternProperties": { + "^[a-zA-Z_][a-zA-Z0-9_]*$": { + "type": "string" + } + }, + "required": [ + "method" + ], + "title": "ResponseAction", + "description": "Defines an action to be performed on the response of a method." + }, + "Param": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "The name of the parameter." + }, + "value": { + "type": "string", + "description": "The value of the parameter." + } + }, + "required": [ + "name", + "value" + ], + "title": "Param", + "description": "Defines a parameter for an action." + } + } +} diff --git a/testApi/src/main/scala/africa/absa/testing/scapi/Exceptions.scala b/testApi/src/main/scala/africa/absa/testing/scapi/Exceptions.scala index a74d999..662a9d1 100644 --- a/testApi/src/main/scala/africa/absa/testing/scapi/Exceptions.scala +++ b/testApi/src/main/scala/africa/absa/testing/scapi/Exceptions.scala @@ -33,7 +33,7 @@ case class ProjectLoadFailedException() extends Exception("Problems during proje case class SuiteLoadFailedException(detail: String) extends Exception(s"Problems during project loading. Details: $detail") -case class SuiteBeforeFailedException(detail: String) +case class BeforeSuiteFailedException(detail: String) extends Exception(s"Problems during running before suite logic. Details: $detail") case class UndefinedHeaderTypeException(undefinedType: String) diff --git a/testApi/src/main/scala/africa/absa/testing/scapi/ScAPIRunner.scala b/testApi/src/main/scala/africa/absa/testing/scapi/ScAPIRunner.scala index 1ce8930..6edb35f 100644 --- a/testApi/src/main/scala/africa/absa/testing/scapi/ScAPIRunner.scala +++ b/testApi/src/main/scala/africa/absa/testing/scapi/ScAPIRunner.scala @@ -17,10 +17,10 @@ package africa.absa.testing.scapi import africa.absa.testing.scapi.config.ScAPIRunnerConfig -import africa.absa.testing.scapi.json.factory.{EnvironmentFactory, SuiteFactory} import africa.absa.testing.scapi.json.Environment +import africa.absa.testing.scapi.json.factory.{EnvironmentFactory, SuiteFactory} import africa.absa.testing.scapi.logging.Logger -import africa.absa.testing.scapi.model.suite.{Suite, SuiteResult} +import africa.absa.testing.scapi.model.suite.{AfterAllSet, BeforeAllSet, Suite, SuiteResult} import africa.absa.testing.scapi.reporter.StdOutReporter import africa.absa.testing.scapi.rest.RestClient import africa.absa.testing.scapi.rest.request.sender.ScAPIRequestSender @@ -35,7 +35,6 @@ import scala.util.{Failure, Success} */ object ScAPIRunner { - /** * The main method that is being invoked to run the ScAPI runner. * @@ -60,8 +59,11 @@ object ScAPIRunner { // jsons to objects val environment: Environment = EnvironmentFactory.fromFile(cmd.envPath) - val suiteBundles: Set[Suite] = SuiteFactory.fromFiles(environment, suitesPath, cmd.filter, cmd.fileFormat) + val (beforeAll: Option[BeforeAllSet], suiteBundles: Set[Suite], afterAll: Option[AfterAllSet]) = SuiteFactory.fromFiles( + environment, suitesPath, cmd.filter, cmd.fileFormat) + SuiteFactory.validateSuiteContent(suiteBundles) + // 2x validation for Alls // run tests and result reporting - use categories for test filtering if (cmd.validateOnly) { @@ -69,7 +71,10 @@ object ScAPIRunner { "" } else { Logger.info("Running tests") + // call BeforeAll + // deal with possible negative result val suiteResults: List[SuiteResult] = SuiteRunner.runSuites(suiteBundles, environment, () => new RestClient(ScAPIRequestSender)) + // call AfterAll StdOutReporter.printReport(suiteResults) } } diff --git a/testApi/src/main/scala/africa/absa/testing/scapi/json/factory/SuiteFactory.scala b/testApi/src/main/scala/africa/absa/testing/scapi/json/factory/SuiteFactory.scala index a001b94..2219c25 100644 --- a/testApi/src/main/scala/africa/absa/testing/scapi/json/factory/SuiteFactory.scala +++ b/testApi/src/main/scala/africa/absa/testing/scapi/json/factory/SuiteFactory.scala @@ -45,9 +45,17 @@ object SuiteFactory { * @param format The format of suite JSON files. * @return Set of Suite instances. */ - def fromFiles(environment: Environment, testRootPath: Path, filter: String, format: String): Set[Suite] = { + def fromFiles(environment: Environment, + testRootPath: Path, + filter: String, + format: String): (Option[BeforeAllSet], Set[Suite], Option[AfterAllSet]) = { // NOTE: format not used as json is only supported format in time od development + val beforeAllSet: Option[BeforeAllSet] = { + val beforeAllFile = testRootPath.resolve("beforeAll.json").toString + Try(loadJsonBeforeAllSet(beforeAllFile, environment.asMap())) + }.get + val suiteLoadingResults: Map[String, Try[Suite]] = { val suiteJsonFiles = findSuiteJsonFiles(testRootPath, filter) val suiteTries = suiteJsonFiles.map { file => @@ -58,12 +66,17 @@ object SuiteFactory { } if (suiteLoadingResults.values.forall(_.isSuccess)) { - Logger.info("All suites loaded.") val suiteBundles: Set[Suite] = suiteLoadingResults.values.collect { case Success(suiteBundle) => suiteBundle }.toSet - filterOnlyOrAll(suiteBundles) + val afterAllSet: Option[AfterAllSet] = { + val afterAllFile = testRootPath.resolve("afterAll.json").toString + Try(loadJsonAfterAllSet(afterAllFile, environment.asMap())) + }.get + + Logger.info("All suites loaded.") + (beforeAllSet, filterOnlyOrAll(suiteBundles), afterAllSet) } else { val failedSuites: Map[String, String] = suiteLoadingResults.collect { @@ -137,28 +150,57 @@ object SuiteFactory { // TODO - code proposal - will be solved in #4 // val functions: Map[String, String] = loadJsonSuiteFunctions(suiteFilePath, environmentMap) - val beforeActions: Option[BeforeTestSet] = loadJsonSuite[BeforeTestSet]( + val beforeSuiteActions: Option[BeforeSuiteSet] = loadJsonSuite[BeforeSuiteSet]( suiteFilePath, suiteName, environmentMap ++ suiteConstants.constants, - ScAPIJsonSchema.SUITE_BEFORE, - "before", - parseToSuiteBefore + ScAPIJsonSchema.BEFORE_SUITE, + "beforeSuite", + parseToBeforeSuite ) - val afterActions: Option[AfterTestSet] = loadJsonSuite[AfterTestSet]( + val afterSuiteActions: Option[AfterSuiteSet] = loadJsonSuite[AfterSuiteSet]( suiteFilePath, suiteName, environmentMap ++ suiteConstants.constants, - ScAPIJsonSchema.SUITE_AFTER, - "after", - parseToSuiteAfter + ScAPIJsonSchema.AFTER_SUITE, + "afterSuite", + parseToAfterSuite ) JsonSchemaValidator.validate(suitePath, ScAPIJsonSchema.SUITE) val jsonString: String = JsonUtils.stringFromPath(suitePath) val notResolvedSuite: TestSet = parseToSuite(jsonString) val resolvedSuite: TestSet = notResolvedSuite.resolveReferences(environmentMap ++ suiteConstants.constants) - Suite(resolvedSuite, beforeActions, afterActions) + Suite(resolvedSuite, beforeSuiteActions, afterSuiteActions) + } + + private def loadJsonBeforeAllSet(suitePath: String, environmentMap: Map[String, String]): Option[BeforeAllSet] = { + val (filePath, fileName) = FileUtils.splitPathAndFileName(suitePath) + + val setConstants: SuiteConstants = loadJsonSuiteConstants(filePath, "", environmentMap) + + loadJsonSuite[BeforeAllSet]( + filePath, + "", + environmentMap ++ setConstants.constants, + ScAPIJsonSchema.BEFORE_ALL, + "beforeAll", + parseToBeforeAll + ) + } + private def loadJsonAfterAllSet(suitePath: String, environmentMap: Map[String, String]): Option[AfterAllSet] = { + val (filePath, fileName) = FileUtils.splitPathAndFileName(suitePath) + + val setConstants: SuiteConstants = loadJsonSuiteConstants(filePath, "", environmentMap) + + loadJsonSuite[AfterAllSet]( + filePath, + "", + environmentMap ++ setConstants.constants, + ScAPIJsonSchema.AFTER_ALL, + "afterAll", + parseToAfterAll + ) } /** @@ -170,7 +212,8 @@ object SuiteFactory { * @return A SuiteConstants instance. */ def loadJsonSuiteConstants(suiteFilePath: String, suiteName: String, properties: Map[String, String]): SuiteConstants = { - val constantsFilePath: Path = Paths.get(suiteFilePath, s"$suiteName.constants.json") + val fileName = if (suiteName.nonEmpty) s"$suiteName.constants.json" else s"constants.json" + val constantsFilePath: Path = Paths.get(suiteFilePath, fileName) if (!Files.exists(constantsFilePath)) { SuiteConstants(Map.empty[String, String]) } else { @@ -198,7 +241,8 @@ object SuiteFactory { jsonSchema: URL, extension: String, parser: String => T): Option[T] = { - val filePath: Path = Paths.get(suiteFilePath, s"$suiteName.$extension.json") + val fileName = if (suiteName.nonEmpty) s"$suiteName.$extension.json" else s"$extension.json" + val filePath: Path = Paths.get(suiteFilePath, fileName) if (!Files.exists(filePath)) { None } else { @@ -220,26 +264,36 @@ object SuiteFactory { jsonString.parseJson.convertTo[SuiteConstants] } + private def parseToBeforeAll(jsonString: String): BeforeAllSet = { + import BeforeAllJsonProtocol.beforeAllFormat + jsonString.parseJson.convertTo[BeforeAllSet] + } + /** - * Method to parse a SuiteBefore instance from the given JSON string. + * Method to parse a BeforeSuite instance from the given JSON string. * * @param jsonString The JSON string to be parsed. - * @return A SuiteBefore instance. + * @return A BeforeSuite instance. */ - private def parseToSuiteBefore(jsonString: String): BeforeTestSet = { - import SuiteBeforeJsonProtocol.suiteBeforeFormat - jsonString.parseJson.convertTo[BeforeTestSet] + private def parseToBeforeSuite(jsonString: String): BeforeSuiteSet = { + import BeforeSuiteJsonProtocol.beforeSuiteFormat + jsonString.parseJson.convertTo[BeforeSuiteSet] } /** - * Method to parse a SuiteAfter instance from the given JSON string. + * Method to parse a AfterSuite instance from the given JSON string. * * @param jsonString The JSON string to be parsed. - * @return A SuiteAfter instance. + * @return A AfterSuite instance. */ - private def parseToSuiteAfter(jsonString: String): AfterTestSet = { - import SuiteAfterJsonProtocol.suiteAfterFormat - jsonString.parseJson.convertTo[AfterTestSet] + private def parseToAfterSuite(jsonString: String): AfterSuiteSet = { + import AfterSuiteJsonProtocol.afterSuiteFormat + jsonString.parseJson.convertTo[AfterSuiteSet] + } + + private def parseToAfterAll(jsonString: String): AfterAllSet = { + import AfterAllJsonProtocol.afterAllFormat + jsonString.parseJson.convertTo[AfterAllSet] } /** @@ -298,28 +352,46 @@ object SuiteConstantJsonProtocol extends DefaultJsonProtocol { implicit val suiteConstantFormat: RootJsonFormat[SuiteConstants] = jsonFormat1(SuiteConstants) } +object BeforeAllJsonProtocol extends DefaultJsonProtocol { + implicit val headerFormat: RootJsonFormat[Header] = jsonFormat2(Header) + implicit val paramFormat: RootJsonFormat[Param] = jsonFormat2(Param) + implicit val testActionFormat: RootJsonFormat[Action] = jsonFormat4(Action) + implicit val responseActionFormat: RootJsonFormat[ResponseAction] = ResponseActionJsonProtocol.ResponseActionJsonFormat + implicit val methodFormat: RootJsonFormat[Method] = jsonFormat4(Method) + implicit val beforeAllFormat: RootJsonFormat[BeforeAllSet] = jsonFormat2(BeforeAllSet) +} + /** - * Object that provides implicit JSON format for SuiteBefore class. + * Object that provides implicit JSON format for BeforeSuite class. */ -object SuiteBeforeJsonProtocol extends DefaultJsonProtocol { +object BeforeSuiteJsonProtocol extends DefaultJsonProtocol { implicit val headerFormat: RootJsonFormat[Header] = jsonFormat2(Header) implicit val paramFormat: RootJsonFormat[Param] = jsonFormat2(Param) implicit val testActionFormat: RootJsonFormat[Action] = jsonFormat4(Action) implicit val responseActionFormat: RootJsonFormat[ResponseAction] = ResponseActionJsonProtocol.ResponseActionJsonFormat implicit val methodFormat: RootJsonFormat[Method] = jsonFormat4(Method) - implicit val suiteBeforeFormat: RootJsonFormat[BeforeTestSet] = jsonFormat2(BeforeTestSet) + implicit val beforeSuiteFormat: RootJsonFormat[BeforeSuiteSet] = jsonFormat2(BeforeSuiteSet) } /** - * Object that provides implicit JSON format for SuiteAfter class. + * Object that provides implicit JSON format for AfterSuite class. */ -object SuiteAfterJsonProtocol extends DefaultJsonProtocol { +object AfterSuiteJsonProtocol extends DefaultJsonProtocol { + implicit val headerFormat: RootJsonFormat[Header] = jsonFormat2(Header) + implicit val paramFormat: RootJsonFormat[Param] = jsonFormat2(Param) + implicit val testActionFormat: RootJsonFormat[Action] = jsonFormat4(Action) + implicit val responseActionFormat: RootJsonFormat[ResponseAction] = ResponseActionJsonProtocol.ResponseActionJsonFormat + implicit val methodFormat: RootJsonFormat[Method] = jsonFormat4(Method) + implicit val afterSuiteFormat: RootJsonFormat[AfterSuiteSet] = jsonFormat2(AfterSuiteSet) +} + +object AfterAllJsonProtocol extends DefaultJsonProtocol { implicit val headerFormat: RootJsonFormat[Header] = jsonFormat2(Header) implicit val paramFormat: RootJsonFormat[Param] = jsonFormat2(Param) implicit val testActionFormat: RootJsonFormat[Action] = jsonFormat4(Action) implicit val responseActionFormat: RootJsonFormat[ResponseAction] = ResponseActionJsonProtocol.ResponseActionJsonFormat implicit val methodFormat: RootJsonFormat[Method] = jsonFormat4(Method) - implicit val suiteAfterFormat: RootJsonFormat[AfterTestSet] = jsonFormat2(AfterTestSet) + implicit val afterAllFormat: RootJsonFormat[AfterAllSet] = jsonFormat2(AfterAllSet) } /** diff --git a/testApi/src/main/scala/africa/absa/testing/scapi/json/schema/ScAPIJsonSchema.scala b/testApi/src/main/scala/africa/absa/testing/scapi/json/schema/ScAPIJsonSchema.scala index 612843a..9502f8e 100644 --- a/testApi/src/main/scala/africa/absa/testing/scapi/json/schema/ScAPIJsonSchema.scala +++ b/testApi/src/main/scala/africa/absa/testing/scapi/json/schema/ScAPIJsonSchema.scala @@ -20,9 +20,11 @@ import java.net.URL object ScAPIJsonSchema { val ENVIRONMENT: URL = getClass.getResource("/schema/env.schema.json") - val SUITE: URL = getClass.getResource("/schema/suite.schema.json") + val BEFORE_ALL: URL = getClass.getResource("/schema/beforeAll.schema.json") + val BEFORE_SUITE: URL = getClass.getResource("/schema/beforeSuite.schema.json") val SUITE_CONSTANTS: URL = getClass.getResource("/schema/constants.schema.json") - val SUITE_BEFORE: URL = getClass.getResource("/schema/before.schema.json") - val SUITE_AFTER: URL = getClass.getResource("/schema/after.schema.json") + val SUITE: URL = getClass.getResource("/schema/suite.schema.json") + val AFTER_SUITE: URL = getClass.getResource("/schema/afterSuite.schema.json") + val AFTER_ALL: URL = getClass.getResource("/schema/afterAll.schema.json") } diff --git a/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/AfterAllSet.scala b/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/AfterAllSet.scala new file mode 100644 index 0000000..7f23080 --- /dev/null +++ b/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/AfterAllSet.scala @@ -0,0 +1,32 @@ +/* + * Copyright 2023 ABSA Group Limited + * + * 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 africa.absa.testing.scapi.model.suite + +/** + * Case class that represents a after all methods. + * + * @param name The name of the after all methods. + * @param methods The set of after all methods. + */ +case class AfterAllSet(name: String, methods: Set[Method]) extends SuitePreAndPostProcessing(name, methods) { + override def resolveReferences(references: Map[String, String]): SuitePreAndPostProcessing = { + AfterAllSet( + name, + methods.map(c => c.resolveReferences(references)) + ) + } +} diff --git a/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/AfterTestSet.scala b/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/AfterSuiteSet.scala similarity index 88% rename from testApi/src/main/scala/africa/absa/testing/scapi/model/suite/AfterTestSet.scala rename to testApi/src/main/scala/africa/absa/testing/scapi/model/suite/AfterSuiteSet.scala index cb85c2d..2266d33 100644 --- a/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/AfterTestSet.scala +++ b/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/AfterSuiteSet.scala @@ -22,9 +22,9 @@ package africa.absa.testing.scapi.model.suite * @param name The name of the after methods. * @param methods The set of suite after methods. */ -case class AfterTestSet(name: String, methods: Set[Method]) extends SuitePreAndPostProcessing(name, methods) { +case class AfterSuiteSet(name: String, methods: Set[Method]) extends SuitePreAndPostProcessing(name, methods) { override def resolveReferences(references: Map[String, String]): SuitePreAndPostProcessing = { - AfterTestSet( + AfterSuiteSet( name, methods.map(c => c.resolveReferences(references)) ) diff --git a/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/BeforeAllSet.scala b/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/BeforeAllSet.scala new file mode 100644 index 0000000..6027bfc --- /dev/null +++ b/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/BeforeAllSet.scala @@ -0,0 +1,32 @@ +/* + * Copyright 2023 ABSA Group Limited + * + * 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 africa.absa.testing.scapi.model.suite + +/** + * Case class that represents a before all methods. + * + * @param name The name of the before all methods. + * @param methods The set of before all methods. + */ +case class BeforeAllSet(name: String, methods: Set[Method]) extends SuitePreAndPostProcessing(name, methods) { + override def resolveReferences(references: Map[String, String]): SuitePreAndPostProcessing = { + BeforeAllSet( + name, + methods.map(c => c.resolveReferences(references)) + ) + } +} diff --git a/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/BeforeTestSet.scala b/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/BeforeSuiteSet.scala similarity index 88% rename from testApi/src/main/scala/africa/absa/testing/scapi/model/suite/BeforeTestSet.scala rename to testApi/src/main/scala/africa/absa/testing/scapi/model/suite/BeforeSuiteSet.scala index d641532..61c2a9f 100644 --- a/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/BeforeTestSet.scala +++ b/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/BeforeSuiteSet.scala @@ -22,9 +22,9 @@ package africa.absa.testing.scapi.model.suite * @param name The name of the before methods. * @param methods The set of suite before methods. */ -case class BeforeTestSet(name: String, methods: Set[Method]) extends SuitePreAndPostProcessing(name, methods) { +case class BeforeSuiteSet(name: String, methods: Set[Method]) extends SuitePreAndPostProcessing(name, methods) { override def resolveReferences(references: Map[String, String]): SuitePreAndPostProcessing = { - BeforeTestSet( + BeforeSuiteSet( name, methods.map(c => c.resolveReferences(references)) ) diff --git a/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/Suite.scala b/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/Suite.scala index d072c75..33164d5 100644 --- a/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/Suite.scala +++ b/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/Suite.scala @@ -21,7 +21,7 @@ package africa.absa.testing.scapi.model.suite * * @constructor Create a new suite bundle with a suite and optional "before" and "after" actions. * @param suite The core suite of tests to be run. - * @param suiteBefore An optional SuiteBefore object, representing any setup actions to be run before the suite. - * @param suiteAfter An optional SuiteAfter object, representing any teardown actions to be run after the suite. + * @param beforeSuite An optional BeforeSuite object, representing any setup actions to be run before the suite. + * @param afterSuite An optional AfterSuite object, representing any teardown actions to be run after the suite. */ -case class Suite(suite: TestSet, suiteBefore: Option[BeforeTestSet] = None, suiteAfter: Option[AfterTestSet] = None) +case class Suite(suite: TestSet, beforeSuite: Option[BeforeSuiteSet] = None, afterSuite: Option[AfterSuiteSet] = None) diff --git a/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/SuitePreAndPostProcessing.scala b/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/SuitePreAndPostProcessing.scala index a654095..5d12e75 100644 --- a/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/SuitePreAndPostProcessing.scala +++ b/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/SuitePreAndPostProcessing.scala @@ -27,7 +27,7 @@ abstract class SuitePreAndPostProcessing(name: String, methods: Set[Method]) { * Method to resolve references within the before methods instance. * * @param references A map containing the references to be resolved. - * @return A new SuiteBefore instance where all references are resolved. + * @return A new BeforeSuite instance where all references are resolved. */ def resolveReferences(references: Map[String, String]): SuitePreAndPostProcessing } diff --git a/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/types/SuiteResultType.scala b/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/types/SuiteResultType.scala index 543997b..ac61cc9 100644 --- a/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/types/SuiteResultType.scala +++ b/testApi/src/main/scala/africa/absa/testing/scapi/model/suite/types/SuiteResultType.scala @@ -19,7 +19,7 @@ package africa.absa.testing.scapi.model.suite.types object SuiteResultType extends Enumeration { type SuiteResultType = Value - val BeforeTestSet: SuiteResultType.Value = Value("before-test-set") - val TestSet: SuiteResultType.Value = Value("test-set") - val AfterTestSet: SuiteResultType.Value = Value("after-test-set") + val BeforeSuiteResult: SuiteResultType.Value = Value("before-suite-result") + val TestResult: SuiteResultType.Value = Value("test-result") + val AfterSuiteResult: SuiteResultType.Value = Value("after-suite-result") } diff --git a/testApi/src/main/scala/africa/absa/testing/scapi/reporter/StdOutReporter.scala b/testApi/src/main/scala/africa/absa/testing/scapi/reporter/StdOutReporter.scala index b2eed46..b223eac 100644 --- a/testApi/src/main/scala/africa/absa/testing/scapi/reporter/StdOutReporter.scala +++ b/testApi/src/main/scala/africa/absa/testing/scapi/reporter/StdOutReporter.scala @@ -62,8 +62,8 @@ object StdOutReporter { printHeader("Simple Text Report") - val successCount = testResults.count(r => r.isSuccess && r.resultType == SuiteResultType.TestSet) - val failureCount = testResults.count(r => !r.isSuccess && r.resultType == SuiteResultType.TestSet) + val successCount = testResults.count(r => r.isSuccess && r.resultType == SuiteResultType.TestResult) + val failureCount = testResults.count(r => !r.isSuccess && r.resultType == SuiteResultType.TestResult) addToReport(s"Number of tests run: ${successCount + failureCount}") addToReport(s"Number of successful tests: $successCount") @@ -71,7 +71,7 @@ object StdOutReporter { if (testResults.nonEmpty) { val suiteSummary = testResults - .filter(_.resultType == SuiteResultType.TestSet) + .filter(_.resultType == SuiteResultType.TestResult) .groupBy(_.suiteName).map { case (suiteName, results) => (suiteName, results.size, results.count(_.isSuccess)) @@ -87,7 +87,7 @@ object StdOutReporter { printTableRowSplitter() addToReport(s"| %-${maxSuiteLength}s | %-${maxTestLength}s | %-13s | %-7s | %-${maxTestCategoriesLength}s | ".format("Suite Name", "Test Name", "Duration (ms)", "Status", "Categories")) printTableRowSplitter() - val resultsList = testResults.filter(_.resultType == SuiteResultType.TestSet) + val resultsList = testResults.filter(_.resultType == SuiteResultType.TestResult) resultsList.zipWithIndex.foreach { case (result, index) => val duration = result.duration.map(_.toString).getOrElse("NA") addToReport(s"| %-${maxSuiteLength}s | %-${maxTestLength}s | %13s | %-7s | %-${maxTestCategoriesLength}s | ".format( @@ -106,9 +106,9 @@ object StdOutReporter { testResults.filter(!_.isSuccess).foreach { result => addToReport(s"Suite: ${result.suiteName}") result.resultType match { - case SuiteResultType.BeforeTestSet => addToReport(s"Before: ${result.name}") - case SuiteResultType.TestSet => addToReport(s"Test: ${result.name}") - case SuiteResultType.AfterTestSet => addToReport(s"After: ${result.name}") + case SuiteResultType.BeforeSuiteResult => addToReport(s"BeforeSuite: ${result.name}") + case SuiteResultType.TestResult => addToReport(s"Test: ${result.name}") + case SuiteResultType.AfterSuiteResult => addToReport(s"AfterSuite: ${result.name}") } addToReport(s"Error: ${result.errorMsg.getOrElse("No details available")}") addToReport(s"Duration: ${result.duration.getOrElse("NA")} ms") diff --git a/testApi/src/main/scala/africa/absa/testing/scapi/suite/runner/SuiteRunner.scala b/testApi/src/main/scala/africa/absa/testing/scapi/suite/runner/SuiteRunner.scala index 9a2b52b..5c8a36c 100644 --- a/testApi/src/main/scala/africa/absa/testing/scapi/suite/runner/SuiteRunner.scala +++ b/testApi/src/main/scala/africa/absa/testing/scapi/suite/runner/SuiteRunner.scala @@ -16,7 +16,7 @@ package africa.absa.testing.scapi.suite.runner -import africa.absa.testing.scapi.SuiteBeforeFailedException +import africa.absa.testing.scapi.BeforeSuiteFailedException import africa.absa.testing.scapi.json.{Environment, Requestable} import africa.absa.testing.scapi.logging.Logger import africa.absa.testing.scapi.model.suite.types.SuiteResultType @@ -46,27 +46,27 @@ object SuiteRunner { suiteBundles.foldLeft(List[SuiteResult]()) { (resultList, suiteBundle) => Logger.debug(s"Suite: ${suiteBundle.suite.name} - Started") - val suiteBeforeResult: List[SuiteResult] = suiteBundle.suiteBefore.toList.flatMap { suiteBefore => - suiteBefore.methods.map { method => runSuiteBefore( + val beforeSuiteResult: List[SuiteResult] = suiteBundle.beforeSuite.toList.flatMap { beforeSuite => + beforeSuite.methods.map { method => runBeforeSuite( suiteBundle.suite.name, - suiteBefore.name, + beforeSuite.name, method, environment, restClientCreator) } } var suiteResult: List[SuiteResult] = List.empty - var suiteAfterResult: List[SuiteResult] = List.empty - if (!suiteBeforeResult.forall(_.isSuccess)) { - val errorMsg = s"Suite-Before for Suite: ${suiteBundle.suite.name} has failed methods. Not executing main tests and Suite-After." + var afterSuiteResult: List[SuiteResult] = List.empty + if (!beforeSuiteResult.forall(_.isSuccess)) { + val errorMsg = s"BeforeSuite for Suite: ${suiteBundle.suite.name} has failed methods. Not executing main tests and After-Suite." Logger.error(errorMsg) // add failed Test suite result instance and it will not be started suiteResult = suiteResult :+ SuiteResult( - resultType = SuiteResultType.TestSet, + resultType = SuiteResultType.TestResult, suiteName = suiteBundle.suite.name, name = "SKIPPED", - result = Failure(SuiteBeforeFailedException(errorMsg)), + result = Failure(BeforeSuiteFailedException(errorMsg)), duration = Some(0L), categories = Some("SKIPPED")) } else { @@ -77,10 +77,10 @@ object SuiteRunner { environment, restClientCreator)) - suiteAfterResult = suiteBundle.suiteAfter.toList.flatMap { suiteAfter => - suiteAfter.methods.map { method => runSuiteAfter( + afterSuiteResult = suiteBundle.afterSuite.toList.flatMap { afterSuite => + afterSuite.methods.map { method => runAfterSuite( suiteBundle.suite.name, - suiteAfter.name, + afterSuite.name, method, environment, restClientCreator) } @@ -88,36 +88,36 @@ object SuiteRunner { } RuntimeCache.expire(SuiteLevel) - resultList ++ suiteBeforeResult ++ suiteResult ++ suiteAfterResult + resultList ++ beforeSuiteResult ++ suiteResult ++ afterSuiteResult } } /** - * Runs all the suite-before methods for a given test suite. + * Runs all the before-suite methods for a given test suite. * * @param suiteName Suite's name. - * @param suiteBeforeName SuiteBefore's name. + * @param beforeSuiteName BeforeSuite's name. * @param method Method to execute. * @param environment The current environment. - * @return SuiteResults after the execution of the suite-before method. + * @return SuiteResults after the execution of the before-suite method. */ - private def runSuiteBefore(suiteName: String, suiteBeforeName: String, method: Method, environment: Environment, restClientCreator: RestClientCreator): SuiteResult = { - Logger.debug(s"Suite-Before: $suiteBeforeName - Started") + private def runBeforeSuite(suiteName: String, beforeSuiteName: String, method: Method, environment: Environment, restClientCreator: RestClientCreator): SuiteResult = { + Logger.debug(s"Before-Suite: $beforeSuiteName - Started") val testStartTime: Long = System.currentTimeMillis() try { val result: Try[Unit] = processRequest(method, environment, restClientCreator) val testEndTime: Long = System.currentTimeMillis() - Logger.debug(s"Suite-Before: method '${method.name}' - ${if (result.isSuccess) "completed successfully" else "failed"}.") + Logger.debug(s"Before-Suite: method '${method.name}' - ${if (result.isSuccess) "completed successfully" else "failed"}.") SuiteResult( - resultType = SuiteResultType.BeforeTestSet, + resultType = SuiteResultType.BeforeSuiteResult, suiteName = suiteName, name = method.name, result = result, duration = Some(testEndTime - testStartTime) ) } catch { - case e: Exception => handleException(e, suiteName, suiteBeforeName, testStartTime, SuiteResultType.BeforeTestSet) + case e: Exception => handleException(e, suiteName, beforeSuiteName, testStartTime, SuiteResultType.BeforeSuiteResult) } } @@ -138,7 +138,7 @@ object SuiteRunner { val testEndTime: Long = System.currentTimeMillis() Logger.debug(s"Suite-Test: '${test.name}' - ${if (result.isSuccess) "completed successfully" else "failed"}.") SuiteResult( - resultType = SuiteResultType.TestSet, + resultType = SuiteResultType.TestResult, suiteName = suiteName, name = test.name, result = result, @@ -147,23 +147,23 @@ object SuiteRunner { ) } catch { - case e: Exception => handleException(e, suiteName, test.name, testStartTime, SuiteResultType.TestSet, Some(test.categories.mkString(","))) + case e: Exception => handleException(e, suiteName, test.name, testStartTime, SuiteResultType.TestResult, Some(test.categories.mkString(","))) } finally { RuntimeCache.expire(TestLevel) } } /** - * Runs all the suite-after methods for a given test suite. + * Runs all the after-suite methods for a given test suite. * * @param suiteName Suite's name. - * @param suiteAfterName SuiteAfter's name. + * @param afterSuiteName AfterSuite's name. * @param method Method to execute. * @param environment The current environment. - * @return SuiteResults after the execution of the suite-after method. + * @return SuiteResults after the execution of the after-suite method. */ - private def runSuiteAfter(suiteName: String, suiteAfterName: String, method: Method, environment: Environment, restClientCreator: RestClientCreator): SuiteResult = { - Logger.debug(s"Suite-After: $suiteAfterName - Started") + private def runAfterSuite(suiteName: String, afterSuiteName: String, method: Method, environment: Environment, restClientCreator: RestClientCreator): SuiteResult = { + Logger.debug(s"After-Suite: $afterSuiteName - Started") val testStartTime: Long = System.currentTimeMillis() try { @@ -171,14 +171,14 @@ object SuiteRunner { val testEndTime: Long = System.currentTimeMillis() Logger.debug(s"After method '${method.name}' ${if (result.isSuccess) "completed successfully" else "failed"}.") SuiteResult( - resultType = SuiteResultType.AfterTestSet, + resultType = SuiteResultType.AfterSuiteResult, suiteName = suiteName, name = method.name, result = result, duration = Some(testEndTime - testStartTime) ) } catch { - case e: Exception => handleException(e, suiteName, suiteAfterName, testStartTime, SuiteResultType.AfterTestSet) + case e: Exception => handleException(e, suiteName, afterSuiteName, testStartTime, SuiteResultType.AfterSuiteResult) } } diff --git a/testApi/src/test/resources/test_project/suites/afterAll.json b/testApi/src/test/resources/test_project/suites/afterAll.json new file mode 100644 index 0000000..ecc5207 --- /dev/null +++ b/testApi/src/test/resources/test_project/suites/afterAll.json @@ -0,0 +1,28 @@ +{ + "name" : "AfterAll", + "methods" : [ + { + "name" : "Log only in AfterAll", + "headers" : [ + { + "name": "authorization", + "value": "{{ constants.headerBasicToken }}" + }, + { + "name": "content-type", + "value": "application/json" + } + ], + "action": { + "method": "get", + "url": "{{ env.url }}/api/owners" + }, + "responseActions": [ + { + "method": "log.info", + "message": "Dummy info log message from AfterAll." + } + ] + } + ] +} diff --git a/testApi/src/test/resources/test_project/suites/beforeAll.json b/testApi/src/test/resources/test_project/suites/beforeAll.json new file mode 100644 index 0000000..1e55796 --- /dev/null +++ b/testApi/src/test/resources/test_project/suites/beforeAll.json @@ -0,0 +1,28 @@ +{ + "name" : "BeforeAll", + "methods" : [ + { + "name" : "Log info only in BeforeAll", + "headers" : [ + { + "name": "authorization", + "value": "{{ constants.headerBasicToken }}" + }, + { + "name": "content-type", + "value": "application/json" + } + ], + "action": { + "method": "get", + "url": "{{ env.url }}/api/owners" + }, + "responseActions": [ + { + "method": "log.info", + "message": "Dummy info log message from BeforeAll." + } + ] + } + ] +} diff --git a/testApi/src/test/resources/test_project/suites/constants.json b/testApi/src/test/resources/test_project/suites/constants.json new file mode 100644 index 0000000..f8349bb --- /dev/null +++ b/testApi/src/test/resources/test_project/suites/constants.json @@ -0,0 +1,8 @@ +{ + "constants" : { + "headerAuth": "Authorization", + "headerBasicToken": "Basic {{ env.basicToken }}", + "contentType": "application/json", + "uniqueKeyName2": "value" + } +} diff --git a/testApi/src/test/resources/test_project/suites/demo/getOwners.after.json b/testApi/src/test/resources/test_project/suites/demo/getOwners.afterSuite.json similarity index 100% rename from testApi/src/test/resources/test_project/suites/demo/getOwners.after.json rename to testApi/src/test/resources/test_project/suites/demo/getOwners.afterSuite.json diff --git a/testApi/src/test/resources/test_project/suites/demo/getOwners.before.json b/testApi/src/test/resources/test_project/suites/demo/getOwners.beforeSuite.json similarity index 100% rename from testApi/src/test/resources/test_project/suites/demo/getOwners.before.json rename to testApi/src/test/resources/test_project/suites/demo/getOwners.beforeSuite.json diff --git a/testApi/src/test/resources/test_project/suites/demo2/getAnotherOwners.suite.json b/testApi/src/test/resources/test_project/suites/demo2/getAnotherOwners.suite.json new file mode 100644 index 0000000..3003437 --- /dev/null +++ b/testApi/src/test/resources/test_project/suites/demo2/getAnotherOwners.suite.json @@ -0,0 +1,23 @@ +{ + "name" : "getAnotherOwners Demo Suite", + "tests": [ + { + "name" : "AnotherOwner ID:998 not found", + "categories": ["SMOKE"], + "headers" : [], + "action": { + "method": "get", + "url": "{{ env.url }}/api/owner/998" + }, + "responseActions": [ + { + "method": "assert.status-code-equals", + "code": "404" + }, + { + "method": "assert.status-code-is-client-error" + } + ] + } + ] +} diff --git a/testApi/src/test/scala/africa/absa/testing/scapi/ScAPIRunnerTest.scala b/testApi/src/test/scala/africa/absa/testing/scapi/ScAPIRunnerTest.scala index bd6983c..9ee0f23 100644 --- a/testApi/src/test/scala/africa/absa/testing/scapi/ScAPIRunnerTest.scala +++ b/testApi/src/test/scala/africa/absa/testing/scapi/ScAPIRunnerTest.scala @@ -26,18 +26,19 @@ class ScAPIRunnerTest extends FunSuite { } } - test("call main with minimum params - report of failures") { + test("call main with minimum params - report of failures".only) { val args: Array[String] = Array( "--env", getClass.getResource("/test_project/localhostBadPort.env.json").getPath, "--test-root-path", getClass.getResource("/test_project").getPath) val report = ScAPIRunner.run(args) assert(report.contains("* Simple Text Report *")) - assert(report.contains("| getOwners Demo Suite | SKIPPED | 0 | Failure | SKIPPED |")) - assert(report.contains("Before: getOwners Demo Before")) + assert(report.replaceAll("\\s+", " ").contains("| getOwners Demo Suite | SKIPPED | 0 | Failure | SKIPPED |")) + assert(report.contains("Test: AnotherOwner ID:998 not found")) + assert(report.contains("BeforeSuite: getOwners Demo Before")) assert(report.contains("Error: Connection refused")) assert(report.contains("Test: SKIPPED")) - assert(report.contains("Error: Problems during running before suite logic. Details: Suite-Before for Suite: getOwners Demo Suite has failed methods. Not executing main tests and Suite-After.")) + assert(report.contains("Error: Problems during running before suite logic. Details: BeforeSuite for Suite: getOwners Demo Suite has failed methods. Not executing main tests and After-Suite.")) } test("call main with minimum params - validate only") { diff --git a/testApi/src/test/scala/africa/absa/testing/scapi/reporter/StdOutReporterTest.scala b/testApi/src/test/scala/africa/absa/testing/scapi/reporter/StdOutReporterTest.scala index 78dcc39..077b092 100644 --- a/testApi/src/test/scala/africa/absa/testing/scapi/reporter/StdOutReporterTest.scala +++ b/testApi/src/test/scala/africa/absa/testing/scapi/reporter/StdOutReporterTest.scala @@ -26,20 +26,20 @@ import scala.util.{Failure, Success} class StdOutReporterTest extends FunSuite { val successTestResults: List[SuiteResult] = List( - SuiteResult(SuiteResultType.TestSet, + SuiteResult(SuiteResultType.TestResult, suiteName = "Suite 1", name = "Test 1", result = Success(()), duration = Some(100L), categories = Some("Category 1")), - SuiteResult(SuiteResultType.TestSet, + SuiteResult(SuiteResultType.TestResult, suiteName = "Suite 1", name = "Test 2", result = Success(()), duration = Some(200L), categories = Some("Category 2") ), - SuiteResult(SuiteResultType.TestSet, + SuiteResult(SuiteResultType.TestResult, suiteName = "Suite 2", name = "Test 1", result = Success(()), @@ -48,19 +48,19 @@ class StdOutReporterTest extends FunSuite { ) val mixedSuccessTestResults: List[SuiteResult] = List( - SuiteResult(SuiteResultType.TestSet, + SuiteResult(SuiteResultType.TestResult, suiteName = "Suite 1", name = "Test 1", result = Success(()), duration = Some(100L), categories = Some("Category 1")), - SuiteResult(SuiteResultType.TestSet, + SuiteResult(SuiteResultType.TestResult, suiteName = "Suite 1", name = "Test 2", result = Failure(AssertionException("Error message")), duration = Some(200L), categories = Some("Category 2")), - SuiteResult(SuiteResultType.TestSet, + SuiteResult(SuiteResultType.TestResult, suiteName = "Suite 2", name = "Test 1", result = Success(()), diff --git a/testApi/src/test/scala/africa/absa/testing/scapi/suite/runner/SuiteRunnerTest.scala b/testApi/src/test/scala/africa/absa/testing/scapi/suite/runner/SuiteRunnerTest.scala index f8af938..395b220 100644 --- a/testApi/src/test/scala/africa/absa/testing/scapi/suite/runner/SuiteRunnerTest.scala +++ b/testApi/src/test/scala/africa/absa/testing/scapi/suite/runner/SuiteRunnerTest.scala @@ -44,12 +44,12 @@ class SuiteRunnerTest extends FunSuite { headers = Seq(header), action = action, responseActions = Seq(responseAction), only = Some(true)) ))), Suite( - suiteBefore = Some(BeforeTestSet(name = "suiteBefore", methods = Set(method))), + beforeSuite = Some(BeforeSuiteSet(name = "beforeSuite", methods = Set(method))), suite = TestSet(name = "name2", tests = Set( SuiteTestScenario(name = "test1", categories = Seq("SMOKE"), headers = Seq(header), action = action, responseActions = Seq(responseAction), only = Some(false)), )), - suiteAfter = Some(AfterTestSet(name = "suiteAfter", methods = Set(method))), + afterSuite = Some(AfterSuiteSet(name = "afterSuite", methods = Set(method))), )) val suitesBundleNoBefore: Set[Suite] = Set( @@ -58,7 +58,7 @@ class SuiteRunnerTest extends FunSuite { SuiteTestScenario(name = "test1", categories = Seq("SMOKE"), headers = Seq(header), action = action, responseActions = Seq(responseAction), only = Some(false)), )), - suiteAfter = Some(AfterTestSet(name = "suiteAfter", methods = Set(method))), + afterSuite = Some(AfterSuiteSet(name = "afterSuite", methods = Set(method))), )) val suitesBundleAfterMethodNotSupported: Set[Suite] = Set( @@ -67,12 +67,12 @@ class SuiteRunnerTest extends FunSuite { SuiteTestScenario(name = "test1", categories = Seq("SMOKE"), headers = Seq(header), action = action, responseActions = Seq(responseAction), only = Some(false)), )), - suiteAfter = Some(AfterTestSet(name = "suiteAfter", methods = Set(methodNotSupported))), + afterSuite = Some(AfterSuiteSet(name = "afterSuite", methods = Set(methodNotSupported))), )) val suitesBundleNoAfter: Set[Suite] = Set( Suite( - suiteBefore = Some(BeforeTestSet(name = "suiteBefore", methods = Set(method))), + beforeSuite = Some(BeforeSuiteSet(name = "beforeSuite", methods = Set(method))), suite = TestSet(name = "name2", tests = Set( SuiteTestScenario(name = "test1", categories = Seq("SMOKE"), headers = Seq(header), action = action, responseActions = Seq(responseAction), only = Some(false)), @@ -101,53 +101,53 @@ class SuiteRunnerTest extends FunSuite { runSuite */ - test("runSuite - SuiteBefore exists") { + test("runSuite - BeforeSuite exists") { val suiteResults: List[SuiteResult] = SuiteRunner.runSuites(suitesBundles, environment, () => new RestClient(FakeScAPIRequestSender)) val beforeSuiteResult: SuiteResult = suiteResults.find(result => - result.resultType == SuiteResultType.BeforeTestSet && result.suiteName == "name2").get + result.resultType == SuiteResultType.BeforeSuiteResult && result.suiteName == "name2").get assert(5 == clue(suiteResults.size)) assert("test" == clue(beforeSuiteResult.name)) assert(clue(beforeSuiteResult.isSuccess)) } - test("runSuite - SuiteBefore empty") { + test("runSuite - BeforeSuite empty") { val suiteResults: List[SuiteResult] = SuiteRunner.runSuites(suitesBundleNoBefore, environment, () => new RestClient(FakeScAPIRequestSender)) val beforeSuiteResult: Option[SuiteResult] = suiteResults.find(result => - result.resultType == SuiteResultType.BeforeTestSet && result.suiteName == "name2") + result.resultType == SuiteResultType.BeforeSuiteResult && result.suiteName == "name2") assert(2 == clue(suiteResults.size)) assert(beforeSuiteResult.isEmpty) } - test("runSuite - SuiteAfter exists") { + test("runSuite - AfterSuite exists") { val suiteResults: List[SuiteResult] = SuiteRunner.runSuites(suitesBundles, environment, () => new RestClient(FakeScAPIRequestSender)) val afterSuiteResult: SuiteResult = suiteResults.find(result => - result.resultType == SuiteResultType.AfterTestSet && result.suiteName == "name2").get + result.resultType == SuiteResultType.AfterSuiteResult && result.suiteName == "name2").get assert(5 == clue(suiteResults.size)) assert("test" == clue(afterSuiteResult.name)) assert(clue(afterSuiteResult.isSuccess)) } - test("runSuite - SuiteAfter empty") { + test("runSuite - AfterSuite empty") { val suiteResults: List[SuiteResult] = SuiteRunner.runSuites(suitesBundleNoAfter, environment, () => new RestClient(FakeScAPIRequestSender)) val afterSuiteResult: Option[SuiteResult] = suiteResults.find(result => - result.resultType == SuiteResultType.AfterTestSet && result.suiteName == "name2") + result.resultType == SuiteResultType.AfterSuiteResult && result.suiteName == "name2") assert(2 == clue(suiteResults.size)) assert(afterSuiteResult.isEmpty) } - test("runSuite - SuiteAfter empty methods") { + test("runSuite - AfterSuite empty methods") { val suiteResults: List[SuiteResult] = SuiteRunner.runSuites(suitesBundleAfterMethodNotSupported, environment, () => new RestClient(FakeScAPIRequestSender)) val afterSuiteResult: SuiteResult = suiteResults.find(result => - result.resultType == SuiteResultType.AfterTestSet && result.suiteName == "name2").get + result.resultType == SuiteResultType.AfterSuiteResult && result.suiteName == "name2").get assert(2 == clue(suiteResults.size)) assert(clue(!afterSuiteResult.isSuccess))