hyperspec 0.0.1 → 0.0.2

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.
data/README.md CHANGED
@@ -1,5 +1,3 @@
1
- # This is Work in Progress
2
-
3
1
  __ __ ______
4
2
  / / / /_ _________________/ ____/________________
5
3
  / /_/ / / / / __ \ _ \_ __/____ \/ __ \ _ \ ___/
@@ -14,42 +12,44 @@ HTTP APIs from the outside.
14
12
 
15
13
  ## Example
16
14
 
17
- service "http://localhost:4567" do
18
- def response_json
19
- JSON.parse(response.body)
20
- end
21
-
22
- resource "/lolz" do
23
- get do
24
- it { responds_with.status :ok }
25
- it { response_json['lolz'].must_be_instance_of Array }
26
-
27
- with_query("q=monorail") do
28
- it "only lists lolz that match the query" do
29
- response_json['lolz'].wont_be_empty
30
- response_json['lolz'].each do |lol|
31
- lol['title'].must_match /monorail/
32
- end
33
- end
15
+ ```ruby
16
+ service "http://localhost:4567" do
17
+ def response_json
18
+ JSON.parse(response.body)
19
+ end
20
+
21
+ resource "/lolz" do
22
+ get do
23
+ it { responds_with.status :ok }
24
+ it { response_json['lolz'].must_be_instance_of Array }
25
+
26
+ with_query("q=monorail") do
27
+ it "only lists lolz that match the query" do
28
+ response_json['lolz'].wont_be_empty
29
+ response_json['lolz'].each do |lol|
30
+ lol['title'].must_match /monorail/
34
31
  end
35
32
  end
33
+ end
34
+ end
36
35
 
37
- post do
38
- describe "without request body" do
39
- it { responds_with.status :unprocessable_entity }
40
- end
36
+ post do
37
+ describe "without request body" do
38
+ it { responds_with.status :unprocessable_entity }
39
+ end
41
40
 
42
- describe "with request body" do
43
- with_headers({ 'Content-Type' => 'application/json' }) do
44
- with_request_body({ "title" => "Roflcopter!" }.to_json) do
45
- it { responds_with.status :created }
46
- it { response_json['lol']['title'].must_equal 'Roflcopter!' }
47
- end
48
- end
41
+ describe "with request body" do
42
+ with_headers({ 'Content-Type' => 'application/json' }) do
43
+ with_request_body({ "title" => "Roflcopter!" }.to_json) do
44
+ it { responds_with.status :created }
45
+ it { response_json['lol']['title'].must_equal 'Roflcopter!' }
49
46
  end
50
47
  end
51
48
  end
52
49
  end
50
+ end
51
+ end
52
+ ```
53
53
 
54
54
  ## Concepts
55
55
 
@@ -99,6 +99,10 @@ Allows for comparing against named status code symbols.
99
99
 
100
100
  - DSL for matching representations.
101
101
 
102
+ ### Documentation
103
+
104
+ - Adding an output format for docs.
105
+
102
106
  ## Concerns
103
107
 
104
108
  - Efficient ways of building up and verifying precondition state.
@@ -107,12 +111,24 @@ Allows for comparing against named status code symbols.
107
111
 
108
112
  ## Acknowledgments
109
113
 
