response_mate 0.1.14 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,12 +3,10 @@
3
3
  class ResponseMate::Manifest
4
4
  include ResponseMate::ManifestParser
5
5
 
6
- attr_accessor :filename, :requests, :requests_text, :base_url, :oauth,
7
- :default_headers, :environment, :name
6
+ attr_accessor :filename, :requests, :requests_text, :environment, :name
8
7
 
9
8
  def initialize(filename, environment = nil)
10
9
  @filename = filename || ResponseMate.configuration.requests_manifest
11
- @oauth = ResponseMate::Oauth.new
12
10
  @environment = environment
13
11
  parse
14
12
  end
@@ -28,35 +26,25 @@ class ResponseMate::Manifest
28
26
 
29
27
  def parse
30
28
  preprocess_manifest
31
- @request_hashes = YAML.load(requests_text)
32
- @base_url = @request_hashes['base_url']
33
- @requests = @request_hashes['requests'].map { |rh| ResponseMate::Request.new(rh) }
34
- @default_headers = @request_hashes['default_headers']
35
- @name = @request_hashes['name'] || filename
29
+ @request_hashes = YAML.load(requests_text).deep_symbolize_keys
30
+ @name = @request_hashes[:name] || filename
31
+ @requests = @request_hashes[:requests].
32
+ map(&:deep_symbolize_keys!).
33
+ map { |rh| ResponseMate::Request.new(rh).normalize! }
36
34
  end
37
35
 
38
- class << self
39
- def parse_request(request)
40
- return parse_request_string(request) if request.is_a? String
41
- parse_request_hash(request) if request.is_a? Hash
42
- end
43
-
44
- def parse_request_hash(hash)
45
- self::DEFAULT_REQUEST.merge(hash.symbolize_keys)
46
- end
36
+ def requests_for_keys(keys)
37
+ if keys.present?
38
+ existing_keys = requests.map(&:key)
39
+ missing_keys = keys - existing_keys
47
40
 
48
- def parse_request_string(request_string)
49
- raise ArgumentError if request_string !~ self::REQUEST_MATCHER
50
- { verb: $~[:verb] || self::DEFAULT_REQUEST[:verb] }
51
- .merge(extract_path_query($~[:path]))
52
- end
41
+ if missing_keys.present?
42
+ raise ResponseMate::KeysNotFound.new(missing_keys.join(','))
43
+ end
53
44
 
54
- def extract_path_query(str)
55
- parsed = Addressable::URI.parse(str)
56
- {
57
- path: parsed.path,
58
- params: parsed.query_values
59
- }
45
+ requests.select! do |r|
46
+ keys.include? r.key
47
+ end
60
48
  end
61
49
  end
62
50
  end
@@ -3,37 +3,26 @@
3
3
  module ResponseMate
4
4
  # Handles recording requests
5
5
  class Recorder
6
- include ResponseMate::ManifestParser
7
-
8
- attr_accessor :base_url, :conn, :manifest, :oauth, :keys
6
+ attr_accessor :conn, :manifest, :keys
9
7
 
10
8
  def initialize(args = {})
11
9
  @manifest = args[:manifest]
12
-
13
- @keys = args[:keys]
14
- @base_url = args[:base_url] || manifest.base_url
15
-
16
- @conn = ResponseMate::Connection.new(base_url)
17
- @conn.set_headers_from_manifest(manifest)
10
+ @conn = ResponseMate::Connection.new
18
11
  end
19
12
 
20
- def record
21
- requests = manifest.requests
22
- requests.select! { |r| keys.include? r.key } if keys.present?
23
- requests.each do |request|
24
- process request.key, request
25
- end
13
+ def record(keys)
14
+ requests = keys.empty? ? manifest.requests : manifest.requests_for_keys(keys)
15
+
16
+ requests.each { |request| process request }
26
17
  end
27
18
 
28
19
  private
29
20
 
30
- def process(key, request)
21
+ def process(request)
31
22
  meta = request.meta
32
- request = ResponseMate::Manifest.parse_request(request.request)
23
+ puts request.to_cli_format
33
24
 
34
- puts "[#{key}] #{request[:verb]}".cyan_on_black.bold << " #{request[:path]}"
35
- puts "\tparams #{request[:params]}" if request[:params].present?
36
- ResponseMate::Tape.new.write(key, request, conn.fetch(request), meta)
25
+ ResponseMate::Tape.new.write(request.key, request, conn.fetch(request), meta)
37
26
  end
38
27
  end
39
28
  end
@@ -1,4 +1,37 @@
1
1
  # coding: utf-8
