From b6d546e470a844b5242ba35a68553f9e4f043828 Mon Sep 17 00:00:00 2001 From: Dmitrii Andreev Date: Tue, 30 Jun 2026 13:49:50 -0500 Subject: [PATCH 1/3] HYPERFLEET-1153 - feat: add ValidateSpecSchemas to entity registry --- pkg/registry/registry.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pkg/registry/registry.go b/pkg/registry/registry.go index 95da534f..a423bc52 100644 --- a/pkg/registry/registry.go +++ b/pkg/registry/registry.go @@ -98,6 +98,21 @@ func Validate() { } } +// ValidateSpecSchemas checks that every descriptor with a non-empty SpecSchemaName +// resolves to an existing schema. Panics with a descriptive message if any +// SpecSchemaName does not resolve. The schemaExists callback decouples registry +// from the OpenAPI library. +func ValidateSpecSchemas(schemaExists func(string) bool) { + for _, d := range descriptors { + if d.SpecSchemaName != "" && !schemaExists(d.SpecSchemaName) { + panic(fmt.Sprintf( + "entity kind %q declares SpecSchemaName %q but it does not resolve to an existing component in the OpenAPI spec", + d.Kind, d.SpecSchemaName, + )) + } + } +} + // Reset clears all registrations. Only for use in tests. func Reset() { descriptors = make(map[string]EntityDescriptor) From e7a11e45709593aaaec8f1bebf2000c8ae4fdaa6 Mon Sep 17 00:00:00 2001 From: Dmitrii Andreev Date: Tue, 30 Jun 2026 13:53:47 -0500 Subject: [PATCH 2/3] HYPERFLEET-1153 - feat: wire startup schema validation and add channel/version tests --- cmd/hyperfleet-api/server/routes.go | 3 ++ pkg/validators/schema_validator.go | 24 ++++------ pkg/validators/schema_validator_test.go | 59 +++++++------------------ 3 files changed, 26 insertions(+), 60 deletions(-) diff --git a/cmd/hyperfleet-api/server/routes.go b/cmd/hyperfleet-api/server/routes.go index 7f961e5a..186fad1f 100755 --- a/cmd/hyperfleet-api/server/routes.go +++ b/cmd/hyperfleet-api/server/routes.go @@ -15,6 +15,7 @@ import ( "github.com/openshift-hyperfleet/hyperfleet-api/pkg/handlers" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/logger" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/middleware" + "github.com/openshift-hyperfleet/hyperfleet-api/pkg/registry" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/validators" ) @@ -107,6 +108,8 @@ func (s *apiServer) routes(tracingEnabled bool) *mux.Router { func registerAPIMiddleware(router *mux.Router) error { router.Use(MetricsMiddleware) + registry.Validate() + schemaPath := env().Config.Server.OpenAPISchemaPath ctx := context.Background() diff --git a/pkg/validators/schema_validator.go b/pkg/validators/schema_validator.go index f6402f1e..188061eb 100644 --- a/pkg/validators/schema_validator.go +++ b/pkg/validators/schema_validator.go @@ -7,7 +7,6 @@ import ( "github.com/getkin/kin-openapi/openapi3" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/errors" - "github.com/openshift-hyperfleet/hyperfleet-api/pkg/logger" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/registry" ) @@ -41,6 +40,10 @@ func NewSchemaValidator(schemaPath string) (*SchemaValidator, error) { return nil, fmt.Errorf("invalid OpenAPI schema: %w", validateErr) } + registry.ValidateSpecSchemas(func(name string) bool { + return doc.Components.Schemas[name] != nil + }) + schemas, err := buildSchemasMap(doc) if err != nil { return nil, err @@ -53,27 +56,16 @@ func NewSchemaValidator(schemaPath string) (*SchemaValidator, error) { } func buildSchemasMap(doc *openapi3.T) (map[string]*ResourceSchema, error) { - ctx := context.Background() schemas := make(map[string]*ResourceSchema) // registeredKinds := make(map[string]bool, len(requiredSpecValidationKinds)) for _, d := range registry.WithSpecSchema() { schemaRef := doc.Components.Schemas[d.SpecSchemaName] if schemaRef == nil { - // TODO : HYPERFLEET-1159 - Uncomment this once Cluster and NodePool are registered - // if isRequiredSpecValidationKind(d.Kind) { - // return nil, fmt.Errorf( - // "%s schema not found in OpenAPI spec (required for entity kind %q)", - // d.SpecSchemaName, d.Kind, - // ) - // } - - logger.With(ctx, - "schema_name", d.SpecSchemaName, - "kind", d.Kind, - "plural", d.Plural, - ).Warn("OpenAPI spec schema not found, skipping validation for entity") - continue + return nil, fmt.Errorf( + "entity kind %q declares SpecSchemaName %q but schema not found in OpenAPI spec", + d.Kind, d.SpecSchemaName, + ) } schemas[d.Plural] = &ResourceSchema{ diff --git a/pkg/validators/schema_validator_test.go b/pkg/validators/schema_validator_test.go index cd2708cc..085f1b14 100644 --- a/pkg/validators/schema_validator_test.go +++ b/pkg/validators/schema_validator_test.go @@ -1,15 +1,12 @@ package validators import ( - "bytes" - "log/slog" "os" "path/filepath" "testing" . "github.com/onsi/gomega" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/errors" - "github.com/openshift-hyperfleet/hyperfleet-api/pkg/logger" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/registry" ) @@ -149,10 +146,10 @@ components: err := os.WriteFile(schemaPath, []byte(invalidSchema), 0600) Expect(err).To(BeNil()) - // Should fail because ClusterSpec is missing - _, err = NewSchemaValidator(schemaPath) - Expect(err).ToNot(BeNil()) - Expect(err.Error()).To(ContainSubstring("ClusterSpec schema not found")) + // Should panic because registered entity's SpecSchemaName doesn't resolve + Expect(func() { + _, _ = NewSchemaValidator(schemaPath) + }).To(PanicWith(ContainSubstring("ClusterSpec"))) } // TODO : HYPERFLEET-1159 - Uncomment this once Cluster and NodePool are registered @@ -176,17 +173,9 @@ components: // Expect(err.Error()).To(ContainSubstring(`entity kind "NodePool" with SpecSchemaName must be registered`)) // } -func TestNewSchemaValidator_OptionalEntityMissingOpenAPISchema_SkipsWithWarning(t *testing.T) { +func TestNewSchemaValidator_RegisteredEntityMissingSchema_Panics(t *testing.T) { RegisterTestingT(t) - var logBuf bytes.Buffer - logger.ReconfigureGlobalLogger(&logger.LogConfig{ - Level: slog.LevelWarn, - Format: logger.FormatText, - Output: &logBuf, - Component: "validators-test", - }) - registerRequiredSpecValidationEntities() registry.Register(registry.EntityDescriptor{ Kind: "WifConfig", @@ -219,16 +208,9 @@ components: err := os.WriteFile(schemaPath, []byte(schemaWithoutWifConfig), 0600) Expect(err).To(BeNil()) - validator, err := NewSchemaValidator(schemaPath) - Expect(err).To(BeNil()) - Expect(validator.HasSchema("clusters")).To(BeTrue()) - Expect(validator.HasSchema("nodepools")).To(BeTrue()) - Expect(validator.HasSchema("wifconfigs")).To(BeFalse()) - - logOutput := logBuf.String() - Expect(logOutput).To(ContainSubstring("skipping validation for entity")) - Expect(logOutput).To(ContainSubstring("WifConfigSpec")) - Expect(logOutput).To(ContainSubstring("WifConfig")) + Expect(func() { + _, _ = NewSchemaValidator(schemaPath) + }).To(PanicWith(ContainSubstring("WifConfigSpec"))) } func TestNewSchemaValidator_RequiredEntityMissingOpenAPISchema_Fails(t *testing.T) { @@ -256,22 +238,14 @@ components: err := os.WriteFile(schemaPath, []byte(schemaWithoutNodePool), 0600) Expect(err).To(BeNil()) - _, err = NewSchemaValidator(schemaPath) - Expect(err).ToNot(BeNil()) - Expect(err.Error()).To(ContainSubstring("NodePoolSpec schema not found")) + Expect(func() { + _, _ = NewSchemaValidator(schemaPath) + }).To(PanicWith(ContainSubstring("NodePoolSpec"))) } -func TestValidate_SkipsWhenOptionalEntitySchemaNotLoaded(t *testing.T) { +func TestValidate_RegisteredEntityMissingSchema_PanicsAtConstruction(t *testing.T) { RegisterTestingT(t) - var logBuf bytes.Buffer - logger.ReconfigureGlobalLogger(&logger.LogConfig{ - Level: slog.LevelWarn, - Format: logger.FormatText, - Output: &logBuf, - Component: "validators-test", - }) - registerRequiredSpecValidationEntities() registry.Register(registry.EntityDescriptor{ Kind: "WifConfig", @@ -304,12 +278,9 @@ components: err := os.WriteFile(schemaPath, []byte(schemaWithoutWifConfig), 0600) Expect(err).To(BeNil()) - validator, err := NewSchemaValidator(schemaPath) - Expect(err).To(BeNil()) - - // Invalid spec would fail validation if WifConfigSpec were loaded. - err = validator.Validate("wifconfigs", map[string]interface{}{}) - Expect(err).To(BeNil()) + Expect(func() { + _, _ = NewSchemaValidator(schemaPath) + }).To(PanicWith(ContainSubstring("WifConfigSpec"))) } func TestValidate_WifConfigSpec_Valid(t *testing.T) { From 6ee6ca1b2b5f23c1b3c98bd13816602678652c18 Mon Sep 17 00:00:00 2001 From: Dmitrii Andreev Date: Tue, 30 Jun 2026 14:32:57 -0500 Subject: [PATCH 3/3] HYPERFLEET-1153 - feat: add RequireSpecSchema, restore warn+skip, address PR feedback --- pkg/registry/descriptor.go | 1 + pkg/registry/registry.go | 10 +-- pkg/registry/registry_test.go | 115 ++++++++++++++++++++++++ pkg/validators/schema_validator.go | 73 +++++---------- pkg/validators/schema_validator_test.go | 107 +++++++++++----------- 5 files changed, 200 insertions(+), 106 deletions(-) diff --git a/pkg/registry/descriptor.go b/pkg/registry/descriptor.go index 9f398b59..04c60d35 100644 --- a/pkg/registry/descriptor.go +++ b/pkg/registry/descriptor.go @@ -18,4 +18,5 @@ type EntityDescriptor struct { OnParentDelete OnParentDeletePolicy // only meaningful when ParentKind != "" SearchDisallowedFields []string // fields blocked from TSL search RequiredAdapters []string // adapters that must finalize before hard-delete + RequireSpecSchema bool // panic at startup if SpecSchemaName missing from spec } diff --git a/pkg/registry/registry.go b/pkg/registry/registry.go index a423bc52..e609fb15 100644 --- a/pkg/registry/registry.go +++ b/pkg/registry/registry.go @@ -98,13 +98,13 @@ func Validate() { } } -// ValidateSpecSchemas checks that every descriptor with a non-empty SpecSchemaName -// resolves to an existing schema. Panics with a descriptive message if any -// SpecSchemaName does not resolve. The schemaExists callback decouples registry -// from the OpenAPI library. +// ValidateSpecSchemas checks descriptors that set RequireSpecSchema and panics if +// their SpecSchemaName is absent from the OpenAPI spec. Entities without +// RequireSpecSchema are left to buildSchemasMap, which warns and skips them. +// See also Validate, which checks registry structural integrity. func ValidateSpecSchemas(schemaExists func(string) bool) { for _, d := range descriptors { - if d.SpecSchemaName != "" && !schemaExists(d.SpecSchemaName) { + if d.SpecSchemaName != "" && d.RequireSpecSchema && !schemaExists(d.SpecSchemaName) { panic(fmt.Sprintf( "entity kind %q declares SpecSchemaName %q but it does not resolve to an existing component in the OpenAPI spec", d.Kind, d.SpecSchemaName, diff --git a/pkg/registry/registry_test.go b/pkg/registry/registry_test.go index 05a7050c..feffbc34 100644 --- a/pkg/registry/registry_test.go +++ b/pkg/registry/registry_test.go @@ -184,6 +184,121 @@ func TestValidate_EmptyRegistry(t *testing.T) { }).ToNot(Panic()) } +func TestValidateSpecSchemas_PanicsOnMissingSchema(t *testing.T) { + RegisterTestingT(t) + Reset() + + Register(EntityDescriptor{ + Kind: "Channel", + Plural: "channels", + SpecSchemaName: "ChannelSpec", + RequireSpecSchema: true, + }) + + Expect(func() { + ValidateSpecSchemas(func(name string) bool { return false }) + }).To(PanicWith(ContainSubstring("ChannelSpec"))) +} + +func TestValidateSpecSchemas_PassesWhenAllSchemasResolve(t *testing.T) { + RegisterTestingT(t) + Reset() + + Register(EntityDescriptor{ + Kind: "Channel", + Plural: "channels", + SpecSchemaName: "ChannelSpec", + RequireSpecSchema: true, + }) + Register(EntityDescriptor{ + Kind: "Version", + Plural: "versions", + ParentKind: "Channel", + SpecSchemaName: "VersionSpec", + RequireSpecSchema: true, + }) + + Expect(func() { + ValidateSpecSchemas(func(name string) bool { return true }) + }).ToNot(Panic()) +} + +func TestValidateSpecSchemas_SkipsDescriptorsWithoutSpecSchema(t *testing.T) { + RegisterTestingT(t) + Reset() + + Register(EntityDescriptor{ + Kind: "Channel", + Plural: "channels", + }) + + called := false + ValidateSpecSchemas(func(name string) bool { + called = true + return false + }) + Expect(called).To(BeFalse()) +} + +func TestValidateSpecSchemas_EmptyRegistry(t *testing.T) { + RegisterTestingT(t) + Reset() + + Expect(func() { + ValidateSpecSchemas(func(name string) bool { + t.Fatal("callback should not be called on empty registry") + return false + }) + }).ToNot(Panic()) +} + +func TestValidateSpecSchemas_MixedDescriptors(t *testing.T) { + RegisterTestingT(t) + Reset() + + Register(EntityDescriptor{ + Kind: "Channel", + Plural: "channels", + }) + Register(EntityDescriptor{ + Kind: "Version", + Plural: "versions", + ParentKind: "Channel", + SpecSchemaName: "VersionSpec", + }) + Register(EntityDescriptor{ + Kind: "Cluster", + Plural: "clusters", + SpecSchemaName: "ClusterSpec", + RequireSpecSchema: true, + }) + + resolved := map[string]bool{"VersionSpec": true, "ClusterSpec": true} + Expect(func() { + ValidateSpecSchemas(func(name string) bool { return resolved[name] }) + }).ToNot(Panic()) + + resolved["ClusterSpec"] = false + Expect(func() { + ValidateSpecSchemas(func(name string) bool { return resolved[name] }) + }).To(PanicWith(ContainSubstring("ClusterSpec"))) +} + +func TestValidateSpecSchemas_SkipsNonRequiredMissingSchema(t *testing.T) { + RegisterTestingT(t) + Reset() + + Register(EntityDescriptor{ + Kind: "Channel", + Plural: "channels", + SpecSchemaName: "ChannelSpec", + }) + + Expect(func() { + ValidateSpecSchemas(func(name string) bool { return false }) + }).ToNot(Panic()) +} + func TestDescriptorFields(t *testing.T) { RegisterTestingT(t) Reset() diff --git a/pkg/validators/schema_validator.go b/pkg/validators/schema_validator.go index 188061eb..8ff4b39c 100644 --- a/pkg/validators/schema_validator.go +++ b/pkg/validators/schema_validator.go @@ -7,12 +7,10 @@ import ( "github.com/getkin/kin-openapi/openapi3" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/errors" + "github.com/openshift-hyperfleet/hyperfleet-api/pkg/logger" "github.com/openshift-hyperfleet/hyperfleet-api/pkg/registry" ) -// TODO : HYPERFLEET-1159 - Uncomment this once Cluster and NodePool are registered -// var requiredSpecValidationKinds = []string{"Cluster", "NodePool"} - // ResourceSchema represents a validation schema for a specific resource type type ResourceSchema struct { Schema *openapi3.SchemaRef @@ -26,9 +24,8 @@ type SchemaValidator struct { } // NewSchemaValidator creates a new schema validator by loading an OpenAPI spec from the given path. -// Cluster and NodePool must be registered with SpecSchemaName and have matching OpenAPI components. -// Other registered entities with SpecSchemaName are validated only when their component exists; -// missing components are skipped with a warning at startup. +// Panics if any registered entity with RequireSpecSchema has a SpecSchemaName that does not resolve. +// Entities without RequireSpecSchema whose schema is absent are skipped with a warning. func NewSchemaValidator(schemaPath string) (*SchemaValidator, error) { loader := openapi3.NewLoader() doc, err := loader.LoadFromFile(schemaPath) @@ -56,69 +53,43 @@ func NewSchemaValidator(schemaPath string) (*SchemaValidator, error) { } func buildSchemasMap(doc *openapi3.T) (map[string]*ResourceSchema, error) { + ctx := context.Background() schemas := make(map[string]*ResourceSchema) - // registeredKinds := make(map[string]bool, len(requiredSpecValidationKinds)) for _, d := range registry.WithSpecSchema() { schemaRef := doc.Components.Schemas[d.SpecSchemaName] if schemaRef == nil { - return nil, fmt.Errorf( - "entity kind %q declares SpecSchemaName %q but schema not found in OpenAPI spec", - d.Kind, d.SpecSchemaName, - ) + logger.With(ctx, + "schema_name", d.SpecSchemaName, + "kind", d.Kind, + "plural", d.Plural, + ).Warn("OpenAPI spec schema not found, skipping validation for entity") + continue } - schemas[d.Plural] = &ResourceSchema{ TypeName: d.SpecSchemaName, Schema: schemaRef, } - // registeredKinds[d.Kind] = true } // TODO : HYPERFLEET-1159 - Remove this once Cluster and NodePool are registered - // Extract ClusterSpec schema - clusterSpecSchema := doc.Components.Schemas["ClusterSpec"] - if clusterSpecSchema == nil { - return nil, fmt.Errorf("ClusterSpec schema not found in OpenAPI spec") - } - - // Extract NodePoolSpec schema - nodePoolSpecSchema := doc.Components.Schemas["NodePoolSpec"] - if nodePoolSpecSchema == nil { - return nil, fmt.Errorf("NodePoolSpec schema not found in OpenAPI spec") - } - - // Build schemas map - schemas["clusters"] = &ResourceSchema{ - TypeName: "ClusterSpec", - Schema: clusterSpecSchema, - } - schemas["nodepools"] = &ResourceSchema{ - TypeName: "NodePoolSpec", - Schema: nodePoolSpecSchema, + for _, hc := range []struct{ kind, plural, schema string }{ + {"Cluster", "clusters", "ClusterSpec"}, + {"NodePool", "nodepools", "NodePoolSpec"}, + } { + schemaRef := doc.Components.Schemas[hc.schema] + if schemaRef == nil { + return nil, fmt.Errorf("%s schema not found in OpenAPI spec", hc.schema) + } + schemas[hc.plural] = &ResourceSchema{ + TypeName: hc.schema, + Schema: schemaRef, + } } - // for _, kind := range requiredSpecValidationKinds { - // if !registeredKinds[kind] { - // return nil, fmt.Errorf( - // "entity kind %q with SpecSchemaName must be registered for schema validation", - // kind, - // ) - // } - // } return schemas, nil } -// TODO : HYPERFLEET-1159 - Uncomment this once Cluster and NodePool are registered -// func isRequiredSpecValidationKind(kind string) bool { -// for _, required := range requiredSpecValidationKinds { -// if kind == required { -// return true -// } -// } -// return false -// } - // HasSchema reports whether a validation schema was loaded for the given resource plural. func (v *SchemaValidator) HasSchema(resourcePlural string) bool { return v.schemas[resourcePlural] != nil diff --git a/pkg/validators/schema_validator_test.go b/pkg/validators/schema_validator_test.go index 085f1b14..6d4176cc 100644 --- a/pkg/validators/schema_validator_test.go +++ b/pkg/validators/schema_validator_test.go @@ -146,41 +146,20 @@ components: err := os.WriteFile(schemaPath, []byte(invalidSchema), 0600) Expect(err).To(BeNil()) - // Should panic because registered entity's SpecSchemaName doesn't resolve - Expect(func() { - _, _ = NewSchemaValidator(schemaPath) - }).To(PanicWith(ContainSubstring("ClusterSpec"))) + _, err = NewSchemaValidator(schemaPath) + Expect(err).ToNot(BeNil()) + Expect(err.Error()).To(ContainSubstring("ClusterSpec schema not found")) } -// TODO : HYPERFLEET-1159 - Uncomment this once Cluster and NodePool are registered -// func TestNewSchemaValidator_MissingRequiredEntityRegistration(t *testing.T) { -// RegisterTestingT(t) - -// registry.Reset() -// registry.Register(registry.EntityDescriptor{ -// Kind: "Cluster", -// Plural: "clusters", -// SpecSchemaName: "ClusterSpec", -// }) - -// tmpDir := t.TempDir() -// schemaPath := filepath.Join(tmpDir, "test-schema.yaml") -// err := os.WriteFile(schemaPath, []byte(testSchema), 0600) -// Expect(err).To(BeNil()) - -// _, err = NewSchemaValidator(schemaPath) -// Expect(err).ToNot(BeNil()) -// Expect(err.Error()).To(ContainSubstring(`entity kind "NodePool" with SpecSchemaName must be registered`)) -// } - func TestNewSchemaValidator_RegisteredEntityMissingSchema_Panics(t *testing.T) { RegisterTestingT(t) registerRequiredSpecValidationEntities() registry.Register(registry.EntityDescriptor{ - Kind: "WifConfig", - Plural: "wifconfigs", - SpecSchemaName: "WifConfigSpec", + Kind: "WifConfig", + Plural: "wifconfigs", + SpecSchemaName: "WifConfigSpec", + RequireSpecSchema: true, }) schemaWithoutWifConfig := ` @@ -213,15 +192,20 @@ components: }).To(PanicWith(ContainSubstring("WifConfigSpec"))) } -func TestNewSchemaValidator_RequiredEntityMissingOpenAPISchema_Fails(t *testing.T) { +func TestNewSchemaValidator_NonRequiredEntityMissingSchema_SkipsWithoutPanic(t *testing.T) { RegisterTestingT(t) registerRequiredSpecValidationEntities() + registry.Register(registry.EntityDescriptor{ + Kind: "WifConfig", + Plural: "wifconfigs", + SpecSchemaName: "WifConfigSpec", + }) - schemaWithoutNodePool := ` + schemaWithoutWifConfig := ` openapi: 3.0.0 info: - title: Cluster Only + title: Cluster NodePool Only version: 1.0.0 paths: {} components: @@ -231,32 +215,60 @@ components: properties: region: type: string + NodePoolSpec: + type: object + properties: + replicas: + type: integer ` tmpDir := t.TempDir() - schemaPath := filepath.Join(tmpDir, "cluster-only.yaml") - err := os.WriteFile(schemaPath, []byte(schemaWithoutNodePool), 0600) + schemaPath := filepath.Join(tmpDir, "cluster-nodepool-only.yaml") + err := os.WriteFile(schemaPath, []byte(schemaWithoutWifConfig), 0600) Expect(err).To(BeNil()) - Expect(func() { - _, _ = NewSchemaValidator(schemaPath) - }).To(PanicWith(ContainSubstring("NodePoolSpec"))) + validator, err := NewSchemaValidator(schemaPath) + Expect(err).To(BeNil()) + Expect(validator.HasSchema("clusters")).To(BeTrue()) + Expect(validator.HasSchema("nodepools")).To(BeTrue()) + Expect(validator.HasSchema("wifconfigs")).To(BeFalse()) } -func TestValidate_RegisteredEntityMissingSchema_PanicsAtConstruction(t *testing.T) { +func TestNewSchemaValidator_RealOpenAPISpec_BootsWithRegisteredEntities(t *testing.T) { RegisterTestingT(t) - registerRequiredSpecValidationEntities() + registry.Reset() + registry.Register(registry.EntityDescriptor{ + Kind: "Channel", + Plural: "channels", + SpecSchemaName: "ChannelSpec", + }) + registry.Register(registry.EntityDescriptor{ + Kind: "Version", + Plural: "versions", + ParentKind: "Channel", + SpecSchemaName: "VersionSpec", + }) registry.Register(registry.EntityDescriptor{ Kind: "WifConfig", Plural: "wifconfigs", SpecSchemaName: "WifConfigSpec", }) - schemaWithoutWifConfig := ` + Expect(func() { + _, _ = NewSchemaValidator("../../openapi/openapi.yaml") + }).ToNot(Panic()) +} + +func TestNewSchemaValidator_RequiredEntityMissingOpenAPISchema_Fails(t *testing.T) { + RegisterTestingT(t) + + registerRequiredSpecValidationEntities() + + schemaWithoutNodePool := ` openapi: 3.0.0 info: - title: Cluster NodePool Only + title: Cluster Only version: 1.0.0 paths: {} components: @@ -266,21 +278,16 @@ components: properties: region: type: string - NodePoolSpec: - type: object - properties: - replicas: - type: integer ` tmpDir := t.TempDir() - schemaPath := filepath.Join(tmpDir, "cluster-nodepool-only.yaml") - err := os.WriteFile(schemaPath, []byte(schemaWithoutWifConfig), 0600) + schemaPath := filepath.Join(tmpDir, "cluster-only.yaml") + err := os.WriteFile(schemaPath, []byte(schemaWithoutNodePool), 0600) Expect(err).To(BeNil()) - Expect(func() { - _, _ = NewSchemaValidator(schemaPath) - }).To(PanicWith(ContainSubstring("WifConfigSpec"))) + _, err = NewSchemaValidator(schemaPath) + Expect(err).ToNot(BeNil()) + Expect(err.Error()).To(ContainSubstring("NodePoolSpec schema not found")) } func TestValidate_WifConfigSpec_Valid(t *testing.T) {