logstash-integration-jdbc 5.2.4 → 5.4.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/docs/filter-jdbc_static.asciidoc +14 -2
  4. data/docs/filter-jdbc_streaming.asciidoc +1 -1
  5. data/docs/input-jdbc.asciidoc +41 -4
  6. data/lib/logstash/filters/jdbc/basic_database.rb +1 -1
  7. data/lib/logstash/filters/jdbc/read_only_database.rb +2 -2
  8. data/lib/logstash/filters/jdbc_static.rb +19 -10
  9. data/lib/logstash/inputs/jdbc.rb +69 -20
  10. data/lib/logstash/plugin_mixins/jdbc/common.rb +2 -1
  11. data/lib/logstash/plugin_mixins/jdbc/jdbc.rb +22 -17
  12. data/lib/logstash/plugin_mixins/jdbc/sequel_bootstrap.rb +21 -0
  13. data/lib/logstash/plugin_mixins/jdbc/statement_handler.rb +51 -45
  14. data/lib/logstash/plugin_mixins/jdbc/timezone_proxy.rb +61 -0
  15. data/lib/logstash/plugin_mixins/jdbc/value_tracking.rb +16 -3
  16. data/lib/logstash-integration-jdbc_jars.rb +4 -2
  17. data/logstash-integration-jdbc.gemspec +6 -6
  18. data/spec/filters/jdbc_static_spec.rb +10 -0
  19. data/spec/filters/jdbc_streaming_spec.rb +7 -10
  20. data/spec/inputs/integration/integ_spec.rb +28 -9
  21. data/spec/inputs/jdbc_spec.rb +202 -59
  22. data/spec/plugin_mixins/jdbc/timezone_proxy_spec.rb +68 -0
  23. data/spec/plugin_mixins/jdbc/value_tracking_spec.rb +113 -0
  24. data/vendor/jar-dependencies/org/apache/derby/derby/10.15.2.1/derby-10.15.2.1.jar +0 -0
  25. data/vendor/jar-dependencies/org/apache/derby/derbyclient/10.15.2.1/derbyclient-10.15.2.1.jar +0 -0
  26. data/vendor/jar-dependencies/org/apache/derby/derbyshared/10.15.2.1/derbyshared-10.15.2.1.jar +0 -0
  27. data/vendor/jar-dependencies/org/apache/derby/derbytools/10.15.2.1/derbytools-10.15.2.1.jar +0 -0
  28. metadata +39 -49
  29. data/lib/logstash/plugin_mixins/jdbc/checked_count_logger.rb +0 -43
  30. data/lib/logstash/plugin_mixins/jdbc/scheduler.rb +0 -175
  31. data/spec/plugin_mixins/jdbc/scheduler_spec.rb +0 -78
  32. data/vendor/jar-dependencies/org/apache/derby/derby/10.14.1.0/derby-10.14.1.0.jar +0 -0
  33. data/vendor/jar-dependencies/org/apache/derby/derbyclient/10.14.1.0/derbyclient-10.14.1.0.jar +0 -0
@@ -2,13 +2,13 @@
2
2
  require "logstash/devutils/rspec/spec_helper"
3
3
  require "logstash/devutils/rspec/shared_examples"
4
4
  require "logstash/inputs/jdbc"
5
- require "jdbc/derby"
6
5
  require "sequel"
7
6
  require "sequel/adapters/jdbc"
8
7
  require "timecop"
9
8
  require "stud/temporary"
10
9
  require "time"
11
10
  require "date"
11
+ require "pathname"
12
12
 
13
13
  # We do not need to set TZ env var anymore because we can have 'Sequel.application_timezone' set to utc by default now.
14
14
 
@@ -32,7 +32,6 @@ describe LogStash::Inputs::Jdbc do
32
32
  before :each do
33
33
  if !RSpec.current_example.metadata[:no_connection]
34
34
  # before body
35
- Jdbc::Derby.load_driver
36
35
  db.create_table :test_table do
