fluent-plugin-kusto 0.0.1.beta → 0.0.2.beta

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.
@@ -243,7 +243,7 @@ class KustoE2ETest < Test::Unit::TestCase
243
243
  kusto_query(".create table #{table_name} #{@columns}", :management)
244
244
  end
245
245
 
246
- def wait_for_ingestion(query, expected_count, max_wait = 240, interval = 5)
246
+ def wait_for_ingestion(query, expected_count, max_wait = 480, interval = 5)
247
247
  waited = 0
248
248
  rows = []
249
249
 
@@ -339,15 +339,15 @@ class KustoE2ETest < Test::Unit::TestCase
339
339
  [time + 4, { 'id' => 2, 'name' => 'write_test_5' }]
340
340
  ]
341
341
 
342
- @driver.run(default_tag: tag) do
342
+ @driver.run(default_tag: tag, timeout: 300) do # Increase driver timeout to 5 minutes
343
343
  events.each do |t, r|
344
344
  @driver.feed(tag, t, r)
345
345
  end
346
- sleep 5 # Wait for buffer flush
346
+ sleep 8 # Increased wait for buffer flush
347
347
  end
348
348
 
349
349
  query = "#{test_table} | extend r = parse_json(record) | where r.id == 2 and r.name startswith \"write_test_\""
350
- rows = wait_for_ingestion(query, 5)
350
+ rows = wait_for_ingestion(query, 5, 600) # Increased timeout to 10 minutes
351
351
 
352
352
  assert(rows.size >= 5, 'Not all events were ingested into Kusto by write')
353
353
  end
@@ -357,7 +357,8 @@ class KustoE2ETest < Test::Unit::TestCase
357
357
  configure_and_start_driver(
358
358
  table_name: test_table,
359
359
  buffered: true,
360
- delayed: true
360
+ delayed: true,
361
+ deferred_commit_timeout: 120 # Increased timeout for chunk commit verification
361
362
  )
362
363
  setup_test_table(test_table)
363
364
 
@@ -371,15 +372,15 @@ class KustoE2ETest < Test::Unit::TestCase
371
372
  [time + 4, { 'id' => 3, 'name' => 'try_write_test_5' }]
372
373
  ]
373
374
 
374
- @driver.run(default_tag: tag) do
375
+ @driver.run(default_tag: tag, timeout: 300) do # Increase driver timeout to 5 minutes
375
376
  events.each do |t, r|
376
377
  @driver.feed(tag, t, r)
377
378
  end
378
- sleep 5 # Wait for buffer flush
379
+ sleep 10 # Increased wait for buffer flush and delayed commit
379
380
  end
380
381
 
381
382
  query = "#{test_table} | extend r = parse_json(record) | where r.id == 3 and r.name startswith \"try_write_test_\""
382
- rows = wait_for_ingestion(query, 5)
383
+ rows = wait_for_ingestion(query, 5, 600) # Increased timeout to 10 minutes
383
384
 
384
385
  assert(rows.size >= 5, 'Not all events were ingested into Kusto by try_write')
385
386
 
@@ -387,7 +388,7 @@ class KustoE2ETest < Test::Unit::TestCase
387
388
  assert(chunk_id, 'chunk_id not found in ingested records')
388
389
 
389
390
  query_chunk = "#{test_table} | extend r = parse_json(record) | where r.chunk_id == '#{chunk_id}'"
390
- chunk_rows = wait_for_ingestion(query_chunk, 5)
391
+ chunk_rows = wait_for_ingestion(query_chunk, 5, 600) # Increased timeout to 10 minutes
391
392
 
392
393
  assert(chunk_rows.size >= 5, 'Not all chunk records were committed in Kusto by try_write')
393
394
  end
@@ -398,7 +399,8 @@ class KustoE2ETest < Test::Unit::TestCase
398
399
  table_name: test_table,
399
400
  buffered: true,
400
401
  delayed: true,
401
- chunk_limit_size: '256'
402
+ chunk_limit_size: '256',
403
+ deferred_commit_timeout: 120 # Increased timeout for parallel chunk verification
402
404
  )
403
405
  setup_test_table(test_table)
404
406
 
@@ -409,15 +411,15 @@ class KustoE2ETest < Test::Unit::TestCase
409
411
  events << [time + i, { 'id' => 4, 'name' => "try_write_parallel_test_#{i + 1}" }]
