fluoride-analyzer 0.0.1
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.
- checksums.yaml +7 -0
- data/lib/fluoride-analyzer.rb +14 -0
- data/lib/fluoride-analyzer/config.rb +43 -0
- data/lib/fluoride-analyzer/exception-result.rb +5 -0
- data/lib/fluoride-analyzer/exchange-result.rb +5 -0
- data/lib/fluoride-analyzer/group-collapser.rb +51 -0
- data/lib/fluoride-analyzer/group-context.rb +86 -0
- data/lib/fluoride-analyzer/parser.rb +261 -0
- data/lib/fluoride-analyzer/pattern-collapser.rb +30 -0
- data/lib/fluoride-analyzer/pattern-context.rb +34 -0
- data/lib/fluoride-analyzer/rails.rb +1 -0
- data/lib/fluoride-analyzer/rails/railtie.rb +14 -0
- data/lib/fluoride-analyzer/request-processor.rb +7 -0
- data/lib/fluoride-analyzer/request-templater.rb +26 -0
- data/lib/fluoride-analyzer/request.rb +11 -0
- data/lib/fluoride-analyzer/result-collection.rb +5 -0
- data/lib/fluoride-analyzer/tasks.rake +46 -0
- data/spec-help/gem-test-suite.rb +17 -0
- data/spec/railtie_spec.rb +27 -0
- data/spec/result-parser.rb +110 -0
- data/spec/result-templater.rb +67 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 29f99f8f8b407486d27622bb63af1b8bd604d87f
|
4
|
+
data.tar.gz: b29dacf810a25eb37374ac88532f2c09217cdd65
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f6c30d82f0adbc6051ee083d9062233bd8a632832b343647acad1c97cd7a82fbb1308a11a4959be511ba29d648c859f84dd2964ffa05c8992d34bdabd8b14c93
|
7
|
+
data.tar.gz: 7c618c9bf042e2319e0ccb95a0fb00338a82a0daa4088827b0573df89748f0b7f0340f2cba3bf44051158a0ea1dbcc7bbf67869bf469ea428121b50b58a2430f
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Fluoride
|
2
|
+
module Analyzer
|
3
|
+
class ConfigurationError < ::StandardError
|
4
|
+
end
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'fluoride-analyzer/config'
|
9
|
+
require 'fluoride-analyzer/request'
|
10
|
+
require 'fluoride-analyzer/request-processor'
|
11
|
+
require 'fluoride-analyzer/exchange-result'
|
12
|
+
require 'fluoride-analyzer/exception-result'
|
13
|
+
require 'fluoride-analyzer/result-collection'
|
14
|
+
require 'fluoride-analyzer/rails' if defined?(Rails)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Fluoride::Analyzer
|
2
|
+
class Config
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
DEFAULT_CONFIG_PATH = '.fluoride.yml'
|
6
|
+
DEFAULT_EXCLUDE_PATHS = [ %r[^/images], %r[^/stylesheets], %r[^/javascripts], %r[^/system], ]
|
7
|
+
DEFAULT_EXCLUDE_MIME_TYPES = [ %r[image], %r[text/css], %r[javascript], %r[shockwave] ]
|
8
|
+
|
9
|
+
attr_accessor :exclude_path_patterns,
|
10
|
+
:exclude_mime_types,
|
11
|
+
:match_on_required_params,
|
12
|
+
:exclude_match_params,
|
13
|
+
:limit_count
|
14
|
+
|
15
|
+
|
16
|
+
def self.load(config_path = DEFAULT_CONFIG_PATH)
|
17
|
+
yaml_config = YAML::load(File.open(config_path))
|
18
|
+
config = self.new(yaml_config)
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(yaml_config)
|
22
|
+
self.exclude_path_patterns = DEFAULT_EXCLUDE_PATHS
|
23
|
+
+ yaml_config['exclude_path_patterns']
|
24
|
+
- yaml_config['include_path_patterns']
|
25
|
+
|
26
|
+
self.exclude_mime_types = DEFAULT_EXCLUDE_MIME_TYPES
|
27
|
+
+ yaml_config['exclude_mime_types']
|
28
|
+
- yaml_config['include_mime_types']
|
29
|
+
|
30
|
+
|
31
|
+
if yaml_config.has_key?('match_on_required_params')
|
32
|
+
self.match_on_required_params = yaml_config['match_on_required_params']
|
33
|
+
else
|
34
|
+
self.match_on_required_params = false
|
35
|
+
end
|
36
|
+
|
37
|
+
self.exclude_match_params = yaml_config['exclude_match_params'] || []
|
38
|
+
self.include_match_params = yaml_config['include_match_params'] || []
|
39
|
+
self.limit_count = yaml_config['limit_count']
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'fluoride-analyzer/group-context'
|
2
|
+
|
3
|
+
module Fluoride::Analyzer
|
4
|
+
class GroupCollapser
|
5
|
+
def initialize(pattern, letname_map, method, status, requests_list)
|
6
|
+
@pattern = pattern
|
7
|
+
@letname_map = letname_map
|
8
|
+
@request_list = requests_list
|
9
|
+
@method, @status = method, status
|
10
|
+
end
|
11
|
+
attr_reader :method, :status, :letname_map
|
12
|
+
|
13
|
+
def stet_list
|
14
|
+
[]
|
15
|
+
end
|
16
|
+
|
17
|
+
def requests
|
18
|
+
@request_list
|
19
|
+
end
|
20
|
+
|
21
|
+
def reduce_path(path, request)
|
22
|
+
path = @pattern.dup
|
23
|
+
request['path_params'].each_pair do |name, value|
|
24
|
+
next if stet_list.include?(name)
|
25
|
+
target_name = letname_map.fetch(name, name)
|
26
|
+
path.sub!(/:#{name}/,"\#{#{target_name}}")
|
27
|
+
end
|
28
|
+
path
|
29
|
+
end
|
30
|
+
|
31
|
+
def reduce_query(query, request)
|
32
|
+
""
|
33
|
+
#none captured (yet?)
|
34
|
+
end
|
35
|
+
|
36
|
+
def each_group_context
|
37
|
+
path_tuples = requests.map do |request|
|
38
|
+
[request['path'], request['query_params'], request]
|
39
|
+
end
|
40
|
+
|
41
|
+
reduced_hash = Hash.new{|h,k| h[k] = []}
|
42
|
+
path_tuples.each do |path, query, request|
|
43
|
+
reduced_hash[ [ reduce_path(path, request), reduce_query(query, request) ] ] << request
|
44
|
+
end
|
45
|
+
|
46
|
+
reduced_hash.each do |(path, query_params), requests|
|
47
|
+
yield GroupContext.new(method, status, requests, path, query_params)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'fluoride-analyzer'
|
2
|
+
|
3
|
+
module Fluoride::Analyzer
|
4
|
+
class GroupContext
|
5
|
+
def initialize(method, status, requests, path, query_params)
|
6
|
+
@method, @status_code, @requests, @path, @query_params = method, status, requests, path, query_params
|
7
|
+
end
|
8
|
+
attr_reader :method, :status_code
|
9
|
+
|
10
|
+
def test_method
|
11
|
+
@method.downcase
|
12
|
+
end
|
13
|
+
|
14
|
+
def status_description
|
15
|
+
case @status_code.to_i
|
16
|
+
when 300..399
|
17
|
+
"Redirect"
|
18
|
+
else
|
19
|
+
"OK"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def request_count
|
24
|
+
@requests.inject(0) do |sum, request|
|
25
|
+
sum + request['sources'].keys.length
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def example_source
|
30
|
+
request['sources'].keys.first
|
31
|
+
end
|
32
|
+
|
33
|
+
def example_path
|
34
|
+
request['path']
|
35
|
+
end
|
36
|
+
|
37
|
+
def request_spec_description
|
38
|
+
"#@method #{spec_request_path}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def should_what
|
42
|
+
case @status_code.to_i
|
43
|
+
when 300..399
|
44
|
+
"redirect"
|
45
|
+
else
|
46
|
+
"succeed"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def redirect_path
|
51
|
+
request['redirect_location'].sub(%r[^https?://#{request['host']}], '')
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_result
|
55
|
+
case @status_code.to_i
|
56
|
+
when 300..399
|
57
|
+
["response.should redirect_to(\"#{redirect_path}\")"]
|
58
|
+
else
|
59
|
+
["response.should be_success", "response.status.should == 200"]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_request(indent)
|
64
|
+
indent = " " * indent
|
65
|
+
test_request = "#{test_method} \"#{spec_request_path}\""
|
66
|
+
if request.has_key? 'post_params'
|
67
|
+
params = request['post_params'].pretty_inspect.split("\n")
|
68
|
+
params = ([params[0]] + params[1..-1].map do |line|
|
69
|
+
indent + line
|
70
|
+
end).join("\n")
|
71
|
+
test_request += ", #{params}"
|
72
|
+
end
|
73
|
+
test_request
|
74
|
+
end
|
75
|
+
|
76
|
+
def spec_request_path
|
77
|
+
"#{@path}#{@query_params}"
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def request
|
83
|
+
@requests.first
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
require 'fluoride-analyzer'
|
2
|
+
require 'rack'
|
3
|
+
require 'yaml'
|
4
|
+
#Also: Rails and ActionDispatch::Request
|
5
|
+
|
6
|
+
module Fluoride::Analyzer
|
7
|
+
class Parser
|
8
|
+
attr_accessor :files, :limit, :target_path
|
9
|
+
attr_reader :exceptions, :results, :counts
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@exceptions = []
|
13
|
+
@counts = {
|
14
|
+
:excluded => 0,
|
15
|
+
:unrecognized => 0,
|
16
|
+
:no_matching_route => 0,
|
17
|
+
:exceptions => 0
|
18
|
+
}
|
19
|
+
@results = Hash.new do|results, pattern| #PATH PATTERN
|
20
|
+
results[pattern] = Hash.new do |by_method, method| #METHOD
|
21
|
+
by_method[method] = Hash.new do |records, status| #STATUS CODE
|
22
|
+
records[status] = Hash.new do |record, key| #formatted record
|
23
|
+
record[key] = []
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
@limit = -1
|
29
|
+
end
|
30
|
+
|
31
|
+
def excluded_content_types
|
32
|
+
[ %r[image], %r[text/css], %r[javascript], %r[shockwave] ]
|
33
|
+
end
|
34
|
+
|
35
|
+
def excluded_paths
|
36
|
+
[ %r[^/images], %r[\.ttf\z], %r[\.woff\z] ]
|
37
|
+
end
|
38
|
+
|
39
|
+
def exclude?(record)
|
40
|
+
return true if record['type'] == 'exception_raised'
|
41
|
+
|
42
|
+
if excluded_content_types.any?{ |reg| reg =~ record['response']['headers']['Content-Type'] }
|
43
|
+
return true
|
44
|
+
end
|
45
|
+
if excluded_paths.any?{|req| req =~ record['request']['path']}
|
46
|
+
return true
|
47
|
+
end
|
48
|
+
return false
|
49
|
+
rescue
|
50
|
+
warn "Exception raised while filtering record: #{record.inspect}"
|
51
|
+
raise
|
52
|
+
end
|
53
|
+
|
54
|
+
def format_record(record, path_params)
|
55
|
+
{
|
56
|
+
'path' => record['request']['path'],
|
57
|
+
'query_params' => record['request']['query_params'],
|
58
|
+
'content-type' => record['request']['content_type'],
|
59
|
+
'path_params' => path_params,
|
60
|
+
'redirect_location' => record['response']['headers']['Location'],
|
61
|
+
'host' => record['request']['host']
|
62
|
+
#'accept' => record['request']['accept']
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def formatted_results
|
67
|
+
formatted = {}
|
68
|
+
results.each do |pattern, methods|
|
69
|
+
frmt_methods = formatted[pattern] = {}
|
70
|
+
methods.each do |method, status|
|
71
|
+
frmt_statuses = frmt_methods[method] = {}
|
72
|
+
status.each do |code, requests|
|
73
|
+
frmt_statuses[code] = requests.keys.map do |req_hash|
|
74
|
+
hash = req_hash.dup
|
75
|
+
hash['sources'] = Hash[requests[req_hash]]
|
76
|
+
hash
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
formatted
|
82
|
+
end
|
83
|
+
|
84
|
+
def post_params(record)
|
85
|
+
return {} unless %w{POST PUT}.include?(record['request']['method'])
|
86
|
+
unless record['request']['content_type'].nil? or
|
87
|
+
%r[^application/x-www-form-urlencoded.*] =~ record['request']['content_type']
|
88
|
+
return {}
|
89
|
+
end
|
90
|
+
|
91
|
+
form_hash = Rack::Utils.parse_nested_query(record['request']['body'])
|
92
|
+
|
93
|
+
{ 'post_params' => form_hash }
|
94
|
+
end
|
95
|
+
|
96
|
+
RoutePattern = Struct.new(:route, :matches, :params, :path_spec, :segment_keys)
|
97
|
+
|
98
|
+
class Patterner
|
99
|
+
def self.for(rails_routes)
|
100
|
+
if rails_routes.respond_to? :recognize_path
|
101
|
+
Rails4.new(rails_routes)
|
102
|
+
else
|
103
|
+
Rails3.new(rails_routes)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def base_env
|
108
|
+
@base_env ||= {
|
109
|
+
"HTTP_REFERER" => '',
|
110
|
+
"HTTP_COOKIE" => '',
|
111
|
+
"HTTP_AUTHORIZATION" => '',
|
112
|
+
"REQUEST_METHOD" => "GET",
|
113
|
+
'HTTP_HOST' => '',
|
114
|
+
'SERVER_NAME' => '',
|
115
|
+
'SERVER_ADDR' => '',
|
116
|
+
'SERVER_PORT' => '80',
|
117
|
+
"SCRIPT_NAME" => '',
|
118
|
+
"QUERY_STRING" => '',
|
119
|
+
'rack.input' => '' #body
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
def initialize(rails_routes)
|
124
|
+
@rails_routes = rails_routes
|
125
|
+
end
|
126
|
+
attr_reader :rails_routes
|
127
|
+
|
128
|
+
def build_request(result_env)
|
129
|
+
ActionDispatch::Request.new(base_env.merge(request_env))
|
130
|
+
end
|
131
|
+
|
132
|
+
def route_map
|
133
|
+
@route_map ||=
|
134
|
+
begin
|
135
|
+
ad_routes_array = rails_routes.routes
|
136
|
+
rack_routes_array = rails_routes.set.instance_eval{ @routes }
|
137
|
+
Hash[ rack_routes_array.zip(ad_routes_array) ]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def route_set
|
142
|
+
@route_set ||=
|
143
|
+
begin
|
144
|
+
set = rails_routes.set
|
145
|
+
set
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class Rails3 < Patterner
|
150
|
+
def build(env)
|
151
|
+
req = build_request(env)
|
152
|
+
route, matches, params = route_set.recognize(req)
|
153
|
+
|
154
|
+
path_spec = :unrecognized
|
155
|
+
segment_keys = {}
|
156
|
+
|
157
|
+
if route_map.has_key?(route)
|
158
|
+
rails_route = route_map[route]
|
159
|
+
path_spec = rails_route.path
|
160
|
+
segment_keys = rails_route.segment_keys
|
161
|
+
end
|
162
|
+
|
163
|
+
RoutePattern.new(route, matches, params, path_spec, segment_keys)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
class Rails4 < Patterner
|
168
|
+
def route_set
|
169
|
+
@route_set ||=
|
170
|
+
begin
|
171
|
+
set = Rails.application.routes.router
|
172
|
+
set
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def build_request(request_env)
|
177
|
+
ActionDispatch::Request.new(base_env.merge(request_env))
|
178
|
+
end
|
179
|
+
|
180
|
+
def build(env)
|
181
|
+
req = build_request(env)
|
182
|
+
pattern = nil
|
183
|
+
route_set.recognize(req) do |route, matches, params|
|
184
|
+
rails_route = route_map[route]
|
185
|
+
|
186
|
+
path_spec = :unrecognized
|
187
|
+
segment_keys = {}
|
188
|
+
|
189
|
+
if route_map.has_key?(route)
|
190
|
+
rails_route = route_map[route]
|
191
|
+
path_spec = rails_route.path.spec.to_s
|
192
|
+
segment_keys = rails_route.segment_keys
|
193
|
+
end
|
194
|
+
|
195
|
+
pattern = RoutePattern.new(route, matches, params, path_spec, segment_keys)
|
196
|
+
end
|
197
|
+
if pattern.nil?
|
198
|
+
pattern = RoutePattern.new(nil,nil,nil,nil,nil)
|
199
|
+
end
|
200
|
+
pattern
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def patterner
|
206
|
+
@patterner ||= Patterner.for(Rails.application.routes)
|
207
|
+
end
|
208
|
+
|
209
|
+
def collect_record(file, record, index)
|
210
|
+
if exclude?(record)
|
211
|
+
@counts[:excluded] += 1
|
212
|
+
return
|
213
|
+
end
|
214
|
+
|
215
|
+
request_env = {
|
216
|
+
"REQUEST_METHOD" => record['request']['method'].to_s.upcase,
|
217
|
+
"PATH_INFO" => record['request']['path'],
|
218
|
+
}
|
219
|
+
unless request_env['REQUEST_METHOD'] == "GET"
|
220
|
+
request_env['rack.input'] = StringIO.new(record['request']['body'])
|
221
|
+
end
|
222
|
+
|
223
|
+
pattern = patterner.build(request_env)
|
224
|
+
|
225
|
+
route = pattern.route
|
226
|
+
|
227
|
+
if route.nil?
|
228
|
+
@counts[:unrecognized] += 1
|
229
|
+
return
|
230
|
+
end
|
231
|
+
|
232
|
+
if pattern.path_spec == :unrecognized
|
233
|
+
@counts[:no_matching_route] += 1
|
234
|
+
warn "Unrecognized route: #{record['request']['path'].inspect}"
|
235
|
+
else
|
236
|
+
route_path = pattern.path_spec
|
237
|
+
path_params = Hash[pattern.segment_keys.map do |key|
|
238
|
+
[key, params[key]]
|
239
|
+
end]
|
240
|
+
end
|
241
|
+
|
242
|
+
formatted_record = format_record(record, path_params).merge(post_params(record))
|
243
|
+
|
244
|
+
self.results[route_path][record['request']['method']][record['response']['status']][formatted_record] << [file, index]
|
245
|
+
end
|
246
|
+
|
247
|
+
def parse_stream(file, string)
|
248
|
+
stream = YAML.load_stream(string)
|
249
|
+
stream = stream.documents if stream.respond_to? :documents
|
250
|
+
|
251
|
+
stream.each_with_index do |record, index|
|
252
|
+
collect_record(file, record, index)
|
253
|
+
end
|
254
|
+
rescue ArgumentError => ex
|
255
|
+
@exceptions << [ex, file]
|
256
|
+
rescue Exception => ex
|
257
|
+
@exceptions << [ex, file]
|
258
|
+
raise
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Fluoride::Analyzer
|
2
|
+
class PatternCollapser
|
3
|
+
def initialize(pattern, methods_hash)
|
4
|
+
@pattern = pattern
|
5
|
+
@methods_hash = methods_hash
|
6
|
+
end
|
7
|
+
|
8
|
+
def erase_list
|
9
|
+
%w{ format }
|
10
|
+
end
|
11
|
+
|
12
|
+
def pattern
|
13
|
+
erase_list.inject(@pattern) do |pattern, erase|
|
14
|
+
pattern.sub(/\(.:#{erase}\)/,'')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def param_letname_map
|
19
|
+
{ :id => :model_id }
|
20
|
+
end
|
21
|
+
|
22
|
+
def params_fields
|
23
|
+
@methods_hash.values.first.values.first.first['path_params'].keys.reject do |key|
|
24
|
+
key == :format
|
25
|
+
end.map do |name|
|
26
|
+
param_letname_map.fetch(name, name)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'fluoride-analyzer/group-collapser'
|
2
|
+
require 'fluoride-analyzer/pattern-collapser'
|
3
|
+
|
4
|
+
module Fluoride::Analyzer
|
5
|
+
class PatternContext
|
6
|
+
def initialize(pattern, methods_hash)
|
7
|
+
@pattern, @methods_hash = pattern, methods_hash
|
8
|
+
@collapser = PatternCollapser.new(pattern, methods_hash)
|
9
|
+
end
|
10
|
+
attr_reader :pattern
|
11
|
+
|
12
|
+
def each_status_group
|
13
|
+
@methods_hash.each_pair do |method, hash|
|
14
|
+
hash.each_pair do |status, requests|
|
15
|
+
GroupCollapser.new(@collapser.pattern, @collapser.param_letname_map, method, status, requests).each_group_context do |context|
|
16
|
+
yield context
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def param_fields
|
23
|
+
@collapser.params_fields
|
24
|
+
end
|
25
|
+
|
26
|
+
def filename
|
27
|
+
@collapser.pattern.gsub(%r{[:/().]+},'_').gsub(/^_|_$/, '') + "_spec.rb"
|
28
|
+
end
|
29
|
+
|
30
|
+
def context_binding
|
31
|
+
binding
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'fluoride-analyzer/rails/railtie'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'fluoride-analyzer/pattern-context'
|
3
|
+
|
4
|
+
module Fluoride::Analyzer
|
5
|
+
class RequestTemplater
|
6
|
+
def initialize
|
7
|
+
|
8
|
+
end
|
9
|
+
attr_accessor :template, :template_string, :target_dir, :results
|
10
|
+
|
11
|
+
def template
|
12
|
+
@template ||= ERB.new(template_string, nil, '<>')
|
13
|
+
end
|
14
|
+
|
15
|
+
def go
|
16
|
+
results.each_pair do |pattern, statuses|
|
17
|
+
context = PatternContext.new(pattern, statuses)
|
18
|
+
|
19
|
+
path = File.join(context.filename)
|
20
|
+
contents = template.result(context.context_binding)
|
21
|
+
|
22
|
+
yield(path, contents)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'fluoride-analyzer/request-templater'
|
2
|
+
|
3
|
+
FLUORIDE_PATH = 'fluoride-collector'
|
4
|
+
LIMIT = 2000
|
5
|
+
|
6
|
+
directory 'spec/requests'
|
7
|
+
|
8
|
+
task :parse_fluoride => 'paths.yml'
|
9
|
+
|
10
|
+
task :rebuild_request_specs => [:clobber_request_specs, :template_request_specs]
|
11
|
+
|
12
|
+
task :clobber_request_specs do
|
13
|
+
sh("rm -rf spec/requests/*")
|
14
|
+
end
|
15
|
+
|
16
|
+
file 'results.yml' => FileList[ "#{FLUORIDE_PATH}/*.yml" ] do |task|
|
17
|
+
Rake::Task[:environment].invoke
|
18
|
+
parser = Fluoride::Analyzer::Parser.new
|
19
|
+
|
20
|
+
task.prerequisites.find_all do |prereq|
|
21
|
+
next unless File.file?(prereq) && __FILE__ != prereq
|
22
|
+
parser.parse_stream(File.read(prereq))
|
23
|
+
end
|
24
|
+
|
25
|
+
#parser.limit = LIMIT #XXX Uncomment to limit number of files parsed
|
26
|
+
parser.target_path = task.name
|
27
|
+
|
28
|
+
File.open(task.name, "w") do |target_file|
|
29
|
+
target_file.write(YAML.dump(parser.formatted_results))
|
30
|
+
end
|
31
|
+
|
32
|
+
puts "Found #{parser.formatted_results.keys.length} unique requests"
|
33
|
+
end
|
34
|
+
|
35
|
+
task :template_request_specs => ['spec/requests', 'results.yml'] do
|
36
|
+
templater = Fluoride::Analyzer::RequestTemplater.new
|
37
|
+
|
38
|
+
templater.template_string = File::read(File::expand_path("../../../templates/request_spec.erb", __FILE__))
|
39
|
+
templater.results = YAML.load(File.read('results.yml'))
|
40
|
+
templater.go do |filename, contents|
|
41
|
+
path = File.join("spec/requests", filename)
|
42
|
+
File.open(path, "w") do |spec_file|
|
43
|
+
spec_file.write(contents)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
puts Dir::pwd
|
2
|
+
require 'test/unit'
|
3
|
+
begin
|
4
|
+
require 'spec'
|
5
|
+
rescue LoadError
|
6
|
+
false
|
7
|
+
end
|
8
|
+
|
9
|
+
class RSpecTest < Test::Unit::TestCase
|
10
|
+
def test_that_rspec_is_available
|
11
|
+
assert_nothing_raised("\n\n * RSpec isn't available - please run: gem install rspec *\n\n"){ ::Spec }
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_that_specs_pass
|
15
|
+
assert(system(*%w{spec -f e -p **/*.rb spec}),"\n\n * Specs failed *\n\n")
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "action_controller/railtie"
|
2
|
+
require 'fluoride-analyzer/rails'
|
3
|
+
|
4
|
+
describe Fluoride::Analyzer::Railtie do
|
5
|
+
|
6
|
+
def config(app)
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
let :rails_application do
|
11
|
+
Class.new(::Rails::Application).tap do |app|
|
12
|
+
app.configure do
|
13
|
+
config.active_support.deprecation = :stderr
|
14
|
+
config.eager_load = false
|
15
|
+
end
|
16
|
+
config(app)
|
17
|
+
app.initialize!
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
after :each do
|
22
|
+
Rails.application = nil #because Rails has ideas of it's own, silly thing
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should add rake tasks"
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'action_controller/railtie'
|
2
|
+
require 'fluoride-analyzer/parser'
|
3
|
+
|
4
|
+
describe Fluoride::Analyzer::Parser do
|
5
|
+
def config(app)
|
6
|
+
app.routes.draw do
|
7
|
+
get "/", :to => 'root#index', :as => :root
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
let! :rails_application do
|
12
|
+
Class.new(::Rails::Application).tap do |app|
|
13
|
+
app.configure do
|
14
|
+
config.active_support.deprecation = :stderr
|
15
|
+
config.eager_load = false
|
16
|
+
end
|
17
|
+
config(app)
|
18
|
+
app.initialize!
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
after :each do
|
23
|
+
Rails.application = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
let :requests_stream do
|
27
|
+
YAML::dump_stream(*requests)
|
28
|
+
end
|
29
|
+
|
30
|
+
let :parser do
|
31
|
+
Fluoride::Analyzer::Parser.new.tap do |parser|
|
32
|
+
parser.parse_stream("test-file.yaml", requests_stream)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
let :results do
|
37
|
+
parser.formatted_results
|
38
|
+
end
|
39
|
+
|
40
|
+
let :requests do
|
41
|
+
[
|
42
|
+
{"type"=>"normal_exchange",
|
43
|
+
"tags"=>nil,
|
44
|
+
"request"=>
|
45
|
+
{"query_string"=>"",
|
46
|
+
"body"=>"",
|
47
|
+
"method"=>"GET",
|
48
|
+
"accept"=>
|
49
|
+
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
50
|
+
"content_type"=>nil,
|
51
|
+
"user_agent"=>
|
52
|
+
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36",
|
53
|
+
"referer"=>nil,
|
54
|
+
"host"=>"0.0.0.0:3000",
|
55
|
+
"authorization"=>nil,
|
56
|
+
"cookies"=>nil,
|
57
|
+
"path"=>"/",
|
58
|
+
"accept_encoding"=>"gzip,deflate,sdch"},
|
59
|
+
"response"=>
|
60
|
+
{"body"=>
|
61
|
+
["<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\n<!--\n\tsomebody@gmail.com june 2014\n-->\n\n\t<meta charset=\"utf-8\" />\n\t<title>Application</title>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-sc"],
|
62
|
+
"status"=>200,
|
63
|
+
"headers"=>
|
64
|
+
{"Content-Type"=>"text/html; charset=utf-8",
|
65
|
+
"ETag"=>"\"5ff1d9c9172424e5d906216dcdacd9ef\"",
|
66
|
+
"X-UA-Compatible"=>"IE=Edge,chrome=1",
|
67
|
+
"Cache-Control"=>"max-age=0, private, must-revalidate",
|
68
|
+
"X-Runtime"=>"0.873536"}}
|
69
|
+
},
|
70
|
+
|
71
|
+
{"type"=>"normal_exchange",
|
72
|
+
"tags"=>nil,
|
73
|
+
"request"=>
|
74
|
+
{"query_string"=>"",
|
75
|
+
"body"=>"",
|
76
|
+
"method"=>"GET",
|
77
|
+
"accept"=>"text/css,*/*;q=0.1",
|
78
|
+
"content_type"=>nil,
|
79
|
+
"user_agent"=>
|
80
|
+
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36",
|
81
|
+
"referer"=>"http://0.0.0.0:3000/",
|
82
|
+
"host"=>"0.0.0.0:3000",
|
83
|
+
"authorization"=>nil,
|
84
|
+
"cookies"=>nil,
|
85
|
+
"path"=>"/stylesheets/2014-bootstrap.css",
|
86
|
+
"accept_encoding"=>"gzip,deflate,sdch"},
|
87
|
+
"response"=>
|
88
|
+
{"body"=>
|
89
|
+
["/*! normalize.css v3.0.0 | MIT License | git.io/normalize */\nhtml {\n font-family: sans-serif;\n -ms-text-size-adjust: 100%;\n -webkit-text-size-adjust: 100%; }\n\nbody {\n margin: 0; }\n\narticle,\naside,\n"],
|
90
|
+
"status"=>200,
|
91
|
+
"headers"=>
|
92
|
+
{"Content-Length"=>"15835",
|
93
|
+
"Content-Type"=>"text/css",
|
94
|
+
"Last-Modified"=>"Sun, 29 Jun 2014 00:00:48 GMT"}},
|
95
|
+
}
|
96
|
+
]
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should have excluded 1 request" do
|
100
|
+
expect(parser.counts[:excluded]).to eq(1)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should have 0 unrecognized requests" do
|
104
|
+
expect(parser.counts[:unrecognized]).to eq(0)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should be a well formatted version of the results" do
|
108
|
+
expect(results).to match("/" => {"GET" => {200 => match([ a_hash_including("path" => "/") ])}})
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'fluoride-analyzer/request-templater'
|
2
|
+
|
3
|
+
describe Fluoride::Analyzer::RequestTemplater do
|
4
|
+
let :template_string do
|
5
|
+
<<-EOT
|
6
|
+
require 'spec_helper'
|
7
|
+
|
8
|
+
describe '<%= pattern %>' do
|
9
|
+
<% param_fields.each do |field| %>
|
10
|
+
let :<%= field %> do
|
11
|
+
FactoryGirl.create(:<%= field %>).id
|
12
|
+
end
|
13
|
+
<%end%>
|
14
|
+
<% each_status_group do |group| %>
|
15
|
+
|
16
|
+
# HTTP status code: <%= group.status_code %> <%= group.status_description %>
|
17
|
+
# <%= group.request_count %> cases recorded by Fluoride
|
18
|
+
# See example: '<%= group.example_source %>'
|
19
|
+
# example path: <%= group.example_path %>
|
20
|
+
describe '<%= group.request_spec_description %>' do
|
21
|
+
it "should <%= group.should_what %>" do
|
22
|
+
<%= group.test_request(6) %>
|
23
|
+
<% group.test_result.each do |test_result_line| %>
|
24
|
+
<%= test_result_line %>
|
25
|
+
<% end %>
|
26
|
+
end
|
27
|
+
end
|
28
|
+
<%end%>
|
29
|
+
end
|
30
|
+
EOT
|
31
|
+
end
|
32
|
+
|
33
|
+
let :results do
|
34
|
+
YAML.load(File.read(File.expand_path("../../spec-help/fixtures/results.yml", __FILE__)))
|
35
|
+
end
|
36
|
+
|
37
|
+
let :templater do
|
38
|
+
Fluoride::Analyzer::RequestTemplater.new.tap do |tmplr|
|
39
|
+
tmplr.template_string = template_string
|
40
|
+
tmplr.results = results
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
let :templated_list do
|
45
|
+
templated_list = []
|
46
|
+
templater.go do |path, contents|
|
47
|
+
templated_list << [path, contents]
|
48
|
+
end
|
49
|
+
templated_list
|
50
|
+
end
|
51
|
+
|
52
|
+
let :paths do
|
53
|
+
templated_list.map{|path, _| path}
|
54
|
+
end
|
55
|
+
|
56
|
+
let :contents do
|
57
|
+
templated_list.map{|_, contents| contents}
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should template once per result" do
|
61
|
+
expect(templated_list.length).to eq(results.keys.length)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should put the right stuff in contents" do
|
65
|
+
expect(contents).to all(match(/HTTP status code:/))
|
66
|
+
end
|
67
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluoride-analyzer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Judson Lester
|
8
|
+
- Evan Down
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-07-10 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: mattock
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - '>'
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - '>'
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: valise
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - '>'
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '>'
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
description: |2
|
43
|
+
Part of the Fluoride suite - tools for making your black box a bit whiter
|
44
|
+
email:
|
45
|
+
- nyarly@gmail.com
|
46
|
+
- evan@lrdesign.com
|
47
|
+
executables: []
|
48
|
+
extensions: []
|
49
|
+
extra_rdoc_files: []
|
50
|
+
files:
|
51
|
+
- lib/fluoride-analyzer/parser.rb
|
52
|
+
- lib/fluoride-analyzer/exception-result.rb
|
53
|
+
- lib/fluoride-analyzer/rails/railtie.rb
|
54
|
+
- lib/fluoride-analyzer/group-context.rb
|
55
|
+
- lib/fluoride-analyzer/config.rb
|
56
|
+
- lib/fluoride-analyzer/request-templater.rb
|
57
|
+
- lib/fluoride-analyzer/group-collapser.rb
|
58
|
+
- lib/fluoride-analyzer/rails.rb
|
59
|
+
- lib/fluoride-analyzer/pattern-collapser.rb
|
60
|
+
- lib/fluoride-analyzer/request.rb
|
61
|
+
- lib/fluoride-analyzer/tasks.rake
|
62
|
+
- lib/fluoride-analyzer/result-collection.rb
|
63
|
+
- lib/fluoride-analyzer/request-processor.rb
|
64
|
+
- lib/fluoride-analyzer/pattern-context.rb
|
65
|
+
- lib/fluoride-analyzer/exchange-result.rb
|
66
|
+
- lib/fluoride-analyzer.rb
|
67
|
+
- spec/result-templater.rb
|
68
|
+
- spec/railtie_spec.rb
|
69
|
+
- spec/result-parser.rb
|
70
|
+
- spec-help/gem-test-suite.rb
|
71
|
+
homepage: http://github.com/lrdesign/fluoride-analyzer
|
72
|
+
licenses:
|
73
|
+
- MIT
|
74
|
+
metadata: {}
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options:
|
77
|
+
- --inline-source
|
78
|
+
- --main
|
79
|
+
- doc/README
|
80
|
+
- --title
|
81
|
+
- fluoride-analyzer-0.0.1 Documentation
|
82
|
+
require_paths:
|
83
|
+
- lib/
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 2.0.14
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: Analysis of recorded requests
|
100
|
+
test_files:
|
101
|
+
- spec-help/gem-test-suite.rb
|