2
2
 
3
3
  class ResponseMate::Request < OpenStruct
4
+ delegate :[], to: :request
5
+
6
+ def normalize!
7
+ unless ResponseMate::ManifestParser::HTTP_VERBS.include? self.request[:verb]
8
+ self.request[:verb] = 'GET'
9
+ end
10
+
11
+ self.request[:url] = URI.encode(adjust_scheme(request[:url], request[:scheme]))
12
+
13
+ self
14
+ end
15
+
16
+ def to_cli_format
17
+ out = "[#{key}] #{request[:verb]}".cyan_on_black.bold << " #{request[:url]}"
18
+ out << "\tparams #{request[:params]}" if request[:params].present?
19
+ out
20
+ end
21
+
22
+ def to_hash
23
+ marshal_dump[:request]
24
+ end
25
+
26
+ private
27
+
28
+ def adjust_scheme(uri, scheme)
29
+ scheme = %w[http https].include?(scheme) ? scheme : 'http'
30
+
31
+ if uri !~ /\Ahttp(s)?/
32
+ "#{scheme}://#{uri}"
33
+ else
34
+ uri
35
+ end
36
+ end
4
37
  end
@@ -2,12 +2,17 @@
2
2
 
3
3
  class ResponseMate::Tape
4
4
  def write(key, request, response, meta = {})
5
- File.open("#{ResponseMate.configuration.output_dir}#{key}.yml", 'w') do |f|
5
+ output_dir = ResponseMate.configuration.output_dir
6
+ output_path = File.join output_dir, "#{key}.yml"
7
+
8
+ File.open(output_path, 'w') do |f|
6
9
  file_content = {
7
- request: request.select { |_, v| !v.nil? },
8
- status: response.status,
9
- headers: response.headers.to_hash,
10
- body: response.body
10
+ request: request.to_hash.select { |_, v| !v.nil? },
11
+ response: {
12
+ status: response.status,
13
+ headers: response.headers,
14
+ body: response.body
15
+ }
11
16
  }
12
17
 
13
18
  file_content.merge!(meta: meta) if meta.present?
@@ -17,4 +22,11 @@ class ResponseMate::Tape
17
22
  rescue Errno::ENOENT
18
23
  raise ResponseMate::OutputDirError
19
24
  end
25
+
26
+ class << self
27
+ def load(key)
28
+ YAML.load_file(File.join(ResponseMate.configuration.output_dir, "#{key}.yml")).
29
+ symbolize_keys
30
+ end
31
+ end
20
32
  end
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
2
 
3
3
  module ResponseMate
4
- VERSION = '0.1.14'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/response_mate.rb CHANGED
@@ -10,6 +10,8 @@ require 'highline/import'
10
10
  require 'mustache'
11
11
  require 'ostruct'
12
12
 
13
+ autoload :YAML, 'yaml'
14
+
13
15
  require 'response_mate/version'
14
16
 
15
17
  require 'response_mate/commands/base'
@@ -28,7 +30,6 @@ require 'response_mate/core'
28
30
  require 'response_mate/request'
29
31
  require 'response_mate/environment'
30
32
  require 'response_mate/manifest'
31
- require 'response_mate/oauth'
32
33
  require 'response_mate/tape'
33
34
  require 'response_mate/recorder'
34
35
  require 'response_mate/inspector'
@@ -22,14 +22,9 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  spec.add_development_dependency 'bundler', '~> 1.3'
24
24
  spec.add_development_dependency 'rake'
25
- spec.add_development_dependency 'pry'
26
- spec.add_development_dependency 'pry-remote'
27
- spec.add_development_dependency 'pry-nav'
28
- spec.add_development_dependency 'rspec', '~> 2.14'
29
25
  spec.add_development_dependency 'fakefs'
30
- spec.add_development_dependency 'rubocop'
31
26
 
32
- spec.add_dependency 'thor', '~> 0.18.1'
27
+ spec.add_dependency 'thor', '~> 0.19'
33
28
  spec.add_dependency 'awesome_print'
34
29
  spec.add_dependency 'activesupport'
35
30
  spec.add_dependency 'colored'
