Skip to content

Commit

Permalink
Don't show the repo coverage section if the repo doesn't have any cov…
Browse files Browse the repository at this point in the history
…erage data to display (#1210)

* Don't show the repo coverage section if the repo doesn't have any coverage data to display

* Add coverage-exists call for repo
  • Loading branch information
craigatk authored Mar 26, 2024
1 parent 11529ba commit 02efa48
Show file tree
Hide file tree
Showing 16 changed files with 505 additions and 112 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,27 @@ class RepositoryCoverageDatabaseRepository(private val dslContext: DSLContext) :
if (timelineEntries.isNotEmpty()) RepositoryCoverageTimeline(timelineEntries.map { it.toFullEntry() }) else null
}

override suspend fun coverageExists(repoName: String, projectName: String?): Boolean =
withContext(Dispatchers.IO) {
dslContext.fetchExists(
dslContext.select(CODE_COVERAGE_RUN.ID)
.from(GIT_METADATA)
.innerJoin(TEST_RUN).on(TEST_RUN.ID.eq(GIT_METADATA.TEST_RUN_ID))
.innerJoin(CODE_COVERAGE_RUN).on(TEST_RUN.PUBLIC_ID.eq(CODE_COVERAGE_RUN.TEST_RUN_PUBLIC_ID))
.innerJoin(CODE_COVERAGE_STATS).on(CODE_COVERAGE_STATS.CODE_COVERAGE_RUN_ID.eq(CODE_COVERAGE_RUN.ID))
.where(
GIT_METADATA.REPO_NAME.eq(repoName)
.let {
if (projectName == null)
it.and(GIT_METADATA.PROJECT_NAME.isNull)
else
it.and(GIT_METADATA.PROJECT_NAME.eq(projectName))
}
.and(CODE_COVERAGE_STATS.SCOPE.eq("GROUP"))
)
)
}

