request-log-analyzer 1.2.1 → 1.2.3

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.
Files changed (35) hide show
  1. data/Rakefile +1 -1
  2. data/lib/request_log_analyzer/aggregator/database.rb +59 -35
  3. data/lib/request_log_analyzer/controller.rb +12 -7
  4. data/lib/request_log_analyzer/line_definition.rb +9 -0
  5. data/lib/request_log_analyzer/source/log_parser.rb +4 -0
  6. data/spec/fixtures/header_and_footer.log +6 -0
  7. data/spec/integration/command_line_usage_spec.rb +0 -2
  8. data/spec/lib/{helper.rb → helpers.rb} +1 -3
  9. data/spec/lib/macros.rb +2 -0
  10. data/spec/lib/matchers.rb +63 -0
  11. data/spec/lib/testing_format.rb +6 -0
  12. data/spec/spec.opts +3 -0
  13. data/spec/spec_helper.rb +13 -4
  14. data/spec/unit/aggregator/database_spec.rb +208 -0
  15. data/spec/unit/aggregator/summarizer_spec.rb +0 -2
  16. data/spec/unit/controller/controller_spec.rb +0 -2
  17. data/spec/unit/controller/log_processor_spec.rb +0 -2
  18. data/spec/unit/file_format/file_format_api_spec.rb +50 -71
  19. data/spec/unit/file_format/line_definition_spec.rb +49 -45
  20. data/spec/unit/file_format/merb_format_spec.rb +0 -1
  21. data/spec/unit/file_format/rails_format_spec.rb +0 -2
  22. data/spec/unit/filter/anonymize_filter_spec.rb +0 -1
  23. data/spec/unit/filter/field_filter_spec.rb +0 -3
  24. data/spec/unit/filter/filter_spec.rb +17 -0
  25. data/spec/unit/filter/timespan_filter_spec.rb +0 -3
  26. data/spec/unit/source/log_parser_spec.rb +5 -4
  27. data/spec/unit/source/request_spec.rb +17 -4
  28. data/spec/unit/tracker/duration_tracker_spec.rb +0 -6
  29. data/spec/unit/tracker/frequency_tracker_spec.rb +0 -6
  30. data/spec/unit/tracker/hourly_spread_spec.rb +0 -4
  31. data/spec/unit/tracker/timespan_tracker_spec.rb +0 -4
  32. data/spec/unit/tracker/tracker_api_spec.rb +0 -2
  33. data/tasks/rspec.rake +8 -1
  34. metadata +12 -6
  35. data/spec/unit/aggregator/database_inserter_spec.rb +0 -106
data/Rakefile CHANGED
@@ -1,3 +1,3 @@
1
1
  Dir[File.dirname(__FILE__) + "/tasks/*.rake"].each { |file| load(file) }
2
2
 
3
- task :default => :spec
3
+ task :default => "spec:fancy"
@@ -16,10 +16,13 @@ module RequestLogAnalyzer::Aggregator
16
16
  # created to log all parse warnings.
17
17
  class Database < Base
18
18
 
19
+ attr_reader :request_class, :request_count, :orm_module, :warning_class
20
+
19
21
  # Establishes a connection to the database and creates the necessary database schema for the
20
22
  # current file format
21
23
  def prepare
22
- ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => options[:database])
24
+ initialize_orm_module!
25
+ establish_database_connection!
23
26
  File.unlink(options[:database]) if File.exist?(options[:database]) # TODO: keep old database?
24
27
  create_database_schema!
25
28
  end
@@ -28,9 +31,9 @@ module RequestLogAnalyzer::Aggregator
28
31
  # This will create a record in the requests table and create a record for every line that has been parsed,
29
32
  # in which the captured values will be stored.
30
33
  def aggregate(request)
31
- @request_object = @request_class.new(:first_lineno => request.first_lineno, :last_lineno => request.last_lineno)
34
+ @request_object = request_class.new(:first_lineno => request.first_lineno, :last_lineno => request.last_lineno)
32
35
  request.lines.each do |line|
