loading
Generated 2025-10-08T23:58:57+00:00

All Files ( 100.0% covered at 15.4 hits/line )

6 files in total.
100 relevant lines, 100 lines covered and 0 lines missed. ( 100.0% )
6 total branches, 6 branches covered and 0 branches missed. ( 100.0% )
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line Branch Coverage Branches Covered branches Missed branches
lib/http/fake.rb 100.00 % 20 11 11 0 1.27 100.00 % 0 0 0
lib/http/fake/builder.rb 100.00 % 52 28 28 0 23.43 100.00 % 4 4 0
lib/http/fake/client.rb 100.00 % 27 14 14 0 3.14 100.00 % 0 0 0
lib/http/fake/connector.rb 100.00 % 29 13 13 0 8.38 100.00 % 0 0 0
lib/http/fake/requester.rb 100.00 % 23 11 11 0 3.18 100.00 % 0 0 0
lib/http/fake/responder.rb 100.00 % 46 23 23 0 29.65 100.00 % 2 2 0

lib/http/fake.rb

100.0% lines covered

100.0% branches covered

11 relevant lines. 11 lines covered and 0 lines missed.
0 total branches, 0 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 require "http"
  3. 1 require "zeitwerk"
  4. 1 Zeitwerk::Loader.new.then do |loader|
  5. 1 loader.inflector.inflect "http" => "HTTP"
  6. 1 loader.tag = "http-fake"
  7. 1 loader.push_dir "#{__dir__}/.."
  8. 1 loader.setup
  9. end
  10. 1 module HTTP
  11. # Main namespace.
  12. 1 module Fake
  13. 1 def self.loader registry = Zeitwerk::Registry
  14. 4 @loader ||= registry.loaders.each.find { |loader| loader.tag == "http-fake" }
  15. end
  16. end
  17. end

lib/http/fake/builder.rb

100.0% lines covered

100.0% branches covered

28 relevant lines. 28 lines covered and 0 lines missed.
4 total branches, 4 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 require "refinements/hash"
  3. 1 require "uri"
  4. 1 module HTTP
  5. 1 module Fake
  6. # Builds the response for a request.
  7. 1 class Builder
  8. 1 using Refinements::Hash
  9. 1 ALLOWED_VERBS = /\A(connect|delete|get|head|options|patch|post|purge|put|trace)\z/
  10. 1 attr_reader :requests
  11. # :reek:DuplicateMethodCall
  12. 1 def initialize verbs: ALLOWED_VERBS, responder: Responder
  13. 39 @verbs = verbs
  14. 39 @responder = responder
  15. 39 @requests ||= Hash.with_default Hash.with_default([])
  16. 39 @responders = Hash.with_default []
  17. end
  18. 1 def request verb, uri, arguments = {}
  19. 65 responder = responders[verb].find { |fake| fake.match? verb, URI(uri).path }
  20. 17 else: 15 then: 2 missing_route_error verb, uri unless responder
  21. 15 requests[uri][verb] << arguments
  22. 15 responder.call uri, arguments
  23. end
  24. 1 def method_missing(verb, *arguments, &)
  25. 124 then: 123 else: 1 respond_to_missing?(verb) ? add_responder(verb, *arguments, &) : super
  26. end
  27. 1 private
  28. 1 attr_reader :verbs, :responder, :responders
  29. 1 def respond_to_missing?(verb, include_private = false) = verbs.match?(verb) || super
  30. 1 def add_responder verb, pattern, &block
  31. 123 responder.new(verb, pattern, block).then do |instance|
  32. 123 responders[[verb, pattern]].append instance
  33. end
  34. end
  35. 1 def missing_route_error verb, uri
  36. 2 fail StandardError, "No route for #{verb.upcase} #{uri} in responders: #{responders}."
  37. end
  38. end
  39. end
  40. end

lib/http/fake/client.rb

100.0% lines covered

100.0% branches covered

