Home > Software engineering >  Ruby: parsing over a csv file and expecting some text
Ruby: parsing over a csv file and expecting some text

Time:10-26

I have some logs that need analyzing to check if the logs do not have abnormalities and are incorrect form so to speak.

I have generated a CSV file for it:

"timestamp","source","message
"2021-10-18T09:12:29.000Z","Storage","Storage apache: [18/Oct/2021:09:12:29  0800] 10.102.141.82 - GET /deviceManager/rest/
"2021-10-18T09:12:29.000Z","Storage","Storage apache: [18/Oct/2021:09:12:29  0800] 10.102.141.82 - GET /deviceManager/rest/
"2021-10-18T09:12:29.000Z","Storage","Storage apache: [18/Oct/2021:09:12:29  0800] 10.102.141.82 - GET /deviceManager/rest/
"2021-10-18T09:12:29.000Z","Storage","Storage apache: [18/Oct/2021:09:12:29  0800] 10.102.141.82 - GET /deviceManager/rest/

I use the CSV gem to parse/read this file and use an RSpec test to expect some value/text/time format etc. I have written the code below. It takes the rows from 8 to 12 for example and I want to expect a text called "Huawei" f.e in those rows.

    RSpec.describe "Log parsing" do
    it 'returns the source' do
      table = CSV.read("Messages_result.csv")
      puts arr = table.values_at(8..12)
      arr.each do |rows|
         expect(rows).to include('Huawei')
      end
    end
end

The problem I am getting is it always executes the expect for the first line but I want to parse/iterate through the whole CSV file and should as well show me for each line a result. My expect message will change of course but I just want to check first for a basic text like Huawei. Can somebody please show what I am doing wrong since each do should theoretically go through the complete rows and throw an expectation for each?

CodePudding user response:

You may want to do something like this ?

RSpec.describe "Log parsing" do
  it 'returns the source' do
    CSV.foreach("Messages_result.csv", :headers => true) do |row|
      expect(row.to_h.values).to include("Storage")
    end
  end
end

Update

i want to parse/iterate through the whole csv file and should as well show me for each line a result.

Do you want to use Rspec as a logger? That's not possible, see Add a configuration option to continue on failure.

That said, by modifying a little the code, you can make Rspec check the whole file and show all the rows that fail your expect:

RSpec.describe "Log parsing" do
  CSV.foreach("Messages_result.csv", :headers => true) do |row|
    it 'returns the source' do
      expect(row.to_h.values).to include("Huawei")
    end
  end
end

outputs:

FF

Failures:

  1) Log parsing returns the source
     Failure/Error: expect(row.to_h.values).to include("Huawei")
       expected ["2021-10-18T09:10:29.000Z", "Storage", "Storage apache: [18/Oct/2021:09:10:29  0800] 10.102.141.82 - GET /deviceManager/rest/"] to include "Huawei"
     # ./Messages_result.rb:10:in `block (3 levels) in <main>'

  2) Log parsing returns the source
     Failure/Error: expect(row.to_h.values).to include("Huawei")
       expected ["2021-10-18T09:11:24.000Z", "Storage", "Storage apache: [18/Oct/2021:09:11:24  0800] 10.102.141.82 -...../license/feature HTTP/1.1 python-requests/2.21.0 - - application/json - / gzip, deflate 200 49 0"] to include "Huawei"
     # ./Messages_result.rb:10:in `block (3 levels) in <main>'

...

If you really need to output a message for each line of the CSV then you don't have any other choice than print it yourself. For example:

# get the output stream that Rspec is currently using
ostream = RSpec.configure { |c| c.output_stream }

# define a few colorization helpers
if ostream.tty?
  def red str; "\e[31m#{str}\e[0m"; end
  def green str; "\e[32m#{str}\e[0m"; end
else
  def red str; str; end
  def green str; str; end
end

RSpec.describe "Log parsing" do
  it 'returns the source' do
    ostream.puts
    ostream.puts "  -) Log parsing returns the source - details"
    expected = "Huawei"
    success = true
    CSV.foreach("Messages_result.csv", :headers => true) do |row|
      values = row.to_h.values
      detail = "expected #{values} to include #{expected.inspect}"
      ostream.print ' ' * 5
      if values.include?(expected)
        ostream.puts green("PASSED: #{detail}")
      else
        ostream.puts red("FAILED: #{detail}")
        success = false
      end
    end
    ostream.puts
    ostream.flush
    expect(success).to be(true)
  end
end
  • Related