33
- class_columns = @orm_module.const_get("#{line[:line_type]}_line".classify).column_names.reject { |column| ['id'].include?(column) }
36
+ class_columns = orm_module.const_get("#{line[:line_type]}_line".classify).column_names.reject { |column| ['id'].include?(column) }
34
37
  attributes = Hash[*line.select { |(k, v)| class_columns.include?(k.to_s) }.flatten]
35
38
  @request_object.send("#{line[:line_type]}_lines").build(attributes)
36
39
  end
@@ -41,13 +44,13 @@ module RequestLogAnalyzer::Aggregator
41
44
 
42
45
  # Finalizes the aggregator by closing the connection to the database
43
46
  def finalize
44
- @request_count = @orm_module::Request.count
45
- ActiveRecord::Base.remove_connection
47
+ @request_count = orm_module::Request.count
48
+ remove_database_connection!
46
49
  end
47
50
 
48
51
  # Records w warining in the warnings table.
49
52
  def warning(type, message, lineno)
50
- @orm_module::Warning.create!(:warning_type => type.to_s, :message => message, :lineno => lineno)
53
+ warning_class.create!(:warning_type => type.to_s, :message => message, :lineno => lineno)
51
54
  end
52
55
 
53
56
  # Prints a short report of what has been inserted into the database
@@ -63,46 +66,72 @@ module RequestLogAnalyzer::Aggregator
63
66
 
64
67
  protected
65
68
 
69
+ # Create a module and a default subclass of ActiveRecord::Base on which to establish the database connection
70
+ def initialize_orm_module!
71
+
72
+ # Create a Database module in the file format if it does not yet exists
73
+ file_format.class.const_set('Database', Module.new) unless file_format.class.const_defined?('Database')
74
+ @orm_module = file_format.class.const_get('Database')
75
+
76
+ # Register the base activerecord class
77
+ unless orm_module.const_defined?('Base')
78
+ orm_base_class = Class.new(ActiveRecord::Base)
79
+ orm_base_class.abstract_class = true
80
+ orm_module.const_set('Base', orm_base_class)
81
+ end
82
+ end
83
+
84
+ # Established a connection with the database for this session
85
+ def establish_database_connection!
86
+ orm_module::Base.establish_connection(:adapter => 'sqlite3', :database => options[:database])
87
+ ActiveRecord::Migration.class_eval("def self.connection; #{@orm_module.to_s}::Base.connection; end ")
88
+ end
89
+
90
+ def remove_database_connection!
91
+ ActiveRecord::Migration.class_eval("def self.connection; ActiveRecord::Base.connection; end ")
92
+ orm_module::Base.remove_connection
93
+ end
94
+
66
95
  # This function creates a database table for a given line definition.
67
96
  # It will create a field for every capture in the line, and adds a lineno field to indicate at
68
97
  # what line in the original file the line was found, and a request_id to link lines related
69
98
  # to the same request. It will also create an index in the request_id field to speed up queries.
70
- def create_database_table(name, definition)
71
- ActiveRecord::Migration.verbose = options[:debug]
72
- ActiveRecord::Migration.create_table("#{name}_lines") do |t|
99
+ def create_database_table(definition)
100
+ ActiveRecord::Migration.verbose = options[:debug] # keep it down a notch please
101
+ ActiveRecord::Migration.create_table("#{definition.name}_lines") do |t|
102
+
103
+ # Add default fields for every line type
73
104
  t.column(:request_id, :integer)
74
105
  t.column(:lineno, :integer)
106
+
75
107
  definition.captures.each do |capture|
76
-
77
108
  # Add a field for every capture
78
109
  t.column(capture[:name], column_type(capture[:type]))
79
-
80
- # If the capture provides other field as well, create them too
81
- if capture[:provides].kind_of?(Hash)
82
- capture[:provides].each do |field, field_type|
83
- t.column(field, column_type(field_type))
84
- end
85
- end
110
+
111
+ # If the capture provides other field as well, create columns for them, too
112
+ capture[:provides].each { |field, field_type| t.column(field, column_type(field_type)) } if capture[:provides].kind_of?(Hash)
86
113
  end
87
114
  end
88
- ActiveRecord::Migration.add_index("#{name}_lines", [:request_id])
115
+
116
+ # Create an index on the request_id column to support faster querying
117
+ ActiveRecord::Migration.add_index("#{definition.name}_lines", [:request_id])
89
118
  end
