1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
require 'cases/helper'
require 'models/topic'
class ValidatesWithTest < ActiveModel::TestCase
def teardown
Topic.clear_validators!
end
ERROR_MESSAGE = "Validation error from validator"
OTHER_ERROR_MESSAGE = "Validation error from other validator"
class ValidatorThatAddsErrors < ActiveModel::Validator
def validate(record)
record.errors[:base] << ERROR_MESSAGE
end
end
class OtherValidatorThatAddsErrors < ActiveModel::Validator
def validate(record)
record.errors[:base] << OTHER_ERROR_MESSAGE
end
end
class ValidatorThatDoesNotAddErrors < ActiveModel::Validator
def validate(record)
end
end
class ValidatorThatValidatesOptions < ActiveModel::Validator
def validate(record)
if options[:field] == :first_name
record.errors[:base] << ERROR_MESSAGE
end
end
end
class ValidatorPerEachAttribute < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors[attribute] << "Value is #{value}"
end
end
class ValidatorCheckValidity < ActiveModel::EachValidator
def check_validity!
raise "boom!"
end
end
test "validation with class that adds errors" do
Topic.validates_with(ValidatorThatAddsErrors)
topic = Topic.new
assert topic.invalid?, "A class that adds errors causes the record to be invalid"
assert topic.errors[:base].include?(ERROR_MESSAGE)
end
test "with a class that returns valid" do
Topic.validates_with(ValidatorThatDoesNotAddErrors)
topic = Topic.new
assert topic.valid?, "A class that does not add errors does not cause the record to be invalid"
end
test "with multiple classes" do
Topic.validates_with(ValidatorThatAddsErrors, OtherValidatorThatAddsErrors)
topic = Topic.new
assert topic.invalid?
assert topic.errors[:base].include?(ERROR_MESSAGE)
assert topic.errors[:base].include?(OTHER_ERROR_MESSAGE)
end
test "with if statements that return false" do
Topic.validates_with(ValidatorThatAddsErrors, if: "1 == 2")
topic = Topic.new
assert topic.valid?
end
test "with if statements that return true" do
Topic.validates_with(ValidatorThatAddsErrors, if: "1 == 1")
topic = Topic.new
assert topic.invalid?
assert topic.errors[:base].include?(ERROR_MESSAGE)
end
test "with unless statements that return true" do
Topic.validates_with(ValidatorThatAddsErrors, unless: "1 == 1")
topic = Topic.new
assert topic.valid?
end
test "with unless statements that returns false" do
Topic.validates_with(ValidatorThatAddsErrors, unless: "1 == 2")
topic = Topic.new
assert topic.invalid?
assert topic.errors[:base].include?(ERROR_MESSAGE)
end
test "passes all configuration options to the validator class" do
topic = Topic.new
validator = Minitest::Mock.new
validator.expect(:new, validator, [{foo: :bar, if: "1 == 1", class: Topic}])
validator.expect(:validate, nil, [topic])
validator.expect(:is_a?, false, [Symbol])
Topic.validates_with(validator, if: "1 == 1", foo: :bar)
assert topic.valid?
validator.verify
end
test "validates_with with options" do
Topic.validates_with(ValidatorThatValidatesOptions, field: :first_name)
topic = Topic.new
assert topic.invalid?
assert topic.errors[:base].include?(ERROR_MESSAGE)
end
test "validates_with each validator" do
Topic.validates_with(ValidatorPerEachAttribute, attributes: [:title, :content])
topic = Topic.new title: "Title", content: "Content"
assert topic.invalid?
assert_equal ["Value is Title"], topic.errors[:title]
assert_equal ["Value is Content"], topic.errors[:content]
end
test "each validator checks validity" do
assert_raise RuntimeError do
Topic.validates_with(ValidatorCheckValidity, attributes: [:title])
end
end
test "each validator expects attributes to be given" do
assert_raise ArgumentError do
Topic.validates_with(ValidatorPerEachAttribute)
end
end
test "each validator skip nil values if :allow_nil is set to true" do
Topic.validates_with(ValidatorPerEachAttribute, attributes: [:title, :content], allow_nil: true)
topic = Topic.new content: ""
assert topic.invalid?
assert topic.errors[:title].empty?
assert_equal ["Value is "], topic.errors[:content]
end
test "each validator skip blank values if :allow_blank is set to true" do
Topic.validates_with(ValidatorPerEachAttribute, attributes: [:title, :content], allow_blank: true)
topic = Topic.new content: ""
assert topic.valid?
assert topic.errors[:title].empty?
assert topic.errors[:content].empty?
end
test "validates_with can validate with an instance method" do
Topic.validates :title, with: :my_validation
topic = Topic.new title: "foo"
assert topic.valid?
assert topic.errors[:title].empty?
topic = Topic.new
assert !topic.valid?
assert_equal ['is missing'], topic.errors[:title]
end
test "optionally pass in the attribute being validated when validating with an instance method" do
Topic.validates :title, :content, with: :my_validation_with_arg
topic = Topic.new title: "foo"
assert !topic.valid?
assert topic.errors[:title].empty?
assert_equal ['is missing'], topic.errors[:content]
end
end
|