cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm leaflet readability golang
at main 324 lines 9.2 kB view raw
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}