testscript_engine 0.0.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.
- 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
|