37
36
  DateTime :created_at
38
37
  BigDecimal :big_num
@@ -51,6 +50,9 @@ describe LogStash::Inputs::Jdbc do
51
50
  db.drop_table(:types_table)
52
51
  db.drop_table(:test1_table)
53
52
  end
53
+
54
+ last_run_default_path = LogStash::SETTINGS.get_value("path.data")
55
+ FileUtils.rm_f("#{last_run_default_path}/plugins/inputs/jdbc/logstash_jdbc_last_run")
54
56
  end
55
57
 
56
58
  context "when registering and tearing down" do
@@ -247,18 +249,6 @@ describe LogStash::Inputs::Jdbc do
247
249
  Timecop.return
248
250
  end
249
251
 
250
- it "cleans up scheduler resources on close" do
251
- runner = Thread.new do
252
- plugin.run(queue)
253
- end
254
- sleep 1
255
- plugin.do_close
256
-
257
- scheduler = plugin.instance_variable_get(:@scheduler)
258
- expect(scheduler).to_not be_nil
259
- expect(scheduler.down?).to be_truthy
260
- end
261
-
262
252
  end
263
253
 
264
254
  context "when scheduling and previous runs are to be preserved" do
@@ -285,7 +275,7 @@ describe LogStash::Inputs::Jdbc do
285
275
  sleep 1
286
276
  for i in 0..1
287
277
  sleep 1
288
- updated_last_run = YAML.load(File.read(settings["last_run_metadata_path"]))
278
+ updated_last_run = LogStash::PluginMixins::Jdbc::ValueTracking.load_yaml(File.read(settings["last_run_metadata_path"]))
289
279
  expect(updated_last_run).to be > last_run_time
290
280
  last_run_time = updated_last_run
291
281
  end
@@ -555,7 +545,7 @@ describe LogStash::Inputs::Jdbc do
555
545
  expect(actual).to eq(expected)
556
546
  plugin.stop
557
547
  raw_last_run_value = File.read(settings["last_run_metadata_path"])
558
- last_run_value = YAML.load(raw_last_run_value)
548
+ last_run_value = LogStash::PluginMixins::Jdbc::ValueTracking.load_yaml(raw_last_run_value)
559
549
  expect(last_run_value).to be_a(DateTime)
560
550
  expect(last_run_value.strftime("%F %T.%N %Z")).to eq("2015-01-02 02:00:00.722000000 +00:00")
561
551
 
@@ -570,7 +560,7 @@ describe LogStash::Inputs::Jdbc do
570
560
  plugin.stop
571
561
  expect(event.get("num")).to eq(12)
572
562
  expect(event.get("custom_time").time).to eq(Time.iso8601("2015-01-02T03:00:00.811Z"))
573
- last_run_value = YAML.load(File.read(settings["last_run_metadata_path"]))
563
+ last_run_value = LogStash::PluginMixins::Jdbc::ValueTracking.load_yaml(File.read(settings["last_run_metadata_path"]))
574
564
  expect(last_run_value).to be_a(DateTime)
575
565
  # verify that sub-seconds are recorded to the file
576
566
  expect(last_run_value.strftime("%F %T.%N %Z")).to eq("2015-01-02 03:00:00.811000000 +00:00")
@@ -611,6 +601,33 @@ describe LogStash::Inputs::Jdbc do
611
601
  # With no timezone set, no change should occur
612
602
  expect(event.get("custom_time").time).to eq(Time.iso8601("2015-01-01T12:00:00Z"))
613
603
  end
