httpotemkin 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/CONTRIBUTING.md +1 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +19 -0
- data/README.md +25 -0
- data/TODO +5 -0
- data/bin/httpotemkin +69 -0
- data/containers/api.rubygems/Dockerfile +12 -0
- data/containers/api.rubygems/latest_specs.4.8.gz +0 -0
- data/containers/api.rubygems/rubygems-update-2.5.0.gemspec.rz +0 -0
- data/containers/api.rubygems/secure_server.rb +21 -0
- data/containers/build_containers +35 -0
- data/containers/client/Dockerfile +30 -0
- data/containers/client/certificates/.gitignore +1 -0
- data/containers/client/oscrc +7 -0
- data/containers/client/run_test.rb +5 -0
- data/containers/client/server.crt +19 -0
- data/containers/obs/Dockerfile +10 -0
- data/containers/obs/secure_server.rb +13 -0
- data/containers/rubygems/Dockerfile +10 -0
- data/containers/rubygems/secure_server.rb +39 -0
- data/containers/server/Dockerfile +17 -0
- data/containers/server/secure_server.rb +13 -0
- data/containers/server/server.rb +17 -0
- data/containers/server/server_base.rb +30 -0
- data/containers/show_certificate.sh +1 -0
- data/expected_output +3 -0
- data/httpotemkin.gemspec +23 -0
- data/lib/client.rb +38 -0
- data/lib/containers.rb +113 -0
- data/lib/httpotemkin.rb +5 -0
- data/lib/test.rb +23 -0
- data/logs/.gitignore +1 -0
- data/spec/integration/bin/client/docker +5 -0
- data/spec/integration/bin/down/docker +8 -0
- data/spec/integration/bin/run.failure/docker +5 -0
- data/spec/integration/bin/run.success/docker +7 -0
- data/spec/integration/bin/status/docker +4 -0
- data/spec/integration/bin/up/docker +8 -0
- data/spec/integration/cli_spec.rb +157 -0
- data/spec/integration/spec_helper.rb +21 -0
- data/spec/system/client_spec.rb +70 -0
- data/spec/system/data/red_herring-000.tar.gz +0 -0
- data/spec/system/data/test.tar.gz +0 -0
- data/spec/system/hello_world_spec.rb +20 -0
- data/spec/system/spec_helper.rb +3 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 680e6b1ecaeb762c4d4ec9ad4e704d07e57d11f8
|
4
|
+
data.tar.gz: bd9941065a464eff2cfe82259f76bcba7bd647cc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 654de56651be4fdd59502f828c9404cd266cf47046f9a6c0a7712f6a58d1a0ba79b484787240b193254597e6602a1845ffca5134d12d237e39b490840ea4cab6
|
7
|
+
data.tar.gz: 88bbd23cf90c764c0505d39af88096b88cd987584067c6a83cb8775d1587913f97fdcef79b1579cfca9022d2203bd80b65a23e7223f1a46ab161565321ffcbe4
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Contributions are welcome. Let them flow through GitHub.
|
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2014 Cornelius Schumacher <schumacher@kde.org>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Httpotemkin provides tools to mock HTTP servers for system-level tests. It uses
|
2
|
+
docker containers to provide mocks of HTTP APIs and Ruby classes which can
|
3
|
+
be used in tests to execute applications in the environment of the mocked APIs.
|
4
|
+
This allows to test applications which talk to HTTP APIs without having to
|
5
|
+
change them in any way for the tests, so that very high-level system tests can
|
6
|
+
be done in a controlled environment suitable for test driven development.
|
7
|
+
|
8
|
+
The tests in `spec/system` provide examples of how tests using httpotemkin can
|
9
|
+
be written.
|
10
|
+
|
11
|
+
## Debugging
|
12
|
+
|
13
|
+
When debugging tests (which actually means reverse engineering the protocol of
|
14
|
+
the service and implementing the required bits) it is convenient to get in a
|
15
|
+
mode of interactive debugging, where there are containers for the server and
|
16
|
+
the client under control of a shell. This allows to manually trigger calls from
|
17
|
+
the client and see the resulting requests in the log of the server. By adapting
|
18
|
+
the server calls one at a time, it's easily possible to provide the minimal
|
19
|
+
mocking to make the tests run.
|
20
|
+
|
21
|
+
One way to enter the state of debugging a specific test is to add debug code to
|
22
|
+
the RSpec test, e.g. add a `binding.pry` call just before the client test call
|
23
|
+
is executed and passing `r.pry` to the `rspec` command. Then you end up in a
|
24
|
+
state where all containers are started, test data is injected, and you can debug
|
25
|
+
the calls which are supposed to take place.
|
data/TODO
ADDED
data/bin/httpotemkin
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require_relative "../lib/httpotemkin"
|
4
|
+
|
5
|
+
containers = Httpotemkin::Containers.new
|
6
|
+
containers.add_server("rubygems")
|
7
|
+
containers.add_server("api.rubygems")
|
8
|
+
containers.add_server("obs")
|
9
|
+
|
10
|
+
cmd = ARGV[0]
|
11
|
+
|
12
|
+
if cmd == "status"
|
13
|
+
containers.print_status
|
14
|
+
elsif cmd == "up"
|
15
|
+
puts "Starting server containers"
|
16
|
+
containers.up
|
17
|
+
elsif cmd == "down"
|
18
|
+
puts "Stopping server containers"
|
19
|
+
containers.down
|
20
|
+
elsif cmd == "client"
|
21
|
+
puts containers.run_client
|
22
|
+
elsif cmd == "run"
|
23
|
+
puts "Running tests"
|
24
|
+
|
25
|
+
expected_output = File.read("expected_output")
|
26
|
+
|
27
|
+
containers.up
|
28
|
+
sleep 3 unless ENV["RUN_BY_RSPEC"]
|
29
|
+
|
30
|
+
actual_output = containers.run_client
|
31
|
+
|
32
|
+
puts "--- Start Test ---"
|
33
|
+
puts actual_output
|
34
|
+
puts "---- End Test ----"
|
35
|
+
|
36
|
+
containers.down(save_logs: true)
|
37
|
+
|
38
|
+
puts
|
39
|
+
if actual_output == expected_output
|
40
|
+
puts "Success."
|
41
|
+
else
|
42
|
+
puts "Failed."
|
43
|
+
puts
|
44
|
+
puts "Expected output:"
|
45
|
+
puts expected_output
|
46
|
+
puts
|
47
|
+
puts "Actual output:"
|
48
|
+
puts actual_output
|
49
|
+
exit 1
|
50
|
+
end
|
51
|
+
else
|
52
|
+
if !cmd
|
53
|
+
STDERR.puts "You need to provide a command"
|
54
|
+
elsif cmd != "help"
|
55
|
+
puts "Unknown command: #{cmd}"
|
56
|
+
end
|
57
|
+
|
58
|
+
puts "Usage: httpotemkin <command>"
|
59
|
+
puts
|
60
|
+
puts "Testing with HTTP mocks based on containers"
|
61
|
+
puts
|
62
|
+
puts "Commands:"
|
63
|
+
puts " status - Show status of containers"
|
64
|
+
puts " up - Start containers"
|
65
|
+
puts " down - Stop containters"
|
66
|
+
puts " client - Run client"
|
67
|
+
puts " run - Run tests"
|
68
|
+
puts " help - Show command line help"
|
69
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
FROM server
|
2
|
+
MAINTAINER Cornelius Schumacher <schumacher@kde.org>
|
3
|
+
|
4
|
+
COPY secure_server.rb /httpotemkin/api.rubygems/
|
5
|
+
COPY server.crt /httpotemkin/api.rubygems/
|
6
|
+
COPY server.key /httpotemkin/api.rubygems/
|
7
|
+
COPY latest_specs.4.8.gz /httpotemkin/api.rubygems/
|
8
|
+
COPY rubygems-update-2.5.0.gemspec.rz /httpotemkin/api.rubygems/
|
9
|
+
|
10
|
+
CMD /httpotemkin/api.rubygems/secure_server.rb
|
11
|
+
|
12
|
+
EXPOSE 443
|
Binary file
|
Binary file
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require "sinatra"
|
4
|
+
|
5
|
+
require_relative "../server/server_base.rb"
|
6
|
+
|
7
|
+
CERT_DIR = File.dirname(__FILE__)
|
8
|
+
|
9
|
+
configure_sinatra
|
10
|
+
|
11
|
+
get '/' do
|
12
|
+
"I'm the secure api.rubygems server\n"
|
13
|
+
end
|
14
|
+
|
15
|
+
get '/latest_specs.4.8.gz' do
|
16
|
+
File.read(File.expand_path("../latest_specs.4.8.gz", __FILE__))
|
17
|
+
end
|
18
|
+
|
19
|
+
get '/quick/Marshal.4.8/rubygems-update-2.5.0.gemspec.rz' do
|
20
|
+
File.read(File.expand_path("../rubygems-update-2.5.0.gemspec.rz", __FILE__))
|
21
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
def run(cmd)
|
6
|
+
puts cmd
|
7
|
+
if !system(cmd)
|
8
|
+
STDERR.puts "Command failed. Exiting."
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def build_image(name)
|
14
|
+
run("docker build -t #{name} #{name}")
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_api_image(image_name, server_name)
|
18
|
+
run("openssl req -x509 -newkey rsa:2048 -keyout #{image_name}/server.key -out #{image_name}/server.crt -days 1000 -nodes -subj '/CN=#{server_name}'")
|
19
|
+
|
20
|
+
FileUtils.copy("#{image_name}/server.crt", "client/certificates/#{image_name}.crt")
|
21
|
+
|
22
|
+
run("docker build -t #{image_name} #{image_name}")
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
build_image("server")
|
27
|
+
|
28
|
+
build_api_image("rubygems", "rubygems.org")
|
29
|
+
build_api_image("api.rubygems", "api.rubygems.org")
|
30
|
+
build_api_image("obs", "api.opensuse.org")
|
31
|
+
|
32
|
+
build_image("client")
|
33
|
+
|
34
|
+
|
35
|
+
puts "Success."
|
@@ -0,0 +1,30 @@
|
|
1
|
+
FROM opensuse:42.1
|
2
|
+
MAINTAINER Cornelius Schumacher <schumacher@kde.org>
|
3
|
+
|
4
|
+
run zypper --non-interactive install ca-certificates-mozilla
|
5
|
+
|
6
|
+
RUN zypper --non-interactive install curl
|
7
|
+
RUN zypper --non-interactive install ruby
|
8
|
+
RUN zypper --non-interactive install --no-recommends osc
|
9
|
+
|
10
|
+
# Dependencies for yes_ship_it
|
11
|
+
RUN zypper --non-interactive install ruby2.1-rubygem-inifile ruby2.1-rubygem-rest-client
|
12
|
+
RUN zypper --non-interactive install git
|
13
|
+
|
14
|
+
# Configuration for OBS client
|
15
|
+
COPY oscrc /root/.oscrc
|
16
|
+
|
17
|
+
COPY certificates/rubygems.crt /etc/pki/trust/anchors/rubygems.pem
|
18
|
+
COPY certificates/api.rubygems.crt /etc/pki/trust/anchors/api.rubygems.pem
|
19
|
+
COPY certificates/obs.crt /etc/pki/trust/anchors/obs.pem
|
20
|
+
|
21
|
+
RUN update-ca-certificates
|
22
|
+
|
23
|
+
COPY run_test.rb /run_test.rb
|
24
|
+
|
25
|
+
# Configuration for gem
|
26
|
+
RUN mkdir /root/.gem
|
27
|
+
RUN echo ":rubygems_api_key: 123" >/root/.gem/credentials
|
28
|
+
RUN chmod 0600 /root/.gem/credentials
|
29
|
+
|
30
|
+
CMD /run_test.rb
|
@@ -0,0 +1 @@
|
|
1
|
+
*.crt
|
@@ -0,0 +1,19 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDCTCCAfGgAwIBAgIJAOgD6vQm36guMA0GCSqGSIb3DQEBBQUAMBsxGTAXBgNV
|
3
|
+
BAMMEGFwaS5ydWJ5Z2Vtcy5vcmcwHhcNMTUwOTMwMTM0MTQ0WhcNMTgwNjI2MTM0
|
4
|
+
MTQ0WjAbMRkwFwYDVQQDDBBhcGkucnVieWdlbXMub3JnMIIBIjANBgkqhkiG9w0B
|
5
|
+
AQEFAAOCAQ8AMIIBCgKCAQEAyLIX5wODf0a4xLLFgJk7XjZ4alx8wOvqIpeKMc6J
|
6
|
+
yKfRp9rJWtHa578KYnLFfKZMSomHiIz1OdA+MWkOL6pt8kjJ8ciYahJ4q0gRx8+k
|
7
|
+
8bwXWBheh2gwm4jPubX+yBvXNn1Jc1+szQyJfVR7Yt1CNDTUnYuoL9K1qCxhaSiK
|
8
|
+
bVTu0IylVfeu83frV+hrr9IAveWZs6pD0Rm1j/V0cEbnaqWvppnHoO1HCh94rJRz
|
9
|
+
42iMVBIUeImwpmP2Eh6Q/1EvERoSP4GK3h8fGgO21iRcaj4/idUmYYd73ih7KHWu
|
10
|
+
FmIdK37nnyEu45dhuAnKb/OUEGW9Z07EFgy8bWDlrEMd4wIDAQABo1AwTjAdBgNV
|
11
|
+
HQ4EFgQUgEX0+CP53wGNF3VaOfk2QOGj/A0wHwYDVR0jBBgwFoAUgEX0+CP53wGN
|
12
|
+
F3VaOfk2QOGj/A0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAkMvw
|
13
|
+
NjeKUX/5S5dhR2fl7rsmVYhXGhFUmT8R8LXMIYsbeLkukBElEH+RIsn+GHkhww0x
|
14
|
+
imQsYKfy+ACmLUQ22rCwCu2UOyQGc+14T/RCHHwDSEMkz1Rnggo5SfjZ7NXLPnrE
|
15
|
+
c7iPlryxt8sMQXgjqZB7BzAFQVQmeRT8UKWhWYJeGvppmiOeY6E2xPiw6udlIeXA
|
16
|
+
Qr1qMIPZYrQIZY7+BJvB/oW9LDOgQH+IZ8clCHRajzE62KnwGEN+BzdycaulRHEK
|
17
|
+
xggqcxq4AGQ/VfY6JEwqERC+Px0ZW3Oi2M/KilfbeXk1QTd47fWFFI1Zqa5LLVJt
|
18
|
+
A+Ujm0Psp39L70exAg==
|
19
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require "sinatra"
|
4
|
+
|
5
|
+
require_relative "../server/server_base.rb"
|
6
|
+
|
7
|
+
CERT_DIR = File.dirname(__FILE__)
|
8
|
+
|
9
|
+
configure_sinatra
|
10
|
+
|
11
|
+
get '/' do
|
12
|
+
"I'm the secure rubygems server\n"
|
13
|
+
end
|
14
|
+
|
15
|
+
post '/api/v1/gems' do
|
16
|
+
"Successfully registered gem: red_herring (0.0.2)"
|
17
|
+
end
|
18
|
+
|
19
|
+
get '/api/v1/versions/red_herring.json' do
|
20
|
+
json = <<EOT
|
21
|
+
[{
|
22
|
+
"authors": "Cornelius Schumacher",
|
23
|
+
"built_at": "2015-06-20T00:00:00.000Z",
|
24
|
+
"created_at": "2015-06-20T06:57:40.675Z",
|
25
|
+
"description": "This is a red herring.",
|
26
|
+
"downloads_count": 371,
|
27
|
+
"metadata": {},
|
28
|
+
"number": "0.0.1",
|
29
|
+
"summary": "Red herring",
|
30
|
+
"platform": "ruby",
|
31
|
+
"ruby_version": "\u003e= 0",
|
32
|
+
"prerelease": false,
|
33
|
+
"licenses": ["MIT"],
|
34
|
+
"requirements": [],
|
35
|
+
"sha": "8afa714a16b34800a3f4e7aefcab871cf98aca33d02758e9c9d658cbf4bd740b"
|
36
|
+
}]
|
37
|
+
EOT
|
38
|
+
json
|
39
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
FROM opensuse
|
2
|
+
MAINTAINER Cornelius Schumacher <schumacher@kde.org>
|
3
|
+
|
4
|
+
RUN zypper --non-interactive install ruby
|
5
|
+
RUN zypper --non-interactive install ruby-devel gcc-c++ make libopenssl-devel
|
6
|
+
RUN zypper --non-interactive install ca-certificates-mozilla
|
7
|
+
RUN zypper --non-interactive install curl
|
8
|
+
RUN gem install --no-document sinatra thin
|
9
|
+
|
10
|
+
COPY server_base.rb /httpotemkin/server/
|
11
|
+
COPY secure_server.rb /httpotemkin/server/
|
12
|
+
COPY server.rb /httpotemkin/server/
|
13
|
+
|
14
|
+
CMD /httpotemkin/server/server.rb -p 80
|
15
|
+
|
16
|
+
EXPOSE 80
|
17
|
+
EXPOSE 443
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require "thin"
|
4
|
+
|
5
|
+
class SecureServer < ::Thin::Backends::TcpServer
|
6
|
+
def initialize(host, port, options)
|
7
|
+
super(host, port)
|
8
|
+
@ssl = true
|
9
|
+
@ssl_options = options
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def configure_sinatra
|
14
|
+
configure do
|
15
|
+
set :environment, :production
|
16
|
+
set :bind, '0.0.0.0'
|
17
|
+
set :port, 443
|
18
|
+
set :server, "thin"
|
19
|
+
class << settings
|
20
|
+
def server_settings
|
21
|
+
{
|
22
|
+
:backend => SecureServer,
|
23
|
+
:private_key_file => CERT_DIR + "/server.key",
|
24
|
+
:cert_chain_file => CERT_DIR + "/server.crt",
|
25
|
+
:verify_peer => false
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
openssl x509 -in server.crt -text -noout
|
data/expected_output
ADDED
data/httpotemkin.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'httpotemkin'
|
3
|
+
s.version = '0.0.1'
|
4
|
+
s.license = 'MIT'
|
5
|
+
s.platform = Gem::Platform::RUBY
|
6
|
+
s.authors = ['Cornelius Schumacher']
|
7
|
+
s.email = ['schumacher@kde.org']
|
8
|
+
s.homepage = 'https://github.com/cornelius/httpotemkin'
|
9
|
+
s.summary = 'Mock HTTP services for system tests'
|
10
|
+
s.description = 'Httpotemkin provides tools to mock HTTP servers for system-level tests.'
|
11
|
+
s.required_rubygems_version = '>= 1.3.6'
|
12
|
+
s.rubyforge_project = 'httpotemkin'
|
13
|
+
|
14
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
15
|
+
s.add_development_dependency 'given_filesystem', '>= 0.1.2'
|
16
|
+
s.add_development_dependency 'cli_tester', '>= 0.0.2'
|
17
|
+
|
18
|
+
s.add_runtime_dependency 'terminal-table', '~> 1.5'
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.require_path = 'lib'
|
22
|
+
s.executables = ["httpotemkin"]
|
23
|
+
end
|
data/lib/client.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module Httpotemkin
|
2
|
+
class Client
|
3
|
+
def initialize(containers)
|
4
|
+
@containers = containers
|
5
|
+
end
|
6
|
+
|
7
|
+
def execute(cmd, working_directory: nil)
|
8
|
+
@out = @containers.exec_client(cmd, working_directory: working_directory)
|
9
|
+
end
|
10
|
+
|
11
|
+
def exit_code
|
12
|
+
0
|
13
|
+
end
|
14
|
+
|
15
|
+
def out
|
16
|
+
@out
|
17
|
+
end
|
18
|
+
|
19
|
+
def err
|
20
|
+
""
|
21
|
+
end
|
22
|
+
|
23
|
+
def inject_tarball(filename)
|
24
|
+
Cheetah.run(["cat", filename], ["docker", "cp", "-", "client:/"])
|
25
|
+
end
|
26
|
+
|
27
|
+
def install_gem_from_spec(specfile)
|
28
|
+
Dir.chdir(File.dirname(specfile)) do
|
29
|
+
out = Cheetah.run(["gem", "build", File.basename(specfile)],
|
30
|
+
stdout: :capture)
|
31
|
+
gemfile = out[/File: (.*)\n/, 1]
|
32
|
+
@containers.run_docker(["cp", gemfile, "client:/tmp"])
|
33
|
+
@containers.run_docker(["exec", "client", "gem", "install", "--local",
|
34
|
+
File.join("/tmp", gemfile)])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/containers.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
module Httpotemkin
|
2
|
+
class Containers
|
3
|
+
attr_accessor :out
|
4
|
+
attr_accessor :log_dir
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@servers = []
|
8
|
+
@host_names = {
|
9
|
+
"rubygems" => "rubygems.org",
|
10
|
+
"api.rubygems" => "api.rubygems.org",
|
11
|
+
"obs" => "api.opensuse.org",
|
12
|
+
"server" => "server"
|
13
|
+
}
|
14
|
+
@out = $stdout
|
15
|
+
@err = $stderr
|
16
|
+
@log_dir = ENV["HTTPOTEMKIN_LOGS_DIR"] || "logs"
|
17
|
+
end
|
18
|
+
|
19
|
+
def run_docker(args, options = {})
|
20
|
+
cmd = ["docker"] + args
|
21
|
+
@out.puts cmd.join(" ")
|
22
|
+
out = Cheetah.run(cmd, options.merge({stdout: :capture}))
|
23
|
+
@out.puts out if out && !out.empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
def host_name(name)
|
27
|
+
@host_names[name]
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_server(name)
|
31
|
+
@servers.push(name)
|
32
|
+
end
|
33
|
+
|
34
|
+
def print_status
|
35
|
+
running = `docker ps --format="{{.Names}}"`.split("\n")
|
36
|
+
table = Terminal::Table.new(headings: ["Container", "Status"]) do |t|
|
37
|
+
status.each do |container, status|
|
38
|
+
t.add_row([container, status ? "up" : "down"])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
@out.puts table
|
43
|
+
end
|
44
|
+
|
45
|
+
def status
|
46
|
+
@status ||= {}
|
47
|
+
if @status.empty?
|
48
|
+
running = `docker ps --format="{{.Names}}"`.split("\n")
|
49
|
+
@servers.each do |server|
|
50
|
+
@status[server] = running.include?(server)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
@status
|
54
|
+
end
|
55
|
+
|
56
|
+
def up
|
57
|
+
@servers.each do |server|
|
58
|
+
if status[server]
|
59
|
+
@out.puts "'#{server}' already up"
|
60
|
+
else
|
61
|
+
run_docker(["run", "--name=#{server}", "-d", server])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def down(save_logs: false)
|
67
|
+
@servers.each do |server|
|
68
|
+
if (save_logs)
|
69
|
+
log_file = File.open(File.join(@log_dir,"#{server}.log"), "w")
|
70
|
+
run_docker(["logs", server], stderr: log_file)
|
71
|
+
end
|
72
|
+
if status[server]
|
73
|
+
run_docker(["rm", "-f", server])
|
74
|
+
else
|
75
|
+
@out.puts "'#{server}' already down"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def start_client
|
81
|
+
if links.empty?
|
82
|
+
run_docker(["run", "--name=client", "-d", "client", "sleep", "infinity"])
|
83
|
+
else
|
84
|
+
run_docker(["run"] + links + ["--name=client", "-d", "client", "sleep", "infinity"])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def stop_client
|
89
|
+
run_docker(["rm", "-f", "client"])
|
90
|
+
end
|
91
|
+
|
92
|
+
def exec_client(client_cmd, working_directory: nil)
|
93
|
+
cmd = ["docker", "exec", "client"]
|
94
|
+
if working_directory
|
95
|
+
cmd += ["sh", "-c", "cd #{working_directory}; #{client_cmd.join(' ')}"]
|
96
|
+
else
|
97
|
+
cmd += client_cmd
|
98
|
+
end
|
99
|
+
@out.puts cmd.join(" ")
|
100
|
+
Cheetah.run(cmd, stdout: :capture)
|
101
|
+
end
|
102
|
+
|
103
|
+
def links
|
104
|
+
@servers.map { |name| "--link=#{name}:#{host_name(name)}" }
|
105
|
+
end
|
106
|
+
|
107
|
+
def run_client
|
108
|
+
cmd = "docker run #{links.join(" ")} client"
|
109
|
+
@out.puts cmd
|
110
|
+
`#{cmd}`
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/lib/httpotemkin.rb
ADDED
data/lib/test.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Httpotemkin
|
2
|
+
class Test
|
3
|
+
def initialize(out: $stdout)
|
4
|
+
@containers = Containers.new
|
5
|
+
@containers.out = out
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_server(name)
|
9
|
+
@containers.add_server(name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
@containers.up
|
14
|
+
@containers.start_client
|
15
|
+
begin
|
16
|
+
yield Client.new(@containers)
|
17
|
+
ensure
|
18
|
+
@containers.stop_client
|
19
|
+
@containers.down
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/logs/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.log
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
include CliTester
|
4
|
+
include GivenFilesystemSpecHelpers
|
5
|
+
|
6
|
+
describe "command line interface" do
|
7
|
+
describe "help" do
|
8
|
+
it "shows help" do
|
9
|
+
expected_output = <<-EOT
|
10
|
+
Usage: httpotemkin <command>
|
11
|
+
|
12
|
+
Testing with HTTP mocks based on containers
|
13
|
+
|
14
|
+
Commands:
|
15
|
+
status - Show status of containers
|
16
|
+
up - Start containers
|
17
|
+
down - Stop containters
|
18
|
+
client - Run client
|
19
|
+
run - Run tests
|
20
|
+
help - Show command line help
|
21
|
+
EOT
|
22
|
+
expect(run_command(args: ["help"])).to exit_with_success(expected_output)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "status" do
|
27
|
+
it "shows status" do
|
28
|
+
with_stubbed_binary("bin/status/docker") do
|
29
|
+
expected_output = <<-EOT
|
30
|
+
+--------------+--------+
|
31
|
+
| Container | Status |
|
32
|
+
+--------------+--------+
|
33
|
+
| rubygems | up |
|
34
|
+
| api.rubygems | down |
|
35
|
+
| obs | up |
|
36
|
+
+--------------+--------+
|
37
|
+
EOT
|
38
|
+
expect(run_command(args: ["status"])).to exit_with_success(expected_output)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "up" do
|
44
|
+
it "starts containers" do
|
45
|
+
with_stubbed_binary("bin/up/docker") do
|
46
|
+
expected_output = <<-EOT
|
47
|
+
Starting server containers
|
48
|
+
'rubygems' already up
|
49
|
+
'api.rubygems' already up
|
50
|
+
docker run --name=obs -d obs
|
51
|
+
123
|
52
|
+
EOT
|
53
|
+
expect(run_command(args: ["up"])).to exit_with_success(expected_output)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "down" do
|
59
|
+
it "stops containers" do
|
60
|
+
with_stubbed_binary("bin/down/docker") do
|
61
|
+
expected_output = <<-EOT
|
62
|
+
Stopping server containers
|
63
|
+
docker rm -f rubygems
|
64
|
+
rubygems
|
65
|
+
docker rm -f api.rubygems
|
66
|
+
api.rubygems
|
67
|
+
'obs' already down
|
68
|
+
EOT
|
69
|
+
expect(run_command(args: ["down"])).to exit_with_success(expected_output)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "client" do
|
75
|
+
it "runs client" do
|
76
|
+
with_stubbed_binary("bin/client/docker") do
|
77
|
+
expected_output = <<-EOT
|
78
|
+
docker run --link=rubygems:rubygems.org --link=api.rubygems:api.rubygems.org --link=obs:api.opensuse.org client
|
79
|
+
Hopss
|
80
|
+
Hopss
|
81
|
+
Hopss
|
82
|
+
EOT
|
83
|
+
expect(run_command(args: ["client"])).to exit_with_success(expected_output)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "run" do
|
89
|
+
use_given_filesystem
|
90
|
+
|
91
|
+
before(:each) do
|
92
|
+
@logs_dir = given_directory
|
93
|
+
ENV["HTTPOTEMKIN_LOGS_DIR"] = @logs_dir
|
94
|
+
end
|
95
|
+
|
96
|
+
it "runs test and succeeds" do
|
97
|
+
with_stubbed_binary("bin/run.success/docker") do
|
98
|
+
expected_output = <<-EOT
|
99
|
+
Running tests
|
100
|
+
docker run --name=rubygems -d rubygems
|
101
|
+
docker run --name=api.rubygems -d api.rubygems
|
102
|
+
docker run --name=obs -d obs
|
103
|
+
docker run --link=rubygems:rubygems.org --link=api.rubygems:api.rubygems.org --link=obs:api.opensuse.org client
|
104
|
+
--- Start Test ---
|
105
|
+
Hopss
|
106
|
+
Hopss
|
107
|
+
Hopss
|
108
|
+
---- End Test ----
|
109
|
+
docker logs rubygems
|
110
|
+
'rubygems' already down
|
111
|
+
docker logs api.rubygems
|
112
|
+
'api.rubygems' already down
|
113
|
+
docker logs obs
|
114
|
+
'obs' already down
|
115
|
+
|
116
|
+
Success.
|
117
|
+
EOT
|
118
|
+
expect(run_command(args: ["run"])).to exit_with_success(expected_output)
|
119
|
+
expect(File.exist?(File.join(@logs_dir, "rubygems.log"))).to be(true)
|
120
|
+
expect(File.exist?(File.join(@logs_dir, "api.rubygems.log"))).to be(true)
|
121
|
+
expect(File.exist?(File.join(@logs_dir, "obs.log"))).to be(true)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
it "runs test and fails" do
|
126
|
+
with_stubbed_binary("bin/run.failure/docker") do
|
127
|
+
expected_output = <<-EOT
|
128
|
+
Running tests
|
129
|
+
docker run --name=rubygems -d rubygems
|
130
|
+
docker run --name=api.rubygems -d api.rubygems
|
131
|
+
docker run --name=obs -d obs
|
132
|
+
docker run --link=rubygems:rubygems.org --link=api.rubygems:api.rubygems.org --link=obs:api.opensuse.org client
|
133
|
+
--- Start Test ---
|
134
|
+
error
|
135
|
+
---- End Test ----
|
136
|
+
docker logs rubygems
|
137
|
+
'rubygems' already down
|
138
|
+
docker logs api.rubygems
|
139
|
+
'api.rubygems' already down
|
140
|
+
docker logs obs
|
141
|
+
'obs' already down
|
142
|
+
|
143
|
+
Failed.
|
144
|
+
|
145
|
+
Expected output:
|
146
|
+
Hopss
|
147
|
+
Hopss
|
148
|
+
Hopss
|
149
|
+
|
150
|
+
Actual output:
|
151
|
+
error
|
152
|
+
EOT
|
153
|
+
expect(run_command(args: ["run"])).to exit_with_error(1, "", expected_output)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "cli_tester"
|
2
|
+
require "given_filesystem/spec_helpers"
|
3
|
+
|
4
|
+
ENV["RUN_BY_RSPEC"] = "1"
|
5
|
+
|
6
|
+
def with_stubbed_binary(bin_path)
|
7
|
+
saved_path = ENV["PATH"]
|
8
|
+
|
9
|
+
full_path = File.expand_path("../", __FILE__)
|
10
|
+
full_path = File.join(full_path, bin_path)
|
11
|
+
if !File.exist?(full_path)
|
12
|
+
STDERR.puts "Error: stubbing binary #{full_path} does not exist"
|
13
|
+
else
|
14
|
+
path = File.dirname(full_path)
|
15
|
+
ENV["PATH"] = path + ":" + ENV["PATH"]
|
16
|
+
end
|
17
|
+
|
18
|
+
yield
|
19
|
+
|
20
|
+
ENV["PATH"] = saved_path
|
21
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
include GivenFilesystemSpecHelpers
|
4
|
+
|
5
|
+
describe "client" do
|
6
|
+
use_given_filesystem(keep_files: true)
|
7
|
+
|
8
|
+
it "injects tarballs" do
|
9
|
+
out = double
|
10
|
+
allow(out).to receive(:puts)
|
11
|
+
|
12
|
+
test = Httpotemkin::Test.new(out: out)
|
13
|
+
|
14
|
+
test.run do |client|
|
15
|
+
client.inject_tarball("spec/system/data/test.tar.gz")
|
16
|
+
|
17
|
+
client.execute(["ls", "test"])
|
18
|
+
|
19
|
+
expect(client.exit_code).to eq(0)
|
20
|
+
expect(client.out).to eq("mydir\nmyfile\n")
|
21
|
+
expect(client.err.empty?).to be(true)
|
22
|
+
|
23
|
+
client.execute(["ls", "test/mydir"])
|
24
|
+
|
25
|
+
expect(client.exit_code).to eq(0)
|
26
|
+
expect(client.out).to eq("myotherfile1\nmyotherfile2\n")
|
27
|
+
expect(client.err.empty?).to be(true)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "installs gem from spec" do
|
32
|
+
out = double
|
33
|
+
allow(out).to receive(:puts)
|
34
|
+
|
35
|
+
test = Httpotemkin::Test.new(out: out)
|
36
|
+
|
37
|
+
test.run do |client|
|
38
|
+
gem_tarball = File.expand_path("../data/red_herring-000.tar.gz", __FILE__)
|
39
|
+
|
40
|
+
gem_dir = given_directory
|
41
|
+
Dir.chdir(gem_dir) do
|
42
|
+
Cheetah.run(["tar", "xzf", gem_tarball])
|
43
|
+
end
|
44
|
+
|
45
|
+
client.install_gem_from_spec(File.join(gem_dir, "red_herring",
|
46
|
+
"red_herring.gemspec"))
|
47
|
+
|
48
|
+
client.execute(["gem", "list", "red_herring"])
|
49
|
+
|
50
|
+
expect(client.exit_code).to eq(0)
|
51
|
+
expect(client.out).to match("red_herring (0.0.1)\n")
|
52
|
+
expect(client.err.empty?).to be(true)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it "executes command in custom working directory" do
|
57
|
+
out = double
|
58
|
+
allow(out).to receive(:puts)
|
59
|
+
|
60
|
+
test = Httpotemkin::Test.new(out: out)
|
61
|
+
|
62
|
+
test.run do |client|
|
63
|
+
client.execute(["ls"], working_directory: "/srv")
|
64
|
+
|
65
|
+
expect(client.exit_code).to eq(0)
|
66
|
+
expect(client.out).to eq("ftp\nwww\n")
|
67
|
+
expect(client.err.empty?).to be(true)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe "hello world" do
|
4
|
+
it "answers" do
|
5
|
+
out = double
|
6
|
+
allow(out).to receive(:puts)
|
7
|
+
|
8
|
+
test = Httpotemkin::Test.new(out: out)
|
9
|
+
test.add_server("server")
|
10
|
+
|
11
|
+
test.run do |client|
|
12
|
+
sleep 2
|
13
|
+
client.execute(["curl", "server/hello"])
|
14
|
+
|
15
|
+
expect(client.exit_code).to eq(0)
|
16
|
+
expect(client.out).to eq("world\n")
|
17
|
+
expect(client.err.empty?).to be(true)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: httpotemkin
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cornelius Schumacher
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-12-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: given_filesystem
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.1.2
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.1.2
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: cli_tester
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.0.2
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.0.2
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: terminal-table
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.5'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.5'
|
69
|
+
description: Httpotemkin provides tools to mock HTTP servers for system-level tests.
|
70
|
+
email:
|
71
|
+
- schumacher@kde.org
|
72
|
+
executables:
|
73
|
+
- httpotemkin
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- ".rspec"
|
79
|
+
- CONTRIBUTING.md
|
80
|
+
- Gemfile
|
81
|
+
- MIT-LICENSE
|
82
|
+
- README.md
|
83
|
+
- TODO
|
84
|
+
- bin/httpotemkin
|
85
|
+
- containers/api.rubygems/Dockerfile
|
86
|
+
- containers/api.rubygems/latest_specs.4.8.gz
|
87
|
+
- containers/api.rubygems/rubygems-update-2.5.0.gemspec.rz
|
88
|
+
- containers/api.rubygems/secure_server.rb
|
89
|
+
- containers/build_containers
|
90
|
+
- containers/client/Dockerfile
|
91
|
+
- containers/client/certificates/.gitignore
|
92
|
+
- containers/client/oscrc
|
93
|
+
- containers/client/run_test.rb
|
94
|
+
- containers/client/server.crt
|
95
|
+
- containers/obs/Dockerfile
|
96
|
+
- containers/obs/secure_server.rb
|
97
|
+
- containers/rubygems/Dockerfile
|
98
|
+
- containers/rubygems/secure_server.rb
|
99
|
+
- containers/server/Dockerfile
|
100
|
+
- containers/server/secure_server.rb
|
101
|
+
- containers/server/server.rb
|
102
|
+
- containers/server/server_base.rb
|
103
|
+
- containers/show_certificate.sh
|
104
|
+
- expected_output
|
105
|
+
- httpotemkin.gemspec
|
106
|
+
- lib/client.rb
|
107
|
+
- lib/containers.rb
|
108
|
+
- lib/httpotemkin.rb
|
109
|
+
- lib/test.rb
|
110
|
+
- logs/.gitignore
|
111
|
+
- spec/integration/bin/client/docker
|
112
|
+
- spec/integration/bin/down/docker
|
113
|
+
- spec/integration/bin/run.failure/docker
|
114
|
+
- spec/integration/bin/run.success/docker
|
115
|
+
- spec/integration/bin/status/docker
|
116
|
+
- spec/integration/bin/up/docker
|
117
|
+
- spec/integration/cli_spec.rb
|
118
|
+
- spec/integration/spec_helper.rb
|
119
|
+
- spec/system/client_spec.rb
|
120
|
+
- spec/system/data/red_herring-000.tar.gz
|
121
|
+
- spec/system/data/test.tar.gz
|
122
|
+
- spec/system/hello_world_spec.rb
|
123
|
+
- spec/system/spec_helper.rb
|
124
|
+
homepage: https://github.com/cornelius/httpotemkin
|
125
|
+
licenses:
|
126
|
+
- MIT
|
127
|
+
metadata: {}
|
128
|
+
post_install_message:
|
129
|
+
rdoc_options: []
|
130
|
+
require_paths:
|
131
|
+
- lib
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: 1.3.6
|
142
|
+
requirements: []
|
143
|
+
rubyforge_project: httpotemkin
|
144
|
+
rubygems_version: 2.2.2
|
145
|
+
signing_key:
|
146
|
+
specification_version: 4
|
147
|
+
summary: Mock HTTP services for system tests
|
148
|
+
test_files: []
|
149
|
+
has_rdoc:
|