Skip to content

Commit 77b5a6d

Browse files
committed
HYPERFLEET-1182 - test: add nodepool integration tests for placement
Add integration tests backfilling coverage before E2E removal: TestNodePoolPatchSoftDeleted, TestNodePoolConcurrentDelete, TestNodePoolGetSoftDeleted, TestNodePoolListIncludesSoftDeleted.
1 parent 719f22d commit 77b5a6d

1 file changed

Lines changed: 163 additions & 0 deletions

File tree

test/integration/node_pools_test.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"fmt"
66
"net/http"
7+
"sync"
78
"testing"
89

910
. "github.com/onsi/gomega"
@@ -1081,3 +1082,165 @@ func TestNodePoolForceDelete(t *testing.T) {
10811082
})
10821083
}
10831084
}
1085+
1086+
func TestNodePoolPatchSoftDeleted(t *testing.T) {
1087+
h, client := test.RegisterIntegration(t)
1088+
1089+
account := h.NewRandAccount()
1090+
ctx := h.NewAuthenticatedContext(account)
1091+
1092+
cluster, err := h.Factories.NewClusters(h.NewID())
1093+
Expect(err).NotTo(HaveOccurred())
1094+
1095+
npResp, err := client.CreateNodePoolWithResponse(
1096+
ctx, cluster.ID, openapi.CreateNodePoolJSONRequestBody(newNodePoolInput("patch-del-np")), test.WithAuthToken(ctx),
1097+
)
1098+
Expect(err).NotTo(HaveOccurred())
1099+
Expect(npResp.StatusCode()).To(Equal(http.StatusCreated))
1100+
nodePoolID := *npResp.JSON201.Id
1101+
1102+
delResp, err := client.DeleteNodePoolByIdWithResponse(ctx, cluster.ID, nodePoolID, test.WithAuthToken(ctx))
1103+
Expect(err).NotTo(HaveOccurred())
1104+
Expect(delResp.StatusCode()).To(Equal(http.StatusAccepted))
1105+
1106+
patchBody := openapi.PatchNodePoolByIdJSONRequestBody{
1107+
Spec: &openapi.NodePoolSpec{"should": "fail"},
1108+
}
1109+
patchResp, err := client.PatchNodePoolByIdWithResponse(ctx, cluster.ID, nodePoolID, patchBody, test.WithAuthToken(ctx))
1110+
Expect(err).NotTo(HaveOccurred())
1111+
Expect(patchResp.StatusCode()).To(Equal(http.StatusConflict),
1112+
"PATCH on soft-deleted nodepool should return 409")
1113+
}
1114+
1115+
func TestNodePoolConcurrentDelete(t *testing.T) {
1116+
h, client := test.RegisterIntegration(t)
1117+
1118+
account := h.NewRandAccount()
1119+
ctx := h.NewAuthenticatedContext(account)
1120+
1121+
cluster, err := h.Factories.NewClusters(h.NewID())
1122+
Expect(err).NotTo(HaveOccurred())
1123+
1124+
input := newNodePoolInput("concurr-del-np")
1125+
npResp, err := client.CreateNodePoolWithResponse(
1126+
ctx, cluster.ID, openapi.CreateNodePoolJSONRequestBody(input), test.WithAuthToken(ctx),
1127+
)
1128+
Expect(err).NotTo(HaveOccurred())
1129+
Expect(npResp.StatusCode()).To(Equal(http.StatusCreated))
1130+
nodePoolID := *npResp.JSON201.Id
1131+
initialGeneration := npResp.JSON201.Generation
1132+
1133+
const concurrency = 5
1134+
type deleteResult struct {
1135+
resp *openapi.DeleteNodePoolByIdResponse
1136+
err error
1137+
}
1138+
results := make([]deleteResult, concurrency)
1139+
1140+
var wg sync.WaitGroup
1141+
wg.Add(concurrency)
1142+
for i := range concurrency {
1143+
go func(idx int) {
1144+
defer wg.Done()
1145+
r, e := client.DeleteNodePoolByIdWithResponse(ctx, cluster.ID, nodePoolID, test.WithAuthToken(ctx))
1146+
results[idx] = deleteResult{resp: r, err: e}
1147+
}(i)
1148+
}
1149+
wg.Wait()
1150+
1151+
for i, r := range results {
1152+
Expect(r.err).NotTo(HaveOccurred(), "DELETE request %d should not return error", i)
1153+
Expect(r.resp.StatusCode()).To(Equal(http.StatusAccepted), "DELETE request %d should return 202", i)
1154+
Expect(r.resp.JSON202.DeletedTime).NotTo(BeNil(), "DELETE request %d should have deleted_time", i)
1155+
}
1156+
1157+
referenceTime := *results[0].resp.JSON202.DeletedTime
1158+
referenceGen := results[0].resp.JSON202.Generation
1159+
for i := 1; i < concurrency; i++ {
1160+
Expect(*results[i].resp.JSON202.DeletedTime).To(Equal(referenceTime),
1161+
"all DELETE responses should carry identical deleted_time")
1162+
Expect(results[i].resp.JSON202.Generation).To(Equal(referenceGen),
1163+
"all DELETE responses should carry identical generation")
1164+
}
1165+
1166+
Expect(referenceGen).To(Equal(initialGeneration+1),
1167+
"generation should increment by exactly 1, not by the number of concurrent requests")
1168+
}
1169+
1170+
func TestNodePoolGetSoftDeleted(t *testing.T) {
1171+
h, client := test.RegisterIntegration(t)
1172+
1173+
account := h.NewRandAccount()
1174+
ctx := h.NewAuthenticatedContext(account)
1175+
1176+
cluster, err := h.Factories.NewClusters(h.NewID())
1177+
Expect(err).NotTo(HaveOccurred())
1178+
1179+
npResp, err := client.CreateNodePoolWithResponse(
1180+
ctx, cluster.ID, openapi.CreateNodePoolJSONRequestBody(newNodePoolInput("get-deleted-np")), test.WithAuthToken(ctx),
1181+
)
1182+
Expect(err).NotTo(HaveOccurred())
1183+
Expect(npResp.StatusCode()).To(Equal(http.StatusCreated))
1184+
nodePoolID := *npResp.JSON201.Id
1185+
1186+
delResp, err := client.DeleteNodePoolByIdWithResponse(ctx, cluster.ID, nodePoolID, test.WithAuthToken(ctx))
1187+
Expect(err).NotTo(HaveOccurred())
1188+
Expect(delResp.StatusCode()).To(Equal(http.StatusAccepted))
1189+
1190+
getResp, err := client.GetNodePoolByIdWithResponse(ctx, cluster.ID, nodePoolID, test.WithAuthToken(ctx))
1191+
Expect(err).NotTo(HaveOccurred())
1192+
Expect(getResp.StatusCode()).To(Equal(http.StatusOK),
1193+
"GET on soft-deleted nodepool should return 200, not 404")
1194+
Expect(getResp.JSON200.DeletedTime).NotTo(BeNil(),
1195+
"soft-deleted nodepool should have deleted_time in GET response")
1196+
Expect(*getResp.JSON200.Id).To(Equal(nodePoolID))
1197+
}
1198+
1199+
func TestNodePoolListIncludesSoftDeleted(t *testing.T) {
1200+
h, client := test.RegisterIntegration(t)
1201+
1202+
account := h.NewRandAccount()
1203+
ctx := h.NewAuthenticatedContext(account)
1204+
1205+
cluster, err := h.Factories.NewClusters(h.NewID())
1206+
Expect(err).NotTo(HaveOccurred())
1207+
1208+
activeResp, err := client.CreateNodePoolWithResponse(
1209+
ctx, cluster.ID, openapi.CreateNodePoolJSONRequestBody(newNodePoolInput("active-np")), test.WithAuthToken(ctx),
1210+
)
1211+
Expect(err).NotTo(HaveOccurred())
1212+
Expect(activeResp.StatusCode()).To(Equal(http.StatusCreated))
1213+
activeID := *activeResp.JSON201.Id
1214+
1215+
deletedResp, err := client.CreateNodePoolWithResponse(
1216+
ctx, cluster.ID, openapi.CreateNodePoolJSONRequestBody(newNodePoolInput("deleted-np")), test.WithAuthToken(ctx),
1217+
)
1218+
Expect(err).NotTo(HaveOccurred())
1219+
Expect(deletedResp.StatusCode()).To(Equal(http.StatusCreated))
1220+
deletedID := *deletedResp.JSON201.Id
1221+
1222+
delResp, err := client.DeleteNodePoolByIdWithResponse(ctx, cluster.ID, deletedID, test.WithAuthToken(ctx))
1223+
Expect(err).NotTo(HaveOccurred())
1224+
Expect(delResp.StatusCode()).To(Equal(http.StatusAccepted))
1225+
1226+
listResp, err := client.GetNodePoolsByClusterIdWithResponse(ctx, cluster.ID, nil, test.WithAuthToken(ctx))
1227+
Expect(err).NotTo(HaveOccurred())
1228+
Expect(listResp.StatusCode()).To(Equal(http.StatusOK))
1229+
1230+
var foundActive, foundDeleted bool
1231+
for _, item := range listResp.JSON200.Items {
1232+
if item.Id == nil {
1233+
continue
1234+
}
1235+
if *item.Id == activeID {
1236+
Expect(item.DeletedTime).To(BeNil(), "active nodepool should not have deleted_time")
1237+
foundActive = true
1238+
}
1239+
if *item.Id == deletedID {
1240+
Expect(item.DeletedTime).NotTo(BeNil(), "soft-deleted nodepool should have deleted_time")
1241+
foundDeleted = true
1242+
}
1243+
}
1244+
Expect(foundActive).To(BeTrue(), "active nodepool should appear in LIST")
1245+
Expect(foundDeleted).To(BeTrue(), "soft-deleted nodepool should appear in LIST")
1246+
}

0 commit comments

Comments
 (0)