# frozen_string_literal: true require_relative '../spec_helper' module Aws describe PageableResponse do def pageable(resp, pager) PageableResponse.apply(resp) resp.pager = pager resp.context[:original_params] = resp.context.params.freeze resp end let(:pager) { Pager.new(options) } let(:resp) { pageable(Seahorse::Client::Response.new, pager) } describe 'pagable operations' do let(:options) {{ tokens: { 'next_token' => 'offset', 'other_token' => 'token' } }} it 'is Enumerable' do expect(resp).to be_kind_of(Enumerable) end it 'returns false from last page if the paging token value is present' do resp.data = { 'next_token' => 'OFFSET' } expect(resp.last_page?).to be(false) expect(resp.next_page?).to be(true) end it 'is not pageable if response data does not contain tokens' do resp.data = { } expect(resp.last_page?).to be(true) expect(resp.next_page?).to be(false) end it 'is not pageable if next token is an empty hash' do resp.data = { 'next_token' => {} } expect(resp.last_page?).to be(true) expect(resp.next_page?).to be(false) end it 'is not pageable if next token is an empty array' do resp.data = { 'next_token' => [] } expect(resp.last_page?).to be(true) expect(resp.next_page?).to be(false) end it 'is not pageable if next token is an empty string' do resp.data = { 'next_token' => '' } expect(resp.last_page?).to be(true) expect(resp.next_page?).to be(false) end it 'responds to #next_page by sending a new request with tokens applied' do client = double('client') new_request = double('new-request') resp.data = { 'next_token' => 'OFFSET' } resp.context.client = client resp.context.operation_name = 'operation-name' expect(client).to receive(:build_request). with('operation-name', { :offset => 'OFFSET' }). and_return(new_request) expect(new_request).to receive(:send_request). and_return(Seahorse::Client::Response.new) resp.next_page end it 'removes previous tokens on subsequent #next_page calls' do client = double('client') request_1 = double('new-request') request_2 = double('new-new-request') resp.data = { 'next_token' => 'OFFSET', 'other_token' => 'TOKEN' } resp.context.client = client resp.context.operation_name = 'operation-name' expect(client).to receive(:build_request). with('operation-name', { :offset => 'OFFSET', :token => 'TOKEN' }). and_return(request_1) context = Seahorse::Client::RequestContext.new( params: { :offset => 'OFFSET', :token => 'TOKEN' } ) new_resp = pageable(Seahorse::Client::Response.new(context: context), pager) new_resp.data = { 'next_token' => 'OFFSET' } new_resp.context.client = client new_resp.context.operation_name = 'operation-name' expect(request_1).to receive(:send_request).and_return(new_resp) second_page = resp.next_page expect(client).to receive(:build_request). with('operation-name', { :offset => 'OFFSET' }). and_return(request_2) expect(request_2).to receive(:send_request). and_return(Seahorse::Client::Response.new) second_page.next_page end end describe 'paging with multiple tokens' do let(:options) {{ tokens: { 'group' => 'offset_a', 'value' => 'offset_b' } }} it 'returns false from last page if all paging tokens are present' do resp.data = { 'group' => 'a', 'value' => 'b' } expect(resp.last_page?).to be(false) expect(resp.next_page?).to be(true) end it 'returns false from last page if ANY paging token is present' do resp.data = { 'group' => 'a' } expect(resp.last_page?).to be(false) expect(resp.next_page?).to be(true) end it 'returns true from last page if NO paging tokens are present' do resp.data = { } expect(resp.last_page?).to be(true) expect(resp.next_page?).to be(false) end it 'sends any tokens found a request params' do client = double('client') new_request = double('new-request', send_request: nil) resp.data = { 'group' => 'a' } resp.context.client = client resp.context.operation_name = 'operation-name' expect(client).to receive(:build_request). with('operation-name', { :offset_a => 'a' }). and_return(new_request) allow(new_request).to receive(:send_request).and_return(resp) resp.next_page end end describe 'paging with truncation indicator' do let(:options) {{ tokens: { 'next_marker' => 'marker' }, more_results: 'is_truncated' }} it 'returns false from last page if the truncation marker is true' do resp.data = { 'is_truncated' => true } expect(resp.last_page?).to be(false) expect(resp.next_page?).to be(true) end it 'returns true from last page if the truncation marker is false' do resp.data = { 'is_truncated' => false } expect(resp.last_page?).to be(true) expect(resp.next_page?).to be(false) end end describe '#each_page' do let(:options) {{ tokens: { 'next_token' => 'offset' } }} it 'yields once per paging result' do client = double('client') new_request = double('new-request') resp.data = { 'next_token' => 'OFFSET' } resp.context.client = client resp.context.operation_name = 'operation-name' resp2 = pageable(Seahorse::Client::Response.new, resp.pager) resp2.data = {} allow(client).to receive(:build_request). with('operation-name', { :offset => 'OFFSET' }). and_return(new_request) allow(new_request).to receive(:send_request).and_return(resp2) pages = [] resp.each { |r| pages << r.data } expect(pages).to eq([resp.data, resp2.data]) end end describe '#count' do let(:options) {{ tokens: {} }} it 'raises not implemented error by default' do data = double('data') resp = Seahorse::Client::Response.new(data:data) page = pageable(resp, pager) expect { page.count }.to raise_error(NoMethodError) end it 'calls count on data' do data = double('data', count: 10) resp = Seahorse::Client::Response.new(data:data) page = pageable(resp, pager) expect(page.count).to eq(10) end it 'returns false from respond_to when count not present' do data = double('data') resp = Seahorse::Client::Response.new(data:data) page = pageable(resp, pager) expect(page.respond_to?(:count)).to be(false) end it 'indicates it responds to count when data#count exists' do data = double('data', count: 10) resp = Seahorse::Client::Response.new(data:data) page = pageable(resp, pager) expect(page.respond_to?(:count)) end it 'correctly answers respond_to? with methods different than #count' do data = double('data', foo: 'bar') resp = Seahorse::Client::Response.new(data:data) page = pageable(resp, pager) expect(page.respond_to?(:foo)).to be(true) end end describe '.apply' do # see https://github.com/aws/aws-sdk-ruby/pull/2670 it 'does not bump RubyVM.stat(:global_constant_state)' do skip 'Only applies to MRI' unless defined? RubyVM.stat key = :global_constant_state # Ruby 3.2 removed global_constant_state # see https://github.com/ruby/ruby/pull/5766 key = :constant_cache_invalidations if RUBY_VERSION >= '3.2' object = Object.new expect { PageableResponse.apply(object) }.to_not change { RubyVM.stat(key) } end end end end