@@ -0,0 +1,61 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe ResponseMate::Commands::Inspect do
6
+ include_context 'stubbed_requests'
7
+
8
+ describe '#run' do
9
+ context 'when no keys are specified' do
10
+ it 'displays help message' do
11
+ error_output = nil
12
+
13
+ quietly do
14
+ error_output = capture(:stderr) do
15
+ ResponseMate::Commands::Inspect.new([], {}).run
16
+ end
17
+ end
18
+
19
+ expect(error_output.strip).to eq("At least one key has to be specified".red)
20
+ end
21
+ end
22
+
23
+ context 'when key is specified' do
24
+ let(:mock_request) do
25
+ ResponseMate::Request.new(key: 'user_issues',
26
+ request: {
27
+ verb: 'GET',
28
+ host: 'www.someapi.com',
29
+ path: '/user/42/issues' }).normalize!
30
+ end
31
+
32
+ let(:captured_output) do
33
+ output = nil
34
+
35
+ quietly do
36
+ output = capture(:stdout) do
37
+ ResponseMate::Commands::Inspect.new(['user_issues'], {}).run
38
+ end
39
+ end
40
+ output.strip
41
+ end
42
+
43
+ it 'displays the key, host, path, verb of the request' do
44
+ expect(captured_output).to include(mock_request.to_cli_format)
45
+ end
46
+
47
+ it 'includes the response status' do
48
+ expect(captured_output).
49
+ to include(":status => #{fake_response_user_issues[:status].to_s}")
50
+ end
51
+
52
+ it 'includes the response body' do
53
+ expect(captured_output).to include(%(:body => "#{fake_response_user_issues[:body]}"))
54
+ end
55
+
56
+ it 'includes the response headers' do
57
+ expect(captured_output).to include(fake_response_user_issues[:headers].values.last)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,53 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe ResponseMate::Commands::List do
6
+ include_context 'stubbed_requests'
7
+
8
+ subject { ResponseMate::Commands::List.new([], {}) }
9
+ let(:cmd_output) { capture(:stdout) { subject.run } }
10
+
11
+ describe '#run' do
12
+ before { subject.stub(:ask_action).and_return(:no) }
13
+
14
+ it 'lists all available keys' do
15
+ expect(cmd_output).to eq("user_issues\nuser_friends\n\n")
16
+ end
17
+
18
+ context 'when record is selected' do
19
+ before { subject.stub(:ask_action).and_return(:record) }
20
+
21
+ context 'and the 2nd key is selected' do
22
+ before { subject.stub(:ask_key).and_return('user_friends') }
23
+
24
+ it 'is recorded' do
25
+ quietly { cmd_output }
26
+ expect(File.basename(output_files.call.last)).to eq('user_friends.yml')
27
+ end
28
+ end
29
+ end
30
+
31
+ context 'when inspect is selected' do
32
+ before { subject.stub(:ask_action).and_return(:inspect) }
33
+
34
+ context 'and the 2nd key is selected' do
35
+ before { subject.stub(:ask_key).and_return('user_friends') }
36
+
37
+ describe 'inspection' do
38
+ it 'contains the response status' do
39
+ expect(cmd_output).to match(/:status/)
40
+ end
41
+
42
+ it 'contains the response headers' do
43
+ expect(cmd_output).to match(/:headers/)
44
+ end
45
+
46
+ it 'contains the response body' do
47
+ expect(cmd_output).to match(/:body/)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,91 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe ResponseMate::Commands::Record do
6
+ include_context 'stubbed_requests'
7
+
8
+ describe '#run' do
9
+ context 'with keys option unspecified' do
10
+ before do
11
+ quietly { ResponseMate::Commands::Record.new([], { keys: [] }).run }
12
+ end
13
+
14
+ describe 'output files' do
15
+ it 'creates on for each request' do
16
+ expect(output_files.call).to have_exactly(2).items
17
+ end
18
+ end
19
+ end
20
+
21
+ context 'with keys option specified' do
22
+ context 'when the requested key exists' do
23
+ before do
24
+ quietly do
25
+ ResponseMate::Commands::Record.new([], { keys: ['user_issues'] }).run
26
+ end
27
+ end
28
+
29
+ it 'creates an output response file' do
30
+ expect(output_files.call).to have_exactly(1).items
31
+ end
32
+
33
+ describe 'output response file' do
34
+ let(:output_filename) do
35
+ File.join(ResponseMate.configuration.output_dir, 'user_issues.yml')
36
+ end
37
+
38
+ it 'has the right file extension' do
39
+ expect(File.exists?(output_filename)).to be_true
40
+ end
41
+
42
+ describe 'YAML content' do
43
+ let(:output_content) { File.read(output_filename) }
44
+ subject { YAML.load(output_content) }
45
+
46
+ it 'is valid' do
47
+ expect { subject }.to_not raise_error
48
+ end
49
+
50
+ it 'contains the original request verb' do
51
+ expect(subject[:request][:verb]).to be_present
52
+ end
53
+
54
+ it 'contains the original request path' do
55
+ pending
56
+ end
57
+
58
+ it 'contains the original request params' do
59
+ pending
60
+ end
61
+
62
+ it 'contains the response status' do
63
+ expect(subject[:response][:status]).
64
+ to eql(fake_response_user_issues[:status])
65
+ end
66
+
67
+ it 'includes the response headers' do
68
+ expect(subject[:response][:headers]).
69
+ to eql(fake_response_user_issues[:headers])
70
+ end
71
+
72
+ it 'includes the response body' do
73
+ expect(subject[:response][:body]).
74
+ to eql(fake_response_user_issues[:body])
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ context 'when the requested key does not exist' do
81
+ subject do
82
+ ResponseMate::Commands::Record.new([], { keys: ['non_existing_key'] }).run
83
+ end
84
+
85
+ it 'raises ResponseMate::KeysNotFound'do
86
+ expect { subject }.to raise_error(ResponseMate::KeysNotFound)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -13,10 +13,6 @@ describe ResponseMate::Configuration do
13
13
  it 'assigns @requests_manifest' do
