Test Failed
Push — main ( 7dfe2c...03ceef )
by Igor
01:04 queued 12s
created

validationtest.*ViolationAssertion.WithError   A

Complexity

Conditions 3

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 12
dl 0
loc 17
rs 9.8
c 0
b 0
f 0
nop 1
1
// Copyright 2021 Igor Lazarev. All rights reserved.
2
// Use of this source code is governed by a MIT-style
3
// license that can be found in the LICENSE file.
4
5
// Package validationtest contains helper functions for testing purposes.
6
package validationtest
7
8
import (
9
	"fmt"
10
	"strconv"
11
	"testing"
12
13
	"github.com/muonsoft/validation"
14
	"github.com/stretchr/testify/assert"
15
)
16
17
// TestingT is an interface wrapper around *testing.T.
18
type TestingT interface {
19
	Helper()
20
	Error(args ...interface{})
21
	Errorf(format string, args ...interface{})
22
	Fatal(args ...interface{})
23
}
24
25
// ViolationAttributes are used to compare violation against expected values. An empty value is not compared.
26
type ViolationAttributes struct {
27
	Error        error
28
	Message      string
29
	PropertyPath string
30
}
31
32
// Assertion is a structure for testing an error for implementing validator violations.
33
type Assertion struct {
34
	t   TestingT
35
	err error
36
}
37
38
// Assert creates a new Assertion for the error.
39
func Assert(t TestingT, err error) *Assertion {
40
	t.Helper()
41
42
	return &Assertion{t: t, err: err}
43
}
44
45
// IsViolation checks that err implements validation.Violation and returns ViolationAssertion
46
// for attributes assertions.
47
func (a *Assertion) IsViolation() *ViolationAssertion {
48
	a.t.Helper()
49
50
	violation, ok := validation.UnwrapViolation(a.err)
51
	if !ok {
52
		assert.Fail(a.t, "failed asserting that err is a Violation")
53
54
		return nil
55
	}
56
57
	return newViolationAssertion(a.t, violation)
58
}
59
60
// IsViolationList checks that err implements validation.IsViolationList and returns ViolationListAssertion
61
// for attributes assertions.
62
func (a *Assertion) IsViolationList() *ViolationListAssertion {
63
	a.t.Helper()
64
65
	violations, ok := validation.UnwrapViolationList(a.err)
66
	if !ok {
67
		assert.Fail(a.t, "failed asserting that err is a ViolationList")
68
69
		return nil
70
	}
71
72
	return &ViolationListAssertion{t: a.t, violations: violations}
73
}
74
75
// ViolationListAssertion is a structure for testing violation list attributes.
76
type ViolationListAssertion struct {
77
	t          TestingT
78
	violations *validation.ViolationList
79
}
80
81
// Assert is used for the client-side side assertion of violations by a callback function.
82
func (a *ViolationListAssertion) Assert(
83
	assert func(tb testing.TB, violations []validation.Violation),
84
) *ViolationListAssertion {
85
	if a == nil {
86
		return nil
87
	}
88
	a.t.Helper()
89
90
	if tb, ok := a.t.(testing.TB); ok {
91
		assert(tb, a.violations.AsSlice())
92
	} else {
93
		a.t.Fatal("t must implement testing.TB")
94
	}
95
96
	return a
97
}
98
99
// WithLen checks that the violation list has exact length.
100
func (a *ViolationListAssertion) WithLen(length int) *ViolationListAssertion {
101
	if a == nil {
102
		return nil
103
	}
104
	a.t.Helper()
105
106
	actual := a.violations.Len()
107
	if actual != length {
108
		a.t.Errorf(fmt.Sprintf(
109
			"failed asserting that violation list length is equal to %d, actual is %d",
110
			length,
111
			actual,
112
		))
113
	}
114
115
	return a
116
}
117
118
// WithOneViolation checks that the violation list contains exactly one violation and returns
119
// a ViolationAssertion to test it.
120
func (a *ViolationListAssertion) WithOneViolation() *ViolationAssertion {
121
	if a == nil {
122
		return nil
123
	}
124
	a.t.Helper()
125
126
	if a.violations.Len() != 1 {
127
		assert.Fail(a.t, "failed asserting that violation list contains exactly one violation")
128
		return nil
129
	}
130
131
	return newViolationAssertionAt(a.t, a.violations.First(), 0)
132
}
133
134
// HasViolationAt checks that the violation list contains element at specific index and returns
135
// a ViolationAssertion to test it.
136
func (a *ViolationListAssertion) HasViolationAt(index int) *ViolationAssertion {
137
	if a == nil {
138
		return nil
139
	}
140
	a.t.Helper()
141
142
	violations := a.violations.AsSlice()
143
	if index >= len(violations) {
144
		assert.Fail(a.t, fmt.Sprintf("failed asserting that violation list contains violation at index %d", index))
145
		return nil
146
	}
147
148
	return newViolationAssertionAt(a.t, violations[index], 0)
149
}
150
151
// WithErrors checks that the violation list contains violations with specific codes in a given order.
152
func (a *ViolationListAssertion) WithErrors(errs ...error) *ViolationListAssertion {
153
	if a == nil {
154
		return nil
155
	}
156
	a.t.Helper()
157
158
	length := a.violations.Len()
159
	if length != len(errs) {
160
		assert.Fail(a.t, fmt.Sprintf(
161
			"failed asserting that violation list length is equal to %d, actual is %d",
162
			len(errs),
163
			length,
164
		))
165
		return a
166
	}
167
168
	a.violations.ForEach(func(i int, violation validation.Violation) error {
169
		if violation.Unwrap() != errs[i] {
170
			assert.Fail(a.t, fmt.Sprintf(
171
				`failed asserting that violation at %d has error "%s", actual is "%s"`,
172
				i,
173
				errs[i],
174
				violation.Unwrap(),
175
			))
176
		}
177
		return nil
178
	})
179
180
	return a
181
}
182
183
// WithAttributes checks that the violation list contains violations with the expected attributes in a given order.
184
// Empty values are not compared.
185
func (a *ViolationListAssertion) WithAttributes(violations ...ViolationAttributes) *ViolationListAssertion {
186
	if a == nil {
187
		return nil
188
	}
189
	a.t.Helper()
190
191
	length := a.violations.Len()
192
	if length != len(violations) {
193
		assert.Fail(a.t, fmt.Sprintf(
194
			"failed asserting that violation list length is equal to %d, actual is %d",
195
			len(violations),
196
			length,
197
		))
198
		return a
199
	}
200
201
	a.violations.ForEach(func(i int, violation validation.Violation) error {
202
		expected := violations[i]
203
204
		if expected.Error != nil && violation.Unwrap() != expected.Error {
205
			assert.Fail(a.t, fmt.Sprintf(
206
				`failed asserting that violation at %d has error "%s", actual is "%s"`,
207
				i,
208
				expected.Error,
209
				violation.Unwrap(),
210
			))
211
		}
212
213
		if expected.Message != "" && violation.Message() != expected.Message {
214
			assert.Fail(a.t, fmt.Sprintf(
215
				`failed asserting that violation at %d has message "%s", actual is "%s"`,
216
				i,
217
				expected.Message,
218
				violation.Message(),
219
			))
220
		}
221
222
		if expected.PropertyPath != "" && violation.PropertyPath().String() != expected.PropertyPath {
223
			assert.Fail(a.t, fmt.Sprintf(
224
				`failed asserting that violation at %d has property path "%s", actual is "%s"`,
225
				i,
226
				expected.PropertyPath,
227
				violation.PropertyPath().String(),
228
			))
229
		}
230
231
		return nil
232
	})
233
234
	return a
235
}
236
237
// ViolationAssertion is a structure for testing violation attributes.
238
type ViolationAssertion struct {
239
	t         TestingT
240
	violation validation.Violation
241
	index     int
242
}
243
244
func newViolationAssertion(t TestingT, violation validation.Violation) *ViolationAssertion {
245
	return &ViolationAssertion{t: t, violation: violation, index: -1}
246
}
247
248
func newViolationAssertionAt(t TestingT, violation validation.Violation, index int) *ViolationAssertion {
249
	return &ViolationAssertion{t: t, violation: violation, index: index}
250
}
251
252
// Assert is used for the client-side assertion of the violation by a callback function.
253
func (a *ViolationAssertion) Assert(
254
	assert func(tb testing.TB, violation validation.Violation),
255
) *ViolationAssertion {
256
	if a == nil {
257
		return nil
258
	}
259
	a.t.Helper()
260
261
	if tb, ok := a.t.(testing.TB); ok {
262
		assert(tb, a.violation)
263
	} else {
264
		a.t.Fatal("t must implement testing.TB")
265
	}
266
267
	return a
268
}
269
270
// WithError checks that violation has expected error.
271
func (a *ViolationAssertion) WithError(err error) *ViolationAssertion {
272
	if a == nil {
273
		return nil
274
	}
275
	a.t.Helper()
276
277
	actual := a.violation.Unwrap()
278
	if actual != err {
279
		assert.Fail(a.t, fmt.Sprintf(
280
			`failed asserting that violation%s has error "%s", actual is "%s"`,
281
			a.atIndex(),
282
			err,
283
			actual,
284
		))
285
	}
286
287
	return a
288
}
289
290
// WithMessage checks that violation has expected message.
291
func (a *ViolationAssertion) WithMessage(message string) *ViolationAssertion {
292
	if a == nil {
293
		return nil
294
	}
295
	a.t.Helper()
296
297
	actual := a.violation.Message()
298
	if actual != message {
299
		assert.Fail(a.t, fmt.Sprintf(
300
			`failed asserting that violation%s has message "%s", actual is "%s"`,
301
			a.atIndex(),
302
			message,
303
			actual,
304
		))
305
	}
306
307
	return a
308
}
309
310
// WithPropertyPath checks that the tested violation has an expected property path.
311
func (a *ViolationAssertion) WithPropertyPath(path string) *ViolationAssertion {
312
	if a == nil {
313
		return nil
314
	}
315
	a.t.Helper()
316
317
	actual := a.violation.PropertyPath().String()
318
	if actual != path {
319
		assert.Fail(a.t, fmt.Sprintf(
320
			`failed asserting that violation%s has property path "%s", actual is "%s"`,
321
			a.atIndex(),
322
			path,
323
			actual,
324
		))
325
	}
326
327
	return a
328
}
329
330
// EqualTo checks that the tested assertion is equal to the expected one.
331
func (a *ViolationAssertion) EqualTo(violation validation.Violation) *ViolationAssertion {
332
	if a == nil {
333
		return nil
334
	}
335
	a.t.Helper()
336
337
	assert.Equal(a.t, violation, a.violation, "failed asserting that violations are equal")
338
339
	return a
340
}
341
342
// EqualToError checks that violation rendered to an error is equal to the expected one.
343
func (a *ViolationAssertion) EqualToError(errString string) *ViolationAssertion {
344
	if a == nil {
345
		return nil
346
	}
347
	a.t.Helper()
348
349
	actual := a.violation.Error()
350
	if actual != errString {
351
		assert.Fail(a.t, fmt.Sprintf(
352
			`failed asserting that violation%s error is equal to "%s", actual is "%s"`,
353
			a.atIndex(),
354
			errString,
355
			actual,
356
		))
357
	}
358
359
	return a
360
}
361
362
func (a *ViolationAssertion) atIndex() string {
363
	if a.index < 0 {
364
		return ""
365
	}
366
	return " #" + strconv.Itoa(a.index)
367
}
368