lhm-shopify 3.4.0 → 3.5.5

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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +24 -15
  3. data/.gitignore +1 -6
  4. data/Appraisals +24 -0
  5. data/CHANGELOG.md +30 -0
  6. data/Gemfile.lock +66 -0
  7. data/README.md +55 -4
  8. data/Rakefile +11 -0
  9. data/dev.yml +31 -6
  10. data/docker-compose.yml +58 -0
  11. data/gemfiles/activerecord_5.2.gemfile +9 -0
  12. data/gemfiles/activerecord_5.2.gemfile.lock +65 -0
  13. data/gemfiles/activerecord_6.0.gemfile +7 -0
  14. data/gemfiles/activerecord_6.0.gemfile.lock +67 -0
  15. data/gemfiles/activerecord_6.1.gemfile +7 -0
  16. data/gemfiles/activerecord_6.1.gemfile.lock +66 -0
  17. data/gemfiles/activerecord_7.0.0.alpha2.gemfile +7 -0
  18. data/gemfiles/activerecord_7.0.0.alpha2.gemfile.lock +64 -0
  19. data/lhm.gemspec +7 -3
  20. data/lib/lhm/atomic_switcher.rb +5 -11
  21. data/lib/lhm/chunk_insert.rb +7 -10
  22. data/lib/lhm/chunker.rb +21 -10
  23. data/lib/lhm/cleanup/current.rb +9 -12
  24. data/lib/lhm/connection.rb +108 -0
  25. data/lib/lhm/entangler.rb +8 -13
  26. data/lib/lhm/invoker.rb +6 -4
  27. data/lib/lhm/locked_switcher.rb +2 -0
  28. data/lib/lhm/migrator.rb +2 -0
  29. data/lib/lhm/printer.rb +10 -6
  30. data/lib/lhm/proxysql_helper.rb +10 -0
  31. data/lib/lhm/sql_retry.rb +129 -10
  32. data/lib/lhm/throttler/slave_lag.rb +19 -2
  33. data/lib/lhm/version.rb +1 -1
  34. data/lib/lhm.rb +41 -16
  35. data/scripts/helpers/wait-for-dbs.sh +21 -0
  36. data/scripts/mysql/reader/create_replication.sql +10 -0
  37. data/scripts/mysql/writer/create_test_db.sql +1 -0
  38. data/scripts/mysql/writer/create_users.sql +6 -0
  39. data/scripts/proxysql/proxysql.cnf +117 -0
  40. data/spec/integration/atomic_switcher_spec.rb +53 -17
  41. data/spec/integration/chunk_insert_spec.rb +3 -2
  42. data/spec/integration/chunker_spec.rb +18 -16
  43. data/spec/integration/cleanup_spec.rb +49 -38
  44. data/spec/integration/database.yml +25 -0
  45. data/spec/integration/entangler_spec.rb +7 -5
  46. data/spec/integration/integration_helper.rb +25 -10
  47. data/spec/integration/lhm_spec.rb +114 -40
  48. data/spec/integration/lock_wait_timeout_spec.rb +2 -2
  49. data/spec/integration/locked_switcher_spec.rb +4 -4
  50. data/spec/integration/proxysql_spec.rb +34 -0
  51. data/spec/integration/sql_retry/db_connection_helper.rb +52 -0
  52. data/spec/integration/sql_retry/lock_wait_spec.rb +8 -6
  53. data/spec/integration/sql_retry/lock_wait_timeout_test_helper.rb +17 -4
  54. data/spec/integration/sql_retry/proxysql_helper.rb +22 -0
  55. data/spec/integration/sql_retry/retry_with_proxysql_spec.rb +109 -0
  56. data/spec/integration/table_spec.rb +11 -19
  57. data/spec/integration/toxiproxy_helper.rb +40 -0
  58. data/spec/test_helper.rb +24 -0
  59. data/spec/unit/atomic_switcher_spec.rb +4 -6
  60. data/spec/unit/chunk_insert_spec.rb +7 -2
  61. data/spec/unit/chunker_spec.rb +47 -42
  62. data/spec/unit/connection_spec.rb +111 -0
  63. data/spec/unit/entangler_spec.rb +85 -22
  64. data/spec/unit/intersection_spec.rb +4 -4
  65. data/spec/unit/lhm_spec.rb +23 -6
  66. data/spec/unit/locked_switcher_spec.rb +13 -18
  67. data/spec/unit/migrator_spec.rb +17 -19
  68. data/spec/unit/printer_spec.rb +14 -26
  69. data/spec/unit/sql_helper_spec.rb +8 -12
  70. data/spec/unit/table_spec.rb +5 -5
  71. data/spec/unit/throttler/slave_lag_spec.rb +14 -9
  72. data/spec/unit/throttler_spec.rb +12 -12
  73. data/spec/unit/unit_helper.rb +13 -0
  74. metadata +85 -14
  75. data/bin/.gitkeep +0 -0
  76. data/dbdeployer/config.json +0 -32
  77. data/dbdeployer/install.sh +0 -64
  78. data/gemfiles/ar-2.3_mysql.gemfile +0 -6
  79. data/gemfiles/ar-3.2_mysql.gemfile +0 -5
  80. data/gemfiles/ar-3.2_mysql2.gemfile +0 -5
  81. data/gemfiles/ar-4.0_mysql2.gemfile +0 -5
  82. data/gemfiles/ar-4.1_mysql2.gemfile +0 -5
  83. data/gemfiles/ar-4.2_mysql2.gemfile +0 -5
  84. data/gemfiles/ar-5.0_mysql2.gemfile +0 -5
@@ -6,6 +6,7 @@ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
6
6
  require 'lhm/table'
7
7
  require 'lhm/migration'
8
8
  require 'lhm/entangler'
9
+ require 'lhm/connection'
9
10
 
10
11
  describe Lhm::Entangler do
11
12
  include UnitHelper
@@ -34,7 +35,7 @@ describe Lhm::Entangler do
34
35
  values (`NEW`.`info`, `NEW`.`tags`)
35
36
  }
36
37
 
37
- @entangler.entangle.must_include strip(ddl)
38
+ value(@entangler.entangle).must_include strip(ddl)
38
39
  end
39
40
 
40
41
  it 'should create an update trigger to the destination table' do
@@ -45,7 +46,7 @@ describe Lhm::Entangler do
45
46
  values (`NEW`.`info`, `NEW`.`tags`)
46
47
  }
47
48
 
48
- @entangler.entangle.must_include strip(ddl)
49
+ value(@entangler.entangle).must_include strip(ddl)
49
50
  end
50
51
 
51
52
  it 'should create a delete trigger to the destination table' do
@@ -56,38 +57,100 @@ describe Lhm::Entangler do
56
57
  where `destination`.`id` = OLD.`id`
57
58
  }
58
59
 
59
- @entangler.entangle.must_include strip(ddl)
60
+ value(@entangler.entangle).must_include strip(ddl)
60
61
  end
61
62
 
62
63
  it 'should retry trigger creation when it hits a lock wait timeout' do
63
- connection = mock()
64
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')
65
+ ar_connection = mock()
66
+ ar_connection.stubs(:execute)
67
+ .returns([["dummy"]], [["dummy"]], [["dummy"]])
68
+ .then
69
+ .raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
70
+ ar_connection.stubs(:active?).returns(true)
71
+
72
+ connection = Lhm::Connection.new(connection: ar_connection, options: {
73
+ reconnect_with_consistent_host: true,
74
+ retriable: {
75
+ base_interval: 0,
76
+ tries: tries
77
+ }
78
+ })
79
+
80
+ @entangler = Lhm::Entangler.new(@migration, connection)
67
81
 
68
82
  assert_raises(Mysql2::Error) { @entangler.before }
69
83
  end
70
84
 
71
85
  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})
86
+ ar_connection = mock()
87
+ ar_connection.stubs(:execute)
88
+ .returns([["dummy"]], [["dummy"]], [["dummy"]])
89
+ .then
90
+ .raises(Mysql2::Error, 'The MySQL server is running with the --read-only option so it cannot execute this statement.')
91
+ ar_connection.stubs(:active?).returns(true)
92
+ connection = Lhm::Connection.new(connection: ar_connection, options: {
93
+ reconnect_with_consistent_host: true,
94
+ retriable: {
95
+ base_interval: 0
96
+ },
97
+ })
98
+
99
+ @entangler = Lhm::Entangler.new(@migration, connection)
76
100
  assert_raises(Mysql2::Error) { @entangler.before }
