Skip to content

Commit

Permalink
complete all updates and fixes with adjustment to specs
Browse files Browse the repository at this point in the history
  • Loading branch information
bradpotts committed Nov 17, 2024
1 parent e3531e2 commit a38e0b1
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 26 deletions.
2 changes: 1 addition & 1 deletion lib/ruby_api_pack_cloudways/connection/cw_connect.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def handle_response(response)

# Parse response from Cloudways API
def parse_response(response)
content_type = response.headers['content-type']
content_type = response.headers&.fetch('content-type', nil)
raise "Unexpected response: #{response.body}" unless content_type&.include?('application/json')

Oj.load(response.body, mode: :strict)
Expand Down
143 changes: 138 additions & 5 deletions spec/ruby_api_pack_cloudways/connection/cw_connect_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
RSpec.describe RubyApiPackCloudways::Connection::CwConnect do
let(:connection) { described_class.new('https://api.cloudways.com/api/v1', '/some_path') }
let(:token_instance) { instance_double(RubyApiPackCloudways::Connection::CwToken, cw_api_token: 'fake_token') }
let(:http_response) { instance_double(HTTParty::Response, code: 200, body: '{"data":"value"}') }
let(:http_response) do
instance_double(
HTTParty::Response,
code: 200,
body: '{"data":"value"}',
headers: { 'content-type' => 'application/json' }
)
end
let(:post_params) { { key: 'value' } }

before do
Expand All @@ -29,7 +36,14 @@
end

context 'when the response code is not 200' do
let(:error_response) { instance_double(HTTParty::Response, code: 500, body: '{"error":"Server error"}') }
let(:error_response) do
instance_double(
HTTParty::Response,
code: 500,
body: '{"error":"Server error"}',
headers: { 'content-type' => 'application/json' }
)
end

before do
allow(HTTParty).to receive(:get).and_return(error_response)
Expand All @@ -41,7 +55,14 @@
end

context 'when parsing the response fails' do
let(:faulty_response) { instance_double(HTTParty::Response, code: 200, body: 'invalid_json') }
let(:faulty_response) do
instance_double(
HTTParty::Response,
code: 200,
body: 'invalid_json',
headers: { 'content-type' => 'application/json' }
)
end

before do
allow(HTTParty).to receive(:get).and_return(faulty_response)
Expand All @@ -52,6 +73,41 @@
expect { connection.cloudways_api_connection }.to raise_error(/Error parsing response: Unexpected character/)
end
end

context 'when rate limit is exceeded' do
let(:rate_limit_error) { RuntimeError.new('Rate limit exceeded') }

before do
attempts = 0
allow(HTTParty).to receive(:get).and_wrap_original do |original_method, *args|
attempts += 1
raise rate_limit_error if attempts == 1

instance_double(
HTTParty::Response,
code: 200,
body: '{"data":"value"}',
headers: { 'content-type' => 'application/json' }
)
end
end

it 'retries after a rate limit error and succeeds' do
response = connection.cloudways_api_connection
expect(response['data']).to eq('value')
expect(HTTParty).to have_received(:get).twice # Confirms retry occurred
end
end

context 'when an unexpected runtime error occurs' do
before do
allow(HTTParty).to receive(:get).and_raise(RuntimeError, 'Unexpected error')
end

it 'raises the unexpected runtime error' do
expect { connection.cloudways_api_connection }.to raise_error(RuntimeError, 'Unexpected error')
end
end
end

describe '#cloudways_api_post_connection' do
Expand All @@ -76,7 +132,14 @@
end

context 'when the POST response code is not 200' do
let(:error_response) { instance_double(HTTParty::Response, code: 500, body: '{"error":"Server error"}') }
let(:error_response) do
instance_double(
HTTParty::Response,
code: 500,
body: '{"error":"Server error"}',
headers: { 'content-type' => 'application/json' }
)
end

before do
allow(HTTParty).to receive(:post).and_return(error_response)
Expand All @@ -88,7 +151,14 @@
end

context 'when parsing the POST response fails' do
let(:faulty_response) { instance_double(HTTParty::Response, code: 200, body: 'invalid_json') }
let(:faulty_response) do
instance_double(
HTTParty::Response,
code: 200,
body: 'invalid_json',
headers: { 'content-type' => 'application/json' }
)
end

before do
allow(HTTParty).to receive(:post).and_return(faulty_response)
Expand All @@ -102,4 +172,67 @@
end
end
end

describe '#parse_response' do
let(:valid_response) do
instance_double(
HTTParty::Response,
body: '{"key":"value"}',
headers: { 'content-type' => 'application/json' }
)
end

let(:invalid_response_missing_content_type) do
instance_double(
HTTParty::Response,
body: '{"key":"value"}',
headers: nil
)
end

let(:invalid_response_wrong_content_type) do
instance_double(
HTTParty::Response,
body: '{"key":"value"}',
headers: { 'content-type' => 'text/html' }
)
end

