httpotemkin 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +2 -0
  4. data/CONTRIBUTING.md +1 -0
  5. data/Gemfile +3 -0
  6. data/MIT-LICENSE +19 -0
  7. data/README.md +25 -0
  8. data/TODO +5 -0
  9. data/bin/httpotemkin +69 -0
  10. data/containers/api.rubygems/Dockerfile +12 -0
  11. data/containers/api.rubygems/latest_specs.4.8.gz +0 -0
  12. data/containers/api.rubygems/rubygems-update-2.5.0.gemspec.rz +0 -0
  13. data/containers/api.rubygems/secure_server.rb +21 -0
  14. data/containers/build_containers +35 -0
  15. data/containers/client/Dockerfile +30 -0
  16. data/containers/client/certificates/.gitignore +1 -0
  17. data/containers/client/oscrc +7 -0
  18. data/containers/client/run_test.rb +5 -0
  19. data/containers/client/server.crt +19 -0
  20. data/containers/obs/Dockerfile +10 -0
  21. data/containers/obs/secure_server.rb +13 -0
  22. data/containers/rubygems/Dockerfile +10 -0
  23. data/containers/rubygems/secure_server.rb +39 -0
  24. data/containers/server/Dockerfile +17 -0
  25. data/containers/server/secure_server.rb +13 -0
  26. data/containers/server/server.rb +17 -0
  27. data/containers/server/server_base.rb +30 -0
  28. data/containers/show_certificate.sh +1 -0
  29. data/expected_output +3 -0
  30. data/httpotemkin.gemspec +23 -0
  31. data/lib/client.rb +38 -0
  32. data/lib/containers.rb +113 -0
  33. data/lib/httpotemkin.rb +5 -0
  34. data/lib/test.rb +23 -0
  35. data/logs/.gitignore +1 -0
  36. data/spec/integration/bin/client/docker +5 -0
  37. data/spec/integration/bin/down/docker +8 -0
  38. data/spec/integration/bin/run.failure/docker +5 -0
  39. data/spec/integration/bin/run.success/docker +7 -0
  40. data/spec/integration/bin/status/docker +4 -0
  41. data/spec/integration/bin/up/docker +8 -0
  42. data/spec/integration/cli_spec.rb +157 -0
  43. data/spec/integration/spec_helper.rb +21 -0
  44. data/spec/system/client_spec.rb +70 -0
  45. data/spec/system/data/red_herring-000.tar.gz +0 -0
  46. data/spec/system/data/test.tar.gz +0 -0
  47. data/spec/system/hello_world_spec.rb +20 -0
  48. data/spec/system/spec_helper.rb +3 -0
  49. 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
@@ -0,0 +1,4 @@
1
+ server.crt
2
+ server.key
3
+ *.gem
4
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ -fd
data/CONTRIBUTING.md ADDED
@@ -0,0 +1 @@
1
+ Contributions are welcome. Let them flow through GitHub.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
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
@@ -0,0 +1,5 @@
1
+ # To do
2
+
3
+ * Implement what's needed for testing of yes_ship_it
4
+
5
+ * Don't recreate certificates when they are already there
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
@@ -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,7 @@
1
+ [general]
2
+
3
+ apiurl = https://api.opensuse.org
4
+
5
+ [https://api.opensuse.org]
6
+ user = myuser
7
+ pass = mypassword
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+
3
+ curl https://rubygems.org -s
4
+ curl https://api.rubygems.org -s
5
+ curl https://api.opensuse.org -s
@@ -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,10 @@
1
+ FROM server
2
+ MAINTAINER Cornelius Schumacher <schumacher@kde.org>
3
+
4
+ COPY secure_server.rb /httpotemkin/obs/
5
+ COPY server.crt /httpotemkin/obs/
6
+ COPY server.key /httpotemkin/obs/
7
+
8
+ CMD /httpotemkin/obs/secure_server.rb
9
+
10
+ EXPOSE 443
@@ -0,0 +1,13 @@
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 OBS server\n"
13
+ end
@@ -0,0 +1,10 @@
1
+ FROM server
2
+ MAINTAINER Cornelius Schumacher <schumacher@kde.org>
3
+
4
+ COPY secure_server.rb /httpotemkin/rubygems/
5
+ COPY server.crt /httpotemkin/rubygems/
6
+ COPY server.key /httpotemkin/rubygems/
7
+
8
+ CMD /httpotemkin/rubygems/secure_server.rb
9
+
10
+ EXPOSE 443
@@ -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,13 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require "sinatra"
4
+
5
+ require_relative "server_base.rb"
6
+
7
+ CERT_DIR = File.dirname(__FILE__)
8
+
9
+ configure_sinatra
10
+
11
+ get '/' do
12
+ "I'm the secure base server\n"
13
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require "sinatra"
4
+
5
+ set :bind, "0.0.0.0"
6
+
7
+ get '/' do
8
+ "Jups\n"
9
+ end
10
+
11
+ get "/hello" do
12
+ "world\n"
13
+ end
14
+
15
+ get "/*" do
16
+ "all the rest\n"
17
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ Hopss
2
+ Hopss
3
+ Hopss
@@ -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
@@ -0,0 +1,5 @@
1
+ require "cheetah"
2
+ require "terminal-table"
3
+ require_relative "containers"
4
+ require_relative "test"
5
+ require_relative "client"
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,5 @@
1
+ #!/usr/bin/ruby
2
+
3
+ 3.times do
4
+ puts "Hopss"
5
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/ruby
2
+
3
+ if ARGV[0] == "ps"
4
+ puts "rubygems"
5
+ puts "api.rubygems"
6
+ else
7
+ puts ARGV[2]
8
+ end
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/ruby
2
+
3
+ if ARGV.last == "client"
4
+ puts "error"
5
+ end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/ruby
2
+
3
+ if ARGV.last == "client"
4
+ 3.times do
5
+ puts "Hopss"
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/ruby
2
+
3
+ puts "rubygems"
4
+ puts "obs"
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/ruby
2
+
3
+ if ARGV[0] == "ps"
4
+ puts "rubygems"
5
+ puts "api.rubygems"
6
+ else
7
+ puts "123"
8
+ end
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ require_relative "../../lib/httpotemkin"
2
+
3
+ require "given_filesystem/spec_helpers"
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: