All Downloads are FREE. Search and download functionalities are using the official Maven repository.

in.specmatic.test.reports.coverage.OpenApiCoverageReportInput.kt Maven / Gradle / Ivy

Go to download

Run contracts as tests in Junit tests using Specmatic. Deprecation Notice for group ID "in.specmatic" ****************************************************************************************************** Updates for "junit5-support" will no longer be available under the deprecated group ID "in.specmatic". Please update your dependencies to use the new group ID "io.specmatic". ******************************************************************************************************

There is a newer version: 1.3.39
Show newest version
package `in`.specmatic.test.reports.coverage

import `in`.specmatic.conversions.SERVICE_TYPE_HTTP
import `in`.specmatic.conversions.convertPathParameterStyle
import `in`.specmatic.core.TestResult
import `in`.specmatic.test.API
import `in`.specmatic.test.TestResultRecord
import `in`.specmatic.test.reports.coverage.console.OpenAPICoverageConsoleReport
import `in`.specmatic.test.reports.coverage.console.OpenApiCoverageConsoleRow
import `in`.specmatic.test.reports.coverage.console.Remarks
import `in`.specmatic.test.reports.coverage.json.OpenApiCoverageJsonReport
import kotlin.math.min
import kotlin.math.roundToInt

class OpenApiCoverageReportInput(
    private var configFilePath:String,
    internal val testResultRecords: MutableList = mutableListOf(),
    private val applicationAPIs: MutableList = mutableListOf(),
    private val excludedAPIs: MutableList = mutableListOf(),
    private val allEndpoints: MutableList = mutableListOf(),
    private var endpointsAPISet: Boolean = false
) {
    fun addTestReportRecords(testResultRecord: TestResultRecord) {
        testResultRecords.add(testResultRecord)
    }

    fun addAPIs(apis: List) {
        applicationAPIs.addAll(apis)
    }

    fun addExcludedAPIs(apis: List){
        excludedAPIs.addAll(apis)
    }

    fun addEndpoints(endpoints: List) {
        allEndpoints.addAll(endpoints)
    }

    fun setEndpointsAPIFlag(isSet: Boolean) {
        endpointsAPISet = isSet
    }

    fun generate(): OpenAPICoverageConsoleReport {
        val testResults = testResultRecords.filter { testResult -> excludedAPIs.none { it == testResult.path } }
        val testResultsWithNotImplementedEndpoints = identifyTestsThatFailedBecauseOfEndpointsThatWereNotImplemented(testResults)
        var allTests = addTestResultsForMissingEndpoints(testResultsWithNotImplementedEndpoints)
        allTests = addTestResultsForTestsNotGeneratedBySpecmatic(allTests, allEndpoints)
        allTests = sortByPathMethodResponseStatus(allTests)

        val apiTestsGrouped = groupTestsByPathMethodAndResponseStatus(allTests)
        val apiCoverageRows: MutableList = mutableListOf()
        apiTestsGrouped.forEach { (route, methodMap) ->
            val routeAPIRows: MutableList = mutableListOf()
            val topLevelCoverageRow = createTopLevelApiCoverageRow(route, methodMap)
            methodMap.forEach { (method, responseCodeMap) ->
                responseCodeMap.forEach { (responseStatus, testResults) ->
                    if (routeAPIRows.isEmpty()) {
                        routeAPIRows.add(topLevelCoverageRow)
                    } else {
                        val rowMethod = if (routeAPIRows.none { it.method == method }) method else ""
                        routeAPIRows.add(
                            topLevelCoverageRow.copy(
                                method = rowMethod,
                                path ="",
                                responseStatus = responseStatus.toString(),
                                count = testResults.count{it.isExercised}.toString(),
                                coveragePercentage = 0,
                                remarks = Remarks.resolve(testResults)
                            )
                        )
                    }
                }
            }
            apiCoverageRows.addAll(routeAPIRows)
        }

        val totalAPICount = apiTestsGrouped.keys.size

        val missedAPICount = allTests.groupBy { it.path }.filter { pathMap -> pathMap.value.all { it.result == TestResult.Skipped  } }.size

        val notImplementedAPICount = allTests.groupBy { it.path }.filter { pathMap -> pathMap.value.all { it.result in setOf(TestResult.NotImplemented,  TestResult.DidNotRun) } }.size

        val partiallyMissedAPICount = allTests.groupBy { it.path }
            .count { (_, tests) ->
                tests.any { it.result == TestResult.Skipped } && tests.any {it.result != TestResult.Skipped }
            }

        val partiallyNotImplementedAPICount = allTests.groupBy { it.path }
            .count { (_, tests) ->
                tests.any { it.result == TestResult.NotImplemented } && tests.any {it.result in setOf(TestResult.Success , TestResult.Skipped, TestResult.Failed) }
            }

        return OpenAPICoverageConsoleReport(apiCoverageRows, totalAPICount, missedAPICount, notImplementedAPICount, partiallyMissedAPICount, partiallyNotImplementedAPICount)
    }

    private fun addTestResultsForTestsNotGeneratedBySpecmatic(allTests: List, allEndpoints: List): List {
        val endpointsWithoutTests =
            allEndpoints.filter { endpoint ->
                allTests.none { it.path == endpoint.path && it.method == endpoint.method && it.responseStatus == endpoint.responseStatus }
                        && excludedAPIs.none { it == endpoint.path }
            }
        return allTests.plus(
            endpointsWithoutTests.map { endpoint ->  TestResultRecord(
                endpoint.path,
                endpoint.method,
                endpoint.responseStatus,
                TestResult.DidNotRun,
                endpoint.sourceProvider,
                endpoint.sourceRepository,
                endpoint.sourceRepositoryBranch,
                endpoint.specification,
                endpoint.serviceType
            ) }
        )
    }

    fun generateJsonReport(): OpenApiCoverageJsonReport {
        val testResults = testResultRecords.filter { testResult -> excludedAPIs.none { it == testResult.path } }
        val testResultsWithNotImplementedEndpoints = identifyTestsThatFailedBecauseOfEndpointsThatWereNotImplemented(testResults)
        val allTests = addTestResultsForMissingEndpoints(testResultsWithNotImplementedEndpoints)
        return OpenApiCoverageJsonReport(configFilePath, allTests)
    }

    private fun groupTestsByPathMethodAndResponseStatus(allAPITests: List): MutableMap>>> {
        return allAPITests.groupBy { it.path }
            .mapValues { (_, pathResults) ->
                pathResults.groupBy { it.method }
                    .mapValues { (_, methodResults) ->
                        methodResults.groupBy { it.responseStatus }
                            .mapValues { (_, responseResults) ->
                                responseResults.toMutableList()
                            }.toMutableMap()
                    }.toMutableMap()
            }.toMutableMap()
    }

    private fun sortByPathMethodResponseStatus(testResultRecordList: List): List {
        val recordsWithFixedURLs = testResultRecordList.map {
            it.copy(path = convertPathParameterStyle(it.path))
        }
        return recordsWithFixedURLs.groupBy {
            "${it.path}-${it.method}-${it.responseStatus}"
        }.let { sortedRecords: Map> ->
            sortedRecords.keys.sorted().map { key ->
                sortedRecords.getValue(key)
            }
        }.flatten()
    }

    private fun addTestResultsForMissingEndpoints(testResults: List): List {
        val testReportRecordsIncludingMissingAPIs = testResults.toMutableList()
        if(endpointsAPISet) {
            applicationAPIs.forEach { api ->
                if (allEndpoints.none { it.path == api.path && it.method == api.method } && excludedAPIs.none { it == api.path }) {
                    testReportRecordsIncludingMissingAPIs.add(
                        TestResultRecord(
                            api.path,
                            api.method,
                            0,
                            TestResult.Skipped,
                            serviceType = SERVICE_TYPE_HTTP
                        )
                    )
                }
            }
        }
        return testReportRecordsIncludingMissingAPIs
    }

    private fun createTopLevelApiCoverageRow(
        route: String,
        methodMap: MutableMap>>,
    ): OpenApiCoverageConsoleRow {
        val method = methodMap.keys.first()
        val responseStatus = methodMap[method]?.keys?.first()
        val remarks = Remarks.resolve(methodMap[method]?.get(responseStatus)!!)
        val exercisedCount = methodMap[method]?.get(responseStatus)?.count { it.isExercised }

        val totalMethodResponseCodeCount = methodMap.values.sumOf { it.keys.size }
        var totalMethodResponseCodeCoveredCount = 0
        methodMap.forEach { (_, responses) ->
            responses.forEach { (_, testResults) ->
                val increment = min(testResults.count { it.isCovered }, 1)
                totalMethodResponseCodeCoveredCount += increment
            }
        }

        val coveragePercentage =
            ((totalMethodResponseCodeCoveredCount.toFloat() / totalMethodResponseCodeCount.toFloat()) * 100).roundToInt()
        return OpenApiCoverageConsoleRow(
            method,
            route,
            responseStatus!!,
            exercisedCount!!,
            coveragePercentage,
            remarks
        )
    }

    private fun identifyTestsThatFailedBecauseOfEndpointsThatWereNotImplemented(testResults: List): List {
        val notImplementedTests =
            testResults.filter { it.result == TestResult.Failed && (endpointsAPISet && applicationAPIs.none { api -> api.path == it.path && api.method == it.method }) }
        return testResults.minus(notImplementedTests.toSet())
            .plus(notImplementedTests.map { it.copy(result = TestResult.NotImplemented) })
    }
}

data class CoverageGroupKey(
    val sourceProvider: String?,
    val sourceRepository: String?,
    val sourceRepositoryBranch: String?,
    val specification: String?,
    val serviceType: String?
)

data class Endpoint(
    val path: String,
    val method: String,
    val responseStatus: Int,
    val sourceProvider: String? = null,
    val sourceRepository: String? = null,
    val sourceRepositoryBranch: String? = null,
    val specification: String? = null,
    val serviceType: String? = null
)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy