flydata 0.0.5.6 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,53 @@
1
+ require 'base64'
2
+
3
+ module Flydata
4
+ module Util
5
+ class Encryptor
6
+ SALT = 'ae8k9h10'
7
+
8
+ class << self
9
+ def encrypt(text, key)
10
+ validate_presence(text: text, key: key)
11
+ cipher = build_cipher(:encrypt, key)
12
+ Base64.encode64(cipher.update(text) + cipher.final)
13
+ end
14
+
15
+ def decrypt(text, key, param_name = nil)
16
+ decrypt!(text, key, param_name)
17
+ rescue => e
18
+ text
19
+ end
20
+
21
+ def decrypt!(text, key, param_name = nil)
22
+ validate_presence(text: text, key: key)
23
+ cipher = build_cipher(:decrypt, key)
24
+ cipher.update(Base64.decode64(text)) + cipher.final
25
+ rescue OpenSSL::Cipher::CipherError => e
26
+ raise "Failed to decrypt '#{param_name}' parameter. error:'#{e.to_s}'"
27
+ end
28
+
29
+ private
30
+
31
+ def validate_presence(names)
32
+ names.each do |name, value|
33
+ raise ArgumentError.new("#{name} is required.") if value.nil? or value.empty?
34
+ end
35
+ end
36
+
37
+ def build_cipher(type, key)
38
+ cipher = OpenSSL::Cipher::Cipher.new('AES-128-CBC')
39
+ case type
40
+ when :encrypt
41
+ cipher.encrypt
42
+ when :decrypt
43
+ cipher.decrypt
44
+ else
45
+ raise "Invalid type: #{type}"
46
+ end
47
+ cipher.pkcs5_keyivgen(key, SALT)
48
+ cipher
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,19 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib', 'flydata', 'fluent-plugins'))
2
+ require 'rspec'
3
+ require 'fluent/test'
4
+
5
+ module Fluent
6
+ module Test
7
+ def self.configure_plugin(plugin, str)
8
+ plugin = plugin.new if plugin.kind_of?(Class)
9
+ if str.is_a?(Fluent::Config::Element)
10
+ plugin.instance_variable_set(:@config, str)
11
+ else
12
+ plugin.instance_variable_set(:@config,
13
+ Config.parse(str, "(test)"))
14
+ end
15
+ plugin.configure(plugin.instance_variable_get(:@config))
16
+ self
17
+ end
18
+ end
19
+ end
@@ -3,35 +3,9 @@ require 'spec_helper'
3
3
  module Flydata
4
4
  module Command
5
5
  describe Sender do
6
- before :each do
7
- @sender = Sender.new
8
- end
9
- describe '#start' do
10
- it 'starts fluentd' do
11
- api_client = double('api_client')
12
- api_client.stub_chain(:data_port, :get, :[]).and_return 'active'
13
- ApiClient.stub(:new).and_return api_client
14
-
15
- @sender.start
16
- end
17
- end
18
- describe '#stop' do
19
- it 'stops fluentd' do
20
- @sender.stop
21
- end
22
- end
23
- describe '#restart' do
24
- before :all do
25
- @sender = Sender.new
26
- @sender.start
27
- end
28
- it 'restart fluentd' do
29
- @sender.restart
30
- end
31
- after :all do
32
- @sender.stop
33
- end
34
- end
6
+ pending '#start'
7
+ pending '#stop'
8
+ pending '#restart'
35
9
  end
36
10
  end
37
11
  end
