Test Setup Failed
Pull Request — main (#67)
by Igor
02:20
created

validationtest.newViolationAssertionAt   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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