inferno_core 0.0.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.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/bin/inferno-console +8 -0
  4. data/lib/inferno.rb +15 -0
  5. data/lib/inferno/apps/web/application.rb +12 -0
  6. data/lib/inferno/apps/web/controllers/controller.rb +34 -0
  7. data/lib/inferno/apps/web/controllers/requests/show.rb +16 -0
  8. data/lib/inferno/apps/web/controllers/test_runs/create.rb +39 -0
  9. data/lib/inferno/apps/web/controllers/test_runs/results/index.rb +18 -0
  10. data/lib/inferno/apps/web/controllers/test_runs/show.rb +16 -0
  11. data/lib/inferno/apps/web/controllers/test_sessions/create.rb +26 -0
  12. data/lib/inferno/apps/web/controllers/test_sessions/results/index.rb +17 -0
  13. data/lib/inferno/apps/web/controllers/test_sessions/show.rb +16 -0
  14. data/lib/inferno/apps/web/controllers/test_suites/index.rb +13 -0
  15. data/lib/inferno/apps/web/controllers/test_suites/show.rb +16 -0
  16. data/lib/inferno/apps/web/router.rb +27 -0
  17. data/lib/inferno/apps/web/serializers/header.rb +12 -0
  18. data/lib/inferno/apps/web/serializers/input.rb +16 -0
  19. data/lib/inferno/apps/web/serializers/message.rb +10 -0
  20. data/lib/inferno/apps/web/serializers/request.rb +26 -0
  21. data/lib/inferno/apps/web/serializers/result.rb +21 -0
  22. data/lib/inferno/apps/web/serializers/serializer.rb +16 -0
  23. data/lib/inferno/apps/web/serializers/test.rb +17 -0
  24. data/lib/inferno/apps/web/serializers/test_group.rb +23 -0
  25. data/lib/inferno/apps/web/serializers/test_run.rb +19 -0
  26. data/lib/inferno/apps/web/serializers/test_session.rb +17 -0
  27. data/lib/inferno/apps/web/serializers/test_suite.rb +18 -0
  28. data/lib/inferno/config/application.rb +22 -0
  29. data/lib/inferno/config/boot.rb +5 -0
  30. data/lib/inferno/config/boot/db.rb +23 -0
  31. data/lib/inferno/config/boot/logging.rb +16 -0
  32. data/lib/inferno/config/boot/suites.rb +27 -0
  33. data/lib/inferno/config/boot/web.rb +17 -0
  34. data/lib/inferno/db/migrations/001_create_initial_structure.rb +165 -0
  35. data/lib/inferno/dsl.rb +35 -0
  36. data/lib/inferno/dsl/assertions.rb +93 -0
  37. data/lib/inferno/dsl/fhir_client.rb +152 -0
  38. data/lib/inferno/dsl/fhir_client_builder.rb +56 -0
  39. data/lib/inferno/dsl/fhir_manipulation.rb +25 -0
  40. data/lib/inferno/dsl/fhir_validation.rb +111 -0
  41. data/lib/inferno/dsl/http_client.rb +122 -0
  42. data/lib/inferno/dsl/http_client_builder.rb +54 -0
  43. data/lib/inferno/dsl/request_storage.rb +101 -0
  44. data/lib/inferno/dsl/results.rb +54 -0
  45. data/lib/inferno/dsl/runnable.rb +250 -0
  46. data/lib/inferno/entities.rb +19 -0
  47. data/lib/inferno/entities/attributes.rb +9 -0
  48. data/lib/inferno/entities/entity.rb +15 -0
  49. data/lib/inferno/entities/header.rb +42 -0
  50. data/lib/inferno/entities/message.rb +22 -0
  51. data/lib/inferno/entities/request.rb +166 -0
  52. data/lib/inferno/entities/result.rb +49 -0
  53. data/lib/inferno/entities/test.rb +173 -0
  54. data/lib/inferno/entities/test_group.rb +87 -0
  55. data/lib/inferno/entities/test_input.rb +20 -0
  56. data/lib/inferno/entities/test_run.rb +63 -0
  57. data/lib/inferno/entities/test_session.rb +26 -0
  58. data/lib/inferno/entities/test_suite.rb +73 -0
  59. data/lib/inferno/exceptions.rb +42 -0
  60. data/lib/inferno/public/217.bundle.js +1 -0
  61. data/lib/inferno/public/assets.json +6 -0
  62. data/lib/inferno/public/bundle.js +2 -0
  63. data/lib/inferno/public/bundle.js.LICENSE.txt +65 -0
  64. data/lib/inferno/public/e09b16f5cb645eb05f90c8f38f3409fb.png +0 -0
  65. data/lib/inferno/public/favicon.ico +0 -0
  66. data/lib/inferno/public/logo192.png +0 -0
  67. data/lib/inferno/public/logo512.png +0 -0
  68. data/lib/inferno/repositories.rb +27 -0
  69. data/lib/inferno/repositories/headers.rb +22 -0
  70. data/lib/inferno/repositories/in_memory_repository.rb +41 -0
  71. data/lib/inferno/repositories/messages.rb +35 -0
  72. data/lib/inferno/repositories/repository.rb +106 -0
  73. data/lib/inferno/repositories/requests.rb +89 -0
  74. data/lib/inferno/repositories/results.rb +72 -0
  75. data/lib/inferno/repositories/test_groups.rb +9 -0
  76. data/lib/inferno/repositories/test_runs.rb +46 -0
  77. data/lib/inferno/repositories/test_sessions.rb +56 -0
  78. data/lib/inferno/repositories/test_suites.rb +9 -0
  79. data/lib/inferno/repositories/tests.rb +9 -0
  80. data/lib/inferno/repositories/validate_runnable_reference.rb +42 -0
  81. data/lib/inferno/spec_support.rb +9 -0
  82. data/lib/inferno/test_runner.rb +81 -0
  83. data/lib/inferno/utils/middleware/request_logger.rb +55 -0
  84. data/lib/inferno/version.rb +3 -0
  85. data/spec/support/factory_bot.rb +21 -0
  86. metadata +514 -0
