lhm-shopify 3.3.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/test.yml +34 -0
  3. data/.gitignore +17 -0
  4. data/.rubocop.yml +183 -0
  5. data/.travis.yml +21 -0
  6. data/CHANGELOG.md +216 -0
  7. data/Gemfile +5 -0
  8. data/LICENSE +27 -0
  9. data/README.md +284 -0
  10. data/Rakefile +22 -0
  11. data/bin/.gitkeep +0 -0
  12. data/dbdeployer/config.json +32 -0
  13. data/dbdeployer/install.sh +64 -0
  14. data/dev.yml +20 -0
  15. data/gemfiles/ar-2.3_mysql.gemfile +6 -0
  16. data/gemfiles/ar-3.2_mysql.gemfile +5 -0
  17. data/gemfiles/ar-3.2_mysql2.gemfile +5 -0
  18. data/gemfiles/ar-4.0_mysql2.gemfile +5 -0
  19. data/gemfiles/ar-4.1_mysql2.gemfile +5 -0
  20. data/gemfiles/ar-4.2_mysql2.gemfile +5 -0
  21. data/gemfiles/ar-5.0_mysql2.gemfile +5 -0
  22. data/lhm.gemspec +34 -0
  23. data/lib/lhm.rb +131 -0
  24. data/lib/lhm/atomic_switcher.rb +52 -0
  25. data/lib/lhm/chunk_finder.rb +32 -0
  26. data/lib/lhm/chunk_insert.rb +51 -0
  27. data/lib/lhm/chunker.rb +87 -0
  28. data/lib/lhm/cleanup/current.rb +74 -0
  29. data/lib/lhm/command.rb +48 -0
  30. data/lib/lhm/entangler.rb +117 -0
  31. data/lib/lhm/intersection.rb +51 -0
  32. data/lib/lhm/invoker.rb +98 -0
  33. data/lib/lhm/locked_switcher.rb +74 -0
  34. data/lib/lhm/migration.rb +43 -0
  35. data/lib/lhm/migrator.rb +237 -0
  36. data/lib/lhm/printer.rb +59 -0
  37. data/lib/lhm/railtie.rb +9 -0
  38. data/lib/lhm/sql_helper.rb +77 -0
  39. data/lib/lhm/sql_retry.rb +61 -0
  40. data/lib/lhm/table.rb +121 -0
  41. data/lib/lhm/table_name.rb +23 -0
  42. data/lib/lhm/test_support.rb +35 -0
  43. data/lib/lhm/throttler.rb +36 -0
  44. data/lib/lhm/throttler/slave_lag.rb +145 -0
  45. data/lib/lhm/throttler/threads_running.rb +53 -0
  46. data/lib/lhm/throttler/time.rb +29 -0
  47. data/lib/lhm/timestamp.rb +11 -0
  48. data/lib/lhm/version.rb +6 -0
  49. data/shipit.rubygems.yml +0 -0
  50. data/spec/.lhm.example +4 -0
  51. data/spec/README.md +58 -0
  52. data/spec/fixtures/bigint_table.ddl +4 -0
  53. data/spec/fixtures/composite_primary_key.ddl +7 -0
  54. data/spec/fixtures/custom_primary_key.ddl +6 -0
  55. data/spec/fixtures/destination.ddl +6 -0
  56. data/spec/fixtures/lines.ddl +7 -0
  57. data/spec/fixtures/origin.ddl +6 -0
  58. data/spec/fixtures/permissions.ddl +5 -0
  59. data/spec/fixtures/small_table.ddl +4 -0
  60. data/spec/fixtures/tracks.ddl +5 -0
  61. data/spec/fixtures/users.ddl +14 -0
  62. data/spec/fixtures/wo_id_int_column.ddl +6 -0
  63. data/spec/integration/atomic_switcher_spec.rb +93 -0
  64. data/spec/integration/chunk_insert_spec.rb +29 -0
  65. data/spec/integration/chunker_spec.rb +185 -0
  66. data/spec/integration/cleanup_spec.rb +136 -0
  67. data/spec/integration/entangler_spec.rb +66 -0
  68. data/spec/integration/integration_helper.rb +237 -0
  69. data/spec/integration/invoker_spec.rb +33 -0
  70. data/spec/integration/lhm_spec.rb +585 -0
  71. data/spec/integration/lock_wait_timeout_spec.rb +30 -0
  72. data/spec/integration/locked_switcher_spec.rb +50 -0
  73. data/spec/integration/sql_retry/lock_wait_spec.rb +125 -0
  74. data/spec/integration/sql_retry/lock_wait_timeout_test_helper.rb +101 -0
  75. data/spec/integration/table_spec.rb +91 -0
  76. data/spec/test_helper.rb +32 -0
  77. data/spec/unit/atomic_switcher_spec.rb +31 -0
  78. data/spec/unit/chunk_finder_spec.rb +73 -0
  79. data/spec/unit/chunk_insert_spec.rb +44 -0
  80. data/spec/unit/chunker_spec.rb +166 -0
  81. data/spec/unit/entangler_spec.rb +124 -0
  82. data/spec/unit/intersection_spec.rb +51 -0
  83. data/spec/unit/lhm_spec.rb +29 -0
  84. data/spec/unit/locked_switcher_spec.rb +51 -0
  85. data/spec/unit/migrator_spec.rb +146 -0
  86. data/spec/unit/printer_spec.rb +97 -0
  87. data/spec/unit/sql_helper_spec.rb +32 -0
  88. data/spec/unit/table_name_spec.rb +39 -0
  89. data/spec/unit/table_spec.rb +47 -0
  90. data/spec/unit/throttler/slave_lag_spec.rb +317 -0
  91. data/spec/unit/throttler/threads_running_spec.rb +64 -0
  92. data/spec/unit/throttler_spec.rb +124 -0
  93. data/spec/unit/unit_helper.rb +13 -0
  94. metadata +239 -0
