httpotemkin 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: