format_parser 2.0.0.pre → 2.0.0.pre.2
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.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +2 -2
- data/CHANGELOG.md +1 -1
- data/format_parser.gemspec +1 -0
- data/lib/format_parser/version.rb +1 -1
- data/lib/remote_io.rb +6 -3
- data/spec/remote_io_spec.rb +227 -144
- data/spec/spec_helper.rb +4 -0
- metadata +16 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 01730fd580c14a2c35b87be1f62384b06f9b2683bd07ba01ed21d21a4211b1f5
         | 
| 4 | 
            +
              data.tar.gz: c27ba63f09c8251ccaf237040cb6f01c495686f6a025cdb7207aa0bfb2fe0098
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 427c1f3652e33592a7bd3a63c0db676a865390b1947732e452905d0fe64f54df82af2b9a339dfdf962d9586c73fe7ac8a2b19da099c502a187efd8215993ddbf
         | 
| 7 | 
            +
              data.tar.gz: 3652ca2c1ae42ddb5e29811dcae13e8bbb0fcf964c4f47547638e992d21612235bdf489d908b5c4bdf3aa2859768eb0dc98c6fc58527ce5031396ee341207354
         | 
    
        data/.github/workflows/main.yml
    CHANGED
    
    | @@ -8,7 +8,7 @@ env: | |
| 8 8 | 
             
            jobs:
         | 
| 9 9 | 
             
              lint:
         | 
| 10 10 | 
             
                name: Code Style
         | 
| 11 | 
            -
                runs-on: ubuntu- | 
| 11 | 
            +
                runs-on: ubuntu-latest
         | 
| 12 12 | 
             
                if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
         | 
| 13 13 | 
             
                strategy:
         | 
| 14 14 | 
             
                  matrix:
         | 
| @@ -54,7 +54,7 @@ jobs: | |
| 54 54 | 
             
                    run: bundle exec rubocop
         | 
| 55 55 | 
             
              test:
         | 
| 56 56 | 
             
                name: Specs
         | 
| 57 | 
            -
                runs-on: ubuntu- | 
| 57 | 
            +
                runs-on: ubuntu-latest
         | 
| 58 58 | 
             
                if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
         | 
| 59 59 | 
             
                strategy:
         | 
| 60 60 | 
             
                  matrix:
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    
    
        data/format_parser.gemspec
    CHANGED
    
    | @@ -39,6 +39,7 @@ Gem::Specification.new do |spec| | |
| 39 39 | 
             
              spec.add_development_dependency 'rake'
         | 
| 40 40 | 
             
              spec.add_development_dependency 'rspec'
         | 
| 41 41 | 
             
              spec.add_development_dependency 'simplecov'
         | 
| 42 | 
            +
              spec.add_development_dependency 'webmock'
         | 
| 42 43 | 
             
              spec.add_development_dependency 'wetransfer_style', '1.0.0'
         | 
| 43 44 | 
             
              spec.add_development_dependency 'yard'
         | 
| 44 45 | 
             
            end
         | 
    
        data/lib/remote_io.rb
    CHANGED
    
    | @@ -122,7 +122,7 @@ class FormatParser::RemoteIO | |
| 122 122 | 
             
                    error_message = [
         | 
| 123 123 | 
             
                      "We requested #{requested_range_size} bytes, but the server sent us more",
         | 
| 124 124 | 
             
                      "(#{response_size} bytes) - it likely has no `Range:` support.",
         | 
| 125 | 
            -
                      "The error occurred when talking to #{uri} | 
| 125 | 
            +
                      "The error occurred when talking to #{uri}"
         | 
| 126 126 | 
             
                    ]
         | 
| 127 127 | 
             
                    raise InvalidRequest.new(response.code, error_message.join("\n"))
         | 
| 128 128 | 
             
                  end
         | 
| @@ -141,10 +141,13 @@ class FormatParser::RemoteIO | |
| 141 141 | 
             
                  # to be 206
         | 
| 142 142 | 
             
                  [size, response.body]
         | 
| 143 143 | 
             
                when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther, Net::HTTPTemporaryRedirect, Net::HTTPPermanentRedirect
         | 
| 144 | 
            -
                  raise RedirectLimitReached | 