90
119
 
91
120
  # Creates an ActiveRecord class for a given line definition.
92
121
  # A subclass of ActiveRecord::Base is created and an association with the Request class is
93
122
  # created using belongs_to / has_many. This association will later be used to create records
94
123
  # in the corresponding table. This table should already be created before this method is called.
95
- def create_activerecord_class(name, definition)
96
- class_name = "#{name}_line".camelize
97
- klass = Class.new(ActiveRecord::Base)
124
+ def create_activerecord_class(definition)
125
+ class_name = "#{definition.name}_line".camelize
126
+ klass = Class.new(orm_module::Base)
98
127
  klass.send(:belongs_to, :request)
99
128
 
100
129
  definition.captures.each do |capture|
101
130
  klass.send(:serialize, capture[:name], Hash) if capture[:provides]
102
131
  end
103
132
 
104
- @orm_module.const_set(class_name, klass) unless @orm_module.const_defined?(class_name)
105
- @request_class.send(:has_many, "#{name}_lines".to_sym)
133
+ orm_module.const_set(class_name, klass) unless orm_module.const_defined?(class_name)
134
+ request_class.send(:has_many, "#{definition.name}_lines".to_sym)
106
135
  end
107
136
 
108
137
  # Creates a requests table, in which a record is created for every request. It also creates an
@@ -114,8 +143,8 @@ module RequestLogAnalyzer::Aggregator
114
143
  t.integer :last_lineno
115
144
  end
116
145
 
117
- @orm_module.const_set('Request', Class.new(ActiveRecord::Base)) unless @orm_module.const_defined?('Request')
118
- @request_class = @orm_module.const_get('Request')
146
+ orm_module.const_set('Request', Class.new(orm_module::Base)) unless orm_module.const_defined?('Request')
147
+ @request_class = orm_module.const_get('Request')
119
148
  end
120
149
 
121
150
  # Creates a warnings table and a corresponding Warning class to communicate with this table using ActiveRecord.
@@ -127,25 +156,19 @@ module RequestLogAnalyzer::Aggregator
127
156
  t.integer :lineno
128
157
  end
129
158
 
130
- @orm_module.const_set('Warning', Class.new(ActiveRecord::Base)) unless @orm_module.const_defined?('Warning')
159
+ orm_module.const_set('Warning', Class.new(orm_module::Base)) unless orm_module.const_defined?('Warning')
160
+ @warning_class = orm_module.const_get('Warning')
131
161
  end
132
162
 
133
163
  # Creates the database schema and related ActiveRecord::Base subclasses that correspond to the
134
164
  # file format definition. These ORM classes will later be used to create records in the database.
135
165
  def create_database_schema!
136
-
137
- if file_format.class.const_defined?('Database')
138
- @orm_module = file_format.class.const_get('Database')
139
- else
140
- @orm_module = file_format.class.const_set('Database', Module.new)
141
- end
142
-
143
166
  create_request_table_and_class
144
167
  create_warning_table_and_class
145
168
 
146
169
  file_format.line_definitions.each do |name, definition|
147
- create_database_table(name, definition)
148
- create_activerecord_class(name, definition)
170
+ create_database_table(definition)
171
+ create_activerecord_class(definition)
149
172
  end
150
173
  end
151
174
 
@@ -154,6 +177,7 @@ module RequestLogAnalyzer::Aggregator
154
177
  def column_type(type_indicator)
155
178
  case type_indicator
156
179
  when :eval; :text
180
+ when :hash; :text
157
181
  when :text; :text
158
182
  when :string; :string
159
183
  when :sec; :double
@@ -191,14 +191,11 @@ module RequestLogAnalyzer
191
191
  def run!
192
192
 
193
193
  @aggregators.each { |agg| agg.prepare }
194
+ install_signal_handlers
194
195
 
195
- begin
196
- @source.each_request do |request|
197
- aggregate_request(filter_request(request))
198
- end
199
- rescue Interrupt => e
200
- handle_progress(:interrupted)
201
- puts "Caught interrupt! Stopped parsing."
196
+ @source.each_request do |request|
197
+ aggregate_request(filter_request(request))
198
+ break if @interrupted
202
199
  end
