@@ -553,3 +553,125 @@ func TestClusterNodePoolsHandler_Patch(t *testing.T) {
553553 })
554554 }
555555}
556+
557+ func TestClusterNodePoolsHandler_ForceDelete (t * testing.T ) {
558+ RegisterTestingT (t )
559+
560+ clusterID := testClusterID
561+ nodePoolID := testNodePoolID
562+
563+ tests := []struct {
564+ setupMocks func (ctrl * gomock.Controller ) (* services.MockClusterService , * services.MockNodePoolService )
565+ name string
566+ nodePoolID string
567+ body string
568+ expectedStatusCode int
569+ }{
570+ {
571+ name : "Success 204 - nodepool force-deleted" ,
572+ nodePoolID : nodePoolID ,
573+ body : `{"reason": "Stuck in finalizing for 2 hours"}` ,
574+ setupMocks : func (ctrl * gomock.Controller ) (* services.MockClusterService , * services.MockNodePoolService ) {
575+ mockClusterSvc := services .NewMockClusterService (ctrl )
576+ mockNodePoolSvc := services .NewMockNodePoolService (ctrl )
577+ mockNodePoolSvc .EXPECT ().
578+ GetByIDAndOwner (gomock .Any (), nodePoolID , clusterID ).
579+ Return (& api.NodePool {}, nil )
580+ mockNodePoolSvc .EXPECT ().
581+ ForceDelete (gomock .Any (), nodePoolID , "Stuck in finalizing for 2 hours" ).
582+ Return (nil )
583+ return mockClusterSvc , mockNodePoolSvc
584+ },
585+ expectedStatusCode : http .StatusNoContent ,
586+ },
587+ {
588+ name : "Error 404 - nodepool not found" ,
589+ nodePoolID : "non-existent-id" ,
590+ body : `{"reason": "some reason"}` ,
591+ setupMocks : func (ctrl * gomock.Controller ) (* services.MockClusterService , * services.MockNodePoolService ) {
592+ mockClusterSvc := services .NewMockClusterService (ctrl )
593+ mockNodePoolSvc := services .NewMockNodePoolService (ctrl )
594+ mockNodePoolSvc .EXPECT ().
595+ GetByIDAndOwner (gomock .Any (), "non-existent-id" , clusterID ).
596+ Return (nil , errors .NotFound ("NodePool with id='non-existent-id' not found" ))
597+ return mockClusterSvc , mockNodePoolSvc
598+ },
599+ expectedStatusCode : http .StatusNotFound ,
600+ },
601+ {
602+ name : "Error 409 - nodepool not in Finalizing state" ,
603+ nodePoolID : nodePoolID ,
604+ body : `{"reason": "some reason"}` ,
605+ setupMocks : func (ctrl * gomock.Controller ) (* services.MockClusterService , * services.MockNodePoolService ) {
606+ mockClusterSvc := services .NewMockClusterService (ctrl )
607+ mockNodePoolSvc := services .NewMockNodePoolService (ctrl )
608+ mockNodePoolSvc .EXPECT ().
609+ GetByIDAndOwner (gomock .Any (), nodePoolID , clusterID ).
610+ Return (& api.NodePool {}, nil )
611+ mockNodePoolSvc .EXPECT ().
612+ ForceDelete (gomock .Any (), nodePoolID , "some reason" ).
613+ Return (errors .ConflictState ("NodePool '%s' is not in Finalizing state" , nodePoolID ))
614+ return mockClusterSvc , mockNodePoolSvc
615+ },
616+ expectedStatusCode : http .StatusConflict ,
617+ },
618+ {
619+ name : "Error 400 - empty reason" ,
620+ nodePoolID : nodePoolID ,
621+ body : `{"reason": ""}` ,
622+ setupMocks : func (ctrl * gomock.Controller ) (* services.MockClusterService , * services.MockNodePoolService ) {
623+ mockClusterSvc := services .NewMockClusterService (ctrl )
624+ mockNodePoolSvc := services .NewMockNodePoolService (ctrl )
625+ return mockClusterSvc , mockNodePoolSvc
626+ },
627+ expectedStatusCode : http .StatusBadRequest ,
628+ },
629+ {
630+ name : "Error 400 - malformed JSON" ,
631+ nodePoolID : nodePoolID ,
632+ body : `not json` ,
633+ setupMocks : func (ctrl * gomock.Controller ) (* services.MockClusterService , * services.MockNodePoolService ) {
634+ mockClusterSvc := services .NewMockClusterService (ctrl )
635+ mockNodePoolSvc := services .NewMockNodePoolService (ctrl )
636+ return mockClusterSvc , mockNodePoolSvc
637+ },
638+ expectedStatusCode : http .StatusBadRequest ,
639+ },
640+ }
641+
642+ for _ , tt := range tests {
643+ t .Run (tt .name , func (t * testing.T ) {
644+ RegisterTestingT (t )
645+
646+ ctrl := gomock .NewController (t )
647+ defer ctrl .Finish ()
648+
649+ mockClusterSvc , mockNodePoolSvc := tt .setupMocks (ctrl )
650+ handler := NewClusterNodePoolsHandler (mockClusterSvc , mockNodePoolSvc )
651+
652+ reqURL := "/api/hyperfleet/v1/clusters/" + clusterID + "/nodepools/" + tt .nodePoolID + "/force-delete"
653+ req := httptest .NewRequest (http .MethodPost , reqURL , strings .NewReader (tt .body ))
654+ req .Header .Set ("Content-Type" , "application/json" )
655+ req = mux .SetURLVars (req , map [string ]string {
656+ "id" : clusterID ,
657+ "nodepool_id" : tt .nodePoolID ,
658+ })
659+
660+ rr := httptest .NewRecorder ()
661+ handler .ForceDelete (rr , req )
662+
663+ Expect (rr .Code ).To (Equal (tt .expectedStatusCode ))
664+
665+ if tt .expectedStatusCode == http .StatusNoContent {
666+ Expect (rr .Body .Len ()).To (Equal (0 ))
667+ }
668+
669+ if tt .expectedStatusCode == http .StatusConflict {
670+ var errResp openapi.Error
671+ err := json .Unmarshal (rr .Body .Bytes (), & errResp )
672+ Expect (err ).NotTo (HaveOccurred ())
673+ Expect (* errResp .Detail ).To (ContainSubstring ("not in Finalizing state" ))
674+ }
675+ })
676+ }
677+ }
0 commit comments