Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion test/e2e/v2/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ This is a Ginkgo v2 BDD test suite for validating hosted cluster control planes.

The framework is organized into the following packages under `test/e2e/v2/`:

- `internal/` — Framework internals (test context, workload registry, fail handler, env var management). Do not add tests here.
- `internal/` — Framework internals (test context, workload registry, fail handler, env var management). Do not add e2e tests here; standard Go unit tests (`func TestXxx(t *testing.T)`) for internal functions are fine.
- `tests/` — All standard v2 test files. Each file is feature-scoped with a top-level `Describe` and `Label`. The suite entry point is `suite_test.go`.
- `util/` — Shared test utilities (pod exec helpers, metrics fetching) consumed by test files. Unlike `internal/`, these are importable by other packages.
- `lifecycle/` — Platform-specific lifecycle helpers (e.g., Azure platform hooks).
Expand Down
27 changes: 23 additions & 4 deletions test/e2e/v2/internal/fail_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,37 @@ package internal

import (
"slices"
"strings"

. "github.com/onsi/ginkgo/v2"
"github.com/onsi/ginkgo/v2/types"
)

// InformingLabel is the Ginkgo label that marks a test as informing.
const InformingLabel = "Informing"

const informingSkipPrefix = "informing test failure: "

// InformingAwareFailHandler checks if the current spec has the "Informing" label.
// If so, it skips the test with the failure message instead of failing the suite.
func InformingAwareFailHandler(message string, callerSkip ...int) {
labels := CurrentSpecReport().Labels()
if slices.Contains(labels, "Informing") {
// Skip marks test as skipped (visible in reports) without failing suite
Skip("informing test failure: " + message, callerSkip...)
if slices.Contains(labels, InformingLabel) {
Skip(informingSkipPrefix+message, callerSkip...)
}
// For non-Informing tests, fail normally
Fail(message, callerSkip...)
}

// IsInformingFailureSkip returns true if the spec was skipped by
// InformingAwareFailHandler due to an informing test failure.
func IsInformingFailureSkip(spec types.SpecReport) bool {
return spec.State == types.SpecStateSkipped &&
slices.Contains(spec.Labels(), InformingLabel) &&
strings.HasPrefix(spec.Failure.Message, informingSkipPrefix)
}

// InformingFailureMessage extracts the original failure message from a spec
// that was skipped by InformingAwareFailHandler.
func InformingFailureMessage(spec types.SpecReport) string {
return strings.TrimPrefix(spec.Failure.Message, informingSkipPrefix)
}
102 changes: 102 additions & 0 deletions test/e2e/v2/internal/junit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//go:build e2ev2
Comment thread
ironcladlou marked this conversation as resolved.

package internal

import (
"encoding/xml"

"github.com/onsi/ginkgo/v2/types"
)

const lifecycleInforming = "informing"

type JUnitTestSuites struct {
XMLName xml.Name `xml:"testsuites"`
Suites []*JUnitTestSuite `xml:"testsuite"`
}

type JUnitTestSuite struct {
XMLName xml.Name `xml:"testsuite"`
Name string `xml:"name,attr"`
NumTests int `xml:"tests,attr"`
NumSkipped int `xml:"skipped,attr"`
NumFailed int `xml:"failures,attr"`
Duration float64 `xml:"time,attr"`
TestCases []*JUnitTestCase `xml:"testcase"`
}

type JUnitTestCase struct {
XMLName xml.Name `xml:"testcase"`
Name string `xml:"name,attr"`
Duration float64 `xml:"time,attr"`
Lifecycle string `xml:"lifecycle,attr,omitempty"`
Properties []*JUnitProperty `xml:"properties>property,omitempty"`
SkipMessage *JUnitSkipMessage `xml:"skipped,omitempty"`
FailureOutput *JUnitFailureOutput `xml:"failure,omitempty"`
}

type JUnitProperty struct {
XMLName xml.Name `xml:"property"`
Name string `xml:"name,attr"`
Value string `xml:"value,attr"`
}

type JUnitSkipMessage struct {
XMLName xml.Name `xml:"skipped"`
Message string `xml:"message,attr,omitempty"`
}

type JUnitFailureOutput struct {
XMLName xml.Name `xml:"failure"`
Message string `xml:"message,attr,omitempty"`
Output string `xml:",chardata"`
}

// BuildInformingTestsLifecycleReport builds a JUnit test suite containing only informing test
// failures from the Ginkgo report. Informing failures converted to skips by
// InformingAwareFailHandler are re-emitted as failures with lifecycle="informing".
// ci-to-bigquery reads this attribute and populates the lifecycle column in
// BigQuery, making informing failures visible to Component Readiness.
func BuildInformingTestsLifecycleReport(suiteName string, specReports types.SpecReports) *JUnitTestSuites {
suite := &JUnitTestSuite{
Name: suiteName + " [informing]",
}

for _, spec := range specReports {
if !IsInformingFailureSkip(spec) {
continue
}

msg := InformingFailureMessage(spec)
tc := &JUnitTestCase{
Name: spec.FullText(),
Duration: spec.RunTime.Seconds(),
Lifecycle: lifecycleInforming,
Properties: []*JUnitProperty{
{Name: "lifecycle", Value: lifecycleInforming},
},
FailureOutput: &JUnitFailureOutput{
Message: msg,
Output: spec.Failure.Location.String(),
},
}

suite.TestCases = append(suite.TestCases, tc)
suite.NumTests++
suite.NumFailed++
}

suite.Duration = sumDuration(suite.TestCases)

return &JUnitTestSuites{
Suites: []*JUnitTestSuite{suite},
}
}

func sumDuration(cases []*JUnitTestCase) float64 {
var total float64
for _, tc := range cases {
total += tc.Duration
}
return total
}
Loading