203
200
 
204
201
  @aggregators.each { |agg| agg.finalize }
@@ -219,5 +216,13 @@ module RequestLogAnalyzer
219
216
  end
220
217
  end
221
218
 
219
+ def install_signal_handlers
220
+ Signal.trap("INT") do
221
+ handle_progress(:interrupted)
222
+ puts "Caught interrupt! Stopping parsing..."
223
+ @interrupted = true
224
+ end
225
+ end
226
+
222
227
  end
223
228
  end
@@ -66,6 +66,8 @@ module RequestLogAnalyzer
66
66
 
67
67
  alias :=~ :matches
68
68
 
69
+ # matches the line and converts the captured values using the request's
70
+ # convert_value function.
69
71
  def match_for(line, request, lineno = nil, parser = nil)
70
72
  if match_info = matches(line, lineno, parser)
71
73
  convert_captured_values(match_info[:captures], request)
@@ -74,11 +76,18 @@ module RequestLogAnalyzer
74
76
  end
75
77
  end
76
78
 
79
+ # Updates a captures hash using the converters specified in the request
80
+ # and handle the :provides option in the line definition.
77
81
  def convert_captured_values(values, request)
78
82
  value_hash = {}
79
83
  captures.each_with_index do |capture, index|
84
+
85
+ # convert the value using the request convert_value function
80
86
  converted = request.convert_value(values[index], capture)
81
87
  value_hash[capture[:name]] ||= converted
88
+
89
+ # Add items directly to the resulting hash from the converted value
90
+ # if it is a hash and they are set in the :provides hash for this line definition
82
91
  if converted.kind_of?(Hash) && capture[:provides].kind_of?(Hash)
83
92
  capture[:provides].each do |name, type|
84
93
  value_hash[name] ||= request.convert_value(converted[name], { :type => type })
@@ -207,6 +207,10 @@ module RequestLogAnalyzer::Source
207
207
  warn(:unclosed_request, "Encountered header line (#{request_data[:line_definition].name.inspect}), but previous request was not closed!")
208
208
  @current_request = nil # remove all data that was parsed, skip next request as well.
209
209
  end
210
+ if footer_line?(request_data)
211
+ handle_request(@current_request, &block) # yield @current_request
212
+ @current_request = nil
213
+ end
210
214
  else
211
215
  @current_request = @file_format.request(request_data)
212
216
  end
@@ -0,0 +1,6 @@
1
+ start!
2
+ this is a header and footer line
3
+ blah
4
+
5
+ this is a header and footer line
6
+ blah
@@ -2,8 +2,6 @@ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
2
 
3
3
  describe RequestLogAnalyzer, 'running from command line' do
4
4
 
5
- include RequestLogAnalyzer::Spec::Helper
6
-
7
5
  before(:each) do
8
6
  cleanup_temp_files!
9
7
  end
@@ -1,7 +1,5 @@
1
- module RequestLogAnalyzer::Spec::Helper
1
+ module RequestLogAnalyzer::Spec::Helpers
2
2
 
3
- include RequestLogAnalyzer::Spec::Mocks
4
-
5
3
  # Create or return a new TestingFormat
6
4
  def testing_format
7
5
  @testing_format ||= TestingFormat.new
