-
Notifications
You must be signed in to change notification settings - Fork 419
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
NODE-2588 Qase.io integration (#3845)
- Loading branch information
Showing
10 changed files
with
238 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 87 additions & 0 deletions
87
lang/testkit/src/test/scala/com/wavesplatform/report/QaseReporter.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package com.wavesplatform.report | ||
|
||
import com.wavesplatform.report.QaseReporter.{CaseIdPattern, QaseProjects, TestResult} | ||
import io.qase.api.QaseClient | ||
import io.qase.api.utils.IntegrationUtils | ||
import io.qase.client.model.ResultCreate | ||
import org.scalatest.Reporter | ||
import org.scalatest.events.* | ||
import play.api.libs.json.{Format, Json} | ||
|
||
import java.io.FileWriter | ||
import java.util.concurrent.ConcurrentHashMap | ||
import scala.jdk.CollectionConverters.* | ||
import scala.util.matching.Regex | ||
|
||
class QaseReporter extends Reporter { | ||
|
||
private val results = initResults() | ||
|
||
override def apply(event: Event): Unit = { | ||
event match { | ||
case ts: TestSucceeded => | ||
extractCaseIds(ts.testName).foreach { case (projectCode, caseId) => | ||
saveTestCaseResults(ResultCreate.StatusEnum.PASSED, ts.testName, projectCode, caseId, None, None, ts.duration) | ||
} | ||
case tf: TestFailed => | ||
extractCaseIds(tf.testName).foreach { case (projectCode, caseId) => | ||
saveTestCaseResults(ResultCreate.StatusEnum.FAILED, tf.testName, projectCode, caseId, tf.throwable, Some(tf.message), tf.duration) | ||
} | ||
case _: RunAborted | _: RunStopped | _: RunCompleted => | ||
saveRunResults() | ||
case _ => () | ||
} | ||
} | ||
|
||
private def extractCaseIds(testName: String): Seq[(String, Long)] = | ||
CaseIdPattern.findAllMatchIn(testName).map(m => m.group(1) -> m.group(2).toLong).toSeq | ||
|
||
private def saveTestCaseResults( | ||
status: ResultCreate.StatusEnum, | ||
testName: String, | ||
projectCode: String, | ||
caseId: Long, | ||
throwable: Option[Throwable], | ||
msgOpt: Option[String], | ||
duration: Option[Long] | ||
): Unit = | ||
if (QaseClient.isEnabled) { | ||
val errMsg = msgOpt.map(msg => s"\n\n**Error**\n$msg").getOrElse("") | ||
val comment = s"$testName$errMsg" | ||
val stacktrace = throwable.map(IntegrationUtils.getStacktrace) | ||
val timeMs = duration.getOrElse(0L) | ||
|
||
results.computeIfPresent(projectCode, (_, results) => TestResult(status.toString, comment, stacktrace, caseId, timeMs) +: results) | ||
} | ||
|
||
private def saveRunResults(): Unit = | ||
if (QaseClient.isEnabled) { | ||
results.asScala.foreach { case (projectCode, results) => | ||
if (results.nonEmpty) { | ||
val writer = new FileWriter(s"./$projectCode-${System.currentTimeMillis()}") | ||
writer.write(Json.toJson(results).toString) | ||
writer.close() | ||
} | ||
} | ||
} | ||
|
||
private def initResults() = { | ||
val results = new ConcurrentHashMap[String, List[TestResult]]() | ||
QaseProjects.foreach(projectCode => results.put(projectCode, List.empty)) | ||
results | ||
} | ||
} | ||
|
||
object QaseReporter { | ||
val RunIdKeyPrefix = "QASE_RUN_ID_" | ||
val CheckPRRunIdKey = "CHECKPR_RUN_ID" | ||
val QaseProjects = Seq("NODE", "RIDE", "BU", "SAPI") | ||
|
||
private val patternStr = s"""(${QaseProjects.mkString("|")})-([0-9]+)""" | ||
val CaseIdPattern: Regex = patternStr.r | ||
|
||
case class TestResult(status: String, comment: String, stackTrace: Option[String], caseId: Long, timeMs: Long) | ||
object TestResult { | ||
implicit val format: Format[TestResult] = Json.format | ||
} | ||
} |
93 changes: 93 additions & 0 deletions
93
lang/testkit/src/test/scala/com/wavesplatform/report/QaseRunCompleter.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package com.wavesplatform.report | ||
|
||
import com.wavesplatform.report.QaseReporter.{CheckPRRunIdKey, QaseProjects, TestResult} | ||
import io.qase.api.QaseClient | ||
import io.qase.api.config.QaseConfig.{PROJECT_CODE_KEY, RUN_ID_KEY} | ||
import io.qase.api.services.impl.ReportersResultOperationsImpl | ||
import io.qase.client.ApiClient | ||
import io.qase.client.api.{CasesApi, ResultsApi, RunsApi} | ||
import io.qase.client.model.{GetCasesFiltersParameter, GetRunsFiltersParameter, ResultCreate, RunCreate} | ||
import play.api.libs.json.Json | ||
|
||
import java.io.File | ||
import java.nio.file.Files | ||
import scala.annotation.tailrec | ||
import scala.io.Source | ||
import scala.util.Using | ||
|
||
object QaseRunCompleter extends App { | ||
if (QaseClient.getConfig.isEnabled) { | ||
|
||
val apiClient: ApiClient = QaseClient.getApiClient | ||
val runsApi = new RunsApi(apiClient) | ||
val resultsApi = new ResultsApi(apiClient) | ||
val casesApi = new CasesApi(apiClient) | ||
|
||
QaseProjects.foreach { projectCode => | ||
waitForActiveRunComplete(runsApi) | ||
|
||
val dir = new File("./") | ||
val resultFiles = dir.listFiles | ||
.filter(_.isFile) | ||
.filter(_.getName.startsWith(projectCode)) | ||
.toSeq | ||
|
||
Using.resource(resultFiles) { resultFiles => | ||
val hasCases = casesApi.getCases(projectCode, new GetCasesFiltersParameter(), 1, 0).getResult.getCount > 0 | ||
|
||
if (hasCases) { | ||
val results = resultFiles.flatMap { file => | ||
Using.resource(Source.fromFile(file)) { source => | ||
Json.parse(source.getLines().mkString("\n")).as[Seq[TestResult]] | ||
} | ||
} | ||
|
||
val description = Option(System.getenv(CheckPRRunIdKey)) | ||
.map(rId => s"[GitHub checkPR action run details](https://github.com/wavesplatform/Waves/actions/runs/$rId)") | ||
.getOrElse("Local checkPR run") | ||
val title = Option(QaseClient.getConfig.runName()).getOrElse("unknown") | ||
|
||
val runId = runsApi | ||
.createRun( | ||
projectCode, | ||
new RunCreate() | ||
.title(title) | ||
.description(description) | ||
.includeAllCases(true) | ||
.isAutotest(true) | ||
) | ||
.getResult | ||
.getId | ||
val resultOps = new ReportersResultOperationsImpl(resultsApi) | ||
results.foreach { result => | ||
resultOps.addBulkResult( | ||
new ResultCreate() | ||
.status(ResultCreate.StatusEnum.fromValue(result.status)) | ||
.comment(result.comment) | ||
.stacktrace(result.stackTrace.orNull) | ||
.caseId(result.caseId) | ||
.timeMs(result.timeMs) | ||
) | ||
} | ||
|
||
QaseClient.getConfig.setProperty(RUN_ID_KEY, runId.toString) | ||
QaseClient.getConfig.setProperty(PROJECT_CODE_KEY, projectCode) | ||
resultOps.sendBulkResult() | ||
|
||
runsApi.completeRun(projectCode, runId.toInt) | ||
} | ||
}(_.foreach(f => Files.delete(f.toPath))) | ||
} | ||
} | ||
|
||
@tailrec | ||
private def waitForActiveRunComplete(runsApi: RunsApi, retries: Int = 10): Unit = { | ||
val hasActiveRuns = QaseProjects.exists { projectCode => | ||
runsApi.getRuns(projectCode, new GetRunsFiltersParameter().status("active"), 1, 0, "cases").getResult.getCount > 0 | ||
} | ||
if (hasActiveRuns && retries > 0) { | ||
Thread.sleep(3000) | ||
waitForActiveRunComplete(runsApi, retries - 1) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.