oxidized-web 0.14.0 → 0.15.0

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.

Potentially problematic release.


This version of oxidized-web might be problematic. Click here for more details.

@@ -2,6 +2,6 @@
2
2
 
3
3
  module Oxidized
4
4
  module API
5
- WEB_VERSION = '0.14.0'
5
+ WEB_VERSION = '0.15.0'
6
6
  end
7
7
  end
@@ -20,10 +20,6 @@
20
20
  %a.nav-link{class: request.path_info == '/nodes/stats' ? 'active' : '',
21
21
  :'aria-current' => request.path_info == '/nodes/stats' ? 'page' : 'false',
22
22
  href: url_for('/nodes/stats')} Stats
23
- %li.nav-item
24
- %a.nav-link{class: request.path_info == '/migration' ? 'active' : '',
25
- :'aria-current' => request.path_info == '/migration' ? 'page' : 'false',
26
- href: url_for('/migration')} Migration
27
23
  %form.d-flex{role: 'search',
28
24
  action: url_for('/nodes/conf_search'),
29
25
  method: 'post'}
@@ -6,7 +6,6 @@ require 'tilt/haml'
6
6
  # rubocop:disable Lint/RedundantRequireStatement
7
7
  require 'pp'
8
8
  # rubocop:enable Lint/RedundantRequireStatement
9
- require 'oxidized/web/mig'
10
9
  require 'htmlentities'
11
10
  require 'charlock_holmes'
12
11
  module Oxidized
@@ -26,9 +25,17 @@ module Oxidized
26
25
  redirect url_for('/images/favicon.ico')
27
26
  end
28
27
 
29
- get '/nodes/:filter/:value.?:format?' do
28
+ # :filter can be "group" or "model"
29
+ # URL: /nodes/group/<GroupName>[.json]
30
+ # URL: /nodes/model/<ModelName>[.json]
31
+ # an optional .json extention returns the data as JSON
32
+ #
33
+ # as GroupName can include /, we use splat to match its value
34
+ # and extract the optional ".json" with route_parse
35
+ get '/nodes/:filter/*' do
36
+ value, @json = route_parse params[:splat].first
30
37
  @data = nodes.list.select do |node|
31
- next unless node[params[:filter].to_sym] == params[:value]
38
+ next unless node[params[:filter].to_sym] == value
32
39
 
33
40
  node[:status] = 'never'
34
41
  node[:time] = 'never'
@@ -100,7 +107,7 @@ module Oxidized
100
107
  out :text
101
108
  end
102
109
 
103
- # URL: /node/fetch/<group>/<node>.json
110
+ # URL: /node/fetch/<group>/<node>[.json]
104
111
  # <group> is optional, and not used
105
112
  # .json is optional. If given, will return 'ok'
106
113
  # if not, it redirects to /nodes
@@ -128,40 +135,17 @@ module Oxidized
128
135
  out :node
129
136
  end
130
137
 
131
- # redirect to the web page for rancid - oxidized migration
132
- get '/migration' do
133
- out :migration
134
- end
135
-
136
- # get the files send
137
- post '/migration' do
138
- number = params[:number].to_i
139
- cloginrc_file = params['cloginrc'][:tempfile]
140
- path_new_file = params['path_new_file']
141
-
142
- router_db_files = []
143
-
144
- i = 1
145
- while i <= number
146
- router_db_files.push({ file: params["file#{i}"][:tempfile], group: params["group#{i}"] })
147
- i += 1
148
- end
149
-
150
- migration = Mig.new(router_db_files, cloginrc_file, path_new_file)
151
- migration.go_rancid_migration
152
- redirect url_for('//nodes')
153
- end
154
-
155
- # show the lists of versions for a node
138
+ # display the versions of a node
139
+ # URL: /node/version[.json]?node_full=<GroupName/NodeName>
156
140
  get '/node/version.?:format?' do
157
141
  @data = nil
158
142
  @group = nil
159
143
  @node = nil
160
144
  node_full = params[:node_full]
161
145
  if node_full.include? '/'
162
- node_full = node_full.split('/')
146
+ node_full = node_full.rpartition("/")
163
147
  @group = node_full[0]
164
- @node = node_full[1]
148
+ @node = node_full[2]
165
149
  @data = nodes.version @node, @group
166
150
  else
167
151
  @node = node_full
@@ -182,7 +166,7 @@ module Oxidized
182
166
  }
183
167
 
184
168
  the_data = nodes.get_version node, @info[:group], @info[:oid]
185
- if params[:format] == 'json' || params[:format] == 'text'
169
+ if %w[json text].include?(params[:format])
186
170
  @data = the_data
187
171
  else
188
172
  utf8_encoded_content = convert_to_utf8(the_data)
@@ -231,6 +215,10 @@ module Oxidized
231
215
  redirect url_for("/node/version/diffs?node=#{params[:node]}&group=#{params[:group]}&oid=#{params[:oid]}&date=#{params[:date]}&num=#{params[:num]}&oid2=#{params[:oid2]}")
232
216
  end
233
217
 
218
+ # Taken von Haml 5.0, so it still works in 6.0
219
+ HTML_ESCAPE = { '&' => '&amp;', '<' => '&lt;', '>' => '&gt;', '"' => '&quot;', "'" => '&#39;' }.freeze
220
+ HTML_ESCAPE_ONCE_REGEX = /['"><]|&(?!(?:[a-zA-Z]+|#(?:\d+|[xX][0-9a-fA-F]+));)/
221
+
234
222
  private
235
223
 
236
224
  def out(template = :text)
@@ -328,9 +316,6 @@ module Oxidized
328
316
  { old_diff: old_diff, new_diff: new_diff }
329
317
  end
330
318
 
331
- # Taken von Haml 5.0, so it still works in 6.0
332
- HTML_ESCAPE = { '&' => '&amp;', '<' => '&lt;', '>' => '&gt;', '"' => '&quot;', "'" => '&#39;' }.freeze
333
- HTML_ESCAPE_ONCE_REGEX = /['"><]|&(?!(?:[a-zA-Z]+|#(?:\d+|[xX][0-9a-fA-F]+));)/
334
319
  def escape_once(text)
335
320
  text = text.to_s
336
321
  text.gsub(HTML_ESCAPE_ONCE_REGEX, HTML_ESCAPE)
data/oxidized-web.gemspec CHANGED
@@ -18,17 +18,18 @@ Gem::Specification.new do |s|
18
18
 
19
19
  s.metadata['rubygems_mfa_required'] = 'true'
20
20
 
21
- s.required_ruby_version = '>= 3.1'
21
+ s.required_ruby_version = '>= 3.1'
22
22
 
23
- s.add_runtime_dependency 'charlock_holmes', '~> 0.7.5'
24
- s.add_runtime_dependency 'emk-sinatra-url-for', '~> 0.2'
25
- s.add_runtime_dependency 'haml', '~> 6.0'
26
- s.add_runtime_dependency 'htmlentities', '~> 4.3'
27
- s.add_runtime_dependency 'json', '~> 2.3'
28
- s.add_runtime_dependency 'oxidized', '~> 0.26'
29
- s.add_runtime_dependency 'puma', '>= 3.11.4', '< 6.5.0'
30
- s.add_runtime_dependency 'sinatra', '>= 1.4.6', '< 5.0'
31
- s.add_runtime_dependency 'sinatra-contrib', '>= 1.4.6', '< 5.0'
23
+ s.add_dependency 'charlock_holmes', '~> 0.7.5'
24
+ s.add_dependency 'emk-sinatra-url-for', '~> 0.2'
25
+ s.add_dependency 'haml', '~> 6.0'
26
+ s.add_dependency 'htmlentities', '~> 4.3'
27
+ s.add_dependency 'json', '~> 2.3'
28
+ s.add_dependency 'ostruct', '~> 0.6'
29
+ s.add_dependency 'oxidized', '~> 0.31'
30
+ s.add_dependency 'puma', '>= 3.11.4'
31
+ s.add_dependency 'sinatra', '>= 1.4.6'
32
+ s.add_dependency 'sinatra-contrib', '>= 1.4.6'
32
33
 
33
34
  s.add_development_dependency 'bundler', '~> 2.2'
34
35
  s.add_development_dependency 'minitest', '~> 5.18'
@@ -36,11 +37,11 @@ Gem::Specification.new do |s|
36
37
  s.add_development_dependency 'rack-test', '~> 2.1'
37
38
  s.add_development_dependency 'rails_best_practices', '~> 1.19'
38
39
  s.add_development_dependency 'rake', '~> 13.0'
39
- s.add_development_dependency 'rubocop', '~> 1.64.1'
40
- s.add_development_dependency 'rubocop-minitest', '~> 0.35.0'
41
- s.add_development_dependency 'rubocop-rails', '~> 2.25.0'
42
- s.add_development_dependency 'rubocop-rake', '~> 0.6.0'
40
+ s.add_development_dependency 'rubocop', '~> 1.72.1'
41
+ s.add_development_dependency 'rubocop-minitest', '~> 0.37.1'
42
+ s.add_development_dependency 'rubocop-rails', '~> 2.30.0'
43
+ s.add_development_dependency 'rubocop-rake', '~> 0.7.1'
43
44
  s.add_development_dependency 'simplecov', '~> 0.22.0'
44
45
  s.add_development_dependency 'simplecov-cobertura', '~> 2.1.0'
45
- s.add_development_dependency 'simplecov-html', '~> 0.12.3'
46
+ s.add_development_dependency 'simplecov-html', '~> 0.13.1'
46
47
  end
data/package-lock.json CHANGED
@@ -60,38 +60,38 @@
60
60
  ]
61
61
  },