604
+
605
+ %w(
606
+ Etc/UTC
607
+ America/Los_Angeles
608
+ Europe/Berlin
609
+ Asia/Tokyo
610
+ ).each do |local_timezone|
611
+ context "when host machine has timezone `#{local_timezone}`" do
612
+ around(:each) do |example|
613
+ begin
614
+ previous_tz = ENV['TZ']
615
+ ENV['TZ'] = local_timezone
616
+ example.call
617
+ ensure
618
+ ENV['TZ'] = previous_tz
619
+ end
620
+ end
621
+
622
+ let(:tz) { TZInfo::Timezone.get(local_timezone) }
623
+
624
+ it "converts the time using the machine's local timezone" do
625
+ plugin.run(queue)
626
+ event = queue.pop
627
+ expect(event.get("custom_time").time).to eq(Time.new(2015,1,1,12,0,0,tz))
628
+ end
629
+ end
630
+ end
614
631
  end
615
632
 
616
633
  context "when iteratively running plugin#run" do
@@ -704,7 +721,7 @@ describe LogStash::Inputs::Jdbc do
704
721
  "last_run_metadata_path" => Stud::Temporary.pathname }
705
722
  end
706
723
 
707
- let(:nums) { [BigDecimal.new(10), BigDecimal.new(20), BigDecimal.new(30), BigDecimal.new(40), BigDecimal.new(50)] }
724
+ let(:nums) { [BigDecimal(10), BigDecimal(20), BigDecimal(30), BigDecimal(40), BigDecimal(50)] }
708
725
 
709
726
  before do
710
727
  plugin.register
@@ -1114,6 +1131,98 @@ describe LogStash::Inputs::Jdbc do
1114
1131
  end
1115
1132
  end
1116
1133
 
1134
+ context "when state is persisted" do
1135
+ context "to file" do
1136
+ let(:settings) do
1137
+ {
1138
+ "statement" => "SELECT * FROM test_table",
1139
+ "record_last_run" => true
1140
+ }
1141
+ end
1142
+
1143
+ before do
1144
+ plugin.register
1145
+ end
1146
+
1147
+ after do
1148
+ plugin.stop
1149
+ end
1150
+
1151
+ context "with default last_run_metadata_path" do
1152
+ it "should save state in data.data subpath" do
1153
+ path = LogStash::SETTINGS.get_value("path.data")
1154
+ expect(plugin.last_run_metadata_file_path).to start_with(path)
1155
+ end
1156
+ end
1157
+
1158
+ context "with customized last_run_metadata_path" do
1159
+ let(:settings) { super().merge({ "last_run_metadata_path" => Stud::Temporary.pathname })}
1160
+
1161
+ it "should save state in data.data subpath" do
1162
+ expect(plugin.last_run_metadata_file_path).to start_with(settings["last_run_metadata_path"])
1163
+ end
1164
+ end
1165
+ end
1166
+
1167
+ context "with customized last_run_metadata_path point to directory" do
1168
+ let(:settings) do
1169
+ path = Stud::Temporary.pathname
1170
+ Pathname.new(path).tap {|path| path.mkpath}
1171
+ super().merge({ "last_run_metadata_path" => path})
1172
+ end
1173
+
1174
+ it "raise configuration error" do
1175
+ expect { plugin.register }.to raise_error(LogStash::ConfigurationError)
1176
+ end
1177
+ end
1178
+ end
1179
+
1180
+ context "update the previous default last_run_metadata_path" do
1181
+ let(:settings) do
1182
+ {
1183
+ "statement" => "SELECT * FROM test_table",
1184
+ "record_last_run" => true
1185
+ }
1186
+ end
1187
+
1188
+ let(:fake_home) do
1189
+ path = Stud::Temporary.pathname
1190
+ Pathname.new(path).tap {|path| path.mkpath}
1191
+ path
1192
+ end
1193
+
1194
+ context "when a file exists" do
1195
+ before do
1196
+ # in a faked HOME folder save a valid previous last_run metadata file
1197
+ allow(ENV).to receive(:[]).with(anything).and_call_original
1198
+ allow(ENV).to receive(:[]).with('HOME').and_return(fake_home)
1199
+
1200
+ File.open("#{fake_home}/.logstash_jdbc_last_run", 'w') do |file|
1201
+ file.write("--- !ruby/object:DateTime '2022-03-08 08:10:00.486889000 Z'")
1202
+ end
1203
+ end
1204
+ let(:old_path) { "#{fake_home}/.logstash_jdbc_last_run" }
1205
+ let(:path_data) { LogStash::SETTINGS.get_value("path.data") }
1206
+ let(:new_path) { "#{path_data}/plugins/inputs/jdbc/logstash_jdbc_last_run" }
1207
+
1208
+ it "should be moved" do
1209
+ plugin.register
1210
+ expect(::File).to_not exist(old_path)
1211
+ expect(::File).to exist(new_path)
1212
+ end
1213
+ context "if the delete fails" do
1214
+ before(:each) do
1215
+ allow(File).to receive(:delete).and_raise ArgumentError
1216
+ end
1217
+ it "should be still be moved" do
1218
+ plugin.register
1219
+ expect(::File).to exist(old_path) # old still exists
1220
+ expect(::File).to exist(new_path)
1221
+ end
1222
+ end
1223
+ end
1224
+ end
1225
+
1117
1226
  context "when setting fetch size" do
