module Inferno::DSL::Runnable

This module contains the DSL for defining child entities in the test definition framework.

Attributes

parent[RW]

Public Class Methods

extended(extending_class) click to toggle source

When a class (e.g. TestSuite/TestGroup) uses this module, set it up so that subclassing it works correctly.

  • add the subclass to the relevant repository when it is created

  • copy the class instance variables from the superclass

  • add a hook to the subclass so that its subclasses do the same

@api private

Calls superclass method
# File lib/inferno/dsl/runnable.rb, line 16
def self.extended(extending_class)
  super

  extending_class.define_singleton_method(:inherited) do |subclass|
    copy_instance_variables(subclass)

    # Whenever the definition of a Runnable class ends, keep track of the
    # file it came from. Once the Suite loader successfully loads a file,
    # it will add all of the Runnable classes from that file to the
    # appropriate repositories.
    TracePoint.trace(:end) do |trace|
      if trace.self == subclass
        subclass.add_self_to_repository
        trace.disable
      end
    end
  end
end

Public Instance Methods

add_self_to_repository() click to toggle source

@api private

# File lib/inferno/dsl/runnable.rb, line 60
def add_self_to_repository
  repository.insert(self)
end
child_metadata(metadata = nil) click to toggle source

@api private

# File lib/inferno/dsl/runnable.rb, line 109
def child_metadata(metadata = nil)
  @child_metadata = metadata if metadata
  @child_metadata
end
child_types() click to toggle source

@api private

# File lib/inferno/dsl/runnable.rb, line 251
def child_types
  return [] if ancestors.include? Inferno::Entities::Test
  return [:groups] if ancestors.include? Inferno::Entities::TestSuite

  [:groups, :tests]
end
children() click to toggle source

@api private

# File lib/inferno/dsl/runnable.rb, line 259
def children
  @children ||= []
end
configure_child_class(klass, hash_args) click to toggle source

@api private

# File lib/inferno/dsl/runnable.rb, line 128
def configure_child_class(klass, hash_args) # rubocop:disable Metrics/CyclomaticComplexity
  inputs.each do |input_definition|
    next if klass.inputs.any? { |input| input[:name] == input_definition[:name] }

    klass.input input_definition[:name], input_definition
  end

  outputs.each do |output_definition|
    next if klass.outputs.include? output_definition

    klass.output output_definition
  end

  new_fhir_client_definitions = klass.instance_variable_get(:@fhir_client_definitions) || {}
  fhir_client_definitions.each do |name, definition|
    next if new_fhir_client_definitions.include? name

    new_fhir_client_definitions[name] = definition.dup
  end
  klass.instance_variable_set(:@fhir_client_definitions, new_fhir_client_definitions)

  new_http_client_definitions = klass.instance_variable_get(:@http_client_definitions) || {}
  http_client_definitions.each do |name, definition|
    next if new_http_client_definitions.include? name

    new_http_client_definitions[name] = definition.dup
  end
  klass.instance_variable_set(:@http_client_definitions, new_http_client_definitions)

  hash_args.each do |key, value|
    klass.send(key, *value)
  end

  klass.children.each do |child_class|
    klass.configure_child_class(child_class, {})
    child_class.add_self_to_repository
  end
end
copy_instance_variables(subclass) click to toggle source

Class instance variables are used to hold the metadata for Runnable classes. When inheriting from a Runnable class, these class instance variables need to be copied. Any child Runnable classes will themselves need to be subclassed so that their parent can be updated. @api private

# File lib/inferno/dsl/runnable.rb, line 40
def copy_instance_variables(subclass)
  instance_variables.each do |variable|
    next if [:@id, :@groups, :@tests, :@parent, :@children, :@test_count].include?(variable)

    subclass.instance_variable_set(variable, instance_variable_get(variable).dup)
  end

  child_types.each do |child_type|
    new_children = send(child_type).map do |child|
      Class.new(child).tap do |subclass_child|
        subclass_child.parent = subclass
      end
    end

    subclass.instance_variable_set(:"@#{child_type}", new_children)
    subclass.children.concat(new_children)
  end
end
create_child_class(hash_args) click to toggle source

@api private

# File lib/inferno/dsl/runnable.rb, line 115
def create_child_class(hash_args)
  superclass_id = hash_args.delete :from

  return Class.new(child_metadata[:class]) if superclass_id.blank?

  superclass = child_metadata[:repo].find(superclass_id)

  raise Exceptions::ParentNotLoadedException.new(child_metadata[:class], superclass_id) unless superclass

  Class.new(superclass)
end
default_id() click to toggle source

@api private

# File lib/inferno/dsl/runnable.rb, line 236
def default_id
  to_s
end
define_child(*args, &block) click to toggle source

This method defines a child entity. Classes using this module should alias the method name they wish to use to define child entities to this method. @api private

# File lib/inferno/dsl/runnable.rb, line 73
def define_child(*args, &block)
  hash_args = process_args(args)

  klass = create_child_class(hash_args)

  klass.parent = self

  child_metadata[:collection] << klass
  children << klass

  configure_child_class(klass, hash_args)

  handle_child_definition_block(klass, &block)

  klass.add_self_to_repository

  klass
end
description(new_description = nil) click to toggle source
# File lib/inferno/dsl/runnable.rb, line 193
def description(new_description = nil)
  return @description if new_description.nil?

  @description = new_description
end
handle_child_definition_block(klass, &block) click to toggle source

@api private

# File lib/inferno/dsl/runnable.rb, line 168
def handle_child_definition_block(klass, &block)
  klass.class_eval(&block) if block_given?
