caruby-tissue 1.3.1 → 1.3.2

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.
Files changed (64) hide show
  1. data/History.txt +4 -0
  2. data/README.md +6 -0
  3. data/lib/catissue/annotation/annotation_class.rb +1 -1
  4. data/lib/catissue/cli/migrate.rb +1 -0
  5. data/lib/catissue/domain/container.rb +13 -8
  6. data/lib/catissue/domain/specimen.rb +9 -3
  7. data/lib/catissue/domain/specimen_event_parameters.rb +1 -8
  8. data/lib/catissue/domain/specimen_requirement.rb +1 -1
  9. data/lib/catissue/domain/storage_container.rb +4 -3
  10. data/lib/catissue/domain/uniquify.rb +82 -0
  11. data/lib/catissue/migration/migrator.rb +15 -7
  12. data/lib/catissue/migration/uniquify.rb +2 -111
  13. data/lib/catissue/util/position.rb +14 -2
  14. data/lib/catissue/version.rb +1 -1
  15. data/test/fixtures/catissue/domain/conf/catissue_override.yaml +9 -0
  16. data/test/fixtures/catissue/extract/conf/scg_extract.yaml +3 -0
  17. data/test/fixtures/catissue/extract/conf/scg_fields.yaml +3 -0
  18. data/test/fixtures/catissue/extract/conf/spc_extract.yaml +3 -0
  19. data/test/fixtures/catissue/extract/conf/spc_fields.yaml +4 -0
  20. data/test/fixtures/lib/catissue/defaults_test_fixture.rb +202 -0
  21. data/test/lib/catissue/database/controlled_values_test.rb +47 -0
  22. data/test/lib/catissue/database/database_test.rb +28 -0
  23. data/test/lib/catissue/domain/address_test.rb +53 -0
  24. data/test/lib/catissue/domain/base_haemotology_pathology_test.rb +25 -0
  25. data/test/lib/catissue/domain/ca_tissue_test_defaults_test.rb +27 -0
  26. data/test/lib/catissue/domain/capacity_test.rb +12 -0
  27. data/test/lib/catissue/domain/collection_event_parameters_test.rb +24 -0
  28. data/test/lib/catissue/domain/collection_protocol_event_test.rb +25 -0
  29. data/test/lib/catissue/domain/collection_protocol_registration_test.rb +71 -0
  30. data/test/lib/catissue/domain/collection_protocol_test.rb +69 -0
  31. data/test/lib/catissue/domain/container_position_test.rb +29 -0
  32. data/test/lib/catissue/domain/department_test.rb +21 -0
  33. data/test/lib/catissue/domain/disposal_event_parameters_test.rb +16 -0
  34. data/test/lib/catissue/domain/location_test.rb +38 -0
  35. data/test/lib/catissue/domain/metadata_test.rb +62 -0
  36. data/test/lib/catissue/domain/participant_medical_identifier_test.rb +26 -0
  37. data/test/lib/catissue/domain/participant_test.rb +96 -0
  38. data/test/lib/catissue/domain/site_test.rb +30 -0
  39. data/test/lib/catissue/domain/specimen_array_test.rb +38 -0
  40. data/test/lib/catissue/domain/specimen_array_type_test.rb +27 -0
  41. data/test/lib/catissue/domain/specimen_characteristics_test.rb +15 -0
  42. data/test/lib/catissue/domain/specimen_collection_group_test.rb +216 -0
  43. data/test/lib/catissue/domain/specimen_event_parameters_test.rb +61 -0
  44. data/test/lib/catissue/domain/specimen_position_test.rb +62 -0
  45. data/test/lib/catissue/domain/specimen_requirement_test.rb +61 -0
  46. data/test/lib/catissue/domain/specimen_test.rb +272 -0
  47. data/test/lib/catissue/domain/storage_container_test.rb +150 -0
  48. data/test/lib/catissue/domain/storage_type_test.rb +70 -0
  49. data/test/lib/catissue/domain/transfer_event_parameters_test.rb +38 -0
  50. data/test/lib/catissue/domain/user_test.rb +50 -0
  51. data/test/lib/catissue/extract/delta_test.rb +25 -0
  52. data/test/lib/catissue/extract/extractor_test.rb +43 -0
  53. data/test/lib/catissue/import/importable_module_test.rb +14 -0
  54. data/test/lib/catissue/migration/test_case.rb +103 -0
  55. data/test/lib/catissue/test_case.rb +225 -0
  56. data/test/lib/examples/galena/domain/examples_test.rb +70 -0
  57. data/test/lib/examples/galena/migration/catissue.log +0 -0
  58. data/test/lib/examples/galena/migration/filter_test.rb +26 -0
  59. data/test/lib/examples/galena/migration/frozen_test.rb +28 -0
  60. data/test/lib/examples/galena/migration/general_test.rb +44 -0
  61. data/test/lib/examples/galena/migration/simple_test.rb +29 -0
  62. data/test/lib/examples/galena/migration/test_case.rb +52 -0
  63. data/test/lib/examples/galena/migration/uniquify.rb +93 -0
  64. metadata +223 -184
