Passed
Pull Request — main (#52)
by Igor
01:50
created

validation.TestInternalViolation_Is   A

Complexity

Conditions 3

Size

Total Lines 28
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 19
dl 0
loc 28
rs 9.45
c 0
b 0
f 0
nop 1
1
package validation_test
2
3
import (
4
	"encoding/json"
5
	"errors"
6
	"fmt"
7
	"testing"
8
9
	"github.com/muonsoft/validation"
10
	"github.com/stretchr/testify/assert"
11
)
12
13
func TestViolation_Error_MessageOnly_ErrorWithMessage(t *testing.T) {
14
	validator := newValidator(t)
15
16
	violation := validator.BuildViolation("", "message").CreateViolation()
17
18
	assert.Equal(t, "violation: message", violation.Error())
19
}
20
21
func TestNewViolationList(t *testing.T) {
22
	first := newViolationWithCode(t, "first")
23
	last := newViolationWithCode(t, "last")
24
25
	violations := validation.NewViolationList(first, last)
26
27
	assert.Equal(t, 2, violations.Len())
28
	if assert.NotNil(t, violations.First()) {
29
		assert.Equal(t, first, violations.First().Violation())
30
	}
31
	if assert.NotNil(t, violations.Last()) {
32
		assert.Equal(t, last, violations.Last().Violation())
33
	}
34
}
35
36
func TestViolationList_Join(t *testing.T) {
37
	tests := []struct {
38
		name          string
39
		list          *validation.ViolationList
40
		joined        *validation.ViolationList
41
		expectedCodes []string
42
	}{
43
		{
44
			name:          "nil joined list",
45
			list:          newViolationList(t),
46
			joined:        nil,
47
			expectedCodes: nil,
48
		},
49
		{
50
			name:          "empty joined list",
51
			list:          newViolationList(t),
52
			joined:        newViolationList(t),
53
			expectedCodes: nil,
54
		},
55
		{
56
			name:          "joined list with one element",
57
			list:          newViolationList(t),
58
			joined:        newViolationList(t, "code"),
59
			expectedCodes: []string{"code"},
60
		},
61
		{
62
			name:          "1 + 1",
63
			list:          newViolationList(t, "first"),
64
			joined:        newViolationList(t, "second"),
65
			expectedCodes: []string{"first", "second"},
66
		},
67
		{
68
			name:          "2 + 1",
69
			list:          newViolationList(t, "first", "second"),
70
			joined:        newViolationList(t, "third"),
71
			expectedCodes: []string{"first", "second", "third"},
72
		},
73
		{
74
			name:          "1 + 0",
75
			list:          newViolationList(t, "first"),
76
			joined:        newViolationList(t),
77
			expectedCodes: []string{"first"},
78
		},
79
	}
80
	for _, test := range tests {
81
		t.Run(test.name, func(t *testing.T) {
82
			test.list.Join(test.joined)
83
84
			if assert.Equal(t, len(test.expectedCodes), test.list.Len()) {
85
				i := 0
86
				for e := test.list.First(); e != nil; e = e.Next() {
87
					assert.Equal(t, test.expectedCodes[i], e.Violation().Code())
88
					i++
89
				}
90
			}
91
		})
92
	}
93
}
94
95
func TestViolationList_Join_WhenEmptyListJoinedCoupleOfTimes_ExpectJoinedList(t *testing.T) {
96
	list := newViolationList(t, "first")
97
	list.Join(newViolationList(t))
98
	list.Join(newViolationList(t))
99
100
	assert.Equal(t, 1, list.Len())
101
}
102
103
func TestInternalViolation_Is(t *testing.T) {
104
	tests := []struct {
105
		name       string
106
		codes      []string
107
		expectedIs bool
108
	}{
109
		{
110
			name:       "empty list",
111
			expectedIs: false,
112
		},
113
		{
114
			name:       "no matches",
115
			codes:      []string{"alpha", "beta"},
116
			expectedIs: false,
117
		},
118
		{
119
			name:       "one of the codes is matching",
120
			codes:      []string{"alpha", "beta", "code"},
121
			expectedIs: true,
122
		},
123
	}
124
	for _, test := range tests {
125
		t.Run(test.name, func(t *testing.T) {
126
			violation := newViolationWithCode(t, "code")
127
128
			is := violation.Is(test.codes...)
129
130
			assert.Equal(t, test.expectedIs, is)
131
		})
132
	}
133
}
134
135
func TestViolationList_Has(t *testing.T) {
136
	tests := []struct {
137
		name       string
138
		codes      []string
139
		expectedIs bool
140
	}{
141
		{
142
			name:       "empty list",
143
			expectedIs: false,
144
		},
145
		{
146
			name:       "no matches",
147
			codes:      []string{"alpha", "beta"},
148
			expectedIs: false,
149
		},
150
		{
151
			name:       "one of the codes is matching",
152
			codes:      []string{"alpha", "beta", "code"},
153
			expectedIs: true,
154
		},
155
	}
156
	for _, test := range tests {
157
		t.Run(test.name, func(t *testing.T) {
158
			violations := validation.NewViolationList(newViolationWithCode(t, "code"))
159
160
			has := violations.Has(test.codes...)
161
162
			assert.Equal(t, test.expectedIs, has)
163
		})
164
	}
165
}
166
167
func TestViolationList_Filter_ViolationsWithCodes_FilteredList(t *testing.T) {
168
	violations := newViolationList(t, "alpha", "beta", "gamma", "delta")
169
170
	filtered := violations.Filter("delta", "beta").AsSlice()
171
172
	if assert.Len(t, filtered, 2) {
173
		assert.Equal(t, "beta", filtered[0].Code())
174
		assert.Equal(t, "delta", filtered[1].Code())
175
	}
176
}
177
178
func TestViolation_Error_MessageAndPropertyPath_ErrorWithPropertyPathAndMessage(t *testing.T) {
179
	validator := newValidator(t)
180
	violation := validator.BuildViolation("", "message").
181
		SetPropertyPath(validation.NewPropertyPath(validation.PropertyNameElement("propertyPath"))).
182
		CreateViolation()
183
184
	err := violation.Error()
185
186
	assert.Equal(t, "violation at 'propertyPath': message", err)
187
}
188
189
func TestViolationList_Error_CoupleOfViolations_JoinedMessage(t *testing.T) {
190
	validator := newValidator(t)
191
	violations := validation.NewViolationList(
192
		validator.BuildViolation("", "first message").
193
			SetPropertyPath(
194
				validation.NewPropertyPath(
195
					validation.PropertyNameElement("path"),
196
					validation.ArrayIndexElement(0)),
197
			).
198
			CreateViolation(),
199
		validator.BuildViolation("", "second message").
200
			SetPropertyPath(
201
				validation.NewPropertyPath(
202
					validation.PropertyNameElement("path"),
203
					validation.ArrayIndexElement(1)),
204
			).
205
			CreateViolation(),
206
	)
207
208
	err := violations.Error()
209
210
	assert.Equal(t, "violation at 'path[0]': first message; violation at 'path[1]': second message", err)
211
}
212
213
func TestViolationList_Error_EmptyList_ErrorWithHelpMessage(t *testing.T) {
214
	violations := validation.NewViolationList()
215
216
	err := violations.Error()
217
218
	assert.Equal(t, "the list of violations is empty, it looks like you forgot to use the AsError method somewhere", err)
219
}
220
221
func TestIsViolation_CustomError_False(t *testing.T) {
222
	err := errors.New("error")
223
224
	is := validation.IsViolation(err)
225
226
	assert.False(t, is)
227
}
228
229
func TestIsViolation_Violation_True(t *testing.T) {
230
	err := fmt.Errorf("%w", newViolationWithCode(t, "code"))
231
232
	is := validation.IsViolation(err)
233
234
	assert.True(t, is)
235
}
236
237
func TestIsViolationList_CustomError_False(t *testing.T) {
238
	err := errors.New("error")
239
240
	is := validation.IsViolationList(err)
241
242
	assert.False(t, is)
243
}
244
245
func TestIsViolationList_Violation_True(t *testing.T) {
246
	err := fmt.Errorf("%w", validation.NewViolationList(newViolationWithCode(t, "code")))
247
248
	is := validation.IsViolationList(err)
249
250
	assert.True(t, is)
251
}
252
253
func TestUnwrapViolation_WrappedViolation_UnwrappedViolation(t *testing.T) {
254
	wrapped := newViolationWithCode(t, "code")
255
	err := fmt.Errorf("error: %w", wrapped)
256
257
	unwrapped, ok := validation.UnwrapViolation(err)
258
259
	assert.True(t, ok)
260
	assert.Equal(t, wrapped, unwrapped)
261
}
262
263
func TestUnwrapViolationList_WrappedViolationList_UnwrappedViolationList(t *testing.T) {
264
	wrapped := validation.NewViolationList(newViolationWithCode(t, "code"))
265
	err := fmt.Errorf("error: %w", wrapped)
266
267
	unwrapped, ok := validation.UnwrapViolationList(err)
268
269
	assert.True(t, ok)
270
	assert.Equal(t, wrapped, unwrapped)
271
}
272
273
func TestMarshalViolationToJSON(t *testing.T) {
274
	validator := newValidator(t)
275
276
	tests := []struct {
277
		name         string
278
		violation    validation.Violation
279
		expectedJSON string
280
	}{
281
		{
282
			name: "full data",
283
			violation: validator.BuildViolation("code", "message").
284
				SetParameters(validation.TemplateParameter{Key: "key", Value: "value"}).
285
				SetPropertyPath(
286
					validation.NewPropertyPath(
287
						validation.PropertyNameElement("properties"),
288
						validation.ArrayIndexElement(1),
289
						validation.PropertyNameElement("name"),
290
					),
291
				).CreateViolation(),
292
			expectedJSON: `{
293
				"code": "code",
294
				"message": "message",
295
				"propertyPath": "properties[1].name"
296
			}`,
297
		},
298
		{
299
			name:         "empty data",
300
			violation:    validator.BuildViolation("", "").CreateViolation(),
301
			expectedJSON: `{"code": "", "message": ""}`,
302
		},
303
	}
304
	for _, test := range tests {
305
		t.Run(test.name, func(t *testing.T) {
306
			data, err := json.Marshal(test.violation)
307
308
			if assert.NoError(t, err) {
309
				assert.JSONEq(t, test.expectedJSON, string(data))
310
			}
311
		})
312
	}
313
}
314
315
func TestMarshalViolationListToJSON(t *testing.T) {
316
	validator := newValidator(t)
317
318
	tests := []struct {
319
		name         string
320
		list         *validation.ViolationList
321
		expectedJSON string
322
	}{
323
		{
324
			name:         "empty list",
325
			list:         validation.NewViolationList(),
326
			expectedJSON: `[]`,
327
		},
328
		{
329
			name: "empty data",
330
			list: validation.NewViolationList(
331
				validator.BuildViolation("", "").CreateViolation(),
332
			),
333
			expectedJSON: `[{"code": "", "message": ""}]`,
334
		},
335
		{
336
			name: "one full violation",
337
			list: validation.NewViolationList(
338
				validator.BuildViolation("code", "message").
339
					SetParameters(validation.TemplateParameter{Key: "key", Value: "value"}).
340
					SetPropertyPath(
341
						validation.NewPropertyPath(
342
							validation.PropertyNameElement("properties"),
343
							validation.ArrayIndexElement(1),
344
							validation.PropertyNameElement("name"),
345
						),
346
					).CreateViolation(),
347
			),
348
			expectedJSON: `[
349
				{
350
					"code": "code",
351
					"message": "message",
352
					"propertyPath": "properties[1].name"
353
				}
354
			]`,
355
		},
356
		{
357
			name: "two violations",
358
			list: validation.NewViolationList(
359
				validator.BuildViolation("code", "message").
360
					SetParameters(validation.TemplateParameter{Key: "key", Value: "value"}).
361
					SetPropertyPath(
362
						validation.NewPropertyPath(
363
							validation.PropertyNameElement("properties"),
364
							validation.ArrayIndexElement(1),
365
							validation.PropertyNameElement("name"),
366
						),
367
					).CreateViolation(),
368
				validator.BuildViolation("code", "message").
369
					SetParameters(validation.TemplateParameter{Key: "key", Value: "value"}).
370
					SetPropertyPath(
371
						validation.NewPropertyPath(
372
							validation.PropertyNameElement("properties"),
373
							validation.ArrayIndexElement(1),
374
							validation.PropertyNameElement("name"),
375
						),
376
					).CreateViolation(),
377
			),
378
			expectedJSON: `[
379
				{
380
					"code": "code",
381
					"message": "message",
382
					"propertyPath": "properties[1].name"
383
				},
384
				{
385
					"code": "code",
386
					"message": "message",
387
					"propertyPath": "properties[1].name"
388
				}
389
			]`,
390
		},
391
	}
392
	for _, test := range tests {
393
		t.Run(test.name, func(t *testing.T) {
394
			data, err := json.Marshal(test.list)
395
396
			if assert.NoError(t, err) {
397
				assert.JSONEq(t, test.expectedJSON, string(data))
398
			}
399
		})
400
	}
401
}
402
403
func newViolationWithCode(t *testing.T, code string) validation.Violation {
404
	t.Helper()
405
	validator := newValidator(t)
406
	violation := validator.BuildViolation(code, "").CreateViolation()
407
	return violation
408
}
409
410
func newViolationList(t *testing.T, codes ...string) *validation.ViolationList {
411
	t.Helper()
412
	validator := newValidator(t)
413
	violations := validation.NewViolationList()
414
	for _, code := range codes {
415
		violation := validator.BuildViolation(code, "").CreateViolation()
416
		violations.Append(violation)
417
	}
418
	return violations
419
}
420
421
func newValidator(t *testing.T) *validation.Validator {
422
	t.Helper()
423
	validator, err := validation.NewValidator()
424
	if err != nil {
425
		t.Fatal(err)
426
	}
427
	return validator
428
}
429