cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm
leaflet
readability
golang
1package services
2
3import (
4 "errors"
5 "strings"
6 "testing"
7)
8
9type validationTC struct {
10 name string
11 value string
12 err bool
13}
14
15func TestValidation(t *testing.T) {
16 t.Run("ValidationError", func(t *testing.T) {
17 err := ValidationError{Field: "testField", Message: "test message"}
18 expected := "validation error for field 'testField': test message"
19 if err.Error() != expected {
20 t.Errorf("Expected %q, got %q", expected, err.Error())
21 }
22 })
23
24 t.Run("ValidationErrors", func(t *testing.T) {
25 t.Run("empty errors", func(t *testing.T) {
26 var errs ValidationErrors
27 expected := "no validation errors"
28 if errs.Error() != expected {
29 t.Errorf("Expected %q, got %q", expected, errs.Error())
30 }
31 })
32
33 t.Run("single error", func(t *testing.T) {
34 errs := ValidationErrors{{Field: "field1", Message: "message1"}}
35 expected := "validation error for field 'field1': message1"
36 if errs.Error() != expected {
37 t.Errorf("Expected %q, got %q", expected, errs.Error())
38 }
39 })
40
41 t.Run("multiple errors", func(t *testing.T) {
42 errs := ValidationErrors{{Field: "field1", Message: "message1"}, {Field: "field2", Message: "message2"}}
43 result := errs.Error()
44 if !strings.Contains(result, "multiple validation errors") {
45 t.Error("Expected 'multiple validation errors' in result")
46 }
47 if !strings.Contains(result, "field1") || !strings.Contains(result, "field2") {
48 t.Error("Expected both field names in result")
49 }
50 })
51 })
52
53 t.Run("RequiredString", func(t *testing.T) {
54 tests := []validationTC{
55 {"empty string", "", true},
56 {"whitespace only", " ", true},
57 {"valid string", "test", false},
58 {"string with spaces", "test value", false},
59 }
60
61 for _, tt := range tests {
62 t.Run(tt.name, func(t *testing.T) {
63 err := RequiredString("testField", tt.value)
64 if (err != nil) != tt.err {
65 t.Errorf("Expected error: %v, got error: %v", tt.err, err != nil)
66 }
67 if err != nil {
68 if !strings.Contains(err.Error(), "testField") {
69 t.Error("Expected field name in error message")
70 }
71 }
72 })
73 }
74 })
75
76 t.Run("ValidURL", func(t *testing.T) {
77 tests := []validationTC{
78 {"empty string", "", false},
79 {"valid http URL", "http://example.com", false},
80 {"valid https URL", "https://example.com", false},
81 {"invalid URL", "not-a-url", true},
82 {"ftp scheme", "ftp://example.com", true},
83 {"URL with path", "https://example.com/path", false},
84 {"URL with query", "https://example.com?param=value", false},
85 }
86
87 for _, tt := range tests {
88 t.Run(tt.name, func(t *testing.T) {
89 err := ValidURL("testField", tt.value)
90 if (err != nil) != tt.err {
91 t.Errorf("Expected error: %v, got error: %v", tt.err, err != nil)
92 }
93 })
94 }
95 })
96
97 t.Run("ValidEmail", func(t *testing.T) {
98 tests := []validationTC{
99 {"empty string", "", false},
100 {"valid email", "test@example.com", false},
101 {"valid email with subdomain", "test@mail.example.com", false},
102 {"invalid email no @", "testexample.com", true},
103 {"invalid email no domain", "test@", true},
104 {"invalid email no local part", "@example.com", true},
105 {"invalid email spaces", "test @example.com", true},
106 }
107
108 for _, tt := range tests {
109 t.Run(tt.name, func(t *testing.T) {
110 err := ValidEmail("testField", tt.value)
111 if (err != nil) != tt.err {
112 t.Errorf("Expected error: %v, got error: %v", tt.err, err != nil)
113 }
114 })
115 }
116 })
117
118 t.Run("StringLength", func(t *testing.T) {
119 tests := []struct {
120 name string
121 value string
122 min int
123 max int
124 shouldErr bool
125 }{
126 {"within range", "test", 2, 10, false},
127 {"too short", "a", 2, 10, true},
128 {"too long", "verylongstring", 2, 10, true},
129 {"exact min", "ab", 2, 10, false},
130 {"exact max", "1234567890", 2, 10, false},
131 {"no min constraint", "a", 0, 10, false},
132 {"no max constraint", "verylongstring", 2, 0, false},
133 {"whitespace trimmed", " test ", 3, 10, false},
134 }
135
136 for _, tt := range tests {
137 t.Run(tt.name, func(t *testing.T) {
138 err := StringLength("testField", tt.value, tt.min, tt.max)
139 if (err != nil) != tt.shouldErr {
140 t.Errorf("Expected error: %v, got error: %v", tt.shouldErr, err != nil)
141 }
142 })
143 }
144 })
145
146 t.Run("ValidDate", func(t *testing.T) {
147 tests := []validationTC{
148 {"empty string", "", false},
149 {"YYYY-MM-DD format", "2024-01-01", false},
150 {"ISO format with time", "2024-01-01T15:04:05Z", false},
151 {"ISO format with timezone", "2024-01-01T15:04:05-07:00", false},
152 {"datetime format", "2024-01-01 15:04:05", false},
153 {"invalid date", "not-a-date", true},
154 {"invalid format", "01/01/2024", true},
155 {"incomplete date", "2024-01", true},
156 }
157
158 for _, tt := range tests {
159 t.Run(tt.name, func(t *testing.T) {
160 err := ValidDate("testField", tt.value)
161 if (err != nil) != tt.err {
162 t.Errorf("Expected error: %v, got error: %v", tt.err, err != nil)
163 }
164 })
165 }
166 })
167
168 t.Run("PositiveID", func(t *testing.T) {
169 tests := []struct {
170 name string
171 value int64
172 shouldErr bool
173 }{
174 {"positive ID", 1, false},
175 {"zero ID", 0, true},
176 {"negative ID", -1, true},
177 {"large positive ID", 999999, false},
178 }
179
180 for _, tt := range tests {
181 t.Run(tt.name, func(t *testing.T) {
182 err := PositiveID("testField", tt.value)
183 if (err != nil) != tt.shouldErr {
184 t.Errorf("Expected error: %v, got error: %v", tt.shouldErr, err != nil)
185 }
186 })
187 }
188 })
189
190 t.Run("ValidEnum", func(t *testing.T) {
191 allowed := []string{"option1", "option2", "option3"}
192
193 tests := []validationTC{
194 {"empty string", "", false},
195 {"valid option1", "option1", false},
196 {"valid option2", "option2", false},
197 {"valid option3", "option3", false},
198 {"invalid option", "option4", true},
199 {"case sensitive", "Option1", true},
200 }
201
202 for _, tt := range tests {
203 t.Run(tt.name, func(t *testing.T) {
204 err := ValidEnum("testField", tt.value, allowed)
205 if (err != nil) != tt.err {
206 t.Errorf("Expected error: %v, got error: %v", tt.err, err != nil)
207 }
208 })
209 }
210 })
211
212 t.Run("ValidFilePath", func(t *testing.T) {
213 tests := []validationTC{
214 {"empty string", "", false},
215 {"valid path", "/path/to/file.txt", false},
216 {"relative path", "path/to/file.txt", false},
217 {"path traversal", "../../../etc/passwd", true},
218 {"path with .. in middle", "/path/../to/file.txt", true},
219 {"invalid characters", "/path/to/file<>.txt", true},
220 {"pipe character", "/path/to/file|.txt", true},
221 {"question mark", "/path/to/file?.txt", true},
222 {"asterisk", "/path/to/file*.txt", true},
223 {"colon", "/path/to/file:.txt", true},
224 {"quotes", "/path/to/\"file\".txt", true},
225 {"windows path", "C:\\path\\to\\file.txt", true},
226 }
227
228 for _, tt := range tests {
229 t.Run(tt.name, func(t *testing.T) {
230 err := ValidFilePath("testField", tt.value)
231 if (err != nil) != tt.err {
232 t.Errorf("Expected error: %v, got error: %v", tt.err, err != nil)
233 }
234 })
235 }
236 })
237
238 t.Run("Validator", func(t *testing.T) {
239 t.Run("empty validator", func(t *testing.T) {
240 v := NewValidator()
241 if !v.IsValid() {
242 t.Error("Expected new validator to be valid")
243 }
244 if v.Errors() != nil {
245 t.Error("Expected new validator to have no errors")
246 }
247 })
248
249 t.Run("single validation error", func(t *testing.T) {
250 v := NewValidator()
251 v.Check(RequiredString("testField", ""))
252
253 if v.IsValid() {
254 t.Error("Expected validator to be invalid after failed check")
255 }
256 if v.Errors() == nil {
257 t.Error("Expected validator to have errors")
258 }
259 })
260
261 t.Run("multiple validation errors", func(t *testing.T) {
262 v := NewValidator()
263 v.Check(RequiredString("field1", ""))
264 v.Check(RequiredString("field2", ""))
265
266 if v.IsValid() {
267 t.Error("Expected validator to be invalid")
268 }
269 err := v.Errors()
270 if err == nil {
271 t.Error("Expected validator to have errors")
272 }
273 if !strings.Contains(err.Error(), "field1") || !strings.Contains(err.Error(), "field2") {
274 t.Error("Expected both field names in error message")
275 }
276 })
277
278 t.Run("mixed valid and invalid checks", func(t *testing.T) {
279 v := NewValidator()
280 v.Check(RequiredString("validField", "valid"))
281 v.Check(RequiredString("invalidField", ""))
282
283 if v.IsValid() {
284 t.Error("Expected validator to be invalid")
285 }
286 err := v.Errors()
287 if err == nil {
288 t.Error("Expected validator to have errors")
289 }
290 if !strings.Contains(err.Error(), "invalidField") {
291 t.Error("Expected invalid field name in error message")
292 }
293 })
294
295 t.Run("fluent interface", func(t *testing.T) {
296 v := NewValidator()
297 result := v.Check(RequiredString("field1", "valid")).Check(RequiredString("field2", "valid"))
298
299 if result != v {
300 t.Error("Expected Check to return the same validator instance")
301 }
302 if !v.IsValid() {
303 t.Error("Expected validator to be valid after valid checks")
304 }
305 })
306
307 t.Run("non-validation error handling", func(t *testing.T) {
308 v := NewValidator()
309 v.Check(errors.New("generic error"))
310
311 if v.IsValid() {
312 t.Error("Expected validator to be invalid")
313 }
314 err := v.Errors()
315 if err == nil {
316 t.Error("Expected validator to have errors")
317 }
318
319 if !strings.Contains(err.Error(), "unknown") {
320 t.Error("Expected 'unknown' field in converted error")
321 }
322 })
323 })
324}