activerecord-import-uuid 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. data/.gitignore +32 -0
  2. data/.rubocop.yml +49 -0
  3. data/.rubocop_todo.yml +36 -0
  4. data/.travis.yml +52 -0
  5. data/Brewfile +3 -0
  6. data/CHANGELOG.md +87 -0
  7. data/Gemfile +54 -0
  8. data/LICENSE +56 -0
  9. data/README.markdown +101 -0
  10. data/Rakefile +66 -0
  11. data/activerecord-import.gemspec +23 -0
  12. data/benchmarks/README +32 -0
  13. data/benchmarks/benchmark.rb +67 -0
  14. data/benchmarks/lib/base.rb +138 -0
  15. data/benchmarks/lib/cli_parser.rb +106 -0
  16. data/benchmarks/lib/float.rb +15 -0
  17. data/benchmarks/lib/mysql2_benchmark.rb +19 -0
  18. data/benchmarks/lib/output_to_csv.rb +19 -0
  19. data/benchmarks/lib/output_to_html.rb +64 -0
  20. data/benchmarks/models/test_innodb.rb +3 -0
  21. data/benchmarks/models/test_memory.rb +3 -0
  22. data/benchmarks/models/test_myisam.rb +3 -0
  23. data/benchmarks/schema/mysql_schema.rb +16 -0
  24. data/gemfiles/3.2.gemfile +3 -0
  25. data/gemfiles/4.0.gemfile +3 -0
  26. data/gemfiles/4.1.gemfile +3 -0
  27. data/gemfiles/4.2.gemfile +7 -0
  28. data/gemfiles/5.0.gemfile +3 -0
  29. data/lib/activerecord-import.rb +19 -0
  30. data/lib/activerecord-import/active_record/adapters/abstract_adapter.rb +9 -0
  31. data/lib/activerecord-import/active_record/adapters/jdbcmysql_adapter.rb +6 -0
  32. data/lib/activerecord-import/active_record/adapters/jdbcpostgresql_adapter.rb +6 -0
  33. data/lib/activerecord-import/active_record/adapters/mysql2_adapter.rb +6 -0
  34. data/lib/activerecord-import/active_record/adapters/postgresql_adapter.rb +6 -0
  35. data/lib/activerecord-import/active_record/adapters/seamless_database_pool_adapter.rb +7 -0
  36. data/lib/activerecord-import/active_record/adapters/sqlite3_adapter.rb +6 -0
  37. data/lib/activerecord-import/adapters/abstract_adapter.rb +78 -0
  38. data/lib/activerecord-import/adapters/em_mysql2_adapter.rb +5 -0
  39. data/lib/activerecord-import/adapters/mysql2_adapter.rb +5 -0
  40. data/lib/activerecord-import/adapters/mysql_adapter.rb +114 -0
  41. data/lib/activerecord-import/adapters/postgresql_adapter.rb +144 -0
  42. data/lib/activerecord-import/adapters/sqlite3_adapter.rb +51 -0
  43. data/lib/activerecord-import/base.rb +38 -0
  44. data/lib/activerecord-import/import.rb +660 -0
  45. data/lib/activerecord-import/mysql2.rb +7 -0
  46. data/lib/activerecord-import/postgresql.rb +7 -0
  47. data/lib/activerecord-import/sqlite3.rb +7 -0
  48. data/lib/activerecord-import/synchronize.rb +66 -0
  49. data/lib/activerecord-import/value_sets_parser.rb +55 -0
  50. data/lib/activerecord-import/version.rb +5 -0
  51. data/test/adapters/jdbcmysql.rb +1 -0
  52. data/test/adapters/jdbcpostgresql.rb +1 -0
  53. data/test/adapters/mysql2.rb +1 -0
  54. data/test/adapters/mysql2_makara.rb +1 -0
  55. data/test/adapters/mysql2spatial.rb +1 -0
  56. data/test/adapters/postgis.rb +1 -0
  57. data/test/adapters/postgresql.rb +1 -0
  58. data/test/adapters/postgresql_makara.rb +1 -0
  59. data/test/adapters/seamless_database_pool.rb +1 -0
  60. data/test/adapters/spatialite.rb +1 -0
  61. data/test/adapters/sqlite3.rb +1 -0
  62. data/test/database.yml.sample +52 -0
  63. data/test/import_test.rb +574 -0
  64. data/test/jdbcmysql/import_test.rb +6 -0
  65. data/test/jdbcpostgresql/import_test.rb +5 -0
  66. data/test/models/book.rb +7 -0
  67. data/test/models/chapter.rb +4 -0
  68. data/test/models/discount.rb +3 -0
  69. data/test/models/end_note.rb +4 -0
  70. data/test/models/group.rb +3 -0
  71. data/test/models/promotion.rb +3 -0
  72. data/test/models/question.rb +3 -0
  73. data/test/models/rule.rb +3 -0
  74. data/test/models/topic.rb +9 -0
  75. data/test/models/widget.rb +24 -0
  76. data/test/mysql2/import_test.rb +5 -0
  77. data/test/mysql2_makara/import_test.rb +6 -0
  78. data/test/mysqlspatial2/import_test.rb +6 -0
  79. data/test/postgis/import_test.rb +4 -0
  80. data/test/postgresql/import_test.rb +8 -0
  81. data/test/schema/generic_schema.rb +144 -0
  82. data/test/schema/mysql_schema.rb +16 -0
  83. data/test/schema/version.rb +10 -0
  84. data/test/sqlite3/import_test.rb +52 -0
  85. data/test/support/active_support/test_case_extensions.rb +70 -0
  86. data/test/support/assertions.rb +73 -0
  87. data/test/support/factories.rb +57 -0
  88. data/test/support/generate.rb +29 -0
  89. data/test/support/mysql/import_examples.rb +85 -0
  90. data/test/support/postgresql/import_examples.rb +242 -0
  91. data/test/support/shared_examples/on_duplicate_key_update.rb +103 -0
  92. data/test/support/shared_examples/recursive_import.rb +122 -0
  93. data/test/synchronize_test.rb +33 -0
  94. data/test/test_helper.rb +59 -0
  95. data/test/travis/database.yml +62 -0
  96. data/test/value_sets_bytes_parser_test.rb +93 -0
  97. data/test/value_sets_records_parser_test.rb +32 -0
  98. metadata +225 -0