@@ -0,0 +1,1049 @@
1
+ # coding: utf-8
2
+ require 'spec_helper'
3
+ require 'socket'
4
+
5
+ module Flydata
6
+ module Command
7
+ describe Sync do
8
+ end
9
+ end
10
+
11
+ module FileUtil
12
+ describe SyncFileManager do
13
+ let(:default_mysqldump_dir) do
14
+ File.join('/tmp', "sync_dump_#{Time.now.to_i}")
15
+ end
16
+ let(:default_data_entry) do
17
+ {"id"=>93,
18
+ "name"=>"flydata_sync_mysql",
19
+ "data_port_id"=>52,
20
+ "display_name"=>"flydata_sync_mysql",
21
+ "enabled"=>true,
22
+ "heroku_log_type"=>nil,
23
+ "heroku_resource_id"=>nil,
24
+ "log_deletion"=>nil,
25
+ "log_file_delimiter"=>nil,
26
+ "log_file_type"=>nil,
27
+ "log_path"=>nil,
28
+ "redshift_schema_name"=>"",
29
+ "redshift_table_name"=>nil,
30
+ "created_at"=>"2014-01-22T18:58:43Z",
31
+ "updated_at"=>"2014-01-30T02:42:26Z",
32
+ "type"=>"RedshiftMysqlDataEntry",
33
+ "tag_name"=>"flydata.a458c641_dp52.flydata_mysql",
34
+ "tag_name_dev"=>"flydata.a458c641_dp52.flydata_mysql.dev",
35
+ "data_port_key"=>"a458c641",
36
+ "mysql_data_entry_preference" =>
37
+ { "host"=>"localhost", "port"=>3306, "username"=>"masashi",
38
+ "password"=>"welcome", "database"=>"sync_test", "tables"=>nil,
39
+ "mysqldump_dir"=>default_mysqldump_dir, "forwarder" => "tcpforwarder",
40
+ "data_servers"=>"localhost:9905" }
41
+ }
42
+ end
43
+ let(:default_sfm) { SyncFileManager.new(default_data_entry) }
44
+
45
+ let (:status) { 'PARSING' }
46
+ let (:table_name) { 'test_table' }
47
+ let (:last_pos) { 9999 }
48
+ let (:binlog_pos) { {binfile: 'mysqlbin.00001', pos: 111} }
49
+ let (:state) { 'CREATE_TABLE' }
50
+ let (:substate) { nil }
51
+
52
+ after :each do
53
+ if Dir.exists?(default_mysqldump_dir)
54
+ Dir.delete(default_mysqldump_dir) rescue nil
55
+ end
56
+ if File.exists?(default_mysqldump_dir)
57
+ File.delete(default_mysqldump_dir) rescue nil
58
+ end
59
+ end
60
+
61
+ describe '#dump_file_path' do
62
+ context 'when mysqldump_dir param is nil' do
63
+ before do
64
+ default_data_entry['mysql_data_entry_preference'].delete('mysqldump_dir')
65
+ end
66
+ it do
67
+ expect(default_sfm.dump_file_path).to eq(
68
+ File.join(ENV['HOME'], '.flydata', 'dump', 'flydata_sync_mysql.dump'))
69
+ end
70
+ end
71
+ context 'when file exists' do
72
+ before { `touch #{default_mysqldump_dir}`}
73
+ it do
74
+ expect{default_sfm.dump_file_path}.to raise_error
75
+ end
76
+ end
77
+ context 'when directory exists' do
78
+ before { `mkdir -p #{default_mysqldump_dir}`}
79
+ it do
80
+ expect(FileUtils).to receive(:mkdir_p).with(default_mysqldump_dir).never
81
+ expect(default_sfm.dump_file_path).to eq(
82
+ File.join(default_mysqldump_dir, 'flydata_sync_mysql.dump'))
83
+ end
84
+ end
85
+ context 'when directory or file does not exist' do
86
+ it do
87
+ expect(FileUtils).to receive(:mkdir_p).with(default_mysqldump_dir).once
88
+ expect(default_sfm.dump_file_path).to eq(
89
+ File.join(default_mysqldump_dir, 'flydata_sync_mysql.dump'))
90
+ end
91
+ end
92
+ context 'when file name includes "~"' do
93
+ let(:default_mysqldump_dir) { "~/tmp/dump/sync_spec_#{Time.now.to_i}" }
94
+ it do
95
+ expected_dir = File.join(ENV['HOME'], default_mysqldump_dir[1..-1])
96
+ expect(FileUtils).to receive(:mkdir_p).with(expected_dir).once
97
+ expect(default_sfm.dump_file_path).to eq(
98
+ File.join(expected_dir, 'flydata_sync_mysql.dump'))
99
+ end
100
+ end
101
+ end
102
+
103
+ describe '#dump_pos_path' do
104
+ it { expect(default_sfm.dump_pos_path).to eq(
105
+ File.join(default_mysqldump_dir, 'flydata_sync_mysql.dump.pos')) }
106
+ end
107
+
108
+ describe '#save_dump_pos' do
109
+ context 'without mysql marshal data' do
110
+ it do
111
+ expect{default_sfm.save_dump_pos(
112
+ status, table_name, last_pos, binlog_pos, state, substate)}.not_to raise_error
113
+ expect(default_sfm.load_dump_pos).to eq({
114
+ status: status, table_name: table_name, last_pos: last_pos,
115
+ binlog_pos: binlog_pos, state: state, substate: substate,
116
+ mysql_table: nil
117
+ })
118
+ end
119
+ end
120
+ end
121
+
122
+ describe '#load_dump_pos' do
123
+ let (:mysql_table) do
124
+ Flydata::Mysql::MysqlTable.new(
125
+ table_name, { 'id' => { format_type: 'int' }, 'value' => { format_type: 'text' } }
126
+ )
127
+ end
128
+
129
+ context 'with mysql marshal data' do
130
+ before do
131
+ default_sfm.save_mysql_table_marshal_dump(mysql_table)
132
+ default_sfm.save_dump_pos(status, table_name, last_pos, binlog_pos, state, substate)
133
+ end
134
+ it do
135
+ ret = default_sfm.load_dump_pos
136
+ mt = ret.delete(:mysql_table)
137
+ expect(ret).to eq({
138
+ status: status, table_name: table_name, last_pos: last_pos,
139
+ binlog_pos: binlog_pos, state: state, substate: substate,
140
+ })
141
+ expect(mt.table_name).to eq(table_name)
142
+ expect(mt.columns).to eq({
143
+ 'id' => { format_type: 'int' },
144
+ 'value' => { format_type: 'text' },
145
+ })
146
+ end
147
+ end
148
+ end
149
+
150
+ describe '#save_binlog' do
151
+ let(:binfile) { 'mysqlbinlog.000001' }
152
+ let(:pos) { 107 }
153
+ let(:binlog_pos) { {binfile: binfile, pos: pos} }
154
+ it do
155
+ default_sfm.save_binlog(binlog_pos)
156
+ expect(`cat #{default_sfm.binlog_path}`).to eq("#{binfile}\t#{pos}")
157
+ end
158
+ end
159
+
160
+ describe '#binlog_path' do
161
+ it { expect(default_sfm.binlog_path).to eq("#{FLYDATA_HOME}/flydata_sync_mysql.binlog.pos") }
162
+ end
163
+ end
164
+ end
165
+
166
+ module Output
167
+ describe ForwarderFactory do
168
+ let(:forwarder) do
169
+ ForwarderFactory.create('tcpforwarder', 'test_tag', ['localhost:3456', 'localhost:4567'])
170
+ end
171
+ before :each do
172
+ conn = double(TCPSocket)
173
+ allow(conn).to receive(:setsockopt)
174
+ allow(conn).to receive(:write)
175
+ allow(conn).to receive(:close)
176
+ allow(TCPSocket).to receive(:new).and_return(conn)
177
+ allow(StringIO).to receive(:open)
178
+ end
179
+
180
+ describe '.create' do
181
+ context 'with nil forwarder_key' do
182
+ it 'should return TcpForwarder object' do
183
+ forwarder = ForwarderFactory.create(nil, 'test_tag', ['localhost:3456', 'localhost:4567'])
184
+ expect(forwarder.kind_of?(TcpForwarder)).to be_true
185
+ end
186
+ end
187
+
188
+ context 'with tcpforwarder forwarder_key' do
189
+ it 'should return TcpForwarder object' do
190
+ forwarder = ForwarderFactory.create('tcpforwarder', 'test_tag', ['localhost:3456', 'localhost:4567'])
191
+ expect(forwarder.kind_of?(TcpForwarder)).to be_true
192
+ end
193
+ end
194
+
195
+ context 'with sslforwarder forwarder_key' do
196
+ it 'should return SslForwarder object' do
197
+ forwarder = ForwarderFactory.create('sslforwarder', 'test_tag', ['localhost:3456', 'localhost:4567'])
198
+ expect(forwarder.kind_of?(SslForwarder)).to be_true
199
+ end
200
+ end
201
+ end
202
+
203
+ describe '#initialize' do
204
+ context 'servers is empty' do
205
+ it do
206
+ expect{ForwarderFactory.create('tcpforwarder', 'test_tag', [])}.to raise_error
207
+ end
208
+ end
209
+ context 'servers is nil' do
210
+ it do
211
+ expect{ForwarderFactory.create('tcpforwarder', 'test_tag', nil)}.to raise_error
212
+ end
213
+ end
214
+ end
215
+
216
+ describe '#emit' do
217
+ let(:record) { {table_name: 'test_table_name', log: '{"key":"value"}'} }
218
+ before :each do
219
+ forwarder.set_options({buffer_size_limit: ([Time.now.to_i,record].to_msgpack.bytesize * 2.5)})
220
+ end
221
+ context 'when the buffer size is less than threthold' do
222
+ it do
223
+ expect(forwarder.emit(record)).to be(false)
224
+ expect(forwarder.buffer_record_count).to be(1)
225
+ end
226
+ end
227
+ context 'when the buffer size exceeds threthold' do
228
+ it do
229
+ expect(forwarder.emit(record)).to be(false)
230
+ expect(forwarder.emit(record)).to be(false)
231
+ expect(forwarder.buffer_record_count).to be(2)
232
+ expect(forwarder.emit(record)).to be(true)
233
+ expect(forwarder.buffer_record_count).to be(0)
234
+ end
235
+ end
236
+ end
237
+
238
+ describe '#pickup_server' do
239
+ context 'with only one server' do
240
+ let(:servers) { ['localhost:1111'] }
241
+ let(:forwarder) {
242
+ ForwarderFactory.create('tcpforwarder', 'test_tag', servers)
243
+ }
244
+ it 'expect to return same server' do
245
+ expect(forwarder.pickup_server).to eq('localhost:1111')
246
+ expect(forwarder.pickup_server).to eq('localhost:1111')
247
+ expect(forwarder.pickup_server).to eq('localhost:1111')
248
+ end
249
+ end
250
+ context 'with servers' do
251
+ let(:servers) { ['localhost:1111', 'localhost:2222', 'localhost:3333'] }
252
+ let(:forwarder) {
253
+ ForwarderFactory.create('tcpforwarder', 'test_tag', servers)
254
+ }
255
+ it 'expect to return same server' do
256
+ expect(forwarder.pickup_server).to eq('localhost:1111')
257
+ expect(forwarder.pickup_server).to eq('localhost:2222')
258
+ expect(forwarder.pickup_server).to eq('localhost:3333')
259
+ expect(forwarder.pickup_server).to eq('localhost:1111')
260
+ end
261
+ end
262
+ end
263
+ end
264
+ end
265
+
266
+ module Mysql
267
+ describe MysqlDumpGenerator do
268
+ let(:stdout) { double(:stdout) }
269
+ let(:stderr) { double(:stderr) }
270
+ let(:status) { double(:status) }
271
+ let(:file_path) { File.join('/tmp', "flydata_sync_spec_mysqldump_#{Time.now.to_i}") }
272
+ let(:default_conf) { {
273
+ 'host' => 'localhost', 'port' => 3306, 'username' => 'admin',
274
+ 'password' => 'pass', 'database' => 'dev', 'tables' => 'users,groups',
275
+ } }
276
+ let(:default_dump_generator) { MysqlDumpGenerator.new(default_conf) }
277
+
278
+ describe '#initialize' do
279
+ context 'with password' do
280
+ subject { default_dump_generator.instance_variable_get(:@dump_cmd) }
281
+ it { should eq('mysqldump -h localhost -P 3306 -uadmin -ppass --skip-lock-tables ' +
282
+ '--single-transaction --flush-logs --hex-blob --master-data=2 dev users groups') }
283
+ end
284
+ context 'without password' do
285
+ let (:dump_generator) do
286
+ MysqlDumpGenerator.new(default_conf.merge({'password' => ''}))
287
+ end
288
+ subject { dump_generator.instance_variable_get(:@dump_cmd) }
289
+ it { should eq('mysqldump -h localhost -P 3306 -uadmin --skip-lock-tables ' +
290
+ '--single-transaction --flush-logs --hex-blob --master-data=2 dev users groups') }
291
+ end
292
+ end
293
+
294
+ describe '#dump' do
295
+ context 'when exit status is not 0' do
296
+ before do
297
+ `touch #{file_path}`
298
+ expect(status).to receive(:exitstatus).and_return 1
299
+ expect(Open3).to receive(:capture3).and_return(
300
+ ['(dummy std out)', '(dummy std err)', status]
301
+ )
302
+ end
303
+ it do
304
+ expect{ default_dump_generator.dump(file_path) }.to raise_error
305
+ expect(File.exists?(file_path)).to be_false
306
+ end
307
+ end
308
+ context 'when exit status is 0 but no file' do
309
+ before do
310
+ expect(status).to receive(:exitstatus).and_return 0
311
+ expect(Open3).to receive(:capture3).and_return(
312
+ ['(dummy std out)', '(dummy std err)', status]
313
+ )
314
+ end
315
+ it do
316
+ expect{ default_dump_generator.dump(file_path) }.to raise_error
317
+ expect(File.exists?(file_path)).to be_false
318
+ end
319
+ end
320
+ context 'when exit status is 0 but file size is 0' do
321
+ before do
322
+ `touch #{file_path}`
323
+ expect(status).to receive(:exitstatus).and_return 0
324
+ expect(Open3).to receive(:capture3).and_return(
325
+ ['(dummy std out)', '(dummy std err)', status]
326
+ )
327
+ end
328
+ it do
329
+ expect{ default_dump_generator.dump(file_path) }.to raise_error
330
+ expect(File.exists?(file_path)).to be_true
331
+ end
332
+ end
333
+ context 'when exit status is 0' do
334
+ before do
335
+ `echo "something..." > #{file_path}`
336
+ expect(status).to receive(:exitstatus).and_return 0
337
+ expect(Open3).to receive(:capture3).and_return(
338
+ ['(dummy std out)', '(dummy std err)', status]
339
+ )
340
+ end
341
+ it do
342
+ expect(default_dump_generator.dump(file_path)).to be_true
343
+ expect(File.exists?(file_path)).to be_true
344
+ end
345
+ end
346
+ after :each do
347
+ File.delete(file_path) if File.exists?(file_path)
348
+ end
349
+ end
350
+ end
351
+
352
+ describe MysqlDumpParser do
353
+ let(:file_path) { File.join('/tmp', "flydata_sync_spec_mysqldump_parse_#{Time.now.to_i}") }
354
+ let(:default_parser) { MysqlDumpParser.new(file_path) }
355
+
356
+ def generate_dump_file(content)
357
+ File.open(file_path, 'w') {|f| f.write(content)}
358
+ end
359
+
360
+ after do
361
+ File.delete(file_path) if File.exists?(file_path)
362
+ end
363
+
364
+ describe '#initialize' do
365
+ context 'when file does not exist' do
366
+ it do
367
+ expect{ MysqlDumpParser.new(file_path) }.to raise_error
368
+ end
369
+ end
370
+ context 'when file exists' do
371
+ before { generate_dump_file('') }
372
+ it do
373
+ expect(MysqlDumpParser.new(file_path)).to be_an_instance_of(MysqlDumpParser)
374
+ end
375
+ end
376
+ end
377
+
378
+ describe '#parse' do
379
+ DUMP_HEADER = <<EOT
380
+ -- MySQL dump 10.13 Distrib 5.6.13, for osx10.9 (x86_64)
381
+ --
382
+ -- Host: localhost Database: sync_test
383
+ -- ------------------------------------------------------
384
+ -- Server version>--5.6.13-log
385
+
386
+ /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
387
+ /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
388
+ /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
389
+ /*!40101 SET NAMES utf8 */;
390
+ /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
391
+ /*!40103 SET TIME_ZONE='+00:00' */;
392
+ /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
393
+ /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
394
+ /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
395
+ /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
396
+
397
+ --
398
+ -- Position to start replication or point-in-time recovery from
399
+ --
400
+
401
+ -- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000267', MASTER_LOG_POS=120;
402
+ EOT
403
+
404
+ def index_after(content, string)
405
+ content.index(string) + string.bytesize + 1
406
+ end
407
+
408
+ let(:default_parser) { MysqlDumpParser.new(file_path) }
409
+ let(:default_binlog_pos) { {binfile: 'mysql-bin.000267', pos: 120 } }
410
+ let(:dump_pos_after_binlog_pos) { index_after(DUMP_HEADER, 'MASTER_LOG_POS=120;') }
411
+
412
+ let(:create_table_block) { double('create_table_block') }
413
+ let(:insert_record_block) { double('insert_record_block') }
414
+ let(:check_point_block) { double('check_point_block') }
415
+
416
+ before do
417
+ generate_dump_file('')
418
+ end
419
+
420
+ context 'when dump does not contain binlog pos' do
421
+ before { generate_dump_file('dummy content') }
422
+ it do
423
+ expect(create_table_block).to receive(:call).never
424
+ expect(insert_record_block).to receive(:call).never
425
+ expect(check_point_block).to receive(:call).never
426
+ binlog_pos = default_parser.parse(
427
+ create_table_block, insert_record_block, check_point_block
428
+ )
429
+ expect(binlog_pos).to be_nil
430
+ end
431
+ end
432
+
433
+ context 'when dump contains only binlog pos' do
434
+ before { generate_dump_file(<<EOT
435
+ #{DUMP_HEADER}
436
+ EOT
437
+ ) }
438
+ it do
439
+ expect(create_table_block).to receive(:call).never
440
+ expect(insert_record_block).to receive(:call).never
441
+ expect(check_point_block).to receive(:call).once
442
+ binlog_pos = default_parser.parse(
443
+ create_table_block, insert_record_block, check_point_block
444
+ )
445
+ expect(binlog_pos).to eq(default_binlog_pos)
446
+ end
447
+ end
448
+
449
+ context 'when dump contains create table without records' do
450
+ let(:dump_content) { <<EOT
451
+ #{DUMP_HEADER}
452
+
453
+ DROP TABLE IF EXISTS `users_login`;
454
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
455
+ /*!40101 SET character_set_client = utf8 */;
456
+ CREATE TABLE `users_login` (
457
+ `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
458
+ `login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
459
+ `create_time` datetime NOT NULL COMMENT 'create time',
460
+ `update_time` datetime NOT NULL COMMENT 'update time',
461
+ PRIMARY KEY (`user_id`)
462
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='';
463
+ /*!40101 SET character_set_client = @saved_cs_client */;
464
+
465
+ --
466
+ -- Dumping data for table `users_login`
467
+ --
468
+
469
+ LOCK TABLES `users_login` WRITE;
470
+ /*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
471
+ /*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
472
+ UNLOCK TABLES;
473
+
474
+ EOT
475
+ }
476
+ before { generate_dump_file(dump_content) }
477
+ it do
478
+ expect(create_table_block).to receive(:call) { |mysql_table|
479
+ expect(mysql_table.table_name).to eq('users_login')
480
+ }.once
481
+ expect(insert_record_block).to receive(:call).never
482
+ expect(check_point_block).to receive(:call) { |mysql_table, last_pos, binlog_pos, state, substate|
483
+ expect(mysql_table).to be_nil
484
+ expect(last_pos).to eq(dump_pos_after_binlog_pos)
485
+ expect(binlog_pos).to eq(default_binlog_pos)
486
+ expect(state).to eq(Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE)
487
+ expect(substate).to be_nil
488
+ }
489
+ expect(check_point_block).to receive(:call) { |mysql_table, last_pos, binlog_pos, state, substate|
490
+ expect(mysql_table.table_name).to eq('users_login')
491
+ expect(mysql_table.columns.keys).to eq(['user_id', 'login_count', 'create_time', 'update_time'])
492
+ expect(last_pos).to eq(index_after(dump_content, 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=\'\';'))
493
+ expect(binlog_pos).to eq(default_binlog_pos)
494
+ expect(state).to eq(Flydata::Mysql::MysqlDumpParser::State::INSERT_RECORD)
495
+ expect(substate).to be_nil
496
+ }
497
+ expect(check_point_block).to receive(:call) { |mysql_table, last_pos, binlog_pos, state, substate|
498
+ expect(mysql_table.table_name).to eq('users_login')
499
+ expect(last_pos).to eq(index_after(dump_content, 'UNLOCK TABLES;'))
500
+ expect(binlog_pos).to eq(default_binlog_pos)
501
+ expect(state).to eq(Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE)
502
+ expect(substate).to be_nil
503
+ }
504
+ expect(check_point_block).to receive(:call).never
505
+
506
+ binlog_pos = default_parser.parse(
507
+ create_table_block, insert_record_block, check_point_block
508
+ )
509
+ expect(binlog_pos).to eq(default_binlog_pos)
510
+ end
511
+ end
512
+
513
+ context 'when dump contains create table with multi inserts' do
514
+ let(:dump_content) { <<EOT
515
+ #{DUMP_HEADER}
516
+
517
+ DROP TABLE IF EXISTS `users_login`;
518
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
519
+ /*!40101 SET character_set_client = utf8 */;
520
+ CREATE TABLE `users_login` (
521
+ `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
522
+ `login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
523
+ `comment` varchar(255) DEFAULT NULL COMMENT 'comment',
524
+ `create_time` datetime NOT NULL COMMENT 'create time',
525
+ `update_time` datetime NOT NULL COMMENT 'update time',
526
+ PRIMARY KEY (`user_id`)
527
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
528
+ /*!40101 SET character_set_client = @saved_cs_client */;
529
+
530
+ --
531
+ -- Dumping data for table `users_login`
532
+ --
533
+
534
+ LOCK TABLES `users_login` WRITE;
535
+ /*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
536
+ INSERT INTO `users_login` VALUES (15,46,'moodier','2001-10-02 08:18:08','1986-06-11 22:50:10'),(35,6,'missあいうえおteer','1991-10-15 19:38:07','1970-10-01 22:03:10'),(52,33,'sub\\\\field','1972-08-23 20:16:08','1974-10-10 23:28:11');
537
+ INSERT INTO `users_login` VALUES (373,31,'out\\'swearing','1979-10-07 08:10:08','2006-02-22 16:26:04'),(493,8,'schizophrenic','1979-07-06 07:34:07','1970-08-09 01:21:01');
538
+ /*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
539
+ UNLOCK TABLES;
540
+ /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
541
+
542
+ EOT
543
+ }
544
+ before { generate_dump_file(dump_content) }
545
+ it do
546
+ # create_table_block
547
+ expect(create_table_block).to receive(:call) { |mysql_table|
548
+ expect(mysql_table.table_name).to eq('users_login')
549
+ expect(mysql_table.columns.keys).to eq(['user_id', 'login_count', 'comment', 'create_time', 'update_time'])
550
+ }.once
551
+ expect(create_table_block).to receive(:call).never
552
+
553
+ # insert_record_block
554
+ [
555
+ [ %w(15 46 moodier 2001-10-02\ 08:18:08 1986-06-11\ 22:50:10),
556
+ %w(35 6 missあいうえおteer 1991-10-15\ 19:38:07 1970-10-01\ 22:03:10),
557
+ %w(52 33 sub\\field 1972-08-23\ 20:16:08 1974-10-10\ 23:28:11),],
558
+ [ %w(373 31 out'swearing 1979-10-07\ 08:10:08 2006-02-22\ 16:26:04),
559
+ %w(493 8 schizophrenic 1979-07-06\ 07:34:07 1970-08-09\ 01:21:01),]
560
+ ].each do |expected_values|
561
+ expect(insert_record_block).to receive(:call) { |mysql_table, values_set|
562
+ expect(mysql_table.table_name).to eq('users_login')
563
+ expect(values_set).to eq(expected_values)
564
+ nil
565
+ }.once
566
+ end
567
+ expect(insert_record_block).to receive(:call).never
568
+
569
+ # insert_record_block
570
+ [
571
+ {state: Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE},
572
+ {state: Flydata::Mysql::MysqlDumpParser::State::INSERT_RECORD},
573
+ {state: Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE,
574
+ last_pos: index_after(dump_content, 'UNLOCK TABLES;') + 10}
575
+ ].each do |expected_params|
576
+ expect(check_point_block).to receive(:call) { |mysql_table, last_pos, binlog_pos, state, substate|
577
+ expect(mysql_table.table_name).to eq('users_login') if mysql_table
578
+ expect(state).to eq(expected_params[:state])
579
+ if expected_params[:last_pos]
580
+ expect(last_pos).to eq(expected_params[:last_pos])
581
+ end
582
+ expect(binlog_pos).to eq(default_binlog_pos)
583
+ expect(substate).to eq(expected_params[:substate])
584
+ expected_params[:result]
585
+ }.once
586
+ end
587
+ expect(check_point_block).to receive(:call).never
588
+
589
+ binlog_pos = default_parser.parse(
590
+ create_table_block, insert_record_block, check_point_block
591
+ )
592
+ expect(binlog_pos).to eq(default_binlog_pos)
593
+ end
594
+ end
595
+
596
+ context 'when dump contains create table with records' do
597
+ let(:dump_content) { <<EOT
598
+ #{DUMP_HEADER}
599
+
600
+ DROP TABLE IF EXISTS `users_login`;
601
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
602
+ /*!40101 SET character_set_client = utf8 */;
603
+ CREATE TABLE `users_login` (
604
+ `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
605
+ `login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
606
+ `comment` varchar(255) DEFAULT NULL COMMENT 'comment',
607
+ `create_time` datetime NOT NULL COMMENT 'create time',
608
+ `update_time` datetime NOT NULL COMMENT 'update time',
609
+ PRIMARY KEY (`user_id`)
610
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
611
+ /*!40101 SET character_set_client = @saved_cs_client */;
612
+
613
+ --
614
+ -- Dumping data for table `users_login`
615
+ --
616
+
617
+ LOCK TABLES `users_login` WRITE;
618
+ /*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
619
+ INSERT INTO `users_login` VALUES (15,46,'moodier','2001-10-02 08:18:08','1986-06-11 22:50:10'),(35,6,NULL,'1991-10-15 19:38:07','1970-10-01 22:03:10'),(52,33,'subfield','1972-08-23 20:16:08','1974-10-10 23:28:11');
620
+ /*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
621
+ UNLOCK TABLES;
622
+ /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
623
+
624
+ EOT
625
+ }
626
+ before { generate_dump_file(dump_content) }
627
+ it do
628
+ # create_table_block
629
+ expect(create_table_block).to receive(:call) { |mysql_table|
630
+ expect(mysql_table.table_name).to eq('users_login')
631
+ expect(mysql_table.columns.keys).to eq(['user_id', 'login_count', 'comment', 'create_time', 'update_time'])
632
+ }.once
633
+ expect(create_table_block).to receive(:call).never
634
+
635
+ # insert_record_block
636
+ [
637
+ [ %w(15 46 moodier 2001-10-02\ 08:18:08 1986-06-11\ 22:50:10),
638
+ ['35', '6', nil, '1991-10-15 19:38:07', '1970-10-01 22:03:10'],
639
+ %w(52 33 subfield 1972-08-23\ 20:16:08 1974-10-10\ 23:28:11),],
640
+ ].each do |expected_values|
641
+ expect(insert_record_block).to receive(:call) { |mysql_table, values_set|
642
+ expect(mysql_table.table_name).to eq('users_login')
643
+ expect(values_set).to eq(expected_values)
644
+ nil
645
+ }.once
646
+ end
647
+ expect(insert_record_block).to receive(:call).never
648
+
649
+ # insert_record_block
650
+ [
651
+ {state: Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE},
652
+ {state: Flydata::Mysql::MysqlDumpParser::State::INSERT_RECORD},
653
+ {state: Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE,
654
+ last_pos: index_after(dump_content, 'UNLOCK TABLES;')}
655
+ ].each do |expected_params|
656
+ expect(check_point_block).to receive(:call) { |mysql_table, last_pos, binlog_pos, state, substate|
657
+ expect(mysql_table.table_name).to eq('users_login') if mysql_table
658
+ expect(state).to eq(expected_params[:state])
659
+ if expected_params[:last_pos]
660
+ expect(last_pos).to eq(expected_params[:last_pos])
661
+ end
662
+ expect(binlog_pos).to eq(default_binlog_pos)
663
+ expect(substate).to eq(expected_params[:substate])
664
+ expected_params[:result]
665
+ }.once
666
+ end
667
+ expect(check_point_block).to receive(:call).never
668
+
669
+ binlog_pos = default_parser.parse(
670
+ create_table_block, insert_record_block, check_point_block
671
+ )
672
+ expect(binlog_pos).to eq(default_binlog_pos)
673
+ end
674
+ end
675
+
676
+ context 'with resume after creating table' do
677
+ let(:dump_content_head) { <<EOT
678
+ #{DUMP_HEADER}
679
+
680
+ DROP TABLE IF EXISTS `users_login`;
681
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
682
+ /*!40101 SET character_set_client = utf8 */;
683
+ CREATE TABLE `users_login` (
684
+ `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
685
+ `login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
686
+ `comment` varchar(255) DEFAULT NULL COMMENT 'comment',
687
+ `create_time` datetime NOT NULL COMMENT 'create time',
688
+ `update_time` datetime NOT NULL COMMENT 'update time',
689
+ PRIMARY KEY (`user_id`)
690
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
691
+ /*!40101 SET character_set_client = @saved_cs_client */;
692
+
693
+ EOT
694
+ }
695
+ let(:dump_content_all) { <<EOT
696
+ #{dump_content_head}
697
+ --
698
+ -- Dumping data for table `users_login`
699
+ --
700
+
701
+ LOCK TABLES `users_login` WRITE;
702
+ /*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
703
+ INSERT INTO `users_login` VALUES (15,46,'moodier','2001-10-02 08:18:08','1986-06-11 22:50:10'),(35,6,'missteer','1991-10-15 19:38:07','1970-10-01 22:03:10'),(52,33,'subfield','1972-08-23 20:16:08','1974-10-10 23:28:11');
704
+ /*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
705
+ UNLOCK TABLES;
706
+ /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
707
+ EOT
708
+ }
709
+ before do
710
+ generate_dump_file(dump_content_head)
711
+ default_parser.parse(
712
+ Proc.new{},
713
+ Proc.new{
714
+ raise "Should not be called"
715
+ },
716
+ Proc.new{ |mysql_table, last_pos, binlog_pos, state, substate|
717
+ if mysql_table
718
+ @mysql_table = mysql_table
719
+ @option = { status: Flydata::Command::Sync::STATUS_PARSING,
720
+ table_name: mysql_table.table_name,
721
+ last_pos: last_pos,
722
+ binlog_pos: binlog_pos,
723
+ state: state,
724
+ substate: substate,
725
+ mysql_table: mysql_table }
726
+ end
727
+ }
728
+ )
729
+ expect(@option).to eq({
730
+ status: Flydata::Command::Sync::STATUS_PARSING,
731
+ table_name: 'users_login',
732
+ last_pos: index_after(dump_content_head, 'ENGINE=InnoDB DEFAULT CHARSET=utf8;'),
733
+ binlog_pos: default_binlog_pos,
734
+ state: Flydata::Mysql::MysqlDumpParser::State::INSERT_RECORD,
735
+ substate: nil,
736
+ mysql_table: @mysql_table
737
+ })
738
+ end
739
+ it do
740
+ generate_dump_file(dump_content_all)
741
+ parser_for_resume = MysqlDumpParser.new(file_path, @option)
742
+
743
+ # create_table_block
744
+ expect(create_table_block).to receive(:call).never
745
+
746
+ # insert_record_block
747
+ [
748
+ [ %w(15 46 moodier 2001-10-02\ 08:18:08 1986-06-11\ 22:50:10),
749
+ %w(35 6 missteer 1991-10-15\ 19:38:07 1970-10-01\ 22:03:10),
750
+ %w(52 33 subfield 1972-08-23\ 20:16:08 1974-10-10\ 23:28:11),],
751
+ ].each do |expected_values|
752
+ expect(insert_record_block).to receive(:call) { |mysql_table, values|
753
+ expect(mysql_table.table_name).to eq('users_login')
754
+ expect(values).to eq(expected_values)
755
+ nil
756
+ }.once
757
+ end
758
+ expect(insert_record_block).to receive(:call).never
759
+
760
+ # check_point_block
761
+ [
762
+ {state: Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE,
763
+ last_pos: index_after(dump_content_all, 'UNLOCK TABLES;')}
764
+ ].each do |expected_params|
765
+ expect(check_point_block).to receive(:call) { |mysql_table, last_pos, binlog_pos, state, substate|
766
+ expect(mysql_table.table_name).to eq('users_login') if mysql_table
767
+ expect(state).to eq(expected_params[:state])
768
+ if expected_params[:last_pos]
769
+ expect(last_pos).to eq(expected_params[:last_pos])
770
+ end
771
+ expect(binlog_pos).to eq(default_binlog_pos)
772
+ expect(substate).to eq(expected_params[:substate])
773
+ expected_params[:result]
774
+ }.once
775
+ end
776
+ expect(check_point_block).to receive(:call).never
777
+
778
+ binlog_pos = parser_for_resume.parse(
779
+ create_table_block, insert_record_block, check_point_block
780
+ )
781
+ expect(binlog_pos).to eq(default_binlog_pos)
782
+ end
783
+ end
784
+
785
+ context 'with resume during insert records' do
786
+ let(:dump_content) { <<EOT
787
+ #{DUMP_HEADER}
788
+
789
+ DROP TABLE IF EXISTS `users_login`;
790
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
791
+ /*!40101 SET character_set_client = utf8 */;
792
+ CREATE TABLE `users_login` (
793
+ `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
794
+ `login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
795
+ `comment` varchar(255) DEFAULT NULL COMMENT 'comment',
796
+ `create_time` datetime NOT NULL COMMENT 'create time',
797
+ `update_time` datetime NOT NULL COMMENT 'update time',
798
+ PRIMARY KEY (`user_id`)
799
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
800
+ /*!40101 SET character_set_client = @saved_cs_client */;
801
+
802
+ --
803
+ -- Dumping data for table `users_login`
804
+ --
805
+
806
+ LOCK TABLES `users_login` WRITE;
807
+ /*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
808
+ INSERT INTO `users_login` VALUES (15,46,'moodier','2001-10-02 08:18:08','1986-06-11 22:50:10'),(35,6,'missteer','1991-10-15 19:38:07','1970-10-01 22:03:10'),(52,33,'subfield','1972-08-23 20:16:08','1974-10-10 23:28:11');
809
+ INSERT INTO `users_login` VALUES (194,11,'pandemonium','2008-01-22 22:15:10','1991-04-04 17:30:05'),(230,7,'cloudburst','2010-12-28 11:46:11','1971-06-22 13:08:01');
810
+ /*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
811
+ UNLOCK TABLES;
812
+ /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
813
+ EOT
814
+ }
815
+ before do
816
+ generate_dump_file(dump_content)
817
+
818
+ insert_record_block_for_resume = double('insert_record_block_for_resume')
819
+ expect(insert_record_block_for_resume).to receive(:call) { true }.once
820
+ expect(insert_record_block_for_resume).to receive(:call) { false }.once
821
+
822
+ default_parser.parse(
823
+ Proc.new{},
824
+ insert_record_block_for_resume,
825
+ Proc.new{ |mysql_table, last_pos, binlog_pos, state, substate|
826
+ if last_pos == index_after(dump_content, "'1972-08-23 20:16:08','1974-10-10 23:28:11');")
827
+ @mysql_table = mysql_table
828
+ @option = { status: Flydata::Command::Sync::STATUS_PARSING,
829
+ table_name: mysql_table.table_name,
830
+ last_pos: last_pos,
831
+ binlog_pos: binlog_pos,
832
+ state: state,
833
+ substate: substate,
834
+ mysql_table: mysql_table }
835
+ end
836
+ }
837
+ )
838
+ expect(@option).to eq({
839
+ status: Flydata::Command::Sync::STATUS_PARSING,
840
+ table_name: 'users_login',
841
+ last_pos: index_after(dump_content, "'1972-08-23 20:16:08','1974-10-10 23:28:11');"),
842
+ binlog_pos: default_binlog_pos,
843
+ state: Flydata::Mysql::MysqlDumpParser::State::INSERT_RECORD,
844
+ substate: nil,
845
+ mysql_table: @mysql_table
846
+ })
847
+ end
848
+ it do
849
+ generate_dump_file(dump_content)
850
+ parser_for_resume = MysqlDumpParser.new(file_path, @option)
851
+
852
+ # create_table_block
853
+ expect(create_table_block).to receive(:call).never
854
+
855
+ # insert_record_block
856
+ [
857
+ [ %w(194 11 pandemonium 2008-01-22\ 22:15:10 1991-04-04\ 17:30:05),
858
+ %w(230 7 cloudburst 2010-12-28\ 11:46:11 1971-06-22\ 13:08:01),],
859
+ ].each do |expected_values|
860
+ expect(insert_record_block).to receive(:call) { |mysql_table, values_set|
861
+ expect(mysql_table.table_name).to eq('users_login')
862
+ expect(values_set).to eq(expected_values)
863
+ nil
864
+ }.once
865
+ end
866
+ expect(insert_record_block).to receive(:call).never
867
+
868
+ # check_point_block
869
+ [
870
+ {state: Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE,
871
+ last_pos: index_after(dump_content, 'UNLOCK TABLES;')}
872
+ ].each do |expected_params|
873
+ expect(check_point_block).to receive(:call) { |mysql_table, last_pos, binlog_pos, state, substate|
874
+ expect(mysql_table.table_name).to eq('users_login') if mysql_table
875
+ expect(state).to eq(expected_params[:state])
876
+ if expected_params[:last_pos]
877
+ expect(last_pos).to eq(expected_params[:last_pos])
878
+ end
879
+ expect(binlog_pos).to eq(default_binlog_pos)
880
+ expect(substate).to eq(expected_params[:substate])
881
+ expected_params[:result]
882
+ }.once
883
+ end
884
+ expect(check_point_block).to receive(:call).never
885
+
886
+ binlog_pos = parser_for_resume.parse(
887
+ create_table_block, insert_record_block, check_point_block
888
+ )
889
+ expect(binlog_pos).to eq(default_binlog_pos)
890
+ end
891
+ end
892
+
893
+ context 'when dump contains contain escape characters' do
894
+ let(:dump_content) { <<EOT
895
+ #{DUMP_HEADER}
896
+
897
+ DROP TABLE IF EXISTS `users_login`;
898
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
899
+ /*!40101 SET character_set_client = utf8 */;
900
+ CREATE TABLE `users_login` (
901
+ `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
902
+ `login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
903
+ `comment` varchar(255) DEFAULT NULL COMMENT 'comment',
904
+ `create_time` datetime NOT NULL COMMENT 'create time',
905
+ `update_time` datetime NOT NULL COMMENT 'update time',
906
+ PRIMARY KEY (`user_id`)
907
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
908
+ /*!40101 SET character_set_client = @saved_cs_client */;
909
+
910
+ --
911
+ -- Dumping data for table `users_login`
912
+ --
913
+
914
+ LOCK TABLES `users_login` WRITE;
915
+ /*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
916
+ INSERT INTO `users_login` VALUES (15,46,'moodier)','2001-10-02 08:18:08','1986-06-11 22:50:10'),(35,6,'miss,teer','1991-10-15 19:38:07','1970-10-01 22:03:10'),(52,33,'subfield\\'','1972-08-23 20:16:08','1974-10-10 23:28:11');
917
+ INSERT INTO `users_login` VALUES (373,31,'outs\\nwearing','1979-10-07 08:10:08','2006-02-22 16:26:04'),(493,8,'schiz\tophrenic','1979-07-06 07:34:07\\'),','1970-08-09,01:21:01,\\')');
918
+ /*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
919
+ UNLOCK TABLES;
920
+ /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
921
+
922
+ EOT
923
+ }
924
+ before { generate_dump_file(dump_content) }
925
+ it do
926
+ # create_table_block
927
+ expect(create_table_block).to receive(:call) { |mysql_table|
928
+ expect(mysql_table.table_name).to eq('users_login')
929
+ expect(mysql_table.columns.keys).to eq(['user_id', 'login_count', 'comment', 'create_time', 'update_time'])
930
+ }.once
931
+ expect(create_table_block).to receive(:call).never
932
+
933
+ # insert_record_block
934
+ [
935
+ [ %w(15 46 moodier\) 2001-10-02\ 08:18:08 1986-06-11\ 22:50:10),
936
+ %w(35 6 miss,teer 1991-10-15\ 19:38:07 1970-10-01\ 22:03:10),
937
+ %w(52 33 subfield' 1972-08-23\ 20:16:08 1974-10-10\ 23:28:11),],
938
+ [ ['373','31',"outs\nwearing",'1979-10-07 08:10:08','2006-02-22 16:26:04'],
939
+ ['493','8',"schiz\tophrenic",'1979-07-06 07:34:07\'),','1970-08-09,01:21:01,\')'] ]
940
+ ].each do |expected_values|
941
+ expect(insert_record_block).to receive(:call) { |mysql_table, values_set|
942
+ expect(mysql_table.table_name).to eq('users_login')
943
+ expect(values_set).to eq(expected_values)
944
+ nil
945
+ }.once
946
+ end
947
+ expect(insert_record_block).to receive(:call).never
948
+
949
+ # insert_record_block
950
+ [
951
+ {state: Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE},
952
+ {state: Flydata::Mysql::MysqlDumpParser::State::INSERT_RECORD},
953
+ {state: Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE,
954
+ last_pos: index_after(dump_content, 'UNLOCK TABLES;')}
955
+ ].each do |expected_params|
956
+ expect(check_point_block).to receive(:call) { |mysql_table, last_pos, binlog_pos, state, substate|
957
+ expect(mysql_table.table_name).to eq('users_login') if mysql_table
958
+ expect(state).to eq(expected_params[:state])
959
+ if expected_params[:last_pos]
960
+ expect(last_pos).to eq(expected_params[:last_pos])
961
+ end
962
+ expect(binlog_pos).to eq(default_binlog_pos)
963
+ expect(substate).to eq(expected_params[:substate])
964
+ expected_params[:result]
965
+ }.once
966
+ end
967
+ expect(check_point_block).to receive(:call).never
968
+
969
+ binlog_pos = default_parser.parse(
970
+ create_table_block, insert_record_block, check_point_block
971
+ )
972
+ expect(binlog_pos).to eq(default_binlog_pos)
973
+ end
974
+ end
975
+ end
976
+ end
977
+ describe MysqlDumpParser::InsertParser do
978
+ describe '#parse' do
979
+ let(:target_line) { '' }
980
+ let(:parser) { MysqlDumpParser::InsertParser.new(StringIO.new(target_line)) }
981
+ subject { parser.parse }
982
+
983
+ context 'when single record' do
984
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'hogehoge','2014-04-15 13:49:14');" }
985
+ it 'should return one element array' do
986
+ expect(subject).to eq([['1','hogehoge','2014-04-15 13:49:14']])
987
+ end
988
+ end
989
+
990
+ context 'when 2 records' do
991
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'foo','2014-04-15 13:49:14'),(2,'var','2014-04-15 14:21:05');" }
992
+ it 'should return two element array' do
993
+ expect(subject).to eq([['1','foo','2014-04-15 13:49:14'],['2','var','2014-04-15 14:21:05']])
994
+ end
995
+ end
996
+
997
+ context 'when data includes carriage return' do
998
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'abcd\\refgh','2014-04-15 13:49:14');" }
999
+ it 'should escape return carriage' do
1000
+ expect(subject).to eq([['1',"abcd\refgh",'2014-04-15 13:49:14']])
1001
+ end
1002
+ end
1003
+
1004
+ context 'when data includes new line' do
1005
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'abcd\\nefgh','2014-04-15 13:49:14');" }
1006
+ it 'should escape new line' do
1007
+ expect(subject).to eq([['1',"abcd\nefgh",'2014-04-15 13:49:14']])
1008
+ end
1009
+ end
1010
+
1011
+ context 'when data includes escaped single quotation' do
1012
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'abcd\\',\\'efgh','2014-04-15 13:49:14');" }
1013
+ it 'should escape single quotation' do
1014
+ expect(subject).to eq([['1',"abcd','efgh",'2014-04-15 13:49:14']])
1015
+ end
1016
+ end
1017
+
1018
+ context 'when data includes back slash' do
1019
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'abcd\\\\efgh','2014-04-15 13:49:14');" }
1020
+ it 'should escape back slash' do
1021
+ expect(subject).to eq([['1',"abcd\\efgh",'2014-04-15 13:49:14']])
1022
+ end
1023
+ end
1024
+
1025
+ context 'when data includes mixed escaped characters' do
1026
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'ab\\rcd\\\\e\\nf\\',\\'gh','2014-04-15 13:49:14');" }
1027
+ it 'should escape all' do
1028
+ expect(subject).to eq([['1',"ab\rcd\\e\nf','gh",'2014-04-15 13:49:14']])
1029
+ end
1030
+ end
1031
+
1032
+ context 'when data includes mixed escaped characters in a row' do
1033
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'ab\\\\ncd\\\\\\nefgh','2014-04-15 13:49:14');" }
1034
+ it 'should escape all' do
1035
+ expect(subject).to eq([['1',"ab\\ncd\\\nefgh",'2014-04-15 13:49:14']])
1036
+ end
1037
+ end
1038
+
1039
+ context 'when data end with back slash' do
1040
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'D:\\\\download\\\\','2014-04-15 13:49:14');" }
1041
+ it 'should escape back slash' do
1042
+ expect(subject).to eq([['1',"D:\\download\\",'2014-04-15 13:49:14']])
1043
+ end
1044
+ end
1045
+ end
1046
+ end
1047
+ end
1048
+
1049
+ end