hoodoo 1.12.4 → 1.13.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/lib/hoodoo/presenters/base_dsl.rb +284 -70
- data/lib/hoodoo/presenters/types/array.rb +74 -7
- data/lib/hoodoo/presenters/types/field.rb +1 -1
- data/lib/hoodoo/presenters/types/hash.rb +31 -16
- data/lib/hoodoo/utilities/utilities.rb +2 -0
- data/lib/hoodoo/version.rb +1 -1
- data/spec/presenters/base_dsl_spec.rb +29 -0
- data/spec/presenters/types/array_spec.rb +246 -9
- data/spec/presenters/types/hash_spec.rb +590 -0
- data/spec/utilities/utilities_spec.rb +2 -0
- metadata +3 -3
@@ -570,6 +570,99 @@ describe Hoodoo::Presenters::Hash do
|
|
570
570
|
|
571
571
|
############################################################################
|
572
572
|
|
573
|
+
class TestHashSpecificKeyTypes < Hoodoo::Presenters::Base
|
574
|
+
schema do
|
575
|
+
hash :specific_key_types, :default => { 'array' => [ 1, 2, 3 ], 'float' => 0.5 } do
|
576
|
+
key :array, :type => :array
|
577
|
+
key :boolean, :type => :boolean
|
578
|
+
key :date, :type => :date
|
579
|
+
key :date_time, :type => :date_time
|
580
|
+
key :decimal, :type => :decimal, :field_precision => 2
|
581
|
+
key :enum, :type => :enum, :field_from => [ :one, :two, :three ]
|
582
|
+
key :float, :type => :float
|
583
|
+
key :integer, :type => :integer, :field_default => 1 # ":field_default" (sic.)
|
584
|
+
key :string, :type => :string, :field_length => 4
|
585
|
+
key :tags, :type => :tags, :default => 'default,tags' # ":default" (sic.)
|
586
|
+
key :text, :type => :text
|
587
|
+
key :uuid, :type => :uuid
|
588
|
+
key :field
|
589
|
+
end
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
############################################################################
|
594
|
+
|
595
|
+
context 'specific keys with elementary types and defaults' do
|
596
|
+
KEY_DATA = {
|
597
|
+
'array' => { :valid => [ [ 2, 3, 4 ] ], :invalid => [ 4, { :one => 1 } ] },
|
598
|
+
'boolean' => { :valid => [ true ], :invalid => [ 4.51, 'false' ] },
|
599
|
+
'date' => { :valid => [ Date.today.iso8601 ], :invalid => [ Date.today, '23rd January 2041' ] },
|
600
|
+
'date_time' => { :valid => [ DateTime.now.iso8601 ], :invalid => [ DateTime.now, '2017-01-27 12:00' ] },
|
601
|
+
'decimal' => { :valid => [ BigDecimal.new(4.51, 2) ], :invalid => [ 4.51, '4.51' ] },
|
602
|
+
'enum' => { :valid => [ 'one' ], :invalid => [ 'One', 1 ] },
|
603
|
+
'float' => { :valid => [ 4.51 ], :invalid => [ BigDecimal.new(4.51, 2), '4.51' ] },
|
604
|
+
'integer' => { :valid => [ 4 ], :invalid => [ '4' ] },
|
605
|
+
'string' => { :valid => [ 'four' ], :invalid => [ 'toolong', 4, true ] },
|
606
|
+
'tags' => { :valid => [ 'tag_a,tag_b,tag_c' ], :invalid => [ 4, true ] },
|
607
|
+
'text' => { :valid => [ 'hello world' ], :invalid => [ 4, true ] },
|
608
|
+
'uuid' => { :valid => [ Hoodoo::UUID.generate() ], :invalid => [ '123456', 4, true ] },
|
609
|
+
'field' => { :valid => [ 4, '4', { :one => 1 } ], :invalid => [ ] }
|
610
|
+
}
|
611
|
+
|
612
|
+
context '#render' do
|
613
|
+
it 'renders correctly with whole-hash defaults' do
|
614
|
+
expected_data = { 'specific_key_types' => { 'array' => [ 1, 2, 3 ],
|
615
|
+
'float' => 0.5,
|
616
|
+
'integer' => 1,
|
617
|
+
'tags' => 'default,tags' } }
|
618
|
+
|
619
|
+
expect( TestHashSpecificKeyTypes.render( {} ) ).to eq( expected_data )
|
620
|
+
expect( TestHashSpecificKeyTypes.render( nil ) ).to eq( expected_data )
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
KEY_DATA.each do | field, values |
|
625
|
+
context '#render' do
|
626
|
+
values[ :valid ].each_with_index do | value, index |
|
627
|
+
it "renders correctly for '#{ field }' (#{ index + 1 })" do
|
628
|
+
|
629
|
+
# Start with the defaults we expect given KEY_DATA definitions
|
630
|
+
# above then merge in the under-test field value. Only the inner
|
631
|
+
# field defaults are carried because we are passing a non-empty
|
632
|
+
# Hash in the top level for rendering, which overrides therefore
|
633
|
+
# the top-level Hash default.
|
634
|
+
#
|
635
|
+
expected_data = { 'specific_key_types' => { 'integer' => 1,
|
636
|
+
'tags' => 'default,tags' } }
|
637
|
+
|
638
|
+
data = { 'specific_key_types' => { field => value } }
|
639
|
+
expected_data = Hoodoo::Utilities.deep_merge_into( expected_data, data )
|
640
|
+
|
641
|
+
expect( TestHashSpecificKeyTypes.render( data ) ).to eq( expected_data )
|
642
|
+
end
|
643
|
+
end
|
644
|
+
end
|
645
|
+
|
646
|
+
context '#validate' do
|
647
|
+
values[ :valid ].each_with_index do | value, index |
|
648
|
+
it "accepts a valid value for '#{ field }' (#{ index + 1 })" do
|
649
|
+
data = { 'specific_key_types' => { field => value } }
|
650
|
+
expect( TestHashSpecificKeyTypes.validate( data ).errors.size ).to( eql( 0 ) )
|
651
|
+
end
|
652
|
+
end
|
653
|
+
|
654
|
+
values[ :invalid ].each_with_index do | value, index |
|
655
|
+
it "rejects an invalid value for '#{ field }' (#{ index + 1 })" do
|
656
|
+
data = { 'specific_key_types' => { field => value } }
|
657
|
+
expect( TestHashSpecificKeyTypes.validate( data ).errors.size ).to( eql( 1 ) )
|
658
|
+
end
|
659
|
+
end
|
660
|
+
end
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
############################################################################
|
665
|
+
|
573
666
|
class TestHashGenericKeyPresenterNoValues < Hoodoo::Presenters::Base
|
574
667
|
schema do
|
575
668
|
hash :generic do
|
@@ -1066,4 +1159,501 @@ describe Hoodoo::Presenters::Hash do
|
|
1066
1159
|
end
|
1067
1160
|
end
|
1068
1161
|
|
1162
|
+
############################################################################
|
1163
|
+
|
1164
|
+
KEYS_DATA = {
|
1165
|
+
'array' => { :definition => { :length => 9, :type => :array }, :valid => [ [ 2, 3, 4 ] ], :invalid => [ 4, { :one => 1 } ] },
|
1166
|
+
'boolean' => { :definition => { :length => 9, :type => :boolean }, :valid => [ true ], :invalid => [ 4.51, 'false' ] },
|
1167
|
+
'date' => { :definition => { :length => 9, :type => :date }, :valid => [ Date.today.iso8601 ], :invalid => [ Date.today, '23rd January 2041' ] },
|
1168
|
+
'date_time' => { :definition => { :length => 9, :type => :date_time }, :valid => [ DateTime.now.iso8601 ], :invalid => [ DateTime.now, '2017-01-27 12:00' ] },
|
1169
|
+
'decimal' => { :definition => { :length => 9, :type => :decimal, :field_precision => 2 }, :valid => [ BigDecimal.new(4.51, 2) ], :invalid => [ 4.51, '4.51' ] },
|
1170
|
+
'enum' => { :definition => { :length => 9, :type => :enum, :field_from => [ :one, :two, :three ] }, :valid => [ 'one' ], :invalid => [ 'One', 1 ] },
|
1171
|
+
'float' => { :definition => { :length => 9, :type => :float }, :valid => [ 4.51 ], :invalid => [ BigDecimal.new(4.51, 2), '4.51' ] },
|
1172
|
+
'integer' => { :definition => { :length => 9, :type => :integer }, :valid => [ 4 ], :invalid => [ '4' ] },
|
1173
|
+
'string' => { :definition => { :length => 9, :type => :string, :field_length => 4 }, :valid => [ 'four' ], :invalid => [ 'toolong', 4, true ] },
|
1174
|
+
'tags' => { :definition => { :length => 9, :type => :tags }, :valid => [ 'tag_a,tag_b,tag_c' ], :invalid => [ 4, true ] },
|
1175
|
+
'text' => { :definition => { :length => 9, :type => :text }, :valid => [ 'hello world' ], :invalid => [ 4, true ] },
|
1176
|
+
'uuid' => { :definition => { :length => 9, :type => :uuid }, :valid => [ Hoodoo::UUID.generate() ], :invalid => [ '123456', 4, true ] },
|
1177
|
+
'field' => { :definition => { :length => 9 }, :valid => [ 4, '4', { :one => 1 } ], :invalid => [ ] },
|
1178
|
+
'1234567890' => { :definition => { :length => 9 }, :valid => [ ], :invalid => [ 'Any value; key is too long' ] },
|
1179
|
+
}
|
1180
|
+
|
1181
|
+
KEYS_DATA.each do | field, values |
|
1182
|
+
context "keys with elementary type '#{ values[ :definition ][ :type ] || 'field' }'" do
|
1183
|
+
before :all do
|
1184
|
+
|
1185
|
+
# Flatten local scope to access 'values' inside the class definition;
|
1186
|
+
# see e.g.:
|
1187
|
+
#
|
1188
|
+
# https://gist.github.com/Integralist/a29212a8eb10bc8154b7#file-07-flattening-the-scope-aka-nested-lexical-scopes-rb
|
1189
|
+
#
|
1190
|
+
# Per-key defaults don't apply to generic Hashes because a key is
|
1191
|
+
# either given to validate or render with in the caller-provided
|
1192
|
+
# input parameters, in which case it already must have a value -
|
1193
|
+
# even if explicitly "nil" - or the key is absent, in which case
|
1194
|
+
# we have nothing to associate a daefault value with.
|
1195
|
+
#
|
1196
|
+
# Per-hash full defaults are supported but we can't really do those
|
1197
|
+
# here as valid defaults will change for every line in KEYS_DATA
|
1198
|
+
# with the changing types required by the keys.
|
1199
|
+
#
|
1200
|
+
@test_class = Class.new( Hoodoo::Presenters::Base ) do
|
1201
|
+
schema do
|
1202
|
+
hash :keys_types do
|
1203
|
+
keys( values[ :definition ] )
|
1204
|
+
end
|
1205
|
+
end
|
1206
|
+
end
|
1207
|
+
end
|
1208
|
+
|
1209
|
+
context '#render' do
|
1210
|
+
values[ :valid ].each_with_index do | value, index |
|
1211
|
+
it "renders correctly for '#{ field }' (#{ index + 1 })" do
|
1212
|
+
data = { 'keys_types' => { field => value } }
|
1213
|
+
expect( @test_class.render( data ) ).to eq( data )
|
1214
|
+
end
|
1215
|
+
end
|
1216
|
+
end
|
1217
|
+
|
1218
|
+
context '#validate' do
|
1219
|
+
values[ :valid ].each_with_index do | value, index |
|
1220
|
+
it "accepts a valid value for '#{ field }' (#{ index + 1 })" do
|
1221
|
+
data = { 'keys_types' => { field => value } }
|
1222
|
+
expect( @test_class.validate( data ).errors.size ).to( eql( 0 ) )
|
1223
|
+
end
|
1224
|
+
end
|
1225
|
+
|
1226
|
+
values[ :invalid ].each_with_index do | value, index |
|
1227
|
+
it "rejects an invalid value for '#{ field }' (#{ index + 1 })" do
|
1228
|
+
data = { 'keys_types' => { field => value } }
|
1229
|
+
expect( @test_class.validate( data ).errors.size ).to( eql( 1 ) )
|
1230
|
+
end
|
1231
|
+
end
|
1232
|
+
end
|
1233
|
+
end
|
1234
|
+
end
|
1235
|
+
|
1236
|
+
############################################################################
|
1237
|
+
|
1238
|
+
class TestHashKeyDefaultAggregation < Hoodoo::Presenters::Base
|
1239
|
+
schema do
|
1240
|
+
hash :test, :default => { :three => 3 } do
|
1241
|
+
|
1242
|
+
key :one, :default => { :foo => 'bar' }
|
1243
|
+
key :two, :default => { 'bar' => :baz }
|
1244
|
+
key :three, :type => :integer
|
1245
|
+
|
1246
|
+
end
|
1247
|
+
end
|
1248
|
+
end
|
1249
|
+
|
1250
|
+
it 'aggregates default shallow Hash and key values' do
|
1251
|
+
expected = {
|
1252
|
+
'test' => {
|
1253
|
+
'one' => { 'foo' => 'bar' },
|
1254
|
+
'two' => { 'bar' => :baz },
|
1255
|
+
'three' => 3
|
1256
|
+
}
|
1257
|
+
}
|
1258
|
+
|
1259
|
+
expect( TestHashKeyDefaultAggregation.render( {} ) ).to eql( expected )
|
1260
|
+
end
|
1261
|
+
|
1262
|
+
it 'overrides default shallow Hash values' do
|
1263
|
+
data = {
|
1264
|
+
'test' => {}
|
1265
|
+
}
|
1266
|
+
|
1267
|
+
expected = {
|
1268
|
+
'test' => {
|
1269
|
+
'one' => { 'foo' => 'bar' }, # From the key default
|
1270
|
+
'two' => { 'bar' => :baz } # From the key default
|
1271
|
+
# No 'three'; we fully overrode the top-level Hash default in 'data'
|
1272
|
+
}
|
1273
|
+
}
|
1274
|
+
|
1275
|
+
expect( TestHashKeyDefaultAggregation.render( data ) ).to eql( expected )
|
1276
|
+
end
|
1277
|
+
|
1278
|
+
it 'overrides shallow default key values' do
|
1279
|
+
data = {
|
1280
|
+
'test' => {
|
1281
|
+
'two' => { 'foo' => 'baz' }
|
1282
|
+
}
|
1283
|
+
}
|
1284
|
+
|
1285
|
+
expected = {
|
1286
|
+
'test' => {
|
1287
|
+
'one' => { 'foo' => 'bar' }, # From the key default
|
1288
|
+
'two' => { 'foo' => 'baz' } # From 'data' above
|
1289
|
+
# No 'three'; we fully overrode the top-level Hash default in 'data'
|
1290
|
+
}
|
1291
|
+
}
|
1292
|
+
|
1293
|
+
expect( TestHashKeyDefaultAggregation.render( data ) ).to eql( expected )
|
1294
|
+
end
|
1295
|
+
|
1296
|
+
# TODO: This class does not work as originally hoped.
|
1297
|
+
# TODO: Illustrates workaround in https://github.com/LoyaltyNZ/hoodoo/issues/194
|
1298
|
+
# TODO: Move default off ":two" and into ":inner_two" if above is addressed.
|
1299
|
+
#
|
1300
|
+
class TestHashKeyDeepDefaultAggregation < Hoodoo::Presenters::Base
|
1301
|
+
schema do
|
1302
|
+
hash :test do # A default here would implicitly override anything on :two below
|
1303
|
+
|
1304
|
+
key :one, :default => { :foo => 'bar' }
|
1305
|
+
key :three, :type => :integer
|
1306
|
+
|
1307
|
+
key :two, :default => { 'inner_two' => { 'inner_three' => 'three' } } do
|
1308
|
+
hash :inner_two do
|
1309
|
+
key :inner_one, :default => { :bar => 'baz' }
|
1310
|
+
key :inner_three, :type => :text
|
1311
|
+
end
|
1312
|
+
end
|
1313
|
+
end
|
1314
|
+
end
|
1315
|
+
end
|
1316
|
+
|
1317
|
+
it 'aggregates default deep Hash and key values' do
|
1318
|
+
expected = {
|
1319
|
+
'test' => {
|
1320
|
+
'one' => { 'foo' => 'bar' },
|
1321
|
+
'two' => {
|
1322
|
+
'inner_two' => {
|
1323
|
+
'inner_one' => { 'bar' => 'baz' },
|
1324
|
+
'inner_three' => 'three'
|
1325
|
+
}
|
1326
|
+
}
|
1327
|
+
}
|
1328
|
+
}
|
1329
|
+
|
1330
|
+
expect( TestHashKeyDeepDefaultAggregation.render( {} ) ).to eql( expected )
|
1331
|
+
end
|
1332
|
+
|
1333
|
+
it 'overrides shallow deep Hash values, preserving deep values' do
|
1334
|
+
data = {
|
1335
|
+
'test' => {}
|
1336
|
+
}
|
1337
|
+
|
1338
|
+
expected = {
|
1339
|
+
'test' => {
|
1340
|
+
'one' => { 'foo' => 'bar' }, # From the key default
|
1341
|
+
# No 'three'; we fully overrode the top-level Hash default in 'data'
|
1342
|
+
'two' => {
|
1343
|
+
'inner_two' => {
|
1344
|
+
'inner_one' => { 'bar' => 'baz' }, # From the key default
|
1345
|
+
'inner_three' => 'three' # From the deep Hash default
|
1346
|
+
}
|
1347
|
+
}
|
1348
|
+
}
|
1349
|
+
}
|
1350
|
+
|
1351
|
+
expect( TestHashKeyDeepDefaultAggregation.render( data ) ).to eql( expected )
|
1352
|
+
end
|
1353
|
+
|
1354
|
+
it 'overrides default deep Hash values' do
|
1355
|
+
data = {
|
1356
|
+
'test' => {
|
1357
|
+
'two' => {
|
1358
|
+
}
|
1359
|
+
}
|
1360
|
+
}
|
1361
|
+
|
1362
|
+
expected = {
|
1363
|
+
'test' => {
|
1364
|
+
'one' => { 'foo' => 'bar' }, # From the key default
|
1365
|
+
# No 'three'; we fully overrode the top-level Hash default in 'data'
|
1366
|
+
'two' => {
|
1367
|
+
'inner_two' => {
|
1368
|
+
'inner_one' => { 'bar' => 'baz' } # From the key default
|
1369
|
+
# No 'inner_three'; we overrode the deep Hash default in 'data'
|
1370
|
+
}
|
1371
|
+
}
|
1372
|
+
}
|
1373
|
+
}
|
1374
|
+
|
1375
|
+
expect( TestHashKeyDeepDefaultAggregation.render( data ) ).to eql( expected )
|
1376
|
+
end
|
1377
|
+
|
1378
|
+
it 'overrides deep deep key values' do
|
1379
|
+
data = {
|
1380
|
+
'test' => {
|
1381
|
+
'two' => {
|
1382
|
+
'inner_two' => {
|
1383
|
+
'inner_one' => { 'bar' => 'hello' }
|
1384
|
+
}
|
1385
|
+
}
|
1386
|
+
}
|
1387
|
+
}
|
1388
|
+
|
1389
|
+
expected = {
|
1390
|
+
'test' => {
|
1391
|
+
'one' => { 'foo' => 'bar' }, # From the key default
|
1392
|
+
# No 'three'; we fully overrode the top-level Hash default in 'data'
|
1393
|
+
'two' => {
|
1394
|
+
'inner_two' => {
|
1395
|
+
'inner_one' => { 'bar' => 'hello' } # From 'data' above
|
1396
|
+
# No 'inner_three'; we overrode the deep Hash default in 'data'
|
1397
|
+
}
|
1398
|
+
}
|
1399
|
+
}
|
1400
|
+
}
|
1401
|
+
|
1402
|
+
expect( TestHashKeyDeepDefaultAggregation.render( data ) ).to eql( expected )
|
1403
|
+
end
|
1404
|
+
|
1405
|
+
############################################################################
|
1406
|
+
|
1407
|
+
context 'RDoc examples' do
|
1408
|
+
class TestHypotheticaHashCurrency < Hoodoo::Presenters::Base
|
1409
|
+
schema do
|
1410
|
+
string :currency_code, :length => 16
|
1411
|
+
integer :precision
|
1412
|
+
end
|
1413
|
+
end
|
1414
|
+
|
1415
|
+
context 'CurrencyHash' do
|
1416
|
+
class TestCurrencyHash < Hoodoo::Presenters::Base
|
1417
|
+
schema do
|
1418
|
+
hash :currencies do
|
1419
|
+
keys :length => 16 do
|
1420
|
+
type TestHypotheticaHashCurrency
|
1421
|
+
end
|
1422
|
+
end
|
1423
|
+
end
|
1424
|
+
end
|
1425
|
+
|
1426
|
+
let( :valid_data ) do
|
1427
|
+
{
|
1428
|
+
'currencies' => {
|
1429
|
+
'one' => {
|
1430
|
+
'currency_code' => 'X_HOODOO_LO',
|
1431
|
+
'precision' => 1
|
1432
|
+
},
|
1433
|
+
'0123456789ABCDEF' => {
|
1434
|
+
'currency_code' => 'X_HOODOO_HI',
|
1435
|
+
'precision' => 4
|
1436
|
+
}
|
1437
|
+
}
|
1438
|
+
}
|
1439
|
+
end
|
1440
|
+
|
1441
|
+
context '#validate' do
|
1442
|
+
it 'enforces field and key restrictions' do
|
1443
|
+
data = {
|
1444
|
+
'currencies' => {
|
1445
|
+
'one' => {
|
1446
|
+
'currency_code' => 'too long a currency code',
|
1447
|
+
'precision' => 1
|
1448
|
+
},
|
1449
|
+
'0123456789ABCDEF' => {
|
1450
|
+
'currency_code' => 'X_HOODOO_HI',
|
1451
|
+
'precision' => 'not an integer'
|
1452
|
+
},
|
1453
|
+
'too long a key name overall' => {
|
1454
|
+
'currency_code' => 'X_HOODOO_LO',
|
1455
|
+
'precision' => 1
|
1456
|
+
}
|
1457
|
+
}
|
1458
|
+
}
|
1459
|
+
|
1460
|
+
errors = TestCurrencyHash.validate( data ).errors
|
1461
|
+
|
1462
|
+
expect( errors.size ).to( eql( 3 ) )
|
1463
|
+
|
1464
|
+
expect( errors[ 0 ][ 'code' ] ).to( eql( 'generic.invalid_string' ) )
|
1465
|
+
expect( errors[ 0 ][ 'reference' ] ).to( eql( 'currencies.one.currency_code' ) )
|
1466
|
+
|
1467
|
+
expect( errors[ 1 ][ 'code' ] ).to( eql( 'generic.invalid_integer' ) )
|
1468
|
+
expect( errors[ 1 ][ 'reference' ] ).to( eql( 'currencies.0123456789ABCDEF.precision' ) )
|
1469
|
+
|
1470
|
+
expect( errors[ 2 ][ 'code' ] ).to( eql( 'generic.invalid_string' ) )
|
1471
|
+
expect( errors[ 2 ][ 'reference' ] ).to( eql( 'currencies.too long a key name overall' ) )
|
1472
|
+
end
|
1473
|
+
|
1474
|
+
it 'is happy with valid data' do
|
1475
|
+
expect( TestCurrencyHash.validate( valid_data() ).errors.size ).to( eql( 0 ) )
|
1476
|
+
end
|
1477
|
+
end
|
1478
|
+
|
1479
|
+
context '#render' do
|
1480
|
+
it 'renders valid data' do
|
1481
|
+
expect( TestCurrencyHash.render( valid_data() ) ).to( eql( valid_data() ) )
|
1482
|
+
end
|
1483
|
+
end
|
1484
|
+
end
|
1485
|
+
|
1486
|
+
context 'AltCurrencyHash' do
|
1487
|
+
class TestAltCurrencyHash < Hoodoo::Presenters::Base
|
1488
|
+
schema do
|
1489
|
+
hash :currencies do
|
1490
|
+
key :one do
|
1491
|
+
type TestHypotheticaHashCurrency
|
1492
|
+
end
|
1493
|
+
|
1494
|
+
key :two do
|
1495
|
+
text :title
|
1496
|
+
text :description
|
1497
|
+
end
|
1498
|
+
end
|
1499
|
+
end
|
1500
|
+
end
|
1501
|
+
|
1502
|
+
let( :valid_data ) do
|
1503
|
+
{
|
1504
|
+
'currencies' => {
|
1505
|
+
'one' => {
|
1506
|
+
'currency_code' => 'X_HOODOO_LO',
|
1507
|
+
'precision' => 1
|
1508
|
+
},
|
1509
|
+
'two' => {
|
1510
|
+
'title' => 'Optional title text',
|
1511
|
+
'description' => 'Optional description text'
|
1512
|
+
}
|
1513
|
+
}
|
1514
|
+
}
|
1515
|
+
end
|
1516
|
+
|
1517
|
+
context '#validate' do
|
1518
|
+
it 'enforces field restrictions' do
|
1519
|
+
data = {
|
1520
|
+
'currencies' => {
|
1521
|
+
'one' => {
|
1522
|
+
'currency_code' => 'too long a currency code',
|
1523
|
+
'precision' => 1
|
1524
|
+
}
|
1525
|
+
}
|
1526
|
+
}
|
1527
|
+
|
1528
|
+
errors = TestAltCurrencyHash.validate( data ).errors
|
1529
|
+
|
1530
|
+
expect( errors.size ).to( eql( 1 ) )
|
1531
|
+
expect( errors[ 0 ][ 'code' ] ).to( eql( 'generic.invalid_string' ) )
|
1532
|
+
expect( errors[ 0 ][ 'reference' ] ).to( eql( 'currencies.one.currency_code' ) )
|
1533
|
+
end
|
1534
|
+
|
1535
|
+
it 'enforces key name restrictions' do
|
1536
|
+
data = {
|
1537
|
+
'currencies' => {
|
1538
|
+
'unrecognised' => {
|
1539
|
+
'currency_code' => 'X_HOODOO_LO',
|
1540
|
+
'precision' => 1
|
1541
|
+
}
|
1542
|
+
}
|
1543
|
+
}
|
1544
|
+
|
1545
|
+
errors = TestAltCurrencyHash.validate( data ).errors
|
1546
|
+
|
1547
|
+
expect( errors.size ).to( eql( 1 ) )
|
1548
|
+
expect( errors[ 0 ][ 'code' ] ).to( eql( 'generic.invalid_hash' ) )
|
1549
|
+
expect( errors[ 0 ][ 'reference' ] ).to( eql( 'currencies' ) )
|
1550
|
+
end
|
1551
|
+
|
1552
|
+
it 'is happy with valid data' do
|
1553
|
+
expect( TestAltCurrencyHash.validate( valid_data() ).errors.size ).to( eql( 0 ) )
|
1554
|
+
end
|
1555
|
+
end
|
1556
|
+
|
1557
|
+
context '#render' do
|
1558
|
+
it 'renders valid data' do
|
1559
|
+
expect( TestAltCurrencyHash.render( valid_data() ) ).to( eql( valid_data() ) )
|
1560
|
+
end
|
1561
|
+
end
|
1562
|
+
end
|
1563
|
+
|
1564
|
+
context 'Person' do
|
1565
|
+
class TestPerson < Hoodoo::Presenters::Base
|
1566
|
+
schema do
|
1567
|
+
hash :name do
|
1568
|
+
key :first, :type => :text
|
1569
|
+
key :last, :type => :text
|
1570
|
+
end
|
1571
|
+
|
1572
|
+
hash :address do
|
1573
|
+
keys :type => :text
|
1574
|
+
end
|
1575
|
+
|
1576
|
+
hash :identifiers, :required => true do
|
1577
|
+
keys :length => 8, :type => :string, :field_length => 32
|
1578
|
+
end
|
1579
|
+
end
|
1580
|
+
end
|
1581
|
+
|
1582
|
+
let( :valid_data ) do
|
1583
|
+
{
|
1584
|
+
'name' => {
|
1585
|
+
'first' => 'Test',
|
1586
|
+
'last' => 'Testy'
|
1587
|
+
},
|
1588
|
+
'address' => {
|
1589
|
+
'road' => '1 Test Street',
|
1590
|
+
'city' => 'Testville',
|
1591
|
+
'post_code' => 'T01 C41'
|
1592
|
+
},
|
1593
|
+
'identifiers' => {
|
1594
|
+
'primary' => '9759c77d188f4bfe85959738dc6f8505',
|
1595
|
+
'postgres' => '1442'
|
1596
|
+
}
|
1597
|
+
}
|
1598
|
+
end
|
1599
|
+
|
1600
|
+
context '#validate' do
|
1601
|
+
it 'enforces a required hash' do
|
1602
|
+
data = Hoodoo::Utilities.deep_dup( valid_data() )
|
1603
|
+
data.delete( 'identifiers' )
|
1604
|
+
|
1605
|
+
errors = TestPerson.validate( data ).errors
|
1606
|
+
|
1607
|
+
expect( errors.size ).to( eql( 1 ) )
|
1608
|
+
expect( errors[ 0 ][ 'code' ] ).to( eql( 'generic.required_field_missing' ) )
|
1609
|
+
expect( errors[ 0 ][ 'reference' ] ).to( eql( 'identifiers' ) )
|
1610
|
+
end
|
1611
|
+
|
1612
|
+
it 'enforces field and key restrictions' do
|
1613
|
+
data = {
|
1614
|
+
'name' => {
|
1615
|
+
'first' => 'Test',
|
1616
|
+
'surname' => 'Testy' # Invalid key name
|
1617
|
+
},
|
1618
|
+
'address' => {
|
1619
|
+
'road' => '1 Test Street',
|
1620
|
+
'city' => 'Testville',
|
1621
|
+
'zip' => 90421 # Integer, not Text
|
1622
|
+
},
|
1623
|
+
'identifiers' => {
|
1624
|
+
'primary' => '9759c77d188f4bfe85959738dc6f8505_441', # Value too long
|
1625
|
+
'postgresql' => '1442' # Key name too long
|
1626
|
+
}
|
1627
|
+
}
|
1628
|
+
|
1629
|
+
errors = TestPerson.validate( data ).errors
|
1630
|
+
|
1631
|
+
expect( errors.size ).to( eql( 4 ) )
|
1632
|
+
|
1633
|
+
expect( errors[ 0 ][ 'code' ] ).to( eql( 'generic.invalid_hash' ) )
|
1634
|
+
expect( errors[ 0 ][ 'reference' ] ).to( eql( 'name' ) )
|
1635
|
+
|
1636
|
+
expect( errors[ 1 ][ 'code' ] ).to( eql( 'generic.invalid_string' ) )
|
1637
|
+
expect( errors[ 1 ][ 'reference' ] ).to( eql( 'address.zip' ) )
|
1638
|
+
|
1639
|
+
expect( errors[ 2 ][ 'code' ] ).to( eql( 'generic.invalid_string' ) )
|
1640
|
+
expect( errors[ 2 ][ 'reference' ] ).to( eql( 'identifiers.primary' ) )
|
1641
|
+
|
1642
|
+
expect( errors[ 3 ][ 'code' ] ).to( eql( 'generic.invalid_string' ) )
|
1643
|
+
expect( errors[ 3 ][ 'reference' ] ).to( eql( 'identifiers.postgresql' ) )
|
1644
|
+
end
|
1645
|
+
|
1646
|
+
it 'is happy with valid data' do
|
1647
|
+
expect( TestPerson.validate( valid_data() ).errors.size ).to( eql( 0 ) )
|
1648
|
+
end
|
1649
|
+
end
|
1650
|
+
|
1651
|
+
context '#render' do
|
1652
|
+
it 'renders valid data' do
|
1653
|
+
expect( TestPerson.render( valid_data() ) ).to( eql( valid_data() ) )
|
1654
|
+
end
|
1655
|
+
end
|
1656
|
+
end
|
1657
|
+
end
|
1658
|
+
|
1069
1659
|
end
|