1package version
2
3import (
4	"reflect"
5	"testing"
6)
7
8func TestNewVersion(t *testing.T) {
9	cases := []struct {
10		version string
11		err     bool
12	}{
13		{"", true},
14		{"1.2.3", false},
15		{"1.0", false},
16		{"1", false},
17		{"1.2.beta", true},
18		{"1.21.beta", true},
19		{"foo", true},
20		{"1.2-5", false},
21		{"1.2-beta.5", false},
22		{"\n1.2", true},
23		{"1.2.0-x.Y.0+metadata", false},
24		{"1.2.0-x.Y.0+metadata-width-hypen", false},
25		{"1.2.3-rc1-with-hypen", false},
26		{"1.2.3.4", false},
27		{"1.2.0.4-x.Y.0+metadata", false},
28		{"1.2.0.4-x.Y.0+metadata-width-hypen", false},
29		{"1.2.0-X-1.2.0+metadata~dist", false},
30		{"1.2.3.4-rc1-with-hypen", false},
31		{"1.2.3.4", false},
32		{"v1.2.3", false},
33		{"foo1.2.3", true},
34		{"1.7rc2", false},
35		{"v1.7rc2", false},
36		{"1.0-", false},
37	}
38
39	for _, tc := range cases {
40		_, err := NewVersion(tc.version)
41		if tc.err && err == nil {
42			t.Fatalf("expected error for version: %q", tc.version)
43		} else if !tc.err && err != nil {
44			t.Fatalf("error for version %q: %s", tc.version, err)
45		}
46	}
47}
48
49func TestNewSemver(t *testing.T) {
50	cases := []struct {
51		version string
52		err     bool
53	}{
54		{"", true},
55		{"1.2.3", false},
56		{"1.0", false},
57		{"1", false},
58		{"1.2.beta", true},
59		{"1.21.beta", true},
60		{"foo", true},
61		{"1.2-5", false},
62		{"1.2-beta.5", false},
63		{"\n1.2", true},
64		{"1.2.0-x.Y.0+metadata", false},
65		{"1.2.0-x.Y.0+metadata-width-hypen", false},
66		{"1.2.3-rc1-with-hypen", false},
67		{"1.2.3.4", false},
68		{"1.2.0.4-x.Y.0+metadata", false},
69		{"1.2.0.4-x.Y.0+metadata-width-hypen", false},
70		{"1.2.0-X-1.2.0+metadata~dist", false},
71		{"1.2.3.4-rc1-with-hypen", false},
72		{"1.2.3.4", false},
73		{"v1.2.3", false},
74		{"foo1.2.3", true},
75		{"1.7rc2", true},
76		{"v1.7rc2", true},
77		{"1.0-", true},
78	}
79
80	for _, tc := range cases {
81		_, err := NewSemver(tc.version)
82		if tc.err && err == nil {
83			t.Fatalf("expected error for version: %q", tc.version)
84		} else if !tc.err && err != nil {
85			t.Fatalf("error for version %q: %s", tc.version, err)
86		}
87	}
88}
89
90func TestVersionCompare(t *testing.T) {
91	cases := []struct {
92		v1       string
93		v2       string
94		expected int
95	}{
96		{"1.2.3", "1.4.5", -1},
97		{"1.2-beta", "1.2-beta", 0},
98		{"1.2", "1.1.4", 1},
99		{"1.2", "1.2-beta", 1},
100		{"1.2+foo", "1.2+beta", 0},
101		{"v1.2", "v1.2-beta", 1},
102		{"v1.2+foo", "v1.2+beta", 0},
103		{"v1.2.3.4", "v1.2.3.4", 0},
104		{"v1.2.0.0", "v1.2", 0},
105		{"v1.2.0.0.1", "v1.2", 1},
106		{"v1.2", "v1.2.0.0", 0},
107		{"v1.2", "v1.2.0.0.1", -1},
108		{"v1.2.0.0", "v1.2.0.0.1", -1},
109		{"v1.2.3.0", "v1.2.3.4", -1},
110		{"1.7rc2", "1.7rc1", 1},
111		{"1.7rc2", "1.7", -1},
112		{"1.2.0", "1.2.0-X-1.2.0+metadata~dist", 1},
113	}
114
115	for _, tc := range cases {
116		v1, err := NewVersion(tc.v1)
117		if err != nil {
118			t.Fatalf("err: %s", err)
119		}
120
121		v2, err := NewVersion(tc.v2)
122		if err != nil {
123			t.Fatalf("err: %s", err)
124		}
125
126		actual := v1.Compare(v2)
127		expected := tc.expected
128		if actual != expected {
129			t.Fatalf(
130				"%s <=> %s\nexpected: %d\nactual: %d",
131				tc.v1, tc.v2,
132				expected, actual)
133		}
134	}
135}
136
137func TestVersionCompare_versionAndSemver(t *testing.T) {
138	cases := []struct {
139		versionRaw string
140		semverRaw  string
141		expected   int
142	}{
143		{"0.0.2", "0.0.2", 0},
144		{"1.0.2alpha", "1.0.2-alpha", 0},
145		{"v1.2+foo", "v1.2+beta", 0},
146		{"v1.2", "v1.2+meta", 0},
147		{"1.2", "1.2-beta", 1},
148		{"v1.2", "v1.2-beta", 1},
149		{"1.2.3", "1.4.5", -1},
150		{"v1.2", "v1.2.0.0.1", -1},
151		{"v1.0.3-", "v1.0.3", -1},
152	}
153
154	for _, tc := range cases {
155		ver, err := NewVersion(tc.versionRaw)
156		if err != nil {
157			t.Fatalf("err: %s", err)
158		}
159
160		semver, err := NewSemver(tc.semverRaw)
161		if err != nil {
162			t.Fatalf("err: %s", err)
163		}
164
165		actual := ver.Compare(semver)
166		if actual != tc.expected {
167			t.Fatalf(
168				"%s <=> %s\nexpected: %d\n actual: %d",
169				tc.versionRaw, tc.semverRaw, tc.expected, actual,
170			)
171		}
172	}
173}
174
175func TestVersionEqual_nil(t *testing.T) {
176	mustVersion := func(v string) *Version {
177		ver, err := NewVersion(v)
178		if err != nil {
179			t.Fatal(err)
180		}
181		return ver
182	}
183	cases := []struct {
184		leftVersion  *Version
185		rightVersion *Version
186		expected     bool
187	}{
188		{mustVersion("1.0.0"), nil, false},
189		{nil, mustVersion("1.0.0"), false},
190		{nil, nil, true},
191	}
192
193	for _, tc := range cases {
194		given := tc.leftVersion.Equal(tc.rightVersion)
195		if given != tc.expected {
196			t.Fatalf("expected Equal to nil to be %t", tc.expected)
197		}
198	}
199}
200
201func TestComparePreReleases(t *testing.T) {
202	cases := []struct {
203		v1       string
204		v2       string
205		expected int
206	}{
207		{"1.2-beta.2", "1.2-beta.2", 0},
208		{"1.2-beta.1", "1.2-beta.2", -1},
209		{"1.2-beta.2", "1.2-beta.11", -1},
210		{"3.2-alpha.1", "3.2-alpha", 1},
211		{"1.2-beta.2", "1.2-beta.1", 1},
212		{"1.2-beta.11", "1.2-beta.2", 1},
213		{"1.2-beta", "1.2-beta.3", -1},
214		{"1.2-alpha", "1.2-beta.3", -1},
215		{"1.2-beta", "1.2-alpha.3", 1},
216		{"3.0-alpha.3", "3.0-rc.1", -1},
217		{"3.0-alpha3", "3.0-rc1", -1},
218		{"3.0-alpha.1", "3.0-alpha.beta", -1},
219		{"5.4-alpha", "5.4-alpha.beta", 1},
220		{"v1.2-beta.2", "v1.2-beta.2", 0},
221		{"v1.2-beta.1", "v1.2-beta.2", -1},
222		{"v3.2-alpha.1", "v3.2-alpha", 1},
223		{"v3.2-rc.1-1-g123", "v3.2-rc.2", 1},
224	}
225
226	for _, tc := range cases {
227		v1, err := NewVersion(tc.v1)
228		if err != nil {
229			t.Fatalf("err: %s", err)
230		}
231
232		v2, err := NewVersion(tc.v2)
233		if err != nil {
234			t.Fatalf("err: %s", err)
235		}
236
237		actual := v1.Compare(v2)
238		expected := tc.expected
239		if actual != expected {
240			t.Fatalf(
241				"%s <=> %s\nexpected: %d\nactual: %d",
242				tc.v1, tc.v2,
243				expected, actual)
244		}
245	}
246}
247
248func TestVersionMetadata(t *testing.T) {
249	cases := []struct {
250		version  string
251		expected string
252	}{
253		{"1.2.3", ""},
254		{"1.2-beta", ""},
255		{"1.2.0-x.Y.0", ""},
256		{"1.2.0-x.Y.0+metadata", "metadata"},
257		{"1.2.0-metadata-1.2.0+metadata~dist", "metadata~dist"},
258	}
259
260	for _, tc := range cases {
261		v, err := NewVersion(tc.version)
262		if err != nil {
263			t.Fatalf("err: %s", err)
264		}
265
266		actual := v.Metadata()
267		expected := tc.expected
268		if actual != expected {
269			t.Fatalf("expected: %s\nactual: %s", expected, actual)
270		}
271	}
272}
273
274func TestVersionPrerelease(t *testing.T) {
275	cases := []struct {
276		version  string
277		expected string
278	}{
279		{"1.2.3", ""},
280		{"1.2-beta", "beta"},
281		{"1.2.0-x.Y.0", "x.Y.0"},
282		{"1.2.0-7.Y.0", "7.Y.0"},
283		{"1.2.0-x.Y.0+metadata", "x.Y.0"},
284		{"1.2.0-metadata-1.2.0+metadata~dist", "metadata-1.2.0"},
285		{"17.03.0-ce", "ce"}, // zero-padded fields
286	}
287
288	for _, tc := range cases {
289		v, err := NewVersion(tc.version)
290		if err != nil {
291			t.Fatalf("err: %s", err)
292		}
293
294		actual := v.Prerelease()
295		expected := tc.expected
296		if actual != expected {
297			t.Fatalf("expected: %s\nactual: %s", expected, actual)
298		}
299	}
300}
301
302func TestVersionSegments(t *testing.T) {
303	cases := []struct {
304		version  string
305		expected []int
306	}{
307		{"1.2.3", []int{1, 2, 3}},
308		{"1.2-beta", []int{1, 2, 0}},
309		{"1-x.Y.0", []int{1, 0, 0}},
310		{"1.2.0-x.Y.0+metadata", []int{1, 2, 0}},
311		{"1.2.0-metadata-1.2.0+metadata~dist", []int{1, 2, 0}},
312		{"17.03.0-ce", []int{17, 3, 0}}, // zero-padded fields
313	}
314
315	for _, tc := range cases {
316		v, err := NewVersion(tc.version)
317		if err != nil {
318			t.Fatalf("err: %s", err)
319		}
320
321		actual := v.Segments()
322		expected := tc.expected
323		if !reflect.DeepEqual(actual, expected) {
324			t.Fatalf("expected: %#v\nactual: %#v", expected, actual)
325		}
326	}
327}
328
329func TestVersionSegments64(t *testing.T) {
330	cases := []struct {
331		version  string
332		expected []int64
333	}{
334		{"1.2.3", []int64{1, 2, 3}},
335		{"1.2-beta", []int64{1, 2, 0}},
336		{"1-x.Y.0", []int64{1, 0, 0}},
337		{"1.2.0-x.Y.0+metadata", []int64{1, 2, 0}},
338		{"1.4.9223372036854775807", []int64{1, 4, 9223372036854775807}},
339	}
340
341	for _, tc := range cases {
342		v, err := NewVersion(tc.version)
343		if err != nil {
344			t.Fatalf("err: %s", err)
345		}
346
347		actual := v.Segments64()
348		expected := tc.expected
349		if !reflect.DeepEqual(actual, expected) {
350			t.Fatalf("expected: %#v\nactual: %#v", expected, actual)
351		}
352
353		{
354			expected := actual[0]
355			actual[0]++
356			actual = v.Segments64()
357			if actual[0] != expected {
358				t.Fatalf("Segments64 is mutable")
359			}
360		}
361	}
362}
363
364func TestVersionString(t *testing.T) {
365	cases := [][]string{
366		{"1.2.3", "1.2.3"},
367		{"1.2-beta", "1.2.0-beta"},
368		{"1.2.0-x.Y.0", "1.2.0-x.Y.0"},
369		{"1.2.0-x.Y.0+metadata", "1.2.0-x.Y.0+metadata"},
370		{"1.2.0-metadata-1.2.0+metadata~dist", "1.2.0-metadata-1.2.0+metadata~dist"},
371		{"17.03.0-ce", "17.3.0-ce"}, // zero-padded fields
372	}
373
374	for _, tc := range cases {
375		v, err := NewVersion(tc[0])
376		if err != nil {
377			t.Fatalf("err: %s", err)
378		}
379
380		actual := v.String()
381		expected := tc[1]
382		if actual != expected {
383			t.Fatalf("expected: %s\nactual: %s", expected, actual)
384		}
385		if actual := v.Original(); actual != tc[0] {
386			t.Fatalf("expected original: %q\nactual: %q", tc[0], actual)
387		}
388	}
389}
390
391func TestEqual(t *testing.T) {
392	cases := []struct {
393		v1       string
394		v2       string
395		expected bool
396	}{
397		{"1.2.3", "1.4.5", false},
398		{"1.2-beta", "1.2-beta", true},
399		{"1.2", "1.1.4", false},
400		{"1.2", "1.2-beta", false},
401		{"1.2+foo", "1.2+beta", true},
402		{"v1.2", "v1.2-beta", false},
403		{"v1.2+foo", "v1.2+beta", true},
404		{"v1.2.3.4", "v1.2.3.4", true},
405		{"v1.2.0.0", "v1.2", true},
406		{"v1.2.0.0.1", "v1.2", false},
407		{"v1.2", "v1.2.0.0", true},
408		{"v1.2", "v1.2.0.0.1", false},
409		{"v1.2.0.0", "v1.2.0.0.1", false},
410		{"v1.2.3.0", "v1.2.3.4", false},
411		{"1.7rc2", "1.7rc1", false},
412		{"1.7rc2", "1.7", false},
413		{"1.2.0", "1.2.0-X-1.2.0+metadata~dist", false},
414	}
415
416	for _, tc := range cases {
417		v1, err := NewVersion(tc.v1)
418		if err != nil {
419			t.Fatalf("err: %s", err)
420		}
421
422		v2, err := NewVersion(tc.v2)
423		if err != nil {
424			t.Fatalf("err: %s", err)
425		}
426
427		actual := v1.Equal(v2)
428		expected := tc.expected
429		if actual != expected {
430			t.Fatalf(
431				"%s <=> %s\nexpected: %t\nactual: %t",
432				tc.v1, tc.v2,
433				expected, actual)
434		}
435	}
436}
437
438func TestGreaterThan(t *testing.T) {
439	cases := []struct {
440		v1       string
441		v2       string
442		expected bool
443	}{
444		{"1.2.3", "1.4.5", false},
445		{"1.2-beta", "1.2-beta", false},
446		{"1.2", "1.1.4", true},
447		{"1.2", "1.2-beta", true},
448		{"1.2+foo", "1.2+beta", false},
449		{"v1.2", "v1.2-beta", true},
450		{"v1.2+foo", "v1.2+beta", false},
451		{"v1.2.3.4", "v1.2.3.4", false},
452		{"v1.2.0.0", "v1.2", false},
453		{"v1.2.0.0.1", "v1.2", true},
454		{"v1.2", "v1.2.0.0", false},
455		{"v1.2", "v1.2.0.0.1", false},
456		{"v1.2.0.0", "v1.2.0.0.1", false},
457		{"v1.2.3.0", "v1.2.3.4", false},
458		{"1.7rc2", "1.7rc1", true},
459		{"1.7rc2", "1.7", false},
460		{"1.2.0", "1.2.0-X-1.2.0+metadata~dist", true},
461	}
462
463	for _, tc := range cases {
464		v1, err := NewVersion(tc.v1)
465		if err != nil {
466			t.Fatalf("err: %s", err)
467		}
468
469		v2, err := NewVersion(tc.v2)
470		if err != nil {
471			t.Fatalf("err: %s", err)
472		}
473
474		actual := v1.GreaterThan(v2)
475		expected := tc.expected
476		if actual != expected {
477			t.Fatalf(
478				"%s > %s\nexpected: %t\nactual: %t",
479				tc.v1, tc.v2,
480				expected, actual)
481		}
482	}
483}
484
485func TestLessThan(t *testing.T) {
486	cases := []struct {
487		v1       string
488		v2       string
489		expected bool
490	}{
491		{"1.2.3", "1.4.5", true},
492		{"1.2-beta", "1.2-beta", false},
493		{"1.2", "1.1.4", false},
494		{"1.2", "1.2-beta", false},
495		{"1.2+foo", "1.2+beta", false},
496		{"v1.2", "v1.2-beta", false},
497		{"v1.2+foo", "v1.2+beta", false},
498		{"v1.2.3.4", "v1.2.3.4", false},
499		{"v1.2.0.0", "v1.2", false},
500		{"v1.2.0.0.1", "v1.2", false},
501		{"v1.2", "v1.2.0.0", false},
502		{"v1.2", "v1.2.0.0.1", true},
503		{"v1.2.0.0", "v1.2.0.0.1", true},
504		{"v1.2.3.0", "v1.2.3.4", true},
505		{"1.7rc2", "1.7rc1", false},
506		{"1.7rc2", "1.7", true},
507		{"1.2.0", "1.2.0-X-1.2.0+metadata~dist", false},
508	}
509
510	for _, tc := range cases {
511		v1, err := NewVersion(tc.v1)
512		if err != nil {
513			t.Fatalf("err: %s", err)
514		}
515
516		v2, err := NewVersion(tc.v2)
517		if err != nil {
518			t.Fatalf("err: %s", err)
519		}
520
521		actual := v1.LessThan(v2)
522		expected := tc.expected
523		if actual != expected {
524			t.Fatalf(
525				"%s < %s\nexpected: %t\nactual: %t",
526				tc.v1, tc.v2,
527				expected, actual)
528		}
529	}
530}
531
532func TestGreaterThanOrEqual(t *testing.T) {
533	cases := []struct {
534		v1       string
535		v2       string
536		expected bool
537	}{
538		{"1.2.3", "1.4.5", false},
539		{"1.2-beta", "1.2-beta", true},
540		{"1.2", "1.1.4", true},
541		{"1.2", "1.2-beta", true},
542		{"1.2+foo", "1.2+beta", true},
543		{"v1.2", "v1.2-beta", true},
544		{"v1.2+foo", "v1.2+beta", true},
545		{"v1.2.3.4", "v1.2.3.4", true},
546		{"v1.2.0.0", "v1.2", true},
547		{"v1.2.0.0.1", "v1.2", true},
548		{"v1.2", "v1.2.0.0", true},
549		{"v1.2", "v1.2.0.0.1", false},
550		{"v1.2.0.0", "v1.2.0.0.1", false},
551		{"v1.2.3.0", "v1.2.3.4", false},
552		{"1.7rc2", "1.7rc1", true},
553		{"1.7rc2", "1.7", false},
554		{"1.2.0", "1.2.0-X-1.2.0+metadata~dist", true},
555	}
556
557	for _, tc := range cases {
558		v1, err := NewVersion(tc.v1)
559		if err != nil {
560			t.Fatalf("err: %s", err)
561		}
562
563		v2, err := NewVersion(tc.v2)
564		if err != nil {
565			t.Fatalf("err: %s", err)
566		}
567
568		actual := v1.GreaterThanOrEqual(v2)
569		expected := tc.expected
570		if actual != expected {
571			t.Fatalf(
572				"%s >= %s\nexpected: %t\nactual: %t",
573				tc.v1, tc.v2,
574				expected, actual)
575		}
576	}
577}
578
579func TestLessThanOrEqual(t *testing.T) {
580	cases := []struct {
581		v1       string
582		v2       string
583		expected bool
584	}{
585		{"1.2.3", "1.4.5", true},
586		{"1.2-beta", "1.2-beta", true},
587		{"1.2", "1.1.4", false},
588		{"1.2", "1.2-beta", false},
589		{"1.2+foo", "1.2+beta", true},
590		{"v1.2", "v1.2-beta", false},
591		{"v1.2+foo", "v1.2+beta", true},
592		{"v1.2.3.4", "v1.2.3.4", true},
593		{"v1.2.0.0", "v1.2", true},
594		{"v1.2.0.0.1", "v1.2", false},
595		{"v1.2", "v1.2.0.0", true},
596		{"v1.2", "v1.2.0.0.1", true},
597		{"v1.2.0.0", "v1.2.0.0.1", true},
598		{"v1.2.3.0", "v1.2.3.4", true},
599		{"1.7rc2", "1.7rc1", false},
600		{"1.7rc2", "1.7", true},
601		{"1.2.0", "1.2.0-X-1.2.0+metadata~dist", false},
602	}
603
604	for _, tc := range cases {
605		v1, err := NewVersion(tc.v1)
606		if err != nil {
607			t.Fatalf("err: %s", err)
608		}
609
610		v2, err := NewVersion(tc.v2)
611		if err != nil {
612			t.Fatalf("err: %s", err)
613		}
614
615		actual := v1.LessThanOrEqual(v2)
616		expected := tc.expected
617		if actual != expected {
618			t.Fatalf(
619				"%s <= %s\nexpected: %t\nactual: %t",
620				tc.v1, tc.v2,
621				expected, actual)
622		}
623	}
624}
625