inferno_core 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/bin/inferno +7 -0
  3. data/lib/inferno/apps/cli/console.rb +12 -0
  4. data/lib/inferno/apps/cli/main.rb +18 -0
  5. data/lib/inferno/apps/cli/migration.rb +14 -0
  6. data/lib/inferno/apps/cli.rb +8 -0
  7. data/lib/inferno/config/application.rb +3 -0
  8. data/lib/inferno/config/boot/db.rb +1 -9
  9. data/lib/inferno/config/boot/logging.rb +2 -0
  10. data/lib/inferno/dsl/assertions.rb +66 -1
  11. data/lib/inferno/dsl/configurable.rb +1 -1
  12. data/lib/inferno/dsl/fhir_client.rb +4 -4
  13. data/lib/inferno/dsl/fhir_client_builder.rb +3 -3
  14. data/lib/inferno/dsl/fhir_validation.rb +1 -1
  15. data/lib/inferno/dsl/http_client.rb +4 -4
  16. data/lib/inferno/dsl/http_client_builder.rb +3 -3
  17. data/lib/inferno/dsl/request_storage.rb +8 -8
  18. data/lib/inferno/dsl/results.rb +1 -1
  19. data/lib/inferno/dsl/resume_test_route.rb +9 -9
  20. data/lib/inferno/dsl/runnable.rb +36 -18
  21. data/lib/inferno/entities/header.rb +14 -7
  22. data/lib/inferno/entities/message.rb +16 -8
  23. data/lib/inferno/entities/request.rb +32 -19
  24. data/lib/inferno/entities/result.rb +36 -29
  25. data/lib/inferno/entities/session_data.rb +12 -6
  26. data/lib/inferno/entities/test.rb +13 -0
  27. data/lib/inferno/entities/test_run.rb +29 -6
  28. data/lib/inferno/entities/test_session.rb +16 -10
  29. data/lib/inferno/public/bundle.js +1 -1
  30. data/lib/inferno/repositories/in_memory_repository.rb +1 -1
  31. data/lib/inferno/repositories/results.rb +1 -1
  32. data/lib/inferno/spec_support.rb +1 -1
  33. data/lib/inferno/test_runner.rb +1 -1
  34. data/lib/inferno/utils/markdown_formatter.rb +1 -1
  35. data/lib/inferno/utils/middleware/request_logger.rb +1 -1
  36. data/lib/inferno/utils/migration.rb +17 -0
  37. data/lib/inferno/version.rb +1 -1
  38. data/lib/inferno.rb +0 -4
  39. metadata +37 -4
  40. data/bin/inferno-console +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 40769c54fd3854730eb080d1bb9e0755ff1551b00761968155d463dd3a6ce3e0
4
- data.tar.gz: 8bbf5aaa2b09d7d5ff54e56a3ed33ce4e1f4df29fc53e3737f18e9972dc34f1e
3
+ metadata.gz: 86c8b0374d22ea35f07944360943d2a1240f8ef7365b97d625b32facde4a2eaa
4
+ data.tar.gz: '0258752a8738b7e9c1da7d9112d0ee4f15bac0a8c7b2acac5f9973bb35d8146c'
5
5
  SHA512:
6
- metadata.gz: 35fe2518d3856f01b3358da68d862318784f05ab067b579d3ac62053dc1deed2be2895e3cfb5ff8efbc97e71d2495a11ebf66eadf23c5b8b4edb502707334838
7
- data.tar.gz: 04e37af9daf40c01f0d4182f055e4ddf187d72f0e9d8a43e8858af5c549a4a60900d71f034a8e027e20f53a56f6b5e835bce1fca3a0b98870b07e0681793a2d9
6
+ metadata.gz: 76cd8133722b109ed66f9b79eea46279c177fdac68cfe9abea4d0e9caaa76b861e533909923a94121d17e15483be5254f8e36e67cf6abb6121bc12f1e37f5fd7
7
+ data.tar.gz: 9be9f363e78e653b33818a8e8eee7fc74474ec2ca019b715054b7926d7e5675c46b290e138c0ee020e586d91eecef2759ead3272b6087461af89afeb8fdd7e94
data/bin/inferno ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pry'
4
+ require_relative '../lib/inferno/config/application'
5
+ require_relative '../lib/inferno/apps/cli'
6
+
7
+ Inferno::CLI::Main.start
@@ -0,0 +1,12 @@
1
+ module Inferno
2
+ module CLI
3
+ class Console
4
+ def run
5
+ require_relative '../../../inferno'
6
+
7
+ Inferno::Application.finalize!
8
+ Pry.start
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'console'
2
+ require_relative 'migration'
3
+
4
+ module Inferno
5
+ module CLI
6
+ class Main < Thor
7
+ desc 'console', 'Start an interactive console session with Inferno'
8
+ def console
9
+ Console.new.run
10
+ end
11
+
12
+ desc 'migrate', 'Run database migrations'
13
+ def migrate
14
+ Migration.new.run
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ require_relative '../../utils/migration'
2
+
3
+ module Inferno
4
+ module CLI
5
+ class Migration
6
+ def run
7
+ Inferno::Application.start(:logging)
8
+ Inferno::Application['logger'].level = Logger::DEBUG
9
+
10
+ Utils::Migration.new.run
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ require 'thor'
2
+
3
+ require_relative 'cli/main'
4
+
5
+ module Inferno
6
+ module CLI
7
+ end
8
+ end
@@ -1,4 +1,7 @@
1
+ require 'active_support/all'
2
+ require 'dotenv'
1
3
  require 'dry/system/container'
4
+ require 'sequel'
2
5
  require_relative 'boot'
3
6
 
4
7
  module Inferno
@@ -12,6 +12,7 @@ Inferno::Application.boot(:db) do
12
12
  config = YAML.load_file(config_path)[ENV['APP_ENV']]
13
13
  .merge(logger: Inferno::Application['logger'])
14
14
  connection = Sequel.connect(config)
15
+ connection.sql_log_level = :debug
15
16
 
16
17
  register('db.config', config)
17
18
  register('db.connection', connection)
@@ -19,14 +20,5 @@ Inferno::Application.boot(:db) do
19
20
 
20
21
  start do
21
22
  Sequel.extension :migration
22
- db = Inferno::Application['db.connection']
23
- migration_path = File.join(Inferno::Application.root, 'lib', 'inferno', 'db', 'migrations')
24
- Sequel::Migrator.run(db, migration_path)
25
-
26
- if ENV['APP_ENV'] == 'development'
27
- schema_path = File.join(Inferno::Application.root, 'lib', 'inferno', 'db', 'schema.rb')
28
- db.extension :schema_dumper
29
- File.open(schema_path, 'w') { |f| f.print(db.dump_schema_migration) }
30
- end
31
23
  end
32
24
  end
@@ -11,6 +11,8 @@ Inferno::Application.boot(:logging) do
11
11
  Logger.new($stdout)
12
12
  end
13
13
 
14
+ logger.level = Logger::INFO
15
+
14
16
  register('logger', logger)
15
17
  end
16
18
  end
@@ -3,22 +3,43 @@ require_relative '../exceptions'
3
3
  module Inferno
4
4
  module DSL
5
5
  module Assertions
6
+ # Make an assertion
7
+ #
8
+ # @param test a value whose truthiness will determine whether the
9
+ # assertion passes or fails
10
+ # @param message [String] failure message
6
11
  def assert(test, message = '')
7
12
  raise Exceptions::AssertionException, message unless test
8
13
  end
9
14
 
15
+ # @private
10
16
  def bad_response_status_message(expected, received)
11
17
  "Bad response status: expected #{Array.wrap(expected).join(', ')}, but received #{received}"
12
18
  end
13
19
 
20
+ # Check an response's status
21
+ #
22
+ # @param status [Integer, Array<Integer>] a single integer or an array of
23
+ # integer status codes
24
+ # @param response [Hash]
14
25
  def assert_response_status(status, response: self.response)
15
26
  assert Array.wrap(status).include?(response[:status]), bad_response_status_message(status, response[:status])
16
27
  end
17
28
 
29
+ # @private
18
30
  def bad_resource_type_message(expected, received)
19
31
  "Bad resource type received: expected #{expected}, but received #{received}"
20
32
  end
21
33
 
34
+ # Check a FHIR resource's type
35
+ #
36
+ # @param resource_type [String, Symbol, Class]
37
+ # @param resource [FHIR::Model]
38
+ # @example
39
+ # # The resource type can be a symbol, String, or FHIR::Model class
40
+ # assert_resource_type(:capability_statement)
41
+ # assert_resource_type('CapabilityStatement')
42
+ # assert_resource_type(FHIR::CapabilityStatement)
22
43
  def assert_resource_type(resource_type, resource: self.resource)
23
44
  resource_type_name = normalize_resource_type(resource_type)
24
45
 
