@@ -11,10 +11,13 @@ import (
1111 "time"
1212
1313 apierrors "k8s.io/apimachinery/pkg/api/errors"
14+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1415 "k8s.io/apimachinery/pkg/runtime/schema"
1516 "k8s.io/apimachinery/pkg/runtime/serializer"
17+ "k8s.io/apimachinery/pkg/util/managedfields"
1618 genericoptions "k8s.io/apiserver/pkg/server/options"
1719 openapiutil "k8s.io/kube-openapi/pkg/util"
20+ "k8s.io/kube-openapi/pkg/validation/spec"
1821
1922 aggregationv1alpha1 "github.com/coder/coder-k8s/api/aggregation/v1alpha1"
2023 coderhelper "github.com/coder/coder-k8s/internal/aggregated/coder"
@@ -76,6 +79,109 @@ func TestOpenAPIDefinitionsIncludeTemplateFiles(t *testing.T) {
7679 }
7780}
7881
82+ func TestOpenAPIDefinitionsIncludeTemplateGVKExtensionAndObjectMetadata (t * testing.T ) {
83+ t .Helper ()
84+
85+ defs := getOpenAPIDefinitions (nil )
86+ templateDefinitionName := openapiutil .GetCanonicalTypeName (& aggregationv1alpha1.CoderTemplate {})
87+
88+ def , ok := defs [templateDefinitionName ]
89+ if ! ok {
90+ t .Fatalf ("expected OpenAPI definition for %s" , templateDefinitionName )
91+ }
92+
93+ for _ , propertyName := range []string {"apiVersion" , "kind" , "metadata" , "spec" , "status" } {
94+ if _ , ok := def .Schema .Properties [propertyName ]; ! ok {
95+ t .Fatalf ("expected template schema to include %q" , propertyName )
96+ }
97+ }
98+
99+ gvk := readGVKExtension (t , def .Schema )
100+ if got , want := gvk ["group" ], aggregationv1alpha1 .SchemeGroupVersion .Group ; got != want {
101+ t .Fatalf ("expected template GVK group %q, got %v" , want , got )
102+ }
103+ if got , want := gvk ["version" ], aggregationv1alpha1 .SchemeGroupVersion .Version ; got != want {
104+ t .Fatalf ("expected template GVK version %q, got %v" , want , got )
105+ }
106+ if got , want := gvk ["kind" ], "CoderTemplate" ; got != want {
107+ t .Fatalf ("expected template GVK kind %q, got %v" , want , got )
108+ }
109+ }
110+
111+ func TestOpenAPIDefinitionsSupportManagedFieldsTypeConversionForTemplate (t * testing.T ) {
112+ t .Helper ()
113+
114+ defs := getOpenAPIDefinitions (nil )
115+ openAPISpec := make (map [string ]* spec.Schema , len (defs ))
116+ for definitionName , definition := range defs {
117+ definitionSchema := definition .Schema
118+ openAPISpec [definitionName ] = & definitionSchema
119+ }
120+
121+ typeConverter , err := managedfields .NewTypeConverter (openAPISpec , false )
122+ if err != nil {
123+ t .Fatalf ("build managed fields type converter from OpenAPI definitions: %v" , err )
124+ }
125+ if typeConverter == nil {
126+ t .Fatal ("expected managed fields type converter to be non-nil" )
127+ }
128+
129+ template := & aggregationv1alpha1.CoderTemplate {
130+ TypeMeta : metav1.TypeMeta {
131+ APIVersion : aggregationv1alpha1 .SchemeGroupVersion .String (),
132+ Kind : "CoderTemplate" ,
133+ },
134+ ObjectMeta : metav1.ObjectMeta {
135+ Name : "default.my-template" ,
136+ Namespace : "test-ns" ,
137+ },
138+ Spec : aggregationv1alpha1.CoderTemplateSpec {
139+ Organization : "default" ,
140+ VersionID : "version-id" ,
141+ },
142+ }
143+
144+ if _ , err := typeConverter .ObjectToTyped (template ); err != nil {
145+ t .Fatalf ("convert template object to structured-merge typed value: %v" , err )
146+ }
147+ }
148+
149+ func readGVKExtension (t * testing.T , schema spec.Schema ) map [string ]interface {} {
150+ t .Helper ()
151+
152+ extension , ok := schema .Extensions ["x-kubernetes-group-version-kind" ]
153+ if ! ok {
154+ t .Fatal ("expected x-kubernetes-group-version-kind OpenAPI extension" )
155+ }
156+
157+ gvkList , ok := extension .([]interface {})
158+ if ! ok {
159+ t .Fatalf ("expected GVK extension to be []interface{}, got %T" , extension )
160+ }
161+ if len (gvkList ) != 1 {
162+ t .Fatalf ("expected exactly one GVK entry, got %d" , len (gvkList ))
163+ }
164+
165+ switch gvk := gvkList [0 ].(type ) {
166+ case map [string ]interface {}:
167+ return gvk
168+ case map [interface {}]interface {}:
169+ normalized := make (map [string ]interface {}, len (gvk ))
170+ for key , value := range gvk {
171+ keyString , ok := key .(string )
172+ if ! ok {
173+ t .Fatalf ("expected GVK extension map key to be string, got %T" , key )
174+ }
175+ normalized [keyString ] = value
176+ }
177+ return normalized
178+ default :
179+ t .Fatalf ("expected GVK entry to be map, got %T" , gvkList [0 ])
180+ }
181+
182+ return nil
183+ }
184+
79185func TestInstallAPIGroupRegistersDiscovery (t * testing.T ) {
80186 t .Helper ()
81187
@@ -196,6 +302,15 @@ func TestNewRecommendedConfigSetsExtendedRequestTimeout(t *testing.T) {
196302 if got , want := recommendedConfig .RequestTimeout , defaultRequestTimeout ; got != want {
197303 t .Fatalf ("expected request timeout %s, got %s" , want , got )
198304 }
305+ if ! recommendedConfig .SkipOpenAPIInstallation {
306+ t .Fatal ("expected OpenAPI handler installation to remain disabled until generic definitions are wired" )
307+ }
308+ if recommendedConfig .OpenAPIConfig == nil {
309+ t .Fatal ("expected non-nil OpenAPI v2 config" )
310+ }
311+ if recommendedConfig .OpenAPIV3Config == nil {
312+ t .Fatal ("expected non-nil OpenAPI v3 config" )
313+ }
199314}
200315
201316func TestBuildClientProviderDefersMissingCoderConfigAsServiceUnavailable (t * testing.T ) {
0 commit comments