Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions internal/validators/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ var (
ErrInvalidNamedArgumentName = errors.New("invalid named argument name format")
ErrArgumentValueStartsWithName = errors.New("argument value cannot start with the argument name")
ErrArgumentDefaultStartsWithName = errors.New("argument default cannot start with the argument name")
ErrValueHintOnNamedArgument = errors.New("valueHint is only valid for positional arguments, not named arguments")

// Server name validation errors
ErrMultipleSlashesInServerName = errors.New("server name cannot contain multiple slashes")
Expand Down
14 changes: 14 additions & 0 deletions internal/validators/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,20 @@ func validateArgument(ctx *ValidationContext, obj *model.Argument) *ValidationRe
// Validate value and default don't start with the name
valueResult := validateArgumentValueFields(ctx, obj.Name, obj.Value, obj.Default)
result.Merge(valueResult)

// valueHint names a positional slot for transport URL variable
// substitution, so it only makes sense on positional arguments. The
// schema allows it on named arguments (the union branches don't set
// additionalProperties: false), so reject it here instead.
if obj.ValueHint != "" {
issue := NewValidationIssueFromError(
ValidationIssueTypeSemantic,
ctx.Field("valueHint").String(),
ErrValueHintOnNamedArgument,
"valuehint-on-named-argument",
)
result.AddIssue(issue)
}
}
return result
}
Expand Down
28 changes: 28 additions & 0 deletions internal/validators/validators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1286,6 +1286,34 @@ func TestValidateArgument_InvalidNamedArgumentNames(t *testing.T) {
}
}

func TestValidateArgument_ValueHintOnNamedArgument(t *testing.T) {
// valueHint identifies a positional slot, so a named argument must not carry
// one. The schema misses this (the argument union branches allow extra
// properties), so the semantic validator is the enforcement point.
t.Run("named_with_valueHint_rejected", func(t *testing.T) {
arg := model.Argument{
Type: model.ArgumentTypeNamed,
Name: "--port",
ValueHint: "port_number",
}
server := createValidServerWithArgument(arg)
result := validators.ValidateServerJSON(&server, validators.ValidationSchemaVersionAndSemantic)
assert.Error(t, result.FirstError(), "named argument carrying valueHint should be rejected")
})

// A positional argument with a valueHint stays valid (no regression).
t.Run("positional_with_valueHint_valid", func(t *testing.T) {
arg := model.Argument{
InputWithVariables: model.InputWithVariables{Input: model.Input{Value: "/etc/config.json"}},
Type: model.ArgumentTypePositional,
ValueHint: "config_path",
}
server := createValidServerWithArgument(arg)
result := validators.ValidateServerJSON(&server, validators.ValidationSchemaVersionAndSemantic)
assert.NoError(t, result.FirstError(), "positional argument with valueHint should remain valid")
})
}

func TestValidateArgument_InvalidValueFields(t *testing.T) {
invalidValueCases := []struct {
name string
Expand Down
Loading