@@ -0,0 +1,6 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/../support/assertions')
4
+ require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples')
5
+
6
+ should_support_mysql_import_functionality
@@ -0,0 +1,5 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/../support/postgresql/import_examples')
4
+
5
+ should_support_postgresql_import_functionality
@@ -0,0 +1,7 @@
1
+ class Book < ActiveRecord::Base
2
+ belongs_to :topic, inverse_of: :books
3
+ has_many :chapters, inverse_of: :book
4
+ has_many :discounts, as: :discountable
5
+ has_many :end_notes, inverse_of: :book
6
+ enum status: [:draft, :published] if ENV['AR_VERSION'].to_f >= 4.1
7
+ end
@@ -0,0 +1,4 @@
1
+ class Chapter < ActiveRecord::Base
2
+ belongs_to :book, inverse_of: :chapters
3
+ validates :title, presence: true
4
+ end
@@ -0,0 +1,3 @@
1
+ class Discount < ActiveRecord::Base
2
+ belongs_to :discountable, polymorphic: true
3
+ end
@@ -0,0 +1,4 @@
1
+ class EndNote < ActiveRecord::Base
2
+ belongs_to :book, inverse_of: :end_notes
3
+ validates :note, presence: true
4
+ end
@@ -0,0 +1,3 @@
1
+ class Group < ActiveRecord::Base
2
+ self.table_name = 'group'
3
+ end
@@ -0,0 +1,3 @@
1
+ class Promotion < ActiveRecord::Base
2
+ self.primary_key = :promotion_id
3
+ end
@@ -0,0 +1,3 @@
1
+ class Question < ActiveRecord::Base
2
+ has_one :rule
3
+ end
@@ -0,0 +1,3 @@
1
+ class Rule < ActiveRecord::Base
2
+ belongs_to :question
3
+ end
@@ -0,0 +1,9 @@
1
+ class Topic < ActiveRecord::Base
2
+ validates_presence_of :author_name
3
+ validates :title, numericality: { only_integer: true }, on: :context_test
4
+
5
+ has_many :books, inverse_of: :topic
6
+ belongs_to :parent, class_name: "Topic"
7
+
8
+ composed_of :description, mapping: [%w(title title), %w(author_name author_name)], allow_nil: true, class_name: "TopicDescription"
9
+ end
@@ -0,0 +1,24 @@
1
+ class CustomCoder
2
+ def load(value)
3
+ if value.nil?
4
+ {}
5
+ else
6
+ YAML.load(value)
7
+ end
8
+ end
9
+
10
+ def dump(value)
11
+ YAML.dump(value)
12
+ end
13
+ end
14
+
15
+ class Widget < ActiveRecord::Base
16
+ self.primary_key = :w_id
17
+
18
+ default_scope -> { where(active: true) }
19
+
20
+ serialize :data, Hash
21
+ serialize :json_data, JSON
22
+ serialize :unspecified_data
23
+ serialize :custom_data, CustomCoder.new
24
+ end
@@ -0,0 +1,5 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../support/assertions')
3
+ require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples')
4
+
5
+ should_support_mysql_import_functionality
@@ -0,0 +1,6 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/../support/assertions')
4
+ require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples')
5
+
6
+ should_support_mysql_import_functionality
@@ -0,0 +1,6 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/../support/assertions')
4
+ require File.expand_path(File.dirname(__FILE__) + '/../support/mysql/import_examples')
5
+
6
+ should_support_mysql_import_functionality
@@ -0,0 +1,4 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../support/postgresql/import_examples')
3
+
4
+ should_support_postgresql_import_functionality
@@ -0,0 +1,8 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../support/postgresql/import_examples')
3
+
4
+ should_support_postgresql_import_functionality
5
+
6
+ if ActiveRecord::Base.connection.supports_on_duplicate_key_update?
7
+ should_support_postgresql_upsert_functionality
8
+ end
@@ -0,0 +1,144 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table :schema_info, force: :cascade do |t|
3
+ t.integer :version, unique: true
4
+ end
5
+ SchemaInfo.create version: SchemaInfo::VERSION
6
+
7
+ create_table :group, force: :cascade do |t|
8
+ t.string :order
9
+ t.timestamps null: true
10
+ end
11
+
12
+ create_table :topics, force: :cascade do |t|
13
+ t.string :title, null: false
14
+ t.string :author_name
15
+ t.string :author_email_address
16
+ t.datetime :written_on
17
+ t.time :bonus_time
18
+ t.datetime :last_read
19
+ t.text :content
20
+ t.boolean :approved, default: '1'
21
+ t.integer :replies_count
22
+ t.integer :parent_id
23
+ t.string :type
24
+ t.datetime :created_at
25
+ t.datetime :created_on
26
+ t.datetime :updated_at
27
+ t.datetime :updated_on
28
+ end
29
+
30
+ create_table :projects, force: :cascade do |t|
31
+ t.string :name
32
+ t.string :type
33
+ end
34
+
35
+ create_table :developers, force: :cascade do |t|
36
+ t.string :name
37
+ t.integer :salary, default: '70000'
38
+ t.datetime :created_at
39
+ t.integer :team_id
40
+ t.datetime :updated_at
41
+ end
42
+
43
+ create_table :addresses, force: :cascade do |t|
44
+ t.string :address
45
+ t.string :city
46
+ t.string :state
47
+ t.string :zip
48
+ t.integer :developer_id
49
+ end
50
+
51
+ create_table :teams, force: :cascade do |t|
52
+ t.string :name
53
+ end
54
+
55
+ create_table :books, force: :cascade do |t|
56
+ t.string :title, null: false
57
+ t.string :publisher, null: false, default: 'Default Publisher'
58
+ t.string :author_name, null: false
59
+ t.datetime :created_at
60
+ t.datetime :created_on
61
+ t.datetime :updated_at
62
+ t.datetime :updated_on
63
+ t.date :publish_date
64
+ t.integer :topic_id
65
+ t.boolean :for_sale, default: true
66
+ t.integer :status, default: 0
67
+ end
68
+
69
+ create_table :chapters, force: :cascade do |t|
70
+ t.string :title
71
+ t.integer :book_id, null: false
72
+ t.datetime :created_at
73
+ t.datetime :updated_at
74
+ end
75
+
76
+ create_table :end_notes, force: :cascade do |t|
77
+ t.string :note
78
+ t.integer :book_id, null: false
79
+ t.datetime :created_at
80
+ t.datetime :updated_at
81
+ end
82
+
83
+ create_table :languages, force: :cascade do |t|
84
+ t.string :name
85
+ t.integer :developer_id
86
+ end
87
+
88
+ create_table :shopping_carts, force: :cascade do |t|
89
+ t.string :name, null: true
90
+ t.datetime :created_at
91
+ t.datetime :updated_at
92
+ end
93
+
94
+ create_table :cart_items, force: :cascade do |t|
95
+ t.string :shopping_cart_id, null: false
96
+ t.string :book_id, null: false
97
+ t.integer :copies, default: 1
98
+ t.datetime :created_at
99
+ t.datetime :updated_at
100
+ end
101
+
102
+ add_index :cart_items, [:shopping_cart_id, :book_id], unique: true, name: 'uk_shopping_cart_books'
103
+
104
+ create_table :animals, force: :cascade do |t|
105
+ t.string :name, null: false
106
+ t.string :size, default: nil
107
+ t.datetime :created_at
108
+ t.datetime :updated_at
109
+ end
110
+
111
+ add_index :animals, [:name], unique: true, name: 'uk_animals'
112
+
113
+ create_table :widgets, id: false, force: :cascade do |t|
114
+ t.integer :w_id
115
+ t.boolean :active, default: false
116
+ t.text :data
117
+ t.text :json_data
118
+ t.text :unspecified_data
119
+ t.text :custom_data
120
+ end
121
+
122
+ create_table :promotions, primary_key: :promotion_id, force: :cascade do |t|
123
+ t.string :code
124
+ t.string :description
125
+ t.decimal :discount
126
+ end
127
+
128
+ add_index :promotions, [:code], unique: true, name: 'uk_code'
129
+
130
+ create_table :discounts, force: :cascade do |t|
131
+ t.decimal :amount
132
+ t.integer :discountable_id
133
+ t.string :discountable_type
134
+ end
135
+
136
+ create_table :rules, force: :cascade do |t|
137
+ t.string :condition_text
138
+ t.integer :question_id
139
+ end
140
+
141
+ create_table :questions, force: :cascade do |t|
142
+ t.string :body
143
+ end
144
+ end
@@ -0,0 +1,16 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table :books, options: 'ENGINE=MyISAM', force: true do |t|
3
+ t.column :title, :string, null: false
4
+ t.column :publisher, :string, null: false, default: 'Default Publisher'
5
+ t.column :author_name, :string, null: false
6
+ t.column :created_at, :datetime
7
+ t.column :created_on, :datetime
8
+ t.column :updated_at, :datetime
9
+ t.column :updated_on, :datetime
10
+ t.column :publish_date, :date
11
+ t.column :topic_id, :integer
12
+ t.column :for_sale, :boolean, default: true
13
+ t.column :status, :integer
14
+ end
15
+ execute "ALTER TABLE books ADD FULLTEXT( `title`, `publisher`, `author_name` )"
16
+ end
@@ -0,0 +1,10 @@
1
+ class SchemaInfo < ActiveRecord::Base
2
+ if respond_to?(:table_name=)
3
+ self.table_name = 'schema_info'
4
+ else
5
+ # this is becoming deprecated in ActiveRecord but not all adapters supported it
6
+ # at this time
7
+ set_table_name 'schema_info'
8
+ end
9
+ VERSION = 12
10
+ end
@@ -0,0 +1,52 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ should_support_recursive_import
4
+
5
+ describe "#supports_imports?" do
6
+ context "and SQLite is 3.7.11 or higher" do
7
+ it "supports import" do
8
+ version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.11")
9
+ assert ActiveRecord::Base.supports_import?(version)
10
+
11
+ version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.12")
12
+ assert ActiveRecord::Base.supports_import?(version)
13
+ end
14
+ end
15
+
16
+ context "and SQLite less than 3.7.11" do
17
+ it "doesn't support import" do
18
+ version = ActiveRecord::ConnectionAdapters::SQLite3Adapter::Version.new("3.7.10")
19
+ assert !ActiveRecord::Base.supports_import?(version)
20
+ end
21
+ end
22
+ end
23
+
24
+ describe "#import" do
25
+ it "imports with a single insert on SQLite 3.7.11 or higher" do
26
+ assert_difference "Topic.count", +507 do
27
+ result = Topic.import Build(7, :topics)
28
+ assert_equal 1, result.num_inserts, "Failed to issue a single INSERT statement. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
29
+ assert_equal 7, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
30
+
31
+ result = Topic.import Build(500, :topics)
32
+ assert_equal 1, result.num_inserts, "Failed to issue a single INSERT statement. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
33
+ assert_equal 507, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
34
+ end
35
+ end
36
+
37
+ it "imports with a two inserts on SQLite 3.7.11 or higher" do
38
+ assert_difference "Topic.count", +501 do
39
+ result = Topic.import Build(501, :topics)
40
+ assert_equal 2, result.num_inserts, "Failed to issue a two INSERT statements. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
41
+ assert_equal 501, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
42
+ end
43
+ end
44
+
45
+ it "imports with a five inserts on SQLite 3.7.11 or higher" do
46
+ assert_difference "Topic.count", +2500 do
47
+ result = Topic.import Build(2500, :topics)
48
+ assert_equal 5, result.num_inserts, "Failed to issue a two INSERT statements. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
49
+ assert_equal 2500, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,70 @@
1
+ class ActiveSupport::TestCase
2
+ include ActiveRecord::TestFixtures
3
+ self.use_transactional_fixtures = true
4
+
5
+ class << self
6
+ def requires_active_record_version(version_string, &blk)
7
+ return unless Gem::Dependency.new('', version_string).match?('', ActiveRecord::VERSION::STRING)
8
+ instance_eval(&blk)
9
+ end
10
+
11
+ def assertion(name, &block)
12
+ mc = class << self; self; end
13
+ mc.class_eval do
14
+ define_method(name) do
15
+ it(name, &block)
16
+ end
17
+ end
18
+ end
19
+
20
+ def asssertion_group(name, &block)
21
+ mc = class << self; self; end
22
+ mc.class_eval do
23
+ define_method(name, &block)
24
+ end
25
+ end
26
+
27
+ def macro(name, &block)
28
+ class_eval do
29
+ define_method(name, &block)
30
+ end
31
+ end
32
+
33
+ def describe(description, toplevel = nil, &blk)
34
+ text = toplevel ? description : "#{name} #{description}"
35
+ klass = Class.new(self)
36
+
37
+ klass.class_eval <<-RUBY_EVAL
38
+ def self.name
39
+ "#{text}"
40
+ end
41
+ RUBY_EVAL
42
+
43
+ # do not inherit test methods from the superclass
44
+ klass.class_eval do
45
+ instance_methods.grep(/^test.+/) do |method|
46
+ undef_method method
47
+ end
48
+ end
49
+
50
+ klass.instance_eval(&blk)
51
+ end
52
+ alias context describe
53
+
54
+ def let(name, &blk)
55
+ define_method(name) do
56
+ instance_variable_name = "@__let_#{name}"
57
+ return instance_variable_get(instance_variable_name) if instance_variable_defined?(instance_variable_name)
58
+ instance_variable_set(instance_variable_name, instance_eval(&blk))
59
+ end
60
+ end
61
+
62
+ def it(description, &blk)
63
+ define_method("test_#{name}_#{description}", &blk)
64
+ end
65
+ end
66
+ end
67
+
68
+ def describe(description, &blk)
69
+ ActiveSupport::TestCase.describe(description, true, &blk)
70
+ end
@@ -0,0 +1,73 @@
1
+ class ActiveSupport::TestCase
2
+ module ImportAssertions
3
+ def self.extended(klass)
4
+ klass.instance_eval do
5
+ assertion(:should_not_update_created_at_on_timestamp_columns) do
6
+ Timecop.freeze Chronic.parse("5 minutes from now") do
7
+ perform_import
8
+ assert_in_delta @topic.created_at.to_i, updated_topic.created_at.to_i, 1
9
+ assert_in_delta @topic.created_on.to_i, updated_topic.created_on.to_i, 1
10
+ end
11
+ end
12
+
13
+ assertion(:should_update_updated_at_on_timestamp_columns) do
14
+ time = Chronic.parse("5 minutes from now")
15
+ Timecop.freeze time do
16
+ perform_import
17
+ assert_in_delta time.to_i, updated_topic.updated_at.to_i, 1
18
+ assert_in_delta time.to_i, updated_topic.updated_on.to_i, 1
19
+ end
20
+ end
21
+
22
+ assertion(:should_not_update_updated_at_on_timestamp_columns) do
23
+ time = Chronic.parse("5 minutes from now")
24
+ Timecop.freeze time do
25
+ perform_import
26
+ assert_in_delta @topic.updated_at.to_i, updated_topic.updated_at.to_i, 1
27
+ assert_in_delta @topic.updated_on.to_i, updated_topic.updated_on.to_i, 1
28
+ end
29
+ end
30
+
31
+ assertion(:should_not_update_timestamps) do
32
+ Timecop.freeze Chronic.parse("5 minutes from now") do
33
+ perform_import timestamps: false
34
+ assert_in_delta @topic.created_at.to_i, updated_topic.created_at.to_i, 1
35
+ assert_in_delta @topic.created_on.to_i, updated_topic.created_on.to_i, 1
36
+ assert_in_delta @topic.updated_at.to_i, updated_topic.updated_at.to_i, 1
37
+ assert_in_delta @topic.updated_on.to_i, updated_topic.updated_on.to_i, 1
38
+ end
39
+ end
40
+
41
+ assertion(:should_not_update_fields_not_mentioned) do
42
+ assert_equal "John Doe", updated_topic.author_name
43
+ end
44
+
45
+ assertion(:should_update_fields_mentioned) do
46
+ perform_import
47
+ assert_equal "Book - 2nd Edition", updated_topic.title
48
+ assert_equal "johndoe@example.com", updated_topic.author_email_address
49
+ end
50
+
51
+ assertion(:should_raise_update_fields_mentioned) do
52
+ assert_raise ActiveRecord::RecordNotUnique do
53
+ perform_import
54
+ end
55
+
56
+ assert_equal "Book", updated_topic.title
57
+ assert_equal "john@doe.com", updated_topic.author_email_address
58
+ end
59
+
60
+ assertion(:should_update_fields_mentioned_with_hash_mappings) do
61
+ perform_import
62
+ assert_equal "johndoe@example.com", updated_topic.title
63
+ assert_equal "Book - 2nd Edition", updated_topic.author_email_address
64
+ end
65
+
66
+ assertion(:should_update_foreign_keys) do
67
+ perform_import
68
+ assert_equal 57, updated_topic.parent_id
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end