File: runner.rb

package info (click to toggle)
ruby-sinatra 4.2.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,932 kB
  • sloc: ruby: 17,700; sh: 25; makefile: 8
file content (164 lines) | stat: -rw-r--r-- 3,648 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# frozen_string_literal: true

require 'open-uri'
require 'net/http'
require 'timeout'

module Sinatra
  # NOTE: This feature is experimental, and missing tests!
  #
  # Helps you spinning up and shutting down your own sinatra app. This is especially helpful for running
  # real network tests against a sinatra backend.
  #
  # The backend server could look like the following (in test/server.rb).
  #
  #   require "sinatra"
  #
  #   get "/" do
  #     "Cheers from test server"
  #   end
  #
  #   get "/ping" do
  #     "1"
  #   end
  #
  # Note that you need to implement a ping action for internal use.
  #
  # Next, you need to write your runner.
  #
  #   require 'sinatra/runner'
  #
  #   class Runner < Sinatra::Runner
  #     def app_file
  #       File.expand_path("server.rb", __dir__)
  #     end
  #   end
  #
  # Override Runner#app_file, #command, #port, #protocol and #ping_path for customization.
  #
  # **Don't forget to override #app_file specific to your application!**
  #
  # Wherever you need this test backend, here's how you manage it. The following example assumes you
  # have a test in your app that needs to be run against your test backend.
  #
  #   runner = ServerRunner.new
  #   runner.run
  #
  #   # ..tests against localhost:4567 here..
  #
  #   runner.kill
  #
  # For an example, check https://github.com/apotonick/roar/blob/master/test/integration/runner.rb
  class Runner
    def app_file
      File.expand_path('server.rb', __dir__)
    end

    def run
      @pipe     = start
      @started  = Time.now
      warn "#{server} up and running on port #{port}" if ping
    end

    def kill
      return unless pipe

      Process.kill('KILL', pipe.pid)
    rescue NotImplementedError
      system "kill -9 #{pipe.pid}"
    rescue Errno::ESRCH
    end

    def get(url)
      Timeout.timeout(1) { get_url("#{protocol}://127.0.0.1:#{port}#{url}") }
    end

    def get_stream(url = '/stream', &block)
      Net::HTTP.start '127.0.0.1', port do |http|
        request = Net::HTTP::Get.new url
        http.request request do |response|
          response.read_body(&block)
        end
      end
    end

    def get_response(url)
      Net::HTTP.start '127.0.0.1', port do |http|
        request = Net::HTTP::Get.new url
        http.request request do |response|
          response
        end
      end
    end

    def log
      @log ||= +''
      loop { @log << pipe.read_nonblock(1) }
    rescue Exception
      @log
    end

    private

    attr_accessor :pipe

    def start
      IO.popen(command)
    end

    # to be overwritten
    def command
      "bundle exec ruby #{app_file} -p #{port} -e production"
    end

    def ping(timeout = 30)
      loop do
        return if alive?

        if Time.now - @started > timeout
          warn command, log
          raise "timeout starting server with command '#{command}'"
        else
          sleep 0.1
        end
      end
    end

    def alive?
      3.times { get(ping_path) }
      true
    rescue EOFError, SystemCallError, OpenURI::HTTPError, Timeout::Error
      false
    end

    # to be overwritten
    def ping_path
      '/ping'
    end

    # to be overwritten
    def port
      4567
    end

    def protocol
      'http'
    end

    def get_url(url)
      uri = URI.parse(url)

      return uri.read unless protocol == 'https'

      get_https_url(uri)
    end

    def get_https_url(uri)
      http = Net::HTTP.new(uri.host, uri.port)
      http.use_ssl      = true
      http.verify_mode  = OpenSSL::SSL::VERIFY_NONE
      request = Net::HTTP::Get.new(uri.request_uri)
      http.request(request).body
    end
  end
end