| 144 | 
            +
                  raise RedirectLimitReached, uri if redirects == 0
         | 
| 145 145 | 
             
                  location = response['location']
         | 
| 146 146 | 
             
                  if location
         | 
| 147 | 
            -
                     | 
| 147 | 
            +
                    new_uri = redirect_uri(location, uri)
         | 
| 148 | 
            +
                    # Clear the Authorization header if the new URI has a different host.
         | 
| 149 | 
            +
                    @headers.delete('Authorization') unless [@uri.scheme, @uri.host, @uri.port] == [new_uri.scheme, new_uri.host, new_uri.port]
         | 
| 150 | 
            +
                    request_range(range, new_uri, redirects - 1)
         | 
| 148 151 | 
             
                  else
         | 
| 149 152 | 
             
                    raise InvalidRequest.new(response.code, "Server at #{uri} replied with a #{response.code}, indicating redirection; however, the location header was empty.")
         | 
| 150 153 | 
             
                  end
         | 
    
        data/spec/remote_io_spec.rb
    CHANGED
    
    | @@ -3,187 +3,270 @@ require 'spec_helper' | |
| 3 3 | 
             
            describe FormatParser::RemoteIO do
         | 
| 4 4 | 
             
              it_behaves_like 'an IO object compatible with IOConstraint'
         | 
| 5 5 |  | 
| 6 | 
            -
               | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
                 | 
| 10 | 
            -
             | 
| 6 | 
            +
              # 2XX
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              context 'when the response code is 200 (OK)' do
         | 
| 9 | 
            +
                context 'when the response size does not exceed the requested range' do
         | 
| 10 | 
            +
                  it 'returns the entire response body' do
         | 
| 11 | 
            +
                    url = 'https://images.invalid/img.jpg'
         | 
| 12 | 
            +
                    body = 'response body'
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    stub = stub_request(:get, url)
         | 
| 15 | 
            +
                      .with(headers: { 'range' => 'bytes=10-109' })
         | 
| 16 | 
            +
                      .to_return(body: body, status: 200)
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    rio = described_class.new(url)
         | 
| 19 | 
            +
                    rio.seek(10)
         | 
| 20 | 
            +
                    read_result = rio.read(100)
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    expect(read_result).to eq(body)
         | 
| 23 | 
            +
                    expect(stub).to have_been_requested
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                context 'when the response size exceeds the requested range' do
         | 
| 28 | 
            +
                  it 'raises an error' do
         | 
| 29 | 
            +
                    url = 'https://images.invalid/img.jpg'
         | 
| 30 | 
            +
                    body = 'This response is way longer than 10 bytes.'
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    stub = stub_request(:get, url)
         | 
| 33 | 
            +
                      .with(headers: { 'range' => 'bytes=10-19' })
         | 
| 34 | 
            +
                      .to_return(body: body, status: 200)
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    rio = described_class.new(url)
         | 
| 37 | 
            +
                    rio.seek(10)
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    expect { rio.read(10) }.to raise_error(
         | 
| 40 | 
            +
                      "We requested 10 bytes, but the server sent us more\n"\
         | 
| 41 | 
            +
                      "(42 bytes) - it likely has no `Range:` support.\n"\
         | 
| 42 | 
            +
                      "The error occurred when talking to #{url}"
         | 
| 43 | 
            +
                    )
         | 
| 44 | 
            +
                    expect(stub).to have_been_requested
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
              end
         | 
| 11 48 |  | 
| 12 | 
            -
             | 
| 13 | 
            -
                 | 
| 49 | 
            +
              context 'when the response status code is 206 (Partial Content)' do
         | 
| 50 | 
            +
                context 'when the Content-Range header is present' do
         | 
| 51 | 
            +
                  it 'returns the response body' do
         | 
| 52 | 
            +
                    url = 'https://images.invalid/img.jpg'
         | 
| 53 | 
            +
                    body = 'response body'
         | 
| 14 54 |  | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
                )
         | 
| 55 | 
            +
                    stub = stub_request(:get, url)
         | 
| 56 | 
            +
                      .with(headers: { 'range' => 'bytes=10-109' })
         | 
