repsheet_visualizer 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0bf1075fbcd0219cc0d7aaf9e414bce1c97f4e82
4
+ data.tar.gz: 07a44a473a4d62fc5ef5266f417f31dbce69f6b8
5
+ SHA512:
6
+ metadata.gz: 307376a60eeea4e67ed8a60b4cdb9c5064ed56281e5199ce8ced8854905149dcbd164540dee2ab0a132e413bb183d667de7dd0803747001bc56195b1fe4ff577
7
+ data.tar.gz: 087dd95a4f7601f36e895f1380dc169b008b269e2c25ba8663939ce15ab0c9fa42370e1b701bf71a596396e49c6988779dfd8a8ddbb4199f8ab872b65e2e579b
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.rvmrc CHANGED
@@ -1 +1 @@
1
- rvm use ruby-1.9.3@repsheet_vizualizer --create
1
+ rvm use ruby-2.0@repsheet_vizualizer --create
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- repsheet_visualizer (0.1.3)
4
+ repsheet_visualizer (0.1.7)
5
5
  geoip
6
6
  json
7
7
  redis
@@ -10,6 +10,7 @@ PATH
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
+ diff-lcs (1.2.4)
13
14
  geoip (1.2.1)
14
15
  json (1.8.0)
15
16
  kgio (2.8.0)
@@ -19,8 +20,16 @@ GEM
19
20
  raindrops (0.11.0)
20
21
  rake (10.0.4)
21
22
  redis (3.0.4)
22
- sinatra (1.4.2)
23
- rack (~> 1.5, >= 1.5.2)
23
+ rspec (2.14.1)
24
+ rspec-core (~> 2.14.0)
25
+ rspec-expectations (~> 2.14.0)
26
+ rspec-mocks (~> 2.14.0)
27
+ rspec-core (2.14.3)
28
+ rspec-expectations (2.14.0)
29
+ diff-lcs (>= 1.1.3, < 2.0)
30
+ rspec-mocks (2.14.1)
31
+ sinatra (1.4.3)
32
+ rack (~> 1.4)
24
33
  rack-protection (~> 1.4)
25
34
  tilt (~> 1.3, >= 1.3.4)
26
35
  tilt (1.4.1)
@@ -36,4 +45,5 @@ DEPENDENCIES
36
45
  bundler
37
46
  rake
38
47
  repsheet_visualizer!
48
+ rspec
39
49
  unicorn
data/Rakefile CHANGED
@@ -1 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new('spec')
5
+
6
+ task :default => :spec
@@ -2,9 +2,9 @@ require 'geoip'
2
2
  require 'sinatra'
3
3
  require 'redis'
4
4
  require 'json'
5
+ require_relative 'backend'
5
6
 
6
7
  class RepsheetVisualizer < Sinatra::Base
7
- # Grab the mount point before every request
8
8
  before do
9
9
  @mount = mount
10
10
  end
@@ -20,7 +20,6 @@ class RepsheetVisualizer < Sinatra::Base
20
20
  end
21
21
  end
22
22
 
23
- # Settings methods
24
23
  def redis_connection
25
24
  host = defined?(settings.redis_host) ? settings.redis_host : "localhost"
26
25
  port = defined?(settings.redis_port) ? settings.redis_port : 6379
@@ -42,101 +41,25 @@ class RepsheetVisualizer < Sinatra::Base
42
41
  defined?(settings.redis_expiry) ? (settings.redis_expiry * 60 * 60) : (24 * 60 * 60)
43
42
  end
44
43
 