410
412
  end
411
413
 
412
- @driver.run(default_tag: tag) do
414
+ @driver.run(default_tag: tag, timeout: 300) do # Increase driver timeout to 5 minutes
413
415
  events.each do |t, r|
414
416
  @driver.feed(tag, t, r)
415
417
  end
416
- sleep 5 # Wait for buffer flush
418
+ sleep 10 # Increased wait for buffer flush and delayed commit
417
419
  end
418
420
 
419
421
  query = "#{test_table} | extend r = parse_json(record) | where r.id == 4 and r.name startswith \"try_write_parallel_test_\""
420
- rows = wait_for_ingestion(query, 10)
422
+ rows = wait_for_ingestion(query, 10, 600) # Increased timeout to 10 minutes
421
423
 
422
424
  assert(rows.size >= 10, 'Not all events were ingested into Kusto by try_write (parallel)')
423
425
 
@@ -428,7 +430,7 @@ class KustoE2ETest < Test::Unit::TestCase
428
430
  chunk_ids.each do |cid|
429
431
  expected_count = rows.count { |row| row[3]['chunk_id'] == cid }
430
432
  query_chunk = "#{test_table} | extend r = parse_json(record) | where r.chunk_id == '#{cid}'"
431
- chunk_rows = wait_for_ingestion(query_chunk, expected_count)
433
+ chunk_rows = wait_for_ingestion(query_chunk, expected_count, 600) # Increased timeout to 10 minutes
432
434
 
433
435
  assert(chunk_rows.size == expected_count,
434
436
  "Not all chunk records were committed in Kusto for chunk_id #{cid} (expected #{expected_count}, got #{chunk_rows.size})")
@@ -475,7 +477,7 @@ class KustoE2ETest < Test::Unit::TestCase
475
477
  tag = 'e2e.memory_buffered.immediate'
476
478
  events = generate_test_events(5, 2000, 'mem_imm')
477
479
 
478
- @driver.run(default_tag: tag) do
480
+ @driver.run(default_tag: tag, timeout: 300) do # Increase driver timeout to 5 minutes
479
481
  events.each do |time, record|
480
482
  @driver.feed(tag, time, record)
481
483
  end
@@ -483,7 +485,7 @@ class KustoE2ETest < Test::Unit::TestCase
483
485
  end
484
486
 
485
487
  query = "#{table_name} | extend r = parse_json(record) | where r.id >= 2000 and r.id <= 2004"
486
- rows = wait_for_ingestion(query, 5)
488
+ rows = wait_for_ingestion(query, 5, 600) # Increased timeout to 10 minutes
487
489
 
488
490
  assert(rows.size >= 5, "Expected 5 records, got #{rows.size} in memory buffered immediate flush")
489
491
  end
@@ -503,15 +505,15 @@ class KustoE2ETest < Test::Unit::TestCase
503
505
  tag = 'e2e.memory_buffered.interval'
504
506
  events = generate_test_events(7, 3000, 'mem_int')
505
507
 
506
- @driver.run(default_tag: tag) do
508
+ @driver.run(default_tag: tag, timeout: 300) do # Increase driver timeout to 5 minutes
507
509
  events.each do |time, record|
508
510
  @driver.feed(tag, time, record)
509
511
  end
510
- sleep 8 # Wait longer than flush_interval
512
+ sleep 10 # Increased wait for buffer flush
511
513
  end
512
514
 
513
515
  query = "#{table_name} | extend r = parse_json(record) | where r.id >= 3000 and r.id <= 3006"
514
- rows = wait_for_ingestion(query, 7)
516
+ rows = wait_for_ingestion(query, 7, 600) # Increased timeout to 10 minutes
515
517
 
516
518
  assert(rows.size >= 7, "Expected 7 records, got #{rows.size} in memory buffered interval flush")
517
519
  end
@@ -543,15 +545,15 @@ class KustoE2ETest < Test::Unit::TestCase
543
545
  ]
544
546
  end
545
547
 
546
- @driver.run(default_tag: tag) do
548
+ @driver.run(default_tag: tag, timeout: 300) do # Increase driver timeout to 5 minutes
547
549
  events.each do |time, record|
548
550
  @driver.feed(tag, time, record)
549
551
  end
