Skip to content
This repository was archived by the owner on Mar 28, 2025. It is now read-only.

Commit 2ec99bb

Browse files
#8 - Implement method ideas #1 (#40)
* #8 - Implement method ideas #1 - Added new method for logging - log-info-response. - Added logic toRichString for Response and Request. - Extended Logger to support TRACE. TRACE Level used for printing full content of Response and Request instances.
1 parent b98e1bb commit 2ec99bb

21 files changed

+235
-42
lines changed

testApi/src/main/resources/log4j2.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<Configuration status="WARN">
2+
<Configuration status="INFO">
33
<Appenders>
44
<Console name="Console" target="SYSTEM_OUT">
55
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
66
</Console>
77
</Appenders>
88
<Loggers>
9-
<Root level="debug">
9+
<Root level="INFO">
1010
<AppenderRef ref="Console"/>
1111
</Root>
1212
</Loggers>

testApi/src/main/resources/schema/after.schema.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@
121121
"properties": {
122122
"method": {
123123
"type": "string",
124-
"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", "extractJson.string-from-list"],
124+
"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"],
125125
"description": "The method to be used for the response action. Restricted to specific values."
126126
}
127127
},

testApi/src/main/resources/schema/before.schema.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@
119119
"properties": {
120120
"method": {
121121
"type": "string",
122-
"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", "extractJson.string-from-list"],
122+
"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"],
123123
"description": "The method to be used for the response action."
124124
}
125125
},

testApi/src/main/resources/schema/suite.schema.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@
130130
"properties": {
131131
"method": {
132132
"type": "string",
133-
"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", "extractJson.string-from-list"],
133+
"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"],
134134
"description": "The method to be used for the response action."
135135
}
136136
},

testApi/src/main/scala/africa/absa/testing/scapi/ScAPIRunner.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ object ScAPIRunner {
4747
case Failure(exception) => throw exception
4848
}
4949

50-
Logger.setLevel(if (cmd.debug) Level.DEBUG else Level.INFO)
50+
System.setProperty("log4j.configurationFile", "log4j2.xml")
51+
cmd match {
52+
case c if c.trace => Logger.setLevel(Level.TRACE)
53+
case c if c.debug => Logger.setLevel(Level.DEBUG)
54+
case _ => Logger.setLevel(Level.INFO)
55+
}
5156
cmd.logConfigInfo()
5257

5358
if (!Files.exists(Paths.get(cmd.testRootPath, "suites"))) throw SuiteLoadFailedException("'suites' directory have to exist in project root.")
@@ -70,6 +75,6 @@ object ScAPIRunner {
7075

7176
def main(args: Array[String]): Unit = {
7277
val output = run(args)
73-
Logger.info(s"\n$output")
78+
Logger.info(s"ScAPI Runner output:\n$output")
7479
}
7580
}

testApi/src/main/scala/africa/absa/testing/scapi/config/ScAPIRunnerConfig.scala

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,20 @@ import scopt.OptionParser
2323
import scala.util.{Failure, Success, Try}
2424

