Skip to content

Port feature gate promotion logic from o/api to sippy - #3871

Open
dgoodwin wants to merge 42 commits into
openshift:mainfrom
dgoodwin:merge-fg-api-review-logic
Open

Port feature gate promotion logic from o/api to sippy#3871
dgoodwin wants to merge 42 commits into
openshift:mainfrom
dgoodwin:merge-fg-api-review-logic

Conversation

@dgoodwin

@dgoodwin dgoodwin commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

This change unifies sippy's feature gate details page with the logic that was rapidly evolving in the o/api presubmit. Teams will now be able to check their promotion status without opening an o/api PR and waiting for a presubmit to run. The sippy version runs fast enough for live queries. o/api presubmit can use the new API to check validity and just link to this page for results.

New logic includes:

  • support for jobs owned by a feature gate (variant Capability=FeatureGateName)
  • owned jobs must have all tests passing at least 92% (allowing one but not two failures in the min 14 runs)
  • tests owned by other techpreview gates do not count against the above
  • api response has hateoas for approximate raw data used for determination (owned job 92% required a custom query for performance reasons)
  • ui exposes three tabs for the raw data
  • owned jobs are clearly displayed with a link to job analysis
  • adds unit, e2e, and seed data coverage

Summary by CodeRabbit

  • New Features

    • Added detailed feature-gate pages with promotion readiness, test results, install-gate checks, capability regressions, and owned-job analysis.
    • Added sortable variant results, warnings, errors, thresholds, and filtered analysis links.
    • Added direct API endpoints and navigation links for feature-gate details.
    • Feature-gate lists now include matching jobs and simplified detail links.
  • Documentation

    • Documented Feature Gates API list and detail endpoints, navigation links, and promotion readiness data.
  • Tests

    • Added comprehensive coverage for promotion evaluation, regressions, filtering, and detail endpoints.

dgoodwin and others added 30 commits July 20, 2026 10:47
Query prow jobs with a matching Capability variant for the feature gate
and return them in the detail API response. Display the job names as
chips in the top panel of the feature gate detail page.

Co-authored-by: Cursor <cursoragent@cursor.com>
Omits failing gate tests for non-default gates other than our own.
@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: automatic mode

@openshift-ci
openshift-ci Bot requested review from petr-muller and sosiouxme August 4, 2026 18:23
@openshift-ci

openshift-ci Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: dgoodwin

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 4, 2026
@dgoodwin dgoodwin changed the title merge fg api review logic Port feature gate promotion logic from o/api to sippy Aug 4, 2026
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 0f1f5fd9-5bb1-4e28-80ec-c0f944e572f9

📥 Commits

Reviewing files that changed from the base of the PR and between e5d63b9 and 0ff0c81.

📒 Files selected for processing (7)
  • pkg/api/README.md
  • pkg/api/featuregatepromotion/filters.go
  • pkg/api/featuregatepromotion/promotion.go
  • pkg/api/tests.go
  • pkg/apis/api/types.go
  • pkg/sippyserver/server.go
  • pkg/util/param/param.go
🚧 Files skipped from review as they are similar to previous changes (7)
  • pkg/util/param/param.go
  • pkg/api/README.md
  • pkg/api/featuregatepromotion/filters.go
  • pkg/sippyserver/server.go
  • pkg/apis/api/types.go
  • pkg/api/featuregatepromotion/promotion.go
  • pkg/api/tests.go

Walkthrough

The change adds feature-gate promotion evaluation across the API and UI. It adds variant and regression analysis, a feature-gate detail endpoint, promotion-focused links and tabs, seeded scenarios, documentation, and end-to-end coverage.

Changes

Feature-gate promotion

