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 +49 -33
- data/examples/service/example.rb +1 -1
- data/hyperspec.gemspec +1 -1
- data/lib/hyperspec.rb +49 -57
- data/lib/hyperspec/version.rb +1 -1
- data/spec/fixtures/vcr_cassettes/localhost.yml +41 -0
- data/spec/hyperspec_spec.rb +40 -4
- metadata +2 -24
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
36
|
+
post do
|
37
|
+
describe "without request body" do
|
38
|
+
it { responds_with.status :unprocessable_entity }
|
39
|
+
end
|
41
40
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
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
|
-
###
|
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
|
-
####
|
132
|
+
#### 2012 02 07
|
117
133
|
|
118
134
|
- Initial release
|
data/examples/service/example.rb
CHANGED
data/hyperspec.gemspec
CHANGED
@@ -38,7 +38,7 @@ Gem::Specification.new do |s|
|
|
38
38
|
s.add_development_dependency *dependency
|
39
39
|
end
|
40
40
|
|
41
|
-
(dependencies +
|
41
|
+
(dependencies + runtime_dependencies).each do |dependency|
|
42
42
|
s.add_runtime_dependency *dependency
|
43
43
|
end
|
44
44
|
end
|
data/lib/hyperspec.rb
CHANGED
@@ -10,75 +10,71 @@ module HyperSpec
|
|
10
10
|
module ObjectExtensions
|
11
11
|
private
|
12
12
|
def service(desc, additional_desc = nil, &block)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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(
|
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)
|
data/lib/hyperspec/version.rb
CHANGED
@@ -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}
|
data/spec/hyperspec_spec.rb
CHANGED
@@ -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.
|
25
|
-
HyperSpec::VERSION.must_equal "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
|
-
|
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.
|
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.
|
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-
|
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
|