123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- require 'rails_helper'
- describe Agents::CsvAgent do
- before(:each) do
- @valid_params = {
- 'mode' => 'parse',
- 'separator' => ',',
- 'use_fields' => '',
- 'output' => 'event_per_row',
- 'with_header' => 'true',
- 'data_path' => '$.data',
- 'data_key' => 'data'
- }
- @checker = Agents::CsvAgent.new(:name => 'somename', :options => @valid_params)
- @checker.user = users(:jane)
- @checker.save!
- @lfa = Agents::LocalFileAgent.new(name: 'local', options: {path: '{{}}', watch: 'false', append: 'false', mode: 'read'})
- @lfa.user = users(:jane)
- @lfa.save!
- end
- it_behaves_like 'FileHandlingConsumer'
- context '#validate_options' do
- it 'is valid with the given options' do
- expect(@checker).to be_valid
- end
- it "requires with_header to be either 'true' or 'false'" do
- @checker.options['with_header'] = 'true'
- expect(@checker).to be_valid
- @checker.options['with_header'] = 'false'
- expect(@checker).to be_valid
- @checker.options['with_header'] = 'test'
- expect(@checker).not_to be_valid
- end
- it "data_path has to be set in serialize mode" do
- @checker.options['mode'] = 'serialize'
- @checker.options['data_path'] = ''
- expect(@checker).not_to be_valid
- end
- end
- context '#working' do
- it 'is not working without having received an event' do
- expect(@checker).not_to be_working
- end
- it 'is working after receiving an event without error' do
- @checker.last_receive_at = Time.now
- expect(@checker).to be_working
- end
- end
- context '#receive' do
- after(:all) do
- FileUtils.rm(File.join(Rails.root, 'tmp', 'csv'))
- end
- def event_with_contents(contents)
- path = File.join(Rails.root, 'tmp', 'csv')
- File.open(path, 'w') do |f|
- f.write(contents)
- end
- Event.new(payload: { 'file_pointer' => {'agent_id' => @lfa.id, 'file' => path } }, user_id: @checker.user_id)
- end
- context "agent options" do
- let(:with_headers) { event_with_contents("one,two\n1,2\n2,3") }
- let(:without_headers) { event_with_contents("1,2\n2,3") }
- context "output" do
- it "creates one event per row" do
- @checker.options['output'] = 'event_per_row'
- expect { @checker.receive([with_headers]) }.to change(Event, :count).by(2)
- expect(Event.last.payload).to eq(@checker.options['data_key'] => {'one' => '2', 'two' => '3'})
- end
- it "creates one event per file" do
- @checker.options['output'] = 'event_per_file'
- expect { @checker.receive([with_headers]) }.to change(Event, :count).by(1)
- expect(Event.last.payload).to eq(@checker.options['data_key'] => [{"one"=>"1", "two"=>"2"}, {"one"=>"2", "two"=>"3"}])
- end
- end
- context "with_header" do
- it "works without headers" do
- @checker.options['with_header'] = 'false'
- expect { @checker.receive([without_headers]) }.to change(Event, :count).by(2)
- expect(Event.last.payload).to eq({@checker.options['data_key']=>["2", "3"]})
- end
- it "works without headers and event_per_file" do
- @checker.options['with_header'] = 'false'
- @checker.options['output'] = 'event_per_file'
- expect { @checker.receive([without_headers]) }.to change(Event, :count).by(1)
- expect(Event.last.payload).to eq({@checker.options['data_key']=>[['1', '2'], ["2", "3"]]})
- end
- end
- context "use_fields" do
- it "extracts the specified columns" do
- @checker.options['use_fields'] = 'one'
- expect { @checker.receive([with_headers]) }.to change(Event, :count).by(2)
- expect(Event.last.payload).to eq(@checker.options['data_key'] => {'one' => '2'})
- end
- end
- context "data_path" do
- it "can receive the CSV via a regular event" do
- @checker.options['data_path'] = '$.data'
- event = Event.new(payload: {'data' => "one,two\r\n1,2\r\n2,3"})
- expect { @checker.receive([event]) }.to change(Event, :count).by(2)
- expect(Event.last.payload).to eq(@checker.options['data_key'] => {'one' => '2', 'two' => '3'})
- end
- end
- end
- context "handling different CSV formats" do
- it "works with windows line endings" do
- event = event_with_contents("one,two\r\n1,2\r\n2,3")
- expect { @checker.receive([event]) }.to change(Event, :count).by(2)
- expect(Event.last.payload).to eq(@checker.options['data_key'] => {'one' => '2', 'two' => '3'})
- end
- it "works with OSX line endings" do
- event = event_with_contents("one,two\r1,2\r2,3")
- expect { @checker.receive([event]) }.to change(Event, :count).by(2)
- expect(Event.last.payload).to eq(@checker.options['data_key'] => {'one' => '2', 'two' => '3'})
- end
- it "handles quotes correctly" do
- event = event_with_contents("\"one\",\"two\"\n1,2\n\"\"\"2, two\",3")
- expect { @checker.receive([event]) }.to change(Event, :count).by(2)
- expect(Event.last.payload).to eq(@checker.options['data_key'] => {'one' => '"2, two', 'two' => '3'})
- end
- it "works with tab seperated csv" do
- event = event_with_contents("one\ttwo\r\n1\t2\r\n2\t3")
- @checker.options['separator'] = '\\t'
- expect { @checker.receive([event]) }.to change(Event, :count).by(2)
- expect(Event.last.payload).to eq(@checker.options['data_key'] => {'one' => '2', 'two' => '3'})
- end
- end
- context "serializing" do
- before(:each) do
- @checker.options['mode'] = 'serialize'
- @checker.options['data_path'] = '$.data'
- @checker.options['data_key'] = 'data'
- end
- it "writes headers when with_header is true" do
- event = Event.new(payload: { 'data' => {'key' => 'value', 'key2' => 'value2', 'key3' => 'value3'} })
- expect { @checker.receive([event])}.to change(Event, :count).by(1)
- expect(Event.last.payload).to eq('data' => "\"key\",\"key2\",\"key3\"\n\"value\",\"value2\",\"value3\"\n")
- end
- it "writes one row per received event" do
- event = Event.new(payload: { 'data' => {'key' => 'value', 'key2' => 'value2', 'key3' => 'value3'} })
- event2 = Event.new(payload: { 'data' => {'key' => '2value', 'key2' => '2value2', 'key3' => '2value3'} })
- expect { @checker.receive([event, event2])}.to change(Event, :count).by(1)
- expect(Event.last.payload).to eq('data' => "\"key\",\"key2\",\"key3\"\n\"value\",\"value2\",\"value3\"\n\"2value\",\"2value2\",\"2value3\"\n")
- end
- it "accepts multiple rows per event" do
- event = Event.new(payload: { 'data' => [{'key' => 'value', 'key2' => 'value2', 'key3' => 'value3'}, {'key' => '2value', 'key2' => '2value2', 'key3' => '2value3'}] })
- expect { @checker.receive([event])}.to change(Event, :count).by(1)
- expect(Event.last.payload).to eq('data' => "\"key\",\"key2\",\"key3\"\n\"value\",\"value2\",\"value3\"\n\"2value\",\"2value2\",\"2value3\"\n")
- end
- it "does not write the headers when with_header is false" do
- @checker.options['with_header'] = 'false'
- event = Event.new(payload: { 'data' => {'key' => 'value', 'key2' => 'value2', 'key3' => 'value3'} })
- expect { @checker.receive([event])}.to change(Event, :count).by(1)
- expect(Event.last.payload).to eq('data' => "\"value\",\"value2\",\"value3\"\n")
- end
- it "only serialize the keys specified in use_fields" do
- @checker.options['use_fields'] = 'key2, key3'
- event = Event.new(payload: { 'data' => {'key' => 'value', 'key2' => 'value2', 'key3' => 'value3'} })
- expect { @checker.receive([event])}.to change(Event, :count).by(1)
- expect(Event.last.payload).to eq('data' => "\"key2\",\"key3\"\n\"value2\",\"value3\"\n")
- end
- it "respects the order of use_fields" do
- @checker.options['use_fields'] = 'key3, key'
- event = Event.new(payload: { 'data' => {'key' => 'value', 'key2' => 'value2', 'key3' => 'value3'} })
- expect { @checker.receive([event])}.to change(Event, :count).by(1)
- expect(Event.last.payload).to eq('data' => "\"key3\",\"key\"\n\"value3\",\"value\"\n")
- end
- it "respects use_fields and writes no header" do
- @checker.options['with_header'] = 'false'
- @checker.options['use_fields'] = 'key2, key3'
- event = Event.new(payload: { 'data' => {'key' => 'value', 'key2' => 'value2', 'key3' => 'value3'} })
- expect { @checker.receive([event])}.to change(Event, :count).by(1)
- expect(Event.last.payload).to eq('data' => "\"value2\",\"value3\"\n")
- end
- context "arrays" do
- it "does not write a header" do
- @checker.options['with_header'] = 'false'
- event = Event.new(payload: { 'data' => ['value1', 'value2'] })
- event2 = Event.new(payload: { 'data' => ['value3', 'value4'] })
- expect { @checker.receive([event, event2])}.to change(Event, :count).by(1)
- expect(Event.last.payload).to eq('data' => "\"value1\",\"value2\"\n\"value3\",\"value4\"\n")
- end
- it "handles nested arrays" do
- event = Event.new(payload: { 'data' => [['value1', 'value2'], ['value3', 'value4']] })
- expect { @checker.receive([event])}.to change(Event, :count).by(1)
- expect(Event.last.payload).to eq('data' => "\"value1\",\"value2\"\n\"value3\",\"value4\"\n")
- end
- end
- end
- end
- context '#event_description' do
- it "works with event_per_row and headers" do
- @checker.options['output'] = 'event_per_row'
- @checker.options['with_header'] = 'true'
- description = @checker.event_description
- expect(description).not_to match(/\n\s+\[\n/)
- expect(description).to include(": {\n")
- end
- it "works with event_per_file and without headers" do
- @checker.options['output'] = 'event_per_file'
- @checker.options['with_header'] = 'false'
- description = @checker.event_description
- expect(description).to match(/\n\s+\[\n/)
- expect(description).not_to include(": {\n")
- end
- it "shows dummy CSV when in serialize mode" do
- @checker.options['mode'] = 'serialize'
- description = @checker.event_description
- expect(description).to include('"generated\",\"csv')
- end
- end
- end
|