77
101
  end
78
102
 
79
103
  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})
104
+ ar_connection = mock()
105
+ ar_connection.stubs(:execute)
106
+ .returns([["dummy"]], [["dummy"]], [["dummy"]])
107
+ .then
108
+ .raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
109
+ .then
110
+ .returns([["dummy"]])
111
+ ar_connection.stubs(:active?).returns(true)
112
+
113
+ connection = Lhm::Connection.new(connection: ar_connection, options: {
114
+ reconnect_with_consistent_host: true,
115
+ retriable: {
116
+ base_interval: 0
117
+ },
118
+ })
119
+
120
+ @entangler = Lhm::Entangler.new(@migration, connection)
83
121
 
84
122
  assert @entangler.before
85
123
  end
86
124
 
87
125
  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})
126
+ ar_connection = mock()
127
+ ar_connection.stubs(:execute)
128
+ .returns([["dummy"]], [["dummy"]], [["dummy"]]) # initial
129
+ .then
130
+ .raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
131
+ .then
132
+ .returns([["dummy"]]) # reconnect 1
133
+ .then
134
+ .raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
135
+ .then
136
+ .returns([["dummy"]]) # reconnect 2
137
+ .then
138
+ .raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction')
139
+ .then
140
+ .returns([["dummy"]]) # reconnect 3
141
+ .then
142
+ .raises(Mysql2::Error, 'Lock wait timeout exceeded; try restarting transaction') # final error
143
+ ar_connection.stubs(:active?).returns(true)
144
+
145
+ connection = Lhm::Connection.new(connection: ar_connection, options: {
146
+ reconnect_with_consistent_host: true,
147
+ retriable: {
148
+ tries: 5,
149
+ base_interval: 0
150
+ },
151
+ })
152
+
153
+ @entangler = Lhm::Entangler.new(@migration, connection)
91
154
 
92
155
  assert_raises(Mysql2::Error) { @entangler.before }
93
156
  end
@@ -101,24 +164,24 @@ describe Lhm::Entangler do
101
164
  end
102
165
 
103
166
  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
167
+ value(@entangler.trigger(:ins).length).must_be :<=, 64
168
+ value(@entangler.trigger(:upd).length).must_be :<=, 64
169
+ value(@entangler.trigger(:del).length).must_be :<=, 64
107
170
  end
108
171
  end
109
172
  end
110
173
 
111
174
  describe 'removal' do
112
175
  it 'should remove insert trigger' do
113
- @entangler.untangle.must_include('drop trigger if exists `lhmt_ins_origin`')
176
+ value(@entangler.untangle).must_include('drop trigger if exists `lhmt_ins_origin`')
114
177
  end
115
178
 
116
179
  it 'should remove update trigger' do
117
- @entangler.untangle.must_include('drop trigger if exists `lhmt_upd_origin`')
180
+ value(@entangler.untangle).must_include('drop trigger if exists `lhmt_upd_origin`')
118
181
  end
119
182
 
120
183
  it 'should remove delete trigger' do
121
- @entangler.untangle.must_include('drop trigger if exists `lhmt_del_origin`')
184
+ value(@entangler.untangle).must_include('drop trigger if exists `lhmt_del_origin`')
122
185
  end
123
186
  end
124
187
  end
@@ -18,7 +18,7 @@ describe Lhm::Intersection do
18
18
  destination.columns['retained'] = varchar
19
19
 
20
20
  intersection = Lhm::Intersection.new(origin, destination)
21
- intersection.destination.include?('dropped').must_equal(false)
21
+ value(intersection.destination.include?('dropped')).must_equal(false)
22
22
  end
23
23
 
24
24
  it 'should have unchanged columns' do
@@ -30,7 +30,7 @@ describe Lhm::Intersection do
30
30
  destination.columns['retained'] = varchar
31
31
 
32
32
  intersection = Lhm::Intersection.new(origin, destination)
33
- intersection.destination.must_equal(['retained'])
33
+ value(intersection.destination).must_equal(['retained'])
34
34
  end
35
35
 
36
36
  it 'should have renamed columns' do
@@ -41,8 +41,8 @@ describe Lhm::Intersection do
41
41
  destination.columns['new_name'] = varchar
