From 3fe2924d92c5fabfb996fc1aaede999c479d4ec3 Mon Sep 17 00:00:00 2001 From: miroslavpojer Date: Thu, 30 Nov 2023 08:36:38 +0100 Subject: [PATCH 1/3] * Logic reading suites files is now using suites folder as root dir. --- .../scala/africa/absa/testing/scapi/ScAPIRunner.scala | 5 +++-- .../absa/testing/scapi/json/factory/SuiteFactory.scala | 4 ++-- .../africa/absa/testing/scapi/utils/file/FileUtils.scala | 6 ++---- .../africa/absa/testing/scapi/json/SuiteFactoryTest.scala | 5 +++-- .../absa/testing/scapi/utils/file/FileUtilsTest.scala | 8 ++++---- 5 files changed, 14 insertions(+), 14 deletions(-) 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 ea6cfe0..1ce8930 100644 --- a/testApi/src/main/scala/africa/absa/testing/scapi/ScAPIRunner.scala +++ b/testApi/src/main/scala/africa/absa/testing/scapi/ScAPIRunner.scala @@ -55,11 +55,12 @@ object ScAPIRunner { } cmd.logConfigInfo() - if (!Files.exists(Paths.get(cmd.testRootPath, "suites"))) throw SuiteLoadFailedException("'suites' directory have to exist in project root.") + val suitesPath = Paths.get(cmd.testRootPath, "suites") + if (!Files.exists(suitesPath)) throw SuiteLoadFailedException("'suites' directory have to exist in project root.") // jsons to objects val environment: Environment = EnvironmentFactory.fromFile(cmd.envPath) - val suiteBundles: Set[Suite] = SuiteFactory.fromFiles(environment, cmd.testRootPath, cmd.filter, cmd.fileFormat) + val suiteBundles: Set[Suite] = SuiteFactory.fromFiles(environment, suitesPath, cmd.filter, cmd.fileFormat) SuiteFactory.validateSuiteContent(suiteBundles) // run tests and result reporting - use categories for test filtering 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 023bdcd..a001b94 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,7 +45,7 @@ object SuiteFactory { * @param format The format of suite JSON files. * @return Set of Suite instances. */ - def fromFiles(environment: Environment, testRootPath: String, filter: String, format: String): Set[Suite] = { + def fromFiles(environment: Environment, testRootPath: Path, filter: String, format: String): Set[Suite] = { // NOTE: format not used as json is only supported format in time od development val suiteLoadingResults: Map[String, Try[Suite]] = { @@ -120,7 +120,7 @@ object SuiteFactory { * @param filter The filter string to be used for finding suite JSON files. * @return Set of paths to suite JSON files. */ - private def findSuiteJsonFiles(path: String, filter: String): Set[String] = FileUtils.findMatchingFiles(path, filter + "\\.suite\\.json") + private def findSuiteJsonFiles(path: Path, filter: String): Set[String] = FileUtils.findMatchingFiles(path, filter + "\\.suite\\.json") /** * Method to load a SuiteBundle instance from the given suite JSON file path. diff --git a/testApi/src/main/scala/africa/absa/testing/scapi/utils/file/FileUtils.scala b/testApi/src/main/scala/africa/absa/testing/scapi/utils/file/FileUtils.scala index cd14387..183a348 100644 --- a/testApi/src/main/scala/africa/absa/testing/scapi/utils/file/FileUtils.scala +++ b/testApi/src/main/scala/africa/absa/testing/scapi/utils/file/FileUtils.scala @@ -31,7 +31,7 @@ object FileUtils { * @param pattern A regex pattern to match file names. The default value is "(.*)" which matches all files. * @return Set A set of file paths as strings that match the provided pattern. */ - def findMatchingFiles(path: String, pattern: String = "(.*)"): Set[String] = { + def findMatchingFiles(path: Path, pattern: String = "(.*)"): Set[String] = { def findFilesRecursive(directory: File): Set[File] = { val files = directory.listFiles.toSet val matchingFiles = files.filter(_.isFile).filter(_.getName.matches(pattern)) @@ -40,9 +40,7 @@ object FileUtils { matchingFiles ++ subDirectories.flatMap(findFilesRecursive) } - val rootDirectory = new File(path) - val matchingFiles = findFilesRecursive(rootDirectory) - + val matchingFiles = findFilesRecursive(path.toFile) matchingFiles.map(_.getPath) } diff --git a/testApi/src/test/scala/africa/absa/testing/scapi/json/SuiteFactoryTest.scala b/testApi/src/test/scala/africa/absa/testing/scapi/json/SuiteFactoryTest.scala index acb2d07..710f932 100644 --- a/testApi/src/test/scala/africa/absa/testing/scapi/json/SuiteFactoryTest.scala +++ b/testApi/src/test/scala/africa/absa/testing/scapi/json/SuiteFactoryTest.scala @@ -17,7 +17,7 @@ package africa.absa.testing.scapi.json import africa.absa.testing.scapi.json.factory.SuiteFactory -import africa.absa.testing.scapi.model.suite.{TestSet, Suite, SuiteTestScenario} +import africa.absa.testing.scapi.model.suite.{Suite, SuiteTestScenario, TestSet} import africa.absa.testing.scapi.{ProjectLoadFailedException, UndefinedConstantsInPropertiesException} import munit.FunSuite import org.apache.logging.log4j.LogManager @@ -26,6 +26,7 @@ import org.apache.logging.log4j.core.appender.OutputStreamAppender import org.apache.logging.log4j.core.layout.PatternLayout import java.io.ByteArrayOutputStream +import java.nio.file.Paths class SuiteFactoryTest extends FunSuite { @@ -62,7 +63,7 @@ class SuiteFactoryTest extends FunSuite { val constants: Map[String, String] = Map.empty val properties: Map[String, String] = Map.empty val environment: Environment = Environment(constants, properties) - val testRootPath: String = getClass.getResource("/project_with_issues").getPath + val testRootPath = Paths.get(getClass.getResource("/project_with_issues").getPath) initTestLogger() diff --git a/testApi/src/test/scala/africa/absa/testing/scapi/utils/file/FileUtilsTest.scala b/testApi/src/test/scala/africa/absa/testing/scapi/utils/file/FileUtilsTest.scala index be65da7..d6b6d6a 100644 --- a/testApi/src/test/scala/africa/absa/testing/scapi/utils/file/FileUtilsTest.scala +++ b/testApi/src/test/scala/africa/absa/testing/scapi/utils/file/FileUtilsTest.scala @@ -43,7 +43,7 @@ class FileUtilsTest extends FunSuite { test("find matching files") { val (tmpDir, expectedFiles): (Path, Set[String]) = prepareTestData - val matchingFiles = FileUtils.findMatchingFiles(tmpDir.toString) + val matchingFiles = FileUtils.findMatchingFiles(tmpDir) assert(clue(expectedFiles) == clue(matchingFiles)) } @@ -52,7 +52,7 @@ class FileUtilsTest extends FunSuite { val (tmpDir, files) = prepareTestData val expectedFiles = files.filter(file => file.endsWith(".json")) - val matchingFiles = FileUtils.findMatchingFiles(tmpDir.toString, "(.*).json") + val matchingFiles = FileUtils.findMatchingFiles(tmpDir, "(.*).json") assert(clue(expectedFiles) == clue(matchingFiles)) } @@ -61,7 +61,7 @@ class FileUtilsTest extends FunSuite { val (tmpDir, files) = prepareTestData val expectedFiles = Set.empty[String] - val matchingFiles = FileUtils.findMatchingFiles(tmpDir.toString, "(.*).nonsense") + val matchingFiles = FileUtils.findMatchingFiles(tmpDir, "(.*).nonsense") assert(clue(expectedFiles) == clue(matchingFiles)) } @@ -70,7 +70,7 @@ class FileUtilsTest extends FunSuite { val (tmpDir, files) = prepareTestData val expectedFiles = files.filter(file => file.endsWith(".xml")) - val matchingFiles = FileUtils.findMatchingFiles(tmpDir.toString, "(.*).xml") + val matchingFiles = FileUtils.findMatchingFiles(tmpDir, "(.*).xml") assert(clue(expectedFiles) == clue(matchingFiles)) } From 45cb322eb41f39bc3b8e7bfefc287684d4bcefac Mon Sep 17 00:00:00 2001 From: miroslavpojer Date: Thu, 30 Nov 2023 08:48:27 +0100 Subject: [PATCH 2/3] * Synced name of Action attribute to 'method'. Now it is used on same way across project. --- testApi/src/main/resources/schema/after.schema.json | 4 ++-- testApi/src/main/resources/schema/before.schema.json | 4 ++-- testApi/src/main/resources/schema/suite.schema.json | 4 ++-- .../africa/absa/testing/scapi/json/ReferenceResolver.scala | 4 ++-- .../africa/absa/testing/scapi/suite/runner/SuiteRunner.scala | 2 +- .../aulgui-controller/undefinedConstantIssue.suite.json | 2 +- .../resources/test_project/suites/demo/getOwners.after.json | 2 +- .../resources/test_project/suites/demo/getOwners.before.json | 2 +- .../resources/test_project/suites/demo/getOwners.suite.json | 4 ++-- .../absa/testing/scapi/json/ReferenceResolverTest.scala | 2 +- .../absa/testing/scapi/suite/runner/SuiteRunnerTest.scala | 4 ++-- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/testApi/src/main/resources/schema/after.schema.json b/testApi/src/main/resources/schema/after.schema.json index 6ac23a2..3a81a10 100644 --- a/testApi/src/main/resources/schema/after.schema.json +++ b/testApi/src/main/resources/schema/after.schema.json @@ -87,7 +87,7 @@ "type": "object", "additionalProperties": false, "properties": { - "methodName": { + "method": { "type": "string", "enum": ["get", "post", "put", "delete"], "description": "The HTTP method name for the action. Restricted to specific values." @@ -109,7 +109,7 @@ } }, "required": [ - "methodName", + "method", "url" ], "title": "Action", diff --git a/testApi/src/main/resources/schema/before.schema.json b/testApi/src/main/resources/schema/before.schema.json index 521a338..949e87f 100644 --- a/testApi/src/main/resources/schema/before.schema.json +++ b/testApi/src/main/resources/schema/before.schema.json @@ -86,7 +86,7 @@ "type": "object", "additionalProperties": false, "properties": { - "methodName": { + "method": { "type": "string", "description": "The HTTP method name for the action." }, @@ -107,7 +107,7 @@ } }, "required": [ - "methodName", + "method", "url" ], "title": "Action", diff --git a/testApi/src/main/resources/schema/suite.schema.json b/testApi/src/main/resources/schema/suite.schema.json index e8d5701..638486c 100644 --- a/testApi/src/main/resources/schema/suite.schema.json +++ b/testApi/src/main/resources/schema/suite.schema.json @@ -98,7 +98,7 @@ "type": "object", "additionalProperties": false, "properties": { - "methodName": { + "method": { "type": "string", "description": "The HTTP method name for the action." }, @@ -119,7 +119,7 @@ } }, "required": [ - "methodName", + "method", "url" ], "title": "Action", diff --git a/testApi/src/main/scala/africa/absa/testing/scapi/json/ReferenceResolver.scala b/testApi/src/main/scala/africa/absa/testing/scapi/json/ReferenceResolver.scala index b6d83bb..e6618c8 100644 --- a/testApi/src/main/scala/africa/absa/testing/scapi/json/ReferenceResolver.scala +++ b/testApi/src/main/scala/africa/absa/testing/scapi/json/ReferenceResolver.scala @@ -188,11 +188,11 @@ case class Header private(name: String, value: String) extends ReferenceResolver * It implements the `ReferenceResolver` trait to support resolution of reference constants. * * @constructor create a new Action with a name and value. - * @param methodName the name of the action. + * @param method the name of the action. * @param url the value of the action. * @param body the body of the action - optional. */ -case class Action private(methodName: String, url: String, body: Option[String] = None, params: Option[Set[Param]] = None) extends ReferenceResolver { +case class Action private(method: String, url: String, body: Option[String] = None, params: Option[Set[Param]] = None) extends ReferenceResolver { /** * Method to resolve references. 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 ac67541..9a2b52b 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 @@ -193,7 +193,7 @@ object SuiteRunner { val resolvedAction = requestable.action.resolveByRuntimeCache() restClientCreator().sendRequest( - method = resolvedAction.methodName, + method = resolvedAction.method, url = RuntimeCache.resolve(resolvedAction.url), headers = RequestHeaders.buildHeaders(requestable.headers.map(header => header.resolveByRuntimeCache())), body = RequestBody.buildBody(resolvedAction.body), diff --git a/testApi/src/test/resources/project_with_issues/suites/aulgui-controller/undefinedConstantIssue.suite.json b/testApi/src/test/resources/project_with_issues/suites/aulgui-controller/undefinedConstantIssue.suite.json index c454396..2152c1f 100644 --- a/testApi/src/test/resources/project_with_issues/suites/aulgui-controller/undefinedConstantIssue.suite.json +++ b/testApi/src/test/resources/project_with_issues/suites/aulgui-controller/undefinedConstantIssue.suite.json @@ -11,7 +11,7 @@ } ], "action": { - "methodName": "get", + "method": "get", "url": "{{ env.url }}/AULGUI/user" }, "responseActions": [ diff --git a/testApi/src/test/resources/test_project/suites/demo/getOwners.after.json b/testApi/src/test/resources/test_project/suites/demo/getOwners.after.json index 0161412..0dbb4c6 100644 --- a/testApi/src/test/resources/test_project/suites/demo/getOwners.after.json +++ b/testApi/src/test/resources/test_project/suites/demo/getOwners.after.json @@ -14,7 +14,7 @@ } ], "action": { - "methodName": "get", + "method": "get", "url": "{{ env.url }}/api/owners" }, "responseActions": [ diff --git a/testApi/src/test/resources/test_project/suites/demo/getOwners.before.json b/testApi/src/test/resources/test_project/suites/demo/getOwners.before.json index c955f6d..1de3bc4 100644 --- a/testApi/src/test/resources/test_project/suites/demo/getOwners.before.json +++ b/testApi/src/test/resources/test_project/suites/demo/getOwners.before.json @@ -14,7 +14,7 @@ } ], "action": { - "methodName": "get", + "method": "get", "url": "{{ env.url }}/api/owners" }, "responseActions": [ diff --git a/testApi/src/test/resources/test_project/suites/demo/getOwners.suite.json b/testApi/src/test/resources/test_project/suites/demo/getOwners.suite.json index d830fee..fb00c44 100644 --- a/testApi/src/test/resources/test_project/suites/demo/getOwners.suite.json +++ b/testApi/src/test/resources/test_project/suites/demo/getOwners.suite.json @@ -6,7 +6,7 @@ "categories": ["SMOKE"], "headers" : [], "action": { - "methodName": "get", + "method": "get", "url": "{{ env.url }}/api/owner/999" }, "responseActions": [ @@ -24,7 +24,7 @@ "categories": ["SMOKE"], "headers" : [], "action": { - "methodName": "get", + "method": "get", "url": "{{ env.url }}/api/owners/5" }, "responseActions": [ diff --git a/testApi/src/test/scala/africa/absa/testing/scapi/json/ReferenceResolverTest.scala b/testApi/src/test/scala/africa/absa/testing/scapi/json/ReferenceResolverTest.scala index 28b98a3..6d789ee 100644 --- a/testApi/src/test/scala/africa/absa/testing/scapi/json/ReferenceResolverTest.scala +++ b/testApi/src/test/scala/africa/absa/testing/scapi/json/ReferenceResolverTest.scala @@ -23,7 +23,7 @@ import munit.FunSuite class ReferenceResolverTest extends FunSuite { val action: Action = Action( - methodName = "get", + method = "get", url = "nice/{{ cache.urlValue }}", body = Option("body {{ cache.surpriseValue }}"), params = Option(Set(Param("message", "value nr.: {{ cache.numberValue }}"))) 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 8e6a910..f8af938 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 @@ -30,8 +30,8 @@ import org.apache.logging.log4j.Level class SuiteRunnerTest extends FunSuite { val header: Header = Header(name = RequestHeaders.AUTHORIZATION, value = "Basic abcdefg") - val action: Action = Action(methodName = "get", url = "nice url") - val actionNotSupported: Action = Action(methodName = "wrong", url = "nice url") + val action: Action = Action(method = "get", url = "nice url") + val actionNotSupported: Action = Action(method = "wrong", url = "nice url") val responseAction: ResponseAction = ResponseAction(group = ResponseActionGroupType.Assert, name = AssertResponseActionType.StatusCodeEquals.toString, Map("code" -> "200")) val method: Method = Method(name = "test", headers = Seq(header), action = action, responseActions = Seq(responseAction)) val methodNotSupported: Method = Method(name = "test", headers = Seq(header), action = actionNotSupported, responseActions = Seq(responseAction)) From bf7ab8532849d86f7df9b05623f2ec9bf5390a21 Mon Sep 17 00:00:00 2001 From: miroslavpojer Date: Thu, 30 Nov 2023 12:53:05 +0100 Subject: [PATCH 3/3] * Improved error handling and error message when no json string provided in Action body. --- .../absa/testing/scapi/rest/request/RequestBody.scala | 9 ++++++++- .../africa/absa/testing/scapi/json/RequestBodyTest.scala | 3 +-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/testApi/src/main/scala/africa/absa/testing/scapi/rest/request/RequestBody.scala b/testApi/src/main/scala/africa/absa/testing/scapi/rest/request/RequestBody.scala index 11c9e9e..5741674 100644 --- a/testApi/src/main/scala/africa/absa/testing/scapi/rest/request/RequestBody.scala +++ b/testApi/src/main/scala/africa/absa/testing/scapi/rest/request/RequestBody.scala @@ -18,6 +18,7 @@ package africa.absa.testing.scapi.rest.request import africa.absa.testing.scapi.ContentValidationFailedException import africa.absa.testing.scapi.utils.cache.RuntimeCache +import spray.json.JsonParser.ParsingException import spray.json._ import scala.util.{Failure, Try} @@ -37,7 +38,13 @@ object RequestBody { */ def buildBody(jsonBody: Option[String] = None): String = { jsonBody match { - case Some(body) if body.trim.nonEmpty => RuntimeCache.resolve(body.parseJson.toString()) + case Some(body) if body.trim.nonEmpty => + try { + val jsonAst = body.parseJson // Attempt to parse the JSON + RuntimeCache.resolve(jsonAst.toString()) // If successful, resolve + } catch { + case e: ParsingException => throw new IllegalArgumentException("Invalid JSON string provided in Action body.") + } case _ => "{}" } } diff --git a/testApi/src/test/scala/africa/absa/testing/scapi/json/RequestBodyTest.scala b/testApi/src/test/scala/africa/absa/testing/scapi/json/RequestBodyTest.scala index 2bb8e66..009a904 100644 --- a/testApi/src/test/scala/africa/absa/testing/scapi/json/RequestBodyTest.scala +++ b/testApi/src/test/scala/africa/absa/testing/scapi/json/RequestBodyTest.scala @@ -19,7 +19,6 @@ package africa.absa.testing.scapi.json import africa.absa.testing.scapi.ContentValidationFailedException import africa.absa.testing.scapi.rest.request.RequestBody import munit.FunSuite -import spray.json.JsonParser.ParsingException class RequestBodyTest extends FunSuite { @@ -36,7 +35,7 @@ class RequestBodyTest extends FunSuite { test("buildBody - throw exception when non json string received") { val jsonBody = Some("""not json string""") - interceptMessage[ParsingException]("Unexpected character 'o' at input index 0 (line 1, position 1), expected JSON Value:\nnot json string\n^\n") { + interceptMessage[IllegalArgumentException]("Invalid JSON string provided in Action body.") { RequestBody.buildBody(jsonBody) } }