1118
1227
 
1119
1228
  let(:settings) do
@@ -1280,6 +1389,34 @@ describe LogStash::Inputs::Jdbc do
1280
1389
  expect { plugin.register }.to_not raise_error
1281
1390
  plugin.stop
1282
1391
  end
1392
+
1393
+ it "does retry when query execution fails" do
1394
+ mixin_settings['statement_retry_attempts'] = 2
1395
+ mixin_settings['statement_retry_attempts_wait_time'] = 0.5
1396
+ queue = Queue.new
1397
+ plugin.register
1398
+
1399
+ handler = plugin.instance_variable_get(:@statement_handler)
1400
+ allow(handler).to receive(:perform_query).with(instance_of(Sequel::JDBC::Database), instance_of(Time)).and_raise(Sequel::PoolTimeout)
1401
+ expect(plugin.logger).to receive(:error).with("Unable to execute statement. Trying again.")
1402
+ expect(plugin.logger).to receive(:error).with("Unable to execute statement. Tried 2 times.")
1403
+
1404
+ plugin.run(queue)
1405
+ plugin.stop
1406
+ end
1407
+
1408
+ it "does not retry when query execution succeeds" do
1409
+ mixin_settings['connection_retry_attempts'] = 2
1410
+ queue = Queue.new
1411
+ plugin.register
1412
+
1413
+ handler = plugin.instance_variable_get(:@statement_handler)
1414
+ allow(handler).to receive(:perform_query).with(instance_of(Sequel::JDBC::Database), instance_of(Time)).and_call_original
1415
+ expect(plugin.logger).not_to receive(:error)
1416
+
1417
+ plugin.run(queue)
1418
+ plugin.stop
1419
+ end
1283
1420
  end
1284
1421
 
1285
1422
  context "when encoding of some columns need to be changed" do
@@ -1432,57 +1569,64 @@ describe LogStash::Inputs::Jdbc do
1432
1569
  end
1433
1570
  end
1434
1571
 
1435
- context "when debug logging and a count query raises a count related error" do
1572
+ context "when retrieving records with ambiguous timestamps" do
1573
+
1436
1574
  let(:settings) do
1437
- { "statement" => "SELECT * from types_table" }
1438
- end
1439
- let(:logger) { double("logger", :debug? => true) }
1440
- let(:statement_logger) { LogStash::PluginMixins::Jdbc::CheckedCountLogger.new(logger) }
1441
- let(:value_tracker) { double("value tracker", :set_value => nil, :write => nil) }
1442
- let(:msg) { 'Java::JavaSql::SQLSyntaxErrorException: Dynamic SQL Error; SQL error code = -104; Token unknown - line 1, column 105; LIMIT [SQLState:42000, ISC error code:335544634]' }
1443
- let(:error_args) do
1444
- {"exception" => msg}
1575
+ {
1576
+ "statement" => "SELECT * from types_table",
1577
+ "jdbc_default_timezone" => jdbc_default_timezone
1578
+ }
1445
1579
  end