45
- # TODO: These methods should get moved out to another place
46
- def summary(connection)
47
- suspects = {}
48
- blacklisted = {}
49
-
50
- if connection.exists("offenders")
51
- connection.zrevrangebyscore("offenders", "+inf", "0").each do |actor|
52
- next if connection.get("#{actor}:repsheet:blacklist") == "true"
53
- suspects[actor] = Hash.new 0
54
- suspects[actor][:detected] = connection.smembers("#{actor}:detected").join(", ")
55
- suspects[actor][:total] = connection.zscore("offenders", actor).to_i
56
- end
57
-
58
- connection.keys("*:*:blacklist").map {|d| d.split(":").first}.reject {|ip| ip.empty?}.each do |actor|
59
- connection.get("#{actor}:repsheet:blacklist") == "true"
60
- blacklisted[actor] = Hash.new 0
61
- blacklisted[actor][:detected] = connection.smembers("#{actor}:detected").join(", ")
62
- connection.smembers("#{actor}:detected").each do |rule|
63
- blacklisted[actor][:total] += connection.get("#{actor}:#{rule}:count").to_i
64
- end
65
- end
66
- else
67
- connection.keys("*:requests").map {|d| d.split(":").first}.reject {|ip| ip.empty?}.each do |actor|
68
- detected = connection.smembers("#{actor}:detected").join(", ")
69
- blacklist = connection.get("#{actor}:repsheet:blacklist")
70
-
71
- if !detected.empty? && blacklist != "true"
72
- suspects[actor] = Hash.new 0
73
- suspects[actor][:detected] = detected
74
- connection.smembers("#{actor}:detected").each do |rule|
75
- suspects[actor][:total] += connection.get("#{actor}:#{rule}:count").to_i
76
- end
77
- end
78
-
79
- if blacklist == "true"
80
- blacklisted[actor] = Hash.new 0
81
- blacklisted[actor][:detected] = detected
82
- connection.smembers("#{actor}:detected").each do |rule|
83
- blacklisted[actor][:total] += connection.get("#{actor}:#{rule}:count").to_i
84
- end
85
- end
86
- end
87
- end
88
-
89
- [suspects.sort_by{|k,v| -v[:total]}.take(10), blacklisted]
90
- end
91
-
92
- def breakdown(connection)
93
- data = {}
94
- offenders = connection.keys("*:repsheet").map {|o| o.split(":").first}
95
- offenders.each do |offender|
96
- data[offender] = {"totals" => {}}
97
- connection.smembers("#{offender}:detected").each do |rule|
98
- data[offender]["totals"][rule] = connection.get "#{offender}:#{rule}:count"
99
- end
100
- end
101
- aggregate = Hash.new 0
102
- data.each {|ip,data| data["totals"].each {|rule,count| aggregate[rule] += count.to_i}}
103
- [data, aggregate]
104
- end
105
-
106
- def activity(connection)
107
- connection.lrange("#{@ip}:requests", 0, -1)
108
- end
109
-
110
- def worldview(connection, database)
111
- data = {}
112
- offenders = connection.keys("*:repsheet*").map {|o| o.split(":").first}
113
- offenders.each do |address|
114
- details = database.country(address)
115
- next if details.nil?
116
- data[address] = [details.latitude, details.longitude]
117
- end
118
- data
119
- end
120
-
121
44
  # This is the actual application
122
45
  get '/' do
123
- @suspects, @blacklisted = summary(redis_connection)
46
+ @suspects, @blacklisted = Backend.summary(redis_connection)
124
47
  erb :actors
125
48
  end
126
49
 
127
50
  get '/breakdown' do
128
- @data, @aggregate = breakdown(redis_connection)
51
+ @data, @aggregate = Backend.breakdown(redis_connection)
129
52
  erb :breakdown
130
53
  end
131
54
 
132
55
  get '/worldview' do
133
- @data = worldview(redis_connection, geoip_database)
56
+ @data = Backend.worldview(redis_connection, geoip_database)
134
57
  erb :worldview
135
58
  end
136
59
 
137
60
  get '/activity/:ip' do
138
61
  @ip = params[:ip]
139
- @data = activity(redis_connection)
62
+ @data = Backend.activity(redis_connection)
140
63
  erb :activity
141
64
  end
142
65
 
