Show a failed-badge behind report links on the artifact index

Each listed report link now carries the failures counter parsed from the
report's Gradle-style index.html (id="failures" info box); reports with
failures get a red "N failed" badge, so a red build reveals which report to
open without clicking through all of them. Pages without such a counter
(Jacoco, profile, documentation) stay unmarked. A full-text FAILED scan was
deliberately not used: even green hsadmin-ng builds contain the word in
embedded test output.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-08-10 16:44:12 +02:00
co-authored by Claude Fable 5
parent 4ca077da80
commit b351ab33c5
4 changed files with 74 additions and 4 deletions
@@ -15,6 +15,9 @@ import de.hoennig.gittally.metrics.MetricAggregate
import de.hoennig.gittally.metrics.SystemMetrics
import de.hoennig.gittally.metrics.SystemMetricsCollector
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.string.shouldNotContain
import io.mockk.clearMocks
import io.mockk.every
import org.hamcrest.Matchers.containsString
@@ -233,6 +236,40 @@ class UiControllerTest : FunSpec() {
).andExpect(content().string(not(containsString("reports/tests/test/packages/index.html"))))
}
test("report links carry a failed-badge from the report's failures counter") {
val artifactDir = Files.createDirectories(tempDir.resolve("main-abc123-key"))
Files.createDirectories(artifactDir.resolve("reports/tests/test"))
Files.writeString(
artifactDir.resolve("reports/tests/test/index.html"),
"""<div class="infoBox" id="failures">
<div class="counter">2</div>
<p>failures</p></div>""",
)
Files.createDirectories(artifactDir.resolve("reports/tests/unitTest"))
Files.writeString(
artifactDir.resolve("reports/tests/unitTest/index.html"),
"""<div class="infoBox" id="failures">
<div class="counter">0</div>
<p>failures</p></div>""",
)
Files.createDirectories(artifactDir.resolve("reports/jacoco"))
Files.writeString(artifactDir.resolve("reports/jacoco/index.html"), "<html>no counter</html>")
every { repository.history() } returns listOf(successResult)
every { artifactStore.artifactDir("main-abc123-key") } returns artifactDir
val page =
mockMvc
.perform(get("/builds/main-abc123-key"))
.andExpect(status().isOk)
.andReturn()
.response.contentAsString
page shouldContain "2 failed"
page shouldNotContain "0 failed"
// exactly one badge: the report with failures, none for the clean or counter-less reports
Regex(""" failed</span>""").findAll(page).count() shouldBe 1
}
test("artifact index of a pruned build explains the missing artifacts") {
every { repository.history() } returns listOf(successResult)
every { artifactStore.artifactDir("main-abc123-key") } returns null