request-log-analyzer 1.2.1 → 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/lib/request_log_analyzer/aggregator/database.rb +59 -35
- data/lib/request_log_analyzer/controller.rb +12 -7
- data/lib/request_log_analyzer/line_definition.rb +9 -0
- data/lib/request_log_analyzer/source/log_parser.rb +4 -0
- data/spec/fixtures/header_and_footer.log +6 -0
- data/spec/integration/command_line_usage_spec.rb +0 -2
- data/spec/lib/{helper.rb → helpers.rb} +1 -3
- data/spec/lib/macros.rb +2 -0
- data/spec/lib/matchers.rb +63 -0
- data/spec/lib/testing_format.rb +6 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +13 -4
- data/spec/unit/aggregator/database_spec.rb +208 -0
- data/spec/unit/aggregator/summarizer_spec.rb +0 -2
- data/spec/unit/controller/controller_spec.rb +0 -2
- data/spec/unit/controller/log_processor_spec.rb +0 -2
- data/spec/unit/file_format/file_format_api_spec.rb +50 -71
- data/spec/unit/file_format/line_definition_spec.rb +49 -45
- data/spec/unit/file_format/merb_format_spec.rb +0 -1
- data/spec/unit/file_format/rails_format_spec.rb +0 -2
- data/spec/unit/filter/anonymize_filter_spec.rb +0 -1
- data/spec/unit/filter/field_filter_spec.rb +0 -3
- data/spec/unit/filter/filter_spec.rb +17 -0
- data/spec/unit/filter/timespan_filter_spec.rb +0 -3
- data/spec/unit/source/log_parser_spec.rb +5 -4
- data/spec/unit/source/request_spec.rb +17 -4
- data/spec/unit/tracker/duration_tracker_spec.rb +0 -6
- data/spec/unit/tracker/frequency_tracker_spec.rb +0 -6
- data/spec/unit/tracker/hourly_spread_spec.rb +0 -4
- data/spec/unit/tracker/timespan_tracker_spec.rb +0 -4
- data/spec/unit/tracker/tracker_api_spec.rb +0 -2
- data/tasks/rspec.rake +8 -1
- metadata +12 -6
- data/spec/unit/aggregator/database_inserter_spec.rb +0 -106
data/Rakefile
CHANGED
@@ -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
|
-
|
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 =
|
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 =
|
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 =
|
45
|
-
|
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
|
-
|
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(
|
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
|
-
|
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(
|
96
|
-
class_name = "#{name}_line".camelize
|
97
|
-
klass = Class.new(
|
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
|
-
|
105
|
-
|
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
|
-
|
118
|
-
@request_class =
|
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
|
-
|
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(
|
148
|
-
create_activerecord_class(
|
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
|
-
|
196
|
-
|
197
|
-
|
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
|
data/spec/lib/macros.rb
ADDED
@@ -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
|
data/spec/lib/testing_format.rb
CHANGED
@@ -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
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
CHANGED
@@ -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
|
-
|
12
|
-
|
13
|
-
require
|
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
|