@@ -0,0 +1,41 @@
1
+ require 'forwardable'
2
+
3
+ module Inferno
4
+ module Repositories
5
+ class InMemoryRepository
6
+ extend Forwardable
7
+
8
+ def_delegators 'self.class', :all, :all_by_id
9
+
10
+ def insert(klass)
11
+ all << klass
12
+ end
13
+
14
+ def find(id)
15
+ all_by_id[id.to_s]
16
+ end
17
+
18
+ def exists?(id)
19
+ all_by_id.include? id
20
+ end
21
+
22
+ class << self
23
+ def all
24
+ @all ||= []
25
+ end
26
+
27
+ # @api private
28
+ def all_by_id
29
+ @all_by_id ||= {}
30
+ @all_by_id.length == all.length ? @all_by_id : index_by_id
31
+ end
32
+
33
+ def index_by_id
34
+ @all_by_id = {}
35
+ all.each { |klass| @all_by_id[klass.id] = klass }
36
+ @all_by_id
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,35 @@
1
+ module Inferno
2
+ module Repositories
3
+ class Messages < Repository
4
+ def messages_for_result(result_id)
5
+ self.class::Model
6
+ .order(:index)
7
+ .where(result_id: result_id)
8
+ .to_a
9
+ .map!(&:to_json_data)
10
+ .each(&:deep_symbolize_keys!)
11
+ .map! { |result| build_entity(result) }
12
+ end
13
+
14
+ class Model < Sequel::Model(db)
15
+ many_to_one :result, class: 'Inferno::Repositories::Results::Model', key: :result_id
16
+
17
+ def before_create
18
+ self.id = SecureRandom.uuid
19
+ time = Time.now
20
+ self.created_at ||= time
21
+ self.updated_at ||= time
22
+ super
23
+ end
24
+
25
+ def validate
26
+ super
27
+ types = Entities::Message::TYPES
28
+ errors.add(:message, 'must be present') if message.blank?
29
+ errors.add(:type, 'must be present') if type.blank?
30
+ errors.add(:type, "'#{type}' is invalid. Must be one of: #{types.join(', ')}") unless types.include?(type)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,106 @@
1
+ module Inferno
2
+ module Repositories
3
+ # Base class for repositories. Subclass and override methods as
4
+ # needed.
5
+ #
6
+ # @abstract
7
+ class Repository
8
+ extend Forwardable
9
+
10
+ def_delegators 'self.class', :db, :table_name
11
+
12
+ # Return the db connection for this repository.
13
+ def self.db
14
+ Application['db.connection'][table_name]
15
+ end
16
+
17
+ # Return the name of the database table for this repository. Override if
18
+ # the table name is not the snake case version of the name of the
19
+ # repository class.
20
+ #
21
+ # @return [String]
22
+ def self.table_name
23
+ name.demodulize.underscore.to_sym
24
+ end
25
+
26
+ # Return the name of the entity class which will be instantiated by a
27
+ # repository. Override if the entity class name is not the singular
28
+ # version of the repository name.
29
+ #
30
+ # @return [Class]
31
+ def entity_class_name
32
+ self.class.name.demodulize.singularize
33
+ end
34
+
35
+ # Return the class of the entity which will be instantiated by a
36
+ # repository. Override if the entity class is not in the
37
+ # `Inferno::Entities` namespace.
38
+ #
39
+ # @return [Class]
40
+ def entity_class
41
+ Entities.const_get(entity_class_name)
42
+ end
43
+
44
+ # Find a database record by id, and instantiate an entity from that
45
+ # record.
46
+ #
47
+ # @param id [String]
48
+ # @return [Inferno::Entities] an instance of the class returned by
49
+ # `#entity_class`
50
+ def find(id)
51
+ result = self.class::Model.find(id: id)
52
+ return result if result.nil?
53
+
54
+ build_entity(result.to_hash)
55
+ end
56
+
57
+ def add_non_db_entities(hash)
58
+ if hash.include? :test_id
59
+ hash[:test] = Tests.new.find(hash[:test_id])
60
+ elsif hash.include? :test_group_id
61
+ hash[:test_group] = TestGroups.new.find(hash[:test_group_id])
62
+ elsif hash.include? :test_suite_id
63
+ hash[:test_suite] = TestSuites.new.find(hash[:test_suite_id])
64
+ end
65
+ end
66
+
67
+ # Create a new record in the database.
68
+ #
69
+ # @param params [Hash]
70
+ # @return [Inferno::Entities] an instance of the entity for this repo
71
+ # @example
72
+ # repo = Inferno::Repositories::SomeEntities.new
73
+ # begin
74
+ # result = repo.create(key1: 'value1', key2: 'value2')
75
+ # rescue Sequel::ValidationFailed => e
76
+ # # handle error
77
+ # end
78
+ def create(params)
79
+ result = self.class::Model.create(db_params(params))
80
+ build_entity(result.to_hash.merge(handle_non_db_params(params)))
81
+ end
82
+
83
+ # Creates an instance of the entity associated with this repository.
84
+ # Override if any special logic is required to create the entity.
85
+ #
86
+ # @param params [Hash]
87
+ # @return [Object] an instance of `#entity_class`
88
+ def build_entity(params)
89
+ add_non_db_entities(params)
90
+ entity_class.new(params)
91
+ end
92
+
93
+ def db_params(params)
94
+ params.slice(*self.class::Model.columns)
95
+ end
96
+
97
+ def non_db_params(params)
98
+ params.reject { |key, _value| self.class::Model.columns.include? key }
99
+ end
100
+
101
+ def handle_non_db_params(params)
102
+ non_db_params(params)
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,89 @@
1
+ module Inferno
2
+ module Repositories
3
+ class Requests < Repository
4
+ include Import[headers_repo: 'repositories.headers']
5
+
6
+ def create(params)
7
+ request = self.class::Model.create(db_params(params))
8
+
9
+ request_headers = (params[:request_headers] || []).map do |header|
10
+ headers_repo.create(header.merge(request_id: request.index, type: 'request'))
11
+ end
12
+ response_headers = (params[:response_headers] || []).map do |header|
13
+ headers_repo.create(header.merge(request_id: request.index, type: 'response'))
14
+ end
15
+
16
+ headers = request_headers + response_headers
17
+
18
+ build_entity(
19
+ request.to_hash
20
+ .merge(headers: headers)
21
+ .merge(non_db_params(params))
22
+ )
23
+ end
24
+
25
+ def find(id)
26
+ result =
27
+ self.class::Model
28
+ .where(id: id)
29
+ .select(*entity_class::SUMMARY_FIELDS)
30
+ .to_a
31
+ return nil if result.blank?
32
+
33
+ build_entity(result.first.to_hash)
34
+ end
35
+
36
+ def find_full_request(id)
37
+ result =
38
+ self.class::Model
39
+ .find(id: id)
40
+ .to_json_data(json_serializer_options)
41
+ .deep_symbolize_keys!
42
+
43
+ build_entity(result)
44
+ end
45
+
46
+ def find_named_request(test_session_id, name)
47
+ results =
48
+ self.class::Model
49
+ .where(test_session_id: test_session_id, name: name.to_s)
50
+ .map { |model| model.to_json_data(json_serializer_options) }
51
+ return nil if results.blank?
52
+
53
+ result = results.reduce { |max, current| current['index'] > max['index'] ? current : max }
54
+ result.deep_symbolize_keys!
55
+
56
+ build_entity(result)
57
+ end
58
+
59
+ def requests_for_result(result_id)
60
+ self.class::Model
61
+ .order(:index)
62
+ .where(result_id: result_id)
63
+ .select(*entity_class::SUMMARY_FIELDS)
64
+ .to_a
65
+ .map(&:to_hash)
66
+ .map! { |result| build_entity(result) }
67
+ end
68
+
69
+ def json_serializer_options
70
+ {
71
+ include: :headers
72
+ }
73
+ end
74
+
75
+ class Model < Sequel::Model(db)
76
+ many_to_one :result, class: 'Inferno::Repositories::Results::Model', key: :result_id
77
+ one_to_many :headers, class: 'Inferno::Repositories::Headers::Model', key: :request_id
78
+
79
+ def before_create
80
+ self.id = SecureRandom.uuid
81
+ time = Time.now
82
+ self.created_at ||= time
83
+ self.updated_at ||= time
84
+ super
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,72 @@
1
+ require_relative 'validate_runnable_reference'
2
+
3
+ module Inferno
4
+ module Repositories
5
+ class Results < Repository
6
+ include Import[
7
+ messages_repo: 'repositories.messages',
8
+ requests_repo: 'repositories.requests',
9
+ tests_repo: 'repositories.tests',
10
+ groups_repo: 'repositories.test_groups',
11
+ suites_repo: 'repositories.test_suites'
12
+ ]
13
+
14
+ def create(params)
15
+ messages = params.delete(:messages) || []
16
+ requests = params.delete(:requests) || []
17
+ super(params).tap do |result|
18
+ messages.each { |message| messages_repo.create(message.merge(result_id: result.id)) }
19
+ requests.each { |request| requests_repo.create(request.to_hash.merge(result_id: result.id)) }
20
+ end
21
+ end
22
+
23
+ def build_entity(params)
24
+ runnable =
25
+ if params[:test_id]
26
+ { test: tests_repo.find(params[:test_id]) }
27
+ elsif params[:test_group_id]
28
+ { test_group: groups_repo.find(params[:test_group_id]) }
29
+ elsif params[:test_suite_id]
30
+ { test_suite: suites_repo.find(params[:test_suite_id]) }
31
+ else
32
+ {}
33
+ end
34
+ entity_class.new(params.merge(runnable))
35
+ end
36
+
37
+ def json_serializer_options
38
+ {
39
+ include: {
40
+ messages: {},
41
+ requests: {
42
+ include: { headers: {} },
43
+ only: Entities::Request::SUMMARY_FIELDS
44
+ }
45
+ }
46
+ }
47
+ end
48
+
49
+ class Model < Sequel::Model(db)
50
+ include ValidateRunnableReference
51
+
52
+ one_to_many :messages, class: 'Inferno::Repositories::Messages::Model', key: :result_id
53
+ one_to_many :requests, class: 'Inferno::Repositories::Requests::Model', key: :result_id
54
+ many_to_one :test_run, class: 'Inferno::Repositories::TestRuns::Model', key: :test_run_id
55
+ many_to_one :test_session, class: 'Inferno::Repositories::TestSessions::Model', key: :test_session_id
56
+
57
+ def before_create
58
+ self.id = SecureRandom.uuid
59
+ time = Time.now
60
+ self.created_at ||= time
61
+ self.updated_at ||= time
62
+ super
63
+ end
64
+
65
+ def validate
66
+ super
67
+ errors.add(:result, "'#{result}' is not valid") unless Entities::Result::RESULT_OPTIONS.include?(result)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,9 @@
1
+ require_relative 'in_memory_repository'
2
+
3
+ module Inferno
4
+ module Repositories
5
+ # Repository that deals with persistence for the `TestGroup` entity.
6
+ class TestGroups < InMemoryRepository
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,46 @@
1
+ require_relative 'validate_runnable_reference'
2
+
3
+ module Inferno
4
+ module Repositories
5
+ class TestRuns < Repository
6
+ include Import[results_repo: 'repositories.results']
7
+
8
+ def json_serializer_options
9
+ {
10
+ include: {
11
+ results: results_repo.json_serializer_options
12
+ }
13
+ }
14
+ end
15
+
16
+ def results_for_test_run(test_run_id)
17
+ test_run_hash =
18
+ self.class::Model
19
+ .find(id: test_run_id)
20
+ .to_json_data(json_serializer_options)
21
+ .deep_symbolize_keys!
22
+
23
+ test_run_hash[:results]
24
+ .map! { |result| results_repo.build_entity(result) }
25
+ end
26
+
27
+ class Model < Sequel::Model(db)
28
+ include ValidateRunnableReference
29
+
30
+ one_to_many :results,
31
+ eager: [:messages, :requests],
32
+ class: 'Inferno::Repositories::Results::Model',
33
+ key: :test_run_id
34
+ many_to_one :test_session, class: 'Inferno::Repositories::TestSessions::Model', key: :test_session_id
35
+
36
+ def before_create
37
+ self.id = SecureRandom.uuid
38
+ time = Time.now
39
+ self.created_at ||= time
40
+ self.updated_at ||= time
41
+ super
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,56 @@
1
+ require_relative 'repository'
2
+
3
+ module Inferno
4
+ module Repositories
5
+ # Repository that deals with persistence for the `TestSession` entity.
6
+ class TestSessions < Repository
7
+ include Import[results_repo: 'repositories.results']
8
+
9
+ def json_serializer_options
10
+ {
11
+ include: {
12
+ results: results_repo.json_serializer_options,
13
+ test_runs: {}
14
+ }
15
+ }
16
+ end
17
+
18
+ def results_for_test_session(test_session_id)
19
+ test_session_hash =
20
+ self.class::Model
21
+ .find(id: test_session_id)
22
+ .to_json_data(json_serializer_options)
23
+ .deep_symbolize_keys!
24
+
25
+ test_session_hash[:results]
26
+ .map! { |result| results_repo.build_entity(result) }
27
+ end
28
+
29
+ class Model < Sequel::Model(db)
30
+ include Import[test_suites_repo: 'repositories.test_suites']
31
+
32
+ one_to_many :results,
33
+ eager: [:messages, :requests],
34
+ class: 'Inferno::Repositories::Results::Model',
35
+ key: :test_session_id
36
+ one_to_many :test_runs, class: 'Inferno::Repositories::TestRuns::Model', key: :test_session_id
37
+
38
+ def before_create
39
+ self.id = SecureRandom.uuid
40
+ time = Time.now
41
+ self.created_at ||= time
42
+ self.updated_at ||= time
43
+ super
44
+ end
45
+
46
+ def validate
47
+ super
48
+ errors.add(:test_suite_id, 'cannot be empty') if test_suite_id.blank?
49
+ unless test_suites_repo.exists? test_suite_id # rubocop:disable Style/GuardClause
50
+ errors.add(:test_suite_id, "'#{test_suite_id}' is not valid")
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end