@@ -0,0 +1,70 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_case')
2
+ require 'caruby/util/uniquifier'
3
+ require 'test/fixtures/lib/catissue/defaults_test_fixture'
4
+
5
+ class StorageTypeTest < Test::Unit::TestCase
6
+ include CaTissue::TestCase
7
+
8
+ def setup
9
+ super
10
+ @spc = defaults.specimen
11
+ @frz_type = CaTissue::StorageType.new(:name => 'Freezer'.uniquify, :columns => 5, :rows => 5)
12
+ @rack_type = CaTissue::StorageType.new(:name => 'Rack'.uniquify, :columns => 1, :rows => 1)
13
+ @box_type = CaTissue::StorageType.new(:name => 'Box'.uniquify, :columns => 1, :rows => 1)
14
+ @array_type = CaTissue::SpecimenArrayType.new(:name => 'SpecimenArray'.uniquify,
15
+ :specimen_class => @spc.specimen_class, :specimen_types => [@spc.specimen_type],
16
+ :columns => 5, :rows => 5)
17
+ @frz_type << @rack_type
18
+ @rack_type << @box_type
19
+ @box_type << @array_type << @spc.specimen_class
20
+ end
21
+
22
+ def test_defaults
23
+ verify_defaults(@frz_type)
24
+ end
25
+
26
+ def test_create_container
27
+ frz = @frz_type.create_container
28
+ assert_same(CaTissue::StorageContainer, frz.class, "Created instance class incorrect")
29
+ assert_same(@frz_type, frz.container_type, "Created container type incorrect")
30
+ end
31
+
32
+ def test_can_hold
33
+ assert(@frz_type.can_hold_child?(@rack_type.create_container), "Freezer can't hold a rack")
34
+ assert(@rack_type.can_hold_child?(@box_type.create_container), "Rack can't hold a box")
35
+ assert(@box_type.can_hold_child?(@array_type.create_container), "Box can't hold an array")
36
+ assert(@array_type.can_hold_child?(@spc), "Array can't hold the specimen")
37
+ assert(@box_type.can_hold_child?(@spc), "Box can't hold the specimen")
38
+ end
39
+
40
+ def test_comparison
41
+ assert_equal(@frz_type, @frz_type, "Same not equal")
42
+ assert_equal(@frz_type, @frz_type.copy(:name), "Name not used in equality")
43
+ assert(@rack_type < @frz_type, "Rack type not < freezer type")
44
+ assert(@frz_type > @rack_type, "Freezer type not > rack type")
45
+ assert(@box_type < @rack_type, "Box type not < rack type")
46
+ end
47
+
48
+ def test_closure
49
+ assert_equal([@frz_type, @rack_type, @box_type], @frz_type.closure, "Freezer type closure incorrect")
50
+ end
51
+
52
+ def test_path_to
53
+ assert_equal([@frz_type, @rack_type, @box_type], @frz_type.path_to(@spc), "Freezer path to specimen incorrect")
54
+ end
55
+
56
+ ## DATABASE TESTS ##
57
+
58
+ def test_save
59
+ verify_save(@box_type)
60
+ assert(@box_type.find_containers.empty?, "Box incorrectly found")
61
+ site = defaults.tissue_bank
62
+ box = @box_type.find_available(site)
63
+ assert_nil(box, "Available box incorrectly created")
64
+ box = @box_type.find_available(site, :create)
65
+ assert_not_nil(box, "Available box not created")
66
+ assert_not_nil(box.identifier, "Available box missing identifier")
67
+ stored = @box_type.find_containers.map { |ctr| ctr.identifier}
68
+ assert_equal([box.identifier], stored, "Box with identifier #{box.identifier} not found")
69
+ end
70
+ end
@@ -0,0 +1,38 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_case')
2
+ require 'test/fixtures/lib/catissue/defaults_test_fixture'
3
+ require 'caruby/database/store_template_builder'
4
+
5
+ class TransferEventParametersTest < Test::Unit::TestCase
6
+ include CaTissue::TestCase
7
+
8
+ def setup
9
+ super
10
+ spc = defaults.specimen
11
+ defaults.box << spc
12
+ @xfr = CaTissue::TransferEventParameters.new(:specimen => spc, :to => spc.location)
13
+ end
14
+
15
+ # def test_inverse_setter
16
+ # assert(@xfr.specimen.event_parameters.include?(@xfr), "EventParameters not found in specimen")
17
+ # end
18
+ #
19
+ # def test_defaults
20
+ # verify_defaults(@xfr)
21
+ # end
22
+
23
+ # def test_save
24
+ # verify_save(@xfr)
25
+ # shift(@xfr)
26
+ # verify_save(@xfr)
27
+ # end
28
+
29
+ private
30
+
31
+ # Resets the given CaTissue::TransferEventParameters xfr so that +from+ is the
32
+ # old +to+, +to+ is the next location, and the identifier is cleared.
33
+ def shift(xfr)
34
+ xfr.from = xfr.to
35
+ xfr.to = xfr.from.succ
36
+ xfr.identifier = nil
37
+ end
38
+ end
@@ -0,0 +1,50 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_case')
2
+ require 'catissue/defaults_test_fixture'
3
+
4
+ class UserTest < Test::Unit::TestCase
5
+ include CaTissue::TestCase
6
+
7
+ def setup
8
+ super
9
+ @user = defaults.tissue_bank.coordinator
10
+ end
11
+
12
+ # # Verifies the secondary key.
13
+ # def test_secondary_key
14
+ # assert_equal(@user.login_name, @user.key, 'Key incorrect')
15
+ # end
16
+ #
17
+ # def test_defaults
18
+ # verify_defaults
19
+ # end
20
+ #
21
+ # # Exercises setting the login name from the email address.
22
+ # def test_default_login_value
23
+ # @user.login_name = nil
24
+ # verify_defaults
25
+ # end
26
+ #
27
+ # # Exercises setting the email address from the login name.
28
+ # def test_default_email_value
29
+ # @user.email_address = nil
30
+ # verify_defaults
31
+ # end
32
+ #
33
+ # def verify_defaults
34
+ # @user.address.identifier = 1
35
+ # @user.cancer_research_group.identifier = 2
36
+ # @user.department.identifier = 3
37
+ # @user.institution.identifier = 4
38
+ # super(@user)
39
+ # end
40
+
41
+ # Tests creating a user.
42
+ def test_save
43
+ addr = @user.email_address
44
+ at_ndx = addr.index('@')
45
+ modifier = "_#{Uniquifier.qualifier.to_s}"
46
+ @user.email_address = addr.insert(at_ndx, modifier)
47
+ @user.login_name = nil
48
+ verify_save(@user)
49
+ end
50
+ end
@@ -0,0 +1,25 @@
1
+ require 'date'
2
+ require File.join(File.dirname(__FILE__), '..', 'test_case')
3
+ require 'catissue/extract/delta'
4
+
5
+ class DeltaTest < Test::Unit::TestCase
6
+ include CaTissue::TestCase
7
+
8
+ def test_specimen
9
+ since = DateTime.now
10
+ spc = defaults.specimen.create
11
+ ids = CaTissue::Delta.new(spc.class, since).to_a
12
+ assert(!ids.empty?, "Delta is empty")
13
+ assert_equal(1, ids.size, "Delta count incorrect")
14
+ assert_equal(spc.identifier, ids.first, "Delta value incorrect")
15
+ end
16
+
17
+ def test_scg
18
+ since = DateTime.now
19
+ scg = defaults.specimen_collection_group.create
20
+ ids = CaTissue::Delta.new(scg.class, since).to_a
21
+ assert(!ids.empty?, "Delta is empty")
22
+ assert_equal(1, ids.size, "Delta count incorrect")
23
+ assert_equal(scg.identifier, ids.first, "Delta value incorrect")
24
+ end
25
+ end
@@ -0,0 +1,43 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_case')
2
+ require 'catissue/extract/extractor'
3
+
4
+ class ExtractorTest < Test::Unit::TestCase
5
+ include CaTissue::TestCase
6
+
7
+ CONFIG_DIR = File.join('test', 'fixtures', 'catissue', 'extract', 'conf')
8
+
9
+ def test_specimen_write
10
+ config = File.join(CONFIG_DIR, 'spc_extract.yaml')
11
+ spc = defaults.specimen.create
12
+ extractor = CaTissue::Extractor.new(:file => config, :ids => [spc.identifier])
13
+ extractor.run
14
+ mapping = File.join(CONFIG_DIR, 'spc_fields.yaml')
15
+ mapper = CaRuby::CsvMapper.new(mapping, spc.class, extractor.output)
16
+ rec = mapper.csvio.to_a.first
17
+ assert_not_nil(rec, "Specimen record not written")
18
+ mrn = spc.path_value("specimen_collection_group.registration.participant_identifier")
19
+ assert_equal(mrn.to_s, rec[:mrn].to_s, "Specimen MRN incorrect")
20
+ spn = spc.path_value("specimen_collection_group.surgical_pathology_number")
21
+ assert_equal(spn.to_s, rec[:spn].to_s, "Specimen SPN incorrect")
22
+ date = spc.path_value("specimen_collection_group.collection_event_parameters.timestamp")
23
+ assert_equal(date.to_s, rec[:collection_date].to_s, "Specimen collection date incorrect")
24
+ assert_equal(spc.initial_quantity.to_s, rec[:quantity].to_s, "Quantity incorrect")
25
+ end
26
+
27
+ def test_scg_write
28
+ config = File.join(CONFIG_DIR, 'scg_extract.yaml')
29
+ scg = defaults.specimen_collection_group.create
30
+ extractor = CaTissue::Extractor.new(:file => config, :ids => [scg.identifier])
31
+ extractor.run
32
+ mapping = File.join(CONFIG_DIR, 'scg_fields.yaml')
33
+ mapper = CaRuby::CsvMapper.new(mapping, scg.class, extractor.output)
34
+ rec = mapper.csvio.to_a.first
35
+ assert_not_nil(rec, "SCG record not written")
36
+ mrn = scg.path_value("registration.participant_identifier")
37
+ assert_equal(mrn.to_s, rec[:mrn].to_s, "SCG MRN incorrect")
38
+ spn = scg.path_value("surgical_pathology_number")
39
+ assert_equal(spn.to_s, rec[:spn].to_s, "SCG SPN incorrect")
40
+ date = scg.path_value("collection_event_parameters.timestamp")
41
+ assert_equal(date.to_s, rec[:collection_date].to_s, "SCG collection date incorrect")
42
+ end
43
+ end
@@ -0,0 +1,14 @@
1
+ $:.unshift 'lib'
2
+ $:.unshift '../caruby/lib'
3
+
4
+ require "test/unit"
5
+
6
+ require 'catissue'
7
+
8
+ class ImportableTest < Test::Unit::TestCase
9
+ LOG_FILE = 'test/results/catissue/import/log/catissue.log'
10
+
11
+ def test_import
12
+ assert_nothing_raised(Exception, 'Java class not imported') { CaTissue::CollectionProtocol }
13
+ end
14
+ end
@@ -0,0 +1,103 @@
1
+ LOG_FILE = 'test/results/log/migration.log'
2
+
3
+ require File.join(File.dirname(__FILE__), '..', 'test_case')
4
+ require 'caruby/domain/uniquify'
5
+ require 'catissue/migration/migrator'
6
+
7
+ module CaTissue
8
+ module MigrationTestCase
9
+ include CaTissue::TestCase
10
+
11
+ #@param [String] fixtures the fixtures directory
12
+ def setup(fixtures)
13
+ @fixtures = fixtures
14
+ # Clear the uniquifier for this migration.
15
+ CaRuby::ResourceUniquifier.instance.clear
16
+ end
17
+
18
+ ## MIGRATION TEST UTILITY METHODS ##
19
+
20
+ private
21
+
22
+ # Runs a migrator on the given input fixture and options.
23
+ #
24
+ # @param (see #create_migrator)
25
+ # @option (see #create_migrator)
26
+ def migrate_to_database(fixture, opts={}, &verifier)
27
+ mgtr = create_migrator(fixture, opts)
28
+ logger.debug { "Migration test migrating #{fixture} fixture..." }
29
+ mgtr.migrate_to_database(&verifier)
30
+ logger.debug { "Migration test migrated #{fixture} fixture." }
31
+ end
32
+
33
+ # Creates a new Migrator for the given fixture with the given options.
34
+ # If a factory block is provided, then that factory is called to make a new
35
+ # Migrator instance. Otherwise, {CaTissue::Migrator#initialize} makes the instance.
36
+ #
37
+ # If there is no :input option, then the migration input is set to the
38
+ # _fixture_.+csv+ file in the {#initialize} fixtures directory.
39
+ #
40
+ # @param [Symbol] fixture the migration test fixture
41
+ # @param [{Symbol => Object}] opts (see CaTissue::Migrator#initialize)
42
+ # @option (see CaTissue::Migrator#initialize)
43
+ # @return [CaTissue::Migrator]
44
+ # @yield [opts] the optional Migrator factory
45
+ def create_migrator(fixture, opts={}, &factory)
46
+ opts[:quiet] = true
47
+ opts[:input] ||= File.join(@fixtures, fixture.to_s + '.csv')
48
+ block_given? ? yield(opts) : CaTissue::Migrator.new(opts)
49
+ end
50
+
51
+ # Verifies that the given test fixture is successfully migrated.
52
+ # Each migrated target object is validated using {CaTissue::TestCase#verify_saved}.
53
+ # In addition, if a verifier block is given to this method, then that block is
54
+ # called on the target migration object, or nil if no target was migrated.
55
+ # Supported options are described in {CaTissue::Migrator#migrate}.
56
+ #
57
+ # @param (see #verify_target)
58
+ # @option (see #verify_target)
59
+ # @yield (see #verify_target)
60
+ # @yieldparam (see #verify_target)
61
+ def verify_save(fixture, opts={})
62
+ logger.debug { "Migrating the #{fixture} test fixture..." }
63
+ opts[:unique] = true unless opts.has_key?(:unique)
64
+ opts[:controlled_values] = true unless opts.has_key?(:controlled_values)
65
+ migrate_to_database(fixture, opts) do |tgt|
66
+ verify_saved(tgt)
67
+ yield tgt if block_given?
68
+ end
69
+ end
70
+
71
+ # Verifies the given fixture migration.
72
+ # Each migrated target object is validated using {#validate_target}.
73
+ # The target is migrated but not stored.
74
+ #
75
+ # @param [Symbol] fixture the test fixture to verify
76
+ # @param [<Symbol>] opts the migration options
77
+ # @option (see CaTissue::Migrator#migrate)
78
+ # @yield [target] verifies the given target
79
+ # @yieldparam [Resource] target the domain object to verify
80
+ def verify_target(fixture, opts={}, &verifier)
81
+ opts[:unique] ||= false
82
+ create_migrator(fixture, opts).migrate { |tgt| validate_target(tgt, &verifier) }
83
+ end
84
+
85
+ # Validates that the given target was successfully migrated.
86
+ # If a target was migrated, then this method calls {CaTissue::TestCase#verify_defaults}
87
+ # to confirm that the target can be stored.
88
+ # In addition, if a block is given to this method then the block is called on the
89
+ # (possibly nil) migration target.
90
+ #
91
+ # @param [Resource] target the domain object to verify
92
+ # @yield (see #verify_target)
93
+ # @yieldparam (see #verify_target)
94
+ def validate_target(target)
95
+ assert_not_nil(target, "Missing target")
96
+ logger.debug { "Validating migration target #{target}..." }
97
+ verify_defaults(target) if target
98
+ yield target if block_given?
99
+ logger.debug { "Validated migration target #{target}." }
100
+ target
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,225 @@
1
+ # load the core gem
2
+ require 'rubygems'
3
+ begin
4
+ gem 'caruby-core'
5
+ rescue LoadError
6
+ # if the gem is not available, then try a local development source
7
+ $:.unshift '../caruby/lib'
8
+ end
9
+
10
+ # open the logger
11
+ LOG_FILE = 'test/results/log/catissue.log' unless defined?(LOG_FILE)
12
+ require 'caruby/util/log' and
13
+ CaRuby::Log.instance.open(LOG_FILE, :shift_age => 10, :shift_size => 1048576, :debug => true)
14
+
15
+ # Add the caruby-tissue source
16
+ $:.unshift '../catissue/lib'
17
+
18
+ require 'catissue'
19
+ require '../catissue/test/fixtures/lib/catissue/defaults_test_fixture'
20
+
21
+ module CaTissue
22
+ module TestCase
23
+ attr_reader :database
24
+
25
+ def setup
26
+ super()
27
+ logger.info("Testing #{name}...")
28
+ @database = CaTissue::Database.instance
29
+ end
30
+
31
+ def teardown
32
+ super
33
+ @database.close if @database
34
+ logger.info("Test #{name} completed.")
35
+ end
36
+
37
+ def defaults
38
+ @defaults ||= CaTissueTest.defaults.uniquify
39
+ end
40
+
41
+ # Tests the domain object +add_defaults_local+ method.
42
+ # Subclasses are responsible for setting every attribute that is a pre-condition for default value initialization.
43
+ def verify_defaults(subject)
44
+ subject.add_defaults
45
+ msg = "#{subject.qp} with default attributes fails validation"
46
+ assert_nothing_raised(ValidationError, msg) { subject.validate }
47
+ end
48
+
49
+ # Tests saving the subject. Calls {Database#save} on the subject and verifies that subject and its
50
+ # references were persisted.
51
+ #
52
+ # @param [Resource] subject the object to save
53
+ def verify_save(subject)
54
+ logger.debug{ "Verifying #{subject.qp} save with content:\n#{dump(subject)}" }
55
+ # capture the save operation time
56
+ @database.clear
57
+ st = Stopwatch.measure { @database.open { |db| db.save(subject) } }.elapsed
58
+ # the database execution time
59
+ dt = @database.execution_time
60
+ logger.debug { "#{subject.qp} save took #{'%.2f' % st} seconds, of which #{'%.2f' % dt} were database operations." }
61
+ verify_saved(subject)
62
+ end
63
+
64
+ # Verifies that the given subject and its references were persisted.
65
+ #
66
+ # @param [Resource] subject the saved object to verify
67
+ def verify_saved(subject)
68
+ logger.debug { "Verifying saved content of #{subject}..." }
69
+ subject.dependent? ? verify_saved_dependent(subject) : verify_saved_independent(subject)
70
+ logger.debug { "Verified saved content of #{subject}." }
71
+ end
72
+
73
+ # Tests a query on the given template.
74
+ # The block given to this method is called on the query result.
75
+ def verify_query(template, *path) # :yields: result
76
+ yield database.query(template, *path)
77
+ end
78
+
79
+ # Returns the Ruby date as determined by setting a Java Date property.
80
+ def date(year, month, day)
81
+ jdate = java.util.Calendar.instance
82
+ jdate.clear
83
+ jdate.set(year, month - 1, day)
84
+ jdate.time.to_ruby_date
85
+ end
86
+
87
+ private
88
+
89
+ def dump(obj)
90
+ @database.lazy_loader.suspend { obj.dump }
91
+ end
92
+
93
+ # @param (see #mock_storable_template)
94
+ # @return (see #mock_storable_template)
95
+ def mock_create_template(obj)
96
+ mock_storable_template(obj) { |ref| ref.class.creatable_domain_attributes }
97
+ end
98
+
99
+ # @param (see #mock_storable_template)
100
+ # @return (see #mock_storable_template)
101
+ def mock_update_template(obj)
102
+ mock_storable_template(obj) { |ref| ref.class.updatable_domain_attributes }
103
+ end
104
+
105
+ # @param [Resource] obj the domain object to "save"
106
+ # @return [Resource] the template to use in the save operation
107
+ # @yield [ref] the storable attributes
108
+ # @yieldparam [Resource] ref the domain object to "save"
109
+ def mock_storable_template(obj, &selector)
110
+ # add fake identifiers to prerequisite references
111
+ obj.class.storable_prerequisite_attributes.each do |attr|
112
+ obj.send(attr).enumerate { |ref| ref.identifier = 1 }
113
+ end
114
+ # the template builder
115
+ bldr = CaRuby::StoreTemplateBuilder.new(@database, &selector)
116
+ # the save template
117
+ bldr.build_template(obj)
118
+ end
119
+
120
+ def verify_saved_dependent(dependent)
121
+ verify_dependency(dependent)
122
+ verify_saved_content(dependent)
123
+ end
124
+
125
+ def verify_dependency(dependent)
126
+ return if dependent.class.owner_attribute.nil?
127
+ owner = dependent.owner
128
+ assert_not_nil(owner, "Owner missing for dependent: #{dependent}")
129
+ attribute = owner.class.dependent_attribute(dependent.class)
130
+ assert_not_nil(attribute, "Dependent attribute missing for #{dependent} owner #{owner}")
131
+ # a dependent collection reference must be refetched
132
+ if owner.class.collection_attribute?(attribute) then
133
+ verify_saved_dependent_collection_member(dependent, owner, attribute)
134
+ else
135
+ assert_not_nil(dependent.identifier, "Stored dependent #{dependent} identifier is not set")
136
+ end
137
+ end
138
+
139
+ # Verifies that the given dependent has an identifier and that the given owner dependent attribute value
140
+ # contains the dependent.
141
+ def verify_saved_dependent_collection_member(dependent, owner, attribute)
142
+ deps = owner.send(attribute)
143
+ assert(deps.include?(dependent), "Owner #{owner.qp} #{attribute} value #{deps.pp_s} does not contain #{dependent}")
144
+ assert_not_nil(dependent.identifier, "Identifier not set for stored owner #{owner.qp} #{attribute} dependent collection member #{dependent}")
145
+ end
146
+
147
+ # Verifies the subject stored content.
148
+ def verify_saved_independent(subject)
149
+ subject_id = subject.identifier
150
+ assert_not_nil(subject_id, "#{subject.class.qp} identifier not set")
151
+ verify_saved_content(subject)
152
+ end
153
+
154
+ # Verifies that the given subject matches the database content. Does not verify subject unless it has
155
+ # a secondary key.
156
+ def verify_saved_content(subject)
157
+ attrs = subject.class.secondary_key_attributes & subject.class.mandatory_attributes
158
+ return if attrs.empty?
159
+ missing = attrs.reject { |attr| subject.send(attr) }
160
+ assert(missing.empty?, "#{subject} is missing values for secondary key attributes #{missing.to_series}")
161
+ # make a secondary key search template
162
+ vh = attrs.to_compact_hash do |attr|
163
+ v = subject.send(attr)
164
+ Resource === v ? v.copy : v
165
+ end
166
+ tmpl = subject.class.new(vh)
167
+ # find the template in the database
168
+ logger.debug { "Verifying #{subject.qp} by finding and comparing template #{tmpl.pp_s}..." }
169
+ assert_not_nil(tmpl.find, "#{subject} not found in database")
170
+ # compare the subject to the fetched template
171
+ verify_that_saved_matches_fetched(subject, tmpl)
172
+ end
173
+
174
+ # Verifies that the given expected domain object has the same content as actual,
175
+ # and that the dependents match.
176
+ #
177
+ # @param [Resource] expected the saved value
178
+ # @param [Resource] actual the fetched value
179
+ def verify_that_saved_matches_fetched(expected, actual)
180
+ expected.class.saved_nondomain_attributes.each do |attr|
181
+ # compare attributes that are fetched and set on create
182
+ attr_md = expected.class.attribute_metadata(attr)
183
+ if verify_saved_attribute?(attr_md) then
184
+ eval = expected.database.lazy_loader.suspend { expected.send(attr) }
185
+ next if eval.nil_or_empty?
186
+ aval = actual.send(attr)
187
+ if eval.nil? then
188
+ assert_nil(aval, "#{expected.qp} was saved without a #{attr} value, but was stored in the database with value #{actual.qp}")
189
+ else
190
+ assert_not_nil(aval, "#{expected.qp} was saved with #{attr} value #{eval.qp}, but this value was not found in the database.")
191
+ assert(CaRuby::Resource.value_equal?(eval, aval), "#{expected.qp} was saved with #{attr} value #{eval.qp} that does not match the database value #{aval.qp}")
192
+ end
193
+ end
194
+ end
195
+ verify_dependents_match(expected, actual)
196
+ end
197
+
198
+ # @param [AttributeMetadata] attr_md the saved attribute to check
199
+ # @return whether the attribute is fetched, creatable and not volatile
200
+ def verify_saved_attribute?(attr_md)
201
+ attr_md.fetched? and attr_md.creatable? and not attr_md.volatile?
202
+ end
203
+
204
+ # Verifies that each expected dependent matches an actual dependent and has the same content.
205
+ def verify_dependents_match(expected, actual)
206
+ expected.class.dependent_attributes.each do |attr|
207
+ edeps = expected.database.lazy_loader.suspend { expected.send(attr) }
208
+ next if edeps.nil_or_empty?
209
+ adeps = actual.send(attr)
210
+ logger.debug { "Verifying #{expected.qp} dependent #{attr} #{edeps.qp} against #{actual.qp} #{adeps.qp}..." } unless edeps.nil_or_empty?
211
+ if edeps.collection? then
212
+ edeps.each do |edep|
213
+ adep = edep.match_in_owner_scope(adeps)
214
+ assert_not_nil(adep, "#{expected} #{attr} dependent #{edep} not found in fetched #{adeps.pp_s}")
215
+ verify_that_saved_matches_fetched(edep, adep)
216
+ end
217
+ else
218
+ edep = edeps; adep = adeps;
219
+ assert_not_nil(adep, "#{expected} #{attr} dependent #{edep} not found in database")
220
+ verify_that_saved_matches_fetched(edep, adep)
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end