Layer / File(s) Summary
Promotion contracts and evaluation
pkg/api/featuregatepromotion/*, pkg/api/tests.go, pkg/api/featuregatepromotion/promotion_test.go
Adds promotion types, filters, variant rules, thresholds, regression detection, lookback aggregation, and unit tests.
Feature-gate detail API and links
pkg/apis/api/types.go, pkg/db/query/feature_gates.go, pkg/sippyserver/server.go, pkg/util/param/param.go, pkg/api/README.md, test/e2e/feature_gates_test.go
Adds promotion fields, matching jobs, detail links, the detail endpoint, parameter validation, documentation, and endpoint tests.
Promotion detail interface
sippy-ng/src/tests/FeatureGateDetail.jsx, sippy-ng/src/tests/FeatureGatePromotionTab.jsx
Adds direct detail loading, dynamic tabs, promotion tables, regression displays, matching-job analysis links, and filtered test-analysis navigation.
Seeded scenarios and end-to-end validation
cmd/sippy/seed_data.go, test/e2e/feature_gate_promotion_test.go
Adds deterministic gate and capability records and validates promotion results, install gates, ignored regressions, and blocking thresholds.

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested labels: ready-for-human-review

Suggested reviewers: sosiouxme, petr-muller

Sequence Diagram(s)

sequenceDiagram
  participant FeatureGateDetail
  participant FeatureGateDetailHandler
  participant GetPromotionStatus
  participant QueryTestResults
  participant Database
  FeatureGateDetail->>FeatureGateDetailHandler: request feature-gate detail
  FeatureGateDetailHandler->>Database: load feature gate and matching jobs
  FeatureGateDetailHandler->>GetPromotionStatus: compute promotion status
  GetPromotionStatus->>QueryTestResults: query gate tests and regressions
  QueryTestResults->>Database: execute filtered test-results query
  QueryTestResults-->>GetPromotionStatus: return test results
  GetPromotionStatus-->>FeatureGateDetailHandler: return promotion status
  FeatureGateDetailHandler-->>FeatureGateDetail: return detail and promotion data
Loading
🚥 Pre-merge checks | ✅ 16 | ❌ 5

❌ Failed checks (5 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 73.08% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Go Error Handling ⚠️ Warning New code drops json.Marshal errors in buildFilteredTestsURL, returns DB/date errors without fmt.Errorf context, and dereferences dbc without nil checks. Handle and propagate json.Marshal errors, wrap every returned DB/date/query error with %w, and validate dbc and dbc.DB before dereferencing.
Excessive Css In React Should Use Styles ⚠️ Warning FeatureGatePromotionTab has 33 sx and 7 style attributes, including a five-property sx object and repeated textDecoration and table-cell styles without useStyles. Extract the five-property and repeated styles into useStyles() or styled components. Keep only simple dynamic values inline.
Test Coverage For New Features ⚠️ Warning The 784-line FeatureGatePromotionTab and modified FeatureGateDetail have no frontend tests; the three pure filter constructors also have zero Go test references. Add focused frontend tests for loading, tabs, sorting, links, warnings, and regressions. Add unit tests for all filter constructors and database helper behavior.
Single Responsibility And Clear Naming ⚠️ Warning The promotion package is cohesive, but exported CapabilityTestRegessions is misspelled in both API layers; PromotionStatus and TestResult also each have 8 top-level fields. Rename CapabilityTestRegessions to CapabilityTestRegressions everywhere, and group promotion diagnostics or test statistics into focused sub-types where practical.
✅ Passed checks (16 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: porting feature gate promotion logic from o/api to Sippy.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Sql Injection Prevention ✅ Passed New promotion and capability queries bind release, featureGate, capability, dates, and exclusions with placeholders; formatting only builds bound values, not SQL text.
Feature Documentation ✅ Passed The current tree documents feature-gate endpoints and promotion data in pkg/api/README.md; no feature-gate doc exists in docs/features, but updates there are encouraged rather than required.
Stable And Deterministic Test Names ✅ Passed PR-changed files contain no Ginkgo imports or It/Describe/Context/When titles; added Go test names and seed test-name literals are static and deterministic.
Test Structure And Quality ✅ Passed The feature-gate tests use Go testing/testify, not Ginkgo. They make bounded 30-second HTTP requests, create no cluster resources, and use no Eventually/Consistently waits.
Microshift Test Compatibility ✅ Passed Changed e2e files use Go testing.T and testify, not Ginkgo; they query Sippy feature-gate APIs and reference no unavailable MicroShift OpenShift APIs or resources.
Single Node Openshift (Sno) Test Compatibility ✅ Passed The added e2e tests use Go testing.T and Sippy API requests only. They contain no Ginkgo declarations or multi-node/HA cluster assumptions.
Topology-Aware Scheduling Compatibility ✅ Passed The pull request changes API, promotion logic, UI, tests, and seed data; no deployment manifests, operators, controllers, or Kubernetes scheduling constraints were added or modified.
Ote Binary Stdout Contract ✅ Passed The PR adds no OTE binary, main/suite setup, or stdout write. Its only Fprintf match targets an HTTP ResponseWriter, not process stdout.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The added tests use standard testing.T, not Ginkgo, and call only the cluster-local Sippy API; no IPv4 literals or public hosts appear, while URL construction uses net.JoinHostPort.
No-Weak-Crypto ✅ Passed The merge-base diff adds no MD5, SHA1, DES, RC4, Blowfish, ECB, custom crypto, or secret/token comparisons; existing MD5 cache code is unchanged.
Container-Privileges ✅ Passed The PR changes no container or Kubernetes manifests and add no privileged, host namespace, SYS_ADMIN, root-user, or allowPrivilegeEscalation settings.
No-Sensitive-Data-In-Logs ✅ Passed The only new production log records release, feature_gate, and test_count; no passwords, tokens, API keys, PII, session IDs, hostnames, or customer data are logged.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
sippy-ng/src/tests/FeatureGateDetail.jsx (1)

42-67: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Ignore a stale response when featureGate changes.

The effect has no cancellation. If the user switches gates quickly, the first response can resolve after the second and setGate writes the older gate into state. The header then shows the new gate name while the body shows the previous gate's data. Track the current request and discard outdated results.

🛡️ Proposed fix
   useEffect(() => {
+    const controller = new AbortController()
     document.title = `Sippy > ${release} > Feature Gates > ${featureGate}`
     setLoaded(false)
     setFetchError('')
 
     fetch(
       import.meta.env.VITE_API_URL +
         `/api/feature_gates/${encodeURIComponent(
           featureGate
-        )}?release=${encodeURIComponent(release)}`
+        )}?release=${encodeURIComponent(release)}`,
+      { signal: controller.signal }
     )
       .then((response) => {
         if (response.status !== 200) {
           throw new Error('server returned ' + response.status)
         }
         return response.json()
       })
       .then((json) => {
         setGate(json)
         setLoaded(true)
       })
       .catch((error) => {
+        if (error.name === 'AbortError') {
+          return
+        }
         setFetchError('Could not retrieve feature gate: ' + error)
         setLoaded(true)
       })
+    return () => controller.abort()
   }, [release, featureGate])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sippy-ng/src/tests/FeatureGateDetail.jsx` around lines 42 - 67, Update the
useEffect request flow to track whether its fetch is still current, and ignore
both successful and failed results from an earlier featureGate or release after
dependencies change. Ensure cleanup invalidates the prior request before the
next effect runs, preventing stale setGate, setFetchError, and setLoaded
updates.
🧹 Nitpick comments (13)
pkg/api/featuregatepromotion/types.go (2)

47-55: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document the JobTiers string format.

JobTiers holds a comma-separated tier list, as ValidateJobTiers and the tests show. Add a short comment so callers do not pass a single tier only.

♻️ Proposed comment
 type JobVariant struct {
 	Cloud        string
 	Architecture string
 	Topology     string
 	NetworkStack string
 	OS           string
-	JobTiers     string
+	// JobTiers is a comma-separated list of job tiers to query, e.g. "standard,candidate".
+	JobTiers     string
 	Optional     bool
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/api/featuregatepromotion/types.go` around lines 47 - 55, Add a concise Go
doc comment to the JobVariant.JobTiers field documenting that it contains a
comma-separated list of tiers, consistent with ValidateJobTiers and its tests.

9-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Misspelled field CapabilityTestRegessions in both promotion structs and the converter. The name misses an r in "Regressions". The declaration was copied into the mirrored API type and into the conversion code, so the typo now appears in three places. The JSON tags are spelled correctly, so renaming the Go field does not change the response body.

  • pkg/api/featuregatepromotion/types.go#L9-L9: rename the PromotionStatus field to CapabilityTestRegressions.
  • pkg/apis/api/types.go#L1045-L1045: rename the FeatureGatePromotion field to CapabilityTestRegressions.
  • pkg/sippyserver/server.go#L821-L822: update the range expression and the append target in convertPromotionStatus to the new field name.

Also update the readers in test/e2e/feature_gate_promotion_test.go, which access fg.Promotion.CapabilityTestRegessions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/api/featuregatepromotion/types.go` at line 9, Rename
CapabilityTestRegessions to CapabilityTestRegressions in PromotionStatus and
FeatureGatePromotion, update the range expression and append target in
convertPromotionStatus, and update all readers in
test/e2e/feature_gate_promotion_test.go; leave the existing JSON tag unchanged.
Affected sites: pkg/api/featuregatepromotion/types.go:9,
pkg/apis/api/types.go:1045, and pkg/sippyserver/server.go:821-822, with
corresponding test-reader updates in test/e2e/feature_gate_promotion_test.go.
pkg/sippyserver/server.go (1)

799-802: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the install-gate check into the promotion package.

strings.Contains(fg.FeatureGate, "Install") repeats the same rule that GetPromotionStatus applies in pkg/api/featuregatepromotion/promotion.go (line 79). The two sites must stay in agreement, because the install_tests link must be present exactly when the promotion query includes install tests. Export one predicate, for example featuregatepromotion.IsInstallGate(featureGate string) bool, and call it from both places.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/sippyserver/server.go` around lines 799 - 802, The install-gate detection
rule is duplicated between the server link-building logic and
GetPromotionStatus. Add and export a shared predicate such as IsInstallGate in
the featuregatepromotion package, update GetPromotionStatus and the
install_tests block in the server to use it, and remove the direct
strings.Contains check so both behaviors remain synchronized.
pkg/api/featuregatepromotion/promotion_test.go (1)

316-344: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace the hand-rolled map[string]bool sets with sets.

Both tests build membership checks manually. The file already imports k8s.io/apimachinery/pkg/util/sets. Use it for the tier lookups.

♻️ Proposed refactor
 func TestDefaultJobTiersIncludeCandidate(t *testing.T) {
 	tiers := JobTiersForVariant(JobVariant{Cloud: "vsphere", Architecture: "amd64", Topology: "ha"})
-	tierSet := make(map[string]bool)
-	for _, tier := range tiers {
-		tierSet[tier] = true
-	}
+	tierSet := sets.New(tiers...)
 	expected := []string{"standard", "informing", "blocking", "candidate"}
 	for _, tier := range expected {
-		if !tierSet[tier] {
+		if !tierSet.Has(tier) {
 			t.Errorf("default tiers missing %q, got: %v", tier, tiers)
 		}
 	}
 }
 
 func TestAllRequiredVariantsQueryCandidateTier(t *testing.T) {
 	allVariants := append(append([]JobVariant{}, RequiredSelfManagedJobVariants...), RequiredHypershiftJobVariants...)
 	for _, variant := range allVariants {
 		tiers := JobTiersForVariant(variant)
-		hasCandidateTier := false
-		for _, tier := range tiers {
-			if tier == "candidate" {
-				hasCandidateTier = true
-				break
-			}
-		}
-		if !hasCandidateTier {
+		if !sets.New(tiers...).Has("candidate") {
 			t.Errorf("variant %+v does not query candidate tier", variant)
 		}
 	}
 }

As per coding guidelines: "Use k8s.io/apimachinery/pkg/util/sets for deduplicating or collecting unique strings; do not use map[string]bool as a hand-rolled set."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/api/featuregatepromotion/promotion_test.go` around lines 316 - 344,
Replace the hand-rolled tier membership checks in the affected tests, including
TestAllRequiredVariantsQueryCandidateTier, with
k8s.io/apimachinery/pkg/util/sets. Build a string set from each tiers result and
use the set’s membership API for candidate and expected-tier checks, removing
the manual map and boolean loops.

Source: Coding guidelines

pkg/apis/api/types.go (1)

1042-1078: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Consider removing the duplicated promotion structs.

FeatureGatePromotion, FeatureGateVariantResult, FeatureGateTestResult, and FeatureGateCapabilityTestRegression mirror PromotionStatus, VariantResult, TestResult, and CapabilityTestRegression in pkg/api/featuregatepromotion/types.go field for field, including the JSON tags. The duplication forces the manual convertPromotionStatus mapping in pkg/sippyserver/server.go (lines 812-852). Every future field addition must be applied in three places.

If the intent is to keep pkg/apis/api free of a dependency on pkg/api/featuregatepromotion, keep the duplication and state that reason in a comment. Otherwise, embed or alias the promotion types and delete the converter.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/apis/api/types.go` around lines 1042 - 1078, The promotion response
structs duplicate the canonical types and require manual conversion. Replace
FeatureGatePromotion, FeatureGateVariantResult, FeatureGateTestResult, and
FeatureGateCapabilityTestRegression with aliases or embedded canonical promotion
types from featuregatepromotion, then remove the corresponding
convertPromotionStatus mapping; if the package dependency must remain avoided,
retain the structs and document that constraint.
pkg/db/query/feature_gates.go (1)

100-112: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Use Pluck for this single-column query. Scan(&names) supports this result, but Pluck("name", &names) states the intended scalar-slice mapping and matches existing usage. Keep sort.Strings(names) because database collation can differ from Go sorting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/db/query/feature_gates.go` around lines 100 - 112, Update
GetMatchingJobsForCapability to use Pluck("name", &names) instead of
Scan(&names) for the single-column name query. Preserve the existing error
handling and sort.Strings(names) call.
pkg/api/featuregatepromotion/promotion.go (3)

178-184: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Explain the date-window arithmetic.

The code adds one day to today, subtracts eight days for the start, calls ResolveDateRanges, then subtracts one day from both resolved bounds. The net window is not obvious, and a later reader cannot tell which off-by-one is intentional. Add a short comment that states why the range is shifted before and after resolution.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/api/featuregatepromotion/promotion.go` around lines 178 - 184, Add a
concise explanatory comment adjacent to the tomorrow/start/end calculations
around ResolveDateRanges, documenting the intentional pre-resolution one-day
shift and post-resolution subtraction, including the resulting date-window
semantics and off-by-one handling.

63-99: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Pass ctx into the database calls.

GetPromotionStatus receives ctx and forwards it to sippyapi.QueryTestResults, but getGateTopologies, getPromotedGateNames, and getCapabilityRegressions run gorm queries without it. If the client cancels the HTTP request, those queries continue. These are local gorm calls in this file, so no shared query-layer signature changes are needed.

♻️ Proposed change
-func getGateTopologies(dbc *db.DB, release, featureGate string) (sets.Set[string], error) {
+func getGateTopologies(ctx context.Context, dbc *db.DB, release, featureGate string) (sets.Set[string], error) {
 	var topologies []string
-	tx := dbc.DB.Table("feature_gates").
+	tx := dbc.DB.WithContext(ctx).Table("feature_gates").

Apply the same change to getPromotedGateNames and getCapabilityRegressions, and update the call sites.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/api/featuregatepromotion/promotion.go` around lines 63 - 99, Thread ctx
through the local database helpers used by GetPromotionStatus: update
getGateTopologies, getPromotedGateNames, and getCapabilityRegressions to accept
the request context and execute their gorm queries with it, then pass ctx from
GetPromotionStatus and between the helper calls. Do not change shared
query-layer signatures.

304-341: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Centralize the default job tiers.

The default tier list is duplicated in JobTiersForVariant; extract it into one package-level value and reuse it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/api/featuregatepromotion/promotion.go` around lines 304 - 341, Define one
package-level value containing the default job tiers, then update
JobTiersForVariant to reuse it for both empty and whitespace-only JobTiers
inputs instead of duplicating the literal list. Keep the existing ordering and
returned behavior unchanged.
sippy-ng/src/tests/FeatureGatePromotionTab.jsx (2)

145-149: 🗄️ Data Integrity & Integration | 🔵 Trivial | 🏗️ Heavy lift

The thresholds duplicate the Go constants.

requiredTests, requiredRuns, and requiredPassRate restate RequiredNumberOfTests, RequiredNumberOfTestRunsPerVariant, and RequiredPassRateOfTestsPerVariant from pkg/api/featuregatepromotion/promotion.go lines 23-25. A change on the Go side leaves this component reporting the old numbers while data.sufficient reflects the new ones. The API already returns errors and warnings built from the real constants. Either render data.errors directly, or add the thresholds to the promotion payload and read them here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sippy-ng/src/tests/FeatureGatePromotionTab.jsx` around lines 145 - 149,
Remove the hardcoded thresholds object from the FeatureGatePromotionTab logic
and use the promotion API’s authoritative validation output instead, preferably
rendering data.errors and data.warnings directly. Ensure sufficient-state
messaging stays consistent with the Go constants RequiredNumberOfTests,
RequiredNumberOfTestRunsPerVariant, and RequiredPassRateOfTestsPerVariant.

22-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the theme palette instead of hardcoded pass and fail colors.

These four hex values are applied as cell backgrounds and text colors at lines 573-574, 689-690, and 726-727. They do not adapt to the dark theme, so the light green and light red backgrounds keep dark text on a dark surface and the contrast drops. sippy-ng/src/tests/TestTable.jsx reads theme.palette.success.dark and theme.palette.error.dark for the same purpose. Use useTheme() and the palette here for consistency with the Material-UI standards followed elsewhere.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sippy-ng/src/tests/FeatureGatePromotionTab.jsx` around lines 22 - 25, Replace
the hardcoded PASS_COLOR, PASS_TEXT, FAIL_COLOR, and FAIL_TEXT constants in
FeatureGatePromotionTab with values derived from Material-UI’s useTheme()
palette, matching TestTable’s use of theme.palette.success.dark and
theme.palette.error.dark for the corresponding cell backgrounds and text colors.
Update the affected component flow so these theme-derived colors are used at all
existing pass/fail cell styling sites.

Source: Coding guidelines

sippy-ng/src/tests/FeatureGateDetail.jsx (1)

259-277: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the "Analyze All" path builder into a shared helper.

sippy-ng/src/tests/FeatureGatePromotionTab.jsx lines 167-180 build the same /jobs/{release}/analysis?filters=... URL from matchingJobs with identical item shape and linkOperator: 'or'. Move the builder into one module and import it in both components, so a change to the filter shape stays consistent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sippy-ng/src/tests/FeatureGateDetail.jsx` around lines 259 - 277, Extract the
duplicated `/jobs/{release}/analysis?filters=...` URL construction into a shared
helper, preserving the existing matching-job filter item shape and
`linkOperator: 'or'`. Update the “Analyze All” button in `FeatureGateDetail` and
the corresponding flow in `FeatureGatePromotionTab` to import and use this
helper.
pkg/api/featuregatepromotion/filters.go (1)

47-48: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the 92% threshold into a named constant.

The value 92 also appears in pkg/api/featuregatepromotion/promotion.go (the raw SQL HAVING clause and the error message) and in the frontend filter. Define one exported constant in this package and reference it from the SQL and the message, so the filter and the query cannot drift.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/api/featuregatepromotion/filters.go` around lines 47 - 48, Define one
exported package-level constant for the 92% promotion threshold, then replace
the hardcoded value in the filter configuration, the raw SQL HAVING clause, and
the related error message with that constant, using appropriate formatting where
interpolation is required. Keep the existing threshold behavior unchanged and
ensure the frontend filter references the same shared value if it is generated
from this package.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/api/featuregatepromotion/filters.go`:
- Line 31: Update the variants filter to use filter.OperatorHasEntry instead of
filter.OperatorContains, matching the exact capability array element and
aligning with CapabilityRegressionsFilter.

In `@pkg/api/featuregatepromotion/promotion.go`:
- Around line 425-436: Update the aggregation around the loop building each
TestResult so the lookback window is chosen once per test name rather than per
row. Accumulate current and previous run, success, failure, and flake counts
separately for all rows, then select the 7-day totals when aggregate current
runs meet RequiredNumberOfTestRunsPerVariant; otherwise use the combined
current-plus-previous totals. Ensure the resulting TestResult totals and
subsequent threshold check use only the selected window.
- Around line 211-218: Update the raw promotion regression query near the
existing never-stable exclusion to also exclude jobs whose variant combinations
contain the aggregated variant, matching CapabilityRegressionsFilter in
filters.go. Preserve the existing never-stable filtering and ensure the query,
failingCount calculation, and gate_job_tests results use the same exclusion
criteria.
- Around line 42-51: Update every JobVariant entry in
OptionalSelfManagedPlatformVariants to set Optional: true explicitly, preserving
all existing cloud, architecture, topology, network stack, and job tier values.

In `@pkg/api/README.md`:
- Around line 598-603: Update featuregatepromotion.getCapabilityRegressions and
its promotion evaluation path to reuse CapabilityRegressionsFilter, including
the aggregated exclusion, so gate_job_tests evaluates the same canonical data as
its HATEOAS link; then retain the README’s “always stay in sync” statement.

In `@pkg/api/tests.go`:
- Around line 356-359: Update the documentation comment for QueryTestResults to
explicitly state that it queries PostgreSQL only through
buildTestsResultsFromPostgres and does not support BigQuery-backed results, so
callers understand its data-source limitation.

In `@sippy-ng/src/tests/FeatureGateDetail.jsx`:
- Around line 119-123: Update the hand-written filters in FeatureGateDetail: at
sippy-ng/src/tests/FeatureGateDetail.jsx lines 119-123, change the current_runs
value from '0' to '1'; at lines 69-77, add the not: true / has entry conditions
for never-stable and aggregated and set linkOperator to 'and', matching the
canonical GateTestFilter definitions.

In `@sippy-ng/src/tests/FeatureGatePromotionTab.jsx`:
- Around line 156-165: Update the minPassRate calculation in the
requiredVariants scan to consider a test result’s pass_percent only when its
total_runs is greater than zero, matching buildVariantResult behavior. Apply the
same condition in the corresponding scan around the secondary occurrence, while
preserving minRuns calculation and the existing zero fallback when no eligible
results exist.

In `@test/e2e/feature_gate_promotion_test.go`:
- Around line 28-32: Ensure the promotion test validates that
fg.Promotion.ResultsByVariant is non-empty before iterating over it. Add
require.NotEmpty immediately before the existing loop, preserving the
per-variant dimension assertions.

In `@test/e2e/feature_gates_test.go`:
- Around line 104-108: Update TestFeatureGateDetailNotFound to assert that the
SippyGet error contains “code 404” instead of only checking that an error
occurred, ensuring the missing feature gate returns the expected HTTP status.

---

Outside diff comments:
In `@sippy-ng/src/tests/FeatureGateDetail.jsx`:
- Around line 42-67: Update the useEffect request flow to track whether its
fetch is still current, and ignore both successful and failed results from an
earlier featureGate or release after dependencies change. Ensure cleanup
invalidates the prior request before the next effect runs, preventing stale
setGate, setFetchError, and setLoaded updates.

---

Nitpick comments:
In `@pkg/api/featuregatepromotion/filters.go`:
- Around line 47-48: Define one exported package-level constant for the 92%
promotion threshold, then replace the hardcoded value in the filter
configuration, the raw SQL HAVING clause, and the related error message with
that constant, using appropriate formatting where interpolation is required.
Keep the existing threshold behavior unchanged and ensure the frontend filter
references the same shared value if it is generated from this package.

In `@pkg/api/featuregatepromotion/promotion_test.go`:
- Around line 316-344: Replace the hand-rolled tier membership checks in the
affected tests, including TestAllRequiredVariantsQueryCandidateTier, with
k8s.io/apimachinery/pkg/util/sets. Build a string set from each tiers result and
use the set’s membership API for candidate and expected-tier checks, removing
the manual map and boolean loops.

In `@pkg/api/featuregatepromotion/promotion.go`:
- Around line 178-184: Add a concise explanatory comment adjacent to the
tomorrow/start/end calculations around ResolveDateRanges, documenting the
intentional pre-resolution one-day shift and post-resolution subtraction,
including the resulting date-window semantics and off-by-one handling.
- Around line 63-99: Thread ctx through the local database helpers used by
GetPromotionStatus: update getGateTopologies, getPromotedGateNames, and
getCapabilityRegressions to accept the request context and execute their gorm
queries with it, then pass ctx from GetPromotionStatus and between the helper
calls. Do not change shared query-layer signatures.
- Around line 304-341: Define one package-level value containing the default job
tiers, then update JobTiersForVariant to reuse it for both empty and
whitespace-only JobTiers inputs instead of duplicating the literal list. Keep
the existing ordering and returned behavior unchanged.

In `@pkg/api/featuregatepromotion/types.go`:
- Around line 47-55: Add a concise Go doc comment to the JobVariant.JobTiers
field documenting that it contains a comma-separated list of tiers, consistent
with ValidateJobTiers and its tests.
- Line 9: Rename CapabilityTestRegessions to CapabilityTestRegressions in
PromotionStatus and FeatureGatePromotion, update the range expression and append
target in convertPromotionStatus, and update all readers in
test/e2e/feature_gate_promotion_test.go; leave the existing JSON tag unchanged.
Affected sites: pkg/api/featuregatepromotion/types.go:9,
pkg/apis/api/types.go:1045, and pkg/sippyserver/server.go:821-822, with
corresponding test-reader updates in test/e2e/feature_gate_promotion_test.go.

In `@pkg/apis/api/types.go`:
- Around line 1042-1078: The promotion response structs duplicate the canonical
types and require manual conversion. Replace FeatureGatePromotion,
FeatureGateVariantResult, FeatureGateTestResult, and
FeatureGateCapabilityTestRegression with aliases or embedded canonical promotion
types from featuregatepromotion, then remove the corresponding
convertPromotionStatus mapping; if the package dependency must remain avoided,
retain the structs and document that constraint.

In `@pkg/db/query/feature_gates.go`:
- Around line 100-112: Update GetMatchingJobsForCapability to use Pluck("name",
&names) instead of Scan(&names) for the single-column name query. Preserve the
existing error handling and sort.Strings(names) call.

In `@pkg/sippyserver/server.go`:
- Around line 799-802: The install-gate detection rule is duplicated between the
server link-building logic and GetPromotionStatus. Add and export a shared
predicate such as IsInstallGate in the featuregatepromotion package, update
GetPromotionStatus and the install_tests block in the server to use it, and
remove the direct strings.Contains check so both behaviors remain synchronized.

In `@sippy-ng/src/tests/FeatureGateDetail.jsx`:
- Around line 259-277: Extract the duplicated
`/jobs/{release}/analysis?filters=...` URL construction into a shared helper,
preserving the existing matching-job filter item shape and `linkOperator: 'or'`.
Update the “Analyze All” button in `FeatureGateDetail` and the corresponding
flow in `FeatureGatePromotionTab` to import and use this helper.

In `@sippy-ng/src/tests/FeatureGatePromotionTab.jsx`:
- Around line 145-149: Remove the hardcoded thresholds object from the
FeatureGatePromotionTab logic and use the promotion API’s authoritative
validation output instead, preferably rendering data.errors and data.warnings
directly. Ensure sufficient-state messaging stays consistent with the Go
constants RequiredNumberOfTests, RequiredNumberOfTestRunsPerVariant, and
RequiredPassRateOfTestsPerVariant.
- Around line 22-25: Replace the hardcoded PASS_COLOR, PASS_TEXT, FAIL_COLOR,
and FAIL_TEXT constants in FeatureGatePromotionTab with values derived from
Material-UI’s useTheme() palette, matching TestTable’s use of
theme.palette.success.dark and theme.palette.error.dark for the corresponding
cell backgrounds and text colors. Update the affected component flow so these
theme-derived colors are used at all existing pass/fail cell styling sites.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 08d3f457-3ac5-4eb4-b081-009ccf4c507d

📥 Commits

Reviewing files that changed from the base of the PR and between 5f29773 and 245c542.

📒 Files selected for processing (15)
  • cmd/sippy/seed_data.go
  • pkg/api/README.md
  • pkg/api/featuregatepromotion/filters.go
  • pkg/api/featuregatepromotion/promotion.go
  • pkg/api/featuregatepromotion/promotion_test.go
  • pkg/api/featuregatepromotion/types.go
  • pkg/api/tests.go
  • pkg/apis/api/types.go
  • pkg/db/query/feature_gates.go
  • pkg/sippyserver/server.go
  • pkg/util/param/param.go
  • sippy-ng/src/tests/FeatureGateDetail.jsx
  • sippy-ng/src/tests/FeatureGatePromotionTab.jsx
  • test/e2e/feature_gate_promotion_test.go
  • test/e2e/feature_gates_test.go

Comment thread pkg/api/featuregatepromotion/filters.go Outdated
Comment thread pkg/api/featuregatepromotion/promotion.go
Comment thread pkg/api/featuregatepromotion/promotion.go
Comment on lines +425 to +436
// Apply lookback: use 7-day window if sufficient, else extend to 14 days
if test.CurrentRuns >= RequiredNumberOfTestRunsPerVariant {
tr.TotalRuns += test.CurrentRuns
tr.SuccessfulRuns += test.CurrentSuccesses
tr.FailedRuns += test.CurrentFailures
tr.FlakedRuns += test.CurrentFlakes
} else {
tr.TotalRuns += test.CurrentRuns + test.PreviousRuns
tr.SuccessfulRuns += test.CurrentSuccesses + test.PreviousSuccesses
tr.FailedRuns += test.CurrentFailures + test.PreviousFailures
tr.FlakedRuns += test.CurrentFlakes + test.PreviousFlakes
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

The lookback decision is made per row, so one test can mix 7-day and 14-day windows.

allTests can contain several rows for the same test name inside one variant combo, for example one row per JobTier or per suite. The loop evaluates test.CurrentRuns >= RequiredNumberOfTestRunsPerVariant for each row separately and accumulates into the same TestResult. A test with one high-volume row and one low-volume row then gets TotalRuns built from a 7-day window for the first row and a 14-day window for the second. The resulting denominator and pass percentage do not correspond to any single window, and the RequiredNumberOfTestRunsPerVariant check at line 465 can pass or fail for the wrong reason.

Decide the window once per test name. Accumulate current and previous counts separately in the loop, then choose which totals to use after the loop.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/api/featuregatepromotion/promotion.go` around lines 425 - 436, Update the
aggregation around the loop building each TestResult so the lookback window is
chosen once per test name rather than per row. Accumulate current and previous
run, success, failure, and flake counts separately for all rows, then select the
7-day totals when aggregate current runs meet
RequiredNumberOfTestRunsPerVariant; otherwise use the combined
current-plus-previous totals. Ensure the resulting TestResult totals and
subsequent threshold check use only the selected window.

Comment thread pkg/api/README.md
Comment thread pkg/api/tests.go
Comment on lines +356 to +359
// QueryTestResults queries test results using the same logic as the /api/tests
// endpoint but without requiring an HTTP request. This allows internal callers
// (such as promotion readiness) to reuse the exact same query path and filters.
func QueryTestResults(ctx context.Context, dbc *db.DB, cacheClient cache.Cache, release string, f *filter.Filter) ([]apitype.Test, error) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

State that this helper queries PostgreSQL only.

/api/tests can also serve BigQuery results; the frontend selects the source through the testTableDBSource cookie in sippy-ng/src/tests/TestTable.jsx. This helper always calls buildTestsResultsFromPostgres, so promotion evaluation can differ from what a BigQuery-backed table shows for the same filter. Name the data source in the comment so callers know the guarantee.

📝 Proposed change
-// QueryTestResults queries test results using the same logic as the /api/tests
-// endpoint but without requiring an HTTP request. This allows internal callers
-// (such as promotion readiness) to reuse the exact same query path and filters.
+// QueryTestResults queries test results using the same PostgreSQL logic as the
+// /api/tests endpoint but without requiring an HTTP request. This allows internal
+// callers (such as promotion readiness) to reuse the same query path and filters.
+// The BigQuery path (/api/tests/v2) is not covered.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// QueryTestResults queries test results using the same logic as the /api/tests
// endpoint but without requiring an HTTP request. This allows internal callers
// (such as promotion readiness) to reuse the exact same query path and filters.
func QueryTestResults(ctx context.Context, dbc *db.DB, cacheClient cache.Cache, release string, f *filter.Filter) ([]apitype.Test, error) {
// QueryTestResults queries test results using the same PostgreSQL logic as the
// /api/tests endpoint but without requiring an HTTP request. This allows internal
// callers (such as promotion readiness) to reuse the same query path and filters.
// The BigQuery path (/api/tests/v2) is not covered.
func QueryTestResults(ctx context.Context, dbc *db.DB, cacheClient cache.Cache, release string, f *filter.Filter) ([]apitype.Test, error) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/api/tests.go` around lines 356 - 359, Update the documentation comment
for QueryTestResults to explicitly state that it queries PostgreSQL only through
buildTestsResultsFromPostgres and does not support BigQuery-backed results, so
callers understand its data-source limitation.

Comment on lines +119 to +123
{
columnField: 'current_runs',
operatorValue: '>=',
value: '0',
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Hand-written filters in the detail view drift from the canonical Go filters. The component re-declares filter definitions that pkg/api/featuregatepromotion/filters.go owns and that the API already publishes through gate.links. Each copy has drifted, so the tables show a different row set than the promotion analysis evaluated. Prefer deriving the tab filters from the returned gate.links payload; until then, correct each copy.

  • sippy-ng/src/tests/FeatureGateDetail.jsx#L119-L123: change the current_runs filter value from '0' to '1' to match CapabilityRegressionsFilter.
  • sippy-ng/src/tests/FeatureGateDetail.jsx#L69-L77: add the two not: true / has entry items for never-stable and aggregated, and set linkOperator: 'and', to match GateTestFilter.
📍 Affects 1 file
  • sippy-ng/src/tests/FeatureGateDetail.jsx#L119-L123 (this comment)
  • sippy-ng/src/tests/FeatureGateDetail.jsx#L69-L77
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sippy-ng/src/tests/FeatureGateDetail.jsx` around lines 119 - 123, Update the
hand-written filters in FeatureGateDetail: at
sippy-ng/src/tests/FeatureGateDetail.jsx lines 119-123, change the current_runs
value from '0' to '1'; at lines 69-77, add the not: true / has entry conditions
for never-stable and aggregated and set linkOperator to 'and', matching the
canonical GateTestFilter definitions.

Comment on lines +156 to +165
let minRuns = Infinity
let minPassRate = Infinity
for (const v of requiredVariants) {
for (const tr of v.test_results || []) {
if (tr.total_runs < minRuns) minRuns = tr.total_runs
if (tr.pass_percent < minPassRate) minPassRate = tr.pass_percent
}
}
if (!isFinite(minRuns)) minRuns = 0
if (!isFinite(minPassRate)) minPassRate = 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

The pass-rate bullet fires for tests that have no runs.

The scan at lines 158-163 includes every test result of the required variants, including results where total_runs is 0 and pass_percent is 0. minPassRate then becomes 0 and the bullet reports "minimum 0%". The backend does not agree: buildVariantResult in pkg/api/featuregatepromotion/promotion.go line 471 raises a pass-rate error only when tr.TotalRuns > 0. A gate that only lacks run volume therefore shows a pass-rate failure it does not have. Apply the same total_runs > 0 condition here.

🐛 Proposed fix
   for (const v of requiredVariants) {
     for (const tr of v.test_results || []) {
       if (tr.total_runs < minRuns) minRuns = tr.total_runs
-      if (tr.pass_percent < minPassRate) minPassRate = tr.pass_percent
+      if (tr.total_runs > 0 && tr.pass_percent < minPassRate) {
+        minPassRate = tr.pass_percent
+      }
     }
   }

Also applies to: 215-221

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sippy-ng/src/tests/FeatureGatePromotionTab.jsx` around lines 156 - 165,
Update the minPassRate calculation in the requiredVariants scan to consider a
test result’s pass_percent only when its total_runs is greater than zero,
matching buildVariantResult behavior. Apply the same condition in the
corresponding scan around the secondary occurrence, while preserving minRuns
calculation and the existing zero fallback when no eligible results exist.

Comment thread test/e2e/feature_gate_promotion_test.go
Comment on lines +104 to +108
func TestFeatureGateDetailNotFound(t *testing.T) {
var fg api.FeatureGate
err := util.SippyGet("/api/feature_gates/NonExistentGate12345?release="+util.Release, &fg)
assert.Error(t, err, "should return error for non-existent gate")
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Find an e2e helper that exposes the HTTP status code.
set -euo pipefail

fd -t f . test/e2e/util | xargs -r ast-grep outline --items all
rg -nP --type=go -C4 'func SippyGet|StatusCode' test/e2e/util

Repository: openshift/sippy

Length of output: 4442


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- test/e2e/util/e2erequest.go ---'
sed -n '1,135p' test/e2e/util/e2erequest.go

printf '%s\n' '--- test/e2e/feature_gates_test.go ---'
sed -n '80,125p' test/e2e/feature_gates_test.go

printf '%s\n' '--- server handler and related tests ---'
rg -n -C8 'FeatureGateDetailNotFound|NonExistentGate|StatusNotFound|feature_gates' pkg/sippyserver test -g '*.go' | head -240

Repository: openshift/sippy

Length of output: 20025


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- feature-gate detail handler ---'
sed -n '700,782p' pkg/sippyserver/server.go

printf '%s\n' '--- status assertions and request-helper usage ---'
rg -n -C3 'sippy API request failed with code|SippyGetAbsolute|http\.StatusNotFound|EqualError|ErrorContains' test pkg -g '*.go' | head -260

Repository: openshift/sippy

Length of output: 10218


Assert the expected status code for the missing gate.

SippyGet includes the response status in its error. Replace assert.Error with assert.ErrorContains(t, err, "code 404") to reject other HTTP, transport, or decode errors.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/feature_gates_test.go` around lines 104 - 108, Update
TestFeatureGateDetailNotFound to assert that the SippyGet error contains “code
404” instead of only checking that an error occurred, ensuring the missing
feature gate returns the expected HTTP status.

dgoodwin and others added 6 commits August 4, 2026 15:39
…results

Remove constant-value parameters from makeTest helper to satisfy unparam
linter. Add platform-specific FilterVariants test cases for AWS, GCP, and
Azure gates. Initialize TestResults to empty slice in convertPromotionStatus
so the API returns [] instead of null for variants with no test data. Remove
unused HIDDEN_VARIANT_KEYS and featureGate prop from PromotionCell.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Without this flag, optional platform variants (nutanix, openstack,
two-node) were treated as required, causing their errors to block
promotion instead of being downgraded to warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The raw SQL query for capability regressions was not excluding
aggregated variant jobs, diverging from the canonical
CapabilityRegressionsFilter which excludes both never-stable
and aggregated variants.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
OperatorContains does substring matching, so Capability:Foo would
incorrectly match Capability:FooBar. Use OperatorHasEntry for exact
array entry matching, consistent with CapabilityRegressionsFilter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests with total_runs=0 have pass_percent=0, which would cause
the promotion summary to show a misleading "minimum 0%" pass rate.
Match the backend behavior which only reports pass-rate errors when
total_runs > 0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Without this guard, the test would silently pass if ResultsByVariant
were empty, since the loop body would never execute.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Scheduling required tests:
/test e2e

}

var rows []queryResult
tx := dbc.DB.Raw(`

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My least favourite part of this PR is this forked query. It was too slow using the normal test functions and filters. This is true if you follow the gate_job_tests hateoas link as well.

)
AND t.name NOT LIKE '%' || ? || '%'
AND t.name NOT LIKE '%' || ? || '%'
AND t.name NOT LIKE '%' || ? || '%'

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I grilled claude:

⏺ The three lines look identical in the SQL but each ? binds to a different value from excludedNames:

excludedNames := []string{"install should succeed", "openshift-tests should work", "infrastructure should work"}

So it's three distinct filters, one per excluded test name. The SQL just looks repetitive because the pattern t.name NOT LIKE '%' || ? || '%' is the same shape for each. It could be cleaner as a single NOT EXISTS with an unnest or a
NOT (t.name LIKE ANY(...)), but functionally it's correct as-is.

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Scheduling required tests:
/test e2e

@openshift-ci

openshift-ci Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

@dgoodwin: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant