marty 1.1.5 → 1.1.6
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 +4 -4
- data/Gemfile.lock +1 -1
- data/app/controllers/marty/diagnostic_controller.rb +15 -416
- data/app/models/diagnostic/aws/ec2_instance.rb +100 -0
- data/app/models/diagnostic/base.rb +69 -0
- data/app/models/diagnostic/base_collection.rb +10 -0
- data/app/models/diagnostic/collection.rb +10 -0
- data/app/models/diagnostic/delayed_job.rb +48 -0
- data/app/models/diagnostic/env.rb +35 -0
- data/app/models/diagnostic/environment.rb +35 -0
- data/app/models/diagnostic/fatal.rb +12 -0
- data/app/models/diagnostic/helper.rb +11 -0
- data/app/models/diagnostic/nodes.rb +18 -0
- data/app/models/diagnostic/reporter.rb +108 -0
- data/app/models/diagnostic/request.rb +28 -0
- data/app/models/diagnostic/version.rb +17 -0
- data/app/models/marty/helper.rb +0 -8
- data/app/views/marty/diagnostic/diag.html.erb +15 -19
- data/app/views/marty/diagnostic/op.html.erb +18 -19
- data/delorean/diagnostics.dl +1 -1
- data/lib/diagnostic/database.rb +28 -0
- data/lib/diagnostic/node.rb +35 -0
- data/lib/diagnostic/packer.rb +47 -0
- data/lib/marty/version.rb +1 -1
- data/spec/controllers/diagnostic_controller_spec.rb +18 -175
- data/spec/models/diagnostic/base_spec.rb +98 -0
- data/spec/models/diagnostic/collection_spec.rb +32 -0
- data/spec/models/diagnostic/delayed_job_spec.rb +46 -0
- data/spec/models/diagnostic/reporter_spec.rb +319 -0
- metadata +21 -1
@@ -0,0 +1,28 @@
|
|
1
|
+
class Diagnostic::Request
|
2
|
+
def self.request
|
3
|
+
raise 'Request object has not been been injected into #{name}' unless
|
4
|
+
@@request
|
5
|
+
|
6
|
+
@@request
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.request= req
|
10
|
+
@@request = req
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.params
|
14
|
+
request.params
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.scope
|
18
|
+
params[:scope]
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.op
|
22
|
+
params[:op]
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.ssl?
|
26
|
+
request.port == 443
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Diagnostic::Version < Diagnostic::Base
|
2
|
+
def self.generate
|
3
|
+
pack do
|
4
|
+
begin
|
5
|
+
message = `cd #{Rails.root.to_s}; git describe --tags --always;`.strip
|
6
|
+
rescue
|
7
|
+
message = error("Failed accessing git")
|
8
|
+
end
|
9
|
+
{
|
10
|
+
'Marty' => Marty::VERSION,
|
11
|
+
'Delorean' => Delorean::VERSION,
|
12
|
+
'Mcfly' => Mcfly::VERSION,
|
13
|
+
'Git' => message,
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/app/models/marty/helper.rb
CHANGED
@@ -6,14 +6,6 @@ class Marty::Helper
|
|
6
6
|
Kernel.sleep seconds
|
7
7
|
end
|
8
8
|
|
9
|
-
delorean_fn :my_ip, sig:0 do
|
10
|
-
Socket.ip_address_list.detect{|intf| intf.ipv4_private?}.ip_address
|
11
|
-
end
|
12
|
-
|
13
|
-
delorean_fn :git, sig:0 do
|
14
|
-
[my_ip, ENV['DELAYED_VER']]
|
15
|
-
end
|
16
|
-
|
17
9
|
delorean_fn :range_step, sig: 3 do
|
18
10
|
|rstart, rend, step|
|
19
11
|
(rstart..rend).step(step).to_a
|
@@ -1,34 +1,30 @@
|
|
1
|
-
<% inconsistent = diff(data) %>
|
2
1
|
<h3><%=name.demodulize%></h3>
|
3
|
-
<%='<h3 class="error">
|
4
|
-
inconsistent%>
|
2
|
+
<%='<h3 class="error">Inconsistency Detected</h3>' unless consistent %>
|
5
3
|
<div class="wrapper">
|
6
4
|
<table>
|
7
|
-
<%#
|
5
|
+
<%# create node table headers if applicable %>
|
8
6
|
<tr>
|
9
|
-
<%='<th></th>'
|
7
|
+
<%='<th></th>' unless success %>
|
10
8
|
<% data.keys.each do |node| %>
|
11
|
-
<th <%='colspan="2"'
|
12
|
-
<%=
|
13
|
-
(type == 'local' ? 'local' : 'consistent') %>
|
9
|
+
<th <%='colspan="2"' if success %> scope="col">
|
10
|
+
<%= success ? 'consistent' : node %>
|
14
11
|
</th>
|
15
|
-
<% break
|
12
|
+
<% break if success %>
|
16
13
|
<% end %>
|
17
14
|
</tr>
|
18
|
-
<%#
|
19
|
-
<% data
|
15
|
+
<%# create row headers and display node results %>
|
16
|
+
<% data.values.map{|v| v.keys}.flatten.compact.uniq.each do |test| %>
|
20
17
|
<tr>
|
21
|
-
<th scope="row"><%=
|
22
|
-
<%
|
23
|
-
|
24
|
-
|
25
|
-
(result[key].to_s != targets[key].to_s)) ? 'error' :
|
26
|
-
'passed' %>">
|
27
|
-
<%= simple_format(result[key].to_s) %>
|
18
|
+
<th class="data" scope="row"><%= test %></th>
|
19
|
+
<% data.values.each do |diagnostic| %>
|
20
|
+
<td class="overflow <%= display_info_css(diagnostic[test]) %>">
|
21
|
+
<%= display_info_description(diagnostic[test]) %>
|
28
22
|
</td>
|
29
|
-
<% break
|
23
|
+
<% break if success %>
|
30
24
|
<% end %>
|
31
25
|
</tr>
|
32
26
|
<% end %>
|
33
27
|
</table>
|
34
28
|
</div>
|
29
|
+
|
30
|
+
<%= display_alert_message if fatal? %>
|
@@ -10,13 +10,12 @@
|
|
10
10
|
color: #333333;
|
11
11
|
font-family: Arial, Helvetica, Verdana, sans-serif;
|
12
12
|
font-weight: normal;
|
13
|
-
font-size: 9pt;
|
13
|
+
font-size: 9pt;}
|
14
14
|
table { border: none;
|
15
15
|
border-collapse: collapse;
|
16
16
|
display: inline-block;
|
17
17
|
margin: 0px 5px 0px 5px;
|
18
|
-
text-align: left;
|
19
|
-
}
|
18
|
+
text-align: left;}
|
20
19
|
th { padding: 9px;
|
21
20
|
border: none;
|
22
21
|
background-color: #d6d6d6 }
|
@@ -24,26 +23,26 @@
|
|
24
23
|
padding: 9px;
|
25
24
|
}
|
26
25
|
tr { text-align: center;}
|
27
|
-
|
26
|
+
td.desc { font-size: 10pt;}
|
28
27
|
td.passed { background-color: #d0e9c6 }
|
28
|
+
td.inconsistent { background-color: #ffa500 }
|
29
29
|
td.error { background-color: #ff5555;
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
color: #ffffff;}
|
31
|
+
td.overflow { max-width: 350px;
|
32
|
+
overflow: auto;}
|
33
|
+
th.data { text-align: left;}
|
33
34
|
p { white-space: pre-line;}
|
34
|
-
h1 { display: block;
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
35
|
+
h1.application { display: block;
|
36
|
+
margin: 0px auto 40px auto;
|
37
|
+
padding: 8px;
|
38
|
+
background-color: #1aaa55;
|
39
|
+
font-size: 18pt;
|
40
|
+
color: #ffffff;
|
41
|
+
line-height: 1.5em;}
|
41
42
|
h1.error {background-color: #ff5555;}
|
42
43
|
h2 {text-align: center;}
|
43
44
|
h3.error {color: red;}
|
44
45
|
h3 {text-align: center;}
|
45
|
-
td.overflow { max-width: 350px;
|
46
|
-
overflow: auto; }
|
47
46
|
.wrapper {
|
48
47
|
overflow-x: auto;
|
49
48
|
white-space: nowrap;
|
@@ -52,11 +51,11 @@
|
|
52
51
|
</head>
|
53
52
|
<body>
|
54
53
|
<div style="text-align:center">
|
55
|
-
<h1 class="<%= 'error'
|
56
|
-
<%=Rails.application.class.parent_name%> Diagnostic
|
54
|
+
<h1 class="application <%= 'error' unless @result['errors'].empty? %>">
|
55
|
+
<%=ENV['DIAG_TITLE'] || Rails.application.class.parent_name%> Diagnostic
|
57
56
|
</h1>
|
58
57
|
<h3><i><%= DateTime.now %></i></h3>
|
59
|
-
<%== @result %>
|
58
|
+
<%== @result['display'] %>
|
60
59
|
</div>
|
61
60
|
</body>
|
62
61
|
</html>
|
data/delorean/diagnostics.dl
CHANGED
@@ -3,7 +3,7 @@ Delay:
|
|
3
3
|
# the delay parameter delays the next created job so that a different
|
4
4
|
# worker is more likely to claim it
|
5
5
|
delay =? 2.6
|
6
|
-
ver = Marty::Helper.sleep(delay) &&
|
6
|
+
ver = Marty::Helper.sleep(delay) && Diagnostic::Helper.git
|
7
7
|
res = [(Delay() | "ver") for i in Marty::Helper.range_step(0, count, 1)]
|
8
8
|
result = [r.to_a for r in res]
|
9
9
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Diagnostic::Database
|
2
|
+
def self.db_name
|
3
|
+
ActiveRecord::Base.connection_config[:database]
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.db_server_name
|
7
|
+
ActiveRecord::Base.connection_config[:host] || 'undefined'
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.db_adapter_name
|
11
|
+
ActiveRecord::Base.connection.adapter_name
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.db_time
|
15
|
+
ActiveRecord::Base.connection.execute('SELECT NOW();')[0]['now']
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.db_version
|
19
|
+
ActiveRecord::Base.connection.execute('SELECT VERSION();')[0]['version']
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.db_schema
|
23
|
+
current = ActiveRecord::Migrator.current_version
|
24
|
+
raise "Migration is needed.\nCurrent Version: #{current}" if
|
25
|
+
ActiveRecord::Migrator.needs_migration?
|
26
|
+
current
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Diagnostic::Node
|
2
|
+
def self.my_ip
|
3
|
+
begin
|
4
|
+
Socket.ip_address_list.detect{|intf| intf.ipv4_private?}.ip_address
|
5
|
+
rescue => e
|
6
|
+
e.message
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.get_postgres_connections
|
11
|
+
conn = ActiveRecord::Base.connection.execute('SELECT datname,'\
|
12
|
+
'application_name,'\
|
13
|
+
'state,'\
|
14
|
+
'pid,'\
|
15
|
+
'client_addr '\
|
16
|
+
'FROM pg_stat_activity')
|
17
|
+
conn.each_with_object({}) do |conn, h|
|
18
|
+
h[conn['datname']] ||= []
|
19
|
+
h[conn['datname']] << conn.except('datname')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.get_target_connections target
|
24
|
+
get_postgres_connections[Diagnostic::Database.db_name].select{|conn|
|
25
|
+
conn['application_name'].include?(target)
|
26
|
+
}.map{|conn|
|
27
|
+
conn['client_addr'] == '127.0.0.1' ? my_ip : conn['client_addr']
|
28
|
+
}.uniq.compact
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.get_nodes
|
32
|
+
nodes = get_target_connections('Passenger')
|
33
|
+
nodes.empty? ? [my_ip] : nodes
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Diagnostic::Packer
|
2
|
+
# expects a block that returns either a String or a Hash value and formats
|
3
|
+
# it into a diagnostic info object.
|
4
|
+
def pack include_ip=true
|
5
|
+
begin
|
6
|
+
data = yield
|
7
|
+
info = case data
|
8
|
+
when Hash
|
9
|
+
is_valid_info?(data) ? {name.demodulize => data} :
|
10
|
+
data.each_with_object({}) do
|
11
|
+
|(key, value), hash|
|
12
|
+
case value
|
13
|
+
when String
|
14
|
+
hash[key] = create_info(value)
|
15
|
+
when Hash
|
16
|
+
raise "Invalid Diagnostic Info #{value}" unless
|
17
|
+
is_valid_info?(value)
|
18
|
+
|
19
|
+
hash[key] = value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
when String
|
23
|
+
{name.demodulize => create_info(data)}
|
24
|
+
else
|
25
|
+
raise "Invalid Data Type: (#{data}, #{data.class}) "\
|
26
|
+
"`package` expects a String or Hash value."
|
27
|
+
end
|
28
|
+
include_ip ? {Diagnostic::Node.my_ip => info} : info
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_info description, status=true, consistent=nil
|
33
|
+
{
|
34
|
+
'description' => description,
|
35
|
+
'status' => status,
|
36
|
+
'consistent' => consistent
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def is_valid_info? info
|
41
|
+
info.keys.to_set == Set['description', 'status', 'consistent']
|
42
|
+
end
|
43
|
+
|
44
|
+
def error description
|
45
|
+
create_info(description, false)
|
46
|
+
end
|
47
|
+
end
|
data/lib/marty/version.rb
CHANGED
@@ -4,190 +4,33 @@ module Marty
|
|
4
4
|
before(:each) { @routes = Marty::Engine.routes }
|
5
5
|
let(:json_response) { JSON.parse(response.body) }
|
6
6
|
|
7
|
-
def git
|
8
|
-
begin
|
9
|
-
message = `cd #{Rails.root.to_s}; git describe --tags --always;`.strip
|
10
|
-
rescue
|
11
|
-
message = error("Failed accessing git")
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def version
|
16
|
-
{
|
17
|
-
"Marty" => Marty::VERSION,
|
18
|
-
"Delorean" => Delorean::VERSION,
|
19
|
-
"Mcfly" => Mcfly::VERSION,
|
20
|
-
"Git" => git,
|
21
|
-
}
|
22
|
-
end
|
23
|
-
|
24
|
-
def environment
|
25
|
-
rbv = "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} (#{RUBY_PLATFORM})"
|
26
|
-
{'Environment' => Rails.env,
|
27
|
-
'Rails' => Rails.version,
|
28
|
-
'Netzke Core' => Netzke::Core::VERSION,
|
29
|
-
'Netzke Basepack' => Netzke::Basepack::VERSION,
|
30
|
-
'Ruby' => rbv,
|
31
|
-
'RubyGems' => Gem::VERSION,
|
32
|
-
'Database Adapter' => described_class::Database.db_adapter_name,
|
33
|
-
'Database Server' => described_class::Database.db_server_name,
|
34
|
-
'Database Version' => described_class::Database.db_version,
|
35
|
-
'Database Schema Version' => described_class::Database.db_schema}
|
36
|
-
end
|
37
|
-
|
38
|
-
def version_display
|
39
|
-
<<-ERB
|
40
|
-
<h3>Version</h3>
|
41
|
-
<div class="wrapper">
|
42
|
-
<table>
|
43
|
-
<tr>
|
44
|
-
<th colspan="2" scope="col">consistent</th>
|
45
|
-
</tr>
|
46
|
-
<tr>
|
47
|
-
<th scope="row">Marty</th>
|
48
|
-
<td class="overflow passed"><p>#{Marty::VERSION}</p>
|
49
|
-
</td>
|
50
|
-
</tr>
|
51
|
-
<tr>
|
52
|
-
<th scope="row">Delorean</th>
|
53
|
-
<td class="overflow passed"><p>#{Delorean::VERSION}</p>
|
54
|
-
</td>
|
55
|
-
</tr>
|
56
|
-
<tr>
|
57
|
-
<th scope="row">Mcfly</th>
|
58
|
-
<td class="overflow passed"><p>#{Mcfly::VERSION}</p>
|
59
|
-
</td>
|
60
|
-
</tr>
|
61
|
-
<tr>
|
62
|
-
<th scope="row">Git</th>
|
63
|
-
<td class="overflow passed"><p>#{git}</p>
|
64
|
-
</td>
|
65
|
-
</tr>
|
66
|
-
</table>
|
67
|
-
</div>
|
68
|
-
ERB
|
69
|
-
end
|
70
|
-
|
71
|
-
def version_display_fail val
|
72
|
-
<<-ERB
|
73
|
-
<h3>Version</h3>
|
74
|
-
<h3 class="error">Issues Detected </h3>
|
75
|
-
<div class="wrapper">
|
76
|
-
<table>
|
77
|
-
<tr>
|
78
|
-
<th></th>
|
79
|
-
<th scope="col">node1</th>
|
80
|
-
<th scope="col">node2</th>
|
81
|
-
</tr>
|
82
|
-
<tr>
|
83
|
-
<th scope="row">Marty</th>
|
84
|
-
<td class="overflow passed"><p>#{Marty::VERSION}</p></td>
|
85
|
-
<td class="overflow error"><p>#{val}</p></td>
|
86
|
-
</tr>
|
87
|
-
<tr>
|
88
|
-
<th scope="row">Delorean</th>
|
89
|
-
<td class="overflow passed"><p>#{Delorean::VERSION}</p></td>
|
90
|
-
<td class="overflow passed"><p>#{Delorean::VERSION}</p></td>
|
91
|
-
</tr>
|
92
|
-
<tr>
|
93
|
-
<th scope="row">Mcfly</th>
|
94
|
-
<td class="overflow passed"><p>#{Mcfly::VERSION}</p></td>
|
95
|
-
<td class="overflow passed"><p>#{Mcfly::VERSION}</p></td>
|
96
|
-
</tr>
|
97
|
-
<tr>
|
98
|
-
<th scope="row">Git</th>
|
99
|
-
<td class="overflow passed"><p>#{git}</p></td>
|
100
|
-
<td class="overflow passed"><p>#{git}</p></td>
|
101
|
-
</tr>
|
102
|
-
</table>
|
103
|
-
</div>
|
104
|
-
ERB
|
105
|
-
end
|
106
|
-
|
107
|
-
def minimize(str)
|
108
|
-
str.gsub(/\s+/, "")
|
109
|
-
end
|
110
|
-
|
111
7
|
describe 'GET #op' do
|
112
|
-
it 'returns http success
|
113
|
-
get :op,
|
8
|
+
it 'returns http success' do
|
9
|
+
get :op, format: :json, op: 'version'
|
114
10
|
expect(response).to have_http_status(:success)
|
115
11
|
end
|
116
12
|
|
117
|
-
it '
|
118
|
-
get :op, format: :json, op: 'version'
|
119
|
-
expect(
|
13
|
+
it 'a request injects the request object into Diagnostic classes' do
|
14
|
+
get :op, format: :json, op: 'version'
|
15
|
+
expect(Diagnostic::Reporter.request).not_to eq(nil)
|
120
16
|
end
|
121
17
|
|
122
|
-
it 'returns the
|
123
|
-
get :op, format: :json, op: '
|
124
|
-
expect(assigns('result')).to eq(environment)
|
125
|
-
end
|
126
|
-
|
127
|
-
it 'produces an html display of the diagnostic (version)' do
|
128
|
-
test = described_class::Version.display({'stub' => version})
|
129
|
-
expect(minimize(test)).to eq(minimize(version_display))
|
130
|
-
end
|
131
|
-
|
132
|
-
it 'masks consistent nodes for display (version)' do
|
133
|
-
data = {'node1' => version, 'node2' => version}
|
134
|
-
test = described_class::Version.display(data)
|
135
|
-
expect(minimize(test)).to eq(minimize(version_display))
|
136
|
-
end
|
137
|
-
|
138
|
-
it 'displays all nodes when there is an inconsistent node (version)' do
|
139
|
-
ver = '0.0.0'
|
140
|
-
data = {'node1' => version, 'node2' => version + {'Marty' => ver}}
|
141
|
-
expected = version_display_fail(ver)
|
142
|
-
test = described_class::Version.display(data)
|
143
|
-
expect(minimize(test)).to eq(minimize(expected))
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
describe 'diagnostic classes and aggregate functions' do
|
148
|
-
it 'has access to DiagnosticController request' do
|
149
|
-
get :op, op: 'version', scope: 'local'
|
150
|
-
expect(described_class::Base.request).not_to eq(nil)
|
151
|
-
end
|
152
|
-
|
153
|
-
it 'can aggregate diagnostics and return appropriate JSON' do
|
154
|
-
# simulate open-uri nodal diag request
|
155
|
-
uri_stub = {:open => nil, :readlines => [version.to_json]}
|
156
|
-
nodes = ['node1', 'node2', 'node3']
|
157
|
-
expected = nodes.each_with_object({}){|n, h| h[n] = version}
|
158
|
-
|
159
|
-
# mock nodes and diag request to node
|
160
|
-
allow(described_class::Base.request).to receive(:port).and_return(3000)
|
161
|
-
allow(described_class::Base).to receive(:get_nodes).and_return(nodes)
|
162
|
-
allow(described_class::Base).to receive_message_chain(uri_stub)
|
18
|
+
it 'returns the current version JSON' do
|
19
|
+
get :op, format: :json, op: 'version', data: 'true'
|
163
20
|
|
164
|
-
#
|
165
|
-
|
166
|
-
|
167
|
-
|
21
|
+
# generate version data and declare all values consistent
|
22
|
+
versions = Diagnostic::Version.generate.each_with_object({}){
|
23
|
+
|(n, v),h|
|
24
|
+
h[n] = v.each{|t, r| r['consistent'] = true}
|
25
|
+
}
|
168
26
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
expect(described_class::Base.diff(inconsistent)).to eq(true)
|
175
|
-
expect(described_class::Base.diff(consistent)).to eq(false)
|
176
|
-
expect(described_class::Version.diff(inconsistent)).to eq(true)
|
177
|
-
expect(described_class::Version.diff(consistent)).to eq(false)
|
178
|
-
end
|
179
|
-
end
|
27
|
+
expected = {
|
28
|
+
'data' => {
|
29
|
+
'Diagnostic::Version' => versions
|
30
|
+
}
|
31
|
+
}
|
180
32
|
|
181
|
-
|
182
|
-
error_free = version
|
183
|
-
error_test = version + {'Git' => described_class::Base.
|
184
|
-
error('Failed accessing git')}
|
185
|
-
aggregate_failures do
|
186
|
-
expect(described_class::Base.errors(error_free)).to eq(0)
|
187
|
-
expect(described_class::Base.errors(error_test)).to eq(1)
|
188
|
-
expect(described_class::Version.errors(error_free)).to eq(0)
|
189
|
-
expect(described_class::Version.errors(error_test)).to eq(1)
|
190
|
-
end
|
33
|
+
expect(assigns('result')).to eq(expected)
|
191
34
|
end
|
192
35
|
end
|
193
36
|
end
|