1446
1580
 
1447
- before do
1448
- db << "INSERT INTO types_table (num, string, started_at, custom_time, ranking) VALUES (1, 'A test', '1999-12-31', '1999-12-31 23:59:59', 95.67)"
1581
+ before(:each) do
1582
+ db << "INSERT INTO types_table (num, string, started_at, custom_time, ranking) VALUES (1, 'A test', '1999-12-31', '2021-11-07 01:23:45', 95.67)"
1449
1583
  plugin.register
1450
- plugin.set_statement_logger(statement_logger)
1451
- plugin.set_value_tracker(value_tracker)
1452
- allow(value_tracker).to receive(:value).and_return("bar")
1453
- allow(statement_logger).to receive(:execute_count).once.and_raise(StandardError.new(msg))
1454
1584
  end
1455
1585
 
1456
- after do
1457
- plugin.stop
1458
- end
1586
+ context "when initialized with a preference for DST being enabled" do
1587
+ let(:jdbc_default_timezone) { 'America/Chicago[dst_enabled_on_overlap:true]' }
1459
1588
 
1460
- context "if the count query raises an error" do
1461
- it "should log a debug line without a count key as its unknown whether a count works at this stage" do
1462
- expect(logger).to receive(:warn).once.with("Attempting a count query raised an error, the generated count statement is most likely incorrect but check networking, authentication or your statement syntax", error_args)
1463
- expect(logger).to receive(:warn).once.with("Ongoing count statement generation is being prevented")
1464
- expect(logger).to receive(:debug).once.with("Executing JDBC query", :statement => settings["statement"], :parameters => {:sql_last_value=>"bar"})
1589
+ it 'treats the timestamp column as if DST was enabled' do
1465
1590
  plugin.run(queue)
1466
- queue.pop
1591
+ event = queue.pop
1592
+ expect(event.get("custom_time")).to be_a_logstash_timestamp_equivalent_to("2021-11-07T06:23:45Z")
1467
1593
  end
1594
+ end
1595
+ context "when initialized with a preference for DST being disabled" do
1596
+ let(:jdbc_default_timezone) { 'America/Chicago[dst_enabled_on_overlap:false]' }
1468
1597
 
1469
- it "should create an event normally" do
1470
- allow(logger).to receive(:warn)
1471
- allow(logger).to receive(:debug)
1598
+ it 'treats the timestamp column as if DST was disabled' do
1472
1599
  plugin.run(queue)
1473
1600
  event = queue.pop
1474
- expect(event.get("num")).to eq(1)
1475
- expect(event.get("string")).to eq("A test")
1476
- expect(event.get("started_at")).to be_a_logstash_timestamp_equivalent_to("1999-12-31T00:00:00.000Z")
1477
- expect(event.get("custom_time")).to be_a_logstash_timestamp_equivalent_to("1999-12-31T23:59:59.000Z")
1478
- expect(event.get("ranking").to_f).to eq(95.67)
1601
+ expect(event.get("custom_time")).to be_a_logstash_timestamp_equivalent_to("2021-11-07T07:23:45Z")
1602
+ end
1603
+ end
1604
+ context "when initialized without a preference for DST being enabled or disabled" do
1605
+ before(:each) { allow(plugin.logger).to receive(:warn) }
1606
+ let(:jdbc_default_timezone) { 'America/Chicago' }
1607
+
1608
+ it 'the error results in helpful log warning' do
1609
+ plugin.run(queue)
1610
+ expect(plugin.logger).to have_received(:warn).with(a_string_including("Exception when executing JDBC query"), a_hash_including(:message => a_string_including("2021-11-07 01:23:45 is an ambiguous local time")))
1479
1611
  end
1480
1612
  end
1481
1613
  end
1482
1614
 