| 57 | 
            +
                      .to_return(body: body, headers: { 'Content-Range' => '10-109/2577' }, status: 206)
         | 
| 19 58 |  | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 59 | 
            +
                    rio = described_class.new(url)
         | 
| 60 | 
            +
                    rio.seek(10)
         | 
| 61 | 
            +
                    read_result = rio.read(100)
         | 
| 23 62 |  | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 63 | 
            +
                    expect(read_result).to eq(body)
         | 
| 64 | 
            +
                    expect(stub).to have_been_requested
         | 
| 65 | 
            +
                  end
         | 
| 26 66 |  | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
                response = Net::HTTPOK.new('2', '200', 'OK')
         | 
| 30 | 
            -
                allow(response).to receive(:body).and_return('Response body')
         | 
| 67 | 
            +
                  it 'maintains and exposes pos' do
         | 
| 68 | 
            +
                    url = 'https://images.invalid/img.jpg'
         | 
| 31 69 |  | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 70 | 
            +
                    stub = stub_request(:get, url)
         | 
| 71 | 
            +
                      .with(headers: { 'range' => 'bytes=0-0' })
         | 
| 72 | 
            +
                      .to_return(body: 'a', headers: { 'Content-Range' => '0-0/13' }, status: 206)
         | 
| 34 73 |  | 
| 35 | 
            -
             | 
| 36 | 
            -
                  an_object_satisfying { |uri| URI::HTTPS === uri && uri.to_s == url },
         | 
| 37 | 
            -
                  a_hash_including('range' => 'bytes=10-109')
         | 
| 38 | 
            -
                )
         | 
| 74 | 
            +
                    rio = described_class.new(url)
         | 
| 39 75 |  | 
| 40 | 
            -
             | 
| 41 | 
            -
                rio.seek(10)
         | 
| 42 | 
            -
                read_result = rio.read(100)
         | 
| 76 | 
            +
                    expect(rio.pos).to eq(0)
         | 
| 43 77 |  | 
| 44 | 
            -
             | 
| 45 | 
            -
              end
         | 
| 78 | 
            +
                    rio.read(1)
         | 
| 46 79 |  | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 80 | 
            +
                    expect(rio.pos).to eq(1)
         | 
| 81 | 
            +
                    expect(stub).to have_been_requested
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
                end
         | 
| 50 84 |  | 
| 51 | 
            -
                 | 
| 52 | 
            -
             | 
| 85 | 
            +
                context 'when the Content-Range header is not present' do
         | 
| 86 | 
            +
                  it 'raises an error' do
         | 
| 87 | 
            +
                    url = 'https://images.invalid/img.jpg'
         | 
| 53 88 |  | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
                )
         | 
| 89 | 
            +
                    stub = stub_request(:get, url)
         | 
| 90 | 
            +
                      .with(headers: { 'range' => 'bytes=10-109' })
         | 
| 91 | 
            +
                      .to_return(status: 206)
         | 
| 58 92 |  | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 93 | 
            +
                    rio = described_class.new(url)
         | 
| 94 | 
            +
                    rio.seek(10)
         | 
| 61 95 |  | 
| 62 | 
            -
             | 
| 96 | 
            +
                    expect { rio.read(100) }.to raise_error("The server replied with 206 status but no Content-Range at #{url}")
         | 
| 97 | 
            +
                    expect(stub).to have_been_requested
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
                end
         | 
| 63 100 | 
             
              end
         | 
| 64 101 |  | 
| 65 | 
            -
               | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 102 | 
            +
              # 3XX
         | 
| 103 | 
            +
             | 
| 104 | 
            +
              [301, 302, 303, 307, 308].each do |code|
         | 
| 105 | 
            +
                context "when the response code is #{code}" do
         | 
| 106 | 
            +
                  context 'when the location header is present and the redirect limit is not exceeded' do
         | 
| 107 | 
            +
                    context 'when the location is absolute' do
         | 
| 108 | 
            +
                      it 'redirects to the specified location, without the Authorization header' do
         | 
| 109 | 
            +
                        redirecting_url = 'https://my_images.invalid/my_image'
         | 
| 110 | 
            +
                        destination_url = 'https://images.invalid/img.jpg'
         | 
