response_mate 0.1.14 → 0.2.0

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.
@@ -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