@@ -0,0 +1,2 @@
1
+ module RequestLogAnalyzer::Spec::Macros
2
+ end
@@ -0,0 +1,63 @@
1
+ module RequestLogAnalyzer::Spec::Matchers
2
+
3
+ class HasLineDefinition
4
+
5
+ def initialize(line_type)
6
+ @line_type = line_type.to_sym
7
+ @captures = []
8
+ end
9
+
10
+ def capturing(*captures)
11
+ @captures += captures
12
+ return self
13
+ end
14
+
15
+ def matches?(file_format)
16
+ if file_format.new.line_definitions.include?(@line_type)
17
+ ld = file_format.new.line_definitions[@line_type]
18
+ @captures.all? { |c| ld.captures.include?(c) }
19
+ else
20
+ false
21
+ end
22
+ end
23
+ end
24
+
25
+ class Parse
26
+ def initialize(line)
27
+ @line = line
28
+ @captures = nil
29
+ end
30
+
31
+ def failure_message
32
+ message = "expected to parse the provided line"
33
+ if @found_captures
34
+ message << "with captures #{@captures.inspect}, but captured #{@found_captures.inspect}"
35
+ end
36
+ return message
37
+ end
38
+
39
+ def capturing(*captures)
40
+ @captures = captures
41
+ return self
42
+ end
43
+
44
+ def matches?(line_definition)
45
+ hash = line_definition.matches(@line)
46
+ if hash
47
+ @found_captures = hash[:captures].to_a
48
+ @captures.nil? ? true : @found_captures.eql?(@captures)
49
+ else
50
+ return false
51
+ end
52
+ end
53
+ end
54
+
55
+ def have_line_definition(line_type)
56
+ return HasLineDefinition.new(line_type)
57
+ end
58
+
59
+ def parse(line)
60
+ Parse.new(line)
61
+ end
62
+
63
+ end
@@ -27,6 +27,12 @@ class TestingFormat < RequestLogAnalyzer::FileFormat::Base
27
27
  line.captures = [{ :name => :request_no, :type => :integer }]
28
28
  end
29
29
 
30
+ format_definition.combined do |line|
31
+ line.header = true
32
+ line.footer = true
33
+ line.regexp = /this is a header and footer line/
34
+ end
35
+
30
36
  report do |analyze|
31
37
  analyze.frequency :test_capture, :title => 'What is testing exactly?'
32
38
  end
@@ -0,0 +1,3 @@
1
+ --colour
2
+ --format specdoc
3
+ --reverse
@@ -2,14 +2,23 @@ $:.reject! { |e| e.include? 'TextMate' }
2
2
  $: << File.join(File.dirname(__FILE__), '..', 'lib')
3
3
 
4
4
  require 'rubygems'
5
- require 'spec'
5
+ require 'spec/autorun'
6
6
  require 'request_log_analyzer'
7
7
 
8
8
  module RequestLogAnalyzer::Spec
9
9
  end
10
10
 
11
- require File.dirname(__FILE__) + '/lib/testing_format'
12
- require File.dirname(__FILE__) + '/lib/mocks'
13
- require File.dirname(__FILE__) + '/lib/helper'
11
+ # Include all files in the spec_helper directory
12
+ Dir[File.dirname(__FILE__) + "/lib/**/*.rb"].each do |file|
13
+ require file
14
+ end
14
15
 
15
16
  Dir.mkdir("#{File.dirname(__FILE__)}/../tmp") unless File.exist?("#{File.dirname(__FILE__)}/../tmp")