14
14
  expect(subject.requests_manifest).to be_present
15
15
  end
16
-
17
- it 'assigns @oauth_manifest' do
18
- expect(subject.oauth_manifest).to be_present
19
- end
20
16
  end
21
17
  end
22
18
 
@@ -45,7 +41,6 @@ describe ResponseMate do
45
41
  ResponseMate.setup do |config|
46
42
  config.output_dir = 'foo'
47
43
  config.requests_manifest = 'bar'
48
- config.oauth_manifest = 'koko'
49
44
  end
50
45
  end
51
46
 
@@ -53,14 +48,12 @@ describe ResponseMate do
53
48
  ResponseMate.setup do |config|
54
49
  config.output_dir = './output/responses/'
55
50
  config.requests_manifest = './requests.yml.erb'
56
- config.oauth_manifest = './oauth.yml'
57
51
  end
58
52
  end
59
53
 
60
54
  it 'properly assigns ivars' do
61
55
  expect(ResponseMate.configuration.output_dir).to eq('foo')
62
56
  expect(ResponseMate.configuration.requests_manifest).to eq('bar')
63
- expect(ResponseMate.configuration.oauth_manifest).to eq('koko')
64
57
  end
65
58
  end
66
59
  end
@@ -1,104 +1,74 @@
1
1
  # coding: utf-8
2
2
  require 'spec_helper'
3
3
 
4
- # TODO: This spec needs some love
5
- describe ResponseMate::Recorder, fakefs: true do
6
- pending '.initialize' do
7
- let(:subject) { ResponseMate::Recorder.new }
8
-
9
- context 'args[:requests_manifest] is present' do
10
- let(:subject) { ResponseMate::Recorder.new(requests_manifest: 'lala.yml') }
11
-
12
- it 'assigns @requests_manifest to that argument' do
13
- expect(subject.requests_manifest).to eq('lala.yml')
14
- end
15
- end
16
-
17
- pending 'args[:requests_manifest] is not present' do
18
- let(:subject) { ResponseMate::Recorder.new }
4
+ describe ResponseMate::Recorder do
5
+ include_context 'stubbed_requests'
6
+
7
+ let(:user_issues_request) do
8
+ ResponseMate::Request.new(
9
+ key: 'user_issues',
10
+ request: {
11
+ url: 'www.someapi.com/user/42/issues'
12
+ }).normalize!
13
+ end
19
14
 
20
- it 'assigns @requests_manifest to ResponseMate.configuration.requests_manifest' do
21
- expect(subject.requests_manifest).
22
- to eq(ResponseMate.configuration.requests_manifest)
23
- end
24
- end
15
+ let(:user_friends_request) do
16
+ ResponseMate::Request.new(
17
+ key: 'user_friends',
18
+ request: {
19
+ url: 'www.someapi.com/user/42/friends',
20
+ params: {
21
+ trusty: 'yes',
22
+ since: 'childhood'
23
+ }
24
+ }).normalize!
25
+ end
25
26
 
26
- context 'args[:base_url] is present' do
27
- let(:subject) { ResponseMate::Recorder.new(base_url: 'http://example.com') }
27
+ let(:requests) { [user_issues_request, user_friends_request] }
28
+ let(:manifest) { double(requests: requests) }
28
29
 
29
- it 'assigns @base_url to that argument' do
30
- expect(subject.base_url).to eq('http://example.com')
31
- end
32
- end
30
+ describe '.initialize' do
31
+ context 'when initialization option :manifest is present' do
32
+ let(:subject) { ResponseMate::Recorder.new(manifest: manifest) }
33
33
 