62
62
  "node_modules/datatables.net": {
63
- "version": "2.0.8",
64
- "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-2.0.8.tgz",
65
- "integrity": "sha512-4/2dYx4vl975zQqZbyoVEm0huPe61qffjBRby7K7V+y9E+ORq4R8KavkgrNMmIgO6cl85Pg4AvCbVjvPCIT1Yg==",
63
+ "version": "2.2.2",
64
+ "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-2.2.2.tgz",
65
+ "integrity": "sha512-gfODIKE3gpgbVeZy2QGj2Dq9roO6hy00S+k1knklrqlMyAMrh1wt0Q6ryBUM7gU96U77ysbq8dYhxFdmcC/oPQ==",
66
66
  "dependencies": {
67
67
  "jquery": ">=1.7"
68
68
  }
69
69
  },
70
70
  "node_modules/datatables.net-bs5": {
71
- "version": "2.0.8",
72
- "resolved": "https://registry.npmjs.org/datatables.net-bs5/-/datatables.net-bs5-2.0.8.tgz",
73
- "integrity": "sha512-rpz/yO2NZMP1Uso/sSsaFAKwdCjYPa1/KLxAVr0JNJJV9ygFLHcuKTcNmoc1cekcsjYcGyybWKaNu4NfpZ74vg==",
71
+ "version": "2.2.2",
72
+ "resolved": "https://registry.npmjs.org/datatables.net-bs5/-/datatables.net-bs5-2.2.2.tgz",
73
+ "integrity": "sha512-0mAbpUf0EpnIEc0RlN6vSrSk9y/+NuReiwDpjHYY3RfzdvH6Lt0+7Q9OU5RIbYxaFxES/z60thxdrw7IUFnBhw==",
74
74
  "dependencies": {
75
- "datatables.net": "2.0.8",
75
+ "datatables.net": "2.2.2",
76
76
  "jquery": ">=1.7"
77
77
  }
78
78
  },
79
79
  "node_modules/datatables.net-buttons": {
80
- "version": "3.0.2",
81
- "resolved": "https://registry.npmjs.org/datatables.net-buttons/-/datatables.net-buttons-3.0.2.tgz",
82
- "integrity": "sha512-J+vk4hLtTivnl+RxzpKPE7CG4ggdgHPQcHnpqViy9w6ia18Uh69dQktX6NJ87QrqNPCTMUyHDzUzsRFURG4/Fw==",
80
+ "version": "3.2.2",
81
+ "resolved": "https://registry.npmjs.org/datatables.net-buttons/-/datatables.net-buttons-3.2.2.tgz",
82
+ "integrity": "sha512-+aLTbkbksNmyGpK+8KXbpwYKXYOXvZQR2ySA/8oOQeJU53Xw/67cOHowenEr2d43/RLaz+I0zvV/1Yn+jMRiDw==",
83
83
  "dependencies": {
84
84
  "datatables.net": "^2",
85
85
  "jquery": ">=1.7"
86
86
  }
87
87
  },
