File: valid_test.go

package info (click to toggle)
golang-github-segmentio-encoding 0.5.3-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 14,468 kB
  • sloc: makefile: 286
file content (132 lines) | stat: -rw-r--r-- 4,568 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package iso8601

import (
	"testing"
	"time"
)

func TestValidate(t *testing.T) {
	tests := []struct {
		value string
		flags ValidFlags
		valid bool
	}{
		// valid
		{"2018-01-01T23:42:59.123456789Z", Strict, true},
		{"2018-01-01T23:42:59.123456789+07:00", Strict, true},
		{"2018-01-01T23:42:59.123456789-07:00", Strict, true},
		{"2018-01-01T23:42:59.000+07:00", Strict, true},

		{"2018-01-01", Flexible, true},
		{"2018-01-01 23:42:59", Flexible, true},
		{"2018-01-01T23:42:59.123-0700", Flexible, true},

		// invalid
		{"", Flexible, false},                                 // empty string
		{"whatever", Flexible, false},                         // not a time
		{"2018-01-01", Strict, false},                         // missing time
		{"2018-01-01 23:42:59-0700", Strict, false},           // missing subsecond
		{"2018-01-01T23:42:59.123456789+0700", Strict, false}, // don't allow numeric time zone
		{"2018_01-01T23:42:59.123456789Z", Strict, false},     // invalid date separator (first)
		{"2018-01_01T23:42:59.123456789Z", Strict, false},     // invalid date separator (second)
		{"2018-01-01 23:42:59.123456789Z", Strict, false},     // invalid date-time separator
		{"2018-01-01T23-42:59.123456789Z", Strict, false},     // invalid time separator (first)
		{"2018-01-01T23:42-59.123456789Z", Strict, false},     // invalid time separator (second)
		{"2018-01-01T23:42:59,123456789Z", Strict, false},     // invalid decimal separator
		{"2018-01-01T23:42:59.123456789", Strict, false},      // missing timezone
		{"18-01-01T23:42:59.123456789Z", Strict, false},       // 2-digit year
		{"2018-1-01T23:42:59.123456789Z", Strict, false},      // 1-digit month
		{"2018-01-1T23:42:59.123456789Z", Strict, false},      // 1-digit day
		{"2018-01-01T3:42:59.123456789Z", Strict, false},      // 1-digit hour
		{"2018-01-01T23:2:59.123456789Z", Strict, false},      // 1-digit minute
		{"2018-01-01T23:42:9.123456789Z", Strict, false},      // 1-digit second
		{"2018-01-01T23:42:59.Z", Strict, false},              // not enough subsecond digits
		{"2018-01-01T23:42:59.1234567890Z", Strict, false},    // too many subsecond digits
		{"2018-01-01T23:42:59.123456789+7:00", Strict, false}, // 1-digit timezone hour
		{"2018-01-01T23:42:59.123456789+07:0", Strict, false}, // 1-digit timezone minute
		{"2018-01-01_23:42:59", Flexible, false},              // invalid date-time separator (not a space)
	}

	for _, test := range tests {
		if test.valid != Valid(test.value, test.flags) {
			t.Errorf("%q expected Valid to return %t", test.value, test.valid)
		} else if test.valid {
			if !isIsoString(test.value) {
				t.Errorf("behavior mismatch, isIsoString says %q must not be a valid date", test.value)
			}
		} else if test.flags != Strict {
			if isIsoString(test.value) {
				t.Errorf("behavior mismatch, isIsoString says %q must be a valid date", test.value)
			}
		}
	}
}

func BenchmarkValidate(b *testing.B) {
	b.Run("success", benchmarkValidateSuccess)
	b.Run("failure", benchmarkValidateFailure)
}

func benchmarkValidateSuccess(b *testing.B) {
	for range b.N {
		if !Valid("2018-01-01T23:42:59.123456789Z", Flexible) {
			b.Fatal("not valid")
		}
	}
}

func benchmarkValidateFailure(b *testing.B) {
	for range b.N {
		if Valid("2018-01-01T23:42:59 oops!", Flexible) {
			b.Fatal("valid but should not")
		}
	}
}

func BenchmarkTimeParse(b *testing.B) {
	b.Run("success", benchmarkTimeParseSuccess)
	b.Run("failure", benchmarkTimeParseFailure)
}

func benchmarkTimeParseSuccess(b *testing.B) {
	for range b.N {
		if _, err := time.Parse(time.RFC3339Nano, "2018-01-01T23:42:59.123456789Z"); err != nil {
			b.Fatal("not valid")
		}
	}
}

func benchmarkTimeParseFailure(b *testing.B) {
	for range b.N {
		if _, err := time.Parse(time.RFC3339Nano, "2018-01-01T23:42:59 oops!"); err == nil {
			b.Fatal("valid but should not")
		}
	}
}

// =============================================================================
// This code is extracted from a library we had that we are replacing with this
// package, we use it to verify that the behavior matches.
// =============================================================================
var validDates = [...]string{
	time.RFC3339Nano,
	time.RFC3339,
	"2006-01-02T15:04:05.999-0700",
	"2006-01-02 15:04:05",
	"2006-01-02",
}

func isIsoString(str string) bool {
	// Per RFC3339Nano Spec a date should never be more than 35 chars.
	if len(str) > 36 {
		return false
	}

	for _, format := range validDates {
		if _, err := time.Parse(format, str); err == nil {
			return true
		}
	}

	return false
}