17
+
18
+ Spec::Runner.configure do |config|
19
+ config.include RequestLogAnalyzer::Spec::Matchers
20
+ config.include RequestLogAnalyzer::Spec::Mocks
21
+ config.include RequestLogAnalyzer::Spec::Helpers
22
+
23
+ config.extend RequestLogAnalyzer::Spec::Macros
24
+ end
@@ -0,0 +1,208 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe RequestLogAnalyzer::Aggregator::Database do
4
+
5
+ before(:each) do
6
+ log_parser = RequestLogAnalyzer::Source::LogParser.new(testing_format)
7
+ @database_inserter = RequestLogAnalyzer::Aggregator::Database.new(log_parser, :database => ':memory:')
8
+
9
+ @line_definition = RequestLogAnalyzer::LineDefinition.new(:test, { :regexp => /Testing (\w+), tries\: (\d+)/,
10
+ :captures => [{ :name => :what, :type => :string }, { :name => :tries, :type => :integer },
11
+ { :name => :evaluated, :type => :hash, :provides => {:evaluated_field => :duration} }]})
12
+ end
13
+
14
+ # The prepare method is called before the parsing starts. It should establish a connection
15
+ # to a database that is suitable for inserting requests later on.
16
+ describe '#prepare' do
17
+
18
+ before(:each) do
19
+ @database_inserter.stub!(:establish_database_connection!)
20
+ @database_inserter.stub!(:create_database_schema!)
21
+ end
22
+
23
+ it 'should establish the database connection' do
24
+ @database_inserter.should_receive(:establish_database_connection!)
25
+ @database_inserter.prepare
26
+ end
27
+
28
+ it 'should create the database schema during preparation' do
29
+ @database_inserter.should_receive(:create_database_schema!)
30
+ @database_inserter.prepare
31
+ end
32
+ end
33
+
34
+ # The create_database_table method should create a database table according to the line definition,
35
+ # so that parsed lines can be stored in it later on.
36
+ describe '#create_database_table' do
37
+
38
+ before(:each) do
39
+ @table_creator = mock('create_table block')
40
+ @table_creator.stub!(:column)
41
+ ActiveRecord::Migration.stub!(:create_table).and_yield(@table_creator)
42
+ ActiveRecord::Migration.stub!(:add_index)
43
+ end
44
+
45
+ it "should create a table based on the line type name" do
46
+ ActiveRecord::Migration.should_receive(:create_table).with('test_lines')
47
+ @database_inserter.send(:create_database_table, @line_definition)
48
+ end
49
+
50
+ it "should create an index on the request_id field" do
51
+ ActiveRecord::Migration.should_receive(:add_index).with('test_lines', [:request_id])
52
+ @database_inserter.send(:create_database_table, @line_definition)
53
+ end
54
+
55
+ it "should create a request_id field to link the requests together" do
56
+ @table_creator.should_receive(:column).with(:request_id, :integer)
57
+ @database_inserter.send(:create_database_table, @line_definition)
58
+ end
59
+
60
+ it "should create a lineno field to save the location of the line in the original file" do
61
+ @table_creator.should_receive(:column).with(:lineno, :integer)
62
+ @database_inserter.send(:create_database_table, @line_definition)
63
+ end
64
+
65
+ it "should create a field of the correct type for every defined field" do
66
+ @table_creator.should_receive(:column).with(:what, :string)
67
+ @table_creator.should_receive(:column).with(:tries, :integer)
68
+ # :hash capture type should map on a :text field type
69
+ @table_creator.should_receive(:column).with(:evaluated, :text)
70
+ @database_inserter.send(:create_database_table, @line_definition)
71
+ end
72
+
73
+ it "should create a field of the correct type for every provided field" do
74
+ # :duration capture type should map on a :double field type
75
+ @table_creator.should_receive(:column).with(:evaluated_field, :double)
76
+ @database_inserter.send(:create_database_table, @line_definition)
77
+ end
78
+ end
79
+
80
+ describe '#create_activerecord_class' do
81
+ before(:each) do
82
+ # Make sure the ORM module exists
83
+ @database_inserter.send(:initialize_orm_module!)
84
+
85
+ # Mockthe request ORM class
86
+ @request_class = mock('Request ActiveRecord::Base class')
87
+ @request_class.stub!(:has_many)
88
+ @database_inserter.stub!(:request_class).and_return(@request_class)
89
+ end
90
+
91
+ it "should register the constant for the line type AR class" do
92
+ @database_inserter.send(:create_activerecord_class, @line_definition)
93
+ @database_inserter.orm_module.const_defined?('TestLine').should be_true
94
+ end
95
+
96
+ it "should create a class that inherits from ActiveRecord::Base and the base class of the ORM module" do
97
+ @database_inserter.send(:create_activerecord_class, @line_definition)
98
+ @database_inserter.orm_module.const_get('TestLine').ancestors.should include(ActiveRecord::Base, @database_inserter.orm_module::Base)
99
+ end
100
+
101
+ describe 'defining the new ORM class' do
102
+ before(:each) do
103
+ # Mock the newly created ORM class for the test_line
104
+ @orm_class = mock('Line ActiveRecord::Base class')
105
+ @orm_class.stub!(:belongs_to)
106
+ @orm_class.stub!(:serialize)
107
+ Class.stub!(:new).and_return(@orm_class)
108
+ end
109
+
110
+ it "should create a has_many relation on the Request class" do
111
+ @request_class.should_receive(:has_many).with(:test_lines)
112
+ @database_inserter.send(:create_activerecord_class, @line_definition)
113
+ end
114
+
115
+ it "should create a belongs_to relation to the request class" do
116
+ @orm_class.should_receive(:belongs_to).with(:request)
117
+ @database_inserter.send(:create_activerecord_class, @line_definition)
118
+ end
119
+
120
+ it "should serialize the :evaluate field into the database" do
121
+ @orm_class.should_receive(:serialize).with(:evaluated, Hash)
122
+ @database_inserter.send(:create_activerecord_class, @line_definition)
123
+ end
124
+ end
125
+ end
126
+
127
+ describe '#create_database_schema!' do
128
+
129
+ before(:each) do
130
+ @line_type_cnt = testing_format.line_definitions.length
131
+
132
+ #Make sure no actual migrations occur
133
+ ActiveRecord::Migration.stub!(:add_index)
134
+ ActiveRecord::Migration.stub!(:create_table)
135
+
136
+ # Stub the expected method calls for the preparation
137
+ @database_inserter.stub!(:create_database_table)
138
+ @database_inserter.stub!(:create_activerecord_class)
139
+
140
+
141
+ # Make sure the ORM module exists
142
+ @database_inserter.send(:initialize_orm_module!)
143
+ end
144
+
145
+ it "should create a requests table to join request lines" do
146
+ ActiveRecord::Migration.should_receive(:create_table).with("requests")
147
+ @database_inserter.send :create_database_schema!
148
+ end
149
+
150
+ it "should create a Request class inheriting from ActiveRecord and the base class of the ORM module" do
151
+ @database_inserter.send :create_database_schema!
152
+ @database_inserter.request_class.ancestors.should include(ActiveRecord::Base, @database_inserter.orm_module::Base)
153
+ end
154
+
155
+ it "should create a warnings table for logging parse warnings" do
156
+ ActiveRecord::Migration.should_receive(:create_table).with("warnings")
157
+ @database_inserter.send :create_database_schema!
158
+ end
159
+
160
+ it "should create a Warnng class inheriting from ActiveRecord and the base class of the ORM module" do
161
+ @database_inserter.send :create_database_schema!
162
+ @database_inserter.warning_class.ancestors.should include(ActiveRecord::Base, @database_inserter.orm_module::Base)
163
+ end
164
+
165
+ it "should create a table for every line type" do
166
+ @database_inserter.should_receive(:create_database_table).with(an_instance_of(RequestLogAnalyzer::LineDefinition)).exactly(@line_type_cnt).times
167
+ @database_inserter.send :create_database_schema!
168
+ end
169
+
170
+ it "should create a ORM for every line type" do
171
+ @database_inserter.should_receive(:create_activerecord_class).with(an_instance_of(RequestLogAnalyzer::LineDefinition)).exactly(@line_type_cnt).times
172
+ @database_inserter.send :create_database_schema!
173
+ end
174
+ end
175
+
176
+ describe '#aggregate' do
177
+ before(:each) do
178
+ @database_inserter.prepare
179
+
180
+ @incomplete_request = testing_format.request( {:line_type => :first, :request_no => 564})
181
+ @completed_request = testing_format.request( {:line_type => :first, :request_no => 564},
182
+ {:line_type => :test, :test_capture => "awesome"},
183
+ {:line_type => :test, :test_capture => "indeed"},
184
+ {:line_type => :eval, :evaluated => { :greating => 'howdy'}, :greating => 'howdy' },
185
+ {:line_type => :last, :request_no => 564})
186
+ end
187
+
188
+ it "should insert a record in the request table" do
189
+ TestingFormat::Database::Request.count.should == 0
190
+ @database_inserter.aggregate(@incomplete_request)
191
+ TestingFormat::Database::Request.count.should == 1
192
+ end
193
+
194
+ it "should insert records in all relevant line tables" do
195
+ @database_inserter.aggregate(@completed_request)
196
+ request = TestingFormat::Database::Request.first
197
+ request.should have(2).test_lines
198
+ request.should have(1).first_lines
199
+ request.should have(1).eval_lines
200
+ request.should have(1).last_lines
201
+ end
202
+
203
+ it "should log a warning in the warnings table" do
204
+ TestingFormat::Database::Warning.should_receive(:create!).with(hash_including(:warning_type => 'test_warning'))
205
+ @database_inserter.warning(:test_warning, "Testing the warning system", 12)
206
+ end
207
+ end
208
+ end