hyperclient 0.0.8 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,39 @@
1
+ require_relative '../test_helper'
2
+ require 'hyperclient/resource'
3
+
4
+ module Hyperclient
5
+ describe Collection do
6
+ let(:representation) do
7
+ JSON.parse( File.read('test/fixtures/element.json'))
8
+ end
9
+
10
+ let(:collection) do
11
+ Collection.new(representation)
12
+ end
13
+
14
+ it 'exposes the collection as methods' do
15
+ collection.title.must_equal 'Real World ASP.NET MVC3'
16
+ collection.description.must_match(/production/)
17
+ collection.permitted.must_equal true
18
+ end
19
+
20
+ it 'exposes collection as a hash' do
21
+ collection['title'].must_equal 'Real World ASP.NET MVC3'
22
+ collection['description'].must_match(/production/)
23
+ collection['permitted'].must_equal true
24
+ end
25
+
26
+ it 'correctly responds to methods' do
27
+ collection.must_respond_to :title
28
+ end
29
+
30
+ it 'acts as enumerable' do
31
+ names = collection.map do |name, value|
32
+ name
33
+ end
34
+
35
+ names.must_equal ['_links', 'title', 'description', 'permitted', '_embedded']
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,51 @@
1
+ require_relative '../test_helper'
2
+ require 'hyperclient/entry_point'
3
+
4
+ module Hyperclient
5
+ describe EntryPoint do
6
+ let(:api) do
7
+ EntryPoint.new 'http://my.api.org'
8
+ end
9
+
10
+ before do
11
+ stub_request(:get, "http://my.api.org/").
12
+ to_return(body: '{"_links": {"self": {"href": "http://my.api.org"}}}', headers: {content_type: 'application/json'})
13
+ end
14
+
15
+ describe 'initialize' do
16
+ it 'initializes a Resource at the entry point' do
17
+ api.links['self'].url.must_equal 'http://my.api.org'
18
+ end
19
+
20
+ it 'setups the HTTP config' do
21
+ options = {:headers => {'accept-encoding' => 'deflate, gzip'}}
22
+
23
+ api = EntryPoint.new('http://my.api.org', options)
24
+
25
+ api.config[:headers].must_include 'accept-encoding'
26
+ end
27
+
28
+ it 'sets the base_uri for HTTP' do
29
+ api = EntryPoint.new('http://my.api.org')
30
+
31
+ api.config[:base_uri].must_equal 'http://my.api.org'
32
+ end
33
+ end
34
+
35
+ describe 'method missing' do
36
+ it 'delegates undefined methods to the API when they exist' do
37
+ Resource.any_instance.expects(:foo).returns 'foo'
38
+ api.foo.must_equal 'foo'
39
+ end
40
+
41
+ it 'responds to missing methods' do
42
+ Resource.any_instance.expects(:respond_to?).with('foo').returns(true)
43
+ api.respond_to?(:foo).must_equal true
44
+ end
45
+
46
+ it 'raises an error when the method does not exist in the API' do
47
+ lambda { api.this_method_does_not_exist }.must_raise(NoMethodError)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -3,33 +3,47 @@ require 'hyperclient/http'
3
3
 
4
4
  module Hyperclient
5
5
  describe HTTP do
6
- let (:resource) do
7
- resource = MiniTest::Mock.new
8
- resource.expect(:url, 'http://api.example.org/productions/1')
6
+ let(:url) do
7
+ '/productions/1'
9
8
  end
10
9
 
11
- let (:http) do
10
+ let(:config) { {base_uri: 'http://api.example.org'} }
11
+
12
+ let(:http) do
12
13
  HTTP.instance_variable_set("@default_options", {})
13
- HTTP.new(resource)
14
+ HTTP.new(url, config)
15
+ end
16
+
17
+ describe 'url' do
18
+ it 'merges the resource url with the base uri' do
19
+ http.url.to_s.must_equal 'http://api.example.org/productions/1'
20
+ end
21
+
22
+ it 'returns the given url if it cannot merge it' do
23
+ config = {base_uri: nil}
24
+ http = HTTP.new(url, config)
25
+ http.url.to_s.must_equal '/productions/1'
26
+ end
14
27
  end
15
28
 
16
29
  describe 'authentication' do
17
30
  it 'sets the authentication options' do
18
- stub_request(:get, 'user:pass@api.example.org/productions/1').
19
- to_return(body: 'This is the resource')
31
+ stub_request(:get, 'http://user:pass@api.example.org/productions/1').
32
+ to_return(body: '{"resource": "This is the resource"}')
33
+
34
+ config.update({auth: {type: :basic, user: 'user', password: 'pass'}})
20
35
 