550
- sleep 8
552
+ sleep 10 # Increased wait for buffer flush
551
553
  end
552
554
 
553
555
  query = "#{table_name} | extend r = parse_json(record) | where r.id >= 4000 and r.id <= 4009"
554
- rows = wait_for_ingestion(query, 10)
556
+ rows = wait_for_ingestion(query, 10, 600) # Increased timeout to 10 minutes
555
557
 
556
558
  assert(rows.size >= 10, "Expected 10 records, got #{rows.size} in chunk size limit test")
557
559
  end
@@ -563,23 +565,23 @@ class KustoE2ETest < Test::Unit::TestCase
563
565
  table_name: table_name,
564
566
  buffered: true,
565
567
  delayed: true,
566
- flush_interval: '3s',
567
- deferred_commit_timeout: 15
568
+ flush_interval: '5s', # Longer flush interval to reduce buffer pressure
569
+ deferred_commit_timeout: 120 # Increased timeout for chunk commit verification
568
570
  )
569
571
  setup_test_table(table_name)
570
572
 
571
573
  tag = 'e2e.delayed_commit.sync'
572
574
  events = generate_test_events(4, 5000, 'delayed_sync')
573
575
 
574
- @driver.run(default_tag: tag) do
576
+ @driver.run(default_tag: tag, timeout: 300) do # Increase driver timeout to 5 minutes
575
577
  events.each do |time, record|
576
578
  @driver.feed(tag, time, record)
577
579
  end
578
- sleep 8
580
+ sleep 15 # Increased wait for buffer flush and delayed commit
579
581
  end
580
582
 
581
583
  query = "#{table_name} | extend r = parse_json(record) | where r.id >= 5000 and r.id <= 5003"
582
- rows = wait_for_ingestion(query, 4)
584
+ rows = wait_for_ingestion(query, 4, 600) # Increased timeout to 10 minutes
583
585
 
584
586
  assert(rows.size >= 4, "Expected 4 records, got #{rows.size} in delayed commit sync mode")
585
587
 
@@ -595,26 +597,28 @@ class KustoE2ETest < Test::Unit::TestCase
595
597
  table_name: table_name,
596
598
  buffered: true,
597
599
  delayed: true,
598
- chunk_limit_size: '300', # Small chunks to force multiple
599
- flush_interval: '4s',
600
- deferred_commit_timeout: 15
600
+ chunk_limit_size: '1k', # Reasonable size to force multiple chunks without overwhelming buffer
601
+ flush_interval: '6s', # Longer flush interval to reduce buffer pressure
602
+ deferred_commit_timeout: 120, # Increased timeout for chunk commit verification
603
+ flush_mode: 'interval' # Ensure interval-based flushing
601
604
  )
602
605
  setup_test_table(table_name)
603
606
 
604
607
  tag = 'e2e.delayed_commit.multi_chunks'
605
- events = generate_test_events(12, 6000, 'multi_chunk')
608
+ # Reduce number of events to prevent buffer overflow
609
+ events = generate_test_events(8, 6000, 'multi_chunk')
606
610
 
607
- @driver.run(default_tag: tag) do
611
+ @driver.run(default_tag: tag, timeout: 300) do # Increase driver timeout to 5 minutes
608
612
  events.each do |time, record|
609
613
  @driver.feed(tag, time, record)
610
614
  end
611
- sleep 10
615
+ sleep 20 # Increased sleep time for buffer flush and delayed commit
612
616
  end
613
617
 
614
- query = "#{table_name} | extend r = parse_json(record) | where r.id >= 6000 and r.id <= 6011"
615
- rows = wait_for_ingestion(query, 12)
618
+ query = "#{table_name} | extend r = parse_json(record) | where r.id >= 6000 and r.id <= 6007"
619
+ rows = wait_for_ingestion(query, 8, 600) # Increased timeout to 10 minutes
616
620
 
617
- assert(rows.size >= 12, "Expected 12 records, got #{rows.size} in delayed commit multiple chunks")
621
+ assert(rows.size >= 8, "Expected 8 records, got #{rows.size} in delayed commit multiple chunks")
618
622
 
619
623
  # Verify multiple chunk_ids exist
620
624
  chunk_ids = rows.map { |row| row[3]['chunk_id'] if row[3] }.compact.uniq