@@ -26,15 +47,48 @@ module Inferno
26
47
  bad_resource_type_message(resource_type_name, resource&.resourceType)
27
48
  end
28
49
 
50
+ # @private
29
51
  def invalid_resource_message(profile_url)
30
52
  "Resource does not conform to the profile: #{profile_url}"
31
53
  end
32
54
 
55
+ # Validate a FHIR resource
56
+ #
57
+ # @param resource [FHIR::Model]
58
+ # @param profile_url [String] url of the profile to validate against,
59
+ # defaults to validating against the base FHIR resource
33
60
  def assert_valid_resource(resource: self.resource, profile_url: nil)
34
61
  assert resource_is_valid?(resource: resource, profile_url: profile_url),
35
62
  invalid_resource_message(profile_url)
36
63
  end
37
64
 
65
+ # Validate each entry of a Bundle
66
+ #
67
+ # @param bundle [FHIR::Bundle]
68
+ # @param resource_types
69
+ # [String,Symbol,FHIR::Model,Array<String,Symbol,FHIR::Model>,Hash] If a
70
+ # string, symbol, or FHIR::Model is provided, only that resource type
71
+ # will be validated. If an array of strings is provided, only those
72
+ # resource types will be validated. If a hash is provided with resource
73
+ # types as keys and profile urls (or nil) as values, only those resource
74
+ # types will be validated against the provided profile url or the base
75
+ # resource if nil.
76
+ # @example
77
+ # # Only validate Patient bundle entries
78
+ # assert_valid_bundle_entries(resource_types: 'Patient')
79
+ #
80
+ # # Only valiadte Patient and Condition bundle entries
81
+ # assert_valid_bundle_entries(resource_types: ['Patient', 'Condition'])
82
+ #
83
+ # # Only validate Patient and Condition bundle entries. Validate Patient
84
+ # # resources against the given profile, and Codition resources against the
85
+ # # base FHIR Condition resource.
86
+ # assert_valid_bundle_entries(
87
+ # resource_types: {
88
+ # 'Patient': 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient',
89
+ # 'Condition': nil
90
+ # }
91
+ # )
38
92
  def assert_valid_bundle_entries(bundle: resource, resource_types: {})
39
93
  assert_resource_type('Bundle', resource: bundle)
40
94
 
@@ -56,6 +110,7 @@ module Inferno
56
110
  assert invalid_resources.empty?, invalid_bundle_entries_message(invalid_resources)
57
111
  end
58
112
 
113
+ # @private
59
114
  def invalid_bundle_entries_message(invalid_resources)
60
115
  identifier_strings =
61
116
  invalid_resources
@@ -64,6 +119,7 @@ module Inferno
64
119
  "The following bundle entries are invalid: #{identifier_strings}"
65
120
  end
66
121
 
122
+ # @private
67
123
  def normalize_resource_type(resource_type)
68
124
  if resource_type.is_a? Class
69
125
  resource_type.name.demodulize
@@ -72,6 +128,7 @@ module Inferno
72
128
  end
73
129
  end
74
130
 
131
+ # @private
75
132
  def normalize_types_to_check(resource_types)
76
133
  case resource_types
77
134
  when Hash
@@ -83,14 +140,22 @@ module Inferno
83
140
  end
84
141
  end
85
142
 
143
+ # Check for valid JSON
144
+ #
145
+ # @param maybe_json_string [String]
146
+ # @param message [String] extra failure message
86
147
  def assert_valid_json(maybe_json_string, message = '')
87
148
  assert JSON.parse(maybe_json_string)
88
149
  rescue JSON::ParserError
89
150
  assert false, "Invalid JSON. #{message}"
90
151
  end
91
152
 
153
+ # Check for a valid http/https uri
154
+ #
155
+ # @param uri [String]
156
+ # @param message [String] custom failure message
92
157
  def assert_valid_http_uri(uri, message = '')
93
- error_message = message || "\"#{uri}\" is not a valid URI"
158
+ error_message = message.presence || "\"#{uri}\" is not a valid URI"
94
159
  assert uri =~ /\A#{URI::DEFAULT_PARSER.make_regexp(['http', 'https'])}\z/, error_message
95
160
  end
96
161
 
@@ -15,7 +15,7 @@ module Inferno
15
15
  @config.apply(new_configuration)
16
16
  end
17
17
 
18
- # @api private
18
+ # @private
19
19
  class Configuration
20
20
  attr_accessor :configuration
21
21
 
@@ -35,7 +35,7 @@ module Inferno
35
35
  # @see Inferno::FHIRClientBuilder Documentation for the client
