spec/acceptance/rest/auth_spec.rb in ably-0.1.6 vs spec/acceptance/rest/auth_spec.rb in ably-0.2.0

- old
+ new

@@ -1,21 +1,47 @@ -require "spec_helper" -require "securerandom" +require 'spec_helper' +require 'securerandom' -describe "REST" do +describe Ably::Auth do + include Ably::Modules::Conversions + [:msgpack, :json].each do |protocol| context "over #{protocol}" do let(:client) do Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol) end let(:auth) { client.auth } + let(:content_type) do + if protocol == :msgpack + 'application/x-msgpack' + else + 'application/json' + end + end + def request_body_includes(request, protocol, key, val) + body = if protocol == :msgpack + MessagePack.unpack(request.body) + else + JSON.parse(request.body) + end + body[key.to_s].to_s == val.to_s + end + + def serialize(object, protocol) + if protocol == :msgpack + MessagePack.pack(token_response) + else + JSON.dump(token_response) + end + end + describe "#request_token" do let(:ttl) { 30 * 60 } - let(:capability) { { :foo => ["publish"] } } + let(:capability) { { :foo => ['publish'] } } - it "returns the requested token" do + it 'returns the requested token' do actual_token = auth.request_token( ttl: ttl, capability: capability ) @@ -23,20 +49,25 @@ expect(actual_token.key_id).to match(/^#{key_id}$/) expect(actual_token.issued_at).to be_within(2).of(Time.now) expect(actual_token.expires_at).to be_within(2).of(Time.now + ttl) end - %w(client_id ttl timestamp capability nonce).each do |option| + %w(client_id capability nonce timestamp ttl).each do |option| context "option :#{option}", webmock: true do let(:random) { SecureRandom.random_number(1_000_000_000).to_s } let(:options) { { option.to_sym => random } } - let(:token_response) { { access_token: {} }.to_json } + let(:token_response) { { access_token: {} } } let!(:request_token_stub) do stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken"). - with(:body => hash_including({ option => random })). - to_return(:status => 201, :body => token_response, :headers => { 'Content-Type' => 'application/json' }) + with do |request| + request_body_includes(request, protocol, option, random) + end.to_return( + :status => 201, + :body => serialize(token_response, protocol), + :headers => { 'Content-Type' => content_type } + ) end before { auth.request_token options } it 'overrides default' do @@ -53,46 +84,50 @@ let(:token_request) { auth.create_token_request(token_options) } let(:mac) do hmac_for(token_request, key_secret) end - let(:token_response) { { access_token: {} }.to_json } + let(:token_response) { { access_token: {} } } let!(:request_token_stub) do stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken"). - with(:body => hash_including({ 'mac' => mac })). - to_return(:status => 201, :body => token_response, :headers => { 'Content-Type' => 'application/json' }) + with do |request| + request_body_includes(request, protocol, 'mac', mac) + end.to_return( + :status => 201, + :body => serialize(token_response, protocol), + :headers => { 'Content-Type' => content_type }) end let!(:token) { auth.request_token(token_options) } specify 'key_id is used in request and signing uses key_secret' do expect(request_token_stub).to have_been_requested end end - context "with :query_time option" do + context 'with :query_time option' do let(:options) { { query_time: true } } it 'queries the server for the time' do expect(client).to receive(:time).and_call_original auth.request_token(options) end end - context "without :query_time option" do + context 'without :query_time option' do let(:options) { { query_time: false } } it 'queries the server for the time' do expect(client).to_not receive(:time) auth.request_token(options) end end context 'with :auth_url option', webmock: true do let(:auth_url) { 'https://www.fictitious.com/get_token' } - let(:token_request) { { id: key_id }.to_json } - let(:token_response) { { access_token: { } }.to_json } + let(:token_request) { { id: key_id } } + let(:token_response) { { access_token: { } } } let(:query_params) { nil } let(:headers) { nil } let(:auth_method) { :get } let(:options) do { @@ -103,19 +138,28 @@ } end let!(:auth_url_request_stub) do stub = stub_request(auth_method, auth_url) - stub.with(:query => hash_including(query_params)) unless query_params.nil? - stub.with(:header => hash_including(headers)) unless headers.nil? - stub.to_return(:status => 201, :body => token_request, :headers => { 'Content-Type' => 'application/json' }) + stub.with(:query => query_params) unless query_params.nil? + stub.with(:headers => headers) unless headers.nil? + stub.to_return( + :status => 201, + :body => token_request.to_json, + :headers => { 'Content-Type' => 'application/json' } + ) end let!(:request_token_stub) do stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken"). - with(:body => hash_including({ 'id' => key_id })). - to_return(:status => 201, :body => token_response, :headers => { 'Content-Type' => 'application/json' }) + with do |request| + request_body_includes(request, protocol, 'id', key_id) + end.to_return( + :status => 201, + :body => serialize(token_response, protocol), + :headers => { 'Content-Type' => content_type } + ) end context 'valid' do before { auth.request_token options } @@ -242,34 +286,34 @@ expect { auth.authorise(force: true) }.to change { auth.current_token } end end end - describe "#create_token_request" do + describe '#create_token_request' do let(:ttl) { 60 * 60 } let(:capability) { { :foo => ["publish"] } } let(:options) { Hash.new } subject { auth.create_token_request(options) } - it "uses the key ID from the client" do + it 'uses the key ID from the client' do expect(subject[:id]).to eql(key_id) end - it "uses the default TTL" do + it 'uses the default TTL' do expect(subject[:ttl]).to eql(Ably::Models::Token::DEFAULTS[:ttl]) end - it "uses the default capability" do + it 'uses the default capability' do expect(subject[:capability]).to eql(Ably::Models::Token::DEFAULTS[:capability].to_json) end - it "has a unique nonce" do + it 'has a unique nonce' do unique_nonces = 100.times.map { auth.create_token_request[:nonce] } expect(unique_nonces.uniq.length).to eql(100) end - it "has a nonce of at least 16 characters" do + it 'has a nonce of at least 16 characters' do expect(subject[:nonce].length).to be >= 16 end %w(ttl capability nonce timestamp client_id).each do |attribute| context "with option :#{attribute}" do @@ -281,51 +325,51 @@ expect(subject[attribute.to_sym]).to eql(option_value) end end end - context "invalid attributes" do + context 'invalid attributes' do let(:options) { { nonce: 'valid', is_not_used_by_token_request: 'invalid' } } specify 'are ignored' do expect(subject.keys).to_not include(:is_not_used_by_token_request) expect(subject.keys).to include(:nonce) expect(subject[:nonce]).to eql('valid') end end - context "missing key ID and/or secret" do + context 'missing key ID and/or secret' do let(:client) { Ably::Rest::Client.new(auth_url: 'http://example.com', protocol: protocol) } - it "should raise an exception if key secret is missing" do + it 'should raise an exception if key secret is missing' do expect { auth.create_token_request(key_id: 'id') }.to raise_error Ably::Exceptions::TokenRequestError end - it "should raise an exception if key id is missing" do + it 'should raise an exception if key id is missing' do expect { auth.create_token_request(key_secret: 'secret') }.to raise_error Ably::Exceptions::TokenRequestError end end - context "with :query_time option" do + context 'with :query_time option' do let(:time) { Time.now - 30 } let(:options) { { query_time: true } } it 'queries the server for the time' do expect(client).to receive(:time).and_return(time) expect(subject[:timestamp]).to eql(time.to_i) end end - context "with :timestamp option" do + context 'with :timestamp option' do let(:token_request_time) { Time.now + 5 } let(:options) { { timestamp: token_request_time } } it 'uses the provided timestamp' do expect(subject[:timestamp]).to eql(token_request_time.to_i) end end - context "signing" do + context 'signing' do let(:options) do { id: SecureRandom.hex, ttl: SecureRandom.hex, capability: SecureRandom.hex, @@ -340,11 +384,11 @@ expect(subject[:mac]).to eql(hmac) end end end - context "client with token authentication" do + context 'client with token authentication' do let(:capability) { { :foo => ["publish"] } } describe "with token_id argument" do let(:ttl) { 60 * 60 } let(:token) do @@ -356,32 +400,32 @@ let(:token_id) { token.id } let(:token_auth_client) do Ably::Rest::Client.new(token_id: token_id, environment: environment, protocol: protocol) end - it "authenticates successfully" do - expect(token_auth_client.channel("foo").publish("event", "data")).to be_truthy + it 'authenticates successfully' do + expect(token_auth_client.channel('foo').publish('event', 'data')).to be_truthy end - it "disallows publishing on unspecified capability channels" do - expect { token_auth_client.channel("bar").publish("event", "data") }.to raise_error do |error| + it 'disallows publishing on unspecified capability channels' do + expect { token_auth_client.channel('bar').publish('event', 'data') }.to raise_error do |error| expect(error).to be_a(Ably::Exceptions::InvalidToken) expect(error.status).to eql(401) expect(error.code).to eql(40160) end end - it "fails if timestamp is invalid" do + it 'fails if timestamp is invalid' do expect { auth.request_token(timestamp: Time.now - 180) }.to raise_error do |error| expect(error).to be_a(Ably::Exceptions::InvalidToken) expect(error.status).to eql(401) expect(error.code).to eql(40101) end end end - describe "implicit through client id" do + describe 'implicit through client id' do let(:client_id) { '999' } let(:client) do Ably::Rest::Client.new(api_key: api_key, client_id: client_id, environment: environment, protocol: protocol) end let(:token_id) { 'unique-token-id' } @@ -402,28 +446,28 @@ stub_request(:post, "#{client.endpoint}/channels/foo/publish"). with(headers: { 'Authorization' => "Bearer #{encode64(token_id)}" }). to_return(status: 201, body: '{}', headers: { 'Content-Type' => 'application/json' }) end - it "will create a token request" do - client.channel("foo").publish("event", "data") + it 'will create a token request' do + client.channel('foo').publish('event', 'data') expect(request_token_stub).to have_been_requested end end - context "will create a token" do + context 'will create a token' do let(:token) { client.auth.current_token } - it "before a request is made" do + it 'before a request is made' do expect(token).to be_nil end - it "when a message is published" do - expect(client.channel("foo").publish("event", "data")).to be_truthy + it 'when a message is published' do + expect(client.channel('foo').publish('event', 'data')).to be_truthy end - it "with capability and TTL defaults" do - client.channel("foo").publish("event", "data") + it 'with capability and TTL defaults' do + client.channel('foo').publish('event', 'data') expect(token).to be_a(Ably::Models::Token) capability_with_str_key = Ably::Models::Token::DEFAULTS[:capability] capability = Hash[capability_with_str_key.keys.map(&:to_sym).zip(capability_with_str_key.values)] expect(token.capability).to eq(capability)