1615
+ def load_derby_version
1616
+ version = {}
1617
+ derby_version = File.join(Dir.pwd, 'derby_version.txt')
1618
+ File.readlines(derby_version, chomp: true).each do |line|
1619
+ key = line.split('=')[0]
1620
+ value = line.split('=')[1]
1621
+ version[key] = value
1622
+ end
1623
+ version
1624
+ end
1625
+
1483
1626
  context "when an unreadable jdbc_driver_path entry is present" do
1484
1627
  let(:driver_jar_path) do
1485
- jar_file = $CLASSPATH.find { |name| name.index(Jdbc::Derby.driver_jar) }
1628
+ derby_version = load_derby_version()['DERBY_VERSION']
1629
+ jar_file = $CLASSPATH.find { |name| name.index("derby-#{derby_version}.jar") }
1486
1630
  raise "derby jar not found on class-path" unless jar_file
1487
1631
  jar_file.sub('file:', '')
1488
1632
  end
@@ -1498,16 +1642,15 @@ describe LogStash::Inputs::Jdbc do
1498
1642
  { "statement" => "SELECT * from types_table", "jdbc_driver_library" => invalid_driver_jar_path }
1499
1643
  end
1500
1644
 
1501
- before do
1502
- plugin.register
1503
- end
1504
-
1505
1645
  after do
1506
1646
  plugin.stop
1507
1647
  end
1508
1648
 
1509
1649
  it "raise a loading error" do
1510
- expect { plugin.run(queue) }.
1650
+ expect(File.exists?(invalid_driver_jar_path)).to be true
1651
+ expect(FileTest.readable?(invalid_driver_jar_path)).to be false
1652
+
1653
+ expect { plugin.register }.
1511
1654
  to raise_error(LogStash::PluginLoadingError, /unable to load .*? from :jdbc_driver_library, file not readable/)
1512
1655
  end
1513
1656
  end
@@ -1619,7 +1762,7 @@ describe LogStash::Inputs::Jdbc do
1619
1762
  plugin.run(queue)
1620
1763
 
1621
1764
  expect(queue.size).to eq(expected_queue_size)
1622
- expect(YAML.load(File.read(settings["last_run_metadata_path"]))).to eq(expected_queue_size)
1765
+ expect(LogStash::PluginMixins::Jdbc::ValueTracking.load_yaml(File.read(settings["last_run_metadata_path"]))).to eq(expected_queue_size)
1623
1766
  end
1624
1767
  end
1625
1768
 
@@ -1644,7 +1787,7 @@ describe LogStash::Inputs::Jdbc do
1644
1787
  plugin.run(queue)
1645
1788
 
1646
1789
  expect(queue.size).to eq(expected_queue_size)
1647
- expect(YAML.load(File.read(settings["last_run_metadata_path"]))).to eq(last_run_value + expected_queue_size)
1790
+ expect(LogStash::PluginMixins::Jdbc::ValueTracking.load_yaml(File.read(settings["last_run_metadata_path"]))).to eq(last_run_value + expected_queue_size)
1648
1791
  end
1649
1792
  end
1650
1793
  end
@@ -1681,7 +1824,7 @@ describe LogStash::Inputs::Jdbc do
1681
1824
  let(:jdbc_driver_class) { "org.apache.NonExistentDriver" }
1682
1825
  it "raises a loading error" do
1683
1826
  expect { plugin.send(:load_driver) }.to raise_error LogStash::PluginLoadingError,
1684
- /java.lang.ClassNotFoundException: org.apache.NonExistentDriver/
1827
+ /ClassNotFoundException: org.apache.NonExistentDriver/
1685
1828
  end
1686
1829
  end
1687
1830
  end