21
- http = HTTP.new(resource, {auth: {type: :basic, credentials: ['user','pass']}})
22
- http.get.must_equal 'This is the resource'
36
+ http.get.must_equal({'resource' => 'This is the resource'})
23
37
  end
24
38
  end
25
39
 
26
40
  describe 'headers' do
27
41
  it 'sets headers from the given option' do
28
- http = HTTP.new(resource, {headers: {'accept-encoding' => 'deflate, gzip'}})
42
+ config.update({headers: {'accept-encoding' => 'deflate, gzip'}})
29
43
 
30
- stub_request(:get, 'api.example.org/productions/1').
44
+ stub_request(:get, 'http://api.example.org/productions/1').
31
45
  with(headers: {'Accept-Encoding' => 'deflate, gzip'}).
32
- to_return(body: 'This is the resource')
46
+ to_return(body: '{"resource": "This is the resource"}')
33
47
 
34
48
  http.get
35
49
  end
@@ -37,14 +51,14 @@ module Hyperclient
37
51
 
38
52
  describe 'debug' do
39
53
  it 'enables debugging' do
40
- http = HTTP.new(resource, {debug: true})
54
+ config.update({debug: true})
41
55
 
42
56
  http.class.instance_variable_get(:@default_options)[:debug_output].must_equal $stderr
43
57
  end
44
58
 
45
59
  it 'uses a custom stream' do
46
60
  stream = StringIO.new
47
- http = HTTP.new(resource, {debug: stream})
61
+ config.update({debug: stream})
48
62
 
49
63
  http.class.instance_variable_get(:@default_options)[:debug_output].must_equal stream
50
64
  end
@@ -52,14 +66,14 @@ module Hyperclient
52
66
 
53
67
  describe 'get' do
54
68
  it 'sends a GET request and returns the response body' do
55
- stub_request(:get, 'api.example.org/productions/1').
56
- to_return(body: 'This is the resource')
69
+ stub_request(:get, 'http://api.example.org/productions/1').
70
+ to_return(body: '{"resource": "This is the resource"}')
57
71
 
58
- http.get.must_equal 'This is the resource'
72
+ http.get.must_equal({'resource' => 'This is the resource'})
59
73
  end
60
74
 
61
75
  it 'returns the parsed response' do
62
- stub_request(:get, 'api.example.org/productions/1').
76
+ stub_request(:get, 'http://api.example.org/productions/1').
63
77
  to_return(body: '{"some_json": 12345 }', headers: {content_type: 'application/json'})
64
78
 
65
79
  http.get.must_equal({'some_json' => 12345})
@@ -68,7 +82,7 @@ module Hyperclient
68
82
 
69
83
  describe 'post' do
70
84
  it 'sends a POST request' do
71
- stub_request(:post, 'api.example.org/productions/1').
85
+ stub_request(:post, 'http://api.example.org/productions/1').
72
86
  to_return(body: 'Posting like a big boy huh?', status: 201)
73
87
 
74
88
  response = http.post({data: 'foo'})
@@ -80,7 +94,7 @@ module Hyperclient
80
94
 
81
95
  describe 'put' do
82
96
  it 'sends a PUT request' do
83
- stub_request(:put, 'api.example.org/productions/1').
97
+ stub_request(:put, 'http://api.example.org/productions/1').
84
98
  to_return(body: 'No changes were made', status: 204)
85
99
 
86
100
  response = http.put({attribute: 'changed'})
@@ -92,7 +106,7 @@ module Hyperclient
92
106
 
93
107
  describe 'options' do
94
108
  it 'sends a OPTIONS request' do
95
- stub_request(:options, 'api.example.org/productions/1').
109
+ stub_request(:options, 'http://api.example.org/productions/1').
96
110
  to_return(status: 200, headers: {allow: 'GET, POST'})
97
111
 
98
112
  response = http.options
@@ -102,7 +116,7 @@ module Hyperclient
102
116
 
103
117
  describe 'head' do
104
118
  it 'sends a HEAD request' do
105
- stub_request(:head, 'api.example.org/productions/1').
119
+ stub_request(:head, 'http://api.example.org/productions/1').
106
120
  to_return(status: 200, headers: {content_type: 'application/json'})
107
121
 
108
122
  response = http.head
@@ -112,7 +126,7 @@ module Hyperclient
112
126
 
113
127
  describe 'delete' do
114
128
  it 'sends a DELETE request' do
115
- stub_request(:delete, 'api.example.org/productions/1').
129
+ stub_request(:delete, 'http://api.example.org/productions/1').
116
130
  to_return(body: 'Resource deleted', status: 200)
117
131
 