36
36
  # configuration DSL
37
37
  module FHIRClient
38
- # @api private
38
+ # @private
39
39
  def self.included(klass)
40
40
  klass.extend ClassMethods
41
41
  klass.extend Forwardable
@@ -54,7 +54,7 @@ module Inferno
54
54
  FHIRClientBuilder.new.build(self, self.class.fhir_client_definitions[client])
55
55
  end
56
56
 
57
- # @api private
57
+ # @private
58
58
  def fhir_clients
59
59
  @fhir_clients ||= {}
60
60
  end
@@ -128,13 +128,13 @@ module Inferno
128
128
 
129
129
  # @todo Make this a FHIR class method? Something like
130
130
  # FHIR.class_for(resource_type)
131
- # @api private
131
+ # @private
132
132
  def fhir_class_from_resource_type(resource_type)
133
133
  FHIR.const_get(resource_type.to_s.camelize)
134
134
  end
135
135
 
136
136
  module ClassMethods
137
- # @api private
137
+ # @private
138
138
  def fhir_client_definitions
139
139
  @fhir_client_definitions ||= {}
140
140
  end
@@ -6,7 +6,7 @@ module Inferno
6
6
  class FHIRClientBuilder
7
7
  attr_accessor :runnable
8
8
 
9
- # @api private
9
+ # @private
10
10
  def build(runnable, block)
11
11
  self.runnable = runnable
12
12
  instance_exec(self, &block)
@@ -56,14 +56,14 @@ module Inferno
56
56
  @headers ||= headers
57
57
  end
58
58
 
59
- # @api private
59
+ # @private
60
60
  def method_missing(name, *args, &block)
61
61
  return runnable.call(name, *args, &block) if runnable.respond_to? name
62
62
 
63
63
  super
64
64
  end
65
65
 
66
- # @api private
66
+ # @private
67
67
  def respond_to_missing?(name)
68
68
  runnable.respond_to?(name) || super
69
69
  end
@@ -89,7 +89,7 @@ module Inferno
89
89
  end
90
90
 
91
91
  module ClassMethods
92
- # @api private
92
+ # @private
93
93
  def fhir_validators
94
94
  @fhir_validators ||= {}
95
95
  end
@@ -16,7 +16,7 @@ module Inferno
16
16
  # # performs a GET to https://example.com
17
17
  # get
18
18
  # # performs a GET to https://example.com/abc
19
- # get('abc')
19
+ # get('abc')
20
20
  #
21
21
  # request # the most recent request
22
22
  # response # the most recent response
@@ -27,7 +27,7 @@ module Inferno
27
27
  # @see Inferno::FHIRClientBuilder Documentation for the client
28
28
  # configuration DSL
29
29
  module HTTPClient
30
- # @api private
30
+ # @private
31
31
  def self.included(klass)
32
32
  klass.extend ClassMethods
33
33
  klass.include RequestStorage
@@ -47,7 +47,7 @@ module Inferno
47
47
  http_clients[client] = HTTPClientBuilder.new.build(self, definition)
48
48
  end
49
49
 
50
- # @api private
50
+ # @private
51
51
  def http_clients
52
52
  @http_clients ||= {}
53
53
  end
@@ -104,7 +104,7 @@ module Inferno
104
104
  end
105
105
 
106
106
  module ClassMethods
107
- # @api private
107
+ # @private
108
108
  def http_client_definitions
109
109
  @http_client_definitions ||= {}
110
110
  end
@@ -4,7 +4,7 @@ module Inferno
4
4
  class HTTPClientBuilder
5
5
  attr_accessor :runnable
6
6
 
7
- # @api private
7
+ # @private
8
8
  def build(runnable, block)
9
9
  self.runnable = runnable
10
10
  instance_exec(self, &block)
@@ -38,14 +38,14 @@ module Inferno
38
38
  @headers ||= headers
39
39
  end
40
40
 
41
- # @api private
41
+ # @private
42
42
  def method_missing(name, *args, &block)
43
43
  return runnable.call(name, *args, &block) if runnable.respond_to? name
44
44
 
45
45
  super
46
46
  end
47
47
 
48
- # @api private
48
+ # @private
49
49
  def respond_to_missing?(name)
50
50
  runnable.respond_to?(name) || super
51
51
  end
@@ -41,7 +41,7 @@ module Inferno
41
41
  requests.find { |request| request.name == self.class.config.request_name(name.to_sym) }
42
42
  end
