github.com/syumai/protoreflect@v1.7.1-0.20200810020253-2ac7e3b3a321/desc/protoparse/validate_test.go (about) 1 package protoparse 2 3 import ( 4 "github.com/syumai/protoreflect/internal/testutil" 5 "strings" 6 "testing" 7 ) 8 9 func TestBasicValidation(t *testing.T) { 10 testCases := []struct { 11 contents string 12 succeeds bool 13 errMsg string 14 }{ 15 { 16 contents: `message Foo { optional double bar = 1 [default = -18446744073709551615]; }`, 17 succeeds: true, 18 }, 19 { 20 // with byte order marker 21 contents: string([]byte{0xEF, 0xBB, 0xBF}) + `message Foo { optional double bar = 1 [default = -18446744073709551615]; }`, 22 succeeds: true, 23 }, 24 { 25 contents: `message Foo { optional double bar = 1 [default = 18446744073709551616]; }`, 26 succeeds: true, 27 }, 28 { 29 contents: `message Foo { optional double bar = 536870912; option message_set_wire_format = true; }`, 30 succeeds: true, 31 }, 32 { 33 contents: `message Foo { oneof bar { group Baz = 1 [deprecated=true] { optional int abc = 1; } } }`, 34 succeeds: true, 35 }, 36 { 37 contents: `syntax = "proto1";`, 38 errMsg: `test.proto:1:10: syntax value must be "proto2" or "proto3"`, 39 }, 40 { 41 contents: `message Foo { optional string s = 5000000000; }`, 42 errMsg: `test.proto:1:35: tag number 5000000000 is higher than max allowed tag number (536870911)`, 43 }, 44 { 45 contents: `message Foo { optional string s = 19500; }`, 46 errMsg: `test.proto:1:35: tag number 19500 is in disallowed reserved range 19000-19999`, 47 }, 48 { 49 contents: `enum Foo { V = 5000000000; }`, 50 errMsg: `test.proto:1:16: value 5000000000 is out of range: should be between -2147483648 and 2147483647`, 51 }, 52 { 53 contents: `enum Foo { V = -5000000000; }`, 54 errMsg: `test.proto:1:16: value -5000000000 is out of range: should be between -2147483648 and 2147483647`, 55 }, 56 { 57 contents: `enum Foo { V = 0; reserved 5000000000; }`, 58 errMsg: `test.proto:1:28: range start 5000000000 is out of range: should be between -2147483648 and 2147483647`, 59 }, 60 { 61 contents: `enum Foo { V = 0; reserved -5000000000; }`, 62 errMsg: `test.proto:1:28: range start -5000000000 is out of range: should be between -2147483648 and 2147483647`, 63 }, 64 { 65 contents: `enum Foo { V = 0; reserved 5000000000 to 5000000001; }`, 66 errMsg: `test.proto:1:28: range start 5000000000 is out of range: should be between -2147483648 and 2147483647`, 67 }, 68 { 69 contents: `enum Foo { V = 0; reserved 5 to 5000000000; }`, 70 errMsg: `test.proto:1:33: range end 5000000000 is out of range: should be between -2147483648 and 2147483647`, 71 }, 72 { 73 contents: `enum Foo { V = 0; reserved -5000000000 to -5; }`, 74 errMsg: `test.proto:1:28: range start -5000000000 is out of range: should be between -2147483648 and 2147483647`, 75 }, 76 { 77 contents: `enum Foo { V = 0; reserved -5000000001 to -5000000000; }`, 78 errMsg: `test.proto:1:28: range start -5000000001 is out of range: should be between -2147483648 and 2147483647`, 79 }, 80 { 81 contents: `enum Foo { V = 0; reserved -5000000000 to 5; }`, 82 errMsg: `test.proto:1:28: range start -5000000000 is out of range: should be between -2147483648 and 2147483647`, 83 }, 84 { 85 contents: `enum Foo { V = 0; reserved -5 to 5000000000; }`, 86 errMsg: `test.proto:1:34: range end 5000000000 is out of range: should be between -2147483648 and 2147483647`, 87 }, 88 { 89 contents: `enum Foo { }`, 90 errMsg: `test.proto:1:1: enum Foo: enums must define at least one value`, 91 }, 92 { 93 contents: `message Foo { oneof Bar { } }`, 94 errMsg: `test.proto:1:15: oneof must contain at least one field`, 95 }, 96 { 97 contents: `message Foo { extensions 1 to max; } extend Foo { }`, 98 errMsg: `test.proto:1:38: extend sections must define at least one extension`, 99 }, 100 { 101 contents: `message Foo { option map_entry = true; }`, 102 errMsg: `test.proto:1:34: message Foo: map_entry option should not be set explicitly; use map type instead`, 103 }, 104 { 105 contents: `message Foo { option map_entry = false; }`, 106 succeeds: true, // okay if explicit setting is false 107 }, 108 { 109 contents: `syntax = "proto2"; message Foo { string s = 1; }`, 110 errMsg: `test.proto:1:41: field Foo.s: field has no label; proto2 requires explicit 'optional' label`, 111 }, 112 { 113 contents: `message Foo { string s = 1; }`, // syntax defaults to proto2 114 errMsg: `test.proto:1:22: field Foo.s: field has no label; proto2 requires explicit 'optional' label`, 115 }, 116 { 117 contents: `syntax = "proto3"; message Foo { optional string s = 1; }`, 118 succeeds: true, // proto3_optional 119 }, 120 { 121 contents: `syntax = "proto3"; import "google/protobuf/descriptor.proto"; extend google.protobuf.MessageOptions { optional string s = 50000; }`, 122 errMsg: `test.proto:1:103: field s: label 'optional' is not allowed on extensions in proto3`, 123 }, 124 { 125 contents: `syntax = "proto3"; message Foo { required string s = 1; }`, 126 errMsg: `test.proto:1:34: field Foo.s: label 'required' is not allowed in proto3`, 127 }, 128 { 129 contents: `message Foo { extensions 1 to max; } extend Foo { required string sss = 100; }`, 130 errMsg: `test.proto:1:51: field sss: extension fields cannot be 'required'`, 131 }, 132 { 133 contents: `syntax = "proto3"; message Foo { optional group Grp = 1 { } }`, 134 errMsg: `test.proto:1:43: field Foo.grp: groups are not allowed in proto3`, 135 }, 136 { 137 contents: `syntax = "proto3"; message Foo { extensions 1 to max; }`, 138 errMsg: `test.proto:1:45: message Foo: extension ranges are not allowed in proto3`, 139 }, 140 { 141 contents: `syntax = "proto3"; message Foo { string s = 1 [default = "abcdef"]; }`, 142 errMsg: `test.proto:1:48: field Foo.s: default values are not allowed in proto3`, 143 }, 144 { 145 contents: `enum Foo { V1 = 1; V2 = 1; }`, 146 errMsg: `test.proto:1:25: enum Foo: values V1 and V2 both have the same numeric value 1; use allow_alias option if intentional`, 147 }, 148 { 149 contents: `enum Foo { option allow_alias = true; V1 = 1; V2 = 1; }`, 150 succeeds: true, 151 }, 152 { 153 contents: `syntax = "proto3"; enum Foo { V1 = 0; reserved 1 to 20; reserved "V2"; }`, 154 succeeds: true, 155 }, 156 { 157 contents: `enum Foo { V1 = 1; reserved 1 to 20; reserved "V2"; }`, 158 errMsg: `test.proto:1:17: enum Foo: value V1 is using number 1 which is in reserved range 1 to 20`, 159 }, 160 { 161 contents: `enum Foo { V1 = 20; reserved 1 to 20; reserved "V2"; }`, 162 errMsg: `test.proto:1:17: enum Foo: value V1 is using number 20 which is in reserved range 1 to 20`, 163 }, 164 { 165 contents: `enum Foo { V2 = 0; reserved 1 to 20; reserved "V2"; }`, 166 errMsg: `test.proto:1:12: enum Foo: value V2 is using a reserved name`, 167 }, 168 { 169 contents: `enum Foo { V0 = 0; reserved 1 to 20; reserved 21 to 40; reserved "V2"; }`, 170 succeeds: true, 171 }, 172 { 173 contents: `enum Foo { V0 = 0; reserved 1 to 20; reserved 20 to 40; reserved "V2"; }`, 174 errMsg: `test.proto:1:47: enum Foo: reserved ranges overlap: 1 to 20 and 20 to 40`, 175 }, 176 { 177 contents: `syntax = "proto3"; enum Foo { FIRST = 1; }`, 178 errMsg: `test.proto:1:39: enum Foo: proto3 requires that first value in enum have numeric value of 0`, 179 }, 180 { 181 contents: `syntax = "proto3"; message Foo { string s = 1; int32 i = 1; }`, 182 errMsg: `test.proto:1:58: message Foo: fields s and i both have the same tag 1`, 183 }, 184 { 185 contents: `message Foo { reserved 1 to 10, 10 to 12; }`, 186 errMsg: `test.proto:1:33: message Foo: reserved ranges overlap: 1 to 10 and 10 to 12`, 187 }, 188 { 189 contents: `message Foo { extensions 1 to 10, 10 to 12; }`, 190 errMsg: `test.proto:1:35: message Foo: extension ranges overlap: 1 to 10 and 10 to 12`, 191 }, 192 { 193 contents: `message Foo { reserved 1 to 10; extensions 10 to 12; }`, 194 errMsg: `test.proto:1:44: message Foo: extension range 10 to 12 overlaps reserved range 1 to 10`, 195 }, 196 { 197 contents: `message Foo { reserved 1, 2 to 10, 11 to 20; extensions 21 to 22; }`, 198 succeeds: true, 199 }, 200 { 201 contents: `message Foo { reserved 10 to 1; }`, 202 errMsg: `test.proto:1:24: range, 10 to 1, is invalid: start must be <= end`, 203 }, 204 { 205 contents: `message Foo { extensions 10 to 1; }`, 206 errMsg: `test.proto:1:26: range, 10 to 1, is invalid: start must be <= end`, 207 }, 208 { 209 contents: `message Foo { reserved 1 to 5000000000; }`, 210 errMsg: `test.proto:1:29: range end 5000000000 is out of range: should be between 0 and 536870911`, 211 }, 212 { 213 contents: `message Foo { extensions 3000000000; }`, 214 errMsg: `test.proto:1:26: range start 3000000000 is out of range: should be between 0 and 536870911`, 215 }, 216 { 217 contents: `message Foo { extensions 3000000000 to 3000000001; }`, 218 errMsg: `test.proto:1:26: range start 3000000000 is out of range: should be between 0 and 536870911`, 219 }, 220 { 221 contents: `message Foo { extensions 100 to 3000000000; }`, 222 errMsg: `test.proto:1:33: range end 3000000000 is out of range: should be between 0 and 536870911`, 223 }, 224 { 225 contents: `message Foo { reserved "foo", "foo"; }`, 226 errMsg: `test.proto:1:31: name "foo" is reserved multiple times`, 227 }, 228 { 229 contents: `message Foo { reserved "foo"; reserved "foo"; }`, 230 errMsg: `test.proto:1:40: name "foo" is reserved multiple times`, 231 }, 232 { 233 contents: `message Foo { reserved "foo"; optional string foo = 1; }`, 234 errMsg: `test.proto:1:47: message Foo: field foo is using a reserved name`, 235 }, 236 { 237 contents: `message Foo { reserved 1 to 10; optional string foo = 1; }`, 238 errMsg: `test.proto:1:55: message Foo: field foo is using tag 1 which is in reserved range 1 to 10`, 239 }, 240 { 241 contents: `message Foo { extensions 1 to 10; optional string foo = 1; }`, 242 errMsg: `test.proto:1:57: message Foo: field foo is using tag 1 which is in extension range 1 to 10`, 243 }, 244 { 245 contents: `message Foo { optional group foo = 1 { } }`, 246 errMsg: `test.proto:1:30: group foo should have a name that starts with a capital letter`, 247 }, 248 { 249 contents: `message Foo { oneof foo { group bar = 1 { } } }`, 250 errMsg: `test.proto:1:33: group bar should have a name that starts with a capital letter`, 251 }, 252 { 253 contents: ``, 254 succeeds: true, 255 }, 256 { 257 contents: `0`, 258 errMsg: `test.proto:1:1: syntax error: unexpected int literal`, 259 }, 260 { 261 contents: `foobar`, 262 errMsg: `test.proto:1:1: syntax error: unexpected identifier`, 263 }, 264 { 265 contents: `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`, 266 errMsg: `test.proto:1:1: syntax error: unexpected identifier`, 267 }, 268 { 269 contents: `"abc"`, 270 errMsg: `test.proto:1:1: syntax error: unexpected string literal`, 271 }, 272 { 273 contents: `0.0.0.0.0`, 274 errMsg: `test.proto:1:1: syntax error: unexpected float literal`, 275 }, 276 } 277 278 for i, tc := range testCases { 279 errs := newErrorHandler(nil, nil) 280 _ = parseProto("test.proto", strings.NewReader(tc.contents), errs, true) 281 err := errs.getError() 282 if tc.succeeds { 283 testutil.Ok(t, err, "case #%d should succeed", i) 284 } else { 285 testutil.Nok(t, err, "case #%d should fail", i) 286 testutil.Eq(t, tc.errMsg, err.Error(), "case #%d bad error message", i) 287 } 288 } 289 }