118
132
  response = http.delete
@@ -0,0 +1,29 @@
1
+ require_relative '../test_helper'
2
+ require 'hyperclient/link_collection'
3
+
4
+ module Hyperclient
5
+ describe LinkCollection do
6
+ let(:entry_point) { stub('Entry point', config: {base_uri: '/'}) }
7
+
8
+ let(:representation) do
9
+ JSON.parse( File.read('test/fixtures/element.json'))
10
+ end
11
+
12
+ let(:links) do
13
+ LinkCollection.new(representation['_links'], entry_point)
14
+ end
15
+
16
+ it 'is a collection' do
17
+ LinkCollection.ancestors.must_include Collection
18
+ end
19
+
20
+ it 'initializes the collection with links' do
21
+ links.must_respond_to :filter
22
+ end
23
+
24
+ it 'returns link objects for each link' do
25
+ links.filter.must_be_kind_of Link
26
+ links['self'].must_be_kind_of Link
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,93 @@
1
+ require_relative '../test_helper'
2
+ require 'hyperclient/link'
3
+
4
+ module Hyperclient
5
+ describe Link do
6
+ let(:entry_point) { stub('Entry point', config: {base_uri: '/'}) }
7
+
8
+ describe 'templated?' do
9
+ it 'returns true if the link is templated' do
10
+ link = Link.new({'templated' => true}, entry_point)
11
+
12
+ link.templated?.must_equal true
13
+ end
14
+
15
+ it 'returns false if the link is not templated' do
16
+ link = Link.new({}, entry_point)
17
+
18
+ link.templated?.must_equal false
19
+ end
20
+ end
21
+
22
+ describe 'resource' do
23
+ let(:http) { mock('HTTP', get: {}) }
24
+
25
+ it 'builds a resource with the hyperlink representation' do
26
+ HTTP.expects(:new).returns(http, {})
27
+ Resource.expects(:new).with({}, entry_point)
28
+
29
+ Link.new({}, entry_point).resource
30
+ end
31
+ end
32
+
33
+ describe 'expand' do
34
+ it 'buils a Link with the templated URI representation' do
35
+ link = Link.new({'href' => '/orders{?id}', 'templated' => true}, entry_point)
36
+
37
+ Link.expects(:new).with(anything, entry_point, {id: '1'})
38
+ link.expand(id: '1')
39
+ end
40
+
41
+ it 'raises if no uri variables are given' do
42
+ link = Link.new({'href' => '/orders{?id}', 'templated' => true}, entry_point)
43
+
44
+ proc { link.resource }.must_raise MissingURITemplateVariablesException
45
+ end
46
+ end
47
+
48
+ describe 'url' do
49
+ it 'raises when missing required uri_variables' do
50
+ link = Link.new({'href' => '/orders{?id}', 'templated' => true}, entry_point)
51
+
52
+ lambda { link.url }.must_raise MissingURITemplateVariablesException
53
+ end
54
+
55
+ it 'expands an uri template with variables' do
56
+ link = Link.new({'href' => '/orders{?id}', 'templated' => true}, entry_point, {id: 1})
57
+
58
+ link.url.must_equal '/orders?id=1'
59
+ end
60
+
61
+ it 'returns the link when no uri template' do
62
+ link = Link.new({'href' => '/orders'}, entry_point)
63
+ link.url.must_equal '/orders'
64
+ end
65
+ end
66
+
67
+ describe 'method_missing' do
68
+ before do
69
+ stub_request(:get, "http://myapi.org/orders").
70
+ to_return(body: '{"resource": "This is the resource"}')
71
+ Resource.expects(:new).returns(resource).at_least_once
72
+ end
73
+
74
+ let(:link) { Link.new({'href' => 'http://myapi.org/orders'}, entry_point) }
75
+ let(:resource) { mock('Resource') }
76
+
77
+ it 'delegates unkown methods to the resource' do
78
+ resource.expects(:embedded)
79
+
80
+ link.embedded
81
+ end
82
+
83
+ it 'raises an error when the method does not exist in the resource' do
84
+ lambda { link.this_method_does_not_exist }.must_raise(NoMethodError)
85
+ end
86
+
87
+ it 'responds to missing methods' do
88
+ resource.expects(:respond_to?).with('embedded').returns(true)
89
+ link.respond_to?(:embedded).must_equal true
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,34 @@
1
+ require_relative '../test_helper'
2
+ require 'hyperclient/resource_collection'
3
+
4
+ module Hyperclient
5
+ describe ResourceCollection do
6
+ let(:entry_point) { stub('Entry point', config: {base_uri: '/'}) }
7
+
8
+ let(:representation) do
9
+ JSON.parse( File.read('test/fixtures/element.json'))
10
+ end
11
+
12
+ let(:resources) do
13
+ ResourceCollection.new(representation['_embedded'], entry_point)
14
+ end
15
+
16
+ it 'is a collection' do
17
+ ResourceCollection.ancestors.must_include Collection
18
+ end
19
+
20
+ it 'initializes the collection with resources' do
21
+ resources.must_respond_to :author
22
+ resources.must_respond_to :episodes
23
+ end
24
+
25
+ it 'returns resource objects for each resource' do
26
+ resources.author.must_be_kind_of Resource
27
+ end
28
+
29
+ it 'also builds arras of resource' do
30
+ resources.episodes.must_be_kind_of Array
31
+ resources.episodes.first.must_be_kind_of Resource
32
+ end
33
+ end
34
+ end
@@ -3,66 +3,60 @@ require 'hyperclient/resource'
3
3
 