@@ -0,0 +1,97 @@
1
+ class Backend
2
+ def self.summary(connection)
3
+ if connection.exists("offenders")
4
+ suspects, blacklisted = optimized(connection)
5
+ else
6
+ suspects, blacklisted = standard(connection)
7
+ end
8
+
9
+ [suspects.sort_by{|k,v| -v[:total]}.take(10), blacklisted]
10
+ end
11
+
12
+ def self.breakdown(connection)
13
+ # data = {}
14
+ # offenders = connection.keys("*:repsheet").map {|o| o.split(":").first}
15
+ # offenders.each do |offender|
16
+ # data[offender] = {"totals" => {}}
17
+ # connection.smembers("#{offender}:detected").each do |rule|
18
+ # data[offender]["totals"][rule] = connection.get "#{offender}:#{rule}:count"
19
+ # end
20
+ # end
21
+ # aggregate = Hash.new 0
22
+ # data.each {|ip,data| data["totals"].each {|rule,count| aggregate[rule] += count.to_i}}
23
+ # [data, aggregate]
24
+ [{},{}]
25
+ end
26
+
27
+ def self.activity(connection)
28
+ connection.lrange("#{@ip}:requests", 0, -1)
29
+ end
30
+
31
+ def self.worldview(connection, database)
32
+ data = {}
33
+ offenders = connection.keys("*:repsheet*").map {|o| o.split(":").first}
34
+ offenders.each do |address|
35
+ details = database.country(address)
36
+ next if details.nil?
37
+ data[address] = [details.latitude, details.longitude]
38
+ end
39
+ data
40
+ end
41
+
42
+ private
43
+
44
+ def self.triggered_rules(connection, actor)
45
+ connection.zrange("#{actor}:detected", 0, -1)
46
+ end
47
+
48
+ def self.optimized(connection)
49
+ suspects = {}
50
+
51
+ connection.zrevrangebyscore("offenders", "+inf", "0").each do |actor|
52
+ next if connection.get("#{actor}:repsheet:blacklist") == "true"
53
+ suspects[actor] = Hash.new 0
54
+ suspects[actor][:detected] = detected.join(", ")
55
+ suspects[actor][:total] = connection.zscore("offenders", actor).to_i
56
+ end
57
+
58
+ [suspects, blacklist(connection)]
59
+ end
60
+
61
+ def self.standard(connection)
62
+ suspects = {}
63
+
64
+ connection.keys("*:requests").map {|d| d.split(":").first}.reject {|ip| ip.empty?}.each do |actor|
65
+ detected = triggered_rules(connection, actor)
66
+ blacklist = connection.get("#{actor}:repsheet:blacklist")
67
+
68
+ if !detected.empty? && blacklist != "true"
69
+ suspects[actor] = Hash.new 0
70
+ suspects[actor][:detected] = detected.join(", ")
71
+ suspects[actor][:total] = score_actor(connection, actor)
72
+ end
73
+ end
74
+
75
+ [suspects, blacklist(connection)]
76
+ end
77
+
78
+ def self.blacklist(connection)
79
+ blacklisted = {}
80
+
81
+ connection.keys("*:*:blacklist").map {|d| d.split(":").first}.reject {|ip| ip.empty?}.each do |actor|
82
+ next unless connection.get("#{actor}:repsheet:blacklist") == "true"
83
+
84
+ blacklisted[actor] = Hash.new 0
85
+ blacklisted[actor][:detected] = triggered_rules(connection, actor).join(", ")
86
+ blacklisted[actor][:total] = score_actor(connection, actor)
87
+ end
88
+
89
+ blacklisted
90
+ end
91
+
92
+ def self.score_actor(connection, actor)
93
+ connection.zrange("#{actor}:detected", 0, -1).reduce(0) do |memo, rule|
94
+ memo += connection.zscore("#{actor}:detected", rule).to_i
95
+ end
96
+ end
97
+ end
@@ -35,7 +35,7 @@
35
35
  <div class="nav-collapse collapse">
36
36
  <ul class="nav">
37
37
  <li><a href="<%= @mount %>">Actors</a></li>
38
- <li><a href="<%= @mount %>breakdown">Breakdown</a></li>
38
+ <!-- <li><a href="<%= @mount %>breakdown">Breakdown</a></li> -->
39
39
  <li><a href="<%= @mount %>worldview">Worldview</a></li>
40
40
  </ul>
41
41
  </div>
@@ -90,7 +90,7 @@
90
90
  <div class="nav-collapse collapse">
91
91
  <ul class="nav">
92
92
  <li class="active"><a href="<%= @mount %>">Actors</a></li>
93
- <li><a href="<%= @mount %>breakdown">Breakdown</a></li>
93
+ <!-- <li><a href="<%= @mount %>breakdown">Breakdown</a></li> -->
94
94
  <li><a href="<%= @mount %>worldview">Worldview</a></li>
