From 9a05906299a992adfeb87092daf19bd6ecdf73f6 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Thu, 16 Oct 2025 15:49:37 +0300 Subject: [PATCH] add operations test --- pkg/services/apiserver/builder/openapi.go | 59 +++++++------- .../apiserver/builder/openapi_test.go | 76 +++++++++++++++++++ 2 files changed, 102 insertions(+), 33 deletions(-) create mode 100644 pkg/services/apiserver/builder/openapi_test.go diff --git a/pkg/services/apiserver/builder/openapi.go b/pkg/services/apiserver/builder/openapi.go index 803b48361e0..0b6509ebef7 100644 --- a/pkg/services/apiserver/builder/openapi.go +++ b/pkg/services/apiserver/builder/openapi.go @@ -3,7 +3,6 @@ package builder import ( "bytes" "encoding/json" - "iter" "maps" "net/http" "strings" @@ -236,37 +235,31 @@ func getOpenAPIPostProcessor(version string, builders []APIGroupBuilder, gvs []s } } -func GetPathOperations(path *spec3.Path) iter.Seq2[string, *spec3.Operation] { - return func(yield func(string, *spec3.Operation) bool) { - if path.Get != nil && !yield(http.MethodGet, path.Get) { - return - } - if path.Get != nil && !yield(http.MethodGet, path.Get) { - return - } - if path.Head != nil && !yield(http.MethodHead, path.Head) { - return - } - if path.Delete != nil && !yield(http.MethodDelete, path.Delete) { - return - } - if path.Patch != nil && !yield(http.MethodPatch, path.Patch) { - return - } - if path.Post != nil && !yield(http.MethodPost, path.Post) { - return - } - if path.Post != nil && !yield(http.MethodPost, path.Post) { - return - } - if path.Put != nil && !yield(http.MethodPut, path.Put) { - return - } - if path.Trace != nil && !yield(http.MethodTrace, path.Trace) { - return - } - if path.Options != nil && !yield(http.MethodOptions, path.Options) { - return - } +func GetPathOperations(path *spec3.Path) map[string]*spec3.Operation { + ops := make(map[string]*spec3.Operation) + if path.Get != nil { + ops[http.MethodGet] = path.Get } + if path.Head != nil { + ops[http.MethodHead] = path.Head + } + if path.Delete != nil { + ops[http.MethodDelete] = path.Delete + } + if path.Post != nil { + ops[http.MethodPost] = path.Post + } + if path.Put != nil { + ops[http.MethodPut] = path.Put + } + if path.Patch != nil { + ops[http.MethodPatch] = path.Patch + } + if path.Trace != nil { + ops[http.MethodTrace] = path.Trace + } + if path.Options != nil { + ops[http.MethodOptions] = path.Options + } + return ops } diff --git a/pkg/services/apiserver/builder/openapi_test.go b/pkg/services/apiserver/builder/openapi_test.go new file mode 100644 index 00000000000..746ae949e9f --- /dev/null +++ b/pkg/services/apiserver/builder/openapi_test.go @@ -0,0 +1,76 @@ +package builder + +import ( + "slices" + "strings" + "testing" + + "github.com/stretchr/testify/require" + "k8s.io/kube-openapi/pkg/spec3" +) + +func TestOpenAPI_GetPathOperations(t *testing.T) { + testCases := []struct { + name string + input *spec3.Path + expect []string // the methods we should see + exclude []string // the methods we should never see + }{ + { + name: "some operations", + input: &spec3.Path{ + PathProps: spec3.PathProps{ + Get: &spec3.Operation{OperationProps: spec3.OperationProps{Summary: "get"}}, + Post: &spec3.Operation{OperationProps: spec3.OperationProps{Summary: "post"}}, + Delete: &spec3.Operation{OperationProps: spec3.OperationProps{Summary: "delete"}}, + }, + }, + expect: []string{"GET", "POST", "DELETE"}, + exclude: []string{"PUT", "PATCH", "OPTIONS", "HEAD", "TRACE"}, + }, + { + name: "all operations", + input: &spec3.Path{ + PathProps: spec3.PathProps{ + Get: &spec3.Operation{OperationProps: spec3.OperationProps{Summary: "get"}}, + Post: &spec3.Operation{OperationProps: spec3.OperationProps{Summary: "post"}}, + Delete: &spec3.Operation{OperationProps: spec3.OperationProps{Summary: "delete"}}, + Put: &spec3.Operation{OperationProps: spec3.OperationProps{Summary: "put"}}, + Patch: &spec3.Operation{OperationProps: spec3.OperationProps{Summary: "patch"}}, + Options: &spec3.Operation{OperationProps: spec3.OperationProps{Summary: "options"}}, + Head: &spec3.Operation{OperationProps: spec3.OperationProps{Summary: "head"}}, + Trace: &spec3.Operation{OperationProps: spec3.OperationProps{Summary: "trace"}}, + }, + }, + expect: []string{"GET", "POST", "DELETE", "PUT", "PATCH", "OPTIONS", "HEAD", "TRACE"}, + exclude: []string{}, + }, + } + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + expect := make(map[string]bool) + for _, k := range tt.expect { + expect[k] = true + } + + for k, op := range GetPathOperations(tt.input) { + require.NotNil(t, op) + require.Equal(t, strings.ToLower(k), op.Summary) + + if !expect[k] { + if slices.Contains(tt.expect, k) { + require.Fail(t, "method returned multiple times", k) + } else { + require.Fail(t, "unexpected method", k) + } + } + delete(expect, k) + require.NotContains(t, tt.exclude, k, "exclude") + } + + if len(expect) > 0 { + require.Fail(t, "missing expected method", expect) + } + }) + } +}