@@ -420,6 +420,108 @@ def write_spec(spec) # :nodoc:
420420 end
421421end
422422
423+ ##
424+ # Minimal CompactIndex implementation for tests.
425+ # This is a simplified version that only implements what's needed for test fixtures.
426+ module CompactIndexBuilder
427+ # Generates the /info/{gem_name} response body
428+ # Format: ---\nVERSION DEPS|METADATA\n
429+ # Where DEPS is: dep_name:requirement,dep_name:requirement
430+ # And METADATA is: checksum:SHA256,ruby:requirement,rubygems:requirement
431+ def self . info ( versions )
432+ lines = [ "---" ]
433+ versions . each do |version |
434+ parts = [ version . version ]
435+
436+ # Add dependencies (if any)
437+ deps = version . dependencies . map { |d | "#{ d . name } :#{ d . requirement } " }
438+ deps_string = deps . join ( "," )
439+
440+ # Build metadata
441+ metadata = [ ]
442+ metadata << "checksum:#{ version . checksum } " if version . checksum
443+ metadata << "ruby:#{ version . ruby_version } " if version . ruby_version && version . ruby_version != ">= 0"
444+ metadata << "rubygems:#{ version . rubygems_version } " if version . rubygems_version && version . rubygems_version != ">= 0"
445+
446+ # Format: "VERSION DEPS|METADATA" or "VERSION |METADATA" (space before | only when no deps)
447+ line = "#{ version . version } #{ deps_string } |" + metadata . join ( "," )
448+ lines << line
449+ end
450+ lines . join ( "\n " ) << "\n "
451+ end
452+
453+ GemVersion = Data . define ( :version , :platform , :checksum , :info_checksum , :dependencies , :ruby_version , :rubygems_version ) do
454+ def initialize ( version :, platform :, checksum :, info_checksum : nil , dependencies : [ ] , ruby_version : nil , rubygems_version : nil )
455+ super ( version :, platform :, checksum :, info_checksum :, dependencies :, ruby_version :, rubygems_version :)
456+ end
457+ end
458+
459+ Dependency = Data . define ( :name , :requirement )
460+ end
461+
462+ ##
463+ # The CompactIndexSetup allows easy setup of compact index endpoints in tests.
464+ # Unlike SpecFetcherSetup, this only sets up compact index (no marshal API).
465+ #
466+ # compact_index do |ci|
467+ # ci.gem "a", 1 do |s|
468+ # s.add_dependency "b", "~> 2.0"
469+ # end
470+ # ci.gem "b", 2
471+ # end
472+
473+ class Gem ::TestCase ::CompactIndexSetup
474+ attr_reader :specs
475+
476+ def initialize ( test , repository )
477+ @test = test
478+ @repository = repository
479+ @test . fetcher = Gem ::FakeFetcher . new
480+ Gem ::RemoteFetcher . fetcher = @test . fetcher
481+ @specs = { }
482+ end
483+
484+ def gem ( name , version , dependencies = nil , &block )
485+ spec = @test . util_spec ( name , version , dependencies , &block )
486+ @specs [ spec . full_name ] = spec
487+ end
488+
489+ def stub
490+ @specs . values . group_by ( &:name ) . each do |name , gem_specs |
491+ versions = gem_specs . map do |spec |
492+ gem_file = Gem ::Package . build ( spec )
493+ gem_contents = Gem . read_binary ( gem_file )
494+ FileUtils . cp gem_file , spec . cache_file
495+
496+ @test . fetcher . data [ "#{ @repository } gems/#{ spec . file_name } " ] = gem_contents
497+
498+ dependencies = spec . dependencies . select ( &:runtime? ) . map do |dep |
499+ CompactIndexBuilder ::Dependency . new ( dep . name , dep . requirement . to_s )
500+ end
501+
502+ checksum = Digest ::SHA256 . hexdigest ( gem_contents )
503+
504+ CompactIndexBuilder ::GemVersion . new (
505+ spec . version . to_s ,
506+ spec . platform . to_s ,
507+ checksum ,
508+ nil ,
509+ dependencies ,
510+ spec . required_ruby_version . to_s ,
511+ spec . required_rubygems_version . to_s
512+ )
513+ end
514+
515+ @test . fetcher . data [ "#{ @repository } info/#{ name } " ] = CompactIndexBuilder . info ( versions )
516+ end
517+
518+ # stub only to pass Compact Index API presence check currently
519+ versions_response = Gem ::Net ::HTTPResponse . new "1.1" , 200 , "OK"
520+ versions_response . uri = Gem ::URI ( "#{ @repository } versions" )
521+ @test . fetcher . data [ "#{ @repository } versions" ] = versions_response
522+ end
523+ end
524+
423525##
424526# A StringIO duck-typed class that uses Tempfile instead of String as the
425527# backing store.
0 commit comments