inferno_core 1.1.2 → 1.2.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 +4 -4
- data/lib/inferno/apps/cli/execute_script.rb +918 -0
- data/lib/inferno/apps/cli/main.rb +46 -0
- data/lib/inferno/apps/cli/session/cancel_run.rb +47 -0
- data/lib/inferno/apps/cli/session/connection.rb +47 -0
- data/lib/inferno/apps/cli/session/create_session.rb +159 -0
- data/lib/inferno/apps/cli/session/errors.rb +45 -0
- data/lib/inferno/apps/cli/session/session_compare.rb +391 -0
- data/lib/inferno/apps/cli/session/session_data.rb +39 -0
- data/lib/inferno/apps/cli/session/session_details.rb +27 -0
- data/lib/inferno/apps/cli/session/session_results.rb +39 -0
- data/lib/inferno/apps/cli/session/session_status.rb +69 -0
- data/lib/inferno/apps/cli/session/start_run.rb +245 -0
- data/lib/inferno/apps/cli/session_commands.rb +66 -0
- data/lib/inferno/apps/cli/templates/%library_name%.gemspec.tt +1 -1
- data/lib/inferno/apps/cli/templates/.gitignore +4 -0
- data/lib/inferno/apps/cli/templates/README.md.tt +14 -0
- data/lib/inferno/apps/cli/templates/Rakefile.tt +13 -0
- data/lib/inferno/apps/cli/templates/execution_scripts/%library_name%_script.yaml.tt +20 -0
- data/lib/inferno/apps/cli/templates/execution_scripts/%library_name%_script_expected.json.tt +244 -0
- data/lib/inferno/apps/cli/templates/execution_scripts/README.md.tt +16 -0
- data/lib/inferno/dsl/fhir_resource_navigation.rb +145 -27
- data/lib/inferno/dsl/must_support_assessment.rb +93 -23
- data/lib/inferno/dsl/must_support_metadata_extractor.rb +139 -21
- data/lib/inferno/dsl/resume_test_route.rb +4 -3
- data/lib/inferno/exceptions.rb +6 -0
- data/lib/inferno/repositories/test_sessions.rb +3 -0
- data/lib/inferno/utils/execution_script_runner.rb +90 -0
- data/lib/inferno/utils/preset_processor.rb +2 -0
- data/lib/inferno/version.rb +1 -1
- metadata +18 -2
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
require 'csv'
|
|
2
|
+
require 'cgi'
|
|
3
|
+
require_relative 'session_details'
|
|
4
|
+
require_relative 'session_results'
|
|
5
|
+
|
|
6
|
+
module Inferno
|
|
7
|
+
module CLI
|
|
8
|
+
module Session
|
|
9
|
+
class SessionCompare < SessionResults
|
|
10
|
+
COMMAND_OPTIONS = {
|
|
11
|
+
expected_results_session: {
|
|
12
|
+
aliases: ['-s'],
|
|
13
|
+
type: :string,
|
|
14
|
+
desc: 'Session ID on the same server. The results of this indicated ' \
|
|
15
|
+
'session will be used as the expected results. When the compared ' \
|
|
16
|
+
"session's results do not match, comparison details will not be " \
|
|
17
|
+
'written to file (use the `-f` option).'
|
|
18
|
+
},
|
|
19
|
+
expected_results_file: {
|
|
20
|
+
aliases: ['-f'],
|
|
21
|
+
type: :string,
|
|
22
|
+
desc: 'Path to a file that contains the expected results. When the session ' \
|
|
23
|
+
'results do not match the expected results in the file, generated ' \
|
|
24
|
+
'comparison files will be placed in the same directory.'
|
|
25
|
+
},
|
|
26
|
+
compare_messages: {
|
|
27
|
+
aliases: ['-m'],
|
|
28
|
+
type: :boolean,
|
|
29
|
+
default: false,
|
|
30
|
+
desc: 'Compare messages when comparing results.'
|
|
31
|
+
},
|
|
32
|
+
compare_result_message: {
|
|
33
|
+
aliases: ['-r'],
|
|
34
|
+
type: :boolean,
|
|
35
|
+
default: false,
|
|
36
|
+
desc: 'Compare result_message when comparing results.'
|
|
37
|
+
},
|
|
38
|
+
normalized_strings: {
|
|
39
|
+
aliases: ['-n'],
|
|
40
|
+
type: :array,
|
|
41
|
+
desc: 'Literal strings or regexes to normalize away before comparing ' \
|
|
42
|
+
'(URL-encoded form of literal strings will also be normalized).'
|
|
43
|
+
}
|
|
44
|
+
}.freeze
|
|
45
|
+
def run
|
|
46
|
+
display_compared_results
|
|
47
|
+
if output_directory.present? && !results_match?
|
|
48
|
+
save_actual_results_to_file
|
|
49
|
+
save_comparison_csv_to_file
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
if results_match?
|
|
53
|
+
exit(0)
|
|
54
|
+
else
|
|
55
|
+
exit(3)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def results_timestamp
|
|
60
|
+
@results_timestamp = Time.now.strftime('%Y%m%d_%H%M%S')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Output directory is the dirname of the expected results file (-f).
|
|
64
|
+
# Returns nil when -f is not provided (e.g. session-to-session comparison),
|
|
65
|
+
# in which case no output files are written on mismatch.
|
|
66
|
+
def output_directory
|
|
67
|
+
options[:expected_results_file].present? && File.dirname(options[:expected_results_file])
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def output_file_prefix
|
|
71
|
+
return '' unless options[:expected_results_file].present?
|
|
72
|
+
|
|
73
|
+
basename = File.basename(options[:expected_results_file])
|
|
74
|
+
basename.end_with?('expected.json') ? basename.sub(/expected\.json$/, '') : ''
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def save_actual_results_to_file
|
|
78
|
+
actual_results_file_name = "#{output_file_prefix}actual_results_#{results_timestamp}.json"
|
|
79
|
+
File.write(File.join(output_directory, actual_results_file_name), session_results.to_json)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def save_comparison_csv_to_file
|
|
83
|
+
compared_csv_file_name = "#{output_file_prefix}compared_results_#{results_timestamp}.csv"
|
|
84
|
+
File.write(File.join(output_directory, compared_csv_file_name),
|
|
85
|
+
compared_results_as_csv)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def display_compared_results
|
|
89
|
+
output = {
|
|
90
|
+
matched: results_match?,
|
|
91
|
+
results: compared_results.map(&:to_h)
|
|
92
|
+
}
|
|
93
|
+
puts JSON.pretty_generate(output)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def compared_results_as_csv
|
|
97
|
+
CSV.generate do |csv|
|
|
98
|
+
csv << comparison_csv_header_row
|
|
99
|
+
compared_results.each do |result|
|
|
100
|
+
next unless result.different_result?
|
|
101
|
+
|
|
102
|
+
csv << result.comparison_csv_row
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def normalizing?
|
|
108
|
+
options[:normalized_strings].present?
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def comparison_csv_header_row
|
|
112
|
+
normalized_suffix = normalizing? ? ' (normalized)' : ''
|
|
113
|
+
header_row = ['id', 'short_id', 'type', 'different?', 'expected result', 'actual result']
|
|
114
|
+
if options[:compare_result_message]
|
|
115
|
+
header_row << 'result_message different?'
|
|
116
|
+
header_row << "expected result_message#{normalized_suffix}"
|
|
117
|
+
header_row << "actual result_message#{normalized_suffix}"
|
|
118
|
+
end
|
|
119
|
+
if options[:compare_messages]
|
|
120
|
+
header_row << 'messages different?'
|
|
121
|
+
header_row << "expected messages#{normalized_suffix}"
|
|
122
|
+
header_row << "actual messages#{normalized_suffix}"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
header_row
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def results_match?
|
|
129
|
+
compared_results.all?(&:same_result?)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def expected_results
|
|
133
|
+
@expected_results ||= if options[:expected_results_session].present?
|
|
134
|
+
results_for_session(options[:expected_results_session])
|
|
135
|
+
elsif options[:expected_results_file].present?
|
|
136
|
+
JSON.parse(File.read(options[:expected_results_file]))
|
|
137
|
+
else
|
|
138
|
+
puts({ errors: 'No expected results provided.' }.to_json)
|
|
139
|
+
exit(3)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def compared_results
|
|
144
|
+
@compared_results = match_result_ids(expected_results, session_results)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def session_details
|
|
148
|
+
@session_details ||= SessionDetails.new(session_id, options).details_for_session
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def short_id_map
|
|
152
|
+
@short_id_map ||= build_short_id_map(session_details['test_suite'])
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def build_short_id_map(runnable, map = {})
|
|
156
|
+
return map unless runnable.is_a?(Hash)
|
|
157
|
+
|
|
158
|
+
map[runnable['id']] = runnable['short_id'] if runnable['id'].present?
|
|
159
|
+
runnable['test_groups']&.each { |group| build_short_id_map(group, map) }
|
|
160
|
+
runnable['tests']&.each { |test| build_short_id_map(test, map) }
|
|
161
|
+
map
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def match_result_ids(expected, actual)
|
|
165
|
+
expected_hash = results_hash_by_id(expected)
|
|
166
|
+
actual_hash = results_hash_by_id(actual)
|
|
167
|
+
|
|
168
|
+
compared_results = expected_hash.map do |id, result|
|
|
169
|
+
ComparedTestResult.new(id, result, actual_hash[id], options, short_id_map)
|
|
170
|
+
end
|
|
171
|
+
actual_hash.keys.reject { |id| expected_hash.key?(id) }.each do |id|
|
|
172
|
+
compared_results << ComparedTestResult.new(id, nil, actual_hash[id], options, short_id_map)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
compared_results
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def results_hash_by_id(results)
|
|
179
|
+
results.each_with_object({}) do |result, hash|
|
|
180
|
+
key = result['test_id'] || result['test_group_id'] || result['test_suite_id']
|
|
181
|
+
hash[key] = result
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
class ComparedTestResult
|
|
186
|
+
attr_reader :id, :expected_result, :actual_result, :options, :short_id_map
|
|
187
|
+
|
|
188
|
+
def initialize(id, expected_result, actual_result, options, short_id_map = {})
|
|
189
|
+
@id = id
|
|
190
|
+
@expected_result = expected_result
|
|
191
|
+
@actual_result = actual_result
|
|
192
|
+
@options = options
|
|
193
|
+
@short_id_map = short_id_map
|
|
194
|
+
@same = same_results?
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def short_id
|
|
198
|
+
short_id_map[id]
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Parses a normalize entry into an array of [pattern, replacement] pairs.
|
|
202
|
+
# Entries may be:
|
|
203
|
+
# - A plain string: literal match, replacement defaults to '<NORMALIZED>'
|
|
204
|
+
# - A "/pattern/[flags]" string: compiled to Regexp, replacement defaults to '<NORMALIZED>'
|
|
205
|
+
# - A hash with 'pattern' or 'patterns' and optional 'replacement' keys (from YAML):
|
|
206
|
+
# pattern: '/code_challenge=[A-Za-z0-9+\/=_-]{20,}/'
|
|
207
|
+
# replacement: '<CODE_CHALLENGE>'
|
|
208
|
+
# Or multiple patterns sharing one replacement:
|
|
209
|
+
# patterns:
|
|
210
|
+
# - '/code_challenge=[A-Za-z0-9+\/=_-]{20,}/'
|
|
211
|
+
# - '/code_verifier=[A-Za-z0-9+\/=_-]{20,}/'
|
|
212
|
+
# replacement: '<PKCE_VALUE>'
|
|
213
|
+
def parse_normalize_entry(entry)
|
|
214
|
+
if entry.is_a?(Hash)
|
|
215
|
+
replacement = entry.fetch('replacement', '<NORMALIZED>')
|
|
216
|
+
Array(entry['patterns'] || entry['pattern']).map do |pattern|
|
|
217
|
+
[parse_pattern_string(pattern.to_s), replacement]
|
|
218
|
+
end
|
|
219
|
+
else
|
|
220
|
+
[[parse_pattern_string(entry.to_s), '<NORMALIZED>']]
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def parse_pattern_string(str)
|
|
225
|
+
return str unless (parsed_regex = str.match(%r{\A/(.+)/([imx]*)\z}m))
|
|
226
|
+
|
|
227
|
+
flags = 0
|
|
228
|
+
flags |= Regexp::IGNORECASE if parsed_regex[2].include?('i')
|
|
229
|
+
flags |= Regexp::MULTILINE if parsed_regex[2].include?('m')
|
|
230
|
+
flags |= Regexp::EXTENDED if parsed_regex[2].include?('x')
|
|
231
|
+
Regexp.new(parsed_regex[1], flags)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def normalize_string(str)
|
|
235
|
+
return str unless str.present?
|
|
236
|
+
|
|
237
|
+
Array(options[:normalized_strings]).reduce(str) do |s, entry|
|
|
238
|
+
parse_normalize_entry(entry).reduce(s) do |s2, (pattern, replacement)|
|
|
239
|
+
if pattern.is_a?(Regexp)
|
|
240
|
+
s2.gsub(pattern, replacement)
|
|
241
|
+
else
|
|
242
|
+
s2.gsub(pattern, replacement).gsub(CGI.escape(pattern), replacement)
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def normalizing?
|
|
249
|
+
options[:normalized_strings].present?
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def same_results?
|
|
253
|
+
return false unless type == 'Compared'
|
|
254
|
+
return false unless expected_result['result'] == actual_result['result']
|
|
255
|
+
|
|
256
|
+
if options[:compare_result_message] &&
|
|
257
|
+
normalize_string(expected_result['result_message']) != normalize_string(actual_result['result_message'])
|
|
258
|
+
return false
|
|
259
|
+
end
|
|
260
|
+
return false if options[:compare_messages] && !same_messages?
|
|
261
|
+
|
|
262
|
+
true
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def message_comparisons
|
|
266
|
+
@message_comparisons ||= build_message_comparisons
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
MESSAGE_TYPE_ORDER = { 'error' => 0, 'warning' => 1, 'info' => 2 }.freeze
|
|
270
|
+
UNKNOWN_MESSAGE_TYPE_ORDER = 99
|
|
271
|
+
|
|
272
|
+
def build_message_comparisons
|
|
273
|
+
expected_msgs = sorted_messages(expected_result)
|
|
274
|
+
actual_msgs = sorted_messages(actual_result)
|
|
275
|
+
max_length = [expected_msgs.size, actual_msgs.size].max
|
|
276
|
+
(0...max_length).map { |i| messages_match?(expected_msgs[i], actual_msgs[i]) }
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def sorted_messages(result)
|
|
280
|
+
Array(result&.dig('messages')).sort_by do |m|
|
|
281
|
+
[MESSAGE_TYPE_ORDER.fetch(m['type'].to_s, UNKNOWN_MESSAGE_TYPE_ORDER),
|
|
282
|
+
normalize_string(m['message'].to_s)]
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def messages_match?(expected_message, actual_message)
|
|
287
|
+
expected_message.present? && actual_message.present? && same_message?(expected_message, actual_message)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def same_messages?
|
|
291
|
+
return false unless expected_result['messages']&.size == actual_result['messages']&.size
|
|
292
|
+
return true unless expected_result['messages'].present?
|
|
293
|
+
|
|
294
|
+
message_comparisons.all?
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def same_message?(expected_message, actual_message)
|
|
298
|
+
expected_message['type'] == actual_message['type'] &&
|
|
299
|
+
normalize_string(expected_message['message']) == normalize_string(actual_message['message'])
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def same_result?
|
|
303
|
+
@same
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def different_result?
|
|
307
|
+
!same_result?
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def different_result_message?
|
|
311
|
+
return false unless type == 'Compared'
|
|
312
|
+
|
|
313
|
+
normalize_string(expected_result['result_message']) != normalize_string(actual_result['result_message'])
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def different_messages?
|
|
317
|
+
return false unless type == 'Compared'
|
|
318
|
+
|
|
319
|
+
!same_messages?
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def to_h
|
|
323
|
+
{
|
|
324
|
+
id: id,
|
|
325
|
+
type: type,
|
|
326
|
+
matched: same_result?,
|
|
327
|
+
expected_result: expected_result&.dig('result'),
|
|
328
|
+
actual_result: actual_result&.dig('result')
|
|
329
|
+
}.merge(optional_to_h_fields)
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def optional_to_h_fields
|
|
333
|
+
fields = {}
|
|
334
|
+
if options[:compare_result_message]
|
|
335
|
+
fields[:expected_result_message] = expected_result&.dig('result_message')
|
|
336
|
+
fields[:actual_result_message] = actual_result&.dig('result_message')
|
|
337
|
+
end
|
|
338
|
+
if options[:compare_messages]
|
|
339
|
+
fields[:expected_messages] = expected_result&.dig('messages')
|
|
340
|
+
fields[:actual_messages] = actual_result&.dig('messages')
|
|
341
|
+
end
|
|
342
|
+
fields
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def comparison_csv_row
|
|
346
|
+
row = [id, short_id, type, different_result?, expected_result&.dig('result'), actual_result&.dig('result')]
|
|
347
|
+
if options[:compare_result_message]
|
|
348
|
+
row << different_result_message?
|
|
349
|
+
row << normalize_string(expected_result&.dig('result_message'))
|
|
350
|
+
row << normalize_string(actual_result&.dig('result_message'))
|
|
351
|
+
end
|
|
352
|
+
if options[:compare_messages]
|
|
353
|
+
row << different_messages?
|
|
354
|
+
row << format_messages_for_csv(expected_result)
|
|
355
|
+
row << format_messages_for_csv(actual_result)
|
|
356
|
+
end
|
|
357
|
+
row
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def type
|
|
361
|
+
if expected_result.nil?
|
|
362
|
+
'Additional'
|
|
363
|
+
elsif actual_result.nil?
|
|
364
|
+
'Missing'
|
|
365
|
+
else
|
|
366
|
+
'Compared'
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def format_messages_for_csv(results)
|
|
371
|
+
return '' unless results&.dig('messages').present?
|
|
372
|
+
|
|
373
|
+
sorted_messages(results).each_with_index.map do |message, index|
|
|
374
|
+
message_text_for_csv(message, index)
|
|
375
|
+
end.join("\n")
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def message_text_for_csv(message, index)
|
|
379
|
+
prefix = message_comparisons[index] ? '- ' : '! '
|
|
380
|
+
text = normalize_string(message['message'].to_s)
|
|
381
|
+
.gsub("\r\n", '\n')
|
|
382
|
+
.gsub("\n", '\n')
|
|
383
|
+
.gsub("\r", '\r')
|
|
384
|
+
.gsub("\t", '\t')
|
|
385
|
+
"#{prefix}(#{message['type']}) \"#{text}\""
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require_relative 'connection'
|
|
2
|
+
require_relative 'errors'
|
|
3
|
+
|
|
4
|
+
module Inferno
|
|
5
|
+
module CLI
|
|
6
|
+
module Session
|
|
7
|
+
class SessionData
|
|
8
|
+
include Connection
|
|
9
|
+
include Errors
|
|
10
|
+
|
|
11
|
+
attr_accessor :session_id, :options
|
|
12
|
+
|
|
13
|
+
def initialize(session_id, options)
|
|
14
|
+
self.session_id = session_id
|
|
15
|
+
self.options = options
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run
|
|
19
|
+
check_session_exists
|
|
20
|
+
inputs = session_data
|
|
21
|
+
|
|
22
|
+
puts JSON.pretty_generate(inputs)
|
|
23
|
+
exit(0)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def session_data
|
|
27
|
+
@session_data ||= data_for_session(session_id)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def data_for_session(id)
|
|
31
|
+
response = get("api/test_sessions/#{id}/session_data", nil, content_type: 'application/json')
|
|
32
|
+
handle_web_api_error(response, :session_data) if response.status != 200
|
|
33
|
+
|
|
34
|
+
JSON.parse(response.body)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
require_relative 'connection'
|
|
3
|
+
require_relative 'errors'
|
|
4
|
+
|
|
5
|
+
module Inferno
|
|
6
|
+
module CLI
|
|
7
|
+
module Session
|
|
8
|
+
class SessionDetails
|
|
9
|
+
include Connection
|
|
10
|
+
include Errors
|
|
11
|
+
|
|
12
|
+
attr_accessor :session_id, :options
|
|
13
|
+
|
|
14
|
+
def initialize(session_id, options)
|
|
15
|
+
self.session_id = session_id
|
|
16
|
+
self.options = options
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def details_for_session
|
|
20
|
+
response = get("api/test_sessions/#{session_id}", nil, content_type: 'application/json')
|
|
21
|
+
handle_web_api_error(response, :session_details) if response.status != 200
|
|
22
|
+
JSON.parse(response.body)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require_relative 'connection'
|
|
2
|
+
require_relative 'errors'
|
|
3
|
+
|
|
4
|
+
module Inferno
|
|
5
|
+
module CLI
|
|
6
|
+
module Session
|
|
7
|
+
class SessionResults
|
|
8
|
+
include Connection
|
|
9
|
+
include Errors
|
|
10
|
+
|
|
11
|
+
attr_accessor :session_id, :options
|
|
12
|
+
|
|
13
|
+
def initialize(session_id, options)
|
|
14
|
+
self.session_id = session_id
|
|
15
|
+
self.options = options
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run
|
|
19
|
+
check_session_exists
|
|
20
|
+
results = session_results
|
|
21
|
+
|
|
22
|
+
puts JSON.pretty_generate(results)
|
|
23
|
+
exit(0)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def session_results
|
|
27
|
+
@session_results ||= results_for_session(session_id)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def results_for_session(id)
|
|
31
|
+
response = get("api/test_sessions/#{id}/results", nil, content_type: 'application/json')
|
|
32
|
+
handle_web_api_error(response, :session_results) if response.status != 200
|
|
33
|
+
|
|
34
|
+
JSON.parse(response.body)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
require_relative 'connection'
|
|
3
|
+
require_relative 'errors'
|
|
4
|
+
|
|
5
|
+
module Inferno
|
|
6
|
+
module CLI
|
|
7
|
+
module Session
|
|
8
|
+
class SessionStatus
|
|
9
|
+
include Connection
|
|
10
|
+
include Errors
|
|
11
|
+
|
|
12
|
+
attr_accessor :session_id, :options
|
|
13
|
+
|
|
14
|
+
def initialize(session_id, options)
|
|
15
|
+
self.session_id = session_id
|
|
16
|
+
self.options = options
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def run
|
|
20
|
+
session_status = status_for_session
|
|
21
|
+
puts JSON.pretty_generate(session_status)
|
|
22
|
+
exit(0)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def status_for_session
|
|
26
|
+
session_status = last_test_run
|
|
27
|
+
|
|
28
|
+
if session_status['id'].present?
|
|
29
|
+
run_id = session_status['id']
|
|
30
|
+
last_test_executed = last_test_executed(run_id)
|
|
31
|
+
if last_test_executed.present?
|
|
32
|
+
session_status['last_test_executed'] = last_test_executed['test_id']
|
|
33
|
+
if session_status['status'] == 'waiting'
|
|
34
|
+
session_status['wait_outputs'] = last_test_executed['outputs']
|
|
35
|
+
session_status['wait_result_message'] = last_test_executed['result_message']
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
session_status
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def last_test_run
|
|
44
|
+
response = get("api/test_sessions/#{session_id}/last_test_run", nil,
|
|
45
|
+
content_type: 'application/json')
|
|
46
|
+
handle_web_api_error(response, :last_session_run) if response.status != 200
|
|
47
|
+
return JSON.parse(response.body) if response.body.present?
|
|
48
|
+
|
|
49
|
+
# no execution has started yet for this session
|
|
50
|
+
{
|
|
51
|
+
'test_session_id' => session_id,
|
|
52
|
+
'status' => 'created'
|
|
53
|
+
}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def last_test_executed(run_id)
|
|
57
|
+
results = run_results(run_id)
|
|
58
|
+
results.sort_by { |r| r['updated_at'] }.reverse.find { |result| result['test_id'].present? }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def run_results(run_id)
|
|
62
|
+
response = get("api/test_runs/#{run_id}/results", nil, content_type: 'application/json')
|
|
63
|
+
handle_web_api_error(response, :test_run_results) if response.status != 200
|
|
64
|
+
JSON.parse(response.body)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|