34
- context 'args[:base_url] is not present' do
35
- it 'assigns @base_url to the one found in the manifest' do
36
- expect(subject.base_url).to eq('http://koko.com')
34
+ it 'assigns manifest to that argument' do
35
+ expect(subject.manifest).to eq(manifest)
37
36
  end
38
37
  end
39
38
 
40
- it 'calls #parse_requests_manifest' do
41
- ResponseMate::Recorder.any_instance.should_receive :parse_requests_manifest
42
- subject
43
- end
44
-
45
- it 'calls #initialize_connection' do
46
- ResponseMate::Recorder.any_instance.should_receive :initialize_connection
47
- subject
39
+ it 'creates a new instance of ResponseMate::Connection' do
40
+ expect(subject.conn).to be_a(ResponseMate::Connection)
48
41
  end
49
42
  end
50
43
 
51
- pending '#record', fakefs: false do
52
- before do
53
- FakeFS.deactivate!
54
- FileUtils.cp 'spec/fixtures/two_keys.yml.erb', 'two_keys.yml.erb'
55
- ResponseMate::Oauth.any_instance.stub(:token).and_return 'some_token'
56
- end
44
+ describe '#record' do
45
+ let(:subject) { ResponseMate::Recorder.new(manifest: manifest) }
57
46
 
58
- after do
59
- FileUtils.rm ['two_keys.yml.erb'] + Dir.glob('output/responses/*.yml')
47
+ before do
48
+ allow(manifest).to receive(:requests_for_keys).and_return([user_issues_request])
60
49
  end
61
50
 
62
- let(:subject) { ResponseMate::Recorder.new(requests_manifest: 'two_keys.yml.erb') }
63
-
64
- describe 'output file' do
65
- before do
66
- subject.stub(:fetch).and_return(double(status: 200, headers: 'koko', body: 'foo'))
67
- subject.record
68
- end
69
-
70
- let(:response) { YAML::load_file 'output/responses/one.yml' }
71
-
72
- it 'is named after the recording key and ends in .yml' do
73
- expect(Dir.glob('output/responses/*.yml').map { |f| File.basename f }).
74
- to eq(%w(one.yml two.yml))
75
- end
76
-
77
- it 'contains response status' do
78
- expect(response[:status]).to eq(200)
79
- end
80
-
81
- it 'contains response headers' do
82
- expect(response[:headers]).to eq('koko')
51
+ context 'with a single key specified' do
52
+ it 'only records this key' do
53
+ subject.should_receive(:process).with(user_issues_request)
54
+ subject.record('user_issues')
83
55
  end
56
+ end
84
57
 
85
- it 'contains response body' do
86
- expect(response[:body]).to eq('foo')
58
+ context 'with no keys specified' do
59
+ it 'records all keys in the manifest' do
60
+ quietly { subject.record([]) }
61
+ expect(output_files.call).to have_exactly(2).items
87
62
  end
63
+ end
64
+ end
88
65
 
89
- describe 'request' do
90
- it 'contains verb' do
91
- expect(response[:request][:verb]).to be_present
92
- end
93
-
94
- it 'contains path' do
95
- expect(response[:request][:path]).to be_present
96
- end
66
+ describe '#process' do
67
+ let(:subject) { ResponseMate::Recorder.new(manifest: manifest) }
97
68
 
98
- it 'contains params' do
99
- expect(response[:request][:params]).to be_present
100
- end
101
- end
69
+ it 'writes a new ResponseMate::Tape for the specified request' do
70
+ ResponseMate::Tape.any_instance.should_receive(:write)
71
+ quietly { subject.send :process, user_issues_request }
102
72
  end
103
73
  end
104
74
  end
@@ -0,0 +1 @@
1
+ oauth_token: some_valid_token
@@ -0,0 +1,20 @@
1
+ default_headers: &default_headers
2
+ accept: 'application/vnd.someapi+json; version=3'
3
+ authorization: 'Bearer {{oauth_token}}'
4
+
5
+ requests:
6
+ -
7
+ key: user_issues
8
+ request:
9
+ url: 'www.someapi.com/user/42/issues'
10
+ headers:
11
+ <<: *default_headers
12
+
13
+ - key: user_friends
14
+ request:
15
+ url: 'www.someapi.com/user/42/friends'
16
+ headers:
17
+ <<: *default_headers
18
+ params:
19
+ trusty: 'yes'
20
+ since: 'childhood'
File without changes