2525
/**
26-
* Case class that represents the configuration provided by command line parameters for ScAPIRunner
26+
* Case class that represents the configuration provided by command line parameters for ScAPIRunner.
2727
*
28-
* @param envPath Path to the environment definition. Default is empty string.
29-
* @param testRootPath Path to the root directory of test definitions. Default is empty string.
28+
* @param envPath Path to the environment definition. Default is an empty string.
29+
* @param testRootPath Path to the root directory of test definitions. Default is an empty string.
3030
* @param filter Filter rule for selecting test definitions files. Default is "(.*)".
3131
* @param categories Test categories to include in the test suite. Default is all categories "*".
3232
* @param threadCount Maximum number of threads used to run the test suite. Default is '1'.
3333
* @param fileFormat Format of definition files. Default is 'json'.
34-
* @param report Path to the report output directory. Default is empty string.
35-
* @param validateOnly Validate input definitions only. Default is 'false'.
34+
* @param report Path to the report output directory. Default is an empty string.
35+
* @param debug Enable debug mode. Default is 'false'.
36+
* @param trace Enable trace mode for more detailed logging. Default is 'false'.
37+
* @param validateOnly Validate input definitions only, without executing tests. Default is 'false'.
3638
*
37-
* @constructor Create a new configuration with a env, testRootPath, filter, categories, threadCount, fileFormat, report, validateOnly.
39+
* @constructor Create a new configuration with envPath, testRootPath, filter, categories, threadCount, fileFormat, report, debug, trace, and validateOnly.
3840
*/
3941
case class ScAPIRunnerConfig(envPath: String = "",
4042
testRootPath: String = "",
@@ -44,6 +46,7 @@ case class ScAPIRunnerConfig(envPath: String = "",
4446
fileFormat: String = DefaultFileFormat,
4547
report: String = DefaultReport,
4648
debug: Boolean = false,
49+
trace: Boolean = false,
4750
validateOnly: Boolean = false) {
4851
/**
4952
* Method to log configuration information
@@ -60,6 +63,7 @@ case class ScAPIRunnerConfig(envPath: String = "",
6063
|--fileFormat: $fileFormat
6164
|--threadCount: $threadCount
6265
|--debug: $debug
66+
|--trace: $trace
6367
|--validate only: $validateOnly
6468
""".stripMargin)
6569
}
@@ -143,6 +147,11 @@ object ScAPIRunnerConfig {
143147
.action((_, config) => { config.copy(debug = true) })
144148
.text("Activate debug regime.")
145149

150+
opt[Unit]("trace")
151+
.optional()
152+
.action((_, config) => { config.copy(trace = true) })
153+
.text("Activate trace regime.")
154+
146155
opt[Unit]("validate-only")
147156
.optional()
148157
.action((_, config) => { config.copy(validateOnly = true) })

testApi/src/main/scala/africa/absa/testing/scapi/json/ReferenceResolver.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,17 @@ case class ResponseAction private(group: ResponseActionGroupType,
251251
def resolveByRuntimeCache(): ResponseAction = this.copy(
252252
params = this.params.map { case (k, v) => k -> RuntimeCache.resolve(v) }
253253
)
254+
255+
def toRichString: String = {
256+
val paramsString = params.map { case (name, value) => s" $name: $value" }.mkString("\n")
257+
258+
s"""
259+
|Group: $group
260+
|Name: $name
261+
|Params:
262+
|$paramsString
263+
|""".stripMargin
264+
}
254265
}
255266

256267
/**

testApi/src/main/scala/africa/absa/testing/scapi/logging/Logger.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package africa.absa.testing.scapi.logging
1818

19+
import org.apache.logging.log4j.core.config.Configurator
1920
import org.apache.logging.log4j.{Level, LogManager}
2021

2122
object Logger {
@@ -32,6 +33,7 @@ object Logger {
3233
.map(_.getClassName)
3334
.getOrElse("unknown")
3435
val logger = LogManager.getLogger(callerClassName)
36+
Configurator.setLevel(logger.getName, level)
3537

3638
if (logger.isEnabled(messageLevel)) logger.log(messageLevel, message)
3739
}
@@ -52,4 +54,8 @@ object Logger {
5254
def error(message: String): Unit = {
5355
log(Level.ERROR, message)
5456
}
57+
58+
def trace(message: String): Unit = {
59+
log(Level.TRACE, message)
60+
}
5561
}

testApi/src/main/scala/africa/absa/testing/scapi/rest/RestClient.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package africa.absa.testing.scapi.rest
1818

19+
import africa.absa.testing.scapi.logging.Logger
1920
import africa.absa.testing.scapi.rest.request.sender.RequestSender
2021
import africa.absa.testing.scapi.rest.response.Response
2122

@@ -41,6 +42,19 @@ class RestClient(requestSender: RequestSender) {
4142
* @throws IllegalArgumentException If the method is not 'get', 'post', 'put' or 'delete'.
4243
*/
4344
def sendRequest(method: String, url: String, body: String, headers: Map[String, String], params: Map[String, String], verifySslCerts: Boolean = false): Response = {
45+
Logger.trace(
46+
s"""Sending $method request to
47+
|URL: $url
48+
|Headers:
49+
|${headers.map { case (name, value) => s" $name: $value" }.mkString("\n")}
50+
|Params:
51+
|${params.map { case (name, value) => s" $name: $value" }.mkString("\n")}
52+
|Body:
53+
|$body
54+
|Verify SSL Certs: $verifySslCerts
55+
|""".stripMargin
56+
)
57+
4458
method.toLowerCase match {
4559
case "get" => requestSender.get(url = url, headers = headers, verifySslCerts = verifySslCerts, data = body, params = params)
4660
case "post" => requestSender.post(url = url, headers = headers, verifySslCerts = verifySslCerts, data = body, params = params)

testApi/src/main/scala/africa/absa/testing/scapi/rest/response/Response.scala

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,25 @@ case class Response(statusCode: Int,
3030
statusMessage: String,
3131
headers: Map[String, Seq[String]],
3232
cookies: Map[String, CookieValue],
33-
duration: Long)
33+
duration: Long) {
34+
35+
def toRichString: String = {
36+
val headerString = headers.map { case (name, value) => s" $name: $value" }.mkString("\n")
37+
val cookieString = cookies.map { case (name, value) => s" $name: $value" }.mkString("\n")
38+
39+
s"""
40+
|URL: $url
41+
|Status: $statusCode $statusMessage
42+
|Duration: ${duration}ms
43+
|Headers:
44+
|$headerString
45+
|Cookies:
46+
|$cookieString
47+
|Body:
48+
|$body
49+
|""".stripMargin
50+
}
51+
}
3452

3553
/**
3654
* A singleton object that is responsible for managing and handling responses.
@@ -54,15 +72,19 @@ object Response {
5472
}
5573

5674
/**
57-
* Performs actions on the given Response based on a set of Assertions.
58-
* Each response action is resolved by the runtime cache before being used.
59-
* The appropriate group's performAssertions method is called based on the group type of each Assertion.
60-
* Returns true only if all assertions return true, and false as soon as any assertion returns false.
75+
* Executes a sequence of response actions on the provided Response object based on a set of ResponseActions.
76+
* Each response action is resolved using runtime cache before execution.
77+
* This method delegates the assertion execution to the corresponding performAssertions method
78+
* based on the group type of each ResponseAction within the sequence.
79+
* The method returns successfully only if all assertions pass.
80+
* If any assertion fails, the method returns a failed Try with an appropriate exception.
6181
*
62-
* @param response The response on which actions will be performed.
63-
* @param responseActions The set of response actions that dictate what actions will be performed on the response.
64-
* @return Boolean indicating whether all response actions passed (true) or any response action failed (false). TODO
65-
* @throws IllegalArgumentException If an response action group is not supported.
82+
* @param response The HTTP response object on which the actions will be performed.
83+
* @param responseActions A sequence of ResponseAction objects representing the actions and assertions
84+
* to be performed on the response.
85+
* @return A Try[Unit] indicating success if all response actions pass, or containing
86+
* an exception if any response action fails.
87+
* @throws IllegalArgumentException If an unsupported response action group is encountered.
6688
*/
6789
def perform(response: Response, responseActions: Seq[ResponseAction]): Try[Unit] = {
6890
def logParameters(response: Response, resolvedResponseAction: ResponseAction, exception: Option[Throwable] = None): Unit = {
@@ -80,7 +102,7 @@ object Response {
80102
| Method->'${resolvedResponseAction.name}',
81103
| Params->'$filteredParams',
82104
| Actual Response:
83-
| $response""".stripMargin
105+
| ${response.toRichString}""".stripMargin
84106
val exceptionLog = exception.map(e => s"\nException: ${e.getMessage}").getOrElse("")
85107
Logger.debug(s"Response-${resolvedResponseAction.group}: '${resolvedResponseAction.name}' - error details:$baseLog$exceptionLog")
86108
}

0 commit comments

Comments
 (0)