Skip to content
Closed
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
57 changes: 57 additions & 0 deletions e2e/adapter/conformance_negative.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package adapter

import (
"context"

"github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" //nolint:staticcheck // dot import for test readability

"github.com/openshift-hyperfleet/hyperfleet-e2e/pkg/api/openapi"
"github.com/openshift-hyperfleet/hyperfleet-e2e/pkg/client"
"github.com/openshift-hyperfleet/hyperfleet-e2e/pkg/helper"
"github.com/openshift-hyperfleet/hyperfleet-e2e/pkg/labels"
)

const conditionTypeReady = "Ready"

var _ = ginkgo.Describe("[Suite: adapter][negative] Legacy adapter behavior rejected by v1 API contract",
ginkgo.Label(labels.Tier0, labels.Negative),
func() {
ginkgo.It("should not have a resource-level Ready condition on a reconciled cluster",
func(ctx context.Context) {
h := helper.New()

ginkgo.By("creating a cluster")
cluster, err := h.Client.CreateClusterFromPayload(ctx, h.TestDataPath("payloads/clusters/cluster-request.json"))
Expect(err).NotTo(HaveOccurred())
Expect(cluster.Id).NotTo(BeNil())
clusterID := *cluster.Id

ginkgo.DeferCleanup(func(ctx context.Context) {
if err := h.CleanupTestCluster(ctx, clusterID); err != nil {
ginkgo.GinkgoWriter.Printf("Warning: failed to cleanup cluster %s: %v\n", clusterID, err)
}
})

ginkgo.By("waiting for Reconciled=True")
Eventually(h.PollCluster(ctx, clusterID), h.Cfg.Timeouts.Cluster.Reconciled, h.Cfg.Polling.Interval).
Should(helper.HaveResourceCondition(client.ConditionTypeReconciled, openapi.ResourceConditionStatusTrue))

ginkgo.By("verifying resource-level Ready condition does not exist")
reconciledCluster, err := h.Client.GetCluster(ctx, clusterID)
Expect(err).NotTo(HaveOccurred())
Expect(reconciledCluster.Status).NotTo(BeNil())

for _, c := range reconciledCluster.Status.Conditions {
Expect(c.Type).NotTo(Equal(conditionTypeReady),
"v1.0.0 removed the Ready condition; no Ready condition should exist regardless of status")
}

ginkgo.By("confirming Reconciled and LastKnownReconciled are the correct v1.0.0 conditions")
Expect(h.HasResourceCondition(reconciledCluster.Status.Conditions,
client.ConditionTypeReconciled, openapi.ResourceConditionStatusTrue)).To(BeTrue())
Expect(h.HasResourceCondition(reconciledCluster.Status.Conditions,
client.ConditionTypeLastKnownReconciled, openapi.ResourceConditionStatusTrue)).To(BeTrue())
})
},
)
156 changes: 156 additions & 0 deletions test-design/testcases/adapter-conformance-negative.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Feature: Adapter Contract Conformance (Negative)

## Table of Contents

1. [POST to statuses endpoint returns 405](#test-title-post-to-statuses-endpoint-returns-405)
2. [PUT with missing mandatory conditions returns 400](#test-title-put-with-missing-mandatory-conditions-returns-400)
3. [Ready condition absent on reconciled cluster](#test-title-ready-condition-absent-on-reconciled-cluster)

---

## Test Title: POST to statuses endpoint returns 405

### Description

Validates that the v1.0.0 API rejects the old v0.2.0 POST method for adapter status reporting. Adapters that have not migrated to PUT will receive a 405 Method Not Allowed response instead of silently succeeding.

---

| **Field** | **Value** |
|-----------|-----------|
| **Pos/Neg** | Negative |
| **Priority** | Tier0 |
| **Status** | Draft |
| **Automation** | Automated |
| **Version** | v1.0.0 |
| **Created** | 2026-06-23 |
| **Updated** | 2026-06-23 |

---

### Preconditions

1. HyperFleet API is deployed with v1.0.0 adapter contract (PUT-only statuses endpoint)
2. HyperFleet Sentinel is deployed and running

---

### Test Steps

#### Step 1: Create a cluster

**Action:**
- Create a cluster via the API using a standard cluster payload

**Expected Result:**
- API returns the created cluster with an ID and generation

#### Step 2: Send a POST status report

**Action:**
- Send a POST request to `/clusters/{id}/statuses` with a valid `AdapterStatusCreateRequest` body containing all three mandatory conditions (Applied, Available, Health)

**Expected Result:**
- API returns HTTP 405 Method Not Allowed
- The POST method was removed in v1.0.0; only PUT is accepted

---

## Test Title: PUT with missing mandatory conditions returns 400

### Description

Validates that the v1.0.0 API rejects adapter status reports that omit the three mandatory conditions (Available, Applied, Health). Sends a PUT with only a `Ready` condition (which was removed in v1.0.0) and no mandatory conditions.

---

| **Field** | **Value** |
|-----------|-----------|
| **Pos/Neg** | Negative |
| **Priority** | Tier0 |
| **Status** | Draft |
| **Automation** | Automated |
| **Version** | v1.0.0 |
| **Created** | 2026-06-23 |
| **Updated** | 2026-06-23 |

---

### Preconditions

1. HyperFleet API is deployed with v1.0.0 adapter contract
2. HyperFleet Sentinel is deployed and running

---

### Test Steps

#### Step 1: Create a cluster

**Action:**
- Create a cluster via the API using a standard cluster payload

**Expected Result:**
- API returns the created cluster with an ID and generation

#### Step 2: Send a PUT with only a Ready condition

**Action:**
- Send a PUT request to `/clusters/{id}/statuses` with an `AdapterStatusCreateRequest` containing only `{Type: "Ready", Status: "True"}`, omitting the mandatory Available, Applied, and Health conditions

**Expected Result:**
- API returns HTTP 400 Bad Request
- The three mandatory conditions (Available, Applied, Health) must be present in every adapter status report

---

## Test Title: Ready condition absent on reconciled cluster

### Description

Validates that the v1.0.0 API does not produce a resource-level `Ready` condition on a fully reconciled cluster. Adapters that poll for `Ready=True` to determine readiness will hang forever. The correct v1.0.0 conditions are `Reconciled` and `LastKnownReconciled`.

---

| **Field** | **Value** |
|-----------|-----------|
| **Pos/Neg** | Negative |
| **Priority** | Tier0 |
| **Status** | Draft |
| **Automation** | Automated |
| **Version** | v1.0.0 |
| **Created** | 2026-06-23 |
| **Updated** | 2026-06-23 |

---

### Preconditions

1. HyperFleet API is deployed with v1.0.0 adapter contract
2. HyperFleet Sentinel is deployed and running
3. At least one adapter is configured and running to drive reconciliation

---

### Test Steps

#### Step 1: Create a cluster and wait for reconciliation

**Action:**
- Create a cluster via the API
- Poll until `Reconciled=True`

**Expected Result:**
- Cluster reaches `Reconciled` condition with `status: "True"`

#### Step 2: Verify Ready condition is absent

**Action:**
- Fetch the reconciled cluster and inspect `status.conditions`

**Expected Result:**
- No condition with `type: "Ready"` exists in the resource-level conditions
- `Reconciled` condition is present with `status: "True"`
- `LastKnownReconciled` condition is present with `status: "True"`

---