14 relevant lines. 14 lines covered and 0 lines missed.
0 total branches, 0 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 require "forwardable"
  3. 1 module HTTP
  4. 1 module Fake
  5. # Provides the primary client for dealing with fake requests and responses.
  6. 1 class Client
  7. 1 include Chainable
  8. 1 extend Forwardable
  9. 1 delegate %i[requests] => :builder
  10. 1 def initialize builder: Builder.new, requester: Requester, &block
  11. 11 @builder = builder
  12. 11 @requester = requester
  13. 11 @builder.instance_eval(&block)
  14. end
  15. 1 private
  16. 1 attr_reader :builder, :requester
  17. 1 def branch(arguments) = requester.new arguments, builder:
  18. end
  19. end
  20. end

lib/http/fake/connector.rb

100.0% lines covered

100.0% branches covered

13 relevant lines. 13 lines covered and 0 lines missed.
0 total branches, 0 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 module HTTP
  3. 1 module Fake
  4. # Connects an HTTP request and response together.
  5. 1 class Connector
  6. 1 def initialize version: Connection::HTTP_1_1, request: Request, response: Response
  7. 25 @version = version
  8. 25 @request = request
  9. 25 @response = response
  10. end
  11. 1 def call **arguments
  12. 25 response.new defaults.merge(
  13. request: build_request(**arguments),
  14. **arguments.slice(:headers, :version, :body, :status)
  15. )
  16. end
  17. 1 private
  18. 1 attr_reader :version, :request, :response
  19. 1 def defaults = {headers: {}, version:, status: 200}
  20. 1 def build_request(**arguments) = request.new arguments.slice :verb, :uri
  21. end
  22. end
  23. end

lib/http/fake/requester.rb

100.0% lines covered

100.0% branches covered

11 relevant lines. 11 lines covered and 0 lines missed.
0 total branches, 0 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 module HTTP
  3. 1 module Fake
  4. # Handles an incoming request.
  5. 1 class Requester
  6. 1 include Chainable
  7. 1 def initialize defaults = {}, builder: Builder.new
  8. 13 @defaults = defaults
  9. 13 @builder = builder
  10. end
  11. 1 def request(verb, uri, arguments = {}) = builder.request verb, uri, defaults.merge(arguments)
  12. 1 private
  13. 1 attr_reader :defaults, :builder
  14. 1 def branch(arguments) = self.class.new defaults.merge(arguments), builder:
  15. end
  16. end
  17. end

lib/http/fake/responder.rb

100.0% lines covered

100.0% branches covered

23 relevant lines. 23 lines covered and 0 lines missed.
2 total branches, 2 branches covered and 0 branches missed.
    
  1. # frozen_string_literal: true
  2. 1 require "mustermann"
  3. 1 require "refinements/string"
  4. 1 module HTTP
  5. 1 module Fake
  6. # Handles an outgoing response.
  7. 1 class Responder
  8. 1 using Refinements::String
  9. 1 attr_reader :headers
  10. 1 def initialize verb, pattern, function
  11. 138 @verb = verb
  12. 138 @pattern = Mustermann.new pattern
  13. 138 @function = function
  14. 138 @headers = {}
  15. end
  16. 1 def match?(verb, path) = !String(pattern.match(path)).blank? && verb == self.verb
  17. 1 def call path, arguments = {}
  18. 23 pattern.params(path)
  19. 23 .then { |params| instance_exec(params, arguments, &function) }
  20. 23 .then { |data| connect path, data }
  21. end
  22. 1 def status(code = nil) = @status ||= code
  23. 1 private
  24. 1 attr_reader :verb, :pattern, :function
  25. 1 def connect path, data
  26. 23 Connector.new.call verb:,
  27. uri: "https://example.com/#{path}",
  28. headers:,
  29. body: body(data),
  30. status:
  31. end
  32. 24 then: 3 else: 20 def body(data) = data == status ? "" : data
  33. end
  34. end
  35. end