main
1# frozen_string_literal: true
2
3RSpec.describe Scim::Kit::V2::Attribute do
4 subject { described_class.new(type: type, resource: resource) }
5
6 let(:resource) { Scim::Kit::V2::Resource.new(schemas: [schema], location: FFaker::Internet.uri('https')) }
7 let(:schema) { Scim::Kit::V2::Schema.new(id: Scim::Kit::V2::Schemas::USER, name: 'User', location: FFaker::Internet.uri('https')) }
8
9 context 'with strings' do
10 let(:type) { Scim::Kit::V2::AttributeType.new(name: 'userName', type: :string) }
11
12 context 'when valid' do
13 let(:user_name) { FFaker::Internet.user_name }
14
15 before { subject._value = user_name }
16
17 specify { expect(subject._value).to eql(user_name) }
18 specify { expect(subject.as_json[:userName]).to eql(user_name) }
19 specify { expect(subject).to be_valid }
20 end
21
22 context 'when multiple values are allowed' do
23 before { type.multi_valued = true }
24
25 specify { expect(subject._value).to match_array([]) }
26
27 context 'when multiple valid values are added' do
28 before do
29 subject._value = %w[superman batman]
30 end
31
32 specify { expect(subject._value).to match_array(%w[superman batman]) }
33 specify { expect(subject).to be_valid }
34 end
35
36 context 'when multiple invalid values are added' do
37 before do
38 subject._assign(['superman', {}], coerce: false)
39 end
40
41 specify { expect(subject).not_to be_valid }
42 end
43 end
44
45 context 'when a single value is provided' do
46 before do
47 type.multi_valued = true
48 subject._value = 'batman'
49 subject.valid?
50 end
51
52 specify { expect(subject).not_to be_valid }
53 specify { expect(subject.errors[:user_name]).to be_present }
54 end
55
56 context 'when the wrong type is used' do
57 before do
58 type.multi_valued = true
59 subject._assign([1.0, 2.0], coerce: false)
60 subject.valid?
61 end
62
63 specify { expect(subject).not_to be_valid }
64 specify { expect(subject.errors[:user_name]).to be_present }
65 end
66
67 context 'when integer' do
68 let(:number) { rand(100) }
69
70 before { subject._value = number }
71
72 specify { expect(subject._value).to eql(number.to_s) }
73 end
74
75 context 'when datetime' do
76 let(:datetime) { DateTime.now }
77
78 before { subject._value = datetime }
79
80 specify { expect(subject._value).to eql(datetime.to_s) }
81 end
82
83 context 'when not matching a canonical value' do
84 before do
85 type.canonical_values = %w[batman robin]
86 subject._value = 'spider man'
87 subject.valid?
88 end
89
90 specify { expect(subject).not_to be_valid }
91 specify { expect(subject.errors[:user_name]).to be_present }
92 end
93
94 context 'when canonical value is given' do
95 before do
96 type.canonical_values = %w[batman robin]
97 subject._value = 'batman'
98 end
99
100 specify { expect(subject._value).to eql('batman') }
101 end
102 end
103
104 context 'with boolean' do
105 let(:type) { Scim::Kit::V2::AttributeType.new(name: 'hungry', type: :boolean) }
106
107 context 'when true' do
108 before { subject._value = true }
109
110 specify { expect(subject._value).to be(true) }
111 specify { expect(subject.as_json[:hungry]).to be(true) }
112 end
113
114 context 'when false' do
115 before { subject._value = false }
116
117 specify { expect(subject._value).to be(false) }
118 specify { expect(subject.as_json[:hungry]).to be(false) }
119 end
120
121 context 'when invalid string' do
122 before { subject._value = 'hello' }
123
124 specify { expect(subject._value).to eql('hello') }
125 specify { expect(subject).not_to be_valid }
126 end
127 end
128
129 context 'with decimal' do
130 let(:type) { Scim::Kit::V2::AttributeType.new(name: 'measurement', type: :decimal) }
131
132 context 'when given float' do
133 before { subject._value = Math::PI }
134
135 specify { expect(subject._value).to eql(Math::PI) }
136 specify { expect(subject.as_json[:measurement]).to be(Math::PI) }
137 end
138
139 context 'when given an integer' do
140 before { subject._value = 42 }
141
142 specify { expect(subject._value).to eql(42.to_f) }
143 specify { expect(subject.as_json[:measurement]).to be(42.to_f) }
144 end
145 end
146
147 context 'with integer' do
148 let(:type) { Scim::Kit::V2::AttributeType.new(name: 'age', type: :integer) }
149
150 context 'when given integer' do
151 before { subject._value = 34 }
152
153 specify { expect(subject._value).to be(34) }
154 specify { expect(subject.as_json[:age]).to be(34) }
155 end
156
157 context 'when given float' do
158 before { subject._value = Math::PI }
159
160 specify { expect(subject._value).to eql(Math::PI.to_i) }
161 end
162 end
163
164 context 'with datetime' do
165 let(:type) { Scim::Kit::V2::AttributeType.new(name: 'birthdate', type: :datetime) }
166 let(:datetime) { DateTime.new(2019, 0o1, 0o6, 12, 35, 0o0) }
167
168 context 'when given a date time' do
169 before { subject._value = datetime }
170
171 specify { expect(subject._value).to eql(datetime) }
172 specify { expect(subject.as_json[:birthdate]).to eql(datetime.iso8601) }
173 end
174
175 context 'when given a string' do
176 before { subject._value = datetime.to_s }
177
178 specify { expect(subject._value).to eql(datetime) }
179 end
180 end
181
182 context 'with binary' do
183 let(:type) { Scim::Kit::V2::AttributeType.new(name: 'photo', type: :binary) }
184 let(:photo) { IO.read('./spec/fixtures/avatar.png', mode: 'rb') }
185
186 context 'when given a .png' do
187 before { subject._value = photo }
188
189 specify { expect(subject._value).to eql(Base64.strict_encode64(photo)) }
190 specify { expect(subject.as_json[:photo]).to eql(Base64.strict_encode64(photo)) }
191 end
192 end
193
194 context 'with reference' do
195 let(:type) { Scim::Kit::V2::AttributeType.new(name: 'group', type: :reference) }
196 let(:uri) { FFaker::Internet.uri('https') }
197
198 before { subject._value = uri }
199
200 specify { expect(subject._value).to eql(uri) }
201 specify { expect(subject.as_json[:group]).to eql(uri) }
202 end
203
204 context 'with complex type' do
205 let(:type) do
206 x = Scim::Kit::V2::AttributeType.new(name: 'name', type: :complex)
207 x.add_attribute(name: 'familyName')
208 x.add_attribute(name: 'givenName')
209 x
210 end
211
212 before do
213 subject.family_name = 'Garrett'
214 subject.given_name = 'Tsuyoshi'
215 end
216
217 specify { expect(subject.family_name).to eql('Garrett') }
218 specify { expect(subject.given_name).to eql('Tsuyoshi') }
219 specify { expect(subject.as_json[:name][:familyName]).to eql('Garrett') }
220 specify { expect(subject.as_json[:name][:givenName]).to eql('Tsuyoshi') }
221 end
222
223 context 'with single valued complex type' do
224 let(:type) do
225 x = Scim::Kit::V2::AttributeType.new(name: :person, type: :complex)
226 x.add_attribute(name: :name)
227 x.add_attribute(name: :age, type: :integer)
228 x
229 end
230
231 specify do
232 subject.name = 'mo'
233 subject.age = 34
234 expect(subject).to be_valid
235 end
236
237 specify do
238 subject.name = 'mo'
239 subject.age = []
240 expect(subject).not_to be_valid
241 end
242 end
243
244 context 'with multi valued complex type' do
245 let(:type) do
246 x = Scim::Kit::V2::AttributeType.new(name: 'emails', type: :complex)
247 x.multi_valued = true
248 x.add_attribute(name: 'value') do |y|
249 y.required = true
250 end
251 x.add_attribute(name: 'primary', type: :boolean)
252 x
253 end
254 let(:email) { FFaker::Internet.email }
255 let(:other_email) { FFaker::Internet.email }
256
257 before do
258 subject._value = [
259 { value: email, primary: true },
260 { value: other_email, primary: false }
261 ]
262 end
263
264 specify { expect(subject._value).to match_array([{ value: email, primary: true }, { value: other_email, primary: false }]) }
265 specify { expect(subject.as_json[:emails]).to match_array([{ value: email, primary: true }, { value: other_email, primary: false }]) }
266 specify { expect(subject).to be_valid }
267
268 context 'when the hash is invalid' do
269 before do
270 subject._value = [{ blah: 'blah' }]
271 subject.valid?
272 end
273
274 specify { expect(subject).not_to be_valid }
275 specify { expect(subject.errors[:blah]).to be_present }
276 specify { expect(subject.errors[:value]).to be_present }
277 end
278 end
279
280 context 'when the resource is in server mode' do
281 let(:type) { Scim::Kit::V2::AttributeType.new(name: 'userName', type: :string) }
282 let(:resource) { instance_double(Scim::Kit::V2::Resource) }
283
284 before do
285 allow(resource).to receive(:mode?).with(:server).and_return(true)
286 allow(resource).to receive(:mode?).with(:client).and_return(false)
287 end
288
289 context 'when the type is read only' do
290 before { type.mutability = :read_only }
291
292 specify { expect(subject).to be_renderable }
293 end
294
295 context 'when the type is write only' do
296 before { type.mutability = :write_only }
297
298 specify { expect(subject).not_to be_renderable }
299 end
300
301 context 'when returned type is `never`' do
302 before { type.returned = :never }
303
304 specify { expect(subject).not_to be_renderable }
305 end
306 end
307
308 context 'when the resource is in client mode' do
309 let(:type) { Scim::Kit::V2::AttributeType.new(name: 'userName', type: :string) }
310 let(:resource) { instance_double(Scim::Kit::V2::Resource) }
311
312 before do
313 allow(resource).to receive(:mode?).with(:server).and_return(false)
314 allow(resource).to receive(:mode?).with(:client).and_return(true)
315 end
316
317 context 'when the type is read only' do
318 before { type.mutability = :read_only }
319
320 specify { expect(subject).not_to be_renderable }
321 end
322
323 context 'when the type is write only' do
324 before { type.mutability = :write_only }
325
326 specify do
327 subject._value = 'hello'
328 expect(subject).to be_renderable
329 end
330
331 specify do
332 subject._value = nil
333 expect(subject).not_to be_renderable
334 end
335 end
336 end
337end