88
88
  "node_modules/datatables.net-buttons-bs5": {
89
- "version": "3.0.2",
90
- "resolved": "https://registry.npmjs.org/datatables.net-buttons-bs5/-/datatables.net-buttons-bs5-3.0.2.tgz",
91
- "integrity": "sha512-whufHsfKgzzdmTqM7JnFUph5hveHTCAvs9N0CP+5t7k7sIr7b94rIxv22/2Wt4veLcd3v73NdhPWl4j/GfzyhA==",
89
+ "version": "3.2.2",
90
+ "resolved": "https://registry.npmjs.org/datatables.net-buttons-bs5/-/datatables.net-buttons-bs5-3.2.2.tgz",
91
+ "integrity": "sha512-xjUcbYCBHcUthD1pvo5ghTNjqE6fTMygRrKd0QjBHKQxcqxmHG/m0djD2s6cFBfm8oov132U7U2JCXgQifOoUA==",
92
92
  "dependencies": {
93
93
  "datatables.net-bs5": "^2",
94
- "datatables.net-buttons": "3.0.2",
94
+ "datatables.net-buttons": "3.2.2",
95
95
  "jquery": ">=1.7"
96
96
  }
97
97
  },
data/spec/node_spec.rb CHANGED
@@ -66,9 +66,9 @@ describe Oxidized::API::WebApp do
66
66
  end
67
67
 
68
68
  # Don't know if this feature is used by anyone...