data class ReportTimelineEntry(
val publicId: String,
val createdTimestamp: Instant,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ import projektor.server.api.repository.coverage.RepositoryCoverageTimeline

interface RepositoryCoverageRepository {
suspend fun fetchRepositoryCoverageTimeline(branchType: BranchType, repoName: String, projectName: String?): RepositoryCoverageTimeline?

suspend fun coverageExists(repoName: String, projectName: String?): Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ import projektor.server.api.repository.coverage.RepositoryCoverageTimeline
class RepositoryCoverageService(private val repositoryCoverageRepository: RepositoryCoverageRepository) {
suspend fun fetchRepositoryCoverageTimeline(branchType: BranchType, repoName: String, projectName: String?): RepositoryCoverageTimeline? =
repositoryCoverageRepository.fetchRepositoryCoverageTimeline(branchType, repoName, projectName)

suspend fun coverageExists(repoName: String, projectName: String?): Boolean =
repositoryCoverageRepository.coverageExists(repoName, projectName)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import io.ktor.server.util.getOrFail
import projektor.compare.PreviousTestRunService
import projektor.coverage.CoverageService
import projektor.repository.coverage.RepositoryCoverageService
import projektor.server.api.coverage.CoverageExists
import projektor.server.api.repository.BranchType
import projektor.server.api.repository.coverage.RepositoryCurrentCoverage

Expand Down Expand Up @@ -48,6 +49,27 @@ fun Route.repositoryCoverage(
?: call.respond(HttpStatusCode.NoContent)
}

get("/repo/{orgPart}/{repoPart}/coverage/exists") {
val orgPart = call.parameters.getOrFail("orgPart")
val repoPart = call.parameters.getOrFail("repoPart")
val fullRepoName = "$orgPart/$repoPart"

val coverageExists = repositoryCoverageService.coverageExists(fullRepoName, null)

call.respond(HttpStatusCode.OK, CoverageExists(coverageExists))
}

get("/repo/{orgPart}/{repoPart}/project/{projectName}/coverage/exists") {
val orgPart = call.parameters.getOrFail("orgPart")
val repoPart = call.parameters.getOrFail("repoPart")
val projectName = call.parameters.getOrFail("projectName")
val fullRepoName = "$orgPart/$repoPart"

val coverageExists = repositoryCoverageService.coverageExists(fullRepoName, projectName)

call.respond(HttpStatusCode.OK, CoverageExists(coverageExists))
}

suspend fun handleCurrentCoverageRequest(
orgPart: String,
repoPart: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package projektor.repository.coverage

import com.fasterxml.jackson.module.kotlin.readValue
import io.ktor.http.*
import io.ktor.server.testing.*
import org.apache.commons.lang3.RandomStringUtils
import org.junit.jupiter.api.Test
import projektor.ApplicationTestCase
import projektor.incomingresults.randomPublicId
import projektor.server.api.coverage.CoverageExists
import projektor.server.example.coverage.JacocoXmlLoader
import strikt.api.expectThat
import strikt.assertions.isEqualTo
import strikt.assertions.isFalse
import strikt.assertions.isTrue
import kotlin.test.assertNotNull

class RepositoryCoverageExistsApplicationTest : ApplicationTestCase() {
@Test
fun `when repo has coverage should exist`() {
val orgName = RandomStringUtils.randomAlphabetic(12)
val repoName = "$orgName/repo"

val runInRepoPublicId = randomPublicId()
val runInDifferentProjectPublicId = randomPublicId()
val runInDifferentRepoPublicId = randomPublicId()

withTestApplication(::createTestApplication) {
handleRequest(HttpMethod.Get, "/repo/$repoName/coverage/exists") {

testRunDBGenerator.createTestRunWithCoverageAndGitMetadata(
publicId = runInRepoPublicId,
coverageText = JacocoXmlLoader().serverAppReduced(),
repoName = repoName,
branchName = "main"
)

testRunDBGenerator.createTestRunWithCoverageAndGitMetadata(
publicId = runInDifferentProjectPublicId,
coverageText = JacocoXmlLoader().junitResultsParser(),
repoName = repoName,
branchName = "main",
projectName = "other-project"
)

testRunDBGenerator.createTestRunWithCoverageAndGitMetadata(
publicId = runInDifferentRepoPublicId,
coverageText = JacocoXmlLoader().junitResultsParser(),
repoName = "other/repo",
branchName = "main"
)
}.apply {
expectThat(response.status()).isEqualTo(HttpStatusCode.OK)
val responseBody = response.content
assertNotNull(responseBody)

val coverageExists: CoverageExists = objectMapper.readValue(responseBody)
expectThat(coverageExists.exists).isTrue()
}
}
}

@Test
fun `when coverage exists only in project should not exist`() {
val orgName = RandomStringUtils.randomAlphabetic(12)
val repoName = "$orgName/repo"

val runInDifferentProjectPublicId = randomPublicId()
val runInDifferentRepoPublicId = randomPublicId()

withTestApplication(::createTestApplication) {
handleRequest(HttpMethod.Get, "/repo/$repoName/coverage/exists") {

testRunDBGenerator.createTestRunWithCoverageAndGitMetadata(
publicId = runInDifferentProjectPublicId,
coverageText = JacocoXmlLoader().junitResultsParser(),
repoName = repoName,
branchName = "main",
projectName = "other-project"
)

testRunDBGenerator.createTestRunWithCoverageAndGitMetadata(
publicId = runInDifferentRepoPublicId,
coverageText = JacocoXmlLoader().junitResultsParser(),
repoName = "other/repo",
branchName = "main"
)
}.apply {
expectThat(response.status()).isEqualTo(HttpStatusCode.OK)
val responseBody = response.content
assertNotNull(responseBody)

val coverageExists: CoverageExists = objectMapper.readValue(responseBody)
expectThat(coverageExists.exists).isFalse()
}
}
}

@Test
fun `when repo and project has coverage should exist`() {
val orgName = RandomStringUtils.randomAlphabetic(12)
val repoName = "$orgName/repo"

val projectName = "myproject"

val runInRepoPublicId = randomPublicId()
val runInProjectPublicId = randomPublicId()
val runInDifferentRepoPublicId = randomPublicId()

withTestApplication(::createTestApplication) {
handleRequest(HttpMethod.Get, "/repo/$repoName/project/$projectName/coverage/exists") {

testRunDBGenerator.createTestRunWithCoverageAndGitMetadata(
publicId = runInRepoPublicId,
coverageText = JacocoXmlLoader().serverAppReduced(),
repoName = repoName,
branchName = "main"
)

testRunDBGenerator.createTestRunWithCoverageAndGitMetadata(
publicId = runInProjectPublicId,
coverageText = JacocoXmlLoader().junitResultsParser(),
repoName = repoName,
branchName = "main",
projectName = projectName
)

testRunDBGenerator.createTestRunWithCoverageAndGitMetadata(
publicId = runInDifferentRepoPublicId,
coverageText = JacocoXmlLoader().junitResultsParser(),
repoName = "other/repo",
branchName = "main"
)
}.apply {
expectThat(response.status()).isEqualTo(HttpStatusCode.OK)
val responseBody = response.content
assertNotNull(responseBody)

val coverageExists: CoverageExists = objectMapper.readValue(responseBody)
expectThat(coverageExists.exists).isTrue()
}
}
}

@Test
fun `when coverage only in null project should not exist`() {
val orgName = RandomStringUtils.randomAlphabetic(12)
val repoName = "$orgName/repo"

val projectName = "myproject"

val runInRepoPublicId = randomPublicId()
val runInDifferentRepoPublicId = randomPublicId()

withTestApplication(::createTestApplication) {
handleRequest(HttpMethod.Get, "/repo/$repoName/project/$projectName/coverage/exists") {

testRunDBGenerator.createTestRunWithCoverageAndGitMetadata(
publicId = runInRepoPublicId,
coverageText = JacocoXmlLoader().serverAppReduced(),
repoName = repoName,
branchName = "main"
)

testRunDBGenerator.createTestRunWithCoverageAndGitMetadata(
publicId = runInDifferentRepoPublicId,
coverageText = JacocoXmlLoader().junitResultsParser(),
repoName = "other/repo",
branchName = "main"
)
}.apply {
expectThat(response.status()).isEqualTo(HttpStatusCode.OK)
val responseBody = response.content
assertNotNull(responseBody)

val coverageExists: CoverageExists = objectMapper.readValue(responseBody)
expectThat(coverageExists.exists).isFalse()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,24 @@ fun loadCoverageWithProjectName() {
val currentResultsResponse = sendGroupedResultsToServer(groupedResultsXmlLoader.passingGroupedResults(metadata = resultsMetadata))
sendCoverageToServer(currentResultsResponse.id, JacocoXmlLoader().serverApp())

println("View run coverage and project name at $uiBaseUrl${currentResultsResponse.uri}")
println("View run with coverage and project name at $uiBaseUrl${currentResultsResponse.uri}")
}

fun loadNoCoverageWithProjectName() {
val repoName = "projektor/projektor"
val branchName = "main"
val projectName = "nocoverage"
val gitMetadata = GitMetadata()
gitMetadata.repoName = repoName
gitMetadata.branchName = branchName
gitMetadata.isMainBranch = true
gitMetadata.projectName = projectName
val resultsMetadata = ResultsMetadata()
resultsMetadata.git = gitMetadata

val currentResultsResponse = sendGroupedResultsToServer(groupedResultsXmlLoader.passingGroupedResults(metadata = resultsMetadata))

println("View run with no coverage and project name at $uiBaseUrl${currentResultsResponse.uri}")
}

fun loadCoverage75WithGit() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ fun main() {
loadMultipleCoverageExample()
loadMultipleCoverageWithPreviousRunExample()
loadCoverageWithProjectName()
loadNoCoverageWithProjectName()
loadCoverage75WithGit()
loadCoverage85WithGit()
loadResultsWithGitButWithoutCoverage()
Expand Down
3 changes: 3 additions & 0 deletions ui/cypress/e2e/organization_coverage.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ context("organization coverage", () => {
fixture: "organization/organization_coverage.json",
});

cy.intercept("GET", `repo/${repoName}/coverage/exists`, {
fixture: "repository/coverage_exists_true.json",
});
cy.intercept("GET", `repo/${repoName}/coverage/timeline`, {
fixture: "repository/coverage_timeline.json",
});
Expand Down
26 changes: 26 additions & 0 deletions ui/cypress/e2e/repository_coverage.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ describe("repository coverage", () => {
it("should display repository coverage graph and link to individual test reports", () => {
const repoName = "cov-org/cov-repo";

cy.intercept("GET", `repo/${repoName}/coverage/exists`, {
fixture: "repository/coverage_exists_true.json",
});
cy.intercept("GET", `repo/${repoName}/coverage/timeline`, {
fixture: "repository/coverage_timeline.json",
});
Expand Down Expand Up @@ -45,6 +48,9 @@ describe("repository coverage", () => {
statusCode: 404,
});

cy.intercept("GET", `repo/${repoName}/coverage/exists`, {
fixture: "repository/coverage_exists_true.json",
});
cy.intercept("GET", `repo/${repoName}/coverage/timeline?branch=ALL`, {
fixture: "repository/coverage_timeline.json",
});
Expand Down Expand Up @@ -79,6 +85,9 @@ describe("repository coverage", () => {
it("should display tooltip with coverage data on graph point hover", () => {
const repoName = "cov-org/cov-repo";

cy.intercept("GET", `repo/${repoName}/coverage/exists`, {
fixture: "repository/coverage_exists_true.json",
});
cy.intercept("GET", `repo/${repoName}/coverage/timeline`, {
fixture: "repository/coverage_timeline.json",
});
Expand Down Expand Up @@ -132,6 +141,9 @@ describe("repository coverage", () => {
});
cy.intercept("GET", `run/${testId}/messages`, { messages: [] });

cy.intercept("GET", `repo/${repoName}/coverage/exists`, {
fixture: "repository/coverage_exists_true.json",
});
cy.intercept("GET", `repo/${repoName}/coverage/timeline`, {
fixture: "repository/coverage_timeline.json",
});
Expand Down Expand Up @@ -173,6 +185,13 @@ describe("repository coverage", () => {
});
cy.intercept("GET", `run/${publicId}/messages`, { messages: [] });

cy.intercept(
"GET",
`repo/${repoName}/project/${projectName}/coverage/exists`,
{
fixture: "repository/coverage_exists_true.json",
},
);
cy.intercept(
"GET",
`repo/${repoName}/project/${projectName}/coverage/timeline`,
Expand Down Expand Up @@ -201,6 +220,13 @@ describe("repository coverage", () => {
const repoName = "cov-org/cov-repo";
const projectName = "cov-project";

cy.intercept(
"GET",
`repo/${repoName}/project/${projectName}/coverage/exists`,
{
fixture: "repository/coverage_exists_true.json",
},
);
cy.intercept(
"GET",
`repo/${repoName}/project/${projectName}/coverage/timeline`,
Expand Down
3 changes: 3 additions & 0 deletions ui/cypress/e2e/repository_home.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ context("repository home page", () => {
fixture: "repository/timeline.json",
});

cy.intercept("GET", `repo/${repoName}/coverage/exists`, {
fixture: "repository/coverage_exists_true.json",
});
cy.intercept("GET", `repo/${repoName}/coverage/timeline`, {
fixture: "repository/coverage_timeline.json",
});
Expand Down
3 changes: 3 additions & 0 deletions ui/cypress/fixtures/repository/coverage_exists_true.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"exists": true
}
2 changes: 1 addition & 1 deletion ui/src/Repository/Coverage/RepositoryCoverageDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const RepositoryCoverageDetails = ({
return (
<Typography
align="center"
data-testid="repo-no-coverage"
data-testid="repo-results-no-coverage"
className={classes.noCoverage}
>
No coverage information available for repository {repoName}
Expand Down
Loading

0 comments on commit 02efa48

Please sign in to comment.