95
95
  </ul>
96
96
  </div>
@@ -38,7 +38,7 @@
38
38
  <div class="nav-collapse collapse">
39
39
  <ul class="nav">
40
40
  <li><a href="<%= @mount %>">Actors</a></li>
41
- <li><a href="<%= @mount %>breakdown">Breakdown</a></li>
41
+ <!-- <li><a href="<%= @mount %>breakdown">Breakdown</a></li> -->
42
42
  <li class="active"><a href="<%= @mount %>worldview">Worldview</a></li>
43
43
  </ul>
44
44
  </div>
@@ -1,3 +1,3 @@
1
1
  module RepsheetVisualizer
2
- VERSION = "0.1.6"
2
+ VERSION = "0.1.7"
3
3
  end
@@ -1 +1,2 @@
1
1
  require 'repsheet_visualizer/application/app.rb'
2
+ require 'repsheet_visualizer/application/backend.rb'
@@ -26,4 +26,5 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency "bundler"
27
27
  spec.add_development_dependency "rake"
28
28
  spec.add_development_dependency "unicorn"
29
+ spec.add_development_dependency "rspec"
29
30
  end
data/script/fill CHANGED
@@ -6,21 +6,29 @@ r = Redis.new
6
6
  r.flushdb
7
7
 
8
8
  255.times do |i|
9
- r.set("1.1.1.#{i}:950001:count", rand(1000))
10
- r.lpush("1.1.1.#{i}:requests", "request")
11
- r.sadd("1.1.1.#{i}:detected", "950001")
9
+ r.zincrby("1.1.1.#{i}:detected", rand(1000), "950001")
10
+ r.lpush("1.1.1.#{i}:requests", "123, Chrome, GET, foo, bar")
11
+
12
12
  if i > 220
13
13
  r.set("1.1.1.#{i}:repsheet:blacklist", "true")
14
14
  end
15
+
16
+ r.expire("1.1.1.#{i}:requests", (24 * 60 * 60))
17
+ r.expire("1.1.1.#{i}:detected", (24 * 60 * 60))
15
18
  end
16
19
 
17
20
  255.times do |i|
18
- r.lpush("2.2.2.#{i}:requests", "request")
21
+ r.lpush("1.1.1.#{i}:requests", "123, Chrome, POST, foo, bar")
19
22
  end
20
23
 
21
24
 
22
25
  20.times do |i|
23
- r.sadd("1.1.1.1:detected", "9900#{i + 10}")
24
- r.set("1.1.1.1:9900#{i + 10}:count", 1000)
26
+ r.zincrby("1.1.1.1:detected", 1000, "9900#{i + 10}")
25
27
  end
26
28
 
29
+ r.lpush("1.1.1.1:requests", "123, Chrome, PUT, foo, bar")
30
+ r.lpush("1.1.1.1:requests", "123, Chrome, DELETE, foo, bar")
31
+ r.lpush("1.1.1.1:requests", "123, Chrome, HEAD, foo, bar")
32
+ r.lpush("1.1.1.1:requests", "123, Chrome, TRACE, foo, bar")
33
+ r.lpush("1.1.1.1:requests", "123, Chrome, OPTIONS, foo, bar")
34
+ r.lpush("1.1.1.1:requests", "123, Chrome, CONNECT, foo, bar")
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Backend do
4
+ before(:each) {@connection = Redis.new}
5
+ after(:each) {@connection.flushdb}
6
+
7
+ describe ".summary" do
8
+ it "call the optimized routine if the proper keys exist" do
9
+ @connection.zincrby("offenders", 10, "1.1.1.1")
10
+ @connection.sadd("detected", "950001")
11
+ Backend.should_receive(:optimized) { [{},{}] }
12
+ Backend.summary(@connection)
13
+ end
14
+
15
+ it "calls the standard routine if they don't" do
16
+ @connection.del("offenders")
17
+ @connection.sadd("detected", "950001")
18
+ Backend.should_receive(:standard) { [{},{}] }
19
+ Backend.summary(@connection)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'redis'
4
+ require_relative '../lib/repsheet_visualizer/application/backend'
5
+
6
+ RSpec.configure do |config|
7
+ config.treat_symbols_as_metadata_keys_with_true_values = true
8
+ config.run_all_when_everything_filtered = true
9
+ config.filter_run :focus
10
+ config.order = 'random'
11
+ end
metadata CHANGED
@@ -1,126 +1,125 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: repsheet_visualizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
5
- prerelease:
4
+ version: 0.1.7
6
5
  platform: ruby