110
- Thanks to Daniel Bornkessel for inspiring me to do README driven development.
114
+ Thanks to:
115
+
116
+ - [Daniel Bornkessel](https://github.com/kesselborn) for inspiring me to do `README` driven development.
117
+ - [Matthias Georgi](https://github.com/georgi) for suggestions for code improvements.
118
+ - [Lars Gierth](https://github.com/lgierth) for updating the example server to respond with 415 Unsupported Media Type in the appropriate case.
119
+ - [Anton Lindqvist](https://github.com/mptre) for adding HTTP Basic Auth
111
120
 
112
121
  ## History
113
122
 
114
- ### 2012 02 07
123
+ ### 0.0.2
124
+
125
+ #### 2012 03 30
126
+
127
+ - Correcting environment dependencies.
128
+ - Adding support for HTTP Basic Auth.
129
+
130
+ ### 0.0.1
115
131
 
116
- #### 0.0.1
132
+ #### 2012 02 07
117
133
 
118
134
  - Initial release
@@ -18,6 +18,6 @@ post "/lolz" do
18
18
  %^{"lol": {"title": "Roflcopter!"}}^
19
19
  end
20
20
  else
21
- status 422
21
+ status 415
22
22
  end
23
23
  end
@@ -38,7 +38,7 @@ Gem::Specification.new do |s|
38
38
  s.add_development_dependency *dependency
39
39
  end
40
40
 
41
- (dependencies + developement_dependencies).each do |dependency|
41
+ (dependencies + runtime_dependencies).each do |dependency|
42
42
  s.add_runtime_dependency *dependency
43
43
  end
44
44
  end
@@ -10,75 +10,71 @@ module HyperSpec
10
10
  module ObjectExtensions
11
11
  private
12
12
  def service(desc, additional_desc = nil, &block)
13
- cls = describe(desc, additional_desc, &block)
14
- cls.send(:define_method, :base_uri) { URI.parse(desc) }
15
- cls.send(:define_method, :headers) { {} }
16
- cls.send(:define_method, :request_body) { "" }
17
- cls
13
+ describe(desc, additional_desc, &block).tap do |cls|
14
+ cls.class_eval do
15
+ define_method(:base_uri) { URI.parse(desc) }
16
+ define_method(:headers) { {} }
17
+ define_method(:request_body) { "" }
18
+ end
19
+ end
18
20
  end
19
21
 
20
22
  def resource(path, additional_desc = nil, &block)
21
- cls = describe(desc, additional_desc, &block)
22
-
23
- cls.send(:define_method, :base_uri) do
24
- s = super()
25
- s.path = [ s.path, path ].reject(&:empty?).join("/")
26
- s
23
+ describe(desc, additional_desc, &block).tap do |cls|
24
+ cls.class_eval do
25
+ define_method(:base_uri) do
26
+ super().tap do |s|
27
+ s.path = [ s.path, path ].reject(&:empty?).join("/")
28
+ end
29
+ end
30
+ end
27
31
  end
28
- cls
29
32
  end
30
33
 
31
34
  def with_headers(hash, additional_desc = nil, &block)
32
- cls = describe("with headers", additional_desc, &block)
33
- cls.send(:define_method, :headers) { super().merge(hash) }
34
- cls
35
+ describe("with headers", additional_desc, &block).tap do |cls|
36
+ cls.class_eval do
37
+ define_method(:headers) { super().merge(hash) }
38
+ end
39
+ end
35
40
  end
36
41
 
37
42
  def with_query(string, additional_desc = nil, &block)
38
- cls = describe("with query", additional_desc, &block)
39
- cls.send(:define_method, :base_uri) do
40
- s = super()
41
- s.query = [ s.query.to_s, string ].reject(&:empty?).join("&")
42
- s
43
+ describe("with query", additional_desc, &block).tap do |cls|
44
+ cls.class_eval do
45
+ define_method(:base_uri) do
46
+ super().tap do |s|
47
+ s.query = [ s.query.to_s, string ].reject(&:empty?).join("&")
48
+ end
49
+ end
50
+ end
43
51
  end
44
- cls
45
52
  end
46
53
 
47
54
  def with_request_body(string, additional_desc = nil, &block)
48
- cls = describe("with request body", additional_desc, &block)
49
- cls.send(:define_method, :request_body) do
50
- string
55
+ describe("with request body", additional_desc, &block).tap do |cls|
56
+ cls.class_eval do
57
+ define_method(:request_body) { string }
58
+ end
51
59
  end
52
- cls
53
60
  end
54
61
 
55
62
  # HTTP method selection
56
63
  #
57
- def get(additional_desc = nil, &block)
58
- cls = describe('GET', additional_desc, &block)
59
- cls.instance_eval { |_| def request_type; :get; end }
60
- cls
61
- end
62
-
63
- def head(additional_desc = nil, &block)
64
- cls = describe('HEAD', additional_desc, &block)
65
- cls.instance_eval { |_| def request_type; :head; end }
66
- cls
67
- end
68
- def post(additional_desc = nil, &block)
69
- cls = describe('POST', additional_desc, &block)
70
- cls.instance_eval { |_| def request_type; :post; end }
71
- cls
72
- end
73
- def put(additional_desc = nil, &block)
74
- cls = describe('PUT', additional_desc, &block)
75
- cls.instance_eval { |_| def request_type; :put; end }
76
- cls
77
- end
78
- def delete(additional_desc = nil, &block)
79
- cls = describe('DELETE', additional_desc, &block)
80
- cls.instance_eval { |_| def request_type; :delete; end }
81
- cls
64
+ {
65
+ 'get' => Net::HTTP::Get,
66
+ 'head' => Net::HTTP::Head,
67
+ 'post' => Net::HTTP::Post,
68
+ 'put' => Net::HTTP::Put,
69
+ 'delete' => Net::HTTP::Delete,
70
+ }.each do |http_method, request_class|
71
+ define_method(http_method) do |additional_desc = nil, &block|
72
+ describe(http_method.upcase, additional_desc, &block).tap do |cls|
73
+ cls.class_eval do
74
+ define_method(:request_class) { request_class }
75
+ end
76
+ end
77
+ end
82
78
  end
83
79
  end
84
80
 
@@ -88,19 +84,14 @@ module HyperSpec
88
84
  do_request
89
85
  end
90
86
 
91
- def request_type
92
- self.class.request_type
93
- end
94
-
95
87
  def responds_with
96
88
  Have.new(response)
97
89
  end
98
90
 
99
91
  private
100
92
  def do_request
101
- klass = eval("Net::HTTP::#{request_type.to_s.gsub(/^\w/) { |c| c.upcase }}")
102
93
  @do_request ||=
103
- request_response(klass, base_uri, headers, request_body)
94
+ request_response(request_class, base_uri, headers, request_body)
104
95
  end
105
96
 
106
97
  def request_response(klass, uri, headers, body = '')
@@ -117,11 +108,13 @@ module HyperSpec
117
108
  req = klass.new(request_uri)
118
109
  headers.inject(req) { |m, (k, v)| m[k] = v; m }
119
110
  req.body = body if body
111
+ req.basic_auth(uri.user, uri.password) if uri.userinfo
120
112
  if headers['Content-Type']
121
113
  req.content_type = headers['Content-Type']
122
114
  end
123
115
  http.request(req)
124
116
  end
117
+
125
118
  Response.from_net_http_response(resp)
126
119
  end
127
120
  end
@@ -188,6 +181,5 @@ module HyperSpec
188
181
  end
189
182
  end
190
183
 
191
-
192
184
  ::Object.send(:include, HyperSpec::ObjectExtensions)
193
185
  ::MiniTest::Spec.send(:include, HyperSpec::MiniTest::SpecExtensions)
@@ -1,6 +1,6 @@
1
1
  module HyperSpec
2
2
  MAJOR = 0
3
3
  MINOR = 0
4
- PATCH = 1
4
+ PATCH = 2
5
5
  VERSION = [ MAJOR, MINOR, PATCH ].join(".")
6
6
  end
@@ -92,3 +92,44 @@
92
92
  body: |+
93
93
  {"is-this-json":true}
94
94
 
95
+ - !ruby/struct:VCR::HTTPInteraction
96
+ request: !ruby/struct:VCR::Request
97
+ method: :get
98
+ uri: http://username@localhost:80/secret
99
+ headers:
100
+ authorization:
101
+ - Basic dXNlcm5hbWU6cGFzc3dvcmQ=
102
+ body:
103
+ response: !ruby/struct:VCR::Response
104
+ http_version: "1.1"
105
+ status: !ruby/struct:VCR::ResponseStatus
106
+ code: 200
107
+ message: OK
108
+ headers:
109
+ content-length:
110
+ - "24"
111
+ content-type:
112
+ - application/json;charset=utf-8
113
+ body: |+
114
+ {"is-this-secret":true}
115
+
116
+ - !ruby/struct:VCR::HTTPInteraction
117
+ request: !ruby/struct:VCR::Request
118
+ method: :get
119
+ uri: http://username:password@localhost:80/secret
120
+ headers:
121
+ authorization:
122
+ - Basic dXNlcm5hbWU6cGFzc3dvcmQ=
123
+ body:
124
+ response: !ruby/struct:VCR::Response
125
+ http_version: "1.1"
126
+ status: !ruby/struct:VCR::ResponseStatus
127
+ code: 200
128
+ message: OK
129
+ headers:
130
+ content-length:
131
+ - "24"
132
+ content-type:
133
+ - application/json;charset=utf-8
134
+ body: |+
135
+ {"is-this-secret":true}
@@ -21,8 +21,8 @@ describe HyperSpec do
21
21
 
22
22
  use_vcr_cassette('localhost')
23
23
 
24
- it "should be of version 0.0.0" do
25
- HyperSpec::VERSION.must_equal "0.0.0"
24
+ it "should be of version 0.0.1" do
25
+ HyperSpec::VERSION.must_equal "0.0.1"
26
26
  end
27
27
 
28
28
  describe "MiniTest::Spec extensions" do
@@ -119,7 +119,13 @@ describe HyperSpec do
119
119
  it { subject.request_body.must_equal "lol[title]=Roflcopter" }
120
120
  end
121
121
 
122
- %w[ get head post put delete ].map(&:to_sym).each do |http_method|
122
+ {
123
+ 'get' => Net::HTTP::Get,
124
+ 'head' => Net::HTTP::Head,
125
+ 'post' => Net::HTTP::Post,
126
+ 'put' => Net::HTTP::Put,
127
+ 'delete' => Net::HTTP::Delete,
128
+ }.each do |http_method, request_class|
123
129
  describe "HTTP method selection" do
124
130
  subject do
125
131
  the_spec do |bound|
@@ -131,7 +137,7 @@ describe HyperSpec do
131
137
  end
132
138
  end
133
139
 
134
- it { subject.request_type.must_equal http_method }
140
+ it { subject.request_class.must_equal request_class }
135
141
  end
136
142
  end
137
143
 
@@ -198,5 +204,35 @@ describe HyperSpec do
198
204
  it { subject.status_code 200 }
199
205
  it { subject.status :ok }
200
206
  end
207
+
208
+ describe "basic auth" do
209
+ describe "with username" do
210
+ subject do
211
+ the_spec do |bound|
212
+ service("http://username@localhost") do
213
+ resource("/secret") do
214
+ bound.value = get {}
215
+ end
216
+ end
217
+ end.response
218
+ end
219
+
220
+ it { subject.status_code.must_equal 200 }
221
+ end
222
+
223
+ describe "with username and password" do
224
+ subject do
225
+ the_spec do |bound|
226
+ service("http://username:password@localhost") do
227
+ resource("/secret") do
228
+ bound.value = get {}
229
+ end
230
+ end
231
+ end.response
232
+ end
233
+
234
+ it { subject.status_code.must_equal 200 }
235
+ end
236
+ end
201
237
  end
202
238
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: hyperspec
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.1
5
+ version: 0.0.2
6
6
  platform: ruby
7
7
  authors:
8
8
  - "Hannes Tyd\xC3\xA9n"
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2012-02-07 00:00:00 Z
13
+ date: 2012-03-30 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: minitest
@@ -56,28 +56,6 @@ dependencies:
56
56
  version: "2.11"
57
57
  type: :runtime
58
58
  version_requirements: *id004
59
- - !ruby/object:Gem::Dependency
60
- name: vcr
61
- prerelease: false
62
- requirement: &id005 !ruby/object:Gem::Requirement
63
- none: false
64
- requirements:
65
- - - ~>
66
- - !ruby/object:Gem::Version
67
- version: "1.6"
68
- type: :runtime
69
- version_requirements: *id005
70
- - !ruby/object:Gem::Dependency
71
- name: webmock
72
- prerelease: false
73
- requirement: &id006 !ruby/object:Gem::Requirement
74
- none: false
75
- requirements:
76
- - - ">="
77
- - !ruby/object:Gem::Version
78
- version: "0"
79
- type: :runtime
80
- version_requirements: *id006
81
59
  description: " By extending minitest/spec HyperSpec provides a Ruby DSL for testing HTTP APIs from the \"outside\".\n"
82
60
  email:
83
61
  - hannes@soundcloud.com