| 111 | 
            +
                        body = 'response body'
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                        redirect_stub = stub_request(:get, redirecting_url)
         | 
| 114 | 
            +
                          .with(headers: { 'Authorization' => 'token', 'range' => 'bytes=10-109' })
         | 
| 115 | 
            +
                          .to_return(headers: { 'location' => destination_url }, status: code)
         | 
| 116 | 
            +
                        destination_stub = stub_request(:get, destination_url)
         | 
| 117 | 
            +
                          .with { |request| request.headers['Range'] == 'bytes=10-109' && !request.headers.key?('Authorization') }
         | 
| 118 | 
            +
                          .to_return(body: body, status: 200)
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                        rio = described_class.new(redirecting_url, headers: { 'Authorization' => 'token' })
         | 
| 121 | 
            +
                        rio.seek(10)
         | 
| 122 | 
            +
                        read_result = rio.read(100)
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                        expect(read_result).to eq(body)
         | 
| 125 | 
            +
                        expect(redirect_stub).to have_been_requested
         | 
| 126 | 
            +
                        expect(destination_stub).to have_been_requested
         | 
| 127 | 
            +
                      end
         | 
| 128 | 
            +
                    end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                    context 'when the location is relative' do
         | 
| 131 | 
            +
                      it 'redirects to the specified location under the same host, with the same Authorization header' do
         | 
| 132 | 
            +
                        host = 'https://images.invalid'
         | 
| 133 | 
            +
                        redirecting_path = '/my_image'
         | 
| 134 | 
            +
                        redirecting_url = host + redirecting_path
         | 
| 135 | 
            +
                        destination_path = '/img.jpg'
         | 
| 136 | 
            +
                        destination_url = host + destination_path
         | 
| 137 | 
            +
                        body = 'response body'
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                        redirect_stub = stub_request(:get, redirecting_url)
         | 
| 140 | 
            +
                          .with(headers: { 'Authorization' => 'token', 'range' => 'bytes=10-109' })
         | 
| 141 | 
            +
                          .to_return(headers: { 'location' => destination_path }, status: code)
         | 
| 142 | 
            +
                        destination_stub = stub_request(:get, destination_url)
         | 
| 143 | 
            +
                          .with(headers: { 'Authorization' => 'token', 'range' => 'bytes=10-109' })
         | 
| 144 | 
            +
                          .to_return(body: body, status: 200)
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                        rio = described_class.new(redirecting_url, headers: { 'Authorization' => 'token' })
         | 
| 147 | 
            +
                        rio.seek(10)
         | 
| 148 | 
            +
                        read_result = rio.read(100)
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                        expect(read_result).to eq(body)
         | 
| 151 | 
            +
                        expect(redirect_stub).to have_been_requested
         | 
| 152 | 
            +
                        expect(destination_stub).to have_been_requested
         | 
| 153 | 
            +
                      end
         | 
| 154 | 
            +
                    end
         | 
| 155 | 
            +
                  end
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                  context 'when the location header is not present' do
         | 
| 158 | 
            +
                    it 'raises an error' do
         | 
| 159 | 
            +
                      url = 'https://images.invalid/my_image'
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                      stub = stub_request(:get, url)
         | 
| 162 | 
            +
                        .with(headers: { 'range' => 'bytes=10-109' })
         | 
| 163 | 
            +
                        .to_return(status: code)
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                      rio = described_class.new(url)
         | 
| 166 | 
            +
                      rio.seek(10)
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                      expect { rio.read(100) }.to raise_error("Server at #{url} replied with a #{code}, indicating redirection; however, the location header was empty.")
         | 
| 169 | 
            +
                      expect(stub).to have_been_requested
         | 
| 170 | 
            +
                    end
         | 
| 171 | 
            +
                  end
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                  context 'when the redirect limit is exceeded' do
         | 
| 174 | 
            +
                    it 'raises an error' do
         | 
| 175 | 
            +
                      redirecting_url = 'https://images.invalid/my_image'
         | 
| 176 | 
            +
                      destination_url = 'https://images.invalid/img.jpg'
         | 
| 177 | 
            +
             | 
| 178 | 
            +
                      stub = stub_request(:get, /https:\/\/images\.invalid.*/)
         | 