@@ -0,0 +1,73 @@
1
+ # Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
2
+ # Schmidt
3
+
4
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
5
+
6
+ describe Lhm::ChunkFinder do
7
+ before(:each) do
8
+ @origin = Lhm::Table.new('foo')
9
+ @destination = Lhm::Table.new('bar')
10
+ @migration = Lhm::Migration.new(@origin, @destination)
11
+ @connection = mock()
12
+ end
13
+
14
+ describe '#validate' do
15
+ describe 'when start is greater than limit' do
16
+ it 'raises' do
17
+ assert_raises { Lhm::ChunkFinder.new(@connection, @migration, {start: 2, limit: 1}).validate }
18
+ end
19
+ end
20
+
21
+ describe 'when start is greater than limit' do
22
+ it 'does not raise' do
23
+ Lhm::ChunkFinder.new(@connection, @migration, {start: 1, limit: 2}).validate # does not raise
24
+ end
25
+ end
26
+ end
27
+
28
+ describe '#start' do
29
+ describe 'when initialized with 5' do
30
+ before(:each) do
31
+ @instance = Lhm::ChunkFinder.new(@connection, @migration, {start: 5, limit: 6})
32
+ end
33
+
34
+ it 'returns 5' do
35
+ assert_equal @instance.start, 5
36
+ end
37
+ end
38
+
39
+ describe 'when initialized with nil and the min(id) is 22' do
40
+ before(:each) do
41
+ @connection.expects(:select_value).returns(22)
42
+ @instance = Lhm::ChunkFinder.new(@migration, @connection, {limit: 6})
43
+ end
44
+
45
+ it 'returns 22' do
46
+ assert_equal @instance.start, 22
47
+ end
48
+ end
49
+ end
50
+
51
+ describe '#limit' do
52
+ describe 'when initialized with 6' do
53
+ before(:each) do
54
+ @instance = Lhm::ChunkFinder.new(@connection, @migration, {start: 5, limit: 6})
55
+ end
56
+
57
+ it 'returns 6' do
58
+ assert_equal @instance.limit, 6
59
+ end
60
+ end
61
+
62
+ describe 'when initialized with nil and the max(id) is 33' do
63
+ before(:each) do
64
+ @connection.expects(:select_value).returns(33)
65
+ @instance = Lhm::ChunkFinder.new(@migration, @connection, {start: 5})
66
+ end
67
+
68
+ it 'returns 33' do
69
+ assert_equal @instance.limit, 33
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,44 @@
1
+ # Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
2
+ # Schmidt
3
+
4
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
5
+
6
+ require 'lhm/chunk_insert'
7
+
8
+ describe Lhm::ChunkInsert do
9
+ before(:each) do
10
+ @connection = stub(:connection)
11
+ @origin = Lhm::Table.new('foo')
12
+ @destination = Lhm::Table.new('bar')
13
+ end
14
+
15
+ describe "#sql" do
16
+ describe "when migration has no conditions" do
17
+ before { @migration = Lhm::Migration.new(@origin, @destination) }
18
+
19
+ it "uses a simple where clause" do
20
+ assert_equal(
21
+ Lhm::ChunkInsert.new(@migration, @connection, 1, 2).sql,
22
+ "insert ignore into `bar` () select from `foo` where `foo`.`id` between 1 and 2"
23
+ )
24
+ end
25
+ end
26
+
27
+ describe "when migration has a WHERE condition" do
28
+ before do
29
+ @migration = Lhm::Migration.new(
30
+ @origin,
31
+ @destination,
32
+ "where foo.created_at > '2013-07-10' or foo.baz = 'quux'"
33
+ )
34
+ end
35
+
36
+ it "combines the clause with the chunking WHERE condition" do
37
+ assert_equal(
38
+ Lhm::ChunkInsert.new(@migration, @connection, 1, 2).sql,
39
+ "insert ignore into `bar` () select from `foo` where (foo.created_at > '2013-07-10' or foo.baz = 'quux') and `foo`.`id` between 1 and 2"
40
+ )
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,166 @@
1
+ # Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
2
+ # Schmidt
3
+
4
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
5
+
6
+ require 'lhm/table'
7
+ require 'lhm/migration'
8
+ require 'lhm/chunker'
9
+ require 'lhm/throttler'
10
+
11
+ describe Lhm::Chunker do
12
+ include UnitHelper
13
+
14
+ before(:each) do
15
+ @origin = Lhm::Table.new('foo')
16
+ @destination = Lhm::Table.new('bar')
17
+ @migration = Lhm::Migration.new(@origin, @destination)
18
+ @connection = mock()
19
+ # This is a poor man's stub
20
+ @throttler = Object.new
21
+ def @throttler.run
22
+ # noop
23
+ end
24
+ def @throttler.stride
25
+ 1
26
+ end
27
+
28
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
29
+ :start => 1,
30
+ :limit => 10)
31
+ end
32
+
33
+ describe '#run' do
34
+
35
+ it 'detects the max id to use in the chunk using the stride and use it if it is lower than the limit' do
36
+ def @throttler.stride
37
+ 5
38
+ end
39
+
40
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 4/)).returns(7)
41
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 8 order by id limit 1 offset 4/)).returns(21)
42
+ @connection.expects(:update).with(regexp_matches(/between 1 and 7/)).returns(2)
43
+ @connection.expects(:update).with(regexp_matches(/between 8 and 10/)).returns(2)
44
+
45
+ @chunker.run
46
+ end
47
+
48
+
49
+ it 'chunks the result set according to the stride size' do
50
+ def @throttler.stride
51
+ 2
52
+ end
53
+
54
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/)).returns(2)
55
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 3 order by id limit 1 offset 1/)).returns(4)
56
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 5 order by id limit 1 offset 1/)).returns(6)
57
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 7 order by id limit 1 offset 1/)).returns(8)
58
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 9 order by id limit 1 offset 1/)).returns(10)
59
+
60
+ @connection.expects(:update).with(regexp_matches(/between 1 and 2/)).returns(2)
61
+ @connection.expects(:update).with(regexp_matches(/between 3 and 4/)).returns(2)
62
+ @connection.expects(:update).with(regexp_matches(/between 5 and 6/)).returns(2)
63
+ @connection.expects(:update).with(regexp_matches(/between 7 and 8/)).returns(2)
64
+ @connection.expects(:update).with(regexp_matches(/between 9 and 10/)).returns(2)
65
+
66
+ @chunker.run
67
+ end
68
+
69
+ it 'handles stride changes during execution' do
70
+ # roll our own stubbing
71
+ def @throttler.stride
72
+ @run_count ||= 0
73
+ @run_count = @run_count + 1
74
+ if @run_count > 1
75
+ 3
76
+ else
77
+ 2
78
+ end
79
+ end
80
+
81
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/)).returns(2)
82
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 3 order by id limit 1 offset 2/)).returns(5)
83
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 6 order by id limit 1 offset 2/)).returns(8)
84
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 9 order by id limit 1 offset 2/)).returns(nil)
85
+
86
+ @connection.expects(:update).with(regexp_matches(/between 1 and 2/)).returns(2)
87
+ @connection.expects(:update).with(regexp_matches(/between 3 and 5/)).returns(2)
88
+ @connection.expects(:update).with(regexp_matches(/between 6 and 8/)).returns(2)
89
+ @connection.expects(:update).with(regexp_matches(/between 9 and 10/)).returns(2)
90
+
91
+ @chunker.run
92
+ end
93
+
94
+ it 'correctly copies single record tables' do
95
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
96
+ :start => 1,
97
+ :limit => 1)
98
+
99
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 0/)).returns(nil)
100
+ @connection.expects(:update).with(regexp_matches(/between 1 and 1/)).returns(1)
101
+
102
+ @chunker.run
103
+ end
104
+
105
+ it 'copies the last record of a table, even it is the start of the last chunk' do
106
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
107
+ :start => 2,
108
+ :limit => 10)
109
+ def @throttler.stride
110
+ 2
111
+ end
112
+
113
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 2 order by id limit 1 offset 1/)).returns(3)
114
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 4 order by id limit 1 offset 1/)).returns(5)
115
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 6 order by id limit 1 offset 1/)).returns(7)
116
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 8 order by id limit 1 offset 1/)).returns(9)
117
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 10 order by id limit 1 offset 1/)).returns(nil)
118
+
119
+ @connection.expects(:update).with(regexp_matches(/between 2 and 3/)).returns(2)
120
+ @connection.expects(:update).with(regexp_matches(/between 4 and 5/)).returns(2)
121
+ @connection.expects(:update).with(regexp_matches(/between 6 and 7/)).returns(2)
122
+ @connection.expects(:update).with(regexp_matches(/between 8 and 9/)).returns(2)
123
+ @connection.expects(:update).with(regexp_matches(/between 10 and 10/)).returns(1)
124
+
125
+ @chunker.run
126
+ end
127
+
128
+
129
+ it 'separates filter conditions from chunking conditions' do
130
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
131
+ :start => 1,
132
+ :limit => 2)
133
+ def @throttler.stride
134
+ 2
135
+ end
136
+
137
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/)).returns(2)
138
+ @connection.expects(:update).with(regexp_matches(/where \(foo.created_at > '2013-07-10' or foo.baz = 'quux'\) and `foo`/)).returns(1)
139
+
140
+ def @migration.conditions
141
+ "where foo.created_at > '2013-07-10' or foo.baz = 'quux'"
142
+ end
143
+
144
+ @chunker.run
145
+ end
146
+
147
+ it "doesn't mess with inner join filters" do
148
+ @chunker = Lhm::Chunker.new(@migration, @connection, :throttler => @throttler,
149
+ :start => 1,
150
+ :limit => 2)
151
+
152
+ def @throttler.stride
153
+ 2
154
+ end
155
+
156
+ @connection.expects(:select_value).with(regexp_matches(/where id >= 1 order by id limit 1 offset 1/)).returns(2)
157
+ @connection.expects(:update).with(regexp_matches(/inner join bar on foo.id = bar.foo_id and/)).returns(1)
158
+
159
+ def @migration.conditions
160
+ 'inner join bar on foo.id = bar.foo_id'
161
+ end
162
+
163
+ @chunker.run
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,124 @@
1
+ # Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
2
+ # Schmidt
3
+
4
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
5
+
6
+ require 'lhm/table'
7
+ require 'lhm/migration'
8
+ require 'lhm/entangler'
9
+
10
+ describe Lhm::Entangler do
11
+ include UnitHelper
12
+
13
+ before(:each) do
14
+ @origin = Lhm::Table.new('origin')
15
+ @destination = Lhm::Table.new('destination')
16
+ @migration = Lhm::Migration.new(@origin, @destination)
17
+ @entangler = Lhm::Entangler.new(@migration)
18
+ end
19
+
20
+ describe 'activation' do
21
+ before(:each) do
22
+ @origin.columns['info'] = { :type => 'varchar(255)' }
23
+ @origin.columns['tags'] = { :type => 'varchar(255)' }
24
+
25
+ @destination.columns['info'] = { :type => 'varchar(255)' }
26
+ @destination.columns['tags'] = { :type => 'varchar(255)' }
27
+ end
28
+
29
+ it 'should create insert trigger to destination table' do
30
+ ddl = %Q{
31
+ create trigger `lhmt_ins_origin`
32
+ after insert on `origin` for each row
33
+ replace into `destination` (`info`, `tags`) /* large hadron migration */
34
+ values (`NEW`.`info`, `NEW`.`tags`)
35
+ }
36
+
37
+ @entangler.entangle.must_include strip(ddl)
38
+ end
39
+
40
+ it 'should create an update trigger to the destination table' do
41
+ ddl = %Q{
42
+ create trigger `lhmt_upd_origin`
43
+ after update on `origin` for each row
44
+ replace into `destination` (`info`, `tags`) /* large hadron migration */
45
+ values (`NEW`.`info`, `NEW`.`tags`)
46
+ }
47
+
48
+ @entangler.entangle.must_include strip(ddl)
49
+ end
50
+
51
+ it 'should create a delete trigger to the destination table' do
52
+ ddl = %Q{
53
+ create trigger `lhmt_del_origin`
54
+ after delete on `origin` for each row
55
+ delete ignore from `destination` /* large hadron migration */
56
+ where `destination`.`id` = OLD.`id`
57
+ }
58
+
59
+ @entangler.entangle.must_include strip(ddl)
60
+ end
61
+
62
+ it 'should retry trigger creation when it hits a lock wait timeout' do
63
+ connection = mock()
64
+ tries = 1
65
+ @entangler = Lhm::Entangler.new(@migration, connection, retriable: {base_interval: 0, tries: tries})
66
+ connection.expects(:execute).times(tries).raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
67
+
68
+ assert_raises(Mysql2::Error) { @entangler.before }
69
+ end
70
+
71
+ it 'should not retry trigger creation with other mysql errors' do
72
+ connection = mock()
73
+ connection.expects(:execute).once.raises(Mysql2::Error, 'The MySQL server is running with the --read-only option so it cannot execute this statement.')
74
+
75
+ @entangler = Lhm::Entangler.new(@migration, connection, retriable: {base_interval: 0})
76
+ assert_raises(Mysql2::Error) { @entangler.before }
77
+ end
78
+
79
+ it 'should succesfully finish after retrying' do
80
+ connection = mock()
81
+ connection.stubs(:execute).raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction').then.returns(true)
82
+ @entangler = Lhm::Entangler.new(@migration, connection, retriable: {base_interval: 0})
83
+
84
+ assert @entangler.before
85
+ end
86
+
87
+ it 'should retry as many times as specified by configuration' do
88
+ connection = mock()
89
+ connection.expects(:execute).times(5).raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
90
+ @entangler = Lhm::Entangler.new(@migration, connection, retriable: {tries: 5, base_interval: 0})
91
+
92
+ assert_raises(Mysql2::Error) { @entangler.before }
93
+ end
94
+
95
+ describe 'super long table names' do
96
+ before(:each) do
97
+ @origin = Lhm::Table.new('a' * 64)
98
+ @destination = Lhm::Table.new('b' * 64)
99
+ @migration = Lhm::Migration.new(@origin, @destination)
100
+ @entangler = Lhm::Entangler.new(@migration)
101
+ end
102
+
103
+ it 'should use truncated names' do
104
+ @entangler.trigger(:ins).length.must_be :<=, 64
105
+ @entangler.trigger(:upd).length.must_be :<=, 64
106
+ @entangler.trigger(:del).length.must_be :<=, 64
107
+ end
108
+ end
109
+ end
110
+
111
+ describe 'removal' do
112
+ it 'should remove insert trigger' do
113
+ @entangler.untangle.must_include('drop trigger if exists `lhmt_ins_origin`')
114
+ end
115
+
116
+ it 'should remove update trigger' do
117
+ @entangler.untangle.must_include('drop trigger if exists `lhmt_upd_origin`')
118
+ end
119
+
120
+ it 'should remove delete trigger' do
121
+ @entangler.untangle.must_include('drop trigger if exists `lhmt_del_origin`')
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,51 @@
1
+ # Copyright (c) 2011 - 2013, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
2
+ # Schmidt
3
+
4
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
5
+
6
+ require 'lhm/table'
7
+ require 'lhm/migrator'
8
+
9
+ describe Lhm::Intersection do
10
+ include UnitHelper
11
+
12
+ it 'should not have dropped changes' do
13
+ origin = Lhm::Table.new('origin')
14
+ origin.columns['dropped'] = varchar
15
+ origin.columns['retained'] = varchar
16
+
17
+ destination = Lhm::Table.new('destination')
18
+ destination.columns['retained'] = varchar
19
+
20
+ intersection = Lhm::Intersection.new(origin, destination)
21
+ intersection.destination.include?('dropped').must_equal(false)
22
+ end
23
+
24
+ it 'should have unchanged columns' do
25
+ origin = Lhm::Table.new('origin')
26
+ origin.columns['dropped'] = varchar
27
+ origin.columns['retained'] = varchar
28
+
29
+ destination = Lhm::Table.new('destination')
30
+ destination.columns['retained'] = varchar
31
+
32
+ intersection = Lhm::Intersection.new(origin, destination)
33
+ intersection.destination.must_equal(['retained'])
34
+ end
35
+
36
+ it 'should have renamed columns' do
37
+ origin = Lhm::Table.new('origin')
38
+ origin.columns['old_name'] = varchar
39
+
40
+ destination = Lhm::Table.new('destination')
41
+ destination.columns['new_name'] = varchar
42
+
43
+ intersection = Lhm::Intersection.new(origin, destination, { 'old_name' => 'new_name' })
44
+ intersection.origin.must_equal(['old_name'])
45
+ intersection.destination.must_equal(['new_name'])
46
+ end
47
+
48
+ def varchar
49
+ { :metadata => 'VARCHAR(255)' }
50
+ end
51
+ end