1package constraints 2 3import ( 4 "testing" 5 6 "github.com/go-test/deep" 7) 8 9func TestParseRubyStyle(t *testing.T) { 10 tests := []struct { 11 Input string 12 Want SelectionSpec 13 WantErr string 14 }{ 15 { 16 "", 17 SelectionSpec{}, 18 "empty specification", 19 }, 20 { 21 "1", 22 SelectionSpec{ 23 Operator: OpEqual, 24 Boundary: VersionSpec{ 25 Major: NumConstraint{Num: 1}, 26 Minor: NumConstraint{Unconstrained: true}, 27 Patch: NumConstraint{Unconstrained: true}, 28 }, 29 }, 30 "", 31 }, 32 { 33 "1.1", 34 SelectionSpec{ 35 Operator: OpEqual, 36 Boundary: VersionSpec{ 37 Major: NumConstraint{Num: 1}, 38 Minor: NumConstraint{Num: 1}, 39 Patch: NumConstraint{Unconstrained: true}, 40 }, 41 }, 42 "", 43 }, 44 { 45 "1.1.1", 46 SelectionSpec{ 47 Operator: OpEqual, 48 Boundary: VersionSpec{ 49 Major: NumConstraint{Num: 1}, 50 Minor: NumConstraint{Num: 1}, 51 Patch: NumConstraint{Num: 1}, 52 }, 53 }, 54 "", 55 }, 56 { 57 "1.0.0.0", 58 SelectionSpec{}, 59 "too many numbered portions; only three are allowed (major, minor, patch)", 60 }, 61 { 62 "v1.0.0", 63 SelectionSpec{}, 64 `a "v" prefix should not be used`, 65 }, 66 { 67 "1.0.0-beta2", 68 SelectionSpec{ 69 Operator: OpEqual, 70 Boundary: VersionSpec{ 71 Major: NumConstraint{Num: 1}, 72 Minor: NumConstraint{Num: 0}, 73 Patch: NumConstraint{Num: 0}, 74 Prerelease: "beta2", 75 }, 76 }, 77 "", 78 }, 79 { 80 "1.0-beta2", 81 SelectionSpec{ 82 Operator: OpEqual, 83 Boundary: VersionSpec{ 84 Major: NumConstraint{Num: 1}, 85 Minor: NumConstraint{Num: 0}, 86 Patch: NumConstraint{Num: 0}, // implied by the prerelease tag to ensure constraint consistency 87 Prerelease: "beta2", 88 }, 89 }, 90 "", 91 }, 92 { 93 "1.0.0-beta.2", 94 SelectionSpec{ 95 Operator: OpEqual, 96 Boundary: VersionSpec{ 97 Major: NumConstraint{Num: 1}, 98 Minor: NumConstraint{Num: 0}, 99 Patch: NumConstraint{Num: 0}, 100 Prerelease: "beta.2", 101 }, 102 }, 103 "", 104 }, 105 { 106 "1.0.0+foo", 107 SelectionSpec{ 108 Operator: OpEqual, 109 Boundary: VersionSpec{ 110 Major: NumConstraint{Num: 1}, 111 Minor: NumConstraint{Num: 0}, 112 Patch: NumConstraint{Num: 0}, 113 Metadata: "foo", 114 }, 115 }, 116 "", 117 }, 118 { 119 "1.0.0+foo.bar", 120 SelectionSpec{ 121 Operator: OpEqual, 122 Boundary: VersionSpec{ 123 Major: NumConstraint{Num: 1}, 124 Minor: NumConstraint{Num: 0}, 125 Patch: NumConstraint{Num: 0}, 126 Metadata: "foo.bar", 127 }, 128 }, 129 "", 130 }, 131 { 132 "1.0.0-beta1+foo.bar", 133 SelectionSpec{ 134 Operator: OpEqual, 135 Boundary: VersionSpec{ 136 Major: NumConstraint{Num: 1}, 137 Minor: NumConstraint{Num: 0}, 138 Patch: NumConstraint{Num: 0}, 139 Prerelease: "beta1", 140 Metadata: "foo.bar", 141 }, 142 }, 143 "", 144 }, 145 { 146 "> 1.1.1", 147 SelectionSpec{ 148 Operator: OpGreaterThan, 149 Boundary: VersionSpec{ 150 Major: NumConstraint{Num: 1}, 151 Minor: NumConstraint{Num: 1}, 152 Patch: NumConstraint{Num: 1}, 153 }, 154 }, 155 ``, 156 }, 157 { 158 ">1.1.1", 159 SelectionSpec{ 160 Operator: OpGreaterThan, 161 Boundary: VersionSpec{ 162 Major: NumConstraint{Num: 1}, 163 Minor: NumConstraint{Num: 1}, 164 Patch: NumConstraint{Num: 1}, 165 }, 166 }, 167 ``, 168 }, 169 { 170 ">= 1.1.1", 171 SelectionSpec{ 172 Operator: OpGreaterThanOrEqual, 173 Boundary: VersionSpec{ 174 Major: NumConstraint{Num: 1}, 175 Minor: NumConstraint{Num: 1}, 176 Patch: NumConstraint{Num: 1}, 177 }, 178 }, 179 ``, 180 }, 181 { 182 ">=1.1.1", 183 SelectionSpec{ 184 Operator: OpGreaterThanOrEqual, 185 Boundary: VersionSpec{ 186 Major: NumConstraint{Num: 1}, 187 Minor: NumConstraint{Num: 1}, 188 Patch: NumConstraint{Num: 1}, 189 }, 190 }, 191 ``, 192 }, 193 { 194 "=> 1.1.1", 195 SelectionSpec{}, 196 `invalid constraint operator "=>"; did you mean ">="?`, 197 }, 198 { 199 "=>1.1.1", 200 SelectionSpec{}, 201 `invalid constraint operator "=>"; did you mean ">="?`, 202 }, 203 { 204 "< 1.1.1", 205 SelectionSpec{ 206 Operator: OpLessThan, 207 Boundary: VersionSpec{ 208 Major: NumConstraint{Num: 1}, 209 Minor: NumConstraint{Num: 1}, 210 Patch: NumConstraint{Num: 1}, 211 }, 212 }, 213 ``, 214 }, 215 { 216 "<= 1.1.1", 217 SelectionSpec{ 218 Operator: OpLessThanOrEqual, 219 Boundary: VersionSpec{ 220 Major: NumConstraint{Num: 1}, 221 Minor: NumConstraint{Num: 1}, 222 Patch: NumConstraint{Num: 1}, 223 }, 224 }, 225 ``, 226 }, 227 { 228 "=< 1.1.1", 229 SelectionSpec{}, 230 `invalid constraint operator "=<"; did you mean "<="?`, 231 }, 232 { 233 "~> 1.1.1", 234 SelectionSpec{ 235 Operator: OpGreaterThanOrEqualPatchOnly, 236 Boundary: VersionSpec{ 237 Major: NumConstraint{Num: 1}, 238 Minor: NumConstraint{Num: 1}, 239 Patch: NumConstraint{Num: 1}, 240 }, 241 }, 242 ``, 243 }, 244 { 245 "~> 1.1", 246 SelectionSpec{ 247 Operator: OpGreaterThanOrEqualMinorOnly, 248 Boundary: VersionSpec{ 249 Major: NumConstraint{Num: 1}, 250 Minor: NumConstraint{Num: 1}, 251 Patch: NumConstraint{Unconstrained: true}, 252 }, 253 }, 254 ``, 255 }, 256 { 257 "~> 1", 258 SelectionSpec{ 259 Operator: OpGreaterThanOrEqualMinorOnly, 260 Boundary: VersionSpec{ 261 Major: NumConstraint{Num: 1}, 262 Minor: NumConstraint{Unconstrained: true}, 263 Patch: NumConstraint{Unconstrained: true}, 264 }, 265 }, 266 ``, 267 }, 268 { 269 "= 1.1.1", 270 SelectionSpec{ 271 Operator: OpEqual, 272 Boundary: VersionSpec{ 273 Major: NumConstraint{Num: 1}, 274 Minor: NumConstraint{Num: 1}, 275 Patch: NumConstraint{Num: 1}, 276 }, 277 }, 278 ``, 279 }, 280 { 281 "!= 1.1.1", 282 SelectionSpec{ 283 Operator: OpNotEqual, 284 Boundary: VersionSpec{ 285 Major: NumConstraint{Num: 1}, 286 Minor: NumConstraint{Num: 1}, 287 Patch: NumConstraint{Num: 1}, 288 }, 289 }, 290 ``, 291 }, 292 { 293 "= 1.1.1", 294 SelectionSpec{}, 295 `only one space is expected after the operator "="`, 296 }, 297 { 298 "garbage", 299 SelectionSpec{}, 300 `invalid characters "garbage"`, 301 }, 302 { 303 "& 1.1.0", 304 SelectionSpec{}, 305 `invalid constraint operator "&"`, 306 }, 307 { 308 "1.*.*", 309 SelectionSpec{}, 310 `can't use wildcard for minor number; omit segments that should be unconstrained`, 311 }, 312 { 313 "1.0.x", 314 SelectionSpec{}, 315 `can't use wildcard for patch number; omit segments that should be unconstrained`, 316 }, 317 { 318 "1.0 || 2.0", 319 SelectionSpec{}, 320 `only one constraint may be specified`, 321 }, 322 { 323 "1.0.0, 2.0.0", 324 SelectionSpec{}, 325 `only one constraint may be specified`, 326 }, 327 { 328 "1.0.0 - 2.0.0", 329 SelectionSpec{}, 330 `range constraints are not supported`, 331 }, 332 } 333 334 for _, test := range tests { 335 t.Run(test.Input, func(t *testing.T) { 336 got, err := ParseRubyStyle(test.Input) 337 var gotErr string 338 if err != nil { 339 gotErr = err.Error() 340 } 341 if gotErr != test.WantErr { 342 t.Errorf("wrong error\ngot: %s\nwant: %s", gotErr, test.WantErr) 343 return 344 } 345 if err != nil { 346 return 347 } 348 349 for _, problem := range deep.Equal(got, test.Want) { 350 t.Error(problem) 351 } 352 }) 353 } 354} 355 356func TestParseRubyStyleMulti(t *testing.T) { 357 tests := []struct { 358 Input string 359 Want IntersectionSpec 360 WantErr string 361 }{ 362 { 363 "", 364 nil, 365 "", 366 }, 367 { 368 "1.1.1", 369 IntersectionSpec{ 370 SelectionSpec{ 371 Operator: OpEqual, 372 Boundary: VersionSpec{ 373 Major: NumConstraint{Num: 1}, 374 Minor: NumConstraint{Num: 1}, 375 Patch: NumConstraint{Num: 1}, 376 }, 377 }, 378 }, 379 "", 380 }, 381 { 382 "~> v1.1.1", 383 nil, 384 `a "v" prefix should not be used`, 385 }, 386 { 387 ">= 1.0, < 2", 388 IntersectionSpec{ 389 SelectionSpec{ 390 Operator: OpGreaterThanOrEqual, 391 Boundary: VersionSpec{ 392 Major: NumConstraint{Num: 1}, 393 Minor: NumConstraint{Num: 0}, 394 Patch: NumConstraint{Unconstrained: true}, 395 }, 396 }, 397 SelectionSpec{ 398 Operator: OpLessThan, 399 Boundary: VersionSpec{ 400 Major: NumConstraint{Num: 2}, 401 Minor: NumConstraint{Unconstrained: true}, 402 Patch: NumConstraint{Unconstrained: true}, 403 }, 404 }, 405 }, 406 "", 407 }, 408 { 409 ">= 1.0 < 2", 410 nil, 411 `missing comma after ">= 1.0"`, 412 }, 413 } 414 415 for _, test := range tests { 416 t.Run(test.Input, func(t *testing.T) { 417 got, err := ParseRubyStyleMulti(test.Input) 418 var gotErr string 419 if err != nil { 420 gotErr = err.Error() 421 } 422 if gotErr != test.WantErr { 423 t.Errorf("wrong error\ngot: %s\nwant: %s", gotErr, test.WantErr) 424 return 425 } 426 if err != nil { 427 return 428 } 429 430 for _, problem := range deep.Equal(got, test.Want) { 431 t.Error(problem) 432 } 433 }) 434 } 435} 436