let(:invalid_json_response) do
instance_double(
HTTParty::Response,
body: 'invalid_json',
headers: { 'content-type' => 'application/json' }
)
end

it 'parses the response body successfully when content-type is application/json' do
result = connection.send(:parse_response, valid_response)
expect(result).to eq({ 'key' => 'value' })
end

it 'raises an error when content-type is missing' do
invalid_response_missing_content_type = instance_double(
HTTParty::Response,
body: '{"key":"value"}',
headers: nil
)
expect do
connection.send(:parse_response, invalid_response_missing_content_type)
end.to raise_error(/Unexpected response/)
end

it 'raises an error when content-type does not include application/json' do
expect do
connection.send(:parse_response, invalid_response_wrong_content_type)
end.to raise_error(/Unexpected response/)
end

it 'raises a parsing error when Oj fails to parse the response body' do
allow(Oj).to receive(:load).and_raise(Oj::ParseError, 'Unexpected character')
expect do
connection.send(:parse_response, invalid_json_response)
end.to raise_error(/Error parsing response: Unexpected character/)
end
end
end
81 changes: 61 additions & 20 deletions spec/ruby_api_pack_cloudways/connection/cw_token_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,22 @@

RSpec.describe RubyApiPackCloudways::Connection::CwToken do
let(:token) { described_class.new }
let(:response) { instance_double(HTTParty::Response, body: '{"access_token":"fake_token"}') }
let(:invalid_json_response) { instance_double(HTTParty::Response, body: 'invalid_json') }
let(:valid_response) do
instance_double(
HTTParty::Response,
code: 200,
body: '{"access_token":"fake_token", "expires_in": 3600}', # Includes "expires_in"
headers: { 'content-type' => 'application/json' }
)
end
let(:error_response) do
instance_double(
HTTParty::Response,
code: 500,
body: '{"error":"Invalid request"}',
headers: { 'content-type' => 'application/json' }
)
end

before do
RubyApiPackCloudways.configure do |config|
Expand All @@ -15,37 +29,64 @@
config.api_email = 'test@example.com'
config.api_key = 'test_key'
end
allow(HTTParty).to receive(:post).and_return(response)

allow(HTTParty).to receive(:post).and_return(valid_response)
end

describe '#cw_api_token' do
it 'returns an access token' do
expect(token.cw_api_token).to eq('fake_token')
context 'when a valid cached token exists' do
before do
allow(token).to receive(:valid_cached_token?).and_return(true)
token.instance_variable_set(:@cached_token, 'cached_token')
end

it 'returns the cached token without making an HTTP request' do
expect(HTTParty).not_to receive(:post)
expect(token.cw_api_token).to eq('cached_token')
end
end

it 'calls the correct endpoint' do
token.cw_api_token
expect_httparty_post_with_correct_params
context 'when no cached token exists or it is expired' do
it 'fetches a new token and returns it' do
expect(token.cw_api_token).to eq('fake_token')
end
end

context 'when the response body is invalid JSON' do
context 'when the response code is not 200' do
before do
allow(Oj).to receive(:load).and_raise(Oj::ParseError, 'Invalid JSON')
allow(HTTParty).to receive(:post).and_return(error_response)
end

it 'raises a parsing error' do
expect do
token.send(:parse_response, invalid_json_response)
end.to raise_error(RuntimeError, 'Error parsing response: Invalid JSON')
it 'raises an error with the response code' do
expect { token.cw_api_token }.to raise_error(RuntimeError, /Failed to fetch token: 500/)
end
end

context 'when the cached token is expired' do
before do
token.instance_variable_set(:@cached_token, 'expired_token')
token.instance_variable_set(:@token_expiry, Time.now - 3600) # Token expired an hour ago
end

it 'fetches a new token' do
expect(token.cw_api_token).to eq('fake_token')
end
end
end

def expect_httparty_post_with_correct_params
expect(HTTParty).to have_received(:post).with(
'https://api.cloudways.com/api/v1/oauth/access_token',
headers: { 'Content-Type' => 'application/x-www-form-urlencoded' },
body: { email: 'test@example.com', api_key: 'test_key' }
)
describe '#parse_response' do
let(:valid_response) { instance_double(HTTParty::Response, body: '{"key":"value"}') }
let(:invalid_response) { instance_double(HTTParty::Response, body: 'invalid_json') }

it 'parses the response body successfully' do
expect(token.send(:parse_response, valid_response)).to eq({ 'key' => 'value' })
end

it 'raises an error when parsing fails' do
allow(Oj).to receive(:load).and_raise(Oj::ParseError, 'Unexpected character')
expect do
token.send(:parse_response, invalid_response)
end.to raise_error(RuntimeError, /Error parsing response: Unexpected character/)
end
end
end

0 comments on commit a38e0b1

Please sign in to comment.