| 179 | 
            +
                        .with(headers: { 'range' => 'bytes=10-109' })
         | 
| 180 | 
            +
                        .to_return(headers: { 'location' => destination_url }, status: code)
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                      rio = described_class.new(redirecting_url)
         | 
| 183 | 
            +
                      rio.seek(10)
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                      expect { rio.read(100) }.to raise_error("Too many redirects; last one to: #{destination_url}")
         | 
| 186 | 
            +
                      expect(stub).to have_been_requested.times(4)
         | 
| 187 | 
            +
                    end
         | 
| 188 | 
            +
                  end
         | 
| 189 | 
            +
                end
         | 
| 190 | 
            +
              end
         | 
| 68 191 |  | 
| 69 | 
            -
             | 
| 70 | 
            -
                allow(Net::HTTP).to receive(:request_get).and_return(response)
         | 
| 192 | 
            +
              # 4XX
         | 
| 71 193 |  | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
                   | 
| 75 | 
            -
                )
         | 
| 194 | 
            +
              context 'when the response status code is 416 (Range Not Satisfiable)' do
         | 
| 195 | 
            +
                it 'returns nil' do
         | 
| 196 | 
            +
                  url = 'https://images.invalid/img.jpg'
         | 
| 76 197 |  | 
| 77 | 
            -
             | 
| 78 | 
            -
             | 
| 198 | 
            +
                  stub = stub_request(:get, url)
         | 
| 199 | 
            +
                    .with(headers: { 'range' => 'bytes=100-199' })
         | 
| 200 | 
            +
                    .to_return(status: 416)
         | 
| 79 201 |  | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 202 | 
            +
                  rio = described_class.new(url)
         | 
| 203 | 
            +
                  rio.seek(100)
         | 
| 82 204 |  | 
| 83 | 
            -
             | 
| 84 | 
            -
             | 
| 85 | 
            -
                 | 
| 205 | 
            +
                  expect(rio.read(100)).to be_nil
         | 
| 206 | 
            +
                  expect(stub).to have_been_requested
         | 
| 207 | 
            +
                end
         | 
| 86 208 |  | 
| 87 | 
            -
                 | 
| 88 | 
            -
             | 
| 209 | 
            +
                it 'does not change pos or size' do
         | 
| 210 | 
            +
                  url = 'https://images.invalid/img.jpg'
         | 
| 89 211 |  | 
| 90 | 
            -
             | 
| 91 | 
            -
             | 
| 92 | 
            -
             | 
| 93 | 
            -
                )
         | 
| 212 | 
            +
                  stub = stub_request(:get, url)
         | 
| 213 | 
            +
                    .with(headers: { 'range' => 'bytes=0-0' })
         | 
| 214 | 
            +
                    .to_return(body: 'response body', headers: { 'Content-Range' => 'bytes 0-0/13' }, status: 206)
         | 
| 94 215 |  | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
                expect { rio.read(100) }.to(raise_error { |e| expect(e.status_code).to eq(403) })
         | 
| 98 | 
            -
              end
         | 
| 216 | 
            +
                  rio = described_class.new(url)
         | 
| 217 | 
            +
                  rio.read(1)
         | 
| 99 218 |  | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
                response = Net::HTTPRangeNotSatisfiable.new('2', '416', 'Range Not Satisfiable')
         | 
| 219 | 
            +
                  expect(rio.size).to eq(13)
         | 
| 220 | 
            +
                  expect(stub).to have_been_requested
         | 
| 103 221 |  | 
| 104 | 
            -
             | 
| 105 | 
            -
             | 
| 222 | 
            +
                  stub = stub_request(:get, url)
         | 
| 223 | 
            +
                    .with(headers: { 'range' => 'bytes=100-199' })
         | 
| 224 | 
            +
                    .to_return(status: 416)
         | 
| 106 225 |  | 
| 107 | 
            -
             | 
| 108 | 
            -
                   | 
| 109 | 
            -
                  a_hash_including('range' => 'bytes=100-199')
         | 
| 110 | 
            -
                )
         | 
| 226 | 
            +
                  rio.seek(100)
         | 
| 227 | 
            +
                  rio.read(100)
         | 
| 111 228 |  | 
| 112 | 
            -
             | 
