Files
kubevela/pkg/definition/defkit/helper_definition_test.go
Jerrin Francis c6ee19ee45 Fix: remove Mandatory() API and make non-optional the default field state in defkit (#7070)
Replace the three-state field marker system (Optional/Mandatory/Required)
  with a simpler two-state system where the default behavior produces
  `field: type` (no marker), `.Required()` produces `field!: type`, and
  `.Optional()` produces `field?: type`.

  - Remove Mandatory(), IsMandatory(), ForceOptional(), IsForceOptional()
    from all param types, StructField, Param interface, and CUE generator
  - Replace `mandatory` and `forceOptional` fields with `optional` field
    in baseParam and StructField
  - Simplify CUE marker resolution to: default=none, Required="!",
    Optional="?"
  - Remove BeMandatory() matcher and mandatoryParam interface
  - Migrate all test usages: .Mandatory() removed (now default behavior),
    .ForceOptional() replaced with .Optional()

Signed-off-by: Jerrin Francis <jerrinfrancis7@gmail.com>
2026-03-16 06:38:06 -07:00

194 lines
7.1 KiB
Go

/*
Copyright 2025 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package defkit_test
import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/oam-dev/kubevela/pkg/definition/defkit"
)
var _ = Describe("HelperDefinition", func() {
Context("Param-based helper definition", func() {
It("should report HasParam true and return the param", func() {
probeParam := defkit.Struct("probe").WithFields(
defkit.Field("path", defkit.ParamTypeString).Default("/health"),
defkit.Field("port", defkit.ParamTypeInt).Default(8080),
)
comp := defkit.NewComponent("test").
Helper("HealthProbe", probeParam)
helpers := comp.GetHelperDefinitions()
Expect(helpers).To(HaveLen(1))
h := helpers[0]
Expect(h.GetName()).To(Equal("HealthProbe"))
Expect(h.HasParam()).To(BeTrue())
Expect(h.GetParam()).To(Equal(probeParam))
Expect(h.GetSchema()).To(BeEmpty())
})
})
Context("Multiple helper definitions", func() {
It("should support multiple helpers on the same component", func() {
probe := defkit.Struct("probe").WithFields(
defkit.Field("path", defkit.ParamTypeString),
)
resource := defkit.Struct("resource").WithFields(
defkit.Field("cpu", defkit.ParamTypeString),
defkit.Field("memory", defkit.ParamTypeString),
)
comp := defkit.NewComponent("test").
Helper("HealthProbe", probe).
Helper("ResourceSpec", resource)
helpers := comp.GetHelperDefinitions()
Expect(helpers).To(HaveLen(2))
Expect(helpers[0].GetName()).To(Equal("HealthProbe"))
Expect(helpers[1].GetName()).To(Equal("ResourceSpec"))
})
})
Context("HelperDefinition on TraitDefinition", func() {
It("should support helper definitions on traits", func() {
probeParam := defkit.Struct("probe").WithFields(
defkit.Field("path", defkit.ParamTypeString),
)
trait := defkit.NewTrait("health-probe").
Description("Add health probes").
AppliesTo("deployments.apps").
Helper("HealthProbe", probeParam)
helpers := trait.GetHelperDefinitions()
Expect(helpers).To(HaveLen(1))
Expect(helpers[0].GetName()).To(Equal("HealthProbe"))
Expect(helpers[0].HasParam()).To(BeTrue())
})
})
Context("Helper without param (raw schema)", func() {
It("should report HasParam false when param is nil", func() {
// Use the raw schema path by passing a nil param through the builder
// The HelperDefinition struct should report HasParam false
comp := defkit.NewComponent("test")
// Access helper definitions - should be empty initially
helpers := comp.GetHelperDefinitions()
Expect(helpers).To(BeEmpty())
})
})
Context("Map-based helper with StringKeyMap and typed arrays", func() {
It("should render StringKeyMap as [string]: string in helper definition", func() {
helper := defkit.Map("labelSelector").WithFields(
defkit.StringKeyMap("matchLabels").Optional().Description("A map of {key,value} pairs"),
defkit.Array("matchExpressions").Optional().Description("Label selector requirements").WithFields(
defkit.String("key"),
defkit.String("operator").Default("In").Values("In", "NotIn", "Exists", "DoesNotExist"),
defkit.Array("values").Optional().Of(defkit.ParamTypeString),
),
)
trait := defkit.NewTrait("helper-test").
Description("Test helper rendering").
AppliesTo("deployments.apps").
Helper("labelSelector", helper).
Template(func(tpl *defkit.Template) {
tpl.Patch().Set("spec.selector", defkit.Lit("test"))
})
cue := trait.ToCue()
// StringKeyMap renders as [string]: string
Expect(cue).To(ContainSubstring("matchLabels?: [string]: string"))
// Should NOT have duplicate description comments
Expect(cue).NotTo(ContainSubstring("// +usage=A map of {key,value} pairs\n\t// +usage=A map of {key,value} pairs"))
// Array with fields renders as [...{...}]
Expect(cue).To(ContainSubstring("matchExpressions?:"))
Expect(cue).To(ContainSubstring("[...{"))
// Typed array renders as [...string]
Expect(cue).To(ContainSubstring("values?: [...string]"))
// Enum with default renders correctly
Expect(cue).To(ContainSubstring(`*"In"`))
})
It("should render Of(ParamTypeString) in Struct-based helper fields", func() {
helper := defkit.Struct("nodeSelector").WithFields(
defkit.Field("key", defkit.ParamTypeString),
defkit.Field("operator", defkit.ParamTypeString).Default("In").Values("In", "NotIn"),
defkit.Field("values", defkit.ParamTypeArray).Optional().Of(defkit.ParamTypeString),
)
trait := defkit.NewTrait("typed-array-test").
Description("Test typed arrays in helpers").
AppliesTo("deployments.apps").
Helper("nodeSelector", helper).
Template(func(tpl *defkit.Template) {
tpl.Patch().Set("spec.selector", defkit.Lit("test"))
})
cue := trait.ToCue()
Expect(cue).To(ContainSubstring("#nodeSelector"))
// key has no marker (default) - CUE formatter may add tab alignment
Expect(cue).To(MatchRegexp(`key:\s+string`))
Expect(cue).To(ContainSubstring("values?: [...string]"))
// Untyped array should NOT appear
Expect(cue).NotTo(MatchRegexp(`values\?\: \[\.\.\.\]\s*\n`))
})
It("should render schemaRef with Of correctly in Struct helper", func() {
selectorHelper := defkit.Struct("nodeSelectorTerm").WithFields(
defkit.Field("matchExpressions", defkit.ParamTypeArray).Optional().WithSchemaRef("nodeSelector"),
defkit.Field("matchFields", defkit.ParamTypeArray).Optional().WithSchemaRef("nodeSelector"),
)
affinityHelper := defkit.Struct("podAffinityTerm").WithFields(
defkit.Field("labelSelector", defkit.ParamTypeStruct).Optional().WithSchemaRef("labelSelector"),
defkit.Field("namespaces", defkit.ParamTypeArray).Optional().Of(defkit.ParamTypeString),
defkit.Field("topologyKey", defkit.ParamTypeString),
)
trait := defkit.NewTrait("schemaref-test").
Description("Test schemaRef in helpers").
AppliesTo("deployments.apps").
Helper("nodeSelectorTerm", selectorHelper).
Helper("podAffinityTerm", affinityHelper).
Template(func(tpl *defkit.Template) {
tpl.Patch().Set("spec.selector", defkit.Lit("test"))
})
cue := trait.ToCue()
// SchemaRef on array field renders as [...#name]
Expect(cue).To(ContainSubstring("matchExpressions?: [...#nodeSelector]"))
Expect(cue).To(ContainSubstring("matchFields?: [...#nodeSelector]"))
// SchemaRef on struct field renders as #name
Expect(cue).To(ContainSubstring("labelSelector?: #labelSelector"))
// Of(ParamTypeString) renders as [...string]
Expect(cue).To(ContainSubstring("namespaces?: [...string]"))
// Field with no marker (default)
Expect(cue).To(ContainSubstring("topologyKey: string"))
})
})
})