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.
- 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
|