69
- it 'attaches author/email/message to the commit when using put and json' do
69
+ it 'attaches user/email/message to the commit when using put and json' do
70
70
  data = {
71
- 'author' => 'me',
71
+ 'user' => 'me',
72
72
  'email' => 'me@example.com',
73
73
  'message' => 'minitest, rack/test & mock simply rock',
74
74
  'from' => 'unused variable!'
@@ -83,7 +83,7 @@ describe Oxidized::API::WebApp do
83
83
 
84
84
  it 'attaches data to the commit when using a group and put, then redirects' do
85
85
  data = {
86
- 'author' => 'me',
86
+ 'user' => 'me',
87
87
  'email' => 'me@example.com',
88
88
  'message' => 'minitest, rack/test & mock simply rock',
89
89
  'from' => 'unused variable!'
@@ -96,48 +96,4 @@ describe Oxidized::API::WebApp do
96
96
  _(last_response.location).must_equal 'http://example.org/nodes'
97
97
  end
98
98
  end
99
-
100
- describe '/node/version/view.?:format?' do
101
- it 'fetches a previous version from git' do
102
- @nodes.expects(:get_version).with('sw5', '', 'c8aa93cab5').returns('Old configuration of sw42')
103
-
104
- get '/node/version/view?node=sw5&group=&oid=c8aa93cab5&date=2024-06-07 08:27:37 +0200&num=2'
105
- _(last_response.ok?).must_equal true
106
- _(last_response.body.include?('Old configuration of sw42')).must_equal true
107
- end
108
-
109
- it 'does not display binary content' do
110
- @nodes.expects(:get_version).with('sw5', '', 'c8aa93cab5').returns("\xff\x42 binary content\x00")
111
-
112
- get '/node/version/view?node=sw5&group=&oid=c8aa93cab5&date=2024-06-07 08:27:37 +0200&num=2'
113
- _(last_response.ok?).must_equal true
114
- _(last_response.body.include?('cannot display')).must_equal true
115
- end
116
-
117
- it 'fetches a git-version when using a group containing /' do
118
- @nodes.expects(:get_version).with('sw5', 'my/group', 'c8aa93cab5').returns('Old configuration of sw42')
119
-
120
- get '/node/version/view?node=sw5&group=my/group&oid=c8aa93cab5&date=2024-06-07 08:27:37 +0200&num=2'
121
- _(last_response.ok?).must_equal true
122
- _(last_response.body.include?('Old configuration of sw42')).must_equal true
123
- end
124
-
125
- it 'does not encode html-chars in text-format' do
126
- configuration = "text &/<> \n ascii;"
127
- @nodes.expects(:get_version).with('sw5', '', 'c8aa93cab5').returns(configuration)
128
- get '/node/version/view?node=sw5&group=&oid=c8aa93cab5&date=2024-06-07 08:27:37 +0200&num=2&format=text'
129
-
130
- _(last_response.ok?).must_equal true
131
- _(last_response.body).must_equal configuration
132
- end
133
-
134
- it 'does not encode html-chars in json-format' do
135
- configuration = "text &/<> \n ascii;"
136
- @nodes.expects(:get_version).with('sw5', '', 'c8aa93cab5').returns(configuration)
137
- get '/node/version/view?node=sw5&group=&oid=c8aa93cab5&date=2024-06-07 08:27:37 +0200&num=2&format=json'
138
-
139
- _(last_response.ok?).must_equal true
140
- _(last_response.body).must_equal '["text &/<> \n"," ascii;"]'
141
- end
142
- end
143
99
  end
@@ -0,0 +1,102 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe Oxidized::API::WebApp do
4
+ include Rack::Test::Methods
5
+
6
+ def app
7
+ Oxidized::API::WebApp
8
+ end
9
+
10
+ before do
11
+ @nodes = mock('Oxidized::Nodes')
12
+ app.set(:nodes, @nodes)
13
+ end
14
+
15
+ describe '/node/version.?:format?' do
16
+ it 'fetches all versions of a node without a group' do
17
+ @nodes.expects(:version).with('sw5', nil).returns(
18
+ [{ oid: "C006", date: "2025-02-05 19:49:00 +0100" },
19
+ { oid: "C003", date: "2025-02-05 19:03:00 +0100" },
20
+ { oid: "C001", date: "2025-02-05 19:01:00 +0100" }]
21
+ )
22
+
23
+ get '/node/version?node_full=sw5'
24
+ _(last_response.ok?).must_equal true
25
+ _(last_response.body.include?(
26
+ "<tr>\n<td>3</td>\n<td>2025-02-05 19:49:00 +0100</td>\n"
27
+ )).must_equal true
28
+ end
29
+
30
+ it 'fetches all versions of a node with a group' do
31
+ @nodes.expects(:version).with('sw5', 'group1').returns(
32
+ [{ oid: "C006", date: "2025-02-05 19:49:00 +0100" },
33
+ { oid: "C003", date: "2025-02-05 19:03:00 +0100" },
34
+ { oid: "C001", date: "2025-02-05 19:01:00 +0100" }]
35
+ )
36
+
37
+ get '/node/version?node_full=group1/sw5'
38
+ _(last_response.ok?).must_equal true
39
+ _(last_response.body.include?(
40
+ "<tr>\n<td>3</td>\n<td>2025-02-05 19:49:00 +0100</td>\n"
41
+ )).must_equal true
42
+ end
43
+
44
+ it 'fetches all versions of a node with a group with /' do
45
+ @nodes.expects(:version).with('sw5', 'gr/oup1').returns(
46
+ [{ oid: "C006", date: "2025-02-05 19:49:00 +0100" },
47
+ { oid: "C003", date: "2025-02-05 19:03:00 +0100" },
48
+ { oid: "C001", date: "2025-02-05 19:01:00 +0100" }]
49
+ )
50
+
51
+ get '/node/version?node_full=gr/oup1/sw5'
52
+ _(last_response.ok?).must_equal true
53
+ _(last_response.body.include?(
54
+ "<tr>\n<td>3</td>\n<td>2025-02-05 19:49:00 +0100</td>\n"
55
+ )).must_equal true
56
+ end
57
+ end
58
+
59
+ describe '/node/version/view.?:format?' do
60
+ it 'fetches a previous version from git' do
61
+ @nodes.expects(:get_version).with('sw5', '', 'c8aa93cab5').returns('Old configuration of sw5')
62
+
63
+ get '/node/version/view?node=sw5&group=&oid=c8aa93cab5&date=2024-06-07 08:27:37 +0200&num=2'
64
+ _(last_response.ok?).must_equal true
65
+ _(last_response.body.include?('Old configuration of sw5')).must_equal true
66
+ end
67
+
68
+ it 'does not display binary content' do
69
+ @nodes.expects(:get_version).with('sw5', '', 'c8aa93cab5').returns("\xff\x42 binary content\x00")
70
+
71
+ get '/node/version/view?node=sw5&group=&oid=c8aa93cab5&date=2024-06-07 08:27:37 +0200&num=2'
72
+ _(last_response.ok?).must_equal true
73
+ _(last_response.body.include?('cannot display')).must_equal true
74
+ end
75
+
76
+ it 'fetches a git-version when using a group containing /' do
77
+ @nodes.expects(:get_version).with('sw5', 'my/group', 'c8aa93cab5').returns('Old configuration of sw5')
78
+
79
+ get '/node/version/view?node=sw5&group=my/group&oid=c8aa93cab5&date=2024-06-07 08:27:37 +0200&num=2'
80
+ _(last_response.ok?).must_equal true
81
+ _(last_response.body.include?('Old configuration of sw5')).must_equal true
82
+ end
83
+
84
+ it 'does not encode html-chars in text-format' do
85
+ configuration = "text &/<> \n ascii;"
86
+ @nodes.expects(:get_version).with('sw5', '', 'c8aa93cab5').returns(configuration)
87
+ get '/node/version/view?node=sw5&group=&oid=c8aa93cab5&date=2024-06-07 08:27:37 +0200&num=2&format=text'
88
+
89
+ _(last_response.ok?).must_equal true
90
+ _(last_response.body).must_equal configuration
91
+ end
92
+
93
+ it 'does not encode html-chars in json-format' do
94
+ configuration = "text &/<> \n ascii;"
95
+ @nodes.expects(:get_version).with('sw5', '', 'c8aa93cab5').returns(configuration)
96
+ get '/node/version/view?node=sw5&group=&oid=c8aa93cab5&date=2024-06-07 08:27:37 +0200&num=2&format=json'
97
+
98
+ _(last_response.ok?).must_equal true
99
+ _(last_response.body).must_equal '["text &/<> \n"," ascii;"]'
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,57 @@
1
+ require_relative 'spec_helper'
2
+ require 'json'
3
+
4
+ describe Oxidized::API::WebApp do
5
+ include Rack::Test::Methods
6
+
7
+ def app
8
+ Oxidized::API::WebApp
9
+ end
10
+
11
+ before do
12
+ @nodes = mock('Oxidized::Nodes')
13
+ @nodes.expects(:list).returns(
14
+ [{ name: 'sw4', ip: '10.10.10.10', model: 'ios', time: 'time', mtime: 'mtime' },
15
+ { name: 'sw5', ip: '10.10.10.5', model: 'ios', time: 'time', mtime: 'mtime' },
16
+ { name: 'sw6', ip: '10.10.10.6', model: 'ios', time: 'time', mtime: 'mtime' },
17
+ { name: 'sw7', ip: '10.10.10.7', model: 'ios', time: 'time', mtime: 'mtime', group: 'group1' },
18
+ { name: 'sw8', ip: '10.10.10.8', model: 'aos', time: 'time', mtime: 'mtime', group: 'group1' },
19
+ { name: 'sw9', ip: '10.10.10.9', model: 'aos', time: 'time', mtime: 'mtime', group: 'gr/oup1' }]
20
+ )
21
+ app.set(:nodes, @nodes)
22
+ end
23
+
24
+ describe '/nodes.?:format?' do
25
+ it 'shows all nodes' do
26
+ get '/nodes.json'
27
+
28
+ _(last_response.ok?).must_equal true
29
+ result = JSON.parse(last_response.body)
30
+ _(result.length).must_equal 6
31
+ end
32
+ end
33
+
34
+ describe '/nodes/:filter/*' do
35
+ it 'shows all nodes of a group' do
36
+ get '/nodes/group/group1.json'
37
+
38
+ _(last_response.ok?).must_equal true
39
+ result = JSON.parse(last_response.body)
40
+ _(result.length).must_equal 2
41
+ end
42
+ it 'shows all nodes of a group with /' do
43
+ get '/nodes/group/gr/oup1.json'
44
+
45
+ _(last_response.ok?).must_equal true
46
+ result = JSON.parse(last_response.body)
47
+ _(result.length).must_equal 1
48
+ end
49
+ it 'shows all nodes of a model' do
50
+ get '/nodes/model/ios.json'
51
+
52
+ _(last_response.ok?).must_equal true
53
+ result = JSON.parse(last_response.body)
54
+ _(result.length).must_equal 4
55
+ end
56
+ end
57
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  # Needed to get the error output on the console and not in last_response.body
2
2
  ENV['APP_ENV'] = 'test'
3
3
 
4
+ require 'simplecov'
5
+ SimpleCov.start
6
+
4
7
  require 'minitest/autorun'
5
8
  require 'rack/test'
6
9
  require 'oxidized'