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.
@@ -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