diff options
author | hsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-09-08 08:45:41 +0000 |
---|---|---|
committer | hsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-09-08 08:45:41 +0000 |
commit | 8598f8c2dc78c6d1ae87cb6ae19c34ba2cb29241 (patch) | |
tree | 0bbd28f684e745cb212761b7c74fe08668f85cc8 /spec/bundler/support/artifice | |
parent | f2e04b77aa8a363d7e36ce5a9cdb60714a537a3c (diff) | |
download | ruby-8598f8c2dc78c6d1ae87cb6ae19c34ba2cb29241.tar.gz |
Merge bundler to standard libraries.
rubygems 2.7.x depends bundler-1.15.x. This is preparation for
rubygems and bundler migration.
* lib/bundler.rb, lib/bundler/*: files of bundler-1.15.4
* spec/bundler/*: rspec examples of bundler-1.15.4. I applied patches.
* https://github.com/bundler/bundler/pull/6007
* Exclude not working examples on ruby repository.
* Fake ruby interpriter instead of installed ruby.
* Makefile.in: Added test task named `test-bundler`. This task is only
working macOS/linux yet. I'm going to support Windows environment later.
* tool/sync_default_gems.rb: Added sync task for bundler.
[Feature #12733][ruby-core:77172]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59779 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'spec/bundler/support/artifice')
35 files changed, 945 insertions, 0 deletions
diff --git a/spec/bundler/support/artifice/compact_index.rb b/spec/bundler/support/artifice/compact_index.rb new file mode 100644 index 0000000000..9111ed8211 --- /dev/null +++ b/spec/bundler/support/artifice/compact_index.rb @@ -0,0 +1,121 @@ +# frozen_string_literal: true +require File.expand_path("../endpoint", __FILE__) + +$LOAD_PATH.unshift Dir[base_system_gems.join("gems/compact_index*/lib")].first.to_s +require "compact_index" + +class CompactIndexAPI < Endpoint + helpers do + def load_spec(name, version, platform, gem_repo) + full_name = "#{name}-#{version}" + full_name += "-#{platform}" if platform != "ruby" + Marshal.load(Gem.inflate(File.open(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz")).read)) + end + + def etag_response + response_body = yield + checksum = Digest::MD5.hexdigest(response_body) + return if not_modified?(checksum) + headers "ETag" => quote(checksum) + headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60" + content_type "text/plain" + requested_range_for(response_body) + rescue => e + puts e + puts e.backtrace + raise + end + + def not_modified?(checksum) + etags = parse_etags(request.env["HTTP_IF_NONE_MATCH"]) + + return unless etags.include?(checksum) + headers "ETag" => quote(checksum) + status 304 + body "" + end + + def requested_range_for(response_body) + ranges = Rack::Utils.byte_ranges(env, response_body.bytesize) + + if ranges + status 206 + body ranges.map! {|range| slice_body(response_body, range) }.join + else + status 200 + body response_body + end + end + + def quote(string) + %("#{string}") + end + + def parse_etags(value) + value ? value.split(/, ?/).select {|s| s.sub!(/"(.*)"/, '\1') } : [] + end + + def slice_body(body, range) + if body.respond_to?(:byteslice) + body.byteslice(range) + else # pre-1.9.3 + body.unpack("@#{range.first}a#{range.end + 1}").first + end + end + + def gems(gem_repo = GEM_REPO) + @gems ||= {} + @gems[gem_repo] ||= begin + specs = Bundler::Deprecate.skip_during do + %w(specs.4.8 prerelease_specs.4.8).map do |filename| + Marshal.load(File.open(gem_repo.join(filename)).read).map do |name, version, platform| + load_spec(name, version, platform, gem_repo) + end + end.flatten + end + + specs.group_by(&:name).map do |name, versions| + gem_versions = versions.map do |spec| + deps = spec.dependencies.select {|d| d.type == :runtime }.map do |d| + reqs = d.requirement.requirements.map {|r| r.join(" ") }.join(", ") + CompactIndex::Dependency.new(d.name, reqs) + end + checksum = begin + Digest::SHA256.file("#{GEM_REPO}/gems/#{spec.original_name}.gem").base64digest + rescue + nil + end + CompactIndex::GemVersion.new(spec.version.version, spec.platform.to_s, checksum, nil, + deps, spec.required_ruby_version, spec.required_rubygems_version) + end + CompactIndex::Gem.new(name, gem_versions) + end + end + end + end + + get "/names" do + etag_response do + CompactIndex.names(gems.map(&:name)) + end + end + + get "/versions" do + etag_response do + file = tmp("versions.list") + file.delete if file.file? + file = CompactIndex::VersionsFile.new(file.to_s) + file.create(gems) + file.contents + end + end + + get "/info/:name" do + etag_response do + gem = gems.find {|g| g.name == params[:name] } + CompactIndex.info(gem ? gem.versions : []) + end + end +end + +Artifice.activate_with(CompactIndexAPI) diff --git a/spec/bundler/support/artifice/compact_index_api_missing.rb b/spec/bundler/support/artifice/compact_index_api_missing.rb new file mode 100644 index 0000000000..6d15b54b85 --- /dev/null +++ b/spec/bundler/support/artifice/compact_index_api_missing.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true +require File.expand_path("../compact_index", __FILE__) + +Artifice.deactivate + +class CompactIndexApiMissing < CompactIndexAPI + get "/fetch/actual/gem/:id" do + $stderr.puts params[:id] + if params[:id] == "rack-1.0.gemspec.rz" + halt 404 + else + File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}") + end + end +end + +Artifice.activate_with(CompactIndexApiMissing) diff --git a/spec/bundler/support/artifice/compact_index_basic_authentication.rb b/spec/bundler/support/artifice/compact_index_basic_authentication.rb new file mode 100644 index 0000000000..bffb5b9e2b --- /dev/null +++ b/spec/bundler/support/artifice/compact_index_basic_authentication.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true +require File.expand_path("../compact_index", __FILE__) + +Artifice.deactivate + +class CompactIndexBasicAuthentication < CompactIndexAPI + before do + unless env["HTTP_AUTHORIZATION"] + halt 401, "Authentication info not supplied" + end + end +end + +Artifice.activate_with(CompactIndexBasicAuthentication) diff --git a/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb b/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb new file mode 100644 index 0000000000..4ac328736c --- /dev/null +++ b/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true +require File.expand_path("../compact_index", __FILE__) + +Artifice.deactivate + +class CompactIndexChecksumMismatch < CompactIndexAPI + get "/versions" do + headers "ETag" => quote("123") + headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60" + content_type "text/plain" + body "" + end +end + +Artifice.activate_with(CompactIndexChecksumMismatch) diff --git a/spec/bundler/support/artifice/compact_index_concurrent_download.rb b/spec/bundler/support/artifice/compact_index_concurrent_download.rb new file mode 100644 index 0000000000..b788a852cf --- /dev/null +++ b/spec/bundler/support/artifice/compact_index_concurrent_download.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true +require File.expand_path("../compact_index", __FILE__) + +Artifice.deactivate + +class CompactIndexConcurrentDownload < CompactIndexAPI + get "/versions" do + versions = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index", + "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions") + + # Verify the original (empty) content hasn't been deleted, e.g. on a retry + File.read(versions) == "" || raise("Original file should be present and empty") + + # Verify this is only requested once for a partial download + env["HTTP_RANGE"] || raise("Missing Range header for expected partial download") + + # Overwrite the file in parallel, which should be then overwritten + # after a successful download to prevent corruption + File.open(versions, "w") {|f| f.puts "another process" } + + etag_response do + file = tmp("versions.list") + file.delete if file.file? + file = CompactIndex::VersionsFile.new(file.to_s) + file.create(gems) + file.contents + end + end +end + +Artifice.activate_with(CompactIndexConcurrentDownload) diff --git a/spec/bundler/support/artifice/compact_index_creds_diff_host.rb b/spec/bundler/support/artifice/compact_index_creds_diff_host.rb new file mode 100644 index 0000000000..0c417f0580 --- /dev/null +++ b/spec/bundler/support/artifice/compact_index_creds_diff_host.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true +require File.expand_path("../compact_index", __FILE__) + +Artifice.deactivate + +class CompactIndexCredsDiffHost < CompactIndexAPI + helpers do + def auth + @auth ||= Rack::Auth::Basic::Request.new(request.env) + end + + def authorized? + auth.provided? && auth.basic? && auth.credentials && auth.credentials == %w(user pass) + end + + def protected! + return if authorized? + response["WWW-Authenticate"] = %(Basic realm="Testing HTTP Auth") + throw(:halt, [401, "Not authorized\n"]) + end + end + + before do + protected! unless request.path_info.include?("/no/creds/") + end + + get "/gems/:id" do + redirect "http://diffhost.com/no/creds/#{params[:id]}" + end + + get "/no/creds/:id" do + if request.host.include?("diffhost") && !auth.provided? + File.read("#{gem_repo1}/gems/#{params[:id]}") + end + end +end + +Artifice.activate_with(CompactIndexCredsDiffHost) diff --git a/spec/bundler/support/artifice/compact_index_extra.rb b/spec/bundler/support/artifice/compact_index_extra.rb new file mode 100644 index 0000000000..8a87fc4343 --- /dev/null +++ b/spec/bundler/support/artifice/compact_index_extra.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true +require File.expand_path("../compact_index", __FILE__) + +Artifice.deactivate + +class CompactIndexExtra < CompactIndexAPI + get "/extra/versions" do + halt 404 + end + + get "/extra/api/v1/dependencies" do + halt 404 + end + + get "/extra/specs.4.8.gz" do + File.read("#{gem_repo2}/specs.4.8.gz") + end + + get "/extra/prerelease_specs.4.8.gz" do + File.read("#{gem_repo2}/prerelease_specs.4.8.gz") + end + + get "/extra/quick/Marshal.4.8/:id" do + redirect "/extra/fetch/actual/gem/#{params[:id]}" + end + + get "/extra/fetch/actual/gem/:id" do + File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}") + end + + get "/extra/gems/:id" do + File.read("#{gem_repo2}/gems/#{params[:id]}") + end +end + +Artifice.activate_with(CompactIndexExtra) diff --git a/spec/bundler/support/artifice/compact_index_extra_api.rb b/spec/bundler/support/artifice/compact_index_extra_api.rb new file mode 100644 index 0000000000..844a9ca9f2 --- /dev/null +++ b/spec/bundler/support/artifice/compact_index_extra_api.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true +require File.expand_path("../compact_index", __FILE__) + +Artifice.deactivate + +class CompactIndexExtraApi < CompactIndexAPI + get "/extra/names" do + etag_response do + CompactIndex.names(gems(gem_repo4).map(&:name)) + end + end + + get "/extra/versions" do + etag_response do + file = tmp("versions.list") + file.delete if file.file? + file = CompactIndex::VersionsFile.new(file.to_s) + file.create(gems(gem_repo4)) + file.contents + end + end + + get "/extra/info/:name" do + etag_response do + gem = gems(gem_repo4).find {|g| g.name == params[:name] } + CompactIndex.info(gem ? gem.versions : []) + end + end + + get "/extra/specs.4.8.gz" do + File.read("#{gem_repo4}/specs.4.8.gz") + end + + get "/extra/prerelease_specs.4.8.gz" do + File.read("#{gem_repo4}/prerelease_specs.4.8.gz") + end + + get "/extra/quick/Marshal.4.8/:id" do + redirect "/extra/fetch/actual/gem/#{params[:id]}" + end + + get "/extra/fetch/actual/gem/:id" do + File.read("#{gem_repo4}/quick/Marshal.4.8/#{params[:id]}") + end + + get "/extra/gems/:id" do + File.read("#{gem_repo4}/gems/#{params[:id]}") + end +end + +Artifice.activate_with(CompactIndexExtraApi) diff --git a/spec/bundler/support/artifice/compact_index_extra_missing.rb b/spec/bundler/support/artifice/compact_index_extra_missing.rb new file mode 100644 index 0000000000..2af5ce9c27 --- /dev/null +++ b/spec/bundler/support/artifice/compact_index_extra_missing.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true +require File.expand_path("../compact_index_extra", __FILE__) + +Artifice.deactivate + +class CompactIndexExtraMissing < CompactIndexExtra + get "/extra/fetch/actual/gem/:id" do + if params[:id] == "missing-1.0.gemspec.rz" + halt 404 + else + File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}") + end + end +end + +Artifice.activate_with(CompactIndexExtraMissing) diff --git a/spec/bundler/support/artifice/compact_index_forbidden.rb b/spec/bundler/support/artifice/compact_index_forbidden.rb new file mode 100644 index 0000000000..b25eea94e7 --- /dev/null +++ b/spec/bundler/support/artifice/compact_index_forbidden.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true +require File.expand_path("../compact_index", __FILE__) + +Artifice.deactivate + +class CompactIndexForbidden < CompactIndexAPI + get "/versions" do + halt 403 + end +end + +Artifice.activate_with(CompactIndexForbidden) diff --git a/spec/bundler/support/artifice/compact_index_host_redirect.rb b/spec/bundler/support/artifice/compact_index_host_redirect.rb new file mode 100644 index 0000000000..6c1ab2def6 --- /dev/null +++ b/spec/bundler/support/artifice/compact_index_host_redirect.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true +require File.expand_path("../compact_index", __FILE__) + +Artifice.deactivate + +class CompactIndexHostRedirect < CompactIndexAPI + get "/fetch/actual/gem/:id", :host_name => "localgemserver.test" do + redirect "http://bundler.localgemserver.test#{request.path_info}" + end + + get "/versions" do + status 404 + end + + get "/api/v1/dependencies" do + status 404 + end +end + +Artifice.activate_with(CompactIndexHostRedirect) diff --git a/spec/bundler/support/artifice/compact_index_partial_update.rb b/spec/bundler/support/artifice/compact_index_partial_update.rb new file mode 100644 index 0000000000..bf6feab877 --- /dev/null +++ b/spec/bundler/support/artifice/compact_index_partial_update.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true +require File.expand_path("../compact_index", __FILE__) + +Artifice.deactivate + +class CompactIndexPartialUpdate < CompactIndexAPI + # Stub the server to never return 304s. This simulates the behaviour of + # Fastly / Rubygems ignoring ETag headers. + def not_modified?(_checksum) + false + end + + get "/versions" do + cached_versions_path = File.join( + Bundler.rubygems.user_home, ".bundle", "cache", "compact_index", + "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions" + ) + + # Verify a cached copy of the versions file exists + unless File.read(cached_versions_path).start_with?("created_at: ") + raise("Cached versions file should be present and have content") + end + + # Verify that a partial request is made, starting from the index of the + # final byte of the cached file. + unless env["HTTP_RANGE"] == "bytes=#{File.read(cached_versions_path).bytesize - 1}-" + raise("Range header should be present, and start from the index of the final byte of the cache.") + end + + etag_response do + # Return the exact contents of the cache. + File.read(cached_versions_path) + end + end +end + +Artifice.activate_with(CompactIndexPartialUpdate) diff --git a/spec/bundler/support/artifice/compact_index_redirects.rb b/spec/bundler/support/artifice/compact_index_redirects.rb new file mode 100644 index 0000000000..ff1d3e43bc --- /dev/null +++ b/spec/bundler/support/artifice/compact_index_redirects.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true +require File.expand_path("../compact_index", __FILE__) + +Artifice.deactivate + +class CompactIndexRedirect < CompactIndexAPI + get "/fetch/actual/gem/:id" do + redirect "/fetch/actual/gem/#{params[:id]}" + end + + get "/versions" do + status 404 + end + + get "/api/v1/dependencies" do + status 404 + end +end + +Artifice.activate_with(CompactIndexRedirect) diff --git a/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb b/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb new file mode 100644 index 0000000000..49a072d2b9 --- /dev/null +++ b/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true +require File.expand_path("../compact_index", __FILE__) + +Artifice.deactivate + +class CompactIndexStrictBasicAuthentication < CompactIndexAPI + before do + unless env["HTTP_AUTHORIZATION"] + halt 401, "Authentication info not supplied" + end + + # Only accepts password == "password" + unless env["HTTP_AUTHORIZATION"] == "Basic dXNlcjpwYXNz" + halt 403, "Authentication failed" + end + end +end + +Artifice.activate_with(CompactIndexStrictBasicAuthentication) diff --git a/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb b/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb new file mode 100644 index 0000000000..25935f5e5d --- /dev/null +++ b/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true +require File.expand_path("../compact_index", __FILE__) + +Artifice.deactivate + +class CompactIndexWrongDependencies < CompactIndexAPI + get "/info/:name" do + etag_response do + gem = gems.find {|g| g.name == params[:name] } + gem.versions.each {|gv| gv.dependencies.clear } if gem + CompactIndex.info(gem ? gem.versions : []) + end + end +end + +Artifice.activate_with(CompactIndexWrongDependencies) diff --git a/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb b/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb new file mode 100644 index 0000000000..3a12a59ae7 --- /dev/null +++ b/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true +require File.expand_path("../compact_index", __FILE__) + +Artifice.deactivate + +class CompactIndexWrongGemChecksum < CompactIndexAPI + get "/info/:name" do + etag_response do + name = params[:name] + gem = gems.find {|g| g.name == name } + checksum = ENV.fetch("BUNDLER_SPEC_#{name.upcase}_CHECKSUM") { "ab" * 22 } + versions = gem ? gem.versions : [] + versions.each {|v| v.checksum = checksum } + CompactIndex.info(versions) + end + end +end + +Artifice.activate_with(CompactIndexWrongGemChecksum) diff --git a/spec/bundler/support/artifice/endopint_marshal_fail_basic_authentication.rb b/spec/bundler/support/artifice/endopint_marshal_fail_basic_authentication.rb new file mode 100644 index 0000000000..f1f8dc5700 --- /dev/null +++ b/spec/bundler/support/artifice/endopint_marshal_fail_basic_authentication.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true +require File.expand_path("../endpoint_marshal_fail", __FILE__) + +Artifice.deactivate + +class EndpointMarshalFailBasicAuthentication < EndpointMarshalFail + before do + unless env["HTTP_AUTHORIZATION"] + halt 401, "Authentication info not supplied" + end + end +end + +Artifice.activate_with(EndpointMarshalFailBasicAuthentication) diff --git a/spec/bundler/support/artifice/endpoint.rb b/spec/bundler/support/artifice/endpoint.rb new file mode 100644 index 0000000000..771d431f22 --- /dev/null +++ b/spec/bundler/support/artifice/endpoint.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true +require File.expand_path("../../path.rb", __FILE__) +require Spec::Path.root.join("lib/bundler/deprecate") +include Spec::Path + +$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gems.join("gems/{artifice,rack,tilt,sinatra}-*/lib")].map(&:to_s)) +require "artifice" +require "sinatra/base" + +class Endpoint < Sinatra::Base + GEM_REPO = Pathname.new(ENV["BUNDLER_SPEC_GEM_REPO"] || Spec::Path.gem_repo1) + set :raise_errors, true + set :show_exceptions, false + + helpers do + def dependencies_for(gem_names, gem_repo = GEM_REPO) + return [] if gem_names.nil? || gem_names.empty? + + require "rubygems" + require "bundler" + Bundler::Deprecate.skip_during do + all_specs = %w(specs.4.8 prerelease_specs.4.8).map do |filename| + Marshal.load(File.open(gem_repo.join(filename)).read) + end.inject(:+) + + all_specs.map do |name, version, platform| + spec = load_spec(name, version, platform, gem_repo) + next unless gem_names.include?(spec.name) + { + :name => spec.name, + :number => spec.version.version, + :platform => spec.platform.to_s, + :dependencies => spec.dependencies.select {|dep| dep.type == :runtime }.map do |dep| + [dep.name, dep.requirement.requirements.map {|a| a.join(" ") }.join(", ")] + end + } + end.compact + end + end + + def load_spec(name, version, platform, gem_repo) + full_name = "#{name}-#{version}" + full_name += "-#{platform}" if platform != "ruby" + Marshal.load(Gem.inflate(File.open(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz")).read)) + end + end + + get "/quick/Marshal.4.8/:id" do + redirect "/fetch/actual/gem/#{params[:id]}" + end + + get "/fetch/actual/gem/:id" do + File.read("#{GEM_REPO}/quick/Marshal.4.8/#{params[:id]}") + end + + get "/gems/:id" do + File.read("#{GEM_REPO}/gems/#{params[:id]}") + end + + get "/api/v1/dependencies" do + Marshal.dump(dependencies_for(params[:gems])) + end + + get "/specs.4.8.gz" do + File.read("#{GEM_REPO}/specs.4.8.gz") + end + + get "/prerelease_specs.4.8.gz" do + File.read("#{GEM_REPO}/prerelease_specs.4.8.gz") + end +end + +Artifice.activate_with(Endpoint) diff --git a/spec/bundler/support/artifice/endpoint_500.rb b/spec/bundler/support/artifice/endpoint_500.rb new file mode 100644 index 0000000000..993630b79e --- /dev/null +++ b/spec/bundler/support/artifice/endpoint_500.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true +require File.expand_path("../../path.rb", __FILE__) +include Spec::Path + +$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gems.join("gems/{artifice,rack,tilt,sinatra}-*/lib")].map(&:to_s)) + +require "artifice" +require "sinatra/base" + +Artifice.deactivate + +class Endpoint500 < Sinatra::Base + before do + halt 500 + end +end + +Artifice.activate_with(Endpoint500) diff --git a/spec/bundler/support/artifice/endpoint_api_forbidden.rb b/spec/bundler/support/artifice/endpoint_api_forbidden.rb new file mode 100644 index 0000000000..21ad9117ed --- /dev/null +++ b/spec/bundler/support/artifice/endpoint_api_forbidden.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true +require File.expand_path("../endpoint", __FILE__) + +Artifice.deactivate + +class EndpointApiForbidden < Endpoint + get "/api/v1/dependencies" do + halt 403 + end +end + +Artifice.activate_with(EndpointApiForbidden) diff --git a/spec/bundler/support/artifice/endpoint_api_missing.rb b/spec/bundler/support/artifice/endpoint_api_missing.rb new file mode 100644 index 0000000000..6f5b5f1323 --- /dev/null +++ b/spec/bundler/support/artifice/endpoint_api_missing.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true +require File.expand_path("../endpoint", __FILE__) + +Artifice.deactivate + +class EndpointApiMissing < Endpoint + get "/fetch/actual/gem/:id" do + $stderr.puts params[:id] + if params[:id] == "rack-1.0.gemspec.rz" + halt 404 + else + File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}") + end + end +end + +Artifice.activate_with(EndpointApiMissing) diff --git a/spec/bundler/support/artifice/endpoint_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_basic_authentication.rb new file mode 100644 index 0000000000..9fafd51a3d --- /dev/null +++ b/spec/bundler/support/artifice/endpoint_basic_authentication.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true +require File.expand_path("../endpoint", __FILE__) + +Artifice.deactivate + +class EndpointBasicAuthentication < Endpoint + before do + unless env["HTTP_AUTHORIZATION"] + halt 401, "Authentication info not supplied" + end + end +end + +Artifice.activate_with(EndpointBasicAuthentication) diff --git a/spec/bundler/support/artifice/endpoint_creds_diff_host.rb b/spec/bundler/support/artifice/endpoint_creds_diff_host.rb new file mode 100644 index 0000000000..cd152848fe --- /dev/null +++ b/spec/bundler/support/artifice/endpoint_creds_diff_host.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true +require File.expand_path("../endpoint", __FILE__) + +Artifice.deactivate + +class EndpointCredsDiffHost < Endpoint + helpers do + def auth + @auth ||= Rack::Auth::Basic::Request.new(request.env) + end + + def authorized? + auth.provided? && auth.basic? && auth.credentials && auth.credentials == %w(user pass) + end + + def protected! + return if authorized? + response["WWW-Authenticate"] = %(Basic realm="Testing HTTP Auth") + throw(:halt, [401, "Not authorized\n"]) + end + end + + before do + protected! unless request.path_info.include?("/no/creds/") + end + + get "/gems/:id" do + redirect "http://diffhost.com/no/creds/#{params[:id]}" + end + + get "/no/creds/:id" do + if request.host.include?("diffhost") && !auth.provided? + File.read("#{gem_repo1}/gems/#{params[:id]}") + end + end +end + +Artifice.activate_with(EndpointCredsDiffHost) diff --git a/spec/bundler/support/artifice/endpoint_extra.rb b/spec/bundler/support/artifice/endpoint_extra.rb new file mode 100644 index 0000000000..ed4e87e65f --- /dev/null +++ b/spec/bundler/support/artifice/endpoint_extra.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true +require File.expand_path("../endpoint", __FILE__) + +Artifice.deactivate + +class EndpointExtra < Endpoint + get "/extra/api/v1/dependencies" do + halt 404 + end + + get "/extra/specs.4.8.gz" do + File.read("#{gem_repo2}/specs.4.8.gz") + end + + get "/extra/prerelease_specs.4.8.gz" do + File.read("#{gem_repo2}/prerelease_specs.4.8.gz") + end + + get "/extra/quick/Marshal.4.8/:id" do + redirect "/extra/fetch/actual/gem/#{params[:id]}" + end + + get "/extra/fetch/actual/gem/:id" do + File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}") + end + + get "/extra/gems/:id" do + File.read("#{gem_repo2}/gems/#{params[:id]}") + end +end + +Artifice.activate_with(EndpointExtra) diff --git a/spec/bundler/support/artifice/endpoint_extra_api.rb b/spec/bundler/support/artifice/endpoint_extra_api.rb new file mode 100644 index 0000000000..1e9e1dc60d --- /dev/null +++ b/spec/bundler/support/artifice/endpoint_extra_api.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true +require File.expand_path("../endpoint", __FILE__) + +Artifice.deactivate + +class EndpointExtraApi < Endpoint + get "/extra/api/v1/dependencies" do + deps = dependencies_for(params[:gems], gem_repo4) + Marshal.dump(deps) + end + + get "/extra/specs.4.8.gz" do + File.read("#{gem_repo4}/specs.4.8.gz") + end + + get "/extra/prerelease_specs.4.8.gz" do + File.read("#{gem_repo4}/prerelease_specs.4.8.gz") + end + + get "/extra/quick/Marshal.4.8/:id" do + redirect "/extra/fetch/actual/gem/#{params[:id]}" + end + + get "/extra/fetch/actual/gem/:id" do + File.read("#{gem_repo4}/quick/Marshal.4.8/#{params[:id]}") + end + + get "/extra/gems/:id" do + File.read("#{gem_repo4}/gems/#{params[:id]}") + end +end + +Artifice.activate_with(EndpointExtraApi) diff --git a/spec/bundler/support/artifice/endpoint_extra_missing.rb b/spec/bundler/support/artifice/endpoint_extra_missing.rb new file mode 100644 index 0000000000..dc79705a26 --- /dev/null +++ b/spec/bundler/support/artifice/endpoint_extra_missing.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true +require File.expand_path("../endpoint_extra", __FILE__) + +Artifice.deactivate + +class EndpointExtraMissing < EndpointExtra + get "/extra/fetch/actual/gem/:id" do + if params[:id] == "missing-1.0.gemspec.rz" + halt 404 + else + File.read("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}") + end + end +end + +Artifice.activate_with(EndpointExtraMissing) diff --git a/spec/bundler/support/artifice/endpoint_fallback.rb b/spec/bundler/support/artifice/endpoint_fallback.rb new file mode 100644 index 0000000000..8a85a41784 --- /dev/null +++ b/spec/bundler/support/artifice/endpoint_fallback.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true +require File.expand_path("../endpoint", __FILE__) + +Artifice.deactivate + +class EndpointFallback < Endpoint + DEPENDENCY_LIMIT = 60 + + get "/api/v1/dependencies" do + if params[:gems] && params[:gems].size <= DEPENDENCY_LIMIT + Marshal.dump(dependencies_for(params[:gems])) + else + halt 413, "Too many gems to resolve, please request less than #{DEPENDENCY_LIMIT} gems" + end + end +end + +Artifice.activate_with(EndpointFallback) diff --git a/spec/bundler/support/artifice/endpoint_host_redirect.rb b/spec/bundler/support/artifice/endpoint_host_redirect.rb new file mode 100644 index 0000000000..250416d8cc --- /dev/null +++ b/spec/bundler/support/artifice/endpoint_host_redirect.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true +require File.expand_path("../endpoint", __FILE__) + +Artifice.deactivate + +class EndpointHostRedirect < Endpoint + get "/fetch/actual/gem/:id", :host_name => "localgemserver.test" do + redirect "http://bundler.localgemserver.test#{request.path_info}" + end + + get "/api/v1/dependencies" do + status 404 + end +end + +Artifice.activate_with(EndpointHostRedirect) diff --git a/spec/bundler/support/artifice/endpoint_marshal_fail.rb b/spec/bundler/support/artifice/endpoint_marshal_fail.rb new file mode 100644 index 0000000000..0fb75ebf31 --- /dev/null +++ b/spec/bundler/support/artifice/endpoint_marshal_fail.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true +require File.expand_path("../endpoint_fallback", __FILE__) + +Artifice.deactivate + +class EndpointMarshalFail < EndpointFallback + get "/api/v1/dependencies" do + "f0283y01hasf" + end +end + +Artifice.activate_with(EndpointMarshalFail) diff --git a/spec/bundler/support/artifice/endpoint_mirror_source.rb b/spec/bundler/support/artifice/endpoint_mirror_source.rb new file mode 100644 index 0000000000..9fb58ecb29 --- /dev/null +++ b/spec/bundler/support/artifice/endpoint_mirror_source.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true +require File.expand_path("../endpoint", __FILE__) + +class EndpointMirrorSource < Endpoint + get "/gems/:id" do + if request.env["HTTP_X_GEMFILE_SOURCE"] == "https://server.example.org/" + File.read("#{gem_repo1}/gems/#{params[:id]}") + else + halt 500 + end + end +end + +Artifice.activate_with(EndpointMirrorSource) diff --git a/spec/bundler/support/artifice/endpoint_redirect.rb b/spec/bundler/support/artifice/endpoint_redirect.rb new file mode 100644 index 0000000000..f80d7600c2 --- /dev/null +++ b/spec/bundler/support/artifice/endpoint_redirect.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true +require File.expand_path("../endpoint", __FILE__) + +Artifice.deactivate + +class EndpointRedirect < Endpoint + get "/fetch/actual/gem/:id" do + redirect "/fetch/actual/gem/#{params[:id]}" + end + + get "/api/v1/dependencies" do + status 404 + end +end + +Artifice.activate_with(EndpointRedirect) diff --git a/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb new file mode 100644 index 0000000000..4b32cbbf5b --- /dev/null +++ b/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true +require File.expand_path("../endpoint", __FILE__) + +Artifice.deactivate + +class EndpointStrictBasicAuthentication < Endpoint + before do + unless env["HTTP_AUTHORIZATION"] + halt 401, "Authentication info not supplied" + end + + # Only accepts password == "password" + unless env["HTTP_AUTHORIZATION"] == "Basic dXNlcjpwYXNz" + halt 403, "Authentication failed" + end + end +end + +Artifice.activate_with(EndpointStrictBasicAuthentication) diff --git a/spec/bundler/support/artifice/endpoint_timeout.rb b/spec/bundler/support/artifice/endpoint_timeout.rb new file mode 100644 index 0000000000..b15650f226 --- /dev/null +++ b/spec/bundler/support/artifice/endpoint_timeout.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true +require File.expand_path("../endpoint_fallback", __FILE__) + +Artifice.deactivate + +class EndpointTimeout < EndpointFallback + SLEEP_TIMEOUT = 15 + + get "/api/v1/dependencies" do + sleep(SLEEP_TIMEOUT) + end +end + +Artifice.activate_with(EndpointTimeout) diff --git a/spec/bundler/support/artifice/fail.rb b/spec/bundler/support/artifice/fail.rb new file mode 100644 index 0000000000..1059c6df4e --- /dev/null +++ b/spec/bundler/support/artifice/fail.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require "net/http" +begin + require "net/https" +rescue LoadError + nil # net/https or openssl +end + +# We can't use artifice here because it uses rack + +module Artifice; end # for < 2.0, Net::HTTP::Persistent::SSLReuse + +class Fail < Net::HTTP + # Net::HTTP uses a @newimpl instance variable to decide whether + # to use a legacy implementation. Since we are subclassing + # Net::HTTP, we must set it + @newimpl = true + + def request(req, body = nil, &block) + raise(exception(req)) + end + + # Ensure we don't start a connect here + def connect + end + + def exception(req) + name = ENV.fetch("BUNDLER_SPEC_EXCEPTION") { "Errno::ENETUNREACH" } + const = name.split("::").reduce(Object) {|mod, sym| mod.const_get(sym) } + const.new("host down: Bundler spec artifice fail! #{req["PATH_INFO"]}") + end +end + +# Replace Net::HTTP with our failing subclass +::Net.class_eval do + remove_const(:HTTP) + const_set(:HTTP, ::Fail) +end diff --git a/spec/bundler/support/artifice/windows.rb b/spec/bundler/support/artifice/windows.rb new file mode 100644 index 0000000000..c18ca454ec --- /dev/null +++ b/spec/bundler/support/artifice/windows.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true +require File.expand_path("../../path.rb", __FILE__) +include Spec::Path + +$LOAD_PATH.unshift Dir[base_system_gems.join("gems/artifice*/lib")].first.to_s +$LOAD_PATH.unshift(*Dir[base_system_gems.join("gems/rack-*/lib")]) +$LOAD_PATH.unshift Dir[base_system_gems.join("gems/tilt*/lib")].first.to_s +$LOAD_PATH.unshift Dir[base_system_gems.join("gems/sinatra*/lib")].first.to_s +require "artifice" +require "sinatra/base" + +Artifice.deactivate + +class Windows < Sinatra::Base + set :raise_errors, true + set :show_exceptions, false + + helpers do + def gem_repo + Pathname.new(ENV["BUNDLER_SPEC_GEM_REPO"] || Spec::Path.gem_repo1) + end + end + + files = ["specs.4.8.gz", + "prerelease_specs.4.8.gz", + "quick/Marshal.4.8/rcov-1.0-mswin32.gemspec.rz", + "gems/rcov-1.0-mswin32.gem"] + + files.each do |file| + get "/#{file}" do + File.read gem_repo.join(file) + end + end + + get "/gems/rcov-1.0-x86-mswin32.gem" do + halt 404 + end + + get "/api/v1/dependencies" do + halt 404 + end + + get "/versions" do + halt 500 + end +end + +Artifice.activate_with(Windows) |