Files
kubevela/pkg/definition/defkit/param_test.go
Ayush Kumar 012a134829 Feat: extend fluent builder API validator patterns (#7092)
* Feat: Add NotEmpty and NegativePattern constraints to StringParam; implement Closed for MapParam

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* feat: add validation support for array and map parameters

- Introduced validators for ArrayParam and MapParam, allowing for cross-field validation within structured parameters.
- Added NonEmpty validation for ArrayParam to ensure arrays are not empty.
- Implemented ConditionalStructOp for conditional struct generation based on specified conditions.
- Created a new Validator type for defining validation rules with optional guard conditions.
- Added tests for various validation scenarios, including mutual exclusion and conditional parameters.
- Enhanced the CUE generation logic to incorporate new validation features and conditional struct handling.

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* feat: extend fluent API with new scoped field conditions and improve validation checks

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* feat: enhance ArrayParam with NotEmpty constraint and update ScopedField documentation

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* feat: rename ScopedField to LocalField for improved clarity in condition building

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* feat: refactor local field conditions to use RegexMatch and streamline condition building

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* feat: simplify condition handling by removing unused comparison types and refactoring NotCondition usage

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* refactor: remove unused raw CUE block handling from baseDefinition and ComponentDefinition

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* test: update condition handling in parameter tests to use NotExpr and Cond methods

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* refactor: remove negative pattern handling from StringParam and related tests

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* feat: add support for emitting raw header blocks in template generation

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* refactor: remove non-empty check from ArrayParam and update related tests

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* refactor: convert parameter constraint tests to use Ginkgo and Gomega for improved readability and maintainability

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* feat: extend fluent APIs for OAM with new CUE generation tests and condition evaluations

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* refactor: clean up whitespace in component, cuegen, expr, param, and resource files

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* feat: enhance CUE generation by adding support for new expression types and iterator references

Signed-off-by: Ayush Kumar <aykumar@guidewire.com>
Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* refactor: remove unnecessary whitespace in cuegen.go

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* refactor: rename LenOf to LenOfExpr for clarity in comparison methods

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* feat: enhance CUE generation and validation for string arrays in ArrayParam

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

* ci: retrigger checks

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>

---------

Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com>
Signed-off-by: Ayush Kumar <aykumar@guidewire.com>
Co-authored-by: Ayush Kumar <aykumar@guidewire.com>
2026-04-09 14:25:06 +01:00

1259 lines
39 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("Parameters", func() {
Context("StringParam", func() {
It("should create a string parameter with name", func() {
p := defkit.String("image")
Expect(p.Name()).To(Equal("image"))
Expect(p.IsRequired()).To(BeFalse())
Expect(p.IsOptional()).To(BeFalse())
Expect(p.HasDefault()).To(BeFalse())
})
It("should support required modifier", func() {
p := defkit.String("image").Required()
Expect(p.IsRequired()).To(BeTrue())
Expect(p.IsOptional()).To(BeFalse())
})
It("should support default value", func() {
p := defkit.String("image").Default("nginx:latest")
Expect(p.HasDefault()).To(BeTrue())
Expect(p.GetDefault()).To(Equal("nginx:latest"))
})
It("should support description", func() {
p := defkit.String("image").Description("Container image to use")
Expect(p.GetDescription()).To(Equal("Container image to use"))
})
It("should support fluent chaining", func() {
p := defkit.String("image").
Required().
Default("nginx:latest").
Description("Container image")
Expect(p.Name()).To(Equal("image"))
Expect(p.IsRequired()).To(BeTrue())
Expect(p.GetDefault()).To(Equal("nginx:latest"))
Expect(p.GetDescription()).To(Equal("Container image"))
})
It("should not be optional by default even with a default value", func() {
p := defkit.String("policy").Default("Honor")
Expect(p.HasDefault()).To(BeTrue())
Expect(p.IsOptional()).To(BeFalse())
Expect(p.IsRequired()).To(BeFalse())
})
It("should support Optional with a default", func() {
p := defkit.String("policy").Default("Honor").Optional().Values("Honor", "Ignore")
Expect(p.HasDefault()).To(BeTrue())
Expect(p.GetDefault()).To(Equal("Honor"))
Expect(p.IsOptional()).To(BeTrue())
Expect(p.IsRequired()).To(BeFalse())
})
It("should support OpenEnum on enum", func() {
p := defkit.String("verbosity").Values("info", "debug").OpenEnum()
Expect(p.IsOpenEnum()).To(BeTrue())
Expect(p.GetEnumValues()).To(ConsistOf("info", "debug"))
})
It("should not allow string by default on enum", func() {
p := defkit.String("verbosity").Values("info", "debug")
Expect(p.IsOpenEnum()).To(BeFalse())
})
})
Context("IntParam", func() {
It("should create an int parameter with name", func() {
p := defkit.Int("replicas")
Expect(p.Name()).To(Equal("replicas"))
Expect(p.IsRequired()).To(BeFalse())
})
It("should support default value", func() {
p := defkit.Int("replicas").Default(3)
Expect(p.HasDefault()).To(BeTrue())
Expect(p.GetDefault()).To(Equal(3))
})
It("should support fluent chaining", func() {
p := defkit.Int("port").
Required().
Default(8080).
Description("Service port")
Expect(p.Name()).To(Equal("port"))
Expect(p.IsRequired()).To(BeTrue())
Expect(p.GetDefault()).To(Equal(8080))
})
It("should not be optional by default even with a default value", func() {
p := defkit.Int("port").Default(8080)
Expect(p.IsOptional()).To(BeFalse())
Expect(p.HasDefault()).To(BeTrue())
})
It("should support Optional with default", func() {
p := defkit.Int("port").Default(8080).Optional()
Expect(p.IsOptional()).To(BeTrue())
Expect(p.HasDefault()).To(BeTrue())
})
})
Context("BoolParam", func() {
It("should create a bool parameter with name", func() {
p := defkit.Bool("enabled")
Expect(p.Name()).To(Equal("enabled"))
Expect(p.IsRequired()).To(BeFalse())
})
It("should support default value", func() {
p := defkit.Bool("enabled").Default(true)
Expect(p.HasDefault()).To(BeTrue())
Expect(p.GetDefault()).To(Equal(true))
})
It("should support fluent chaining", func() {
p := defkit.Bool("debug").
Optional().
Default(false).
Description("Enable debug mode")
Expect(p.Name()).To(Equal("debug"))
Expect(p.IsOptional()).To(BeTrue())
Expect(p.GetDefault()).To(Equal(false))
})
It("should not be optional by default even with a default value", func() {
p := defkit.Bool("auto").Default(true)
Expect(p.IsOptional()).To(BeFalse())
Expect(p.HasDefault()).To(BeTrue())
})
It("should support Optional with default", func() {
p := defkit.Bool("auto").Default(true).Optional()
Expect(p.IsOptional()).To(BeTrue())
Expect(p.HasDefault()).To(BeTrue())
})
})
Context("FloatParam", func() {
It("should create a float parameter with name", func() {
p := defkit.Float("cpu")
Expect(p.Name()).To(Equal("cpu"))
Expect(p.IsRequired()).To(BeFalse())
})
It("should support default value", func() {
p := defkit.Float("memory").Default(0.5)
Expect(p.HasDefault()).To(BeTrue())
Expect(p.GetDefault()).To(Equal(0.5))
})
It("should support fluent chaining", func() {
p := defkit.Float("ratio").
Required().
Default(1.0).
Description("Resource ratio")
Expect(p.Name()).To(Equal("ratio"))
Expect(p.IsRequired()).To(BeTrue())
Expect(p.GetDefault()).To(Equal(1.0))
})
It("should not be optional by default even with a default value", func() {
p := defkit.Float("ratio").Default(1.0)
Expect(p.IsOptional()).To(BeFalse())
Expect(p.HasDefault()).To(BeTrue())
})
It("should support Optional with default", func() {
p := defkit.Float("ratio").Default(1.0).Optional()
Expect(p.IsOptional()).To(BeTrue())
Expect(p.HasDefault()).To(BeTrue())
})
})
Context("ArrayParam", func() {
It("should create an array parameter with name", func() {
p := defkit.Array("ports")
Expect(p.Name()).To(Equal("ports"))
Expect(p.IsRequired()).To(BeFalse())
})
It("should support element type specification", func() {
p := defkit.Array("ports").Of(defkit.ParamTypeInt)
Expect(p.ElementType()).To(Equal(defkit.ParamTypeInt))
})
It("should support default value", func() {
p := defkit.Array("tags").Default([]any{"app", "web"})
Expect(p.HasDefault()).To(BeTrue())
Expect(p.GetDefault()).To(Equal([]any{"app", "web"}))
})
It("should support fluent chaining", func() {
p := defkit.Array("volumes").
Of(defkit.ParamTypeString).
Required().
Description("Volume mounts")
Expect(p.Name()).To(Equal("volumes"))
Expect(p.IsRequired()).To(BeTrue())
Expect(p.ElementType()).To(Equal(defkit.ParamTypeString))
})
})
Context("MapParam", func() {
It("should create a map parameter with name", func() {
p := defkit.Map("labels")
Expect(p.Name()).To(Equal("labels"))
Expect(p.IsRequired()).To(BeFalse())
})
It("should support value type specification", func() {
p := defkit.Map("annotations").Of(defkit.ParamTypeString)
Expect(p.ValueType()).To(Equal(defkit.ParamTypeString))
})
It("should support default value", func() {
p := defkit.Map("env").Default(map[string]any{"DEBUG": "true"})
Expect(p.HasDefault()).To(BeTrue())
Expect(p.GetDefault()).To(Equal(map[string]any{"DEBUG": "true"}))
})
It("should support fluent chaining", func() {
p := defkit.Map("config").
Of(defkit.ParamTypeString).
Optional().
Description("Configuration map")
Expect(p.Name()).To(Equal("config"))
Expect(p.IsOptional()).To(BeTrue())
Expect(p.ValueType()).To(Equal(defkit.ParamTypeString))
})
})
Context("StructParam", func() {
It("should create a struct parameter with name", func() {
p := defkit.Struct("resources")
Expect(p.Name()).To(Equal("resources"))
Expect(p.IsRequired()).To(BeFalse())
})
It("should support field definitions", func() {
p := defkit.Struct("resources").WithFields(
defkit.Field("cpu", defkit.ParamTypeString),
defkit.Field("memory", defkit.ParamTypeString),
)
Expect(p.GetFields()).To(HaveLen(2))
Expect(p.GetField("cpu")).NotTo(BeNil())
Expect(p.GetField("memory")).NotTo(BeNil())
})
It("should return nil for non-existent field", func() {
p := defkit.Struct("resources").WithFields(
defkit.Field("cpu", defkit.ParamTypeString),
)
Expect(p.GetField("nonexistent")).To(BeNil())
})
It("should support field modifiers", func() {
f := defkit.Field("cpu", defkit.ParamTypeString).
Required().
Default("100m").
Description("CPU request")
Expect(f.Name()).To(Equal("cpu"))
Expect(f.FieldType()).To(Equal(defkit.ParamTypeString))
Expect(f.IsRequired()).To(BeTrue())
Expect(f.HasDefault()).To(BeTrue())
Expect(f.GetDefault()).To(Equal("100m"))
Expect(f.GetDescription()).To(Equal("CPU request"))
})
It("should support nested structs", func() {
requests := defkit.Struct("requests").WithFields(
defkit.Field("cpu", defkit.ParamTypeString),
defkit.Field("memory", defkit.ParamTypeString),
)
p := defkit.Struct("resources").WithFields(
defkit.Field("requests", defkit.ParamTypeStruct).Nested(requests),
)
reqField := p.GetField("requests")
Expect(reqField).NotTo(BeNil())
Expect(reqField.GetNested()).NotTo(BeNil())
Expect(reqField.GetNested().GetField("cpu")).NotTo(BeNil())
})
It("should support fluent chaining", func() {
p := defkit.Struct("container").
WithFields(
defkit.Field("name", defkit.ParamTypeString).Required(),
defkit.Field("image", defkit.ParamTypeString).Required(),
defkit.Field("port", defkit.ParamTypeInt).Default(80),
).
Required().
Description("Container specification")
Expect(p.Name()).To(Equal("container"))
Expect(p.IsRequired()).To(BeTrue())
Expect(p.GetDescription()).To(Equal("Container specification"))
Expect(p.GetFields()).To(HaveLen(3))
})
})
Context("EnumParam", func() {
It("should create an enum parameter with name", func() {
p := defkit.Enum("protocol")
Expect(p.Name()).To(Equal("protocol"))
Expect(p.IsRequired()).To(BeFalse())
})
It("should support enum values", func() {
p := defkit.Enum("protocol").Values("TCP", "UDP", "SCTP")
Expect(p.GetValues()).To(Equal([]string{"TCP", "UDP", "SCTP"}))
})
It("should support default value", func() {
p := defkit.Enum("protocol").
Values("TCP", "UDP").
Default("TCP")
Expect(p.HasDefault()).To(BeTrue())
Expect(p.GetDefault()).To(Equal("TCP"))
})
It("should support fluent chaining", func() {
p := defkit.Enum("restartPolicy").
Values("Always", "OnFailure", "Never").
Required().
Default("Always").
Description("Pod restart policy")
Expect(p.Name()).To(Equal("restartPolicy"))
Expect(p.IsRequired()).To(BeTrue())
Expect(p.GetValues()).To(HaveLen(3))
})
It("should not be optional by default even with a default value", func() {
p := defkit.Enum("mode").Values("a", "b").Default("a")
Expect(p.IsOptional()).To(BeFalse())
Expect(p.HasDefault()).To(BeTrue())
})
It("should support Optional with default", func() {
p := defkit.Enum("mode").Values("a", "b").Default("a").Optional()
Expect(p.IsOptional()).To(BeTrue())
Expect(p.HasDefault()).To(BeTrue())
})
})
Context("OneOfParam", func() {
It("should create a oneof parameter with name", func() {
p := defkit.OneOf("probe")
Expect(p.Name()).To(Equal("probe"))
Expect(p.GetDiscriminator()).To(Equal("type")) // default
})
It("should support custom discriminator", func() {
p := defkit.OneOf("probe").Discriminator("kind")
Expect(p.GetDiscriminator()).To(Equal("kind"))
})
It("should support variant definitions", func() {
p := defkit.OneOf("probe").Variants(
defkit.Variant("http").WithFields(
defkit.Field("path", defkit.ParamTypeString).Required(),
defkit.Field("port", defkit.ParamTypeInt).Required(),
),
defkit.Variant("tcp").WithFields(
defkit.Field("port", defkit.ParamTypeInt).Required(),
),
)
Expect(p.GetVariants()).To(HaveLen(2))
Expect(p.GetVariant("http")).NotTo(BeNil())
Expect(p.GetVariant("tcp")).NotTo(BeNil())
Expect(p.GetVariant("http").GetFields()).To(HaveLen(2))
})
It("should return nil for non-existent variant", func() {
p := defkit.OneOf("probe").Variants(
defkit.Variant("http"),
)
Expect(p.GetVariant("nonexistent")).To(BeNil())
})
It("should support fluent chaining", func() {
p := defkit.OneOf("storage").
Discriminator("type").
Variants(
defkit.Variant("pvc"),
defkit.Variant("emptyDir"),
defkit.Variant("hostPath"),
).
Required().
Description("Storage configuration")
Expect(p.Name()).To(Equal("storage"))
Expect(p.IsRequired()).To(BeTrue())
Expect(p.GetDiscriminator()).To(Equal("type"))
Expect(p.GetVariants()).To(HaveLen(3))
})
})
Context("Parameter as Variable Pattern", func() {
Context("Comparison methods", func() {
It("should create Eq condition from IntParam", func() {
replicas := defkit.Int("replicas")
cond := replicas.Eq(3)
pcc, ok := cond.(*defkit.ParamCompareCondition)
Expect(ok).To(BeTrue(), "expected *ParamCompareCondition")
Expect(pcc.ParamName()).To(Equal("replicas"))
Expect(pcc.Op()).To(Equal("=="))
Expect(pcc.CompareValue()).To(Equal(3))
})
It("should create comparison conditions from StringParam", func() {
status := defkit.String("status")
eqCond, ok := status.Eq("running").(*defkit.ParamCompareCondition)
Expect(ok).To(BeTrue())
Expect(eqCond.ParamName()).To(Equal("status"))
Expect(eqCond.Op()).To(Equal("=="))
Expect(eqCond.CompareValue()).To(Equal("running"))
neCond, ok := status.Ne("error").(*defkit.ParamCompareCondition)
Expect(ok).To(BeTrue())
Expect(neCond.ParamName()).To(Equal("status"))
Expect(neCond.Op()).To(Equal("!="))
Expect(neCond.CompareValue()).To(Equal("error"))
})
It("should create numeric comparison conditions", func() {
replicas := defkit.Int("replicas")
gtCond, ok := replicas.Gt(1).(*defkit.ParamCompareCondition)
Expect(ok).To(BeTrue())
Expect(gtCond.Op()).To(Equal(">"))
Expect(gtCond.CompareValue()).To(Equal(1))
gteCond, ok := replicas.Gte(1).(*defkit.ParamCompareCondition)
Expect(ok).To(BeTrue())
Expect(gteCond.Op()).To(Equal(">="))
Expect(gteCond.CompareValue()).To(Equal(1))
ltCond, ok := replicas.Lt(10).(*defkit.ParamCompareCondition)
Expect(ok).To(BeTrue())
Expect(ltCond.Op()).To(Equal("<"))
Expect(ltCond.CompareValue()).To(Equal(10))
lteCond, ok := replicas.Lte(10).(*defkit.ParamCompareCondition)
Expect(ok).To(BeTrue())
Expect(lteCond.Op()).To(Equal("<="))
Expect(lteCond.CompareValue()).To(Equal(10))
})
})
Context("Arithmetic expressions", func() {
It("should create Add expression from IntParam", func() {
replicas := defkit.Int("replicas")
expr := replicas.Add(1)
arith, ok := expr.(*defkit.ParamArithExpr)
Expect(ok).To(BeTrue(), "expected *ParamArithExpr")
Expect(arith.ParamName()).To(Equal("replicas"))
Expect(arith.Op()).To(Equal("+"))
Expect(arith.ArithValue()).To(Equal(1))
})
It("should create arithmetic expressions from IntParam", func() {
replicas := defkit.Int("replicas")
addExpr, ok := replicas.Add(1).(*defkit.ParamArithExpr)
Expect(ok).To(BeTrue())
Expect(addExpr.Op()).To(Equal("+"))
Expect(addExpr.ArithValue()).To(Equal(1))
subExpr, ok := replicas.Sub(1).(*defkit.ParamArithExpr)
Expect(ok).To(BeTrue())
Expect(subExpr.Op()).To(Equal("-"))
Expect(subExpr.ArithValue()).To(Equal(1))
mulExpr, ok := replicas.Mul(2).(*defkit.ParamArithExpr)
Expect(ok).To(BeTrue())
Expect(mulExpr.Op()).To(Equal("*"))
Expect(mulExpr.ArithValue()).To(Equal(2))
divExpr, ok := replicas.Div(2).(*defkit.ParamArithExpr)
Expect(ok).To(BeTrue())
Expect(divExpr.Op()).To(Equal("/"))
Expect(divExpr.ArithValue()).To(Equal(2))
})
})
Context("String expressions", func() {
It("should create Concat expression from StringParam", func() {
name := defkit.String("name")
expr := name.Concat("-suffix")
concat, ok := expr.(*defkit.ParamConcatExpr)
Expect(ok).To(BeTrue(), "expected *ParamConcatExpr")
Expect(concat.ParamName()).To(Equal("name"))
Expect(concat.Suffix()).To(Equal("-suffix"))
Expect(concat.Prefix()).To(BeEmpty())
})
It("should create Prepend expression from StringParam", func() {
name := defkit.String("name")
expr := name.Prepend("prefix-")
concat, ok := expr.(*defkit.ParamConcatExpr)
Expect(ok).To(BeTrue(), "expected *ParamConcatExpr")
Expect(concat.ParamName()).To(Equal("name"))
Expect(concat.Prefix()).To(Equal("prefix-"))
Expect(concat.Suffix()).To(BeEmpty())
})
})
Context("Struct field access", func() {
It("should create field reference from StructParam", func() {
config := defkit.Struct("config").
WithFields(
defkit.Field("host", defkit.ParamTypeString),
defkit.Field("port", defkit.ParamTypeInt),
)
fieldRef := config.Field("port")
Expect(fieldRef).NotTo(BeNil())
Expect(fieldRef.ParamName()).To(Equal("config"))
Expect(fieldRef.FieldPath()).To(Equal("port"))
})
It("should create nested field reference", func() {
config := defkit.Struct("config")
fieldRef := config.Field("database.host")
Expect(fieldRef.ParamName()).To(Equal("config"))
Expect(fieldRef.FieldPath()).To(Equal("database.host"))
})
It("should create IsSet condition from field ref", func() {
config := defkit.Struct("config")
fieldRef := config.Field("port")
cond := fieldRef.IsSet()
isSet, ok := cond.(*defkit.ParamPathIsSetCondition)
Expect(ok).To(BeTrue(), "expected *ParamPathIsSetCondition")
Expect(isSet.Path()).To(Equal("config.port"))
})
It("should create Eq condition from field ref", func() {
config := defkit.Struct("config")
fieldRef := config.Field("enabled")
cond := fieldRef.Eq(true)
pcc, ok := cond.(*defkit.ParamCompareCondition)
Expect(ok).To(BeTrue(), "expected *ParamCompareCondition")
Expect(pcc.ParamName()).To(Equal("config.enabled"))
Expect(pcc.Op()).To(Equal("=="))
Expect(pcc.CompareValue()).To(Equal(true))
})
})
})
Context("Additional Param Types", func() {
It("should create StringList parameter", func() {
p := defkit.StringList("tags")
Expect(p.Name()).To(Equal("tags"))
Expect(p.ElementType()).To(Equal(defkit.ParamTypeString))
Expect(p.IsRequired()).To(BeFalse())
})
It("should create IntList parameter", func() {
p := defkit.IntList("ports")
Expect(p.Name()).To(Equal("ports"))
Expect(p.ElementType()).To(Equal(defkit.ParamTypeInt))
Expect(p.IsRequired()).To(BeFalse())
})
It("should create StringKeyMap parameter", func() {
p := defkit.StringKeyMap("labels")
Expect(p.Name()).To(Equal("labels"))
Expect(p.GetType()).To(Equal(defkit.ParamTypeMap))
})
It("should create List parameter", func() {
p := defkit.List("items")
Expect(p.Name()).To(Equal("items"))
Expect(p.IsRequired()).To(BeFalse())
})
It("should create Object parameter", func() {
p := defkit.Object("config")
Expect(p.Name()).To(Equal("config"))
Expect(p.IsRequired()).To(BeFalse())
})
})
Context("IsSet and NotSet conditions", func() {
It("should create IsSet condition from param", func() {
replicas := defkit.Int("replicas")
cond := replicas.IsSet()
isSet, ok := cond.(*defkit.IsSetCondition)
Expect(ok).To(BeTrue(), "expected *IsSetCondition")
Expect(isSet.ParamName()).To(Equal("replicas"))
})
It("should create NotSet condition from param", func() {
replicas := defkit.Int("replicas")
cond := replicas.NotSet()
notExpr, ok := cond.(*defkit.NotExpr)
Expect(ok).To(BeTrue(), "expected *NotExpr")
inner, ok := notExpr.Cond().(*defkit.IsSetCondition)
Expect(ok).To(BeTrue(), "expected inner *IsSetCondition")
Expect(inner.ParamName()).To(Equal("replicas"))
})
})
Context("BoolParam conditions", func() {
It("should create IsTrue condition", func() {
enabled := defkit.Bool("enabled")
cond := enabled.IsTrue()
truthy, ok := cond.(*defkit.TruthyCondition)
Expect(ok).To(BeTrue(), "expected *TruthyCondition")
Expect(truthy.ParamName()).To(Equal("enabled"))
})
It("should create IsFalse condition", func() {
enabled := defkit.Bool("enabled")
cond := enabled.IsFalse()
falsy, ok := cond.(*defkit.FalsyCondition)
Expect(ok).To(BeTrue(), "expected *FalsyCondition")
Expect(falsy.ParamName()).To(Equal("enabled"))
})
})
Context("ArrayParam additional methods", func() {
It("should set min and max items constraints", func() {
p := defkit.Array("tags").
Of(defkit.ParamTypeString).
MinItems(1).
MaxItems(10)
Expect(*p.GetMinItems()).To(Equal(1))
Expect(*p.GetMaxItems()).To(Equal(10))
})
It("should create length constraint conditions", func() {
arr := defkit.Array("items")
gteCond := arr.LenGte(1)
lenCond, ok := gteCond.(*defkit.LenCondition)
Expect(ok).To(BeTrue(), "expected *LenCondition")
Expect(lenCond.ParamName()).To(Equal("items"))
Expect(lenCond.Op()).To(Equal(">="))
Expect(lenCond.Length()).To(Equal(1))
})
It("should set WithFields for array items", func() {
p := defkit.List("ports").WithFields(
defkit.Int("port").Required(),
defkit.String("name"),
)
Expect(p.GetFields()).To(HaveLen(2))
})
})
Context("MapParam Optional method", func() {
It("should set map as optional", func() {
p := defkit.Map("labels").Optional()
Expect(p.IsOptional()).To(BeTrue())
})
It("should set map as required", func() {
p := defkit.Map("labels").Required()
Expect(p.IsRequired()).To(BeTrue())
})
})
Context("StructParam Optional method", func() {
It("should set struct as optional", func() {
p := defkit.Struct("config").Optional()
Expect(p.IsOptional()).To(BeTrue())
})
})
Context("EnumParam Optional method", func() {
It("should set enum as optional", func() {
p := defkit.Enum("protocol").
Values("TCP", "UDP").
Optional()
Expect(p.IsOptional()).To(BeTrue())
})
})
Context("OneOfParam Optional method", func() {
It("should set oneof as optional", func() {
p := defkit.OneOf("storage").Optional()
Expect(p.IsOptional()).To(BeTrue())
})
})
Context("OneOfParam Default method", func() {
It("should set default variant name", func() {
p := defkit.OneOf("type").Default("emptyDir")
Expect(p.HasDefault()).To(BeTrue())
Expect(p.GetDefault()).To(Equal("emptyDir"))
})
It("should support fluent chaining with Default", func() {
p := defkit.OneOf("type").
Default("emptyDir").
Description("Volume type").
Variants(
defkit.Variant("pvc").WithFields(
defkit.Field("claimName", defkit.ParamTypeString).Required(),
),
defkit.Variant("emptyDir"),
)
Expect(p.HasDefault()).To(BeTrue())
Expect(p.GetDefault()).To(Equal("emptyDir"))
Expect(p.GetDescription()).To(Equal("Volume type"))
Expect(p.GetVariants()).To(HaveLen(2))
})
})
Context("IntParam Optional method", func() {
It("should set int as optional", func() {
p := defkit.Int("replicas").Optional()
Expect(p.IsOptional()).To(BeTrue())
})
})
Context("FloatParam Optional method", func() {
It("should set float as optional", func() {
p := defkit.Float("ratio").Optional()
Expect(p.IsOptional()).To(BeTrue())
})
})
Context("StringParam Enum method", func() {
It("should set enum values on string param", func() {
p := defkit.String("protocol").Values("TCP", "UDP", "SCTP")
Expect(p.GetEnumValues()).To(ConsistOf("TCP", "UDP", "SCTP"))
})
})
Context("ArrayParam Length Conditions", func() {
It("should create LenLt condition", func() {
arr := defkit.Array("items")
cond := arr.LenLt(10)
lenCond, ok := cond.(*defkit.LenCondition)
Expect(ok).To(BeTrue(), "expected *LenCondition")
Expect(lenCond.ParamName()).To(Equal("items"))
Expect(lenCond.Op()).To(Equal("<"))
Expect(lenCond.Length()).To(Equal(10))
})
It("should create LenLte condition", func() {
arr := defkit.Array("items")
cond := arr.LenLte(10)
lenCond, ok := cond.(*defkit.LenCondition)
Expect(ok).To(BeTrue(), "expected *LenCondition")
Expect(lenCond.ParamName()).To(Equal("items"))
Expect(lenCond.Op()).To(Equal("<="))
Expect(lenCond.Length()).To(Equal(10))
})
})
Context("ArrayParam WithSchema methods", func() {
It("should set WithSchema on array", func() {
p := defkit.Array("items").WithSchema("{ name: string }")
Expect(p.GetSchema()).To(Equal("{ name: string }"))
})
It("should set WithSchemaRef on array", func() {
p := defkit.Array("items").WithSchemaRef("#Schema")
Expect(p.GetSchemaRef()).To(Equal("#Schema"))
})
})
Context("DynamicMap param", func() {
It("should create dynamic map", func() {
p := defkit.DynamicMap()
Expect(p.IsDynamicMap()).To(BeTrue())
})
It("should set ValueType", func() {
p := defkit.DynamicMap().ValueType(defkit.ParamTypeString)
Expect(p.GetValueType()).To(Equal(defkit.ParamTypeString))
})
It("should set ValueTypeUnion", func() {
p := defkit.DynamicMap().ValueTypeUnion("string | int")
Expect(p.GetValueTypeUnion()).To(Equal("string | int"))
})
It("should set Description", func() {
p := defkit.DynamicMap().Description("Dynamic key-value pairs")
Expect(p.GetDescription()).To(Equal("Dynamic key-value pairs"))
})
})
Context("MapParam additional methods", func() {
It("should set Description", func() {
p := defkit.Map("labels").Description("Key-value labels")
Expect(p.GetDescription()).To(Equal("Key-value labels"))
})
It("should get ValueType after setting with Of", func() {
p := defkit.Map("labels").Of(defkit.ParamTypeString)
Expect(p.ValueType()).To(Equal(defkit.ParamTypeString))
})
})
Context("StructParam additional methods", func() {
It("should set WithSchemaRef on struct", func() {
p := defkit.Struct("config").WithSchemaRef("#ConfigSchema")
Expect(p.GetSchemaRef()).To(Equal("#ConfigSchema"))
})
})
Context("OpenStruct param", func() {
It("should create open struct", func() {
p := defkit.OpenStruct()
Expect(p.GetName()).To(Equal("")) // OpenStruct has no name - represents entire parameter schema
Expect(p.IsOpen()).To(BeTrue())
})
It("should set description", func() {
p := defkit.OpenStruct().Description("Open config")
Expect(p.GetDescription()).To(Equal("Open config"))
})
It("should return correct type", func() {
p := defkit.OpenStruct()
Expect(p.GetType()).To(Equal(defkit.ParamTypeStruct))
})
It("should be optional by default", func() {
p := defkit.OpenStruct()
Expect(p.IsRequired()).To(BeFalse())
})
It("should have nil default", func() {
p := defkit.OpenStruct()
Expect(p.GetDefault()).To(BeNil())
})
})
Context("OpenArray param", func() {
It("should create open array", func() {
p := defkit.OpenArray("items")
Expect(p.Name()).To(Equal("items"))
})
It("should set description", func() {
p := defkit.OpenArray("items").Description("Open items array")
Expect(p.GetDescription()).To(Equal("Open items array"))
})
It("should return correct type", func() {
p := defkit.OpenArray("items")
Expect(p.GetType()).To(Equal(defkit.ParamTypeArray))
})
It("should be optional by default", func() {
p := defkit.OpenArray("items")
Expect(p.IsRequired()).To(BeFalse())
})
It("should have nil default", func() {
p := defkit.OpenArray("items")
Expect(p.GetDefault()).To(BeNil())
})
})
Context("ParamPath", func() {
It("should create param path", func() {
path := defkit.ParamPath("config.database.host")
Expect(path).NotTo(BeNil())
Expect(path.Path()).To(Equal("config.database.host"))
})
It("should create IsSet condition", func() {
path := defkit.ParamPath("config.port")
cond := path.IsSet()
isSet, ok := cond.(*defkit.ParamPathIsSetCondition)
Expect(ok).To(BeTrue(), "expected *ParamPathIsSetCondition")
Expect(isSet.Path()).To(Equal("config.port"))
})
})
Context("StructField Name method", func() {
It("should return field name", func() {
f := defkit.Field("port", defkit.ParamTypeInt)
Expect(f.Name()).To(Equal("port"))
})
})
Context("StructField Required, Optional, Default", func() {
It("should mark field as required", func() {
f := defkit.Field("port", defkit.ParamTypeInt).Required()
Expect(f.IsRequired()).To(BeTrue())
})
It("should mark field as optional", func() {
f := defkit.Field("port", defkit.ParamTypeInt).Optional()
Expect(f.IsRequired()).To(BeFalse())
})
It("should set default value", func() {
f := defkit.Field("port", defkit.ParamTypeInt).Default(8080)
Expect(f.HasDefault()).To(BeTrue())
Expect(f.GetDefault()).To(Equal(8080))
})
})
Context("IntParam Ne condition", func() {
It("should create Ne condition", func() {
p := defkit.Int("count")
cond := p.Ne(0)
pcc, ok := cond.(*defkit.ParamCompareCondition)
Expect(ok).To(BeTrue(), "expected *ParamCompareCondition")
Expect(pcc.ParamName()).To(Equal("count"))
Expect(pcc.Op()).To(Equal("!="))
Expect(pcc.CompareValue()).To(Equal(0))
})
})
Context("Short method", func() {
It("should set short flag on StringParam", func() {
p := defkit.String("image").Short("i")
Expect(p.GetShort()).To(Equal("i"))
})
It("should set short flag on IntParam", func() {
p := defkit.Int("port").Short("p")
Expect(p.GetShort()).To(Equal("p"))
})
It("should set short flag on BoolParam", func() {
p := defkit.Bool("debug").Short("d")
Expect(p.GetShort()).To(Equal("d"))
})
It("should set short flag on FloatParam", func() {
p := defkit.Float("ratio").Short("r")
Expect(p.GetShort()).To(Equal("r"))
})
It("should set short flag on EnumParam", func() {
p := defkit.Enum("protocol").Values("TCP", "UDP").Short("p")
Expect(p.GetShort()).To(Equal("p"))
})
It("should return empty string when not set", func() {
p := defkit.String("image")
Expect(p.GetShort()).To(BeEmpty())
})
It("should support fluent chaining with other methods", func() {
p := defkit.String("image").Required().Description("Container image").Short("i")
Expect(p.Name()).To(Equal("image"))
Expect(p.IsRequired()).To(BeTrue())
Expect(p.GetDescription()).To(Equal("Container image"))
Expect(p.GetShort()).To(Equal("i"))
})
})
Context("Ignore method", func() {
It("should mark StringParam as ignored", func() {
p := defkit.String("port").Ignore()
Expect(p.IsIgnore()).To(BeTrue())
})
It("should mark IntParam as ignored", func() {
p := defkit.Int("port").Ignore()
Expect(p.IsIgnore()).To(BeTrue())
})
It("should mark BoolParam as ignored", func() {
p := defkit.Bool("debug").Ignore()
Expect(p.IsIgnore()).To(BeTrue())
})
It("should mark FloatParam as ignored", func() {
p := defkit.Float("ratio").Ignore()
Expect(p.IsIgnore()).To(BeTrue())
})
It("should mark EnumParam as ignored", func() {
p := defkit.Enum("type").Values("A", "B").Ignore()
Expect(p.IsIgnore()).To(BeTrue())
})
It("should not be ignored by default", func() {
p := defkit.String("image")
Expect(p.IsIgnore()).To(BeFalse())
})
It("should support fluent chaining with Short and other methods", func() {
p := defkit.Int("port").Ignore().Description("Deprecated field").Short("p")
Expect(p.IsIgnore()).To(BeTrue())
Expect(p.GetShort()).To(Equal("p"))
Expect(p.GetDescription()).To(Equal("Deprecated field"))
})
})
Context("ClosedUnionParam", func() {
It("should create a closed union parameter with name", func() {
p := defkit.ClosedUnion("url")
Expect(p.Name()).To(Equal("url"))
Expect(p.IsRequired()).To(BeFalse())
Expect(p.IsOptional()).To(BeFalse())
Expect(p.HasDefault()).To(BeFalse())
Expect(p.GetOptions()).To(HaveLen(0))
})
It("should support required modifier", func() {
p := defkit.ClosedUnion("url").Required()
Expect(p.IsRequired()).To(BeTrue())
Expect(p.IsOptional()).To(BeFalse())
})
It("should support optional modifier", func() {
p := defkit.ClosedUnion("url").Required().Optional()
Expect(p.IsRequired()).To(BeFalse())
})
It("should support description", func() {
p := defkit.ClosedUnion("url").Description("the url")
Expect(p.GetDescription()).To(Equal("the url"))
})
It("should support options with fields", func() {
p := defkit.ClosedUnion("url").Options(
defkit.ClosedStruct().WithFields(
defkit.Field("value", defkit.ParamTypeString).Required(),
),
defkit.ClosedStruct().WithFields(
defkit.Field("secretRef", defkit.ParamTypeStruct).Required(),
),
)
Expect(p.GetOptions()).To(HaveLen(2))
Expect(p.GetOptions()[0].GetFields()).To(HaveLen(1))
Expect(p.GetOptions()[0].GetFields()[0].Name()).To(Equal("value"))
Expect(p.GetOptions()[1].GetFields()).To(HaveLen(1))
Expect(p.GetOptions()[1].GetFields()[0].Name()).To(Equal("secretRef"))
})
It("should support fluent chaining", func() {
p := defkit.ClosedUnion("source").
Required().
Description("the source").
Options(
defkit.ClosedStruct().WithFields(
defkit.Field("hcl", defkit.ParamTypeString).Required(),
),
defkit.ClosedStruct().WithFields(
defkit.Field("remote", defkit.ParamTypeString).Required(),
defkit.Field("path", defkit.ParamTypeString),
),
)
Expect(p.Name()).To(Equal("source"))
Expect(p.IsRequired()).To(BeTrue())
Expect(p.GetDescription()).To(Equal("the source"))
Expect(p.GetOptions()).To(HaveLen(2))
Expect(p.GetOptions()[1].GetFields()).To(HaveLen(2))
})
})
Context("StringParam NotEmpty", func() {
It("should default to false", func() {
p := defkit.String("name")
Expect(p.GetNotEmpty()).To(BeFalse())
})
It("should set NotEmpty flag", func() {
p := defkit.String("name").NotEmpty()
Expect(p.GetNotEmpty()).To(BeTrue())
})
It("should chain with other constraints", func() {
p := defkit.String("name").NotEmpty().Pattern("^[a-z]+$").MinLen(3)
Expect(p.GetNotEmpty()).To(BeTrue())
Expect(p.GetPattern()).To(Equal("^[a-z]+$"))
Expect(p.GetMinLen()).NotTo(BeNil())
Expect(*p.GetMinLen()).To(Equal(3))
})
})
Context("ArrayParam NotEmpty elements", func() {
It("should default to false", func() {
p := defkit.StringList("tags")
Expect(p.HasNotEmpty()).To(BeFalse())
})
It("should set NotEmpty flag for elements", func() {
p := defkit.StringList("tags").NotEmpty()
Expect(p.HasNotEmpty()).To(BeTrue())
})
It("should chain with other methods", func() {
p := defkit.StringList("tags").NotEmpty().Optional()
Expect(p.HasNotEmpty()).To(BeTrue())
Expect(p.IsOptional()).To(BeTrue())
})
})
Context("ArrayParam OfEnum", func() {
It("should set schema for enum array", func() {
p := defkit.Array("methods").OfEnum("GET", "POST", "DELETE")
Expect(p.GetSchema()).To(ContainSubstring(`"GET"`))
Expect(p.GetSchema()).To(ContainSubstring(`"POST"`))
Expect(p.GetSchema()).To(ContainSubstring(`"DELETE"`))
Expect(p.GetSchema()).To(ContainSubstring("|"))
})
It("should chain with other methods", func() {
p := defkit.Array("methods").OfEnum("GET", "POST").Optional()
Expect(p.IsOptional()).To(BeTrue())
Expect(p.GetSchema()).To(ContainSubstring(`"GET"`))
})
})
Context("ArrayParam Validators", func() {
It("should store validators", func() {
v := defkit.Validate("check").WithName("_v")
p := defkit.Array("items").WithFields(
defkit.String("name"),
).Validators(v)
Expect(p.GetValidators()).To(HaveLen(1))
Expect(p.GetValidators()[0]).To(Equal(v))
})
It("should accumulate across calls", func() {
p := defkit.Array("items").WithFields(defkit.String("name")).
Validators(defkit.Validate("a").WithName("_a")).
Validators(defkit.Validate("b").WithName("_b"))
Expect(p.GetValidators()).To(HaveLen(2))
})
It("should return empty when not set", func() {
p := defkit.Array("items")
Expect(p.GetValidators()).To(BeEmpty())
})
})
Context("MapParam Closed", func() {
It("should default to false", func() {
p := defkit.Object("config")
Expect(p.IsClosed()).To(BeFalse())
})
It("should set closed flag", func() {
p := defkit.Object("config").Closed()
Expect(p.IsClosed()).To(BeTrue())
})
It("should chain with WithFields", func() {
p := defkit.Object("config").Closed().WithFields(
defkit.String("name"),
)
Expect(p.IsClosed()).To(BeTrue())
Expect(p.GetFields()).To(HaveLen(1))
})
})
Context("MapParam Validators", func() {
It("should store validators", func() {
v := defkit.Validate("check").WithName("_v")
p := defkit.Object("governance").WithFields(
defkit.String("name"),
).Validators(v)
Expect(p.GetValidators()).To(HaveLen(1))
Expect(p.GetValidators()[0]).To(Equal(v))
})
It("should accumulate across calls", func() {
p := defkit.Object("governance").
Validators(defkit.Validate("a").WithName("_a")).
Validators(defkit.Validate("b").WithName("_b"))
Expect(p.GetValidators()).To(HaveLen(2))
})
It("should return empty when not set", func() {
p := defkit.Object("config")
Expect(p.GetValidators()).To(BeEmpty())
})
})
Context("MapParam ConditionalFields", func() {
It("should store conditional field branches", func() {
branch := defkit.WhenParam(defkit.Bool("x").Eq(true)).
Params(defkit.String("secret").Required())
p := defkit.Object("config").ConditionalFields(branch)
Expect(p.GetConditionalFields()).To(HaveLen(1))
Expect(p.GetConditionalFields()[0]).To(Equal(branch))
})
It("should accumulate branches across calls", func() {
b1 := defkit.WhenParam(defkit.Bool("x").Eq(true)).Params(defkit.String("a"))
b2 := defkit.WhenParam(defkit.Bool("x").Eq(false)).Params(defkit.String("b"))
p := defkit.Object("config").
ConditionalFields(b1).
ConditionalFields(b2)
Expect(p.GetConditionalFields()).To(HaveLen(2))
})
It("should return empty when not set", func() {
p := defkit.Object("config")
Expect(p.GetConditionalFields()).To(BeEmpty())
})
It("should chain with other methods", func() {
p := defkit.Object("config").Optional().
WithFields(defkit.String("base")).
ConditionalFields(
defkit.WhenParam(defkit.Bool("x").Eq(true)).Params(defkit.String("extra")),
)
Expect(p.IsOptional()).To(BeTrue())
Expect(p.GetFields()).To(HaveLen(1))
Expect(p.GetConditionalFields()).To(HaveLen(1))
})
})
Context("ClosedStructOption", func() {
It("should create an empty closed struct", func() {
cs := defkit.ClosedStruct()
Expect(cs.GetFields()).To(HaveLen(0))
})
It("should add fields with WithFields", func() {
cs := defkit.ClosedStruct().WithFields(
defkit.Field("name", defkit.ParamTypeString).Required(),
defkit.Field("key", defkit.ParamTypeString).Required(),
)
Expect(cs.GetFields()).To(HaveLen(2))
Expect(cs.GetFields()[0].Name()).To(Equal("name"))
Expect(cs.GetFields()[1].Name()).To(Equal("key"))
})
It("should support nested struct fields", func() {
cs := defkit.ClosedStruct().WithFields(
defkit.Field("secretRef", defkit.ParamTypeStruct).Nested(
defkit.Struct("secretRef").WithFields(
defkit.Field("name", defkit.ParamTypeString).Required(),
defkit.Field("key", defkit.ParamTypeString).Required(),
),
),
)
Expect(cs.GetFields()).To(HaveLen(1))
Expect(cs.GetFields()[0].GetNested()).NotTo(BeNil())
Expect(cs.GetFields()[0].GetNested().GetFields()).To(HaveLen(2))
})
})
})