43
43
 
44
- # @api private
44
+ # @private
45
45
  def store_request(direction, name = nil, &block)
46
46
  response = block.call
47
47
 
@@ -61,7 +61,7 @@ module Inferno
61
61
  request
62
62
  end
63
63
 
64
- # @api private
64
+ # @private
65
65
  def load_named_requests
66
66
  requests_repo = Inferno::Repositories::Requests.new
67
67
  self.class.named_requests_used.map do |request_name|
@@ -74,19 +74,19 @@ module Inferno
74
74
  end
75
75
 
76
76
  module ClassMethods
77
- # @api private
77
+ # @private
78
78
  def named_requests_made
79
79
  @named_requests_made ||= []
80
80
  end
81
81
 
82
- # @api private
82
+ # @private
83
83
  def named_requests_used
84
84
  @named_requests_used ||= []
85
85
  end
86
86
 
87
87
  # Specify the named requests made by a test
88
88
  #
89
- # @param *identifiers [Symbol] one or more Symbols
89
+ # @param identifiers [Symbol] one or more request identifiers
90
90
  def makes_request(*identifiers)
91
91
  named_requests_made.concat(identifiers).uniq!
92
92
  identifiers.each do |identifier|
@@ -96,20 +96,20 @@ module Inferno
96
96
 
97
97
  # Specify the name for a request received by a test
98
98
  #
99
- # @param *identifiers [Symbol] one or more Symbols
99
+ # @param identifier [Symbol]
100
100
  def receives_request(identifier)
101
101
  config.add_request(identifier)
102
102
  @incoming_request_name = identifier
103
103
  end
104
104
 
105
- # @api private
105
+ # @private
106
106
  def incoming_request_name
107
107
  @incoming_request_name
108
108
  end
109
109
 
110
110
  # Specify the named requests used by a test
111
111
  #
112
- # @param *identifiers [Symbol] one or more Symbols
112
+ # @param identifiers [Symbol] one or more request identifiers
113
113
  def uses_request(*identifiers)
114
114
  named_requests_used.concat(identifiers).uniq!
115
115
  identifiers.each do |identifier|
@@ -94,7 +94,7 @@ module Inferno
94
94
  # and should not be used in real tests.
95
95
  #
96
96
  # @param message [String]
97
- # @api private
97
+ # @private
98
98
  def cancel(message = '')
99
99
  raise Exceptions::CancelException, message
100
100
  end
@@ -4,7 +4,7 @@ module Inferno
4
4
  module DSL
5
5
  # A base class for creating routes to resume test execution upon receiving
6
6
  # an incoming request.
7
- # @api private
7
+ # @private
8
8
  # @see Inferno::DSL::Runnable#resume_test_route
9
9
  class ResumeTestRoute
10
10
  include Hanami::Action
@@ -26,23 +26,23 @@ module Inferno
26
26
  @request ||= Inferno::Entities::Request.from_rack_env(@params.env)
27
27
  end
28
28
 
29
- # @api private
29
+ # @private
30
30
  def test_run
31
31
  @test_run ||=
32
32
  test_runs_repo.find_latest_waiting_by_identifier(test_run_identifier)
33
33
  end
34
34
 
35
- # @api private
35
+ # @private
36
36
  def waiting_result
37
37
  @waiting_result ||= results_repo.find_waiting_result(test_run_id: test_run.id)
38
38
  end
39
39
 
40
- # @api private
40
+ # @private
41
41
  def update_result
42
42
  results_repo.pass_waiting_result(waiting_result.id)
43
43
  end
44
44
 
45
- # @api private
45
+ # @private
46
46
  def persist_request
47
47
  requests_repo.create(
48
48
  request.to_hash.merge(
@@ -53,22 +53,22 @@ module Inferno
53
53
  )
54
54
  end
55
55
 
56
- # @api private
56
+ # @private
57
57
  def redirect_route
58
58
  "/test_sessions/#{test_run.test_session_id}##{waiting_group_id}"
59
59
  end
60
60
 
61
- # @api private
61
+ # @private
62
62
  def test
63
63
  @test ||= tests_repo.find(waiting_result.test_id)
64
64
  end
65
65
 
66
- # @api private
66
+ # @private
67
67
  def waiting_group_id
68
68
  test.parent.id
69
69
  end
70
70
 
71
- # @api private
71
+ # @private
72
72
  def call(_params)
73
73
  if test_run.nil?
74
74
  status(500, "Unable to find test run with identifier '#{test_run_identifier}'.")