7
6
  authors:
8
7
  - Aaron Bedra
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-06-20 00:00:00.000000000 Z
11
+ date: 2013-07-25 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: geoip
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: json
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: redis
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: sinatra
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ! '>='
59
+ - - '>='
68
60
  - !ruby/object:Gem::Version
69
61
  version: '0'
70
62
  type: :runtime
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ! '>='
66
+ - - '>='
76
67
  - !ruby/object:Gem::Version
77
68
  version: '0'
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: bundler
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
- - - ! '>='
73
+ - - '>='
84
74
  - !ruby/object:Gem::Version
85
75
  version: '0'
86
76
  type: :development
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
- - - ! '>='
80
+ - - '>='
92
81
  - !ruby/object:Gem::Version
93
82
  version: '0'
94
83
  - !ruby/object:Gem::Dependency
95
84
  name: rake
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
- - - ! '>='
87
+ - - '>='
100
88
  - !ruby/object:Gem::Version
101
89
  version: '0'
102
90
  type: :development
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
- - - ! '>='
94
+ - - '>='
108
95
  - !ruby/object:Gem::Version
109
96
  version: '0'
110
97
  - !ruby/object:Gem::Dependency
111
98
  name: unicorn
112
99
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
100
  requirements:
115
- - - ! '>='
101
+ - - '>='
116
102
  - !ruby/object:Gem::Version
117
103
  version: '0'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
107
  requirements:
123
- - - ! '>='
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
124
123
  - !ruby/object:Gem::Version
125
124
  version: '0'
126
125
  description: Visualizer for Repsheet
@@ -132,6 +131,7 @@ extensions: []
132
131
  extra_rdoc_files: []
133
132
  files:
134
133
  - .gitignore
134
+ - .rspec
135
135
  - .rvmrc
136
136
  - Gemfile
137
137
  - Gemfile.lock
@@ -141,6 +141,7 @@ files:
141
141
  - bin/repsheet_visualizer
142
142
  - lib/repsheet_visualizer.rb
143
143
  - lib/repsheet_visualizer/application/app.rb
144
+ - lib/repsheet_visualizer/application/backend.rb
144
145
  - lib/repsheet_visualizer/application/public/css/blue/asc.gif
145
146
  - lib/repsheet_visualizer/application/public/css/blue/bg.gif
146
147
  - lib/repsheet_visualizer/application/public/css/blue/desc.gif
@@ -162,29 +163,32 @@ files:
162
163
  - lib/repsheet_visualizer/version.rb
163
164
  - repsheet_visualizer.gemspec
164
165
  - script/fill
166
+ - spec/backend_spec.rb
167
+ - spec/spec_helper.rb
165
168
  homepage: https://github.com/Repsheet/visualizer
166
169
  licenses:
167
170
  - MIT
171
+ metadata: {}
168
172
  post_install_message:
169
173
  rdoc_options: []
170
174
  require_paths:
171
175
  - lib
172
176
  required_ruby_version: !ruby/object:Gem::Requirement
173
- none: false
174
177
  requirements:
175
- - - ! '>='
178
+ - - '>='
176
179
  - !ruby/object:Gem::Version
177
180
  version: '0'
178
181
  required_rubygems_version: !ruby/object:Gem::Requirement
179
- none: false
180
182
  requirements:
181
- - - ! '>='
183
+ - - '>='
182
184
  - !ruby/object:Gem::Version
183
185
  version: '0'
184
186
  requirements: []
185
187
  rubyforge_project:
186
- rubygems_version: 1.8.23
188
+ rubygems_version: 2.0.3
187
189
  signing_key:
188
- specification_version: 3
190
+ specification_version: 4
189
191
  summary: A visualization package for Repsheet
190
- test_files: []
192
+ test_files:
193
+ - spec/backend_spec.rb
194
+ - spec/spec_helper.rb