From 7a1cf3b24746554d71d8157b475016355cd87a83 Mon Sep 17 00:00:00 2001 From: Honza2 Date: Sun, 23 Feb 2020 23:55:43 +0100 Subject: [PATCH 1/5] feat(generators): Pass context and add ProviderState generator --- lib/pact/provider/generators.rb | 26 +++++++++ .../provider/generators/provider_state.rb | 58 +++++++++++++++++++ lib/pact/provider/request.rb | 9 ++- lib/pact/provider/rspec.rb | 10 +++- lib/pact/provider/test_methods.rb | 12 +++- 5 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 lib/pact/provider/generators.rb create mode 100644 lib/pact/provider/generators/provider_state.rb diff --git a/lib/pact/provider/generators.rb b/lib/pact/provider/generators.rb new file mode 100644 index 00000000..bbafd09a --- /dev/null +++ b/lib/pact/provider/generators.rb @@ -0,0 +1,26 @@ + +require 'pact/provider/generators/provider_state'; + +module Pact + module Provider + class Generators + def self.add_generator generator + generators.unshift(generator) + end + + def self.generators + @generators ||= [] + end + + def self.execute_generators object, interaction_context = nil + generators.each do | parser | + return parser.call(object, interaction_context) if parser.can_generate?(object) + end + + raise Pact::UnrecognizePactFormatError.new("This document does not use a recognised Pact generator: #{object}") + end + + add_generator(ProviderStateGenerator.new) + end + end +end diff --git a/lib/pact/provider/generators/provider_state.rb b/lib/pact/provider/generators/provider_state.rb new file mode 100644 index 00000000..fc86b67d --- /dev/null +++ b/lib/pact/provider/generators/provider_state.rb @@ -0,0 +1,58 @@ +require 'pact/provider/generators' + +module Pact + module Provider + class ProviderStateGenerator + + + # rewrite of https://github.com/DiUS/pact-jvm/blob/master/core/support/src/main/kotlin/au/com/dius/pact/core/support/expressions/ExpressionParser.kt#L27 + VALUES_SEPARATOR = "," + START_EXPRESSION = "\${" + END_EXPRESSION = '}' + def parse_expression expression, params + + return_string = [] + + buffer = expression; + # initial value + position = buffer.index(START_EXPRESSION) + + while (position && position >= 0) + if (position > 0) + # add string + return_string.push(buffer[0...position]) + end + end_position = buffer.index(END_EXPRESSION, position) + if (end_position < 0) + raise "Missing closing brace in expression string \"#{$value}\"" + end + + variable = "" + + if (end_position - position > 2) + expression = params[buffer[position+2...end_position]] || "" + end + return_string.push(expression) + + buffer = buffer[end_position + 1...-1] + position = buffer.index(START_EXPRESSION) + end + + return_string.join("") + end + + def call hash, interaction_context = nil + params = interaction_context.state_params || {} + + parse_expression hash["expression"], params + end + + def can_generate?(hash) + hash.key?('type') && hash['type'] === 'ProviderState' + end + end + end +end + + + diff --git a/lib/pact/provider/request.rb b/lib/pact/provider/request.rb index ad4b1998..3701421a 100644 --- a/lib/pact/provider/request.rb +++ b/lib/pact/provider/request.rb @@ -1,6 +1,7 @@ require 'json' require 'pact/reification' require 'pact/shared/null_expectation' +require 'pact/provider/generators' module Pact module Provider @@ -10,8 +11,9 @@ class Replayable # See https://github.com/rack/rack/blob/e7d741c6282ca4cf4e01506f5681e6e6b14c0b32/SPEC#L87-89 NO_HTTP_PREFIX = ["CONTENT-TYPE", "CONTENT-LENGTH"] - def initialize expected_request + def initialize expected_request, interaction_context = nil @expected_request = expected_request + @interaction_context = interaction_context end def method @@ -19,6 +21,11 @@ def method end def path + if expected_request.methods.include? :generators + if expected_request.generators["path"] + return Pact::Provider::Generators.execute_generators(expected_request.generators["path"], @interaction_context) + end + end expected_request.full_path end diff --git a/lib/pact/provider/rspec.rb b/lib/pact/provider/rspec.rb index a6e7372c..57ed8acc 100644 --- a/lib/pact/provider/rspec.rb +++ b/lib/pact/provider/rspec.rb @@ -25,6 +25,7 @@ def honour_pactfile pact_source, pact_json, options pact_uri = pact_source.uri Pact.configuration.output_stream.puts "INFO: Reading pact at #{pact_uri}" consumer_contract = Pact::ConsumerContract.from_json(pact_json) + suffix = pact_uri.metadata[:pending] ? " [PENDING]": "" example_group_description = "Verifying a pact between #{consumer_contract.consumer.name} and #{consumer_contract.provider.name}#{suffix}" example_group_metadata = { pactfile_uri: pact_uri, pact_criteria: options[:criteria] } @@ -77,7 +78,6 @@ def describe_interaction_with_provider_state interaction, options end def describe_interaction interaction, options - # pact_uri and pact_interaction are used by # Pact::Provider::RSpec::PactBrokerFormatter @@ -103,8 +103,9 @@ def describe_interaction interaction, options before do | example | interaction_context.run_once :before do Pact.configuration.logger.info "Running example '#{Pact::RSpec.full_description(example)}'" - set_up_provider_states interaction.provider_states, options[:consumer] - replay_interaction interaction, options[:request_customizer] + state_params = set_up_provider_states interaction.provider_states, options[:consumer] + interaction_context.state_params = state_params + replay_interaction interaction, options[:request_customizer], interaction_context interaction_context.last_response = last_response end end @@ -129,6 +130,7 @@ def describe_message expected_response, interaction_context include Pact::RSpec::Matchers extend Pact::Matchers::Messages + let(:expected_contents) { expected_response.body[:contents].as_json } let(:response) { interaction_context.last_response } let(:differ) { Pact.configuration.body_differ_for_content_type diff_content_type } @@ -214,6 +216,8 @@ class InteractionContext attr_accessor :last_response + attr_accessor :state_params + def initialize @already_run = [] end diff --git a/lib/pact/provider/test_methods.rb b/lib/pact/provider/test_methods.rb index f27466af..94e8207c 100644 --- a/lib/pact/provider/test_methods.rb +++ b/lib/pact/provider/test_methods.rb @@ -14,8 +14,8 @@ module TestMethods include Pact::Logging include Rack::Test::Methods - def replay_interaction interaction, request_customizer = nil - request = Request::Replayable.new(interaction.request) + def replay_interaction interaction, request_customizer = nil, interaction_context + request = Request::Replayable.new(interaction.request, interaction_context) request = request_customizer.call(request, interaction) if request_customizer args = [request.path, request.body, request.headers] @@ -42,11 +42,17 @@ def parse_body_from_response rack_response end def set_up_provider_states provider_states, consumer, options = {} + state_params = {}; # If there are no provider state, execute with an nil state to ensure global and base states are executed Pact.configuration.provider_state_set_up.call(nil, consumer, options) if provider_states.nil? || provider_states.empty? provider_states.each do | provider_state | - Pact.configuration.provider_state_set_up.call(provider_state.name, consumer, options.merge(params: provider_state.params)) + result = Pact.configuration.provider_state_set_up.call(provider_state.name, consumer, options.merge(params: provider_state.params)) + if result.is_a?(Hash) + state_params = state_params.merge(result) + end end + + state_params end def tear_down_provider_states provider_states, consumer, options = {} From f55adf1dff5d53575cf54c50cde9c11f46e5fb91 Mon Sep 17 00:00:00 2001 From: Steve Taylor Date: Thu, 15 Sep 2022 10:26:45 +1000 Subject: [PATCH 2/5] feat(generators): Add more generators as per spec --- lib/pact/provider/generator/boolean.rb | 16 +++ lib/pact/provider/generator/date.rb | 64 +++++++++ lib/pact/provider/generator/datetime.rb | 18 +++ lib/pact/provider/generator/provider_state.rb | 57 ++++++++ lib/pact/provider/generator/random_decimal.rb | 39 ++++++ .../provider/generator/random_hexadecimal.rb | 21 +++ lib/pact/provider/generator/random_int.rb | 18 +++ lib/pact/provider/generator/random_string.rb | 18 +++ lib/pact/provider/generator/regex.rb | 19 +++ lib/pact/provider/generator/time.rb | 18 +++ lib/pact/provider/generator/uuid.rb | 21 +++ lib/pact/provider/generators.rb | 62 +++++++-- .../provider/generators/provider_state.rb | 58 -------- lib/pact/provider/request.rb | 20 ++- lib/pact/provider/rspec.rb | 10 +- lib/pact/provider/test_methods.rb | 10 +- pact.gemspec | 4 +- .../pact/provider/generator/boolean_spec.rb | 20 +++ spec/lib/pact/provider/generator/date_spec.rb | 20 +++ .../pact/provider/generator/datetime_spec.rb | 21 +++ .../provider/generator/provider_state_spec.rb | 37 ++++++ .../provider/generator/random_decimal_spec.rb | 27 ++++ .../generator/random_hexadecimal_spec.rb | 25 ++++ .../provider/generator/random_int_spec.rb | 25 ++++ .../provider/generator/random_string_spec.rb | 25 ++++ .../lib/pact/provider/generator/regex_spec.rb | 20 +++ spec/lib/pact/provider/generator/time_spec.rb | 20 +++ spec/lib/pact/provider/generator/uuid_spec.rb | 20 +++ spec/lib/pact/provider/generators_spec.rb | 124 ++++++++++++++++++ 29 files changed, 765 insertions(+), 92 deletions(-) create mode 100644 lib/pact/provider/generator/boolean.rb create mode 100644 lib/pact/provider/generator/date.rb create mode 100644 lib/pact/provider/generator/datetime.rb create mode 100644 lib/pact/provider/generator/provider_state.rb create mode 100644 lib/pact/provider/generator/random_decimal.rb create mode 100644 lib/pact/provider/generator/random_hexadecimal.rb create mode 100644 lib/pact/provider/generator/random_int.rb create mode 100644 lib/pact/provider/generator/random_string.rb create mode 100644 lib/pact/provider/generator/regex.rb create mode 100644 lib/pact/provider/generator/time.rb create mode 100644 lib/pact/provider/generator/uuid.rb delete mode 100644 lib/pact/provider/generators/provider_state.rb create mode 100644 spec/lib/pact/provider/generator/boolean_spec.rb create mode 100644 spec/lib/pact/provider/generator/date_spec.rb create mode 100644 spec/lib/pact/provider/generator/datetime_spec.rb create mode 100644 spec/lib/pact/provider/generator/provider_state_spec.rb create mode 100644 spec/lib/pact/provider/generator/random_decimal_spec.rb create mode 100644 spec/lib/pact/provider/generator/random_hexadecimal_spec.rb create mode 100644 spec/lib/pact/provider/generator/random_int_spec.rb create mode 100644 spec/lib/pact/provider/generator/random_string_spec.rb create mode 100644 spec/lib/pact/provider/generator/regex_spec.rb create mode 100644 spec/lib/pact/provider/generator/time_spec.rb create mode 100644 spec/lib/pact/provider/generator/uuid_spec.rb create mode 100644 spec/lib/pact/provider/generators_spec.rb diff --git a/lib/pact/provider/generator/boolean.rb b/lib/pact/provider/generator/boolean.rb new file mode 100644 index 00000000..499e326a --- /dev/null +++ b/lib/pact/provider/generator/boolean.rb @@ -0,0 +1,16 @@ +module Pact + module Provider + module Generator + # Boolean provides the boolean generator which will give a true or false value + class Boolean + def can_generate?(hash) + hash.key?('type') && hash['type'] == 'Boolean' + end + + def call(_hash, _params = nil, _example_value = nil) + [true, false].sample + end + end + end + end +end diff --git a/lib/pact/provider/generator/date.rb b/lib/pact/provider/generator/date.rb new file mode 100644 index 00000000..7b05c247 --- /dev/null +++ b/lib/pact/provider/generator/date.rb @@ -0,0 +1,64 @@ +require 'date' + +module Pact + module Provider + module Generator + # Date provides the time generator which will give the current date in the defined format + class Date + def can_generate?(hash) + hash.key?('type') && hash['type'] == type + end + + def call(hash, _params = nil, _example_value = nil) + format = hash['format'] || default_format + ::Time.now.strftime(convert_from_java_simple_date_format(format)) + end + + def type + 'Date' + end + + def default_format + 'yyyy-MM-dd' + end + + # Format for the pact specficiation should be the Java DateTimeFormmater + # This tries to convert to something Ruby can format. + def convert_from_java_simple_date_format(format) + # Year + format.sub!(/(?= 0 + if position.positive? + # add string + return_string.push(buffer[0...position]) + end + end_position = buffer.index(END_EXPRESSION, position) + raise 'Missing closing brace in expression string' if !end_position || end_position.negative? + + variable = buffer[position + 2...end_position] + + if !params[variable] + logger.info "Could not subsitute provider state key #{variable}, have #{params}" + end + + expression = params[variable] || '' + return_string.push(expression) + + buffer = buffer[end_position + 1...-1] + position = buffer.index(START_EXPRESSION) + end + + return_string.join('') + end + end + end + end +end diff --git a/lib/pact/provider/generator/random_decimal.rb b/lib/pact/provider/generator/random_decimal.rb new file mode 100644 index 00000000..5a756552 --- /dev/null +++ b/lib/pact/provider/generator/random_decimal.rb @@ -0,0 +1,39 @@ +require 'bigdecimal' + +module Pact + module Provider + module Generator + # RandomDecimal provides the random decimal generator which will generate a decimal value of digits length + class RandomDecimal + def can_generate?(hash) + hash.key?('type') && hash['type'] == 'RandomDecimal' + end + + def call(hash, _params = nil, _example_value = nil) + digits = hash['digits'] || 6 + + raise 'RandomDecimalGenerator digits must be > 0, got $digits' if digits < 1 + + return rand(0..9) if digits == 1 + + return rand(0..9) + rand(1..9) / 10 if digits == 2 + + pos = rand(1..digits - 1) + precision = digits - pos + integers = '' + decimals = '' + while pos.positive? + integers += String(rand(1..9)) + pos -= 1 + end + while precision.positive? + decimals += String(rand(1..9)) + precision -= 1 + end + + Float("#{integers}.#{decimals}") + end + end + end + end +end diff --git a/lib/pact/provider/generator/random_hexadecimal.rb b/lib/pact/provider/generator/random_hexadecimal.rb new file mode 100644 index 00000000..f2e4f4db --- /dev/null +++ b/lib/pact/provider/generator/random_hexadecimal.rb @@ -0,0 +1,21 @@ +require 'securerandom' + +module Pact + module Provider + module Generator + # RandomHexadecimal provides the random hexadecimal generator which will generate a hexadecimal + class RandomHexadecimal + def can_generate?(hash) + hash.key?('type') && hash['type'] == 'RandomHexadecimal' + end + + def call(hash, _params = nil, _example_value = nil) + digits = hash['digits'] || 8 + bytes = (digits / 2).ceil + string = SecureRandom.hex(bytes) + string[0, digits] + end + end + end + end +end diff --git a/lib/pact/provider/generator/random_int.rb b/lib/pact/provider/generator/random_int.rb new file mode 100644 index 00000000..5f749359 --- /dev/null +++ b/lib/pact/provider/generator/random_int.rb @@ -0,0 +1,18 @@ +module Pact + module Provider + module Generator + # RandomInt provides the random int generator which generate a random integer, with a min/max + class RandomInt + def can_generate?(hash) + hash.key?('type') && hash['type'] == 'RandomInt' + end + + def call(hash, _params = nil, _example_value = nil) + min = hash['min'] || 0 + max = hash['max'] || 2_147_483_647 + rand(min..max) + end + end + end + end +end diff --git a/lib/pact/provider/generator/random_string.rb b/lib/pact/provider/generator/random_string.rb new file mode 100644 index 00000000..5c56b08d --- /dev/null +++ b/lib/pact/provider/generator/random_string.rb @@ -0,0 +1,18 @@ +module Pact + module Provider + module Generator + # RandomString provides the random string generator which generate a random string of size length + class RandomString + def can_generate?(hash) + hash.key?('type') && hash['type'] == 'RandomString' + end + + def call(hash, _params = nil, _example_value = nil) + size = hash['size'] || 20 + string = rand(36**(size + 2)).to_s(36) + string[0, size] + end + end + end + end +end diff --git a/lib/pact/provider/generator/regex.rb b/lib/pact/provider/generator/regex.rb new file mode 100644 index 00000000..1415cf87 --- /dev/null +++ b/lib/pact/provider/generator/regex.rb @@ -0,0 +1,19 @@ +require 'string_pattern' + +module Pact + module Provider + module Generator + # Regex provides the regex generator which will generate a value based on the regex pattern provided + class Regex + def can_generate?(hash) + hash.key?('type') && hash['type'] == 'Regex' + end + + def call(hash, _params = nil, _example_value = nil) + pattern = hash['pattern'] || '' + StringPattern.generate(Regexp.new(pattern)) + end + end + end + end +end diff --git a/lib/pact/provider/generator/time.rb b/lib/pact/provider/generator/time.rb new file mode 100644 index 00000000..0db4d214 --- /dev/null +++ b/lib/pact/provider/generator/time.rb @@ -0,0 +1,18 @@ +require 'date' + +module Pact + module Provider + module Generator + # Time provides the time generator which will give the current time in the defined format + class Time < Date + def type + 'Time' + end + + def default_format + 'HH:mm' + end + end + end + end +end diff --git a/lib/pact/provider/generator/uuid.rb b/lib/pact/provider/generator/uuid.rb new file mode 100644 index 00000000..f1441340 --- /dev/null +++ b/lib/pact/provider/generator/uuid.rb @@ -0,0 +1,21 @@ +require 'securerandom' + +module Pact + module Provider + module Generator + # Uuid provides the uuid generator + class Uuid + def can_generate?(hash) + hash.key?('type') && hash['type'] == 'Uuid' + end + + # If we had the example value, we could determine what type of uuid + # to send, this is what pact-jvm does + # See https://github.com/pact-foundation/pact-jvm/blob/master/core/model/src/main/kotlin/au/com/dius/pact/core/model/generators/Generator.kt + def call(_hash, _params = nil, _example_value = nil) + SecureRandom.uuid + end + end + end + end +end diff --git a/lib/pact/provider/generators.rb b/lib/pact/provider/generators.rb index bbafd09a..2cd77cdb 100644 --- a/lib/pact/provider/generators.rb +++ b/lib/pact/provider/generators.rb @@ -1,26 +1,66 @@ - -require 'pact/provider/generators/provider_state'; +require 'pact/provider/generator/boolean' +require 'pact/provider/generator/date' +require 'pact/provider/generator/datetime' +require 'pact/provider/generator/provider_state' +require 'pact/provider/generator/random_decimal' +require 'pact/provider/generator/random_hexadecimal' +require 'pact/provider/generator/random_int' +require 'pact/provider/generator/random_string' +require 'pact/provider/generator/regex' +require 'pact/provider/generator/time' +require 'pact/provider/generator/uuid' +require 'pact/matching_rules/jsonpath' +require 'pact/matching_rules/v3/extract' +require 'jsonpath' module Pact - module Provider + module Provider class Generators - def self.add_generator generator + def self.add_generator(generator) generators.unshift(generator) end - def self.generators + def self.generators @generators ||= [] end - def self.execute_generators object, interaction_context = nil - generators.each do | parser | - return parser.call(object, interaction_context) if parser.can_generate?(object) + def self.execute_generators(object, state_params = nil, example_value = nil) + generators.each do |parser| + return parser.call(object, state_params, example_value) if parser.can_generate?(object) + end + + raise Pact::UnrecognizePactFormatError, "This document does not use a recognised Pact generator: #{object}" + end + + def self.apply_generators(expected_request, component, example_value, state_params) + # Latest pact-support is required to have generators exposed + if expected_request.methods.include?(:generators) && expected_request.generators[component] + # Some component will have single generator without selectors, i.e. path + generators = expected_request.generators[component] + if generators.is_a?(Hash) && generators.key?('type') + return execute_generators(generators, state_params, example_value) + end + + generators.each do |selector, generator| + val = JsonPath.new(selector).on(example_value) + replace = execute_generators(generator, state_params, val) + example_value = JsonPath.for(example_value).gsub(selector) { |_v| replace }.to_hash + end end - - raise Pact::UnrecognizePactFormatError.new("This document does not use a recognised Pact generator: #{object}") + example_value end - add_generator(ProviderStateGenerator.new) + add_generator(Generator::Boolean.new) + add_generator(Generator::Date.new) + add_generator(Generator::DateTime.new) + add_generator(Generator::ProviderState.new) + add_generator(Generator::RandomDecimal.new) + add_generator(Generator::RandomHexadecimal.new) + add_generator(Generator::RandomInt.new) + add_generator(Generator::RandomString.new) + add_generator(Generator::Regex.new) + add_generator(Generator::Time.new) + add_generator(Generator::Uuid.new) end end end diff --git a/lib/pact/provider/generators/provider_state.rb b/lib/pact/provider/generators/provider_state.rb deleted file mode 100644 index fc86b67d..00000000 --- a/lib/pact/provider/generators/provider_state.rb +++ /dev/null @@ -1,58 +0,0 @@ -require 'pact/provider/generators' - -module Pact - module Provider - class ProviderStateGenerator - - - # rewrite of https://github.com/DiUS/pact-jvm/blob/master/core/support/src/main/kotlin/au/com/dius/pact/core/support/expressions/ExpressionParser.kt#L27 - VALUES_SEPARATOR = "," - START_EXPRESSION = "\${" - END_EXPRESSION = '}' - def parse_expression expression, params - - return_string = [] - - buffer = expression; - # initial value - position = buffer.index(START_EXPRESSION) - - while (position && position >= 0) - if (position > 0) - # add string - return_string.push(buffer[0...position]) - end - end_position = buffer.index(END_EXPRESSION, position) - if (end_position < 0) - raise "Missing closing brace in expression string \"#{$value}\"" - end - - variable = "" - - if (end_position - position > 2) - expression = params[buffer[position+2...end_position]] || "" - end - return_string.push(expression) - - buffer = buffer[end_position + 1...-1] - position = buffer.index(START_EXPRESSION) - end - - return_string.join("") - end - - def call hash, interaction_context = nil - params = interaction_context.state_params || {} - - parse_expression hash["expression"], params - end - - def can_generate?(hash) - hash.key?('type') && hash['type'] === 'ProviderState' - end - end - end -end - - - diff --git a/lib/pact/provider/request.rb b/lib/pact/provider/request.rb index 3701421a..561e24a6 100644 --- a/lib/pact/provider/request.rb +++ b/lib/pact/provider/request.rb @@ -11,9 +11,9 @@ class Replayable # See https://github.com/rack/rack/blob/e7d741c6282ca4cf4e01506f5681e6e6b14c0b32/SPEC#L87-89 NO_HTTP_PREFIX = ["CONTENT-TYPE", "CONTENT-LENGTH"] - def initialize expected_request, interaction_context = nil + def initialize expected_request, state_params = nil @expected_request = expected_request - @interaction_context = interaction_context + @state_params = state_params end def method @@ -21,12 +21,7 @@ def method end def path - if expected_request.methods.include? :generators - if expected_request.generators["path"] - return Pact::Provider::Generators.execute_generators(expected_request.generators["path"], @interaction_context) - end - end - expected_request.full_path + Pact::Provider::Generators.apply_generators(expected_request, "path", expected_request.full_path, @state_params) end def body @@ -34,17 +29,20 @@ def body when String then expected_request.body when NullExpectation then '' else - reified_body + Pact::Provider::Generators.apply_generators(expected_request, "body", reified_body, @state_params) end end def headers request_headers = {} return request_headers if expected_request.headers.is_a?(Pact::NullExpectation) + expected_request.headers.each do |key, value| - request_headers[rack_request_header_for(key)] = Pact::Reification.from_term(value) + request_headers[key] = Pact::Reification.from_term(value) end - request_headers + + request_headers = Pact::Provider::Generators.apply_generators(expected_request, "header", request_headers, @state_params) + request_headers.map{ |key,value| [rack_request_header_for(key), value]}.to_h end private diff --git a/lib/pact/provider/rspec.rb b/lib/pact/provider/rspec.rb index 57ed8acc..b498def5 100644 --- a/lib/pact/provider/rspec.rb +++ b/lib/pact/provider/rspec.rb @@ -25,7 +25,7 @@ def honour_pactfile pact_source, pact_json, options pact_uri = pact_source.uri Pact.configuration.output_stream.puts "INFO: Reading pact at #{pact_uri}" consumer_contract = Pact::ConsumerContract.from_json(pact_json) - + suffix = pact_uri.metadata[:pending] ? " [PENDING]": "" example_group_description = "Verifying a pact between #{consumer_contract.consumer.name} and #{consumer_contract.provider.name}#{suffix}" example_group_metadata = { pactfile_uri: pact_uri, pact_criteria: options[:criteria] } @@ -103,9 +103,9 @@ def describe_interaction interaction, options before do | example | interaction_context.run_once :before do Pact.configuration.logger.info "Running example '#{Pact::RSpec.full_description(example)}'" - state_params = set_up_provider_states interaction.provider_states, options[:consumer] - interaction_context.state_params = state_params - replay_interaction interaction, options[:request_customizer], interaction_context + provider_states_result = set_up_provider_states interaction.provider_states, options[:consumer] + state_params = provider_states_result[interaction.provider_state]; + replay_interaction interaction, options[:request_customizer], state_params interaction_context.last_response = last_response end end @@ -216,8 +216,6 @@ class InteractionContext attr_accessor :last_response - attr_accessor :state_params - def initialize @already_run = [] end diff --git a/lib/pact/provider/test_methods.rb b/lib/pact/provider/test_methods.rb index 94e8207c..1619633d 100644 --- a/lib/pact/provider/test_methods.rb +++ b/lib/pact/provider/test_methods.rb @@ -14,8 +14,8 @@ module TestMethods include Pact::Logging include Rack::Test::Methods - def replay_interaction interaction, request_customizer = nil, interaction_context - request = Request::Replayable.new(interaction.request, interaction_context) + def replay_interaction interaction, request_customizer = nil, state_params = nil + request = Request::Replayable.new(interaction.request, state_params) request = request_customizer.call(request, interaction) if request_customizer args = [request.path, request.body, request.headers] @@ -42,17 +42,17 @@ def parse_body_from_response rack_response end def set_up_provider_states provider_states, consumer, options = {} - state_params = {}; + provider_states_result = {}; # If there are no provider state, execute with an nil state to ensure global and base states are executed Pact.configuration.provider_state_set_up.call(nil, consumer, options) if provider_states.nil? || provider_states.empty? provider_states.each do | provider_state | result = Pact.configuration.provider_state_set_up.call(provider_state.name, consumer, options.merge(params: provider_state.params)) if result.is_a?(Hash) - state_params = state_params.merge(result) + provider_states_result[provider_state.name] = result end end - state_params + provider_states_result end def tear_down_provider_states provider_states, consumer, options = {} diff --git a/pact.gemspec b/pact.gemspec index e3a0c556..6456a809 100644 --- a/pact.gemspec +++ b/pact.gemspec @@ -31,8 +31,10 @@ Gem::Specification.new do |gem| gem.add_runtime_dependency 'thor', '>= 0.20', '< 2.0' gem.add_runtime_dependency 'webrick', '~> 1.3' gem.add_runtime_dependency 'term-ansicolor', '~> 1.0' + gem.add_runtime_dependency 'string_pattern', '~> 2.0' + gem.add_runtime_dependency 'jsonpath', '~> 1.0' - gem.add_runtime_dependency 'pact-support', '~> 1.16', '>= 1.16.9' + gem.add_runtime_dependency 'pact-support', '~> 1.19', '>= 1.19.0' gem.add_runtime_dependency 'pact-mock_service', '~> 3.0', '>= 3.3.1' gem.add_development_dependency 'rake', '~> 13.0' diff --git a/spec/lib/pact/provider/generator/boolean_spec.rb b/spec/lib/pact/provider/generator/boolean_spec.rb new file mode 100644 index 00000000..0ef0d7e0 --- /dev/null +++ b/spec/lib/pact/provider/generator/boolean_spec.rb @@ -0,0 +1,20 @@ +require 'pact/provider/generator/boolean' + +describe Pact::Provider::Generator::Boolean do + generator = Pact::Provider::Generator::Boolean.new + + it 'can_generate with a supported hash' do + hash = { 'type' => 'Boolean' } + expect(generator.can_generate?(hash)).to be true + end + + it 'can_generate with a unsupported hash' do + hash = { 'type' => 'unknown' } + expect(generator.can_generate?(hash)).to be false + end + + it 'call' do + hash = { 'type' => 'Boolean' } + expect(generator.call(hash)).to eq(true).or eq(false) + end +end diff --git a/spec/lib/pact/provider/generator/date_spec.rb b/spec/lib/pact/provider/generator/date_spec.rb new file mode 100644 index 00000000..46b32d16 --- /dev/null +++ b/spec/lib/pact/provider/generator/date_spec.rb @@ -0,0 +1,20 @@ +require 'pact/provider/generator/date' + +describe Pact::Provider::Generator::Date do + generator = Pact::Provider::Generator::Date.new + + it 'can_generate with a supported hash' do + hash = { 'type' => 'Date' } + expect(generator.can_generate?(hash)).to be true + end + + it 'can_generate with a unsupported hash' do + hash = { 'type' => 'unknown' } + expect(generator.can_generate?(hash)).to be false + end + + it 'call' do + hash = { 'type' => 'Date' } + expect(generator.call(hash).length).to eq(10) + end +end diff --git a/spec/lib/pact/provider/generator/datetime_spec.rb b/spec/lib/pact/provider/generator/datetime_spec.rb new file mode 100644 index 00000000..32c8c1b5 --- /dev/null +++ b/spec/lib/pact/provider/generator/datetime_spec.rb @@ -0,0 +1,21 @@ +require 'pact/provider/generator/datetime' + +describe Pact::Provider::Generator::DateTime do + generator = Pact::Provider::Generator::DateTime.new + + it 'can_generate with a supported hash' do + hash = { 'type' => 'DateTime' } + expect(generator.can_generate?(hash)).to be true + end + + it 'can_generate with a unsupported hash' do + hash = { 'type' => 'unknown' } + expect(generator.can_generate?(hash)).to be false + end + + it 'call' do + hash = { 'type' => 'DateTime' } + p generator.call(hash) + expect(generator.call(hash).length).to eq(16) + end +end diff --git a/spec/lib/pact/provider/generator/provider_state_spec.rb b/spec/lib/pact/provider/generator/provider_state_spec.rb new file mode 100644 index 00000000..3e3d4519 --- /dev/null +++ b/spec/lib/pact/provider/generator/provider_state_spec.rb @@ -0,0 +1,37 @@ +require 'pact/provider/generator/provider_state' + +describe Pact::Provider::Generator::ProviderState do + generator = Pact::Provider::Generator::ProviderState.new + + it 'can_generate with a supported hash' do + hash = { 'type' => 'ProviderState' } + expect(generator.can_generate?(hash)).to be true + end + + it 'can_generate with a unsupported hash' do + hash = { 'type' => 'unknown' } + expect(generator.can_generate?(hash)).to be false + end + + it 'call without params' do + hash = { 'type' => 'ProviderState', 'expression' => 'Bearer ${access_token}' } + expect(generator.call(hash)).to eq 'Bearer ' + end + + it 'call with correct params' do + hash = { 'type' => 'ProviderState', 'expression' => 'Bearer ${access_token}' } + params = { 'access_token' => 'ABC' } + expect(generator.call(hash, params)).to eq 'Bearer ABC' + end + + it 'call with wrong params' do + hash = { 'type' => 'ProviderState', 'expression' => 'Bearer ${access_token}' } + params = { 'refresh_token' => 'ABC' } + expect(generator.call(hash, params)).to eq 'Bearer ' + end + + it 'call with incomplete expression' do + hash = { 'type' => 'ProviderState', 'expression' => 'Bearer ${access_token' } + expect { generator.call(hash) }.to raise_error('Missing closing brace in expression string') + end +end diff --git a/spec/lib/pact/provider/generator/random_decimal_spec.rb b/spec/lib/pact/provider/generator/random_decimal_spec.rb new file mode 100644 index 00000000..eb495367 --- /dev/null +++ b/spec/lib/pact/provider/generator/random_decimal_spec.rb @@ -0,0 +1,27 @@ +require 'pact/provider/generator/random_decimal' + +describe Pact::Provider::Generator::RandomDecimal do + generator = Pact::Provider::Generator::RandomDecimal.new + + it 'can_generate with a supported hash' do + hash = { 'type' => 'RandomDecimal' } + expect(generator.can_generate?(hash)).to be true + end + + it 'can_generate with a unsupported hash' do + hash = { 'type' => 'unknown' } + expect(generator.can_generate?(hash)).to be false + end + + it 'call' do + hash = { 'type' => 'RandomDecimal' } + value = generator.call(hash) + expect(String(value).length).to eq 7 + end + + it 'call with digits' do + hash = { 'type' => 'RandomDecimal', 'digits' => 10 } + value = generator.call(hash) + expect(String(value).length).to eq 11 + end +end diff --git a/spec/lib/pact/provider/generator/random_hexadecimal_spec.rb b/spec/lib/pact/provider/generator/random_hexadecimal_spec.rb new file mode 100644 index 00000000..48bd6f94 --- /dev/null +++ b/spec/lib/pact/provider/generator/random_hexadecimal_spec.rb @@ -0,0 +1,25 @@ +require 'pact/provider/generator/random_hexadecimal' + +describe Pact::Provider::Generator::RandomHexadecimal do + generator = Pact::Provider::Generator::RandomHexadecimal.new + + it 'can_generate with a supported hash' do + hash = { 'type' => 'RandomHexadecimal' } + expect(generator.can_generate?(hash)).to be true + end + + it 'can_generate with a unsupported hash' do + hash = { 'type' => 'unknown' } + expect(generator.can_generate?(hash)).to be false + end + + it 'call' do + hash = { 'type' => 'RandomHexadecimal' } + expect(generator.call(hash).length).to eq(8) + end + + it 'call with size' do + hash = { 'type' => 'RandomHexadecimal', 'digits' => 2 } + expect(generator.call(hash).length).to eq(2) + end +end diff --git a/spec/lib/pact/provider/generator/random_int_spec.rb b/spec/lib/pact/provider/generator/random_int_spec.rb new file mode 100644 index 00000000..e267c79b --- /dev/null +++ b/spec/lib/pact/provider/generator/random_int_spec.rb @@ -0,0 +1,25 @@ +require 'pact/provider/generator/random_int' + +describe Pact::Provider::Generator::RandomInt do + generator = Pact::Provider::Generator::RandomInt.new + + it 'can_generate with a supported hash' do + hash = { 'type' => 'RandomInt' } + expect(generator.can_generate?(hash)).to be true + end + + it 'can_generate with a unsupported hash' do + hash = { 'type' => 'unknown' } + expect(generator.can_generate?(hash)).to be false + end + + it 'call' do + hash = { 'type' => 'RandomInt' } + expect(generator.call(hash).instance_of?(Integer)).to be true + end + + it 'call with min/max' do + hash = { 'type' => 'RandomInt', 'min' => 5, 'max' => 5 } + expect(generator.call(hash)).to eq 5 + end +end diff --git a/spec/lib/pact/provider/generator/random_string_spec.rb b/spec/lib/pact/provider/generator/random_string_spec.rb new file mode 100644 index 00000000..098563ec --- /dev/null +++ b/spec/lib/pact/provider/generator/random_string_spec.rb @@ -0,0 +1,25 @@ +require 'pact/provider/generator/random_string' + +describe Pact::Provider::Generator::RandomString do + generator = Pact::Provider::Generator::RandomString.new + + it 'can_generate with a supported hash' do + hash = { 'type' => 'RandomString' } + expect(generator.can_generate?(hash)).to be true + end + + it 'can_generate with a unsupported hash' do + hash = { 'type' => 'unknown' } + expect(generator.can_generate?(hash)).to be false + end + + it 'call' do + hash = { 'type' => 'RandomString' } + expect(generator.call(hash).length).to eq(20) + end + + it 'call with size' do + hash = { 'type' => 'RandomString', 'size' => 30 } + expect(generator.call(hash).length).to eq(30) + end +end diff --git a/spec/lib/pact/provider/generator/regex_spec.rb b/spec/lib/pact/provider/generator/regex_spec.rb new file mode 100644 index 00000000..db2738a1 --- /dev/null +++ b/spec/lib/pact/provider/generator/regex_spec.rb @@ -0,0 +1,20 @@ +require 'pact/provider/generator/regex' + +describe Pact::Provider::Generator::Regex do + generator = Pact::Provider::Generator::Regex.new + + it 'can_generate with a supported hash' do + hash = { 'type' => 'Regex' } + expect(generator.can_generate?(hash)).to be true + end + + it 'can_generate with a unsupported hash' do + hash = { 'type' => 'unknown' } + expect(generator.can_generate?(hash)).to be false + end + + it 'call' do + hash = { 'type' => 'Regex', 'pattern' => '(one|two)' } + expect(generator.call(hash)).to eq('one').or eq('two') + end +end diff --git a/spec/lib/pact/provider/generator/time_spec.rb b/spec/lib/pact/provider/generator/time_spec.rb new file mode 100644 index 00000000..9baa6b9b --- /dev/null +++ b/spec/lib/pact/provider/generator/time_spec.rb @@ -0,0 +1,20 @@ +require 'pact/provider/generator/time' + +describe Pact::Provider::Generator::Time do + generator = Pact::Provider::Generator::Time.new + + it 'can_generate with a supported hash' do + hash = { 'type' => 'Time' } + expect(generator.can_generate?(hash)).to be true + end + + it 'can_generate with a unsupported hash' do + hash = { 'type' => 'unknown' } + expect(generator.can_generate?(hash)).to be false + end + + it 'call' do + hash = { 'type' => 'Time' } + expect(generator.call(hash).length).to eq(5) + end +end diff --git a/spec/lib/pact/provider/generator/uuid_spec.rb b/spec/lib/pact/provider/generator/uuid_spec.rb new file mode 100644 index 00000000..c19665bb --- /dev/null +++ b/spec/lib/pact/provider/generator/uuid_spec.rb @@ -0,0 +1,20 @@ +require 'pact/provider/generator/uuid' + +describe Pact::Provider::Generator::Uuid do + generator = Pact::Provider::Generator::Uuid.new + + it 'can_generate with a supported hash' do + hash = { 'type' => 'Uuid' } + expect(generator.can_generate?(hash)).to be true + end + + it 'can_generate with a unsupported hash' do + hash = { 'type' => 'unknown' } + expect(generator.can_generate?(hash)).to be false + end + + it 'call' do + hash = { 'type' => 'Uuid' } + expect(generator.call(hash).length).to eq(36) + end +end diff --git a/spec/lib/pact/provider/generators_spec.rb b/spec/lib/pact/provider/generators_spec.rb new file mode 100644 index 00000000..198e1068 --- /dev/null +++ b/spec/lib/pact/provider/generators_spec.rb @@ -0,0 +1,124 @@ +require 'pact/provider/generators' +require 'pact/provider/request' + +describe Pact::Provider::Generators do + it 'execute_generators with Boolean' do + hash = { 'type' => 'Boolean' } + expect(Pact::Provider::Generators.execute_generators(hash)).to eq(true).or eq(false) + end + + it 'execute_generators with Date' do + hash = { 'type' => 'Date' } + expect(Pact::Provider::Generators.execute_generators(hash).length).to eq(10) + end + + it 'execute_generators with DateTime' do + hash = { 'type' => 'DateTime' } + expect(Pact::Provider::Generators.execute_generators(hash).length).to eq(16) + end + + it 'execute_generators with ProviderState' do + hash = { 'type' => 'ProviderState', 'expression' => 'Bearer ${access_token}' } + params = { 'access_token' => 'ABC' } + expect(Pact::Provider::Generators.execute_generators(hash, params)).to eq('Bearer ABC') + end + + it 'execute_generators with RandomDecimal' do + hash = { 'type' => 'RandomDecimal' } + expect(String(Pact::Provider::Generators.execute_generators(hash)).length).to eq(7) + end + + it 'execute_generators with RandomHexadecimal' do + hash = { 'type' => 'RandomHexadecimal' } + expect(Pact::Provider::Generators.execute_generators(hash).length).to eq(8) + end + + it 'execute_generators with RandomInt' do + hash = { 'type' => 'RandomInt' } + expect(Pact::Provider::Generators.execute_generators(hash).instance_of?(Integer)).to be true + end + + it 'execute_generators with RandomString' do + hash = { 'type' => 'RandomString' } + expect(Pact::Provider::Generators.execute_generators(hash).length).to eq(20) + end + + it 'execute_generators with Regex' do + hash = { 'type' => 'Regex', 'pattern' => '(one|two)' } + expect(Pact::Provider::Generators.execute_generators(hash)).to eq('one').or eq('two') + end + + it 'execute_generators with Time' do + hash = { 'type' => 'Time' } + expect(Pact::Provider::Generators.execute_generators(hash).length).to eq(5) + end + + it 'execute_generators with Uuid' do + hash = { 'type' => 'Uuid' } + expect(Pact::Provider::Generators.execute_generators(hash).length).to eq(36) + end + + it 'apply_generators for path' do + expected_request = Pact::Request::Expected.from_hash({ + method: 'GET', + path: '/path/1', + generators: { + 'path' => { + 'type' => 'ProviderState', + 'expression' => '/path/${itemID}' + } + } + }) + state_params = { + 'itemID' => 2 + } + request = Pact::Provider::Request::Replayable.new(expected_request, state_params) + expect(request.path).to eq('/path/2') + end + + it 'apply_generators for headers' do + expected_request = Pact::Request::Expected.from_hash({ + method: 'GET', + path: '/path/1', + headers: { + 'Authorization' => 'Bearer 123' + }, + generators: { + 'header' => { + '$.Authorization' => { + 'expression' => 'Bearer ${accessToken}', + 'type' => 'ProviderState' + } + } + } + }) + state_params = { + 'accessToken' => 'ABC' + } + request = Pact::Provider::Request::Replayable.new(expected_request, state_params) + expect(request.headers).to eq({ + 'HTTP_AUTHORIZATION' => 'Bearer ABC' + }) + end + + it 'apply_generators for body' do + expected_request = Pact::Request::Expected.from_hash({ + method: 'GET', + path: '/path/1', + body: { + 'result' => [ + '12345F' + ] + }, + generators: { + 'body' => { + '$.result[0]' => { + 'type' => 'RandomHexadecimal' + } + } + } + }) + request = Pact::Provider::Request::Replayable.new(expected_request) + expect(request.body['result'][0].length).to eq(8) + end +end From 6bf53a5a7d382d497efc373f17c74ba60c2687f5 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Tue, 13 Aug 2024 15:57:05 +0100 Subject: [PATCH 3/5] chore: migrate generator code into pact-support --- lib/pact/provider/generator/boolean.rb | 16 ----- lib/pact/provider/generator/date.rb | 64 ------------------ lib/pact/provider/generator/datetime.rb | 18 ----- lib/pact/provider/generator/provider_state.rb | 57 ---------------- lib/pact/provider/generator/random_decimal.rb | 39 ----------- .../provider/generator/random_hexadecimal.rb | 21 ------ lib/pact/provider/generator/random_int.rb | 18 ----- lib/pact/provider/generator/random_string.rb | 18 ----- lib/pact/provider/generator/regex.rb | 19 ------ lib/pact/provider/generator/time.rb | 18 ----- lib/pact/provider/generator/uuid.rb | 21 ------ lib/pact/provider/generators.rb | 66 ------------------- lib/pact/provider/request.rb | 8 +-- .../pact/provider/generator/boolean_spec.rb | 20 ------ spec/lib/pact/provider/generator/date_spec.rb | 20 ------ .../pact/provider/generator/datetime_spec.rb | 21 ------ .../provider/generator/provider_state_spec.rb | 37 ----------- .../provider/generator/random_decimal_spec.rb | 27 -------- .../generator/random_hexadecimal_spec.rb | 25 ------- .../provider/generator/random_int_spec.rb | 25 ------- .../provider/generator/random_string_spec.rb | 25 ------- .../lib/pact/provider/generator/regex_spec.rb | 20 ------ spec/lib/pact/provider/generator/time_spec.rb | 20 ------ spec/lib/pact/provider/generator/uuid_spec.rb | 20 ------ spec/lib/pact/provider/generators_spec.rb | 60 +---------------- 25 files changed, 6 insertions(+), 697 deletions(-) delete mode 100644 lib/pact/provider/generator/boolean.rb delete mode 100644 lib/pact/provider/generator/date.rb delete mode 100644 lib/pact/provider/generator/datetime.rb delete mode 100644 lib/pact/provider/generator/provider_state.rb delete mode 100644 lib/pact/provider/generator/random_decimal.rb delete mode 100644 lib/pact/provider/generator/random_hexadecimal.rb delete mode 100644 lib/pact/provider/generator/random_int.rb delete mode 100644 lib/pact/provider/generator/random_string.rb delete mode 100644 lib/pact/provider/generator/regex.rb delete mode 100644 lib/pact/provider/generator/time.rb delete mode 100644 lib/pact/provider/generator/uuid.rb delete mode 100644 lib/pact/provider/generators.rb delete mode 100644 spec/lib/pact/provider/generator/boolean_spec.rb delete mode 100644 spec/lib/pact/provider/generator/date_spec.rb delete mode 100644 spec/lib/pact/provider/generator/datetime_spec.rb delete mode 100644 spec/lib/pact/provider/generator/provider_state_spec.rb delete mode 100644 spec/lib/pact/provider/generator/random_decimal_spec.rb delete mode 100644 spec/lib/pact/provider/generator/random_hexadecimal_spec.rb delete mode 100644 spec/lib/pact/provider/generator/random_int_spec.rb delete mode 100644 spec/lib/pact/provider/generator/random_string_spec.rb delete mode 100644 spec/lib/pact/provider/generator/regex_spec.rb delete mode 100644 spec/lib/pact/provider/generator/time_spec.rb delete mode 100644 spec/lib/pact/provider/generator/uuid_spec.rb diff --git a/lib/pact/provider/generator/boolean.rb b/lib/pact/provider/generator/boolean.rb deleted file mode 100644 index 499e326a..00000000 --- a/lib/pact/provider/generator/boolean.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Pact - module Provider - module Generator - # Boolean provides the boolean generator which will give a true or false value - class Boolean - def can_generate?(hash) - hash.key?('type') && hash['type'] == 'Boolean' - end - - def call(_hash, _params = nil, _example_value = nil) - [true, false].sample - end - end - end - end -end diff --git a/lib/pact/provider/generator/date.rb b/lib/pact/provider/generator/date.rb deleted file mode 100644 index 7b05c247..00000000 --- a/lib/pact/provider/generator/date.rb +++ /dev/null @@ -1,64 +0,0 @@ -require 'date' - -module Pact - module Provider - module Generator - # Date provides the time generator which will give the current date in the defined format - class Date - def can_generate?(hash) - hash.key?('type') && hash['type'] == type - end - - def call(hash, _params = nil, _example_value = nil) - format = hash['format'] || default_format - ::Time.now.strftime(convert_from_java_simple_date_format(format)) - end - - def type - 'Date' - end - - def default_format - 'yyyy-MM-dd' - end - - # Format for the pact specficiation should be the Java DateTimeFormmater - # This tries to convert to something Ruby can format. - def convert_from_java_simple_date_format(format) - # Year - format.sub!(/(?= 0 - if position.positive? - # add string - return_string.push(buffer[0...position]) - end - end_position = buffer.index(END_EXPRESSION, position) - raise 'Missing closing brace in expression string' if !end_position || end_position.negative? - - variable = buffer[position + 2...end_position] - - if !params[variable] - logger.info "Could not subsitute provider state key #{variable}, have #{params}" - end - - expression = params[variable] || '' - return_string.push(expression) - - buffer = buffer[end_position + 1...-1] - position = buffer.index(START_EXPRESSION) - end - - return_string.join('') - end - end - end - end -end diff --git a/lib/pact/provider/generator/random_decimal.rb b/lib/pact/provider/generator/random_decimal.rb deleted file mode 100644 index 5a756552..00000000 --- a/lib/pact/provider/generator/random_decimal.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'bigdecimal' - -module Pact - module Provider - module Generator - # RandomDecimal provides the random decimal generator which will generate a decimal value of digits length - class RandomDecimal - def can_generate?(hash) - hash.key?('type') && hash['type'] == 'RandomDecimal' - end - - def call(hash, _params = nil, _example_value = nil) - digits = hash['digits'] || 6 - - raise 'RandomDecimalGenerator digits must be > 0, got $digits' if digits < 1 - - return rand(0..9) if digits == 1 - - return rand(0..9) + rand(1..9) / 10 if digits == 2 - - pos = rand(1..digits - 1) - precision = digits - pos - integers = '' - decimals = '' - while pos.positive? - integers += String(rand(1..9)) - pos -= 1 - end - while precision.positive? - decimals += String(rand(1..9)) - precision -= 1 - end - - Float("#{integers}.#{decimals}") - end - end - end - end -end diff --git a/lib/pact/provider/generator/random_hexadecimal.rb b/lib/pact/provider/generator/random_hexadecimal.rb deleted file mode 100644 index f2e4f4db..00000000 --- a/lib/pact/provider/generator/random_hexadecimal.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'securerandom' - -module Pact - module Provider - module Generator - # RandomHexadecimal provides the random hexadecimal generator which will generate a hexadecimal - class RandomHexadecimal - def can_generate?(hash) - hash.key?('type') && hash['type'] == 'RandomHexadecimal' - end - - def call(hash, _params = nil, _example_value = nil) - digits = hash['digits'] || 8 - bytes = (digits / 2).ceil - string = SecureRandom.hex(bytes) - string[0, digits] - end - end - end - end -end diff --git a/lib/pact/provider/generator/random_int.rb b/lib/pact/provider/generator/random_int.rb deleted file mode 100644 index 5f749359..00000000 --- a/lib/pact/provider/generator/random_int.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Pact - module Provider - module Generator - # RandomInt provides the random int generator which generate a random integer, with a min/max - class RandomInt - def can_generate?(hash) - hash.key?('type') && hash['type'] == 'RandomInt' - end - - def call(hash, _params = nil, _example_value = nil) - min = hash['min'] || 0 - max = hash['max'] || 2_147_483_647 - rand(min..max) - end - end - end - end -end diff --git a/lib/pact/provider/generator/random_string.rb b/lib/pact/provider/generator/random_string.rb deleted file mode 100644 index 5c56b08d..00000000 --- a/lib/pact/provider/generator/random_string.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Pact - module Provider - module Generator - # RandomString provides the random string generator which generate a random string of size length - class RandomString - def can_generate?(hash) - hash.key?('type') && hash['type'] == 'RandomString' - end - - def call(hash, _params = nil, _example_value = nil) - size = hash['size'] || 20 - string = rand(36**(size + 2)).to_s(36) - string[0, size] - end - end - end - end -end diff --git a/lib/pact/provider/generator/regex.rb b/lib/pact/provider/generator/regex.rb deleted file mode 100644 index 1415cf87..00000000 --- a/lib/pact/provider/generator/regex.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'string_pattern' - -module Pact - module Provider - module Generator - # Regex provides the regex generator which will generate a value based on the regex pattern provided - class Regex - def can_generate?(hash) - hash.key?('type') && hash['type'] == 'Regex' - end - - def call(hash, _params = nil, _example_value = nil) - pattern = hash['pattern'] || '' - StringPattern.generate(Regexp.new(pattern)) - end - end - end - end -end diff --git a/lib/pact/provider/generator/time.rb b/lib/pact/provider/generator/time.rb deleted file mode 100644 index 0db4d214..00000000 --- a/lib/pact/provider/generator/time.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'date' - -module Pact - module Provider - module Generator - # Time provides the time generator which will give the current time in the defined format - class Time < Date - def type - 'Time' - end - - def default_format - 'HH:mm' - end - end - end - end -end diff --git a/lib/pact/provider/generator/uuid.rb b/lib/pact/provider/generator/uuid.rb deleted file mode 100644 index f1441340..00000000 --- a/lib/pact/provider/generator/uuid.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'securerandom' - -module Pact - module Provider - module Generator - # Uuid provides the uuid generator - class Uuid - def can_generate?(hash) - hash.key?('type') && hash['type'] == 'Uuid' - end - - # If we had the example value, we could determine what type of uuid - # to send, this is what pact-jvm does - # See https://github.com/pact-foundation/pact-jvm/blob/master/core/model/src/main/kotlin/au/com/dius/pact/core/model/generators/Generator.kt - def call(_hash, _params = nil, _example_value = nil) - SecureRandom.uuid - end - end - end - end -end diff --git a/lib/pact/provider/generators.rb b/lib/pact/provider/generators.rb deleted file mode 100644 index 2cd77cdb..00000000 --- a/lib/pact/provider/generators.rb +++ /dev/null @@ -1,66 +0,0 @@ -require 'pact/provider/generator/boolean' -require 'pact/provider/generator/date' -require 'pact/provider/generator/datetime' -require 'pact/provider/generator/provider_state' -require 'pact/provider/generator/random_decimal' -require 'pact/provider/generator/random_hexadecimal' -require 'pact/provider/generator/random_int' -require 'pact/provider/generator/random_string' -require 'pact/provider/generator/regex' -require 'pact/provider/generator/time' -require 'pact/provider/generator/uuid' -require 'pact/matching_rules/jsonpath' -require 'pact/matching_rules/v3/extract' -require 'jsonpath' - -module Pact - module Provider - class Generators - def self.add_generator(generator) - generators.unshift(generator) - end - - def self.generators - @generators ||= [] - end - - def self.execute_generators(object, state_params = nil, example_value = nil) - generators.each do |parser| - return parser.call(object, state_params, example_value) if parser.can_generate?(object) - end - - raise Pact::UnrecognizePactFormatError, "This document does not use a recognised Pact generator: #{object}" - end - - def self.apply_generators(expected_request, component, example_value, state_params) - # Latest pact-support is required to have generators exposed - if expected_request.methods.include?(:generators) && expected_request.generators[component] - # Some component will have single generator without selectors, i.e. path - generators = expected_request.generators[component] - if generators.is_a?(Hash) && generators.key?('type') - return execute_generators(generators, state_params, example_value) - end - - generators.each do |selector, generator| - val = JsonPath.new(selector).on(example_value) - replace = execute_generators(generator, state_params, val) - example_value = JsonPath.for(example_value).gsub(selector) { |_v| replace }.to_hash - end - end - example_value - end - - add_generator(Generator::Boolean.new) - add_generator(Generator::Date.new) - add_generator(Generator::DateTime.new) - add_generator(Generator::ProviderState.new) - add_generator(Generator::RandomDecimal.new) - add_generator(Generator::RandomHexadecimal.new) - add_generator(Generator::RandomInt.new) - add_generator(Generator::RandomString.new) - add_generator(Generator::Regex.new) - add_generator(Generator::Time.new) - add_generator(Generator::Uuid.new) - end - end -end diff --git a/lib/pact/provider/request.rb b/lib/pact/provider/request.rb index 561e24a6..89e196ef 100644 --- a/lib/pact/provider/request.rb +++ b/lib/pact/provider/request.rb @@ -1,7 +1,7 @@ require 'json' require 'pact/reification' require 'pact/shared/null_expectation' -require 'pact/provider/generators' +require 'pact/generators' module Pact module Provider @@ -21,7 +21,7 @@ def method end def path - Pact::Provider::Generators.apply_generators(expected_request, "path", expected_request.full_path, @state_params) + Pact::Generators.apply_generators(expected_request, "path", expected_request.full_path, @state_params) end def body @@ -29,7 +29,7 @@ def body when String then expected_request.body when NullExpectation then '' else - Pact::Provider::Generators.apply_generators(expected_request, "body", reified_body, @state_params) + Pact::Generators.apply_generators(expected_request, "body", reified_body, @state_params) end end @@ -41,7 +41,7 @@ def headers request_headers[key] = Pact::Reification.from_term(value) end - request_headers = Pact::Provider::Generators.apply_generators(expected_request, "header", request_headers, @state_params) + request_headers = Pact::Generators.apply_generators(expected_request, "header", request_headers, @state_params) request_headers.map{ |key,value| [rack_request_header_for(key), value]}.to_h end diff --git a/spec/lib/pact/provider/generator/boolean_spec.rb b/spec/lib/pact/provider/generator/boolean_spec.rb deleted file mode 100644 index 0ef0d7e0..00000000 --- a/spec/lib/pact/provider/generator/boolean_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'pact/provider/generator/boolean' - -describe Pact::Provider::Generator::Boolean do - generator = Pact::Provider::Generator::Boolean.new - - it 'can_generate with a supported hash' do - hash = { 'type' => 'Boolean' } - expect(generator.can_generate?(hash)).to be true - end - - it 'can_generate with a unsupported hash' do - hash = { 'type' => 'unknown' } - expect(generator.can_generate?(hash)).to be false - end - - it 'call' do - hash = { 'type' => 'Boolean' } - expect(generator.call(hash)).to eq(true).or eq(false) - end -end diff --git a/spec/lib/pact/provider/generator/date_spec.rb b/spec/lib/pact/provider/generator/date_spec.rb deleted file mode 100644 index 46b32d16..00000000 --- a/spec/lib/pact/provider/generator/date_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'pact/provider/generator/date' - -describe Pact::Provider::Generator::Date do - generator = Pact::Provider::Generator::Date.new - - it 'can_generate with a supported hash' do - hash = { 'type' => 'Date' } - expect(generator.can_generate?(hash)).to be true - end - - it 'can_generate with a unsupported hash' do - hash = { 'type' => 'unknown' } - expect(generator.can_generate?(hash)).to be false - end - - it 'call' do - hash = { 'type' => 'Date' } - expect(generator.call(hash).length).to eq(10) - end -end diff --git a/spec/lib/pact/provider/generator/datetime_spec.rb b/spec/lib/pact/provider/generator/datetime_spec.rb deleted file mode 100644 index 32c8c1b5..00000000 --- a/spec/lib/pact/provider/generator/datetime_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'pact/provider/generator/datetime' - -describe Pact::Provider::Generator::DateTime do - generator = Pact::Provider::Generator::DateTime.new - - it 'can_generate with a supported hash' do - hash = { 'type' => 'DateTime' } - expect(generator.can_generate?(hash)).to be true - end - - it 'can_generate with a unsupported hash' do - hash = { 'type' => 'unknown' } - expect(generator.can_generate?(hash)).to be false - end - - it 'call' do - hash = { 'type' => 'DateTime' } - p generator.call(hash) - expect(generator.call(hash).length).to eq(16) - end -end diff --git a/spec/lib/pact/provider/generator/provider_state_spec.rb b/spec/lib/pact/provider/generator/provider_state_spec.rb deleted file mode 100644 index 3e3d4519..00000000 --- a/spec/lib/pact/provider/generator/provider_state_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'pact/provider/generator/provider_state' - -describe Pact::Provider::Generator::ProviderState do - generator = Pact::Provider::Generator::ProviderState.new - - it 'can_generate with a supported hash' do - hash = { 'type' => 'ProviderState' } - expect(generator.can_generate?(hash)).to be true - end - - it 'can_generate with a unsupported hash' do - hash = { 'type' => 'unknown' } - expect(generator.can_generate?(hash)).to be false - end - - it 'call without params' do - hash = { 'type' => 'ProviderState', 'expression' => 'Bearer ${access_token}' } - expect(generator.call(hash)).to eq 'Bearer ' - end - - it 'call with correct params' do - hash = { 'type' => 'ProviderState', 'expression' => 'Bearer ${access_token}' } - params = { 'access_token' => 'ABC' } - expect(generator.call(hash, params)).to eq 'Bearer ABC' - end - - it 'call with wrong params' do - hash = { 'type' => 'ProviderState', 'expression' => 'Bearer ${access_token}' } - params = { 'refresh_token' => 'ABC' } - expect(generator.call(hash, params)).to eq 'Bearer ' - end - - it 'call with incomplete expression' do - hash = { 'type' => 'ProviderState', 'expression' => 'Bearer ${access_token' } - expect { generator.call(hash) }.to raise_error('Missing closing brace in expression string') - end -end diff --git a/spec/lib/pact/provider/generator/random_decimal_spec.rb b/spec/lib/pact/provider/generator/random_decimal_spec.rb deleted file mode 100644 index eb495367..00000000 --- a/spec/lib/pact/provider/generator/random_decimal_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'pact/provider/generator/random_decimal' - -describe Pact::Provider::Generator::RandomDecimal do - generator = Pact::Provider::Generator::RandomDecimal.new - - it 'can_generate with a supported hash' do - hash = { 'type' => 'RandomDecimal' } - expect(generator.can_generate?(hash)).to be true - end - - it 'can_generate with a unsupported hash' do - hash = { 'type' => 'unknown' } - expect(generator.can_generate?(hash)).to be false - end - - it 'call' do - hash = { 'type' => 'RandomDecimal' } - value = generator.call(hash) - expect(String(value).length).to eq 7 - end - - it 'call with digits' do - hash = { 'type' => 'RandomDecimal', 'digits' => 10 } - value = generator.call(hash) - expect(String(value).length).to eq 11 - end -end diff --git a/spec/lib/pact/provider/generator/random_hexadecimal_spec.rb b/spec/lib/pact/provider/generator/random_hexadecimal_spec.rb deleted file mode 100644 index 48bd6f94..00000000 --- a/spec/lib/pact/provider/generator/random_hexadecimal_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'pact/provider/generator/random_hexadecimal' - -describe Pact::Provider::Generator::RandomHexadecimal do - generator = Pact::Provider::Generator::RandomHexadecimal.new - - it 'can_generate with a supported hash' do - hash = { 'type' => 'RandomHexadecimal' } - expect(generator.can_generate?(hash)).to be true - end - - it 'can_generate with a unsupported hash' do - hash = { 'type' => 'unknown' } - expect(generator.can_generate?(hash)).to be false - end - - it 'call' do - hash = { 'type' => 'RandomHexadecimal' } - expect(generator.call(hash).length).to eq(8) - end - - it 'call with size' do - hash = { 'type' => 'RandomHexadecimal', 'digits' => 2 } - expect(generator.call(hash).length).to eq(2) - end -end diff --git a/spec/lib/pact/provider/generator/random_int_spec.rb b/spec/lib/pact/provider/generator/random_int_spec.rb deleted file mode 100644 index e267c79b..00000000 --- a/spec/lib/pact/provider/generator/random_int_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'pact/provider/generator/random_int' - -describe Pact::Provider::Generator::RandomInt do - generator = Pact::Provider::Generator::RandomInt.new - - it 'can_generate with a supported hash' do - hash = { 'type' => 'RandomInt' } - expect(generator.can_generate?(hash)).to be true - end - - it 'can_generate with a unsupported hash' do - hash = { 'type' => 'unknown' } - expect(generator.can_generate?(hash)).to be false - end - - it 'call' do - hash = { 'type' => 'RandomInt' } - expect(generator.call(hash).instance_of?(Integer)).to be true - end - - it 'call with min/max' do - hash = { 'type' => 'RandomInt', 'min' => 5, 'max' => 5 } - expect(generator.call(hash)).to eq 5 - end -end diff --git a/spec/lib/pact/provider/generator/random_string_spec.rb b/spec/lib/pact/provider/generator/random_string_spec.rb deleted file mode 100644 index 098563ec..00000000 --- a/spec/lib/pact/provider/generator/random_string_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'pact/provider/generator/random_string' - -describe Pact::Provider::Generator::RandomString do - generator = Pact::Provider::Generator::RandomString.new - - it 'can_generate with a supported hash' do - hash = { 'type' => 'RandomString' } - expect(generator.can_generate?(hash)).to be true - end - - it 'can_generate with a unsupported hash' do - hash = { 'type' => 'unknown' } - expect(generator.can_generate?(hash)).to be false - end - - it 'call' do - hash = { 'type' => 'RandomString' } - expect(generator.call(hash).length).to eq(20) - end - - it 'call with size' do - hash = { 'type' => 'RandomString', 'size' => 30 } - expect(generator.call(hash).length).to eq(30) - end -end diff --git a/spec/lib/pact/provider/generator/regex_spec.rb b/spec/lib/pact/provider/generator/regex_spec.rb deleted file mode 100644 index db2738a1..00000000 --- a/spec/lib/pact/provider/generator/regex_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'pact/provider/generator/regex' - -describe Pact::Provider::Generator::Regex do - generator = Pact::Provider::Generator::Regex.new - - it 'can_generate with a supported hash' do - hash = { 'type' => 'Regex' } - expect(generator.can_generate?(hash)).to be true - end - - it 'can_generate with a unsupported hash' do - hash = { 'type' => 'unknown' } - expect(generator.can_generate?(hash)).to be false - end - - it 'call' do - hash = { 'type' => 'Regex', 'pattern' => '(one|two)' } - expect(generator.call(hash)).to eq('one').or eq('two') - end -end diff --git a/spec/lib/pact/provider/generator/time_spec.rb b/spec/lib/pact/provider/generator/time_spec.rb deleted file mode 100644 index 9baa6b9b..00000000 --- a/spec/lib/pact/provider/generator/time_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'pact/provider/generator/time' - -describe Pact::Provider::Generator::Time do - generator = Pact::Provider::Generator::Time.new - - it 'can_generate with a supported hash' do - hash = { 'type' => 'Time' } - expect(generator.can_generate?(hash)).to be true - end - - it 'can_generate with a unsupported hash' do - hash = { 'type' => 'unknown' } - expect(generator.can_generate?(hash)).to be false - end - - it 'call' do - hash = { 'type' => 'Time' } - expect(generator.call(hash).length).to eq(5) - end -end diff --git a/spec/lib/pact/provider/generator/uuid_spec.rb b/spec/lib/pact/provider/generator/uuid_spec.rb deleted file mode 100644 index c19665bb..00000000 --- a/spec/lib/pact/provider/generator/uuid_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'pact/provider/generator/uuid' - -describe Pact::Provider::Generator::Uuid do - generator = Pact::Provider::Generator::Uuid.new - - it 'can_generate with a supported hash' do - hash = { 'type' => 'Uuid' } - expect(generator.can_generate?(hash)).to be true - end - - it 'can_generate with a unsupported hash' do - hash = { 'type' => 'unknown' } - expect(generator.can_generate?(hash)).to be false - end - - it 'call' do - hash = { 'type' => 'Uuid' } - expect(generator.call(hash).length).to eq(36) - end -end diff --git a/spec/lib/pact/provider/generators_spec.rb b/spec/lib/pact/provider/generators_spec.rb index 198e1068..575dae80 100644 --- a/spec/lib/pact/provider/generators_spec.rb +++ b/spec/lib/pact/provider/generators_spec.rb @@ -1,63 +1,7 @@ -require 'pact/provider/generators' +require 'pact/generators' require 'pact/provider/request' -describe Pact::Provider::Generators do - it 'execute_generators with Boolean' do - hash = { 'type' => 'Boolean' } - expect(Pact::Provider::Generators.execute_generators(hash)).to eq(true).or eq(false) - end - - it 'execute_generators with Date' do - hash = { 'type' => 'Date' } - expect(Pact::Provider::Generators.execute_generators(hash).length).to eq(10) - end - - it 'execute_generators with DateTime' do - hash = { 'type' => 'DateTime' } - expect(Pact::Provider::Generators.execute_generators(hash).length).to eq(16) - end - - it 'execute_generators with ProviderState' do - hash = { 'type' => 'ProviderState', 'expression' => 'Bearer ${access_token}' } - params = { 'access_token' => 'ABC' } - expect(Pact::Provider::Generators.execute_generators(hash, params)).to eq('Bearer ABC') - end - - it 'execute_generators with RandomDecimal' do - hash = { 'type' => 'RandomDecimal' } - expect(String(Pact::Provider::Generators.execute_generators(hash)).length).to eq(7) - end - - it 'execute_generators with RandomHexadecimal' do - hash = { 'type' => 'RandomHexadecimal' } - expect(Pact::Provider::Generators.execute_generators(hash).length).to eq(8) - end - - it 'execute_generators with RandomInt' do - hash = { 'type' => 'RandomInt' } - expect(Pact::Provider::Generators.execute_generators(hash).instance_of?(Integer)).to be true - end - - it 'execute_generators with RandomString' do - hash = { 'type' => 'RandomString' } - expect(Pact::Provider::Generators.execute_generators(hash).length).to eq(20) - end - - it 'execute_generators with Regex' do - hash = { 'type' => 'Regex', 'pattern' => '(one|two)' } - expect(Pact::Provider::Generators.execute_generators(hash)).to eq('one').or eq('two') - end - - it 'execute_generators with Time' do - hash = { 'type' => 'Time' } - expect(Pact::Provider::Generators.execute_generators(hash).length).to eq(5) - end - - it 'execute_generators with Uuid' do - hash = { 'type' => 'Uuid' } - expect(Pact::Provider::Generators.execute_generators(hash).length).to eq(36) - end - +describe Pact::Generators do it 'apply_generators for path' do expected_request = Pact::Request::Expected.from_hash({ method: 'GET', From 0e91d63213560758dfe2d1ecea787656ebeb7312 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Tue, 13 Aug 2024 15:58:28 +0100 Subject: [PATCH 4/5] chore: point to v3 generator branch --- Gemfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gemfile b/Gemfile index 649524a1..dca8ba42 100644 --- a/Gemfile +++ b/Gemfile @@ -17,6 +17,8 @@ if ENV['X_PACT_DEVELOPMENT'] gem "pact-support", path: '../pact-support' gem "pact-mock_service", path: '../pact-mock_service' gem "pry-byebug" +else + gem "pact-support", git: 'https://github.com/pact-foundation/pact-support.git', branch: 'feat/v3_generators' end group :local_development do From 02f39f7f215e542189391018787c952078ca13d2 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Fri, 29 Nov 2024 13:05:17 +0000 Subject: [PATCH 5/5] chore: remove pact-support --- Gemfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Gemfile b/Gemfile index dca8ba42..649524a1 100644 --- a/Gemfile +++ b/Gemfile @@ -17,8 +17,6 @@ if ENV['X_PACT_DEVELOPMENT'] gem "pact-support", path: '../pact-support' gem "pact-mock_service", path: '../pact-mock_service' gem "pry-byebug" -else - gem "pact-support", git: 'https://github.com/pact-foundation/pact-support.git', branch: 'feat/v3_generators' end group :local_development do