From 9aa83b2c0d7bad3f06897ade5291fc6f2541d3e0 Mon Sep 17 00:00:00 2001 From: Michal Witkowski Date: Sun, 30 Aug 2015 18:23:42 +0100 Subject: [PATCH] Validator: add automatic validation based on protobuf extensions. --- gogoproto/gogo.pb.go | 50 ++++ gogoproto/gogo.proto | 7 + gogoproto/helper.go | 10 + plugin/validator/validator.go | 302 +++++++++++++++++++++++++ proto/validator_gogo.go | 38 ++++ protoc-gen-gogo/generator/generator.go | 2 +- test/validator/Makefile | 33 +++ test/validator/validator_proto2.pb.go | 234 +++++++++++++++++++ test/validator/validator_proto2.proto | 54 +++++ test/validator/validator_proto3.pb.go | 167 ++++++++++++++ test/validator/validator_proto3.proto | 50 ++++ test/validator/validator_test.go | 130 +++++++++++ vanity/command/command.go | 2 + 13 files changed, 1078 insertions(+), 1 deletion(-) create mode 100644 plugin/validator/validator.go create mode 100644 proto/validator_gogo.go create mode 100644 test/validator/Makefile create mode 100644 test/validator/validator_proto2.pb.go create mode 100644 test/validator/validator_proto2.proto create mode 100644 test/validator/validator_proto3.pb.go create mode 100644 test/validator/validator_proto3.proto create mode 100644 test/validator/validator_test.go diff --git a/gogoproto/gogo.pb.go b/gogoproto/gogo.pb.go index f654d6e494..9d89a37d45 100644 --- a/gogoproto/gogo.pb.go +++ b/gogoproto/gogo.pb.go @@ -9,6 +9,7 @@ It is generated from these files: gogo.proto It has these top-level messages: + FieldValidator */ package gogoproto @@ -22,6 +23,46 @@ var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +type FieldValidator struct { + Regex *string `protobuf:"bytes,1,opt,name=regex" json:"regex,omitempty"` + IntGt *int64 `protobuf:"varint,2,opt,name=int_gt" json:"int_gt,omitempty"` + IntLt *int64 `protobuf:"varint,3,opt,name=int_lt" json:"int_lt,omitempty"` + MsgExists *bool `protobuf:"varint,4,opt,name=msg_exists" json:"msg_exists,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *FieldValidator) Reset() { *m = FieldValidator{} } +func (m *FieldValidator) String() string { return proto.CompactTextString(m) } +func (*FieldValidator) ProtoMessage() {} + +func (m *FieldValidator) GetRegex() string { + if m != nil && m.Regex != nil { + return *m.Regex + } + return "" +} + +func (m *FieldValidator) GetIntGt() int64 { + if m != nil && m.IntGt != nil { + return *m.IntGt + } + return 0 +} + +func (m *FieldValidator) GetIntLt() int64 { + if m != nil && m.IntLt != nil { + return *m.IntLt + } + return 0 +} + +func (m *FieldValidator) GetMsgExists() bool { + if m != nil && m.MsgExists != nil { + return *m.MsgExists + } + return false +} + var E_GoprotoEnumPrefix = &proto.ExtensionDesc{ ExtendedType: (*google_protobuf.EnumOptions)(nil), ExtensionType: (*bool)(nil), @@ -438,6 +479,14 @@ var E_Casttype = &proto.ExtensionDesc{ Tag: "bytes,65007,opt,name=casttype", } +var E_Validator = &proto.ExtensionDesc{ + ExtendedType: (*google_protobuf.FieldOptions)(nil), + ExtensionType: (*FieldValidator)(nil), + Field: 65008, + Name: "gogoproto.validator", + Tag: "bytes,65008,opt,name=validator", +} + func init() { proto.RegisterExtension(E_GoprotoEnumPrefix) proto.RegisterExtension(E_GoprotoEnumStringer) @@ -491,4 +540,5 @@ func init() { proto.RegisterExtension(E_Jsontag) proto.RegisterExtension(E_Moretags) proto.RegisterExtension(E_Casttype) + proto.RegisterExtension(E_Validator) } diff --git a/gogoproto/gogo.proto b/gogoproto/gogo.proto index 54b7d18a91..f1afd53fd7 100644 --- a/gogoproto/gogo.proto +++ b/gogoproto/gogo.proto @@ -103,5 +103,12 @@ extend google.protobuf.FieldOptions { optional string jsontag = 65005; optional string moretags = 65006; optional string casttype = 65007; + optional FieldValidator validator = 65008; } +message FieldValidator { + optional string regex = 1; + optional int64 int_gt = 2; + optional int64 int_lt = 3; + optional bool msg_exists = 4; +} diff --git a/gogoproto/helper.go b/gogoproto/helper.go index b463d7b6ad..7fb7ce0dd0 100644 --- a/gogoproto/helper.go +++ b/gogoproto/helper.go @@ -111,6 +111,16 @@ func GetMoreTags(field *google_protobuf.FieldDescriptorProto) *string { return nil } +func GetValidator(field *google_protobuf.FieldDescriptorProto) *FieldValidator { + if field.Options != nil { + v, err := proto.GetExtension(field.Options, E_Validator) + if err == nil && v.(*FieldValidator) != nil { + return (v.(*FieldValidator)) + } + } + return nil +} + type EnableFunc func(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool func EnabledGoEnumPrefix(file *google_protobuf.FileDescriptorProto, enum *google_protobuf.EnumDescriptorProto) bool { diff --git a/plugin/validator/validator.go b/plugin/validator/validator.go new file mode 100644 index 0000000000..6b5daa2876 --- /dev/null +++ b/plugin/validator/validator.go @@ -0,0 +1,302 @@ +// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved. +// http://github.com/gogo/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* + +The validator plugin generates a Validate method for each message. +By default, if none of the message's fields are annotated with the gogo validator annotation, it returns a nil error. +In case some of the fields are annotated, the Validate function returns nil upon sucessful validation, or an error +describing why the validation failed. +The Validate method is called recursively for all submessage of the message. + +TODO(michal): ADD COMMENTS. + +Equal is enabled using the following extensions: + + - equal + - equal_all + +While VerboseEqual is enable dusing the following extensions: + + - verbose_equal + - verbose_equal_all + +The equal plugin also generates a test given it is enabled using one of the following extensions: + + - testgen + - testgen_all + +Let us look at: + + github.com/gogo/protobuf/test/example/example.proto + +Btw all the output can be seen at: + + github.com/gogo/protobuf/test/example/* + +The following message: + + + +given to the equal plugin, will generate the following code: + + + +and the following test code: + + +*/ +package validator + +import ( + "fmt" + "os" + + "github.com/gogo/protobuf/gogoproto" + descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" + "github.com/gogo/protobuf/protoc-gen-gogo/generator" +) + +type plugin struct { + *generator.Generator + generator.PluginImports + regexPkg generator.Single + fmtPkg generator.Single + protoPkg generator.Single +} + +func NewPlugin() *plugin { + return &plugin{} +} + +func (p *plugin) Name() string { + return "validator" +} + +func (p *plugin) Init(g *generator.Generator) { + p.Generator = g +} + +func (p *plugin) Generate(file *generator.FileDescriptor) { + p.PluginImports = generator.NewPluginImports(p.Generator) + p.regexPkg = p.NewImport("regexp") + p.fmtPkg = p.NewImport("fmt") + + for _, msg := range file.Messages() { + if msg.DescriptorProto.GetOptions().GetMapEntry() { + continue + } + p.generateRegexVars(file, msg) + if gogoproto.IsProto3(file.FileDescriptorProto) { + p.generateProto3Message(file, msg) + } else { + p.generateProto2Message(file, msg) + } + + } +} + +func (p *plugin) isSupportedInt(field *descriptor.FieldDescriptorProto) bool { + switch *(field.Type) { + case descriptor.FieldDescriptorProto_TYPE_INT32, descriptor.FieldDescriptorProto_TYPE_INT64: + return true + case descriptor.FieldDescriptorProto_TYPE_UINT32, descriptor.FieldDescriptorProto_TYPE_UINT64: + return true + case descriptor.FieldDescriptorProto_TYPE_SINT32, descriptor.FieldDescriptorProto_TYPE_SINT64: + return true + } + return false +} + +func (p *plugin) generateRegexVars(file *generator.FileDescriptor, message *generator.Descriptor) { + ccTypeName := generator.CamelCaseSlice(message.TypeName()) + for _, field := range message.Field { + validator := gogoproto.GetValidator(field) + if validator != nil && validator.Regex != nil { + fieldName := p.GetFieldName(message, field) + p.P(`var `, p.regexName(ccTypeName, fieldName), ` = `, p.regexPkg.Use(), `.MustCompile("`, validator.Regex, `")`) + } + } +} + +func (p *plugin) generateProto2Message(file *generator.FileDescriptor, message *generator.Descriptor) { + ccTypeName := generator.CamelCaseSlice(message.TypeName()) + + p.P(`func (this *`, ccTypeName, `) Validate() error {`) + p.In() + for _, field := range message.Field { + fieldName := p.GetFieldName(message, field) + validator := gogoproto.GetValidator(field) + if validator == nil && !field.IsMessage() { + continue + } + if p.validatorWithMessageExists(validator) { + fmt.Fprintf(os.Stderr, "WARNING: field %v.%v is a proto2 message, validator.msg_exists has no effect\n", ccTypeName, fieldName) + } + variableName := "this." + fieldName + repeated := field.IsRepeated() + nullable := gogoproto.IsNullable(field) + if repeated { + p.P(`for _, item := range `, variableName, `{`) + p.In() + variableName = "item" + } else if nullable { + p.P(`if `, variableName, ` != nil {`) + p.In() + variableName = "*(" + variableName + ")" + } + if field.IsString() { + p.generateStringValidator(variableName, ccTypeName, fieldName, validator) + } else if p.isSupportedInt(field) { + p.generateIntValidator(variableName, ccTypeName, fieldName, validator) + } else if field.IsMessage() { + if repeated && nullable { + variableName = "*(item)" + } + p.P(`if err := proto.CallValidatorIfExists(&(`, variableName, `)); err != nil {`) + p.In() + p.P(`return err`) + p.Out() + p.P(`}`) + } + if repeated { + // end the repeated loop + p.Out() + p.P(`}`) + } else if nullable { + // end the if around nullable + p.Out() + p.P(`}`) + } + } + p.P(`return nil`) + p.Out() + p.P(`}`) +} + +func (p *plugin) generateProto3Message(file *generator.FileDescriptor, message *generator.Descriptor) { + ccTypeName := generator.CamelCaseSlice(message.TypeName()) + p.P(`func (this *`, ccTypeName, `) Validate() error {`) + p.In() + for _, field := range message.Field { + validator := gogoproto.GetValidator(field) + if validator == nil && !field.IsMessage() { + continue + } + fieldName := p.GetFieldName(message, field) + variableName := "this." + fieldName + repeated := field.IsRepeated() + nullable := gogoproto.IsNullable(field) + if repeated { + p.P(`for _, item := range `, variableName, `{`) + p.In() + variableName = "item" + } + if field.IsString() { + p.generateStringValidator(variableName, ccTypeName, fieldName, validator) + } else if p.isSupportedInt(field) { + p.generateIntValidator(variableName, ccTypeName, fieldName, validator) + } else if field.IsMessage() { + if p.validatorWithMessageExists(validator) { + if nullable && !repeated { + p.P(`if nil == `, variableName, `{`) + p.In() + p.P(`return `, p.fmtPkg.Use(), `.Errorf("validation error: `, ccTypeName + "." + fieldName, ` message must exist")`) + p.Out() + p.P(`}`) + } else if repeated { + fmt.Fprintf(os.Stderr, "WARNING: field %v.%v is a repeated, validator.msg_exists has no effect\n", ccTypeName, fieldName) + } else if !nullable { + fmt.Fprintf(os.Stderr, "WARNING: field %v.%v is a nullable=false, validator.msg_exists has no effect\n", ccTypeName, fieldName) + } + } + if nullable { + p.P(`if `, variableName, ` != nil {`) + p.In() + } else { + // non-nullable fields in proto3 store actual structs, we need pointers to operate on interaces + variableName = "&(" + variableName + ")" + } + p.P(`if err := proto.CallValidatorIfExists(`, variableName, `); err != nil {`) + p.In() + p.P(`return err`) + p.Out() + p.P(`}`) + if nullable { + p.Out() + p.P(`}`) + } + } + if repeated { + // end the repeated loop + p.Out() + p.P(`}`) + } + } + p.P(`return nil`) + p.Out() + p.P(`}`) +} + +func (p *plugin) generateIntValidator(variableName string, ccTypeName string, fieldName string, validator *gogoproto.FieldValidator) { + fieldIdentifier := ccTypeName + "." + fieldName + if validator.IntGt != nil { + p.P(`if !(`, variableName, ` > `, validator.IntGt, `){`) + p.In() + p.P(`return `, p.fmtPkg.Use(), `.Errorf("validation error: `, fieldIdentifier, ` must be greater than '`, validator.IntGt, `'")`) + p.Out() + p.P(`}`) + } + if validator.IntLt != nil { + p.P(`if !(`, variableName, ` < `, validator.IntLt, `){`) + p.In() + p.P(`return `, p.fmtPkg.Use(), `.Errorf("validation error: `, fieldIdentifier, ` must be less than '`, validator.IntLt, `'")`) + p.Out() + p.P(`}`) + } +} + +func (p *plugin) generateStringValidator(variableName string, ccTypeName string, fieldName string, validator *gogoproto.FieldValidator) { + fieldIdentifier := ccTypeName + "." + fieldName + if validator.Regex != nil { + p.P(`if !`, p.regexName(ccTypeName, fieldName), `.MatchString(`, variableName, `) {`) + p.In() + p.P(`return `, p.fmtPkg.Use(), `.Errorf("validation error: `, fieldIdentifier, ` must conform to regex '`, validator.Regex, `'")`) + p.Out() + p.P(`}`) + } +} + +func (p *plugin) validatorWithMessageExists(validator *gogoproto.FieldValidator) bool { + return validator != nil && validator.MsgExists != nil && *(validator.MsgExists) +} + +func (p *plugin) regexName(ccTypeName string, fieldName string) string { + return "_regex_" + ccTypeName + "_" + fieldName +} +func init() { + generator.RegisterPlugin(NewPlugin()) +} diff --git a/proto/validator_gogo.go b/proto/validator_gogo.go new file mode 100644 index 0000000000..6c81ae2adb --- /dev/null +++ b/proto/validator_gogo.go @@ -0,0 +1,38 @@ +// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved. +// http://github.com/gogo/protobuf/gogoproto +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +type Validator interface { + Validate() error +} + +func CallValidatorIfExists(candidate interface{}) error { + if validator, ok := candidate.(Validator); ok { + return validator.Validate() + } + return nil +} \ No newline at end of file diff --git a/protoc-gen-gogo/generator/generator.go b/protoc-gen-gogo/generator/generator.go index bc2fb9001f..d95f187b73 100644 --- a/protoc-gen-gogo/generator/generator.go +++ b/protoc-gen-gogo/generator/generator.go @@ -571,7 +571,7 @@ func (g *Generator) CommandLineParameters(parameter string) { if pluginList == "none" { pluginList = "" } - gogoPluginNames := []string{"unmarshal", "unsafeunmarshaler", "union", "stringer", "size", "populate", "marshalto", "unsafemarshaler", "gostring", "face", "equal", "enumstringer", "embedcheck", "description", "defaultcheck", "oneofcheck"} + gogoPluginNames := []string{"unmarshal", "unsafeunmarshaler", "union", "stringer", "size", "populate", "marshalto", "unsafemarshaler", "gostring", "face", "equal", "enumstringer", "embedcheck", "description", "defaultcheck", "oneofcheck", "validator"} pluginList = strings.Join(append(gogoPluginNames, pluginList), "+") if pluginList != "" { // Amend the set of plugins. diff --git a/test/validator/Makefile b/test/validator/Makefile new file mode 100644 index 0000000000..763b042b1d --- /dev/null +++ b/test/validator/Makefile @@ -0,0 +1,33 @@ +# Extensions for Protocol Buffers to create more go like structures. +# +# Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved. +# http://github.com/gogo/protobuf +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +regenerate: + (protoc --proto_path=../../../../../:../../protobuf/:. --gogo_out=. validator_proto2.proto) + (protoc --proto_path=../../../../../:../../protobuf/:. --gogo_out=. validator_proto3.proto) + + diff --git a/test/validator/validator_proto2.pb.go b/test/validator/validator_proto2.pb.go new file mode 100644 index 0000000000..4ff42c7042 --- /dev/null +++ b/test/validator/validator_proto2.pb.go @@ -0,0 +1,234 @@ +// Code generated by protoc-gen-gogo. +// source: validator_proto2.proto +// DO NOT EDIT! + +/* +Package validatortest is a generated protocol buffer package. + +It is generated from these files: + validator_proto2.proto + +It has these top-level messages: + ValidatorMessage +*/ +package validatortest + +import proto "github.com/gogo/protobuf/proto" +import math "math" + +// discarding unused import gogoproto "github.com/gogo/protobuf/gogoproto" + +import regexp "regexp" +import fmt "fmt" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = math.Inf + +type ValidatorMessage struct { + StringReq *string `protobuf:"bytes,1,req" json:"StringReq,omitempty"` + StringReqNonNull string `protobuf:"bytes,2,req" json:"StringReqNonNull"` + StringOpt *string `protobuf:"bytes,3,opt" json:"StringOpt,omitempty"` + StringOptNonNull string `protobuf:"bytes,4,opt" json:"StringOptNonNull"` + IntReq *uint32 `protobuf:"varint,6,req" json:"IntReq,omitempty"` + IntReqNonNull uint32 `protobuf:"varint,7,req" json:"IntReqNonNull"` + IntRep []uint32 `protobuf:"varint,8,rep" json:"IntRep,omitempty"` + IntRepNonNull []uint32 `protobuf:"varint,9,rep" json:"IntRepNonNull,omitempty"` + EmbeddedReq *ValidatorMessage_Embedded `protobuf:"bytes,10,req,name=embeddedReq" json:"embeddedReq,omitempty"` + EmbeddedNonNull ValidatorMessage_Embedded `protobuf:"bytes,11,req,name=embeddedNonNull" json:"embeddedNonNull"` + EmbeddedRep []*ValidatorMessage_Embedded `protobuf:"bytes,12,rep,name=embeddedRep" json:"embeddedRep,omitempty"` + EmbeddedRepNonNullable []ValidatorMessage_Embedded `protobuf:"bytes,13,rep,name=embeddedRepNonNullable" json:"embeddedRepNonNullable"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ValidatorMessage) Reset() { *m = ValidatorMessage{} } +func (m *ValidatorMessage) String() string { return proto.CompactTextString(m) } +func (*ValidatorMessage) ProtoMessage() {} + +func (m *ValidatorMessage) GetStringReq() string { + if m != nil && m.StringReq != nil { + return *m.StringReq + } + return "" +} + +func (m *ValidatorMessage) GetStringReqNonNull() string { + if m != nil { + return m.StringReqNonNull + } + return "" +} + +func (m *ValidatorMessage) GetStringOpt() string { + if m != nil && m.StringOpt != nil { + return *m.StringOpt + } + return "" +} + +func (m *ValidatorMessage) GetStringOptNonNull() string { + if m != nil { + return m.StringOptNonNull + } + return "" +} + +func (m *ValidatorMessage) GetIntReq() uint32 { + if m != nil && m.IntReq != nil { + return *m.IntReq + } + return 0 +} + +func (m *ValidatorMessage) GetIntReqNonNull() uint32 { + if m != nil { + return m.IntReqNonNull + } + return 0 +} + +func (m *ValidatorMessage) GetIntRep() []uint32 { + if m != nil { + return m.IntRep + } + return nil +} + +func (m *ValidatorMessage) GetIntRepNonNull() []uint32 { + if m != nil { + return m.IntRepNonNull + } + return nil +} + +func (m *ValidatorMessage) GetEmbeddedReq() *ValidatorMessage_Embedded { + if m != nil { + return m.EmbeddedReq + } + return nil +} + +func (m *ValidatorMessage) GetEmbeddedNonNull() ValidatorMessage_Embedded { + if m != nil { + return m.EmbeddedNonNull + } + return ValidatorMessage_Embedded{} +} + +func (m *ValidatorMessage) GetEmbeddedRep() []*ValidatorMessage_Embedded { + if m != nil { + return m.EmbeddedRep + } + return nil +} + +func (m *ValidatorMessage) GetEmbeddedRepNonNullable() []ValidatorMessage_Embedded { + if m != nil { + return m.EmbeddedRepNonNullable + } + return nil +} + +type ValidatorMessage_Embedded struct { + Identifier *string `protobuf:"bytes,1,opt" json:"Identifier,omitempty"` + SomeValue *int64 `protobuf:"varint,2,req" json:"SomeValue,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *ValidatorMessage_Embedded) Reset() { *m = ValidatorMessage_Embedded{} } +func (m *ValidatorMessage_Embedded) String() string { return proto.CompactTextString(m) } +func (*ValidatorMessage_Embedded) ProtoMessage() {} + +func (m *ValidatorMessage_Embedded) GetIdentifier() string { + if m != nil && m.Identifier != nil { + return *m.Identifier + } + return "" +} + +func (m *ValidatorMessage_Embedded) GetSomeValue() int64 { + if m != nil && m.SomeValue != nil { + return *m.SomeValue + } + return 0 +} + +var _regex_ValidatorMessage_StringReq = regexp.MustCompile("^.{2,5}$") +var _regex_ValidatorMessage_StringReqNonNull = regexp.MustCompile("^.{2,5}$") +var _regex_ValidatorMessage_StringOpt = regexp.MustCompile("^.{2,5}$") +var _regex_ValidatorMessage_StringOptNonNull = regexp.MustCompile("^.{2,5}$") + +func (this *ValidatorMessage) Validate() error { + if this.StringReq != nil { + if !_regex_ValidatorMessage_StringReq.MatchString(*(this.StringReq)) { + return fmt.Errorf("validation error: ValidatorMessage.StringReq must conform to regex '^.{2,5}$'") + } + } + if !_regex_ValidatorMessage_StringReqNonNull.MatchString(this.StringReqNonNull) { + return fmt.Errorf("validation error: ValidatorMessage.StringReqNonNull must conform to regex '^.{2,5}$'") + } + if this.StringOpt != nil { + if !_regex_ValidatorMessage_StringOpt.MatchString(*(this.StringOpt)) { + return fmt.Errorf("validation error: ValidatorMessage.StringOpt must conform to regex '^.{2,5}$'") + } + } + if !_regex_ValidatorMessage_StringOptNonNull.MatchString(this.StringOptNonNull) { + return fmt.Errorf("validation error: ValidatorMessage.StringOptNonNull must conform to regex '^.{2,5}$'") + } + if this.IntReq != nil { + if !(*(this.IntReq) > 10) { + return fmt.Errorf("validation error: ValidatorMessage.IntReq must be greater than '10'") + } + } + if !(this.IntReqNonNull > 0) { + return fmt.Errorf("validation error: ValidatorMessage.IntReqNonNull must be greater than '0'") + } + for _, item := range this.IntRep { + if !(item > 10) { + return fmt.Errorf("validation error: ValidatorMessage.IntRep must be greater than '10'") + } + } + for _, item := range this.IntRepNonNull { + if !(item > 0) { + return fmt.Errorf("validation error: ValidatorMessage.IntRepNonNull must be greater than '0'") + } + } + if this.EmbeddedReq != nil { + if err := proto.CallValidatorIfExists(&(*(this.EmbeddedReq))); err != nil { + return err + } + } + if err := proto.CallValidatorIfExists(&(this.EmbeddedNonNull)); err != nil { + return err + } + for _, item := range this.EmbeddedRep { + if err := proto.CallValidatorIfExists(&(*(item))); err != nil { + return err + } + } + for _, item := range this.EmbeddedRepNonNullable { + if err := proto.CallValidatorIfExists(&(item)); err != nil { + return err + } + } + return nil +} + +var _regex_ValidatorMessage_Embedded_Identifier = regexp.MustCompile("^[a-z]{2,5}$") + +func (this *ValidatorMessage_Embedded) Validate() error { + if this.Identifier != nil { + if !_regex_ValidatorMessage_Embedded_Identifier.MatchString(*(this.Identifier)) { + return fmt.Errorf("validation error: ValidatorMessage_Embedded.Identifier must conform to regex '^[a-z]{2,5}$'") + } + } + if this.SomeValue != nil { + if !(*(this.SomeValue) > 0) { + return fmt.Errorf("validation error: ValidatorMessage_Embedded.SomeValue must be greater than '0'") + } + if !(*(this.SomeValue) < 100) { + return fmt.Errorf("validation error: ValidatorMessage_Embedded.SomeValue must be less than '100'") + } + } + return nil +} diff --git a/test/validator/validator_proto2.proto b/test/validator/validator_proto2.proto new file mode 100644 index 0000000000..efde71cbf9 --- /dev/null +++ b/test/validator/validator_proto2.proto @@ -0,0 +1,54 @@ +// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved. +// http://github.com/gogo/protobuf/gogoproto +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto2"; +package validatortest; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +message ValidatorMessage { + message Embedded { + optional string Identifier = 1 [(gogoproto.validator) = {regex: "^[a-z]{2,5}$"}]; + required int64 SomeValue = 2 [(gogoproto.validator) = {int_gt: 0, int_lt: 100}]; + } + + required string StringReq = 1 [(gogoproto.validator) = {regex: "^.{2,5}$"}]; + required string StringReqNonNull = 2 [(gogoproto.validator) = {regex: "^.{2,5}$"}, (gogoproto.nullable) = false]; + optional string StringOpt = 3 [(gogoproto.validator) = {regex: "^.{2,5}$"}]; + optional string StringOptNonNull = 4 [(gogoproto.validator) = {regex: "^.{2,5}$"}, (gogoproto.nullable) = false]; + + required uint32 IntReq = 6 [(gogoproto.validator) = {int_gt: 10}]; + required uint32 IntReqNonNull = 7 [(gogoproto.validator) = {int_gt: 0}, (gogoproto.nullable) = false]; + repeated uint32 IntRep = 8 [(gogoproto.validator) = {int_gt: 10}]; + repeated uint32 IntRepNonNull = 9 [(gogoproto.validator) = {int_gt: 0}, (gogoproto.nullable) = false]; + + + required Embedded embeddedReq = 10; + required Embedded embeddedNonNull = 11 [(gogoproto.nullable) = false]; + repeated Embedded embeddedRep = 12; + repeated Embedded embeddedRepNonNullable = 13 [(gogoproto.nullable) = false]; + +} \ No newline at end of file diff --git a/test/validator/validator_proto3.pb.go b/test/validator/validator_proto3.pb.go new file mode 100644 index 0000000000..5418e5c7ee --- /dev/null +++ b/test/validator/validator_proto3.pb.go @@ -0,0 +1,167 @@ +// Code generated by protoc-gen-gogo. +// source: validator_proto3.proto +// DO NOT EDIT! + +/* +Package validatortest is a generated protocol buffer package. + +It is generated from these files: + validator_proto3.proto + +It has these top-level messages: + ValidatorMessage3 +*/ +package validatortest + +import proto "github.com/gogo/protobuf/proto" + +// discarding unused import gogoproto "github.com/gogo/protobuf/gogoproto" + +import regexp "regexp" +import fmt "fmt" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal + +type ValidatorMessage3 struct { + SomeString string `protobuf:"bytes,1,opt,proto3" json:"SomeString,omitempty"` + SomeStringRep []string `protobuf:"bytes,2,rep" json:"SomeStringRep,omitempty"` + SomeInt uint32 `protobuf:"varint,6,opt,proto3" json:"SomeInt,omitempty"` + SomeIntRep []uint32 `protobuf:"varint,7,rep" json:"SomeIntRep,omitempty"` + SomeIntRepNonNull []uint32 `protobuf:"varint,8,rep" json:"SomeIntRepNonNull,omitempty"` + SomeEmbedded *ValidatorMessage3_Embedded `protobuf:"bytes,10,opt,name=someEmbedded" json:"someEmbedded,omitempty"` + SomeEmbeddedNonNullable ValidatorMessage3_Embedded `protobuf:"bytes,11,opt,name=someEmbeddedNonNullable" json:"someEmbeddedNonNullable"` + SomeEmbeddedExists *ValidatorMessage3_Embedded `protobuf:"bytes,12,opt,name=someEmbeddedExists" json:"someEmbeddedExists,omitempty"` + SomeEmbeddedExistsNonNullable ValidatorMessage3_Embedded `protobuf:"bytes,13,opt,name=someEmbeddedExistsNonNullable" json:"someEmbeddedExistsNonNullable"` + SomeEmbeddedRep []*ValidatorMessage3_Embedded `protobuf:"bytes,14,rep,name=someEmbeddedRep" json:"someEmbeddedRep,omitempty"` + SomeEmbeddedRepNonNullable []ValidatorMessage3_Embedded `protobuf:"bytes,15,rep,name=someEmbeddedRepNonNullable" json:"someEmbeddedRepNonNullable"` +} + +func (m *ValidatorMessage3) Reset() { *m = ValidatorMessage3{} } +func (m *ValidatorMessage3) String() string { return proto.CompactTextString(m) } +func (*ValidatorMessage3) ProtoMessage() {} + +func (m *ValidatorMessage3) GetSomeEmbedded() *ValidatorMessage3_Embedded { + if m != nil { + return m.SomeEmbedded + } + return nil +} + +func (m *ValidatorMessage3) GetSomeEmbeddedNonNullable() ValidatorMessage3_Embedded { + if m != nil { + return m.SomeEmbeddedNonNullable + } + return ValidatorMessage3_Embedded{} +} + +func (m *ValidatorMessage3) GetSomeEmbeddedExists() *ValidatorMessage3_Embedded { + if m != nil { + return m.SomeEmbeddedExists + } + return nil +} + +func (m *ValidatorMessage3) GetSomeEmbeddedExistsNonNullable() ValidatorMessage3_Embedded { + if m != nil { + return m.SomeEmbeddedExistsNonNullable + } + return ValidatorMessage3_Embedded{} +} + +func (m *ValidatorMessage3) GetSomeEmbeddedRep() []*ValidatorMessage3_Embedded { + if m != nil { + return m.SomeEmbeddedRep + } + return nil +} + +func (m *ValidatorMessage3) GetSomeEmbeddedRepNonNullable() []ValidatorMessage3_Embedded { + if m != nil { + return m.SomeEmbeddedRepNonNullable + } + return nil +} + +type ValidatorMessage3_Embedded struct { + Identifier string `protobuf:"bytes,1,opt,proto3" json:"Identifier,omitempty"` + SomeValue int64 `protobuf:"varint,2,opt,proto3" json:"SomeValue,omitempty"` +} + +func (m *ValidatorMessage3_Embedded) Reset() { *m = ValidatorMessage3_Embedded{} } +func (m *ValidatorMessage3_Embedded) String() string { return proto.CompactTextString(m) } +func (*ValidatorMessage3_Embedded) ProtoMessage() {} + +var _regex_ValidatorMessage3_SomeString = regexp.MustCompile("^.{2,5}$") +var _regex_ValidatorMessage3_SomeStringRep = regexp.MustCompile("^.{2,5}$") + +func (this *ValidatorMessage3) Validate() error { + if !_regex_ValidatorMessage3_SomeString.MatchString(this.SomeString) { + return fmt.Errorf("validation error: ValidatorMessage3.SomeString must conform to regex '^.{2,5}$'") + } + for _, item := range this.SomeStringRep { + if !_regex_ValidatorMessage3_SomeStringRep.MatchString(item) { + return fmt.Errorf("validation error: ValidatorMessage3.SomeStringRep must conform to regex '^.{2,5}$'") + } + } + if !(this.SomeInt > 10) { + return fmt.Errorf("validation error: ValidatorMessage3.SomeInt must be greater than '10'") + } + for _, item := range this.SomeIntRep { + if !(item > 10) { + return fmt.Errorf("validation error: ValidatorMessage3.SomeIntRep must be greater than '10'") + } + } + for _, item := range this.SomeIntRepNonNull { + if !(item > 10) { + return fmt.Errorf("validation error: ValidatorMessage3.SomeIntRepNonNull must be greater than '10'") + } + } + if this.SomeEmbedded != nil { + if err := proto.CallValidatorIfExists(this.SomeEmbedded); err != nil { + return err + } + } + if err := proto.CallValidatorIfExists(&(this.SomeEmbeddedNonNullable)); err != nil { + return err + } + if nil == this.SomeEmbeddedExists { + return fmt.Errorf("validation error: ValidatorMessage3.SomeEmbeddedExists message must exist") + } + if this.SomeEmbeddedExists != nil { + if err := proto.CallValidatorIfExists(this.SomeEmbeddedExists); err != nil { + return err + } + } + if err := proto.CallValidatorIfExists(&(this.SomeEmbeddedExistsNonNullable)); err != nil { + return err + } + for _, item := range this.SomeEmbeddedRep { + if item != nil { + if err := proto.CallValidatorIfExists(item); err != nil { + return err + } + } + } + for _, item := range this.SomeEmbeddedRepNonNullable { + if err := proto.CallValidatorIfExists(&(item)); err != nil { + return err + } + } + return nil +} + +var _regex_ValidatorMessage3_Embedded_Identifier = regexp.MustCompile("^[a-z]{2,5}$") + +func (this *ValidatorMessage3_Embedded) Validate() error { + if !_regex_ValidatorMessage3_Embedded_Identifier.MatchString(this.Identifier) { + return fmt.Errorf("validation error: ValidatorMessage3_Embedded.Identifier must conform to regex '^[a-z]{2,5}$'") + } + if !(this.SomeValue > 0) { + return fmt.Errorf("validation error: ValidatorMessage3_Embedded.SomeValue must be greater than '0'") + } + if !(this.SomeValue < 100) { + return fmt.Errorf("validation error: ValidatorMessage3_Embedded.SomeValue must be less than '100'") + } + return nil +} diff --git a/test/validator/validator_proto3.proto b/test/validator/validator_proto3.proto new file mode 100644 index 0000000000..701bd551b7 --- /dev/null +++ b/test/validator/validator_proto3.proto @@ -0,0 +1,50 @@ +// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved. +// http://github.com/gogo/protobuf/gogoproto +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; +package validatortest; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +message ValidatorMessage3 { + message Embedded { + string Identifier = 1 [(gogoproto.validator) = {regex: "^[a-z]{2,5}$"}]; + int64 SomeValue = 2 [(gogoproto.validator) = {int_gt: 0, int_lt: 100}]; + } + string SomeString = 1 [(gogoproto.validator) = {regex: "^.{2,5}$"}]; + repeated string SomeStringRep = 2 [(gogoproto.validator) = {regex: "^.{2,5}$"}]; + + uint32 SomeInt = 6 [(gogoproto.validator) = {int_gt: 10}]; + repeated uint32 SomeIntRep = 7 [(gogoproto.validator) = {int_gt: 10}]; + repeated uint32 SomeIntRepNonNull = 8 [(gogoproto.validator) = {int_gt: 10}, (gogoproto.nullable) = false]; + + Embedded someEmbedded = 10; + Embedded someEmbeddedNonNullable = 11 [(gogoproto.nullable) = false]; + Embedded someEmbeddedExists = 12 [(gogoproto.validator) = {msg_exists : true}]; + Embedded someEmbeddedExistsNonNullable = 13 [(gogoproto.nullable) = false, (gogoproto.validator) = {msg_exists:true}]; + repeated Embedded someEmbeddedRep = 14; + repeated Embedded someEmbeddedRepNonNullable = 15 [(gogoproto.nullable) = false]; +} diff --git a/test/validator/validator_test.go b/test/validator/validator_test.go new file mode 100644 index 0000000000..c806ec5a3e --- /dev/null +++ b/test/validator/validator_test.go @@ -0,0 +1,130 @@ +package validatortest + +import "testing" + +func buildProto3(someString string, someInt uint32, identifier string, someValue int64) *ValidatorMessage3 { + goodEmbeddedProto3 := &ValidatorMessage3_Embedded{ + Identifier: identifier, + SomeValue: someValue, + } + + goodProto3 := &ValidatorMessage3{ + SomeString: someString, + SomeStringRep: []string{someString, "xyz34"}, + SomeInt: someInt, + SomeIntRep: []uint32{someInt, 12, 13, 14, 15, 16}, + SomeIntRepNonNull: []uint32{someInt, 102}, + SomeEmbedded: nil, + SomeEmbeddedNonNullable: *goodEmbeddedProto3, + SomeEmbeddedExists: goodEmbeddedProto3, + SomeEmbeddedExistsNonNullable: *goodEmbeddedProto3, + SomeEmbeddedRep: []*ValidatorMessage3_Embedded{goodEmbeddedProto3}, + SomeEmbeddedRepNonNullable: []ValidatorMessage3_Embedded{*goodEmbeddedProto3}, + } + return goodProto3 +} + +func buildProto2(someString string, someInt uint32, identifier string, someValue int64) *ValidatorMessage { + goodEmbeddedProto2 := &ValidatorMessage_Embedded{ + Identifier: &identifier, + SomeValue: &someValue, + } + + goodProto2 := &ValidatorMessage{ + StringReq: &someString, + StringReqNonNull: someString, + + StringOpt: nil, + StringOptNonNull: someString, + + IntReq: &someInt, + IntReqNonNull: someInt, + IntRep: []uint32{someInt, 12, 13, 14, 15, 16}, + IntRepNonNull: []uint32{someInt, 12, 13, 14, 15, 16}, + + EmbeddedReq: goodEmbeddedProto2, + EmbeddedNonNull: *goodEmbeddedProto2, + EmbeddedRep: []*ValidatorMessage_Embedded{goodEmbeddedProto2}, + EmbeddedRepNonNullable: []ValidatorMessage_Embedded{*goodEmbeddedProto2}, + } + return goodProto2 +} + +func TestGoodProto3(t *testing.T) { + var err error + goodProto3 := buildProto3("-%ab", 11, "abba", 99) + err = goodProto3.Validate() + if err != nil { + t.Fatalf("unexpected fail in validator: %v", err) + } +} + +func TestGoodProto2(t *testing.T) { + var err error + goodProto2 := buildProto2("-%ab", 11, "abba", 99) + err = goodProto2.Validate() + if err != nil { + t.Fatalf("unexpected fail in validator: %v", err) + } +} + +func TestStringRegex(t *testing.T) { + tooLong1Proto3 := buildProto3("toolong", 11, "abba", 99) + if tooLong1Proto3.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } + tooLong2Proto3 := buildProto3("-%ab", 11, "bad#", 99) + if tooLong2Proto3.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } + tooLong1Proto2 := buildProto2("toolong", 11, "abba", 99) + if tooLong1Proto2.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } + tooLong2Proto2 := buildProto2("-%ab", 11, "bad#", 99) + if tooLong2Proto2.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } +} + +func TestIntLowerBounds(t *testing.T) { + lowerThan10Proto3 := buildProto3("-%ab", 9, "abba", 99) + if lowerThan10Proto3.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } + lowerThan10Proto2 := buildProto2("-%ab", 9, "abba", 99) + if lowerThan10Proto2.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } + lowerThan0Proto3 := buildProto3("-%ab", 11, "abba", -1) + if lowerThan0Proto3.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } + lowerThan0Proto2 := buildProto2("-%ab", 11, "abba", -1) + if lowerThan0Proto2.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } +} + +func TestIntUpperBounds(t *testing.T) { + higherThan100Proto3 := buildProto3("-%ab", 11, "abba", 101) + if higherThan100Proto3.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } + higherThan100Proto2 := buildProto2("-%ab", 11, "abba", 101) + if higherThan100Proto2.Validate() == nil { + t.Fatalf("expected fail in validator, but it didn't happen") + } +} + +func TestMsgExist(t *testing.T) { + someProto3 := buildProto3("-%ab", 11, "abba", 99) + someProto3.SomeEmbedded = nil + if err := someProto3.Validate(); err != nil { + t.Fatalf("valiate shoudlnt fail on missing SomeEmbedded, not annotated") + } + someProto3.SomeEmbeddedExists = nil + if err := someProto3.Validate(); err == nil { + t.Fatalf("expected fail due to lacking SomeEmbeddedExists") + } +} diff --git a/vanity/command/command.go b/vanity/command/command.go index 2c6191477a..a5f53433e7 100644 --- a/vanity/command/command.go +++ b/vanity/command/command.go @@ -52,6 +52,8 @@ import ( _ "github.com/gogo/protobuf/plugin/stringer" _ "github.com/gogo/protobuf/plugin/union" _ "github.com/gogo/protobuf/plugin/unmarshal" + _ "github.com/gogo/protobuf/plugin/validator" + "github.com/gogo/protobuf/plugin/testgen"