| 113 | 
            -
             | 
| 114 | 
            -
             | 
| 115 | 
            -
                 | 
| 116 | 
            -
              end
         | 
| 117 | 
            -
             | 
| 118 | 
            -
              it 'does not overwrite size when the range cannot be satisfied and the response is 416' do
         | 
| 119 | 
            -
                url = 'https://images.invalid/img.jpg'
         | 
| 120 | 
            -
                response_1 = Net::HTTPPartialContent.new('2', '206', 'Partial Content')
         | 
| 121 | 
            -
                response_1['Content-Range'] = 'bytes 0-0/13'
         | 
| 122 | 
            -
                allow(response_1).to receive(:body).and_return('Response body')
         | 
| 123 | 
            -
                response_2 = Net::HTTPRangeNotSatisfiable.new('2', '416', 'Range Not Satisfiable')
         | 
| 124 | 
            -
             | 
| 125 | 
            -
                allow(Net::HTTP).to receive(:start).and_yield(Net::HTTP).and_return(response_1, response_2)
         | 
| 126 | 
            -
                allow(Net::HTTP).to receive(:request_get).and_return(response_1, response_2)
         | 
| 127 | 
            -
             | 
| 128 | 
            -
                expect(Net::HTTP).to receive(:request_get)
         | 
| 129 | 
            -
                  .with(
         | 
| 130 | 
            -
                    an_object_satisfying { |uri| uri.to_s == url },
         | 
| 131 | 
            -
                    a_hash_including('range' => 'bytes=0-0')
         | 
| 132 | 
            -
                  )
         | 
| 133 | 
            -
                  .ordered
         | 
| 134 | 
            -
                expect(Net::HTTP).to receive(:request_get)
         | 
| 135 | 
            -
                  .with(
         | 
| 136 | 
            -
                    an_object_satisfying { |uri| uri.to_s == url },
         | 
| 137 | 
            -
                    a_hash_including('range' => 'bytes=100-199')
         | 
| 138 | 
            -
                  )
         | 
| 139 | 
            -
                  .ordered
         | 
| 140 | 
            -
             | 
| 141 | 
            -
                rio = described_class.new(url)
         | 
| 142 | 
            -
                rio.read(1)
         | 
| 143 | 
            -
             | 
| 144 | 
            -
                expect(rio.size).to eq(13)
         | 
| 145 | 
            -
             | 
| 146 | 
            -
                rio.seek(100)
         | 
| 147 | 
            -
             | 
| 148 | 
            -
                expect(rio.read(100)).to be_nil
         | 
| 149 | 
            -
                expect(rio.size).to eq(13)
         | 
| 229 | 
            +
                  expect(rio.pos).to eq(100)
         | 
| 230 | 
            +
                  expect(rio.size).to eq(13)
         | 
| 231 | 
            +
                  expect(stub).to have_been_requested
         | 
| 232 | 
            +
                end
         | 
| 150 233 | 
             
              end
         | 
| 151 234 |  | 
| 152 | 
            -
               | 
| 153 | 
            -
                 | 
| 154 | 
            -
             | 
| 235 | 
            +
              [*400..415, *417..499].each do |code|
         | 
| 236 | 
            +
                context "when the response status code is #{code}" do
         | 
| 237 | 
            +
                  it 'raises an error' do
         | 
| 238 | 
            +
                    url = 'https://images.invalid/img.jpg'
         | 
| 155 239 |  | 
| 156 | 
            -
             | 
| 157 | 
            -
             | 
| 240 | 
            +
                    stub = stub_request(:get, url)
         | 
| 241 | 
            +
                      .with(headers: { 'range' => 'bytes=100-199' })
         | 
| 242 | 
            +
                      .to_return(status: code)
         | 
| 158 243 |  | 
| 159 | 
            -
             | 
| 160 | 
            -
             | 
| 161 | 
            -
                  a_hash_including('range' => 'bytes=100-199')
         | 
| 162 | 
            -
                )
         | 
| 244 | 
            +
                    rio = described_class.new(url)
         | 
| 245 | 
            +
                    rio.seek(100)
         | 
| 163 246 |  | 
| 164 | 
            -
             | 