@@ -638,15 +642,15 @@ class KustoE2ETest < Test::Unit::TestCase
638
642
  tag = 'e2e.file_buffer.persistent'
639
643
  events = generate_test_events(6, 20_000, 'file_buf')
640
644
 
641
- @driver.run(default_tag: tag) do
645
+ @driver.run(default_tag: tag, timeout: 300) do # Increase driver timeout to 5 minutes
642
646
  events.each do |time, record|
643
647
  @driver.feed(tag, time, record)
644
648
  end
645
- sleep 8
649
+ sleep 10 # Increased wait for buffer flush
646
650
  end
647
651
 
648
652
  query = "#{table_name} | extend r = parse_json(record) | where r.id >= 20000 and r.id <= 20005"
649
- rows = wait_for_ingestion(query, 6)
653
+ rows = wait_for_ingestion(query, 6, 600) # Increased timeout to 10 minutes
650
654
 
651
655
  assert(rows.size >= 6, "Expected 6 records, got #{rows.size} in file buffer persistent storage test")
652
656
  end
@@ -666,18 +670,267 @@ class KustoE2ETest < Test::Unit::TestCase
666
670
  tag = 'e2e.buffered.compression'
667
671
  events = generate_test_events(10, 7000, 'compression')
668
672
 
669
- @driver.run(default_tag: tag) do
673
+ @driver.run(default_tag: tag, timeout: 300) do # Increase driver timeout to 5 minutes
670
674
  events.each do |time, record|
671
675
  @driver.feed(tag, time, record)
672
676
  end
673
- sleep 8
677
+ sleep 10 # Increased wait for buffer flush
674
678
  end
675
679
 
676
680
  query = "#{table_name} | extend r = parse_json(record) | where r.id >= 7000 and r.id <= 7009"
677
- rows = wait_for_ingestion(query, 10)
681
+ rows = wait_for_ingestion(query, 10, 600) # Increased timeout to 10 minutes
678
682
 
679
683
  assert(rows.size >= 10, "Expected 10 records, got #{rows.size} in compression test")
680
684
  end
681
685
 
682
686
  # ESSENTIAL E2E BUFFERING TEST CASES - END
