testscript_engine 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/testscript_engine +108 -0
- data/lib/testscript_engine/assertion.rb +284 -0
- data/lib/testscript_engine/message_handler.rb +332 -0
- data/lib/testscript_engine/operation.rb +160 -0
- data/lib/testscript_engine/testreport_handler.rb +247 -0
- data/lib/testscript_engine/testscript_runnable.rb +252 -0
- data/lib/testscript_engine.rb +134 -0
- metadata +140 -0
@@ -0,0 +1,247 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
4
|
+
# #
|
5
|
+
# TestReportHandler Module #
|
6
|
+
# #
|
7
|
+
# The TestReportHandler (handler) module is intended to be imported into the #
|
8
|
+
# TestScriptRunnable (runnable) class. The handler instantiates a #
|
9
|
+
# TestReportBuilder (builder) object tailored to the parent runnable instance. #
|
10
|
+
# In executing a runnable, calls (i.e. 'Pass', 'Fail') are made to the handler #
|
11
|
+
# module -- which then directs the builder instance to update its report #
|
12
|
+
# accordingly. Think of the handler as the 'API' to interact with the #
|
13
|
+
# TestReport output by the execution of a runnable. Each time the runnable is #
|
14
|
+
# executed, it instantiates a new builder instance, using the initial builder #
|
15
|
+
# as a template. #
|
16
|
+
# #
|
17
|
+
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
18
|
+
|
19
|
+
module TestReportHandler
|
20
|
+
def testreport
|
21
|
+
report_builder.report
|
22
|
+
end
|
23
|
+
|
24
|
+
def fresh_testreport
|
25
|
+
@report_builder = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def report_builder
|
29
|
+
@report_builder ||= fresh_builder
|
30
|
+
end
|
31
|
+
|
32
|
+
def fresh_builder
|
33
|
+
builder_template.clone
|
34
|
+
end
|
35
|
+
|
36
|
+
def builder_template
|
37
|
+
@builder_template ||= TestReportBuilder.new(script)
|
38
|
+
end
|
39
|
+
|
40
|
+
# TODO: Remove!
|
41
|
+
def pass(message = nil)
|
42
|
+
report_builder.pass
|
43
|
+
end
|
44
|
+
|
45
|
+
def fail(message = nil)
|
46
|
+
report_builder.fail(message)
|
47
|
+
end
|
48
|
+
|
49
|
+
def skip(message = nil)
|
50
|
+
report_builder.skip(message)
|
51
|
+
end
|
52
|
+
|
53
|
+
def warning(message = nil)
|
54
|
+
report_builder.warning(message)
|
55
|
+
end
|
56
|
+
|
57
|
+
def error(message = nil)
|
58
|
+
report_builder.error(message)
|
59
|
+
end
|
60
|
+
|
61
|
+
def finalize_report
|
62
|
+
report_builder.finalize_report
|
63
|
+
testreport
|
64
|
+
end
|
65
|
+
|
66
|
+
def cascade_skips(number_to_skip)
|
67
|
+
while number_to_skip > 0
|
68
|
+
report_builder.skip
|
69
|
+
number_to_skip -= 1
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# A 'script' method ought to be defined in the klass
|
74
|
+
# that includes the handler - if 'script' is undefined,
|
75
|
+
# this feeds an empty testscript to the builder
|
76
|
+
def script
|
77
|
+
begin
|
78
|
+
super
|
79
|
+
rescue NoMethodError
|
80
|
+
FHIR::TestScript.new
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class TestReportBuilder
|
85
|
+
attr_accessor :pass_count, :total_test_count, :actions
|
86
|
+
|
87
|
+
def actions
|
88
|
+
@actions ||= []
|
89
|
+
end
|
90
|
+
|
91
|
+
def report
|
92
|
+
@report ||= FHIR::TestReport.new
|
93
|
+
end
|
94
|
+
|
95
|
+
def initialize(testscript_blueprint = nil)
|
96
|
+
add_boilerplate(testscript_blueprint)
|
97
|
+
build_setup(testscript_blueprint.setup)
|
98
|
+
build_test(testscript_blueprint.test)
|
99
|
+
build_teardown(testscript_blueprint.teardown)
|
100
|
+
|
101
|
+
self.pass_count = 0
|
102
|
+
self.total_test_count = actions.length
|
103
|
+
end
|
104
|
+
|
105
|
+
def build_setup(setup_blueprint)
|
106
|
+
return unless setup_blueprint
|
107
|
+
|
108
|
+
actions = setup_blueprint.action.map { |action| build_action(action) }
|
109
|
+
report.setup = FHIR::TestReport::Setup.new(action: actions)
|
110
|
+
end
|
111
|
+
|
112
|
+
def build_test(test_blueprint)
|
113
|
+
return if test_blueprint.empty?
|
114
|
+
|
115
|
+
report.test = test_blueprint.map do |test|
|
116
|
+
actions = test.action.map { |action| build_action(action) }
|
117
|
+
FHIR::TestReport::Test.new(action: actions)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def build_teardown(teardown_blueprint)
|
122
|
+
return unless teardown_blueprint
|
123
|
+
|
124
|
+
actions = teardown_blueprint.action.map { |action| build_action(action) }
|
125
|
+
report.teardown = FHIR::TestReport::Teardown.new(action: actions)
|
126
|
+
end
|
127
|
+
|
128
|
+
def build_action(action_blueprint)
|
129
|
+
phase = action_blueprint.class.to_s.split("::")[2]
|
130
|
+
|
131
|
+
action_definition = {
|
132
|
+
id: action_blueprint.id,
|
133
|
+
operation: build_operation(action_blueprint.operation),
|
134
|
+
assert: (build_assert(action_blueprint.assert) unless phase == 'Teardown')
|
135
|
+
}
|
136
|
+
|
137
|
+
"FHIR::TestReport::#{phase}::Action".constantize.new(action_definition)
|
138
|
+
end
|
139
|
+
|
140
|
+
def build_operation(operation_blueprint)
|
141
|
+
return unless operation_blueprint
|
142
|
+
|
143
|
+
operation_def = {
|
144
|
+
id: operation_blueprint.label || operation_blueprint.id || 'unlabeled operation',
|
145
|
+
message: operation_blueprint.description
|
146
|
+
}
|
147
|
+
|
148
|
+
operation = FHIR::TestReport::Setup::Action::Operation.new(operation_def)
|
149
|
+
store_action(operation)
|
150
|
+
operation
|
151
|
+
end
|
152
|
+
|
153
|
+
def build_assert(assert_blueprint)
|
154
|
+
return unless assert_blueprint
|
155
|
+
|
156
|
+
assert_def = {
|
157
|
+
id: assert_blueprint.label || assert_blueprint.id || 'unlabeled assert',
|
158
|
+
message: assert_blueprint.description
|
159
|
+
}
|
160
|
+
|
161
|
+
assert = FHIR::TestReport::Setup::Action::Assert.new(assert_def)
|
162
|
+
store_action(assert)
|
163
|
+
assert
|
164
|
+
end
|
165
|
+
|
166
|
+
def add_boilerplate(testscript_blueprint)
|
167
|
+
report.result = 'pending'
|
168
|
+
report.status = 'in-progress'
|
169
|
+
report.tester = 'The MITRE Corporation'
|
170
|
+
report.id = testscript_blueprint.id&.gsub(/(?i)testscript/, 'testreport')
|
171
|
+
report.name = testscript_blueprint.name&.gsub(/(?i)testscript/, 'TestReport')
|
172
|
+
report.testScript = FHIR::Reference.new({
|
173
|
+
reference: testscript_blueprint.url,
|
174
|
+
type: "http://hl7.org/fhir/R4B/testscript.html"
|
175
|
+
})
|
176
|
+
end
|
177
|
+
|
178
|
+
def finalize_report
|
179
|
+
report.issued = DateTime.now.to_s
|
180
|
+
report.status = 'completed'
|
181
|
+
report.score = (self.pass_count.to_f / self.total_test_count).round(2) * 100
|
182
|
+
report.result = (report.score == 100.0 ? 'pass' : 'fail')
|
183
|
+
end
|
184
|
+
|
185
|
+
def action
|
186
|
+
actions.first
|
187
|
+
end
|
188
|
+
|
189
|
+
def store_action(action)
|
190
|
+
actions.concat(Array(action))
|
191
|
+
end
|
192
|
+
|
193
|
+
def next_action
|
194
|
+
actions.shift
|
195
|
+
finalize_report if actions.empty?
|
196
|
+
end
|
197
|
+
|
198
|
+
def pass
|
199
|
+
action.result = 'pass'
|
200
|
+
self.pass_count += 1
|
201
|
+
next_action
|
202
|
+
end
|
203
|
+
|
204
|
+
def skip(message = nil)
|
205
|
+
action.result = 'skip'
|
206
|
+
action.message = message if message
|
207
|
+
self.total_test_count -= 1
|
208
|
+
next_action
|
209
|
+
end
|
210
|
+
|
211
|
+
def warning(message = nil)
|
212
|
+
action.result = 'warning'
|
213
|
+
action.message = message if message
|
214
|
+
next_action
|
215
|
+
end
|
216
|
+
|
217
|
+
def fail(message = nil)
|
218
|
+
action.result = 'fail'
|
219
|
+
action.message = message if message
|
220
|
+
next_action
|
221
|
+
end
|
222
|
+
|
223
|
+
def error(message = nil)
|
224
|
+
action.result = 'error'
|
225
|
+
action.message = message if message
|
226
|
+
next_action
|
227
|
+
end
|
228
|
+
|
229
|
+
def clone
|
230
|
+
builder_dup = self.deep_dup
|
231
|
+
builder_dup.actions.clear
|
232
|
+
builder_dup.instance_eval('@report = FHIR::TestReport.new(self.report.to_hash)')
|
233
|
+
|
234
|
+
clone_actions(builder_dup.report.setup, builder_dup)
|
235
|
+
builder_dup.report.test.each { |test| clone_actions(test, builder_dup) }
|
236
|
+
clone_actions(builder_dup.report.teardown, builder_dup)
|
237
|
+
|
238
|
+
builder_dup
|
239
|
+
end
|
240
|
+
|
241
|
+
def clone_actions(report_phase, clone)
|
242
|
+
report_phase.try(:action)&.each do |action|
|
243
|
+
clone.store_action(action.operation || action.assert)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
@@ -0,0 +1,252 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'operation'
|
3
|
+
require_relative 'assertion'
|
4
|
+
require_relative 'message_handler'
|
5
|
+
require_relative 'testreport_handler'
|
6
|
+
|
7
|
+
class TestScriptRunnable
|
8
|
+
include Operation
|
9
|
+
include Assertion
|
10
|
+
prepend MessageHandler
|
11
|
+
include TestReportHandler
|
12
|
+
|
13
|
+
attr_accessor :script, :client, :reply
|
14
|
+
|
15
|
+
def id_map
|
16
|
+
@id_map ||= {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def fixtures
|
20
|
+
@fixtures ||= {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def request_map
|
24
|
+
@request_map ||= {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def response_map
|
28
|
+
@response_map ||= {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def autocreate
|
32
|
+
@autocreate ||= []
|
33
|
+
end
|
34
|
+
|
35
|
+
def autodelete_ids
|
36
|
+
@autodelete_ids ||= []
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize(script)
|
40
|
+
raise ArgumentError.new(messages(:bad_script)) unless script.is_a?(FHIR::TestScript)
|
41
|
+
raise ArgumentError.new(messages(:invalid_script_input)) unless script.valid?
|
42
|
+
|
43
|
+
@script = script
|
44
|
+
load_fixtures
|
45
|
+
end
|
46
|
+
|
47
|
+
def run(client)
|
48
|
+
@client = client
|
49
|
+
|
50
|
+
fresh_testreport
|
51
|
+
|
52
|
+
preprocess
|
53
|
+
setup
|
54
|
+
test
|
55
|
+
teardown
|
56
|
+
postprocessing
|
57
|
+
|
58
|
+
finalize_report
|
59
|
+
end
|
60
|
+
|
61
|
+
def preprocess
|
62
|
+
return info(:no_preprocess) if autocreate.empty?
|
63
|
+
autocreate.each do |fixture|
|
64
|
+
begin
|
65
|
+
client.send(*build_request((create_operation(fixture))))
|
66
|
+
rescue => e
|
67
|
+
error(:uncaught_error, e.message)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def setup
|
73
|
+
return info(:no_setup) unless script.setup
|
74
|
+
handle_actions(script.setup.action, true)
|
75
|
+
end
|
76
|
+
|
77
|
+
def test
|
78
|
+
script.test.each { |test| handle_actions(test.action, false) }
|
79
|
+
end
|
80
|
+
|
81
|
+
def teardown
|
82
|
+
return info(:no_teardown) unless script.teardown
|
83
|
+
handle_actions(script.teardown.action, false)
|
84
|
+
end
|
85
|
+
|
86
|
+
def postprocessing
|
87
|
+
return info(:no_postprocess) if autocreate.empty?
|
88
|
+
|
89
|
+
autodelete_ids.each do |fixture_id|
|
90
|
+
begin
|
91
|
+
client.send(*build_request((delete_operation(fixture_id))))
|
92
|
+
rescue => e
|
93
|
+
error(:uncaught_error, e.message)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
@ended = nil
|
98
|
+
@id_map = {}
|
99
|
+
@request_map = {}
|
100
|
+
@response_map = {}
|
101
|
+
end
|
102
|
+
|
103
|
+
def handle_actions(actions, end_on_fail)
|
104
|
+
return abort_test(actions) if @ended
|
105
|
+
@modify_report = true
|
106
|
+
current_action = 0
|
107
|
+
|
108
|
+
begin
|
109
|
+
actions.each do |action|
|
110
|
+
current_action += 1
|
111
|
+
if action.operation
|
112
|
+
execute(action.operation)
|
113
|
+
elsif action.respond_to?(:assert)
|
114
|
+
begin
|
115
|
+
evaluate(action.assert)
|
116
|
+
rescue AssertionException => ae
|
117
|
+
if ae.outcome == :skip
|
118
|
+
skip(:eval_assert_result, ae.details)
|
119
|
+
elsif ae.outcome == :fail
|
120
|
+
next warning(:eval_assert_result, ae.details) if action.assert.warningOnly
|
121
|
+
if end_on_fail
|
122
|
+
@ended = true
|
123
|
+
fail(:eval_assert_result, ae.details)
|
124
|
+
cascade_skips_with_message(actions, current_action) unless current_action == actions.length
|
125
|
+
return
|
126
|
+
else
|
127
|
+
fail(:eval_assert_result, ae.details)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
rescue OperationException => oe
|
134
|
+
error(oe.details)
|
135
|
+
if end_on_fail
|
136
|
+
@ended = true
|
137
|
+
cascade_skips_with_message(actions, current_action) unless current_action == actions.length
|
138
|
+
end
|
139
|
+
rescue => e
|
140
|
+
error(:uncaught_error, e.message)
|
141
|
+
cascade_skips_with_message(actions, current_action) unless current_action == actions.length
|
142
|
+
end
|
143
|
+
|
144
|
+
@modify_report = false
|
145
|
+
end
|
146
|
+
|
147
|
+
def cascade_skips_with_message(actions, current_action)
|
148
|
+
actions_to_skip = actions.slice(current_action, actions.length)
|
149
|
+
cascade_skips(:skip_on_fail, actions_to_skip, actions_to_skip.length)
|
150
|
+
end
|
151
|
+
|
152
|
+
def abort_test(actions_to_skip)
|
153
|
+
cascade_skips(:abort_test, actions_to_skip, 'setup', actions_to_skip.length)
|
154
|
+
end
|
155
|
+
|
156
|
+
def load_fixtures
|
157
|
+
script.fixture.each do |fixture|
|
158
|
+
next warning(:no_static_fixture_id) unless fixture.id
|
159
|
+
next warning(:no_static_fixture_resource) unless fixture.resource
|
160
|
+
|
161
|
+
resource = get_resource_from_ref(fixture.resource)
|
162
|
+
next warning(:bad_static_fixture_reference) unless resource
|
163
|
+
|
164
|
+
fixtures[fixture.id] = resource
|
165
|
+
autocreate << fixture.id if fixture.autocreate
|
166
|
+
autodelete_ids << fixture.id if fixture.autodelete
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def get_resource_from_ref(reference)
|
171
|
+
return warning(:bad_reference) unless reference.is_a?(FHIR::Reference)
|
172
|
+
|
173
|
+
ref = reference.reference
|
174
|
+
return warning(:no_reference) unless ref
|
175
|
+
return warning(:unsupported_ref, ref) if ref.start_with? 'http'
|
176
|
+
|
177
|
+
if ref.start_with? '#'
|
178
|
+
contained = script.contained.find { |r| r.id == ref[1..] }
|
179
|
+
return contained || warning(:no_contained_resource, ref)
|
180
|
+
end
|
181
|
+
|
182
|
+
begin
|
183
|
+
fixture_path = script.url.split('/')[0...-1].join('/') + '/fixtures'
|
184
|
+
filepath = File.expand_path(ref, File.absolute_path(fixture_path))
|
185
|
+
file = File.open(filepath, 'r:UTF-8', &:read)
|
186
|
+
file.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
|
187
|
+
resource = FHIR.from_contents(file)
|
188
|
+
info(:loaded_static_fixture, ref, script.id)
|
189
|
+
return resource
|
190
|
+
rescue => e
|
191
|
+
warning(:resource_extraction, ref, e.message)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def storage(op)
|
196
|
+
@reply = client.reply
|
197
|
+
reply.nil? ? return : client.reply = nil
|
198
|
+
|
199
|
+
request_map[op.requestId] = reply.request if op.requestId
|
200
|
+
response_map[op.responseId] = reply.response if op.responseId
|
201
|
+
|
202
|
+
(reply.resource = FHIR.from_contents(reply.response&.[](:body).to_s)) rescue {}
|
203
|
+
(reply.response[:body] = reply.resource)
|
204
|
+
response_map[op.responseId][:body] = reply.resource if reply.resource and response_map[op.responseId]
|
205
|
+
|
206
|
+
if op.targetId and (reply.request[:method] == :delete) and [200, 201, 204].include?(reply.response[:code])
|
207
|
+
id_map.delete(op.targetId) and return
|
208
|
+
end
|
209
|
+
|
210
|
+
dynamic_id = reply.resource&.id || begin
|
211
|
+
reply.response&.[](:headers)&.[]('location')&.remove(reply.request[:url].to_s)&.split('/')&.[](2)
|
212
|
+
end
|
213
|
+
|
214
|
+
id_map[op.responseId] = dynamic_id if op.responseId and dynamic_id
|
215
|
+
id_map[op.sourceId] = dynamic_id if op.sourceId and dynamic_id
|
216
|
+
return
|
217
|
+
end
|
218
|
+
|
219
|
+
def find_resource id
|
220
|
+
fixtures[id] || response_map[id]&.[](:body)
|
221
|
+
end
|
222
|
+
|
223
|
+
def replace_variables placeholder
|
224
|
+
return placeholder unless placeholder&.include? '${'
|
225
|
+
replaced = placeholder.clone
|
226
|
+
|
227
|
+
script.variable.each do |var|
|
228
|
+
next unless replaced.include? "${#{var.name}}"
|
229
|
+
replacement = evaluate_variable(var)
|
230
|
+
replaced.gsub!("${#{var.name}}", replacement) if replacement
|
231
|
+
end
|
232
|
+
|
233
|
+
return replaced
|
234
|
+
end
|
235
|
+
|
236
|
+
def evaluate_variable var
|
237
|
+
if var.expression
|
238
|
+
evaluate_expression(var.expression, find_resource(var.sourceId))
|
239
|
+
elsif var.path
|
240
|
+
evaluate_path(var.path, find_resource(var.sourceId))
|
241
|
+
elsif var.headerField
|
242
|
+
headers = response_map[var.sourceId]&.[](:headers)
|
243
|
+
headers&.find { |h, v| h == var.headerField || h == var.headerField.downcase }&.last
|
244
|
+
end || var.defaultValue
|
245
|
+
end
|
246
|
+
|
247
|
+
def evaluate_expression(expression, resource)
|
248
|
+
return unless expression and resource
|
249
|
+
|
250
|
+
return FHIRPath.evaluate(expression, resource.to_hash)
|
251
|
+
end
|
252
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'pry-nav'
|
2
|
+
require 'fhir_client'
|
3
|
+
require 'fhir_models'
|
4
|
+
require_relative 'testscript_engine/testscript_runnable'
|
5
|
+
require_relative 'testscript_engine/message_handler'
|
6
|
+
|
7
|
+
class TestScriptEngine
|
8
|
+
prepend MessageHandler
|
9
|
+
|
10
|
+
attr_accessor :endpoint, :testscript_path, :testreport_path
|
11
|
+
|
12
|
+
def scripts
|
13
|
+
@scripts ||= {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def runnables
|
17
|
+
@runnables ||= {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def reports
|
21
|
+
@reports ||= {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def client
|
25
|
+
@client ||= begin
|
26
|
+
info(:begin_initialize_client)
|
27
|
+
client = FHIR::Client.new(endpoint || 'localhost:3000')
|
28
|
+
info(:finish_initialize_client)
|
29
|
+
client
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(endpoint, testscript_path, testreport_path)
|
34
|
+
self.endpoint = endpoint
|
35
|
+
self.testscript_path = testscript_path
|
36
|
+
self.testreport_path = testreport_path
|
37
|
+
self.debug_mode = true
|
38
|
+
end
|
39
|
+
|
40
|
+
# TODO: Tie-in stronger validation. Possibly, Inferno validator.
|
41
|
+
def valid_testscript? script
|
42
|
+
return (script.is_a? FHIR::TestScript) && script.valid?
|
43
|
+
end
|
44
|
+
|
45
|
+
# @path [String] Optional, specifies the path to the folder containing the
|
46
|
+
# TestScript Resources to-be loaded into the engine.
|
47
|
+
def load_scripts
|
48
|
+
if File.file?(testscript_path)
|
49
|
+
on_deck = [testscript_path]
|
50
|
+
elsif File.directory?(testscript_path)
|
51
|
+
on_deck = Dir.glob (["#{testscript_path}/**/*.{json}", "#{testscript_path}/**/*.{xml}"])
|
52
|
+
end
|
53
|
+
on_deck.each do |resource|
|
54
|
+
next if resource.include? "/fixtures/"
|
55
|
+
|
56
|
+
begin
|
57
|
+
script = FHIR.from_contents File.read(resource)
|
58
|
+
if valid_testscript? script
|
59
|
+
script.url = resource
|
60
|
+
if scripts[script.id]
|
61
|
+
info(:overwrite_existing_script, script.id)
|
62
|
+
else
|
63
|
+
info(:loaded_script, script.id)
|
64
|
+
end
|
65
|
+
scripts[script.id] = script
|
66
|
+
else
|
67
|
+
info(:invalid_script, resource)
|
68
|
+
end
|
69
|
+
rescue
|
70
|
+
info(:bad_serialized_script, resource)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# @script [FHIR::TestScript] Optional, a singular TestScript resource to be
|
76
|
+
# transformed into a runnable. If no resource is
|
77
|
+
# given, all stored TestScript are by default
|
78
|
+
# transformed into and stored as runnables.
|
79
|
+
def make_runnables script = nil
|
80
|
+
begin
|
81
|
+
if valid_testscript? script
|
82
|
+
runnables[script.id] = TestScriptRunnable.new script
|
83
|
+
info(:created_runnable, script.id)
|
84
|
+
else
|
85
|
+
scripts.each do |_, script|
|
86
|
+
runnables[script.id] = TestScriptRunnable.new script
|
87
|
+
info(:created_runnable, script.id)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
rescue => e
|
91
|
+
error(:unable_to_create_runnable, script.id)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# TODO: Clean-up, possibly modularize into a pretty_print type method
|
96
|
+
# @runnable_id [String] Optional, specifies the id of the runnable to be
|
97
|
+
# tested against the endpoint.
|
98
|
+
def execute_runnables runnable_id = nil
|
99
|
+
if runnable_id
|
100
|
+
if runnables[runnable_id]
|
101
|
+
reports[runnable_id] = runnables[runnable_id].run(client)
|
102
|
+
else
|
103
|
+
error(:unable_to_locate_runnable, runnable_id)
|
104
|
+
end
|
105
|
+
else
|
106
|
+
runnables.each do |id, runnable|
|
107
|
+
reports[id] = runnable.run(client)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def verify_runnable(runnable_id)
|
113
|
+
return true unless runnables[runnable_id].nil?
|
114
|
+
false
|
115
|
+
end
|
116
|
+
|
117
|
+
def new_client(url)
|
118
|
+
@client = nil
|
119
|
+
@endpoint = url
|
120
|
+
end
|
121
|
+
|
122
|
+
# @path [String] Optional, specifies the path to the folder which the
|
123
|
+
# TestReport resources should be written to.
|
124
|
+
def write_reports path = nil
|
125
|
+
report_directory = path || testreport_path
|
126
|
+
FileUtils.mkdir_p report_directory
|
127
|
+
|
128
|
+
reports.each do |_, report|
|
129
|
+
File.open("#{report_directory}/#{report.name.downcase.split(' ')[1...].join('_')}.json", 'w') do |f|
|
130
|
+
f.write(report.to_json)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|