| 165 | 
            -
             | 
| 166 | 
            -
             | 
| 167 | 
            -
                 | 
| 247 | 
            +
                    expect { rio.read(100) }.to raise_error("Server at #{url} replied with a #{code} and refused our request")
         | 
| 248 | 
            +
                    expect(stub).to have_been_requested
         | 
| 249 | 
            +
                  end
         | 
| 250 | 
            +
                end
         | 
| 168 251 | 
             
              end
         | 
| 169 252 |  | 
| 170 | 
            -
               | 
| 171 | 
            -
             | 
| 172 | 
            -
             | 
| 173 | 
            -
                response | 
| 174 | 
            -
             | 
| 253 | 
            +
              # 5XX
         | 
| 254 | 
            +
             | 
| 255 | 
            +
              (500..599).each do |code|
         | 
| 256 | 
            +
                context "when the response status code is #{code}" do
         | 
| 257 | 
            +
                  it 'raises an error' do
         | 
| 258 | 
            +
                    url = 'https://images.invalid/img.jpg'
         | 
| 175 259 |  | 
| 176 | 
            -
             | 
| 177 | 
            -
             | 
| 260 | 
            +
                    stub = stub_request(:get, url)
         | 
| 261 | 
            +
                      .with(headers: { 'range' => 'bytes=100-199' })
         | 
| 262 | 
            +
                      .to_return(status: code)
         | 
| 178 263 |  | 
| 179 | 
            -
             | 
| 180 | 
            -
             | 
| 181 | 
            -
                  a_hash_including('range' => 'bytes=0-0')
         | 
| 182 | 
            -
                )
         | 
| 264 | 
            +
                    rio = described_class.new(url)
         | 
| 265 | 
            +
                    rio.seek(100)
         | 
| 183 266 |  | 
| 184 | 
            -
             | 
| 185 | 
            -
             | 
| 186 | 
            -
             | 
| 187 | 
            -
                 | 
| 267 | 
            +
                    expect { rio.read(100) }.to raise_error("Server at #{url} replied with a #{code} and we might want to retry")
         | 
| 268 | 
            +
                    expect(stub).to have_been_requested
         | 
| 269 | 
            +
                  end
         | 
| 270 | 
            +
                end
         | 
| 188 271 | 
             
              end
         | 
| 189 272 | 
             
            end
         | 
    
        data/spec/spec_helper.rb
    CHANGED
    
    | @@ -7,6 +7,10 @@ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) | |
| 7 7 | 
             
            $LOAD_PATH.unshift(File.dirname(__FILE__))
         | 
| 8 8 |  | 
| 9 9 | 
             
            require 'rspec'
         | 
| 10 | 
            +
            require 'webmock/rspec'
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            WebMock.disable_net_connect!(allow_localhost: true)
         | 
| 13 | 
            +
             | 
| 10 14 | 
             
            require 'format_parser'
         | 
| 11 15 |  | 
| 12 16 | 
             
            module SpecHelpers
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: format_parser
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 2.0.0.pre
         | 
| 4 | 
            +
              version: 2.0.0.pre.2
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Noah Berman
         | 
| @@ -9,7 +9,7 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: exe
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2022-10- | 
| 12 | 
            +
            date: 2022-10-31 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: exifr
         | 
| @@ -123,6 +123,20 @@ dependencies: | |
| 123 123 | 
             
                - - ">="
         | 
| 124 124 | 
             
                  - !ruby/object:Gem::Version
         | 
| 125 125 | 
             
                    version: '0'
         | 
| 126 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 127 | 
            +
              name: webmock
         | 
| 128 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 129 | 
            +
                requirements:
         | 
| 130 | 
            +
                - - ">="
         | 
| 131 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 132 | 
            +
                    version: '0'
         | 
| 133 | 
            +
              type: :development
         | 
| 134 | 
            +
              prerelease: false
         | 
| 135 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 136 | 
            +
                requirements:
         | 
| 137 | 
            +
                - - ">="
         | 
| 138 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 139 | 
            +
                    version: '0'
         | 
| 126 140 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 127 141 | 
             
              name: wetransfer_style
         | 
| 128 142 | 
             
              requirement: !ruby/object:Gem::Requirement
         |