From 0bd7d98c03c998815768be6002ccaba307d6e9db Mon Sep 17 00:00:00 2001 From: Martin Hradil Date: Tue, 7 Feb 2017 14:04:13 +0000 Subject: [PATCH] Move report_formatter specs from manageiq the formatters themselves already live here --- spec/lib/report_formater/c3_formatter_spec.rb | 95 +++++++++ spec/lib/report_formater/chart_common_spec.rb | 54 +++++ .../jqplot_formater_charting_counts_spec.rb | 77 +++++++ spec/lib/report_formater/timeline_spec.rb | 193 ++++++++++++++++++ 4 files changed, 419 insertions(+) create mode 100644 spec/lib/report_formater/c3_formatter_spec.rb create mode 100644 spec/lib/report_formater/chart_common_spec.rb create mode 100644 spec/lib/report_formater/jqplot_formater_charting_counts_spec.rb create mode 100644 spec/lib/report_formater/timeline_spec.rb diff --git a/spec/lib/report_formater/c3_formatter_spec.rb b/spec/lib/report_formater/c3_formatter_spec.rb new file mode 100644 index 00000000000..cc56c407bae --- /dev/null +++ b/spec/lib/report_formater/c3_formatter_spec.rb @@ -0,0 +1,95 @@ +describe ReportFormatter::C3Formatter do + include Spec::Support::ReportHelper + + before(:each) do + allow(Charting).to receive(:backend).and_return(:c3) + allow(Charting).to receive(:format).and_return(:c3) + end + + describe "#add_series" do + it "does not raise error for 'stack' chart" do + report = numeric_chart_3d(true) + expect { render_report(report) }.to_not raise_error + end + end + + context '#build_numeric_chart_grouped' do + [true, false].each do |other| + it "builds 2d numeric charts from summaries #{other ? 'with' : 'without'} 'other'" do + report = numeric_charts_2d_from_summaries(other) + + expect_any_instance_of(described_class).to receive(:build_numeric_chart_grouped).once.and_call_original + render_report(report) + expect(report.chart[:data][:columns][0][1]).to eq(4.0) + expect(report.chart[:data][:columns][0][-1]).to eq(4) if other + end + end + end + + context '#build_numeric_chart_simple' do + let(:report) { numeric_chart_simple } + let(:long_report) { numeric_chart_simple_with_long_strings } + + it "report chart have right data in ascending order" do + report.col_formats = [nil, :general_number_precision_0] + render_report(report) + expect(report.chart[:data][:columns][0].count).to eq(report.table.data.count + 1) + expect(report.chart[:data][:columns][0][1]).to eq(2024) + end + + it "handles null data in chart column" do + report = null_data_chart + + expect_any_instance_of(described_class).to receive(:build_numeric_chart_simple).once.and_call_original + render_report(report) + end + + it "handle long strings" do + render_report(long_report) + expect(long_report.chart[:miq][:category_table][2]).to eq(long_category) + expect(long_report.chart[:miq][:name_table]['1']).to eq('RAM Size (MB)') + end + end + + context '#build_numeric_chart_simple' do + [true, false].each do |other| + it "builds 2d numeric charts #{other ? 'with' : 'without'} 'other'" do + report = numeric_chart_simple2(other) + + expect_any_instance_of(described_class).to receive(:build_numeric_chart_simple).once.and_call_original + render_report(report) + expect(report.chart[:data][:columns][0][1]).to eq(15) + expect(report.chart[:data][:columns][0][-1]).to eq(1) if other + end + end + + it "handles null data in chart column" do + report = null_data_chart + + expect_any_instance_of(described_class).to receive(:build_numeric_chart_simple).once.and_call_original + render_report(report) + end + end + + context '#build_numeric_chart_grouped_2dim' do + [true, false].each do |other| + it "builds 3d numeric charts #{other ? 'with' : 'without'} 'other'" do + report = numeric_chart_3d(other) + + expect_any_instance_of(described_class).to receive(:build_numeric_chart_grouped_2dim).once.and_call_original + render_report(report) + expect(report.chart[:data][:columns][0][1]).to eq(6_656) + expect(report.chart[:data][:columns][0][2]).to eq(4_096) + expect(report.chart[:data][:columns][1][1]).to eq(1_024) + expect(report.chart[:data][:columns][0][-1]).to eq(1_024) if other + end + end + + it 'handles namespace-prefixed class names in chart column' do + report = chart_with_namespace_prefix + + expect_any_instance_of(described_class).to receive(:build_numeric_chart_grouped_2dim).once.and_call_original + render_report(report) + end + end +end diff --git a/spec/lib/report_formater/chart_common_spec.rb b/spec/lib/report_formater/chart_common_spec.rb new file mode 100644 index 00000000000..748332855c6 --- /dev/null +++ b/spec/lib/report_formater/chart_common_spec.rb @@ -0,0 +1,54 @@ +# describe ReportFormatter::ChartCommon do +# +# We have to operate on the specific class although we are testing the common behavior. +# Otherwise expect_any_instance_of(described_class).to receive(:build_performance_chart_area).once.and_call_original +# leads to with: +# SystemStackError: +# stack level too deep +# # ./lib/report_formatter/chart_common.rb:555:in `build_performance_chart' +# # ./lib/report_formatter/chart_common.rb:57:in `call' + +describe ReportFormatter::C3Formatter do + include Spec::Support::ReportHelper + + before(:each) do + allow(Charting).to receive(:backend).and_return(:c3) + allow(Charting).to receive(:format).and_return(:c3) + end + context '#build_performance_chart_area' do + it "builds a daily chart with all nils" do + report = MiqReport.new( + :db => "VimPerformanceDaily", + :cols => cols = %w(timestamp cpu_usagemhz_rate_average min_cpu_usagemhz_rate_average max_cpu_usagemhz_rate_average trend_max_cpu_usagemhz_rate_average resource.cpu_usagemhz_rate_average_high_over_time_period resource.cpu_usagemhz_rate_average_low_over_time_period), + :include => { + "resource" => { + "columns" => %w(cpu_usagemhz_rate_average_high_over_time_period cpu_usagemhz_rate_average_low_over_time_period derived_memory_used_high_over_time_period derived_memory_used_low_over_time_period), + } + }, + :col_order => cols, + :headers => ["Date/Time", "Avg Used", "Max Available", "Max Reserved", "Trend Max Used", "foo", "bar"], + :order => "ascending", + :sortby => "timestamp", + :group => "n", + :graph => { + :type => "Line", + :columns => %w(cpu_usagemhz_rate_average min_cpu_usagemhz_rate_average max_cpu_usagemhz_rate_average trend_max_cpu_usagemhz_rate_average resource.cpu_usagemhz_rate_average_high_over_time_period resource.cpu_usagemhz_rate_average_low_over_time_period), + :legends => nil, + :max_col_size => nil + }, + :dims => nil, + :col_formats => nil, + :col_options => nil, + :rpt_options => nil, + ) + + report.table = Ruport::Data::Table.new( + :column_names => %w(timestamp cpu_usagemhz_rate_average min_cpu_usagemhz_rate_average max_cpu_usagemhz_rate_average trend_max_cpu_usagemhz_rate_average), + :data => [["Sun, 20 Mar 2016 00:00:00 UTC +00:00", 0.0, nil, nil, 0]]) + + expect_any_instance_of(described_class).to receive(:build_performance_chart_area).once.and_call_original + render_report(report) { |e| e.options.graph_options[:chart_type] = :performance } + expect(report.chart[:data]).to be + end + end +end diff --git a/spec/lib/report_formater/jqplot_formater_charting_counts_spec.rb b/spec/lib/report_formater/jqplot_formater_charting_counts_spec.rb new file mode 100644 index 00000000000..74a9b588da4 --- /dev/null +++ b/spec/lib/report_formater/jqplot_formater_charting_counts_spec.rb @@ -0,0 +1,77 @@ +describe ReportFormatter::JqplotFormatter do + include Spec::Support::ReportHelper + + before(:each) do + allow(Charting).to receive(:backend).and_return(:jqplot) + allow(Charting).to receive(:format).and_return(:jqplot) + end + context '#build_reporting_chart_dim2' do + it 'builds a stacked chart' do + report = MiqReport.new( + :db => "Vm", + :cols => %w(os_image_name), + :include => {"ext_management_system" => {"columns" => ["name"]}}, + :col_order => ["ext_management_system.name", "os_image_name"], + :headers => ["Cloud/Infrastructure Provider Name", "OS Name"], + :order => "Ascending", + :group => nil, + :graph => {:type => "StackedBar", :mode => "counts", :column => nil, :count => 10, :other => false}, + :dims => 2, + :col_options => {}, + :rpt_options => {}, + :sortby => %w(ext_management_system.name os_image_name) + ) + + report.table = Ruport::Data::Table.new( + :column_names => %w(os_image_name ext_management_system.name id), + :data => [ + %w(linux_centos MTC-RHEVM-3.0 10000000000012), + %w(linux_centos MTC-RHEVM-3.0 10000000000013), + %w(linux_redhat MTC-RHEVM-3.1 10000000000014), + %w(linux_centos MTC-RHEVM-3.1 10000000000015), + ] + ) + + expect_any_instance_of(described_class).to receive(:build_reporting_chart_dim2).once.and_call_original + render_report(report) + expect(report.chart[:data]).to eq([[2, 1], [0, 1]]) + expect(report.chart[:options][:seriesDefaults][:renderer]).to eq("jQuery.jqplot.BarRenderer") + expect(report.chart[:options][:series]).to eq([{:label => "linux_centos"}, {:label => "linux_redhat"}]) + end + end + + context "#build_reporting_chart_other" do + it 'builds a pie chart' do + report = MiqReport.new( + :db => "Host", + :cols => %w(os_image_name), + :include => {}, + :col_order => ["os_image_name"], + :headers => ["OS Name"], + :order => "Ascending", + :sortby => ["os_image_name"], + :group => nil, + :graph => {:type => "Pie", :mode => "counts", :column => nil, :count => 10, :other => true}, + :dims => 1, + :col_options => {}, + :rpt_options => {}, + ) + + report.table = Ruport::Data::Table.new( + :column_names => %w(os_image_name id), + :data => [ + ["linux_esx", 5], + ["linux_esx", 6], + ["linux_esx", 7], + ["widloze", 8], + ] + ) + + expect_any_instance_of(described_class).to receive(:build_reporting_chart_other).once.and_call_original + render_report(report) + expect(report.chart[:data][0]).to eq([["linux_esx: 3", 3], ["widloze: 1", 1]]) + expect(report.chart[:options][:seriesDefaults][:renderer]).to eq("jQuery.jqplot.PieRenderer") + expect(report.chart[:options][:highlighter]).to be_truthy + end + end +end diff --git a/spec/lib/report_formater/timeline_spec.rb b/spec/lib/report_formater/timeline_spec.rb new file mode 100644 index 00000000000..498b75608da --- /dev/null +++ b/spec/lib/report_formater/timeline_spec.rb @@ -0,0 +1,193 @@ +describe ReportFormatter::ReportTimeline do + context '#bubble_icon' do + def stub_bottleneck_event(resource_type, ems_type = nil) + bottleneck_event = BottleneckEvent.create!(:resource_type => resource_type) + unless ems_type.nil? + ems = FactoryGirl.create(:ems_redhat) + allow(ems).to receive(:emstype).and_return(ems_type) + allow(bottleneck_event).to receive(:resource).and_return(ems) + end + bottleneck_event + end + + it 'shows a generic icon for MiqEnterprise' do + expect(ReportFormatter::ReportTimeline.new.bubble_icon(stub_bottleneck_event(MiqEnterprise))).to eq('enterprise') + end + + it 'shows a generic icon for EmsCluster' do + expect(ReportFormatter::ReportTimeline.new.bubble_icon(stub_bottleneck_event(EmsCluster))).to eq('cluster') + end + + it 'shows a generic icon for ExtManagementSystem' do + expect(ReportFormatter::ReportTimeline.new.bubble_icon(stub_bottleneck_event(ExtManagementSystem))).to eq('ems') + end + + it 'shows a Red Hat logo for RHEVM EMS' do + expect(ReportFormatter::ReportTimeline.new.bubble_icon(stub_bottleneck_event(ExtManagementSystem, 'rhevm'))).to eq('vendor-redhat') + end + end +end + +describe ReportFormatter::TimelineMessage do + describe '#message_html on container event' do + row = {} + let(:ems) { FactoryGirl.create(:ems_redhat, :id => 42) } + let(:event) do + FactoryGirl.create(:ems_event, + :event_type => 'CONTAINER_CREATED', + :ems_id => 6, + :container_group_name => 'hawkular-cassandra-1-wb1z6', + :container_namespace => 'openshift-infra', + :container_name => 'hawkular-cassandra-1', + :ext_management_system => ems) + end + + flags = {:ems_cloud => false, + :ems_container => true, + :time_zone => nil} + tests = {'event_type' => 'test timeline', + 'ext_management_system.name' => 'test timeline', + 'container_node_name' => ''} + + tests.each do |column, href| + it "Evaluate column #{column} content" do + row[column] = 'test timeline' + val = ReportFormatter::TimelineMessage.new(row, event, flags, 'EmsEvent').message_html(column) + expect(val).to eq(href) + end + end + end + + describe '#message_html on vm event' do + row = {} + let(:vm) { FactoryGirl.create(:vm_redhat, :id => 42) } + let(:event) do + FactoryGirl.create(:ems_event, + :event_type => 'VM_CREATED', + :vm_or_template => vm) + end + + flags = {:ems_cloud => false, + :ems_container => false, + :time_zone => nil} + tests = {'event_type' => 'test timeline', + 'ext_management_system.name' => '', + 'src_vm_name' => 'test timeline'} + + tests.each do |column, href| + it "Evaluate column #{column} content" do + row[column] = 'test timeline' + val = ReportFormatter::TimelineMessage.new(row, event, flags, 'EmsEvent').message_html(column) + expect(val).to eq(href) + end + end + end + + describe '#message_html on bottleneck event' do + row = {} + let(:ems_cluster) { FactoryGirl.create(:ems_cluster, :id => 42, :name => 'Test Cluster') } + let(:event) do + FactoryGirl.create(:bottleneck_event, + :event_type => 'MemoryUsage', + :resource_id => 42, + :resource_name => ems_cluster.name, + :resource_type => 'EmsCluster') + end + + tests = {'event_type' => 'MemoryUsage', + 'resource_name' => 'Test Cluster'} + + tests.each do |column, href| + it "Evaluate column #{column} content" do + row[column] = 'MemoryUsage' + val = ReportFormatter::TimelineMessage.new(row, event, {}, 'BottleneckEvent').message_html(column) + expect(val).to eq(href) + end + end + end + + describe '#message_html on policy event' do + row = {} + let(:vm) { FactoryGirl.create(:vm_redhat, :id => 42, :name => 'Test VM') } + let(:event) do + FactoryGirl.create(:policy_event, + :event_type => 'vm_poweroff', + :target_id => 42, + :target_name => vm.name, + :target_class => 'VmOrTemplate') + end + + tests = {'event_type' => 'vm_poweroff', + 'target_name' => 'Test VM
VM or Template: Test VM
Assigned Profiles: '} + + tests.each do |column, href| + it "Evaluate column #{column} content" do + row[column] = 'vm_poweroff' + val = ReportFormatter::TimelineMessage.new(row, event, {}, 'PolicyEvent').message_html(column) + expect(val).to eq(href) + end + end + end + + describe '#events count for different categories' do + def stub_ems_event(event_type) + ems = FactoryGirl.create(:ems_redhat) + ems_event = EventStream.create!(:event_type => event_type, :ems_id => ems.id) + ems_event + end + + before do + @report = FactoryGirl.create(:miq_report, + :db => "EventStream", + :col_order => %w(id name event_type timestamp), + :headers => %w(id name event_type timestamp), + :timeline => {:field => "EmsEvent-timestamp", :position => "Last"}) + @report.rpt_options = {:categories => {:power => {:display_name => "Power Activity", + :event_groups => %w(VmPoweredOffEvent VmPoweredOnEvent)}, + :snapshot => {:display_name => "Snapshot Activity", + :event_groups => %w(AlarmCreatedEvent AlarmRemovedEvent)}} + } + + data = [] + 30.times do + data.push(Ruport::Data::Record.new("id" => stub_ems_event("VmPoweredOffEvent").id, + "name" => "Baz", + "event_type" => "VmPoweredOffEvent", + "timestamp" => Time.zone.now)) + end + + 15.times do + data.push(Ruport::Data::Record.new("id" => stub_ems_event("AlarmCreatedEvent").id, + "name" => "Baz", + "event_type" => "AlarmCreatedEvent", + "timestamp" => Time.zone.now)) + end + + @report.table = Ruport::Data::Table.new( + :column_names => %w(id name event_type timestamp), + :data => data + ) + end + + it 'shows correct count of timeline events based on categories' do + allow_any_instance_of(Ruport::Controller::Options).to receive(:mri).and_return(@report) + events = ReportFormatter::ReportTimeline.new.build_document_body + expect(JSON.parse(events)[0]["data"][0].length).to eq(30) + expect(JSON.parse(events)[1]["data"][0].length).to eq(15) + end + + it 'shows correct count of timeline events together for report object with no categories' do + @report.rpt_options = {} + allow_any_instance_of(Ruport::Controller::Options).to receive(:mri).and_return(@report) + events = ReportFormatter::ReportTimeline.new.build_document_body + expect(JSON.parse(events)[0]["data"][0].length).to eq(45) + end + + it 'shows correct count of timeline events for timeline based report when rpt_options is nil' do + @report.rpt_options = nil + allow_any_instance_of(Ruport::Controller::Options).to receive(:mri).and_return(@report) + events = ReportFormatter::ReportTimeline.new.build_document_body + expect(JSON.parse(events)[0]["data"][0].length).to eq(45) + end + end +end