symmetric-encryption 3.4.0 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +102 -55
- data/Rakefile +13 -8
- data/lib/rails/generators/symmetric_encryption/config/config_generator.rb +1 -1
- data/lib/rails/generators/symmetric_encryption/heroku_config/templates/symmetric-encryption.yml +2 -2
- data/lib/rails/generators/symmetric_encryption/new_keys/new_keys_generator.rb +2 -2
- data/lib/symmetric_encryption.rb +7 -6
- data/lib/symmetric_encryption/cipher.rb +4 -4
- data/lib/symmetric_encryption/extensions/active_record/base.rb +6 -46
- data/lib/symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key.rb +129 -0
- data/lib/symmetric_encryption/{mongoid.rb → extensions/mongoid/encrypted.rb} +12 -46
- data/lib/symmetric_encryption/generator.rb +54 -0
- data/lib/symmetric_encryption/railtie.rb +3 -3
- data/lib/symmetric_encryption/railties/symmetric_encryption.rake +1 -1
- data/lib/symmetric_encryption/railties/symmetric_encryption_validator.rb +1 -1
- data/lib/symmetric_encryption/reader.rb +3 -3
- data/lib/symmetric_encryption/symmetric_encryption.rb +25 -15
- data/lib/symmetric_encryption/version.rb +1 -1
- data/lib/symmetric_encryption/writer.rb +4 -4
- data/test/active_record_test.rb +474 -0
- data/test/cipher_test.rb +15 -15
- data/test/config/mongo_mapper.yml +7 -0
- data/test/{field_encrypted_test.rb → mongo_mapper_test.rb} +68 -67
- data/test/mongoid_test.rb +535 -0
- data/test/reader_test.rb +10 -10
- data/test/symmetric_encryption_test.rb +27 -27
- data/test/test_db.sqlite3 +0 -0
- data/test/test_helper.rb +0 -1
- data/test/writer_test.rb +2 -2
- metadata +14 -8
- data/test/attr_encrypted_test.rb +0 -622
@@ -0,0 +1,474 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
ActiveRecord::Base.logger = SemanticLogger[ActiveRecord]
|
4
|
+
ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read('test/config/database.yml')).result)
|
5
|
+
ActiveRecord::Base.establish_connection('test')
|
6
|
+
|
7
|
+
ActiveRecord::Schema.define version: 0 do
|
8
|
+
create_table :users, force: true do |t|
|
9
|
+
t.string :encrypted_bank_account_number
|
10
|
+
t.string :encrypted_social_security_number
|
11
|
+
t.string :encrypted_string
|
12
|
+
t.text :encrypted_long_string
|
13
|
+
t.text :encrypted_data_yaml
|
14
|
+
t.text :encrypted_data_json
|
15
|
+
t.string :name
|
16
|
+
|
17
|
+
t.string :encrypted_integer_value
|
18
|
+
t.string :encrypted_float_value
|
19
|
+
t.string :encrypted_decimal_value
|
20
|
+
t.string :encrypted_datetime_value
|
21
|
+
t.string :encrypted_time_value
|
22
|
+
t.string :encrypted_date_value
|
23
|
+
t.string :encrypted_true_value
|
24
|
+
t.string :encrypted_false_value
|
25
|
+
|
26
|
+
t.string :encrypted_text
|
27
|
+
t.string :encrypted_number
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class User < ActiveRecord::Base
|
32
|
+
attr_encrypted :bank_account_number
|
33
|
+
attr_encrypted :social_security_number
|
34
|
+
attr_encrypted :string, random_iv: true
|
35
|
+
attr_encrypted :long_string, random_iv: true, compress: true
|
36
|
+
attr_encrypted :data_yaml, random_iv: true, compress: true, type: :yaml
|
37
|
+
attr_encrypted :data_json, random_iv: true, compress: true, type: :json
|
38
|
+
|
39
|
+
attr_encrypted :integer_value, type: :integer
|
40
|
+
attr_encrypted :float_value, type: :float
|
41
|
+
attr_encrypted :decimal_value, type: :decimal
|
42
|
+
attr_encrypted :datetime_value, type: :datetime
|
43
|
+
attr_encrypted :time_value, type: :time
|
44
|
+
attr_encrypted :date_value, type: :date
|
45
|
+
attr_encrypted :true_value, type: :boolean
|
46
|
+
attr_encrypted :false_value, type: :boolean
|
47
|
+
|
48
|
+
validates :encrypted_bank_account_number, symmetric_encryption: true
|
49
|
+
validates :encrypted_social_security_number, symmetric_encryption: true
|
50
|
+
|
51
|
+
attr_encrypted :text, type: :string
|
52
|
+
attr_encrypted :number, type: :integer
|
53
|
+
|
54
|
+
validates :text, format: { with: /\A[a-zA-Z ]+\z/, message: "only allows letters" }, presence: true
|
55
|
+
validates :number, presence: true
|
56
|
+
end
|
57
|
+
|
58
|
+
# Initialize the database connection
|
59
|
+
config_file = File.join(File.dirname(__FILE__), 'config', 'database.yml')
|
60
|
+
raise "database config not found. Create a config file at: test/config/database.yml" unless File.exists? config_file
|
61
|
+
|
62
|
+
cfg = YAML.load(ERB.new(File.new(config_file).read).result)['test']
|
63
|
+
raise("Environment 'test' not defined in test/config/database.yml") unless cfg
|
64
|
+
|
65
|
+
User.establish_connection(cfg)
|
66
|
+
|
67
|
+
#
|
68
|
+
# Unit Test for attr_encrypted extensions in ActiveRecord
|
69
|
+
#
|
70
|
+
class ActiveRecordTest < Test::Unit::TestCase
|
71
|
+
context 'ActiveRecord' do
|
72
|
+
INTEGER_VALUE = 12
|
73
|
+
FLOAT_VALUE = 88.12345
|
74
|
+
DECIMAL_VALUE = BigDecimal.new("22.51")
|
75
|
+
DATETIME_VALUE = DateTime.new(2001, 11, 26, 20, 55, 54, "-5")
|
76
|
+
TIME_VALUE = Time.new(2013, 01, 01, 22, 30, 00, "-04:00")
|
77
|
+
DATE_VALUE = Date.new(1927, 04, 02)
|
78
|
+
|
79
|
+
setup do
|
80
|
+
@bank_account_number = "1234567890"
|
81
|
+
@bank_account_number_encrypted = "QEVuQwIAL94ArJeFlJrZp6SYsvoOGA=="
|
82
|
+
|
83
|
+
@social_security_number = "987654321"
|
84
|
+
@social_security_number_encrypted = "QEVuQwIAS+8X1NRrqdfEIQyFHVPuVA=="
|
85
|
+
|
86
|
+
@string = "A string containing some data to be encrypted with a random initialization vector"
|
87
|
+
@long_string = "A string containing some data to be encrypted with a random initialization vector and compressed since it takes up so much space in plain text form"
|
88
|
+
|
89
|
+
@name = 'Joe Bloggs'
|
90
|
+
|
91
|
+
@h = { a: 'A', b: 'B' }
|
92
|
+
|
93
|
+
@user = User.new(
|
94
|
+
# Encrypted Attribute
|
95
|
+
bank_account_number: @bank_account_number,
|
96
|
+
# Encrypted Attribute
|
97
|
+
social_security_number: @social_security_number,
|
98
|
+
name: @name,
|
99
|
+
# data type specific fields
|
100
|
+
integer_value: INTEGER_VALUE,
|
101
|
+
float_value: FLOAT_VALUE,
|
102
|
+
decimal_value: DECIMAL_VALUE,
|
103
|
+
datetime_value: DATETIME_VALUE,
|
104
|
+
time_value: TIME_VALUE,
|
105
|
+
date_value: DATE_VALUE,
|
106
|
+
true_value: true,
|
107
|
+
false_value: false,
|
108
|
+
data_yaml: @h.dup,
|
109
|
+
data_json: @h.dup,
|
110
|
+
text: 'hello',
|
111
|
+
number: '21'
|
112
|
+
)
|
113
|
+
end
|
114
|
+
|
115
|
+
should 'have encrypted methods' do
|
116
|
+
assert_equal true, @user.respond_to?(:encrypted_bank_account_number)
|
117
|
+
assert_equal true, @user.respond_to?(:bank_account_number)
|
118
|
+
assert_equal true, @user.respond_to?(:encrypted_social_security_number)
|
119
|
+
assert_equal true, @user.respond_to?(:social_security_number)
|
120
|
+
assert_equal true, @user.respond_to?(:data_yaml)
|
121
|
+
assert_equal true, @user.respond_to?(:data_json)
|
122
|
+
assert_equal false, @user.respond_to?(:encrypted_name)
|
123
|
+
end
|
124
|
+
|
125
|
+
should 'have unencrypted values' do
|
126
|
+
assert_equal @bank_account_number, @user.bank_account_number
|
127
|
+
assert_equal @social_security_number, @user.social_security_number
|
128
|
+
end
|
129
|
+
|
130
|
+
should 'have encrypted values' do
|
131
|
+
assert_equal @bank_account_number_encrypted, @user.encrypted_bank_account_number
|
132
|
+
assert_equal @social_security_number_encrypted, @user.encrypted_social_security_number
|
133
|
+
end
|
134
|
+
|
135
|
+
should 'support same iv' do
|
136
|
+
@user.social_security_number = @social_security_number
|
137
|
+
assert first_value = @user.social_security_number
|
138
|
+
# Assign the same value
|
139
|
+
@user.social_security_number = @social_security_number
|
140
|
+
assert_equal first_value, @user.social_security_number
|
141
|
+
end
|
142
|
+
|
143
|
+
should 'support a random iv' do
|
144
|
+
@user.string = @string
|
145
|
+
assert first_value = @user.encrypted_string
|
146
|
+
# Assign the same value
|
147
|
+
@user.string = @string.dup
|
148
|
+
assert_equal true, first_value != @user.encrypted_string
|
149
|
+
end
|
150
|
+
|
151
|
+
should 'support a random iv and compress' do
|
152
|
+
@user.string = @long_string
|
153
|
+
@user.long_string = @long_string
|
154
|
+
|
155
|
+
assert_equal true, (@user.encrypted_long_string.length.to_f / @user.encrypted_string.length) < 0.8
|
156
|
+
end
|
157
|
+
|
158
|
+
should 'encrypt' do
|
159
|
+
user = User.new
|
160
|
+
user.bank_account_number = @bank_account_number
|
161
|
+
assert_equal @bank_account_number, user.bank_account_number
|
162
|
+
assert_equal @bank_account_number_encrypted, user.encrypted_bank_account_number
|
163
|
+
end
|
164
|
+
|
165
|
+
should 'allow lookups using unencrypted or encrypted column name' do
|
166
|
+
@user.save!
|
167
|
+
|
168
|
+
inq = User.find_by_bank_account_number(@bank_account_number)
|
169
|
+
assert_equal @bank_account_number, inq.bank_account_number
|
170
|
+
assert_equal @bank_account_number_encrypted, inq.encrypted_bank_account_number
|
171
|
+
|
172
|
+
@user.delete
|
173
|
+
end
|
174
|
+
|
175
|
+
should 'all paths should lead to the same result' do
|
176
|
+
assert_equal @bank_account_number_encrypted, (@user.encrypted_social_security_number = @bank_account_number_encrypted)
|
177
|
+
assert_equal @bank_account_number, @user.social_security_number
|
178
|
+
assert_equal @bank_account_number_encrypted, @user.encrypted_social_security_number
|
179
|
+
end
|
180
|
+
|
181
|
+
should 'all paths should lead to the same result 2' do
|
182
|
+
assert_equal @bank_account_number, (@user.social_security_number = @bank_account_number)
|
183
|
+
assert_equal @bank_account_number_encrypted, @user.encrypted_social_security_number
|
184
|
+
assert_equal @bank_account_number, @user.social_security_number
|
185
|
+
end
|
186
|
+
|
187
|
+
should 'all paths should lead to the same result, check uninitialized' do
|
188
|
+
user = User.new
|
189
|
+
assert_equal nil, user.social_security_number
|
190
|
+
assert_equal @bank_account_number, (user.social_security_number = @bank_account_number)
|
191
|
+
assert_equal @bank_account_number, user.social_security_number
|
192
|
+
assert_equal @bank_account_number_encrypted, user.encrypted_social_security_number
|
193
|
+
|
194
|
+
assert_equal nil, (user.social_security_number = nil)
|
195
|
+
assert_equal nil, user.social_security_number
|
196
|
+
assert_equal nil, user.encrypted_social_security_number
|
197
|
+
end
|
198
|
+
|
199
|
+
should 'allow unencrypted values to be passed to the constructor' do
|
200
|
+
user = User.new(bank_account_number: @bank_account_number, social_security_number: @social_security_number)
|
201
|
+
assert_equal @bank_account_number, user.bank_account_number
|
202
|
+
assert_equal @social_security_number, user.social_security_number
|
203
|
+
assert_equal @bank_account_number_encrypted, user.encrypted_bank_account_number
|
204
|
+
assert_equal @social_security_number_encrypted, user.encrypted_social_security_number
|
205
|
+
end
|
206
|
+
|
207
|
+
should 'return encrypted attributes for the class' do
|
208
|
+
expect = {social_security_number: :encrypted_social_security_number, bank_account_number: :encrypted_bank_account_number}
|
209
|
+
result = User.encrypted_attributes
|
210
|
+
expect.each_pair {|k,v| assert_equal expect[k], result[k]}
|
211
|
+
end
|
212
|
+
|
213
|
+
should 'return encrypted keys for the class' do
|
214
|
+
expect = [:social_security_number, :bank_account_number]
|
215
|
+
result = User.encrypted_keys
|
216
|
+
expect.each {|val| assert_equal true, result.include?(val)}
|
217
|
+
|
218
|
+
# Also check encrypted_attribute?
|
219
|
+
expect.each {|val| assert_equal true, User.encrypted_attribute?(val)}
|
220
|
+
end
|
221
|
+
|
222
|
+
should 'return encrypted columns for the class' do
|
223
|
+
expect = [:encrypted_social_security_number, :encrypted_bank_account_number]
|
224
|
+
result = User.encrypted_columns
|
225
|
+
expect.each {|val| assert_equal true, result.include?(val)}
|
226
|
+
|
227
|
+
# Also check encrypted_column?
|
228
|
+
expect.each {|val| assert_equal true, User.encrypted_column?(val)}
|
229
|
+
end
|
230
|
+
|
231
|
+
should 'validate encrypted data' do
|
232
|
+
assert_equal true, @user.valid?
|
233
|
+
@user.encrypted_bank_account_number = '123'
|
234
|
+
assert_equal false, @user.valid?
|
235
|
+
assert_equal ["must be a value encrypted using SymmetricEncryption.encrypt"], @user.errors[:encrypted_bank_account_number]
|
236
|
+
@user.encrypted_bank_account_number = SymmetricEncryption.encrypt('123')
|
237
|
+
assert_equal true, @user.valid?
|
238
|
+
@user.bank_account_number = '123'
|
239
|
+
assert_equal true, @user.valid?
|
240
|
+
end
|
241
|
+
|
242
|
+
should 'validate un-encrypted string data' do
|
243
|
+
assert_equal true, @user.valid?
|
244
|
+
@user.text = '123'
|
245
|
+
assert_equal false, @user.valid?
|
246
|
+
assert_equal ["only allows letters"], @user.errors[:text]
|
247
|
+
@user.text = nil
|
248
|
+
assert_equal false, @user.valid?
|
249
|
+
assert_equal ["only allows letters", "can't be blank"], @user.errors[:text]
|
250
|
+
@user.text = ''
|
251
|
+
assert_equal false, @user.valid?
|
252
|
+
assert_equal ["only allows letters", "can't be blank"], @user.errors[:text]
|
253
|
+
end
|
254
|
+
|
255
|
+
should 'validate un-encrypted integer data with coercion' do
|
256
|
+
assert_equal true, @user.valid?
|
257
|
+
@user.number = '123'
|
258
|
+
assert_equal true, @user.valid?
|
259
|
+
assert_equal 123, @user.number
|
260
|
+
assert_equal true, @user.valid?
|
261
|
+
@user.number = ''
|
262
|
+
assert_equal false, @user.valid?
|
263
|
+
assert_equal nil, @user.number
|
264
|
+
assert_equal ["can't be blank"], @user.errors[:number]
|
265
|
+
@user.number = nil
|
266
|
+
assert_equal nil, @user.number
|
267
|
+
assert_equal nil, @user.encrypted_number
|
268
|
+
assert_equal false, @user.valid?
|
269
|
+
assert_equal ["can't be blank"], @user.errors[:number]
|
270
|
+
end
|
271
|
+
|
272
|
+
context "with saved user" do
|
273
|
+
setup do
|
274
|
+
@user.save!
|
275
|
+
end
|
276
|
+
|
277
|
+
teardown do
|
278
|
+
@user.destroy
|
279
|
+
end
|
280
|
+
|
281
|
+
should "return correct data type before save" do
|
282
|
+
u = User.new(integer_value: "5")
|
283
|
+
assert_equal 5, u.integer_value
|
284
|
+
assert u.integer_value.kind_of?(Integer)
|
285
|
+
end
|
286
|
+
|
287
|
+
should "handle gsub! for non-encrypted_field" do
|
288
|
+
@user.name.gsub!('a', 'v')
|
289
|
+
new_name = @name.gsub('a', 'v')
|
290
|
+
assert_equal new_name, @user.name
|
291
|
+
@user.reload
|
292
|
+
assert_equal new_name, @user.name
|
293
|
+
end
|
294
|
+
|
295
|
+
should "prevent gsub! on non-encrypted value of encrypted_field" do
|
296
|
+
# can't modify frozen String
|
297
|
+
assert_raises RuntimeError do
|
298
|
+
@user.bank_account_number.gsub!('5', '4')
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
should "revert changes on reload" do
|
303
|
+
new_bank_account_number = '444444444'
|
304
|
+
@user.bank_account_number = new_bank_account_number
|
305
|
+
assert_equal new_bank_account_number, @user.bank_account_number
|
306
|
+
|
307
|
+
# Reload User model from the database
|
308
|
+
@user.reload
|
309
|
+
assert_equal @bank_account_number_encrypted, @user.encrypted_bank_account_number
|
310
|
+
assert_equal @bank_account_number, @user.bank_account_number
|
311
|
+
end
|
312
|
+
|
313
|
+
should "revert changes to encrypted field on reload" do
|
314
|
+
new_bank_account_number = '111111111'
|
315
|
+
new_encrypted_bank_account_number = SymmetricEncryption.encrypt(new_bank_account_number)
|
316
|
+
@user.encrypted_bank_account_number = new_encrypted_bank_account_number
|
317
|
+
assert_equal new_encrypted_bank_account_number, @user.encrypted_bank_account_number
|
318
|
+
assert_equal new_bank_account_number, @user.bank_account_number
|
319
|
+
|
320
|
+
# Reload User model from the database
|
321
|
+
@user.reload
|
322
|
+
assert_equal @bank_account_number_encrypted, @user.encrypted_bank_account_number
|
323
|
+
assert_equal @bank_account_number, @user.bank_account_number
|
324
|
+
end
|
325
|
+
|
326
|
+
context "data types" do
|
327
|
+
setup do
|
328
|
+
@user_clone = User.find(@user.id)
|
329
|
+
end
|
330
|
+
|
331
|
+
[
|
332
|
+
{ attribute: :integer_value, klass: Integer, value: INTEGER_VALUE, new_value: 98 },
|
333
|
+
{ attribute: :float_value, klass: Float, value: FLOAT_VALUE, new_value: 45.4321 },
|
334
|
+
{ attribute: :decimal_value, klass: BigDecimal, value: DECIMAL_VALUE, new_value: BigDecimal.new("99.95"), coercible: "22.51"},
|
335
|
+
{ attribute: :datetime_value, klass: DateTime, value: DATETIME_VALUE, new_value: DateTime.new(1998, 10, 21, 8, 33, 28, "+5"), coercible: DATETIME_VALUE.to_time},
|
336
|
+
{ attribute: :time_value, klass: Time, value: TIME_VALUE, new_value: Time.new(2000, 01, 01, 22, 30, 00, "-04:00") },
|
337
|
+
{ attribute: :date_value, klass: Date, value: DATE_VALUE, new_value: Date.new(2027, 04, 02), coercible: DATE_VALUE.to_time },
|
338
|
+
{ attribute: :true_value, klass: TrueClass, value: true, new_value: false },
|
339
|
+
{ attribute: :false_value, klass: FalseClass, value: false, new_value: true },
|
340
|
+
].each do |value_test|
|
341
|
+
context "#{value_test[:klass]} values" do
|
342
|
+
setup do
|
343
|
+
@attribute = value_test[:attribute]
|
344
|
+
@klass = value_test[:klass]
|
345
|
+
@value = value_test[:value]
|
346
|
+
@coercible = value_test[:coercible] || @value.to_s
|
347
|
+
@new_value = value_test[:new_value]
|
348
|
+
end
|
349
|
+
|
350
|
+
should "return correct data type" do
|
351
|
+
assert_equal @value, @user_clone.send(@attribute)
|
352
|
+
assert @user.clone.send(@attribute).kind_of?(@klass)
|
353
|
+
end
|
354
|
+
|
355
|
+
should "coerce data type before save" do
|
356
|
+
u = User.new(@attribute => @value)
|
357
|
+
assert_equal @value, u.send(@attribute)
|
358
|
+
assert u.send(@attribute).kind_of?(@klass), "Value supposed to be coerced into #{@klass}, but is #{u.send(@attribute).class.name}"
|
359
|
+
end
|
360
|
+
|
361
|
+
should "permit replacing value with nil" do
|
362
|
+
@user_clone.send("#{@attribute}=".to_sym, nil)
|
363
|
+
@user_clone.save!
|
364
|
+
|
365
|
+
@user.reload
|
366
|
+
assert_nil @user.send(@attribute)
|
367
|
+
assert_nil @user.send("encrypted_#{@attribute}".to_sym)
|
368
|
+
end
|
369
|
+
|
370
|
+
should "permit replacing value with an empty string" do
|
371
|
+
@user_clone.send("#{@attribute}=".to_sym, '')
|
372
|
+
@user_clone.save!
|
373
|
+
|
374
|
+
@user.reload
|
375
|
+
assert_nil @user.send(@attribute)
|
376
|
+
assert_nil @user.send("encrypted_#{@attribute}".to_sym)
|
377
|
+
end
|
378
|
+
|
379
|
+
should "permit replacing value with a blank string" do
|
380
|
+
@user_clone.send("#{@attribute}=".to_sym, ' ')
|
381
|
+
@user_clone.save!
|
382
|
+
|
383
|
+
@user.reload
|
384
|
+
assert_nil @user.send(@attribute)
|
385
|
+
assert_nil @user.send("encrypted_#{@attribute}".to_sym)
|
386
|
+
end
|
387
|
+
|
388
|
+
should "permit replacing value" do
|
389
|
+
@user_clone.send("#{@attribute}=".to_sym, @new_value)
|
390
|
+
@user_clone.save!
|
391
|
+
|
392
|
+
@user.reload
|
393
|
+
assert_equal @new_value, @user.send(@attribute)
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
context "JSON Serialization" do
|
399
|
+
setup do
|
400
|
+
# JSON Does not support symbols, so they will come back as strings
|
401
|
+
# Convert symbols to string in the test
|
402
|
+
@h.keys.each do |k|
|
403
|
+
@h[k.to_s] = @h[k]
|
404
|
+
@h.delete(k)
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
should "return correct data type" do
|
409
|
+
assert_equal @h, @user_clone.data_json
|
410
|
+
assert @user.clone.data_json.kind_of?(Hash)
|
411
|
+
end
|
412
|
+
|
413
|
+
should "not coerce data type (leaves as hash) before save" do
|
414
|
+
u = User.new(data_json: @h)
|
415
|
+
assert_equal @h, u.data_json
|
416
|
+
assert u.data_json.kind_of?(Hash)
|
417
|
+
end
|
418
|
+
|
419
|
+
should "permit replacing value with nil" do
|
420
|
+
@user_clone.data_json = nil
|
421
|
+
@user_clone.save!
|
422
|
+
|
423
|
+
@user.reload
|
424
|
+
assert_nil @user.data_json
|
425
|
+
assert_nil @user.encrypted_data_json
|
426
|
+
end
|
427
|
+
|
428
|
+
should "permit replacing value" do
|
429
|
+
new_value = @h.clone
|
430
|
+
new_value['c'] = 'C'
|
431
|
+
@user_clone.data_json = new_value
|
432
|
+
@user_clone.save!
|
433
|
+
|
434
|
+
@user.reload
|
435
|
+
assert_equal new_value, @user.data_json
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
context "YAML Serialization" do
|
440
|
+
should "return correct data type" do
|
441
|
+
assert_equal @h, @user_clone.data_yaml
|
442
|
+
assert @user.clone.data_yaml.kind_of?(Hash)
|
443
|
+
end
|
444
|
+
|
445
|
+
should "not coerce data type (leaves as hash) before save" do
|
446
|
+
u = User.new(data_yaml: @h)
|
447
|
+
assert_equal @h, u.data_yaml
|
448
|
+
assert u.data_yaml.kind_of?(Hash)
|
449
|
+
end
|
450
|
+
|
451
|
+
should "permit replacing value with nil" do
|
452
|
+
@user_clone.data_yaml = nil
|
453
|
+
@user_clone.save!
|
454
|
+
|
455
|
+
@user.reload
|
456
|
+
assert_nil @user.data_yaml
|
457
|
+
assert_nil @user.encrypted_data_yaml
|
458
|
+
end
|
459
|
+
|
460
|
+
should "permit replacing value" do
|
461
|
+
new_value = @h.clone
|
462
|
+
new_value[:c] = 'C'
|
463
|
+
@user_clone.data_yaml = new_value
|
464
|
+
@user_clone.save!
|
465
|
+
|
466
|
+
@user.reload
|
467
|
+
assert_equal new_value, @user.data_yaml
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
end
|
472
|
+
end
|
473
|
+
end
|
474
|
+
end
|