42
42
 
43
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'])
44
+ value(intersection.origin).must_equal(['old_name'])
45
+ value(intersection.destination).must_equal(['new_name'])
46
46
  end
47
47
 
48
48
  def varchar
@@ -11,9 +11,9 @@ describe Lhm do
11
11
  describe 'logger' do
12
12
 
13
13
  it 'should use the default parameters if no logger explicitly set' do
14
- Lhm.logger.must_be_kind_of Logger
15
- Lhm.logger.level.must_equal Logger::INFO
16
- Lhm.logger.instance_eval { @logdev }.dev.must_equal STDOUT
14
+ value(Lhm.logger).must_be_kind_of Logger
15
+ value(Lhm.logger.level).must_equal Logger::INFO
16
+ value(Lhm.logger.instance_eval { @logdev }.dev).must_equal STDOUT
17
17
  end
18
18
 
19
19
  it 'should use s new logger if set' do
@@ -21,9 +21,26 @@ describe Lhm do
21
21
  l.level = Logger::ERROR
22
22
  Lhm.logger = l
23
23
 
24
- Lhm.logger.level.must_equal Logger::ERROR
25
- Lhm.logger.instance_eval { @logdev }.dev.must_be_kind_of File
26
- Lhm.logger.instance_eval { @logdev }.dev.path.must_equal 'omg.ponies'
24
+ value(Lhm.logger.level).must_equal Logger::ERROR
25
+ value(Lhm.logger.instance_eval { @logdev }.dev).must_be_kind_of File
26
+ value(Lhm.logger.instance_eval { @logdev }.dev.path).must_equal 'omg.ponies'
27
+ end
28
+ end
29
+
30
+ describe 'api' do
31
+
32
+ before(:each) do
33
+ @connection = mock()
34
+ end
35
+
36
+ it 'should create a new connection when calling setup' do
37
+ Lhm.setup(@connection)
38
+ value(Lhm.connection).must_be_kind_of(Lhm::Connection)
39
+ end
40
+
41
+ it 'should create a new connection when none is created' do
42
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
43
+ value(Lhm.connection).must_be_kind_of(Lhm::Connection)
27
44
  end
28
45
  end
29
46
  end
@@ -20,32 +20,27 @@ describe Lhm::LockedSwitcher do
20
20
 
21
21
  describe 'uncommitted' do
22
22
  it 'should disable autocommit first' do
23
- @switcher.
24
- statements[0..1].
25
- must_equal([
26
- 'set @lhm_auto_commit = @@session.autocommit',
27
- 'set session autocommit = 0'
28
- ])
23
+ value(@switcher.statements[0..1]).must_equal([
24
+ 'set @lhm_auto_commit = @@session.autocommit',
25
+ 'set session autocommit = 0'
26
+ ])
29
27
  end
30
28
 
31
29
  it 'should reapply original autocommit settings at the end' do
32
- @switcher.
33
- statements[-1].
34
- must_equal('set session autocommit = @lhm_auto_commit')
30
+ value(@switcher.statements[-1])
31
+ .must_equal('set session autocommit = @lhm_auto_commit')
35
32
  end
36
33
  end
37
34
 
38
35
  describe 'switch' do
39
36
  it 'should lock origin and destination table, switch, commit and unlock' do
40
- @switcher.
41
- switch.
42
- must_equal([
43
- 'lock table `origin` write, `destination` write',
44
- "alter table `origin` rename `#{ @migration.archive_name }`",
45
- 'alter table `destination` rename `origin`',
46
- 'commit',
47
- 'unlock tables'
48
- ])
37
+ value(@switcher.switch).must_equal([
38
+ 'lock table `origin` write, `destination` write',
39
+ "alter table `origin` rename `#{ @migration.archive_name }`",
40
+ 'alter table `destination` rename `origin`',
41
+ 'commit',
42
+ 'unlock tables'
43
+ ])
49
44
  end
50
45
  end
51
46
  end
@@ -18,7 +18,7 @@ describe Lhm::Migrator do
18
18
  it 'should add an index' do
19
19
  @creator.add_index(:a)
20
20
 