end
id(new_id = nil) click to toggle source
# File lib/inferno/dsl/runnable.rb, line 172
def id(new_id = nil)
  return @id if new_id.nil? && @id.present?

  prefix =
    if parent
      "#{parent.id}-"
    else
      ''
    end

  @base_id = new_id || @base_id || default_id

  @id = "#{prefix}#{@base_id}"
end
input(name, *other_names, **input_definition) click to toggle source

Define inputs

@param name [Symbol] name of the input @param other_names [Symbol] array of symbols if specifying multiple inputs @param input_definition [Hash] options for input such as type, description, or title @option input_definition [String] :title Human readable title for input @option input_definition [String] :description Description for the input @option input_definition [String] :type text | textarea @option input_definition [String] :default The default value for the input @return [void] @example

input :patientid, title: 'Patient ID', description: 'The ID of the patient being searched for',
                  default: 'default_patient_id'

@example

input :textarea, title: 'Textarea Input Example', type: 'textarea'
# File lib/inferno/dsl/runnable.rb, line 214
def input(name, *other_names, **input_definition)
  if other_names.present?
    [name, *other_names].each do |input_name|
      inputs.push({ name: input_name, title: nil, description: nil, type: 'text' })
    end
  else
    input_definition[:type] = 'text' unless input_definition.key? :type
    inputs.push({ name: name }.merge(input_definition))
  end
end
inputs() click to toggle source

@api private

# File lib/inferno/dsl/runnable.rb, line 241
def inputs
  @inputs ||= []
end
output(*output_definitions) click to toggle source

Define outputs

@param output_definitions [Symbol] @return [void] @example

output :patient_id, :bearer_token
# File lib/inferno/dsl/runnable.rb, line 231
def output(*output_definitions)
  outputs.concat(output_definitions)
end
outputs() click to toggle source

@api private

# File lib/inferno/dsl/runnable.rb, line 246
def outputs
  @outputs ||= []
end
process_args(args) click to toggle source

@api private

# File lib/inferno/dsl/runnable.rb, line 93
def process_args(args)
  hash_args =
    if args[0].is_a? Hash
      args[0]
    elsif args[1].is_a? Hash
      args[1]
    else
      {}
    end

  hash_args[:title] = args[0] if args[0].is_a? String

  hash_args
end
repository() click to toggle source

An instance of the repository for the class using this module

# File lib/inferno/dsl/runnable.rb, line 65
def repository
  nil
end
resume_test_route(method, path, &block) click to toggle source

Create a route which will resume a test run when a request is received

@see Inferno::DSL::Results#wait @example

resume_test_route :get, '/launch' do
  request.query_parameters['iss']
end

test do
  input :issuer
  receives_request :launch

  run do
    wait(
      identifier: issuer,
      message: "Wating to receive a request with an issuer of #{issuer}"
    )
  end
end

@param method [Symbol] the HTTP request type (:get, :post, etc.) for the

incoming request

@param path [String] the path for this request. The route will be served

with a prefix of `/custom/TEST_SUITE_ID` to prevent path conflicts.
[Any of the path options available in Hanami
Router](https://github.com/hanami/router/tree/f41001d4c3ee9e2d2c7bb142f74b43f8e1d3a265#a-beautiful-dsl)
can be used here.

@yield This method takes a block which must return the identifier

defined when a test was set to wait for the test run that hit this
route. The block has access to the `request` method which returns a
{Inferno::DSL::Request} object with the information for the incoming
request.
# File lib/inferno/dsl/runnable.rb, line 308
def resume_test_route(method, path, &block)
  route_class = Class.new(ResumeTestRoute) do
    define_method(:test_run_identifier, &block)
    define_method(:request_name, -> { options[:name] })
  end

  route(method, path, route_class)
end
route(method, path, handler) click to toggle source

Create a route to handle a request

@param method [Symbol] the HTTP request type (:get, :post, etc.) for the

incoming request. `:all` will accept all HTTP request types.

@param path [String] the path for this request. The route will be served

with a prefix of `/custom/TEST_SUITE_ID` to prevent path conflicts.
[Any of the path options available in Hanami
Router](https://github.com/hanami/router/tree/f41001d4c3ee9e2d2c7bb142f74b43f8e1d3a265#a-beautiful-dsl)
can be used here.

@param handler [#call] the route handler. This can be any Rack

compatible object (e.g. a `Proc` object, a [Sinatra
app](http://sinatrarb.com/)) as described in the [Hanami Router
documentation.](https://github.com/hanami/router/tree/f41001d4c3ee9e2d2c7bb142f74b43f8e1d3a265#mount-rack-applications)
# File lib/inferno/dsl/runnable.rb, line 330
def route(method, path, handler)
  Inferno.routes << { method: method, path: path, handler: handler, suite: suite }
end
suite() click to toggle source

@api private

# File lib/inferno/dsl/runnable.rb, line 270
def suite
  return self if ancestors.include? Inferno::Entities::TestSuite

  parent.suite
end
test_count() click to toggle source
# File lib/inferno/dsl/runnable.rb, line 334
def test_count
  @test_count ||= children&.reduce(0) { |sum, child| sum + child.test_count } || 0
end
title(new_title = nil) click to toggle source
# File lib/inferno/dsl/runnable.rb, line 187
def title(new_title = nil)
  return @title if new_title.nil?

  @title = new_title
end
validator_url(url = nil) click to toggle source
# File lib/inferno/dsl/runnable.rb, line 263
def validator_url(url = nil)
  return @validator_url ||= parent&.validator_url if url.nil?

  @validator_url = url
end