inferno_core 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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