package api import ( "net/http" "net/http/httptest" "strings" "sync" "testing" "github.com/gin-gonic/gin" ) func countLifecycleWinners(instanceID string, workers int) int { start := make(chan struct{}) results := make(chan bool, workers) var wg sync.WaitGroup for range workers { wg.Add(1) go func() { defer wg.Done() <-start results <- beginInstanceLifecycleOperation(instanceID) }() } close(start) wg.Wait() close(results) winners := 0 for won := range results { if won { winners++ } } return winners } func TestBeginInstanceLifecycleOperationSingleFlight(t *testing.T) { instanceID := "ocid1.instance.oc1..single-flight" instanceLifecycleGuard.Delete(instanceID) t.Cleanup(func() { instanceLifecycleGuard.Delete(instanceID) }) if got, want := countLifecycleWinners(instanceID, 16), 1; got != want { t.Errorf("winners = %d, want %d", got, want) } } func instanceOperationRouter() *gin.Engine { h := &ociConfigHandler{} r := gin.New() r.DELETE("/oci-configs/:id/instances/:instanceId", h.terminateInstance) r.POST("/oci-configs/:id/instances/:instanceId/action", h.instanceAction) return r } func TestInstanceLifecycleOperationRejectsLaterRequest(t *testing.T) { gin.SetMode(gin.TestMode) instanceID := "ocid1.instance.oc1..same-physical-instance" instanceLifecycleGuard.Store(instanceID, struct{}{}) t.Cleanup(func() { instanceLifecycleGuard.Delete(instanceID) }) r := instanceOperationRouter() tests := []struct { name, method, path, body string }{ {"action先占位时跨配置terminate被拒", http.MethodDelete, "/oci-configs/2/instances/" + instanceID, ""}, {"terminate先占位时action被拒", http.MethodPost, "/oci-configs/1/instances/" + instanceID + "/action", `{"action":"STOP"}`}, {"action先占位时第二个action被拒", http.MethodPost, "/oci-configs/3/instances/" + instanceID + "/action", `{"action":"RESET"}`}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req := httptest.NewRequest(tt.method, tt.path, strings.NewReader(tt.body)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() r.ServeHTTP(w, req) if got, want := w.Code, http.StatusConflict; got != want { t.Errorf("status = %d, want %d, body %s", got, want, w.Body.String()) } }) } }