@@ -0,0 +1,68 @@
1
+ # encoding: utf-8
2
+ require "logstash/devutils/rspec/spec_helper"
3
+ require "logstash/plugin_mixins/jdbc/timezone_proxy"
4
+
5
+ describe LogStash::PluginMixins::Jdbc::TimezoneProxy do
6
+ subject(:timezone) { described_class.load(timezone_spec) }
7
+
8
+ context 'when handling a daylight-savings ambiguous time' do
9
+ context 'without extensions' do
10
+ let(:timezone_spec) { 'America/Los_Angeles[]' }
11
+ it 'raises an AmbiguousTime error' do
12
+ expect { timezone.local_time(2021,11,7,1,17) }.to raise_error(::TZInfo::AmbiguousTime)
13
+ end
14
+ end
15
+ context 'with extension `dst_enabled_on_overlap:true`' do
16
+ let(:timezone_spec) { 'America/Los_Angeles[dst_enabled_on_overlap:true]' }
17
+ it 'resolves as if DST were enabled' do
18
+ timestamp = timezone.local_time(2021,11,7,1,17)
19
+ aggregate_failures do
20
+ expect(timestamp.dst?).to be true
21
+ expect(timestamp.zone).to eq('PDT') # Pacific Daylight Time
22
+ expect(timestamp.getutc).to eq(Time.utc(2021,11,7,8,17))
23
+ expect(timestamp.utc_offset).to eq( -7 * 3600 )
24
+ end
25
+ end
26
+ end
27
+ context 'with extension `dst_enabled_on_overlap:false`' do
28
+ let(:timezone_spec) { 'America/Los_Angeles[dst_enabled_on_overlap:false]' }
29
+ it 'resolves as if DST were disabled' do
30
+ timestamp = timezone.local_time(2021,11,7,1,17)
31
+ aggregate_failures do
32
+ expect(timestamp.dst?).to be false
33
+ expect(timestamp.zone).to eq('PST') # Pacific Standard Time
34
+ expect(timestamp.getutc).to eq(Time.utc(2021,11,7,9,17))
35
+ expect(timestamp.utc_offset).to eq( -8 * 3600 )
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ context '#load' do
42
+ context 'when spec is a normal timezone instance' do
43
+ let(:timezone_spec) { ::TZInfo::Timezone.get('America/Los_Angeles') }
44
+ it 'returns that instance' do
45
+ expect(timezone).to be(timezone_spec)
46
+ end
47
+ end
48
+ context 'when spec is a valid unextended timezone spec' do
49
+ let(:timezone_spec) { 'America/Los_Angeles' }
50
+ it 'returns the canonical timezone' do
51
+ expect(timezone).to eq(::TZInfo::Timezone.get('America/Los_Angeles'))
52
+ end
53
+ end
54
+ context 'when spec is an invalid timezone spec' do
55
+ let(:timezone_spec) { 'NotAValidTimezoneIdentifier' }
56
+
57
+ it 'propagates the TZInfo exception' do
58
+ expect { timezone }.to raise_exception(::TZInfo::InvalidTimezoneIdentifier)
59
+ end
60
+ end
61
+ context 'with invalid extension' do
62
+ let(:timezone_spec) { 'America/Los_Angeles[dst_enabled_on_overlap:false;nope:wrong]' }
63
+ it 'raises an exception with a helpful message' do
64
+ expect { timezone }.to raise_exception(ArgumentError, a_string_including("Invalid timezone extension `nope:wrong`"))
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,113 @@
1
+ # encoding: utf-8
2
+ require "logstash/plugin_mixins/jdbc/value_tracking"
3
+ require "tempfile"
4
+
5
+ module LogStash module PluginMixins module Jdbc
6
+ describe ValueTracking do
7
+ context "#load_yaml" do
8
+
9
+ context "with date string" do
10
+ let(:yaml_date_source) { "--- !ruby/object:DateTime '2023-06-15 09:59:30.558000000 +02:00'\n" }
11
+
12
+ it "should be loaded" do
13
+ parsed_date = LogStash::PluginMixins::Jdbc::ValueTracking.load_yaml(yaml_date_source)
14
+ expect(parsed_date.class).to eq DateTime
15
+ expect(parsed_date.year).to eq 2023
16
+ expect(parsed_date.month).to eq 6
17
+ expect(parsed_date.day).to eq 15
18
+ end
19
+ end
20
+
21
+ context "with time string" do
22
+ let(:yaml_time_source) { "--- 2023-06-15 15:28:15.227874000 +02:00\n" }
23
+
24
+ it "should be loaded" do
25
+ parsed_time = LogStash::PluginMixins::Jdbc::ValueTracking.load_yaml(yaml_time_source)
26
+ expect(parsed_time.class).to eq Time
27
+ expect(parsed_time.year).to eq 2023
28
+ expect(parsed_time.month).to eq 6
29
+ expect(parsed_time.day).to eq 15
30
+ expect(parsed_time.hour).to eq 15
31
+ expect(parsed_time.min).to eq 28
32
+ expect(parsed_time.sec).to eq 15
33
+ end
34
+ end
35
+
36
+ context "with date string" do
37
+ let(:yaml_bigdecimal_source) { "--- !ruby/object:BigDecimal '0:0.1e1'\n" }
38
+
39
+ it "should be loaded" do
40
+ parsed_bigdecimal = LogStash::PluginMixins::Jdbc::ValueTracking.load_yaml(yaml_bigdecimal_source)
41
+ expect(parsed_bigdecimal.class).to eq BigDecimal
42
+ expect(parsed_bigdecimal.to_i).to eq 1
43
+ end
44
+ end
45
+ end
46
+
47
+ context "#build_last_value_tracker" do
48
+
49
+ let(:plugin) { double("fake plugin") }
50
+ let(:temp_file) { Tempfile.new('last_run_tracker') }
51
+
52
+ before(:each) do
53
+ allow(plugin).to receive(:record_last_run).and_return(true)
54
+ allow(plugin).to receive(:clean_run).and_return(false)
55
+ allow(plugin).to receive(:last_run_metadata_file_path).and_return(temp_file.path)
56
+ end
57
+
58
+ context "create numerical tracker" do
59
+ before(:each) do
60
+ allow(plugin).to receive(:use_column_value).and_return(true)
61
+ allow(plugin).to receive(:tracking_column_type).and_return("numeric")
62
+ end
63
+
64
+ it "should write correctly" do
65
+ tracker = ValueTracking.build_last_value_tracker(plugin)
66
+ tracker.set_value(1)
67
+ tracker.write
68
+
69
+ temp_file.rewind
70
+ v = ValueTracking.load_yaml(::File.read(temp_file.path))
71
+ expect(v).to eq 1
72
+ end
73
+ end
74
+
75
+ context "create date time tracker" do
76
+ before(:each) do
77
+ allow(plugin).to receive(:use_column_value).and_return(false)
78
+ allow(plugin).to receive(:jdbc_default_timezone).and_return(:something_not_nil)
79
+ end
80
+
81
+ it "should write correctly" do
82
+ tracker = ValueTracking.build_last_value_tracker(plugin)
83
+ tracker.set_value("2023-06-15T15:28:15+02:00")
84
+ tracker.write
85
+
86
+ temp_file.rewind
87
+ v = ValueTracking.load_yaml(::File.read(temp_file.path))
88
+ expect(v.class).to eq DateTime
89
+ expect(v.year).to eq 2023
90
+ end
91
+ end
92
+
93
+ context "create time tracker" do
94
+ before(:each) do
95
+ allow(plugin).to receive(:use_column_value).and_return(false)
96
+ allow(plugin).to receive(:jdbc_default_timezone).and_return(nil)
97
+ end
98
+
99
+ it "should write correctly" do
100
+ tracker = ValueTracking.build_last_value_tracker(plugin)
101
+ tracker.set_value("2023-06-15T15:28:15+02:00")
102
+ tracker.write
103
+
104
+ temp_file.rewind
105
+ v = ValueTracking.load_yaml(::File.read(temp_file.path))
106
+ expect(v.class).to eq Time
107
+ expect(v.min).to eq 28
108
+ end
109
+ end
110
+
111
+ end
112
+ end
113
+ end end end