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