21
- @creator.statements.must_equal([
21
+ value(@creator.statements).must_equal([
22
22
  'create index `index_alt_on_a` on `lhmn_alt` (`a`)'
23
23
  ])
24
24
  end
@@ -26,7 +26,7 @@ describe Lhm::Migrator do
26
26
  it 'should add a composite index' do
27
27
  @creator.add_index([:a, :b])
28
28
 
29
- @creator.statements.must_equal([
29
+ value(@creator.statements).must_equal([
30
30
  'create index `index_alt_on_a_and_b` on `lhmn_alt` (`a`, `b`)'
31
31
  ])
32
32
  end
@@ -34,7 +34,7 @@ describe Lhm::Migrator do
34
34
  it 'should add an index with prefix length' do
35
35
  @creator.add_index(['a(10)', 'b'])
36
36
 
37
- @creator.statements.must_equal([
37
+ value(@creator.statements).must_equal([
38
38
  'create index `index_alt_on_a_and_b` on `lhmn_alt` (`a`(10), `b`)'
39
39
  ])
40
40
  end
@@ -42,7 +42,7 @@ describe Lhm::Migrator do
42
42
  it 'should add an index with a custom name' do
43
43
  @creator.add_index([:a, :b], :custom_index_name)
44
44
 
45
- @creator.statements.must_equal([
45
+ value(@creator.statements).must_equal([
46
46
  'create index `custom_index_name` on `lhmn_alt` (`a`, `b`)'
47
47
  ])
48
48
  end
@@ -56,7 +56,7 @@ describe Lhm::Migrator do
56
56
  it 'should add a unique index' do
57
57
  @creator.add_unique_index(['a(5)', :b])
58
58
 
59
- @creator.statements.must_equal([
59
+ value(@creator.statements).must_equal([
60
60
  'create unique index `index_alt_on_a_and_b` on `lhmn_alt` (`a`(5), `b`)'
61
61
  ])
62
62
  end
@@ -64,7 +64,7 @@ describe Lhm::Migrator do
64
64
  it 'should add a unique index with a custom name' do
65
65
  @creator.add_unique_index([:a, :b], :custom_index_name)
66
66
 
67
- @creator.statements.must_equal([
67
+ value(@creator.statements).must_equal([
68
68
  'create unique index `custom_index_name` on `lhmn_alt` (`a`, `b`)'
69
69
  ])
70
70
  end
@@ -78,7 +78,7 @@ describe Lhm::Migrator do
78
78
  it 'should remove an index' do
79
79
  @creator.remove_index(['b', 'a'])
80
80
 
81
- @creator.statements.must_equal([
81
+ value(@creator.statements).must_equal([
82
82
  'drop index `index_alt_on_b_and_a` on `lhmn_alt`'
83
83
  ])
84
84
  end
@@ -86,7 +86,7 @@ describe Lhm::Migrator do
86
86
  it 'should remove an index with a custom name' do
87
87
  @creator.remove_index([:a, :b], :custom_index_name)
88
88
 
89
- @creator.statements.must_equal([
89
+ value(@creator.statements).must_equal([
90
90
  'drop index `custom_index_name` on `lhmn_alt`'
91
91
  ])
92
92
  end
@@ -96,7 +96,7 @@ describe Lhm::Migrator do
96
96
  it 'should add a column' do
97
97
  @creator.add_column('logins', 'INT(12)')
98
98
 
99
- @creator.statements.must_equal([
99
+ value(@creator.statements).must_equal([
100
100
  'alter table `lhmn_alt` add column `logins` INT(12)'
101
101
  ])
102
102
  end
@@ -104,7 +104,7 @@ describe Lhm::Migrator do
104
104
  it 'should remove a column' do
105
105
  @creator.remove_column('logins')
106
106
 
107
- @creator.statements.must_equal([
107
+ value(@creator.statements).must_equal([
108
108
  'alter table `lhmn_alt` drop `logins`'
109
109
  ])
110
110
  end
@@ -112,7 +112,7 @@ describe Lhm::Migrator do
112
112
  it 'should change a column' do
113
113
  @creator.change_column('logins', 'INT(11)')
114
114
 
115
- @creator.statements.must_equal([
115
+ value(@creator.statements).must_equal([
116
116
  'alter table `lhmn_alt` modify column `logins` INT(11)'
117
117
  ])
118
118
  end
@@ -122,7 +122,7 @@ describe Lhm::Migrator do
122
122
  it 'should accept a ddl statement' do
123
123
  @creator.ddl('alter table `%s` add column `f` tinyint(1)' % @creator.name)
124
124
 
125
- @creator.statements.must_equal([
125
+ value(@creator.statements).must_equal([
126
126
  'alter table `lhmn_alt` add column `f` tinyint(1)'
127
127
  ])
128
128
  end
@@ -132,15 +132,13 @@ describe Lhm::Migrator do
132
132
  it 'should add two columns' do
133
133
  @creator.add_column('first', 'VARCHAR(64)')
134
134
  @creator.add_column('last', 'VARCHAR(64)')
135
- @creator.statements.length.must_equal(2)
135
+ value(@creator.statements.length).must_equal(2)
136
136
 
137
- @creator.
138
- statements[0].
139
- must_equal('alter table `lhmn_alt` add column `first` VARCHAR(64)')
137
+ value(@creator.statements[0])
138
+ .must_equal('alter table `lhmn_alt` add column `first` VARCHAR(64)')
140
139
 
141
- @creator.
142
- statements[1].
143
- must_equal('alter table `lhmn_alt` add column `last` VARCHAR(64)')
140
+ value(@creator.statements[1])
141
+ .must_equal('alter table `lhmn_alt` add column `last` VARCHAR(64)')
144
142
  end
145
143
  end
146
144
  end
@@ -1,6 +1,9 @@
1
1
  require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
2
2
 
3
3
  require 'lhm/printer'
4
+ require 'logger'
5
+
6
+
4
7
 
5
8
  describe Lhm::Printer do
6
9
  include UnitHelper
@@ -12,24 +15,17 @@ describe Lhm::Printer do
12
15
  end
13
16
 
14
17
  it 'prints the percentage' do
15
- mock = MiniTest::Mock.new
18
+ r, w = IO.pipe
19
+ Lhm.logger = Logger.new(w)
20
+
16
21
  10.times do |i|
17
- mock.expect(:write, :return_value) do |message|
18
- message = message.first if message.is_a?(Array)
19
- assert_match(/^\r/, message)
20
- assert_match(/#{i}\/10/, message)
21
- end
22
+ @printer.notify(i, 10)
23
+ assert_match(/#{i}\/10/, log_expression_message(r.gets))
22
24
  end
23
-
24
- @printer.instance_variable_set(:@output, mock)
25
- 10.times { |i| @printer.notify(i, 10) }
26
- mock.verify
27
25
  end
28
26
 
29
27
  it 'always prints a bigger message' do
30
28
  @length = 0
31
- printer_mock = mock()
32
- printer_mock.expects(:write).at_least_once
33
29
 
34
30
  def assert_length(printer)
35
31
  new_length = printer.instance_variable_get(:@max_length)
@@ -37,7 +33,6 @@ describe Lhm::Printer do
37
33
  @length = new_length
38
34
  end
39
35
 
40
- @printer.instance_variable_set(:@output, printer_mock)
41
36
  @printer.notify(10, 100)
42
37
  assert_length(@printer)
43
38
  @printer.notify(0, 100)
@@ -51,27 +46,20 @@ describe Lhm::Printer do
51
46
  end
52
47
 
53
48
  it 'prints the end message' do
54
- mock = MiniTest::Mock.new
55
- mock.expect(:write, :return_value, [String])
56
- mock.expect(:write, :return_value, ["\n"])
57
-
58
- @printer.instance_variable_set(:@output, mock)
49
+ r, w = IO.pipe
50
+ Lhm.logger = Logger.new(w)
59
51
  @printer.end
60
52
 
61
- mock.verify
53
+ assert_equal(log_expression_message(r.gets), "100% complete\n")
62
54
  end
63
55
 
64
56
  it 'prints the exception message' do
65
- mock = MiniTest::Mock.new
66
- mock.expect(:write, :return_value, ["\rfailed: woops"])
67
- mock.expect(:write, :return_value, ["\n"])
68
-
57
+ r, w = IO.pipe
58
+ Lhm.logger = Logger.new(w)
69
59
  e = StandardError.new('woops')
70
-
71
- @printer.instance_variable_set(:@output, mock)
72
60
  @printer.exception(e)
73
61
 
74
- mock.verify
62
+ assert_equal(log_expression_message(r.gets), "failed: #{e}\n")
75
63
  end
76
64
  end
77
65
 
@@ -7,26 +7,22 @@ require 'lhm/sql_helper'
7
7
 
8
8
  describe Lhm::SqlHelper do
9
9
  it 'should name index with a single column' do
10
- Lhm::SqlHelper.
11
- idx_name(:users, :name).
12
- must_equal('index_users_on_name')
10
+ value(Lhm::SqlHelper.idx_name(:users, :name))
11
+ .must_equal('index_users_on_name')
13
12
  end
14
13
 
15
14
  it 'should name index with multiple columns' do
16
- Lhm::SqlHelper.
17
- idx_name(:users, [:name, :firstname]).
18
- must_equal('index_users_on_name_and_firstname')
15
+ value(Lhm::SqlHelper.idx_name(:users, [:name, :firstname]))
16
+ .must_equal('index_users_on_name_and_firstname')
19
17
  end
20
18
 
21
19
  it 'should name index with prefixed column' do
22
- Lhm::SqlHelper.
23
- idx_name(:tracks, ['title(10)', 'album']).
24
- must_equal('index_tracks_on_title_and_album')
20
+ value(Lhm::SqlHelper.idx_name(:tracks, ['title(10)', 'album']))
21
+ .must_equal('index_tracks_on_title_and_album')
25
22
  end
26
23
 
27
24
  it 'should quote column names in index specification' do
28
- Lhm::SqlHelper.
29
- idx_spec(['title(10)', 'album']).
30
- must_equal('`title`(10), `album`')
25
+ value(Lhm::SqlHelper.idx_spec(['title(10)', 'album']))
26
+ .must_equal('`title`(10), `album`')
31
27
  end
32
28
  end
@@ -11,7 +11,7 @@ describe Lhm::Table do
11
11
  describe 'names' do
12
12
  it 'should name destination' do
13
13
  @table = Lhm::Table.new('users')
14
- @table.destination_name.must_equal 'lhmn_users'
14
+ value(@table.destination_name).must_equal 'lhmn_users'
15
15
  end
16
16
  end
17
17
 
@@ -23,25 +23,25 @@ describe Lhm::Table do
23
23
  it 'should be satisfied with a single column primary key called id' do
24
24
  @table = Lhm::Table.new('table', 'id')
25
25
  set_columns(@table, { 'id' => { :type => 'int(1)' } })
26
- @table.satisfies_id_column_requirement?.must_equal true
26
+ value(@table.satisfies_id_column_requirement?).must_equal true
27
27
  end
28
28
 
29
29
  it 'should be satisfied with a primary key not called id, as long as there is still an id' do
30
30
  @table = Lhm::Table.new('table', 'uuid')
31
31
  set_columns(@table, { 'id' => { :type => 'int(1)' } })
32
- @table.satisfies_id_column_requirement?.must_equal true
32
+ value(@table.satisfies_id_column_requirement?).must_equal true
33
33
  end
34
34
 
35
35
  it 'should be satisifed if display attributes are not present (deprecated in mysql 8)' do
36
36
  @table = Lhm::Table.new('table', 'id')
37
37
  set_columns(@table, { 'id' => { :type => 'int' } })
38
- @table.satisfies_id_column_requirement?.must_equal true
38
+ value(@table.satisfies_id_column_requirement?).must_equal true
39
39
  end
40
40
 
41
41
  it 'should not be satisfied if id is not numeric' do
42
42
  @table = Lhm::Table.new('table', 'id')
43
43
  set_columns(@table, { 'id' => { :type => 'varchar(255)' } })
44
- @table.satisfies_id_column_requirement?.must_equal false
44
+ value(@table.satisfies_id_column_requirement?).must_equal false
45
45
  end
46
46
  end
47
47
  end
@@ -39,7 +39,7 @@ describe Lhm::Throttler::Slave do
39
39
  @logs = StringIO.new
40
40
  Lhm.logger = Logger.new(@logs)
41
41
 
42
- @dummy_mysql_client_config = lambda { {'username' => 'user', 'password' => 'pw', 'database' => 'db'} }
42
+ @dummy_mysql_client_config = lambda { { 'username' => 'user', 'password' => 'pw', 'database' => 'db' } }
43
43
  end
44
44
 
45
45
  describe "#client" do
@@ -64,7 +64,7 @@ describe Lhm::Throttler::Slave do
64
64
 
65
65
  describe 'with proper config' do
66
66
  it "creates a new Mysql2::Client" do
67
- expected_config = {username: 'user', password: 'pw', database: 'db', host: 'slave'}
67
+ expected_config = { username: 'user', password: 'pw', database: 'db', host: 'slave' }
68
68
  Mysql2::Client.stubs(:new).with(expected_config).returns(mock())
69
69
 
70
70
  assert Lhm::Throttler::Slave.new('slave', @dummy_mysql_client_config).connection
@@ -73,8 +73,12 @@ describe Lhm::Throttler::Slave do
73
73
 
74
74
  describe 'with active record config' do
75
75
  it 'logs and creates client' do
76
- active_record_config = {username: 'user', password: 'pw', database: 'db'}
77
- ActiveRecord::Base.stubs(:connection_pool).returns(stub(spec: stub(config: active_record_config)))
76
+ active_record_config = { username: 'user', password: 'pw', database: 'db' }
77
+ if ActiveRecord::VERSION::MAJOR > 6 || ActiveRecord::VERSION::MAJOR == 6 && ActiveRecord::VERSION::MINOR >= 1
78
+ ActiveRecord::Base.stubs(:connection_pool).returns(stub(db_config: stub(configuration_hash: active_record_config)))
79
+ else
80
+ ActiveRecord::Base.stubs(:connection_pool).returns(stub(spec: stub(config: active_record_config)))
81
+ end
78
82
 
79
83
  Mysql2::Client.stubs(:new).returns(mock())
80
84
 
@@ -92,9 +96,9 @@ describe Lhm::Throttler::Slave do
92
96
  class Connection
93
97
  def self.query(query)
94
98
  if query == Lhm::Throttler::Slave::SQL_SELECT_MAX_SLAVE_LAG
95
- [{'Seconds_Behind_Master' => 20}]
99
+ [{ 'Seconds_Behind_Master' => 20 }]
96
100
  elsif query == Lhm::Throttler::Slave::SQL_SELECT_SLAVE_HOSTS
97
- [{'host' => '1.1.1.1:80'}]
101
+ [{ 'host' => '1.1.1.1:80' }]
98
102
  end
99
103
  end
100
104
  end
@@ -104,7 +108,7 @@ describe Lhm::Throttler::Slave do
104
108
 
105
109
  class StoppedConnection
106
110
  def self.query(query)
107
- [{'Seconds_Behind_Master' => nil}]
111
+ [{ 'Seconds_Behind_Master' => nil }]
108
112
  end
109
113
  end
110
114
 
@@ -138,7 +142,7 @@ describe Lhm::Throttler::Slave do
138
142
  Lhm::Throttler::Slave.any_instance.stubs(:config).returns([])
139
143
 
140
144
  slave = Lhm::Throttler::Slave.new('slave', @dummy_mysql_client_config)
141
- assert_send([Lhm.logger, :info, "Unable to connect and/or query slave: error"])
145
+ Logger.any_instance.expects(:info).with("Unable to connect and/or query slave: Can't connect to MySQL server")
142
146
  assert_equal(0, slave.lag)
143
147
  end
144
148
  end
@@ -286,7 +290,7 @@ describe Lhm::Throttler::SlaveLag do
286
290
  describe 'with the :check_only option' do
287
291
  describe 'with a callable argument' do
288
292
  before do
289
- check_only = lambda {{'host' => '1.1.1.3'}}
293
+ check_only = lambda { { 'host' => '1.1.1.3' } }
290
294
  @throttler = Lhm::Throttler::SlaveLag.new :check_only => check_only
291
295
  end
292
296
 
@@ -300,6 +304,7 @@ describe Lhm::Throttler::SlaveLag do
300
304
  describe 'with a non-callable argument' do
301
305
  before do
302
306
  @throttler = Lhm::Throttler::SlaveLag.new :check_only => 'I cannot be called'
307
+
303
308
  def @throttler.master_slave_hosts
304
309
  ['1.1.1.1', '1.1.1.4']
305
310
  end