687
+
688
+ # INGESTION MAPPING REFERENCE TESTS - START
689
+
690
+ # Test ingestion with mapping reference specified
691
+ test 'ingestion_with_mapping_reference' do
692
+ test_table = "FluentD_mapping_ref_#{Time.now.to_i}"
693
+ mapping_name = "test_mapping_#{Time.now.to_i}"
694
+
695
+ # Create table and mapping
696
+ kusto_query(".drop table #{test_table} ifexists", :management)
697
+ kusto_query(".create table #{test_table} (tag:string, timestamp:datetime, record:dynamic)", :management)
698
+ kusto_query(<<~MAPPING_QUERY, :management)
699
+ .create table #{test_table} ingestion json mapping "#{mapping_name}"
700
+ '[{"column":"tag","path":"$.tag"},{"column":"timestamp","path":"$.timestamp"},{"column":"record","path":"$.record"}]'
701
+ MAPPING_QUERY
702
+
703
+ # Configure driver with mapping reference
704
+ config_options = {
705
+ table_name: test_table,
706
+ buffered: true,
707
+ delayed: true
708
+ }
709
+
710
+ # Add ingestion_mapping_reference if specified
711
+ mapping_config = config_options[:ingestion_mapping_reference] ? "ingestion_mapping_reference #{config_options[:ingestion_mapping_reference]}" : ''
712
+
713
+ @conf = <<-CONF
714
+ @type kusto
715
+ @log_level debug
716
+ buffered #{config_options[:buffered]}
717
+ delayed #{config_options[:delayed]}
718
+ endpoint #{@engine_url}
719
+ database_name #{@database}
720
+ table_name #{config_options[:table_name]}
721
+ compression_enabled true
722
+ ingestion_mapping_reference #{mapping_name}
723
+ #{@auth_lines}
724
+ <buffer>
725
+ @type memory
726
+ chunk_limit_size 8k
727
+ flush_interval 3s
728
+ flush_mode interval
729
+ </buffer>
730
+ CONF
731
+
732
+ @driver = Fluent::Test::Driver::Output.new(Fluent::Plugin::KustoOutput).configure(@conf)
733
+ @driver.instance.instance_variable_set(:@logger, @logger)
734
+ @driver.instance.start
735
+
736
+ tag = 'e2e.mapping_ref'
737
+ events = [
738
+ [Time.now.to_i, { 'id' => 8001, 'name' => 'mapping_test_1', 'type' => 'with_mapping' }],
739
+ [Time.now.to_i + 1, { 'id' => 8002, 'name' => 'mapping_test_2', 'type' => 'with_mapping' }],
740
+ [Time.now.to_i + 2, { 'id' => 8003, 'name' => 'mapping_test_3', 'type' => 'with_mapping' }]
741
+ ]
742
+
743
+ @driver.run(default_tag: tag, timeout: 300) do # Increase driver timeout to 5 minutes
744
+ events.each do |time, record|
745
+ @driver.feed(tag, time, record)
746
+ end
747
+ sleep 10 # Increased wait for buffer flush
748
+ end
749
+
750
+ query = "#{test_table} | extend r = parse_json(record) | where r.id >= 8001 and r.id <= 8003"
751
+ rows = wait_for_ingestion(query, 3, 600) # Increased timeout to 10 minutes
752
+
753
+ assert(rows.size >= 3, "Expected 3 records with mapping reference, got #{rows.size}")
754
+
755
+ # Verify the mapping was used by checking data structure
756
+ found_with_mapping = false
757
+ rows.each do |row|
758
+ r = row[2] # record column should be dynamic
759
+ if r && r['id'] && r['id'] >= 8001 && r['id'] <= 8003
760
+ found_with_mapping = true
761
+ break
762
+ end
763
+ end
764
+
765
+ assert(found_with_mapping, 'Expected records with mapping reference not found')
766
+
767
+ # Clean up mapping
768
+ kusto_query(".drop table #{test_table} ingestion json mapping '#{mapping_name}'", :management)
769
+ end
770
+
771
+ # Test ingestion without mapping reference (default behavior)
772
+ test 'ingestion_without_mapping_reference' do
773
+ test_table = "FluentD_no_mapping_#{Time.now.to_i}"
774
+
775
+ # Create table without specific mapping
776
+ kusto_query(".drop table #{test_table} ifexists", :management)
777
+ kusto_query(".create table #{test_table} (tag:string, timestamp:datetime, record:string)", :management)
778
+
779
+ configure_and_start_driver(
780
+ table_name: test_table,
781
+ buffered: true,
782
+ delayed: false
783
+ # No ingestion_mapping_reference specified
784
+ )
785
+
786
+ tag = 'e2e.no_mapping'
787
+ events = [
788
+ [Time.now.to_i, { 'id' => 9001, 'name' => 'no_mapping_test_1', 'type' => 'default' }],
789
+ [Time.now.to_i + 1, { 'id' => 9002, 'name' => 'no_mapping_test_2', 'type' => 'default' }]
790
+ ]
791
+
792
+ @driver.run(default_tag: tag, timeout: 300) do # Increase driver timeout to 5 minutes
793
+ events.each do |time, record|
794
+ @driver.feed(tag, time, record)
795
+ end
796
+ sleep 8 # Increased wait for buffer flush
797
+ end
798
+
799
+ query = "#{test_table} | extend r = parse_json(record) | where r.id >= 9001 and r.id <= 9002"
800
+ rows = wait_for_ingestion(query, 2, 600) # Increased timeout to 10 minutes
801
+
802
+ assert(rows.size >= 2, "Expected 2 records without mapping reference, got #{rows.size}")
803
+
804
+ # Verify default string serialization was used
805
+ found_default_format = false
806
+ rows.each do |row|
807
+ record_str = row[2] # record column should be string
808
+ if record_str.is_a?(String) && record_str.include?('"id":900')
809
+ found_default_format = true
810
+ break
811
+ end
812
+ end
813
+
814
+ assert(found_default_format, 'Expected default JSON string format not found')
815
+ end
816
+
817
+ # Test ingestion mapping with delayed commit
818
+ test 'ingestion_mapping_with_delayed_commit' do
819
+ test_table = "FluentD_mapping_delayed_#{Time.now.to_i}"
820
+ mapping_name = "delayed_mapping_#{Time.now.to_i}"
821
+
822
+ # Create table and mapping
823
+ kusto_query(".drop table #{test_table} ifexists", :management)
824
+ kusto_query(".create table #{test_table} (tag:string, timestamp:datetime, record:dynamic)", :management)
825
+ kusto_query(<<~MAPPING_QUERY, :management)
826
+ .create table #{test_table} ingestion json mapping "#{mapping_name}"
827
+ '[{"column":"tag","path":"$.tag"},{"column":"timestamp","path":"$.timestamp"},{"column":"record","path":"$.record"}]'
828
+ MAPPING_QUERY
829
+
830
+ # Configure with both mapping reference and delayed commit
831
+ @conf = <<-CONF
832
+ @type kusto
833
+ @log_level debug
834
+ buffered true
835
+ delayed true
836
+ endpoint #{@engine_url}
837
+ database_name #{@database}
838
+ table_name #{test_table}
839
+ compression_enabled true
840
+ ingestion_mapping_reference #{mapping_name}
841
+ deferred_commit_timeout 120
842
+ #{@auth_lines}
843
+ <buffer>
844
+ @type memory
845
+ chunk_limit_size 4k
846
+ flush_interval 5s
847
+ flush_mode interval
848
+ </buffer>
849
+ CONF
850
+
851
+ @driver = Fluent::Test::Driver::Output.new(Fluent::Plugin::KustoOutput).configure(@conf)
852
+ @driver.instance.instance_variable_set(:@logger, @logger)
853
+ @driver.instance.start
854
+
855
+ tag = 'e2e.mapping_delayed'
856
+ events = [
857
+ [Time.now.to_i, { 'id' => 10001, 'name' => 'delayed_mapping_1', 'type' => 'delayed_with_mapping' }],
858
+ [Time.now.to_i + 1, { 'id' => 10002, 'name' => 'delayed_mapping_2', 'type' => 'delayed_with_mapping' }],
859
+ [Time.now.to_i + 2, { 'id' => 10003, 'name' => 'delayed_mapping_3', 'type' => 'delayed_with_mapping' }]
860
+ ]
861
+
862
+ @driver.run(default_tag: tag, timeout: 300) do # Increase driver timeout to 5 minutes
863
+ events.each do |time, record|
864
+ @driver.feed(tag, time, record)
865
+ end
866
+ sleep 12 # Increased wait for buffer flush and delayed commit
867
+ end
868
+
869
+ query = "#{test_table} | extend r = parse_json(record) | where r.id >= 10001 and r.id <= 10003"
870
+ rows = wait_for_ingestion(query, 3, 600) # Increased timeout to 10 minutes
871
+
872
+ assert(rows.size >= 3, "Expected 3 records with mapping and delayed commit, got #{rows.size}")
873
+
874
+ # Verify chunk_id exists (from delayed commit) and mapping was applied
875
+ chunk_ids = []
876
+ mapped_records = 0
877
+
878
+ rows.each do |row|
879
+ r = row[2] # record column
880
+ if r && r['chunk_id'] # Should have chunk_id from delayed commit
881
+ chunk_ids << r['chunk_id']
882
+ end
883
+ if r && r['id'] && r['id'] >= 10001 && r['id'] <= 10003
884
+ mapped_records += 1
885
+ end
886
+ end
887
+
888
+ assert(chunk_ids.uniq.size >= 1, 'Expected chunk_ids from delayed commit not found')
889
+ assert(mapped_records >= 3, 'Expected mapped records not found')
890
+
891
+ # Clean up mapping
892
+ kusto_query(".drop table #{test_table} ingestion json mapping '#{mapping_name}'", :management)
893
+ end
894
+
895
+ # Test configuration validation for ingestion_mapping_reference
896
+ test 'ingestion_mapping_reference_configuration' do
897
+ test_table = "FluentD_config_test_#{Time.now.to_i}"
898
+ setup_test_table(test_table)
899
+
900
+ # Test that plugin accepts ingestion_mapping_reference parameter
901
+ config_with_mapping = <<-CONF
902
+ @type kusto
903
+ buffered false
904
+ endpoint #{@engine_url}
905
+ database_name #{@database}
906
+ table_name #{test_table}
907
+ ingestion_mapping_reference test_mapping_name
908
+ #{@auth_lines}
909
+ CONF
910
+
911
+ driver_with_mapping = nil
912
+ assert_nothing_raised('Configuration with ingestion_mapping_reference should be valid') do
913
+ driver_with_mapping = Fluent::Test::Driver::Output.new(Fluent::Plugin::KustoOutput).configure(config_with_mapping)
914
+ end
915
+
916
+ # Verify the parameter is accessible
917
+ plugin_instance = driver_with_mapping.instance
918
+ assert_respond_to(plugin_instance, :ingestion_mapping_reference, 'Plugin should respond to ingestion_mapping_reference')
919
+
920
+ # Test without mapping reference
921
+ config_without_mapping = <<-CONF
922
+ @type kusto
923
+ buffered false
924
+ endpoint #{@engine_url}
925
+ database_name #{@database}
926
+ table_name #{test_table}
927
+ #{@auth_lines}
928
+ CONF
929
+
930
+ assert_nothing_raised('Configuration without ingestion_mapping_reference should be valid') do
931
+ Fluent::Test::Driver::Output.new(Fluent::Plugin::KustoOutput).configure(config_without_mapping)
932
+ end
933
+ end
934
+
935
+ # INGESTION MAPPING REFERENCE TESTS - END
683
936
  end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test/unit'