4
4
  module Hyperclient
5
5
  describe Resource do
6
- let(:representation) do
7
- File.read('test/fixtures/element.json')
8
- end
9
-
10
- let(:parsed_representation) do
11
- JSON.parse(representation)
12
- end
6
+ let(:entry_point) { mock('Entry point') }
13
7
 
14
- before do
15
- Resource.entry_point = 'http://api.example.org'
16
- end
8
+ describe 'initialize' do
9
+ it 'initializes its links' do
10
+ LinkCollection.expects(:new).with({"self" => { "href" => "/orders/523" }}, entry_point)
17
11
 
18
- describe 'url' do
19
- it 'merges the resource url with the entry point' do
20
- resource = Resource.new('/path/to/resource')
21
- resource.url.to_s.must_equal 'http://api.example.org/path/to/resource'
12
+ Resource.new({'_links' => {"self" => { "href" => "/orders/523" } }}, entry_point)
22
13
  end
23
14
 
24
- it 'returns the given url if it cannot merge it' do
25
- resource = Resource.new('/search={terms}')
26
- resource.url.to_s.must_equal '/search={terms}'
27
- end
28
- end
15
+ it 'initializes its attributes' do
16
+ Attributes.expects(:new).with({foo: :bar})
29
17
 
30
- describe 'initialize' do
31
- before do
32
- stub_request(:get, 'http://api.example.org')
18
+ Resource.new({foo: :bar}, entry_point)
33
19
  end
34
20
 
35
- it 'initializes the representation when one is given' do
36
- resource = Resource.new('/', {representation: JSON.parse(representation)})
21
+ it 'initializes links' do
22
+ ResourceCollection.expects(:new).with({"orders" => []}, entry_point)
37
23
 
38
- assert_not_requested(:get, 'http://api.example.org/')
24
+ Resource.new({'_embedded' => {"orders" => [] }}, entry_point)
39
25
  end
26
+ end
40
27
 
41
- it 'sets the resource name' do
42
- resource = Resource.new('/', {name: 'posts'})
43
-
44
- resource.name.must_equal 'posts'
28
+ describe 'accessors' do
29
+ let(:resource) do
30
+ Resource.new({}, entry_point)
45
31
  end
46
- end
47
32
 
48
- describe 'reload' do
49
- before do
50
- stub_request(:get, "http://api.example.org/productions/1").
51
- to_return(:status => 200, :body => representation, headers: {content_type: 'application/json'})
33
+ describe 'links' do
34
+ it 'returns a LinkCollection' do
35
+ resource.links.must_be_kind_of LinkCollection
36
+ end
52
37
  end
53
38
 
54
- it 'retrives itself from the API' do
55
- resource = Resource.new('/productions/1')
56
- resource.reload
39
+ describe 'attributes' do
40
+ it 'returns a Attributes' do
41
+ resource.attributes.must_be_kind_of Attributes
42
+ end
43
+ end
57
44
 
58
- assert_requested(:get, 'http://api.example.org/productions/1', times: 1)
45
+ describe 'embedded' do
46
+ it 'returns a ResourceCollection' do
47
+ resource.embedded.must_be_kind_of ResourceCollection
48
+ end
59
49
  end
50
+ end
60
51
 
61
- it 'returns itself' do
62
- resource = Resource.new('/productions/1')
52
+ it 'uses its self Link to handle HTTP connections' do
53
+ self_link = mock('Self Link')
54
+ self_link.expects(:get)
63
55
 
64
- resource.reload.must_equal resource
65
- end
56
+ LinkCollection.expects(:new).returns({'self' => self_link})
57
+ resource = Resource.new({}, entry_point)
58
+
59
+ resource.get
66
60
  end
67
61
  end
68
62
  end