4
+ require 'mocha/test_unit'
5
+ require_relative '../../lib/fluent/plugin/auth/mi_tokenprovider'
6
+
7
+ class DummyConfigForMI
8
+ attr_reader :kusto_endpoint, :managed_identity_client_id
9
+
10
+ def initialize(kusto_endpoint, managed_identity_client_id = nil)
11
+ @kusto_endpoint = kusto_endpoint
12
+ @managed_identity_client_id = managed_identity_client_id
13
+ end
14
+
15
+ def logger
16
+ require 'logger'
17
+ Logger.new($stdout)
18
+ end
19
+ end
20
+
21
+ class ManagedIdentityTokenProviderTest < Test::Unit::TestCase
22
+ def setup
23
+ @resource = 'https://kusto.kusto.windows.net'
24
+ @client_id = '074c3c54-29e2-4230-a81f-333868b8d6ca'
25
+ end
26
+
27
+ def test_initialize_with_user_managed_identity
28
+ config = DummyConfigForMI.new(@resource, @client_id)
29
+ provider = ManagedIdentityTokenProvider.new(config)
30
+
31
+ # Verify instance variables are set correctly
32
+ assert_equal @resource, provider.instance_variable_get(:@resource)
33
+ assert_equal @client_id, provider.instance_variable_get(:@managed_identity_client_id)
34
+ assert_equal false, provider.instance_variable_get(:@use_system_assigned)
35
+ assert_equal true, provider.instance_variable_get(:@use_user_assigned)
36
+ assert_not_nil provider.instance_variable_get(:@token_acquire_url)
37
+ end
38
+
39
+ def test_initialize_with_system_managed_identity
40
+ config = DummyConfigForMI.new(@resource, 'SYSTEM')
41
+ provider = ManagedIdentityTokenProvider.new(config)
42
+
43
+ # Verify instance variables are set correctly for system managed identity
44
+ assert_equal @resource, provider.instance_variable_get(:@resource)
45
+ assert_equal 'SYSTEM', provider.instance_variable_get(:@managed_identity_client_id)
46
+ assert_equal true, provider.instance_variable_get(:@use_system_assigned)
47
+ assert_equal false, provider.instance_variable_get(:@use_user_assigned)
48
+ assert_not_nil provider.instance_variable_get(:@token_acquire_url)
49
+ end
50
+
51
+ def test_initialize_with_empty_client_id
52
+ config = DummyConfigForMI.new(@resource, '')
53
+ provider = ManagedIdentityTokenProvider.new(config)
54
+
55
+ # Verify instance variables are set correctly for empty client_id
56
+ assert_equal @resource, provider.instance_variable_get(:@resource)
57
+ assert_equal '', provider.instance_variable_get(:@managed_identity_client_id)
58
+ assert_equal false, provider.instance_variable_get(:@use_system_assigned)
59
+ assert_equal false, provider.instance_variable_get(:@use_user_assigned)
60
+ end
61
+
62
+ def test_token_acquire_url_formation_user_managed_identity
63
+ config = DummyConfigForMI.new(@resource, @client_id)
64
+ provider = ManagedIdentityTokenProvider.new(config)
65
+
66
+ token_url = provider.instance_variable_get(:@token_acquire_url)
67
+ expected_base = 'http://169.254.169.254/metadata/identity/oauth2/token'
68
+
69
+ assert token_url.include?(expected_base), "Token URL should contain base IMDS endpoint"
70
+ assert token_url.include?('resource=https%3A%2F%2Fkusto.kusto.windows.net'), "Token URL should contain encoded resource"
71
+ assert token_url.include?('api-version=2018-02-01'), "Token URL should contain API version"
72
+ assert token_url.include?("client_id=#{@client_id}"), "Token URL should contain client_id for user managed identity"
73
+ end
74
+
75
+ def test_token_acquire_url_formation_system_managed_identity
76
+ config = DummyConfigForMI.new(@resource, 'SYSTEM')
77
+ provider = ManagedIdentityTokenProvider.new(config)
78
+
79
+ token_url = provider.instance_variable_get(:@token_acquire_url)
80
+ expected_base = 'http://169.254.169.254/metadata/identity/oauth2/token'
81
+
82
+ assert token_url.include?(expected_base), "Token URL should contain base IMDS endpoint"
83
+ assert token_url.include?('resource=https%3A%2F%2Fkusto.kusto.windows.net'), "Token URL should contain encoded resource"
84
+ assert token_url.include?('api-version=2018-02-01'), "Token URL should contain API version"
85
+ assert !token_url.include?('client_id='), "Token URL should NOT contain client_id for system managed identity"
86
+ end
87
+
88
+ def test_fetch_token_success
89
+ config = DummyConfigForMI.new(@resource, @client_id)
90
+ provider = ManagedIdentityTokenProvider.new(config)
91
+
92
+ # Mock successful HTTP response
93
+ mock_response = {
94
+ 'access_token' => 'fake-access-token',
95
+ 'expires_in' => 3600
96
+ }
97
+
98
+ provider.stubs(:post_token_request).returns(mock_response)
99
+
100
+ result = provider.send(:fetch_token)
101
+ assert_equal 'fake-access-token', result[:access_token]
102
+ assert_equal 3600, result[:expires_in]
103
+ end
104
+
105
+ def test_post_token_request_retries_on_failure
106
+ config = DummyConfigForMI.new(@resource, @client_id)
107
+ provider = ManagedIdentityTokenProvider.new(config)
108
+
109
+ # Mock HTTP failure followed by success
110
+ mock_http = mock
111
+ mock_response_fail = mock
112
+ mock_response_fail.stubs(:code).returns(500)
113
+ mock_response_fail.stubs(:body).returns('Internal Server Error')
114
+
115
+ mock_response_success = mock
116
+ mock_response_success.stubs(:code).returns(200)
117
+ mock_response_success.stubs(:body).returns('{"access_token":"fake-token","expires_in":3600}')
118
+
119
+ mock_request = mock
120
+ Net::HTTP::Get.stubs(:new).returns(mock_request)
121
+
122
+ mock_http.expects(:request).twice.returns(mock_response_fail, mock_response_success)
123
+ Net::HTTP.stubs(:new).returns(mock_http)
124
+
125
+ # Stub sleep to speed up test
126
+ provider.stubs(:sleep)
127
+
128
+ result = provider.send(:post_token_request)
129
+ assert_equal 'fake-token', result['access_token']
130
+ end
131
+
132
+ def test_post_token_request_raises_after_max_retries
133
+ config = DummyConfigForMI.new(@resource, @client_id)
134
+ provider = ManagedIdentityTokenProvider.new(config)
135
+
136
+ # Mock HTTP failure for all attempts
137
+ mock_http = mock
138
+ mock_response_fail = mock
139
+ mock_response_fail.stubs(:code).returns(500)
140
+ mock_response_fail.stubs(:body).returns('Internal Server Error')
141
+
142
+ mock_request = mock
143
+ Net::HTTP::Get.stubs(:new).returns(mock_request)
144
+
145
+ mock_http.stubs(:request).returns(mock_response_fail)
146
+ Net::HTTP.stubs(:new).returns(mock_http)
147
+
148
+ # Stub sleep to speed up test
149
+ provider.stubs(:sleep)
150
+
151
+ assert_raise(RuntimeError, 'Failed to get managed identity token after 2 attempts.') do
152
+ provider.send(:post_token_request)
153
+ end
154
+ end
155
+ end