rack-spec 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3052bec055e4531609acc4f509cf2109088495d7
4
- data.tar.gz: cfeeb3b1b98f02388affd0445fe3f26fcb0aeafe
3
+ metadata.gz: 87132891a38319e6296f71af99cfb46bb54d9041
4
+ data.tar.gz: 2a9700230c057c9894140ffad108f90fb6a314b3
5
5
  SHA512:
6
- metadata.gz: 72ed9fed21dac59c45ce32cd123a26a1b6b970f8814f749704092e6f72b7ac7712924748bfb23566eeb439fd6ea4fec79d400bbee8e68edd4cd3fea5f3ea84ce
7
- data.tar.gz: 830a617bad45287beba0865e2d8e1288e199a0a5eba055261a4aae3931fdf61d3bcab4ce3667aae7477d39ef6ed5d8d292cc0bfee5aea02a0a79231cfd9699a7
6
+ metadata.gz: e7ce5b2e4e7385afbcfc7cd0b3cb2b83a5b8492d4e91d43d0ec4cf79714aca7c3aaffad2e239e22bf2bba68f626182b1510107c7c063cc29144c0e91238a3fb1
7
+ data.tar.gz: 489751d27fb8458b88939c3b71e46ebcd89d48fbd7212f39bfc21de8e8e5f41b391020248f5a781e251a2fd847b849ea28a682b8af063ffe93329a00ebde2e06
@@ -1,3 +1,7 @@
1
+ ## 0.1.4
2
+ * Add Rack::Spec::Mock
3
+ * Prettify response JSON
4
+
1
5
  ## v0.1.3
2
6
  * Array response support of Rack::Spec::ResponseValidation
3
7
 
data/README.md CHANGED
@@ -9,49 +9,117 @@ schema = JSON.parse(str)
9
9
  use Rack::Spec::ErrorHandler
10
10
  use Rack::Spec::RequestValidation, schema: schema
11
11
  use Rack::Spec::ResponseValidation, schema: schema if ENV["RACK_ENV"] == "test"
12
+ use Rack::Spec::Mock, schema: schema if ENV["RACK_ENV"] == "mock"
12
13
  ```
13
14
 
14
- ### Example
15
+ ### Rack::Spec::RequestValidation
16
+ Validates request and raises errors below.
17
+
18
+ * Rack::Spec::RequestValidation::InvalidContentType
19
+ * Rack::Spec::RequestValidation::InvalidJson
20
+ * Rack::Spec::RequestValidation::InvalidParameter
21
+ * Rack::Spec::RequestValidation::LinkNotFound
22
+
15
23
  ```sh
16
- $ curl http://localhost:9292/recipes
17
- {"id":"link_not_found","message":"Not found"}
24
+ $ curl http://localhost:9292/users
25
+ {
26
+ "id": "link_not_found",
27
+ "message": "Not found"
28
+ }
18
29
 
19
30
  $ curl http://localhost:9292/apps -H "Content-Type: application/json" -d "invalid-json"
20
- {"id":"invalid_json","message":"Request body wasn't valid JSON"}
31
+ {
32
+ "id": "invalid_json",
33
+ "message": "Request body wasn't valid JSON"
34
+ }
21
35
 
22
36
  $ curl http://localhost:9292/apps -H "Content-Type: text/plain" -d "{}"
23
- {"id":"invalid_content_type","message":"Invalid content type"}
37
+ {
38
+ "id": "invalid_content_type",
39
+ "message": "Invalid content type"
40
+ }
24
41
 
25
42
  $ curl http://localhost:9292/apps -H "Content-Type: application/json" -d '{"name":"x"}'
26
- {"id":"invalid_parameter","message":"Invalid request.\n#/name: failed schema #/definitions/app/links/0/schema/properties/name: Expected string to match pattern \"/^[a-z][a-z0-9-]{3,50}$/\", value was: x."}
43
+ {
44
+ "id": "invalid_parameter",
45
+ "message": "Invalid request.\n#/name: failed schema #/definitions/app/links/0/schema/properties/name: Expected string to match pattern \"/^[a-z][a-z0-9-]{3,50}$/\", value was: x."
46
+ }
27
47
  ```
28
48
 
29
- ### Rack::Spec::RequestValidation
30
- Validates request and raises following errors:
31
-
32
- * Rack::Spec::RequestValidation::InvalidContentType
33
- * Rack::Spec::RequestValidation::InvalidJson
34
- * Rack::Spec::RequestValidation::InvalidParameter
35
- * Rack::Spec::RequestValidation::LinkNotFound
36
-
37
- ### Rack::Spec::RequestValidation
38
- Validates response and raises following errors:
49
+ ### Rack::Spec::ResponseValidation
50
+ Validates request and raises errors below.
39
51
 
40
52
  * Rack::Spec::RequestValidation::InvalidResponseContentType
41
53
  * Rack::Spec::RequestValidation::InvalidResponseType
42
54
 
55
+ ```sh
56
+ $ curl http://localhost:9292/apps
57
+ {
58
+ "id": "invalid_response_content_type",
59
+ "message": "Response Content-Type wasn't for JSON"
60
+ }
61
+
62
+ $ curl http://localhost:9292/apps
63
+ {
64
+ "id": "invalid_response_type",
65
+ "message": "#: failed schema #/definitions/app: Expected data to be of type \"object\"; value was: [\"message\", \"dummy\"]."
66
+ }
67
+ ```
68
+
69
+ ### Rack::Spec::Mock
70
+ Generates dummy response by using example property in JSON schema.
71
+
72
+ ```sh
73
+ $ curl http://localhost:9292/apps/1
74
+ [
75
+ {
76
+ "id": "01234567-89ab-cdef-0123-456789abcdef",
77
+ "name": "example"
78
+ }
79
+ ]
80
+
81
+ $ curl http://localhost:9292/apps/01234567-89ab-cdef-0123-456789abcdef
82
+ {
83
+ "id": "01234567-89ab-cdef-0123-456789abcdef",
84
+ "name": "example"
85
+ }
86
+
87
+ $ curl http://localhost:9292/apps/1 -d '{"name":"example"}'
88
+ {
89
+ "id": "01234567-89ab-cdef-0123-456789abcdef",
90
+ "name": "example"
91
+ }
92
+
93
+ $ curl http://localhost:9292/recipes
94
+ {
95
+ "id": "example_not_found",
96
+ "message": "No example found for #/definitions/recipe/id"
97
+ }
98
+ ```
99
+
43
100
  ### Rack::Spec::ErrorHandler
44
101
  Returns appropriate error response including following properties when RequestValidation raises error.
45
102
 
46
- * id: Error type identifier (e.g. `link_not_found`, `invalid_content_type`)
47
- * message: Human readable message (e.g. `Not Found`, `Invalid Content-Type`)
103
+ * message: Human readable message
104
+ * id: Error type identifier listed below
105
+ * invalid_content_type
106
+ * invalid_json
107
+ * invalid_parameter
108
+ * invalid_response_content_type
109
+ * invalid_response_type
110
+ * link_not_found
111
+
112
+ Here is a tree of all possible errors defined in Rack::Spec.
48
113
 
49
- ### Errors
50
114
  ```
51
115
  StandardError
52
116
  |
53
117
  Rack::Spec::Error
54
118
  |
119
+ |--Rack::Spec::Mock::Error
120
+ | |
121
+ | `--Rack::Spec::RequestValidation::ExampleNotFound
122
+ |
55
123
  |--Rack::Spec::RequestValidation::Error
56
124
  | |
57
125
  | |--Rack::Spec::RequestValidation::InvalidContentType
@@ -2,9 +2,10 @@ require "json"
2
2
  require "json_schema"
3
3
  require "multi_json"
4
4
 
5
- require "rack/spec/base_validator"
5
+ require "rack/spec/base_request_handler"
6
6
  require "rack/spec/error"
7
7
  require "rack/spec/error_handler"
8
+ require "rack/spec/mock"
8
9
  require "rack/spec/request_validation"
9
10
  require "rack/spec/response_validation"
10
11
  require "rack/spec/schema"
@@ -1,6 +1,7 @@
1
1
  module Rack
2
2
  module Spec
3
- class BaseValidator
3
+ # Base class for providing some utility methods to handle Rack env and JSON Schema
4
+ class BaseRequestHandler
4
5
  # Utility wrapper method
5
6
  def self.call(**args)
6
7
  new(**args).call
@@ -48,6 +49,16 @@ module Rack
48
49
  def has_link_for_current_action?
49
50
  !!link
50
51
  end
52
+
53
+ # @return [JsonSchema::Schema] Schema for current link, specified by targetSchema or parent schema
54
+ def schema_for_current_link
55
+ link.target_schema || link.parent
56
+ end
57
+
58
+ # @return [true, false] True if response is intended to be list data
59
+ def has_list_data?
60
+ link.rel == "instances" && !link.target_schema
61
+ end
51
62
  end
52
63
  end
53
64
  end
@@ -23,7 +23,7 @@ module Rack
23
23
  end
24
24
 
25
25
  def body
26
- { id: id, message: message }.to_json
26
+ MultiJson.encode({ id: id, message: message }, pretty: true) + "\n"
27
27
  end
28
28
  end
29
29
  end
@@ -0,0 +1,93 @@
1
+ module Rack
2
+ module Spec
3
+ class Mock
4
+ # Behaves as a rack-middleware
5
+ # @param app [Object] Rack application
6
+ # @param schema [Hash] Schema object written in JSON schema format
7
+ # @raise [JsonSchema::SchemaError]
8
+ def initialize(app, schema: nil)
9
+ @app = app
10
+ @schema = Schema.new(schema)
11
+ end
12
+
13
+ # @param env [Hash] Rack env
14
+ def call(env)
15
+ RequestHandler.call(app: @app, env: env, schema: @schema)
16
+ end
17
+
18
+ class RequestHandler < BaseRequestHandler
19
+ # @param app [Object] Rack application
20
+ def initialize(app: nil, **args)
21
+ @app = app
22
+ super(**args)
23
+ end
24
+
25
+ # Returns dummy response if JSON schema is defined for the current link
26
+ # @return [Array] Rack response
27
+ def call
28
+ if has_link_for_current_action?
29
+ dummy_response
30
+ else
31
+ @app.call(@env)
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def dummy_response
38
+ [dummy_status, dummy_headers, [dummy_body]]
39
+ end
40
+
41
+ def dummy_status
42
+ method == "POST" ? 201 : 200
43
+ end
44
+
45
+ def dummy_headers
46
+ { "Content-Type" => "application/json" }
47
+ end
48
+
49
+ def dummy_body
50
+ document = ResponseGenerator.call(schema_for_current_link)
51
+ document = [document] if has_list_data?
52
+ MultiJson.encode(document, pretty: true) + "\n"
53
+ end
54
+ end
55
+
56
+ class ResponseGenerator
57
+ # Generates example response Hash from given schema
58
+ # @return [Hash]
59
+ # @example
60
+ # Rack::Spec::Mock::ResponseGenerator(schema) #=> { "id" => 1, "name" => "example" }
61
+ def self.call(schema)
62
+ schema.properties.inject({}) do |result, (key, value)|
63
+ result.merge(
64
+ key => case
65
+ when !value.properties.empty?
66
+ call(value)
67
+ when !value.data["example"].nil?
68
+ value.data["example"]
69
+ when value.type.include?("null")
70
+ nil
71
+ else
72
+ raise ExampleNotFound, "No example found for #{schema.pointer}/#{key}"
73
+ end
74
+ )
75
+ end
76
+ end
77
+ end
78
+
79
+ class Error < Error
80
+ end
81
+
82
+ class ExampleNotFound < Error
83
+ def id
84
+ "example_not_found"
85
+ end
86
+
87
+ def status
88
+ 500
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -17,7 +17,7 @@ module Rack
17
17
  @app.call(env)
18
18
  end
19
19
 
20
- class Validator < BaseValidator
20
+ class Validator < BaseRequestHandler
21
21
  # Utility wrapper method
22
22
  def self.call(**args)
23
23
  new(**args).call
@@ -18,7 +18,7 @@ module Rack
18
18
  end
19
19
  end
20
20
 
21
- class Validator < BaseValidator
21
+ class Validator < BaseRequestHandler
22
22
  # @param env [Hash] Rack env
23
23
  # @param response [Array] Rack response
24
24
  # @param schema [JsonSchema::Schema] Schema object
@@ -28,14 +28,16 @@ module Rack
28
28
  @schema = schema
29
29
  end
30
30
 
31
- # Raises an error if any error detected
31
+ # Raises an error if any error detected, skipping validation for non-defined link
32
32
  # @raise [Rack::Spec::ResponseValidation::InvalidResponse]
33
33
  def call
34
- case
35
- when !has_json_content_type?
36
- raise InvalidResponseContentType
37
- when !valid?
38
- raise InvalidResponseType, validator.errors
34
+ if has_link_for_current_action?
35
+ case
36
+ when !has_json_content_type?
37
+ raise InvalidResponseContentType
38
+ when !valid?
39
+ raise InvalidResponseType, validator.errors
40
+ end
39
41
  end
40
42
  end
41
43
 
@@ -46,7 +48,7 @@ module Rack
46
48
 
47
49
  # @return [true, false] True if given data is valid to the JSON schema
48
50
  def valid?
49
- !has_link_for_current_action? || validator.validate(example_item)
51
+ validator.validate(example_item)
50
52
  end
51
53
 
52
54
  # @return [Hash] Choose an item from response data, to be validated
@@ -58,11 +60,6 @@ module Rack
58
60
  end
59
61
  end
60
62
 
61
- # @return [true, false] True if response is intended to be list data
62
- def has_list_data?
63
- link.rel == "instances" && !link.target_schema
64
- end
65
-
66
63
  # @return [Array, Hash] Response body data, decoded from JSON
67
64
  def data
68
65
  MultiJson.decode(body)
@@ -74,11 +71,6 @@ module Rack
74
71
  @validator ||= JsonSchema::Validator.new(schema_for_current_link)
75
72
  end
76
73
 
77
- # @return [JsonSchema::Schema] Schema for current link, specified by targetSchema or parent schema
78
- def schema_for_current_link
79
- link.target_schema || link.parent
80
- end
81
-
82
74
  # @return [Hash] Response headers
83
75
  def headers
84
76
  @response[1]
@@ -86,9 +78,9 @@ module Rack
86
78
 
87
79
  # @return [String] Response body
88
80
  def body
89
- @response[2].reduce("") do |result, str|
90
- result << str
91
- end
81
+ result = ""
82
+ @response[2].each {|str| result << str }
83
+ result
92
84
  end
93
85
  end
94
86
 
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  module Spec
3
- VERSION = "0.1.3"
3
+ VERSION = "0.1.4"
4
4
  end
5
5
  end
@@ -95,11 +95,46 @@
95
95
  "$ref": "#/definitions/app/definitions/name"
96
96
  }
97
97
  }
98
+ },
99
+ "recipe": {
100
+ "$schema": "http://json-schema.org/draft-04/hyper-schema",
101
+ "description": "cooking Recipe",
102
+ "title": "Recipe",
103
+ "type": [
104
+ "object"
105
+ ],
106
+ "definitions": {
107
+ "id": {
108
+ "description": "unique identifier of recipe",
109
+ "format": "uuid",
110
+ "readOnly": true,
111
+ "type": [
112
+ "string"
113
+ ]
114
+ }
115
+ },
116
+ "links": [
117
+ {
118
+ "description": "List recipes",
119
+ "href": "/recipes",
120
+ "method": "GET",
121
+ "rel": "instances",
122
+ "title": "list"
123
+ }
124
+ ],
125
+ "properties": {
126
+ "id": {
127
+ "$ref": "#/definitions/recipe/definitions/id"
128
+ }
129
+ }
98
130
  }
99
131
  },
100
132
  "properties": {
101
133
  "app": {
102
134
  "$ref": "#/definitions/app"
135
+ },
136
+ "recipe": {
137
+ "$ref": "#/definitions/recipe"
103
138
  }
104
139
  },
105
140
  "type": [
@@ -0,0 +1,122 @@
1
+ require "spec_helper"
2
+
3
+ describe Rack::Spec::Mock do
4
+ include Rack::Test::Methods
5
+
6
+ let(:app) do
7
+ local_schema = schema
8
+ Rack::Builder.app do
9
+ use Rack::Spec::ErrorHandler
10
+ use Rack::Spec::Mock, schema: local_schema
11
+ run ->(env) do
12
+ [
13
+ 200,
14
+ {},
15
+ ["dummy"],
16
+ ]
17
+ end
18
+ end
19
+ end
20
+
21
+ let(:schema) do
22
+ str = File.read(schema_path)
23
+ JSON.parse(str)
24
+ end
25
+
26
+ let(:schema_path) do
27
+ File.expand_path("../../../fixtures/schema.json", __FILE__)
28
+ end
29
+
30
+ let(:response) do
31
+ last_response
32
+ end
33
+
34
+ let(:env) do
35
+ {}
36
+ end
37
+
38
+ let(:params) do
39
+ {}
40
+ end
41
+
42
+ subject do
43
+ send(verb, path, params, env)
44
+ response.status
45
+ end
46
+
47
+ describe "#call" do
48
+ context "with list API" do
49
+ let(:verb) do
50
+ :get
51
+ end
52
+
53
+ let(:path) do
54
+ "/apps"
55
+ end
56
+
57
+ it "returns Array dummy response" do
58
+ should == 200
59
+ response.body.should be_json_as(
60
+ [
61
+ {
62
+ id: schema["definitions"]["app"]["definitions"]["id"]["example"],
63
+ name: schema["definitions"]["app"]["definitions"]["name"]["example"],
64
+ }
65
+ ]
66
+ )
67
+ end
68
+ end
69
+
70
+ context "with info API" do
71
+ let(:verb) do
72
+ :get
73
+ end
74
+
75
+ let(:path) do
76
+ "/apps/1"
77
+ end
78
+
79
+ it "returns dummy response" do
80
+ should == 200
81
+ response.body.should be_json_as(
82
+ {
83
+ id: schema["definitions"]["app"]["definitions"]["id"]["example"],
84
+ name: schema["definitions"]["app"]["definitions"]["name"]["example"],
85
+ }
86
+ )
87
+ end
88
+ end
89
+
90
+ context "with POST API" do
91
+ let(:verb) do
92
+ :post
93
+ end
94
+
95
+ let(:path) do
96
+ "/apps"
97
+ end
98
+
99
+ it "returns dummy response with 201" do
100
+ should == 201
101
+ end
102
+ end
103
+
104
+ context "without example" do
105
+ let(:verb) do
106
+ :get
107
+ end
108
+
109
+ let(:path) do
110
+ "/recipes"
111
+ end
112
+
113
+ it "returns example_not_found error" do
114
+ should == 500
115
+ response.body.should be_json_as(
116
+ id: "example_not_found",
117
+ message: "No example found for #/definitions/recipe/id",
118
+ )
119
+ end
120
+ end
121
+ end
122
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-spec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryo Nakamura
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-05 00:00:00.000000000 Z
11
+ date: 2014-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json_schema
@@ -151,15 +151,17 @@ files:
151
151
  - Rakefile
152
152
  - lib/rack-spec.rb
153
153
  - lib/rack/spec.rb
154
- - lib/rack/spec/base_validator.rb
154
+ - lib/rack/spec/base_request_handler.rb
155
155
  - lib/rack/spec/error.rb
156
156
  - lib/rack/spec/error_handler.rb
157
+ - lib/rack/spec/mock.rb
157
158
  - lib/rack/spec/request_validation.rb
158
159
  - lib/rack/spec/response_validation.rb
159
160
  - lib/rack/spec/schema.rb
160
161
  - lib/rack/spec/version.rb
161
162
  - rack-spec.gemspec
162
163
  - spec/fixtures/schema.json
164
+ - spec/rack/spec/mock_spec.rb
163
165
  - spec/rack/spec_spec.rb
164
166
  - spec/spec_helper.rb
165
167
  homepage: https://github.com/r7kamura/rack-spec
@@ -188,6 +190,7 @@ specification_version: 4
188
190
  summary: JSON Schema based Rack middlewares
189
191
  test_files:
190
192
  - spec/fixtures/schema.json
193
+ - spec/rack/spec/mock_spec.rb
191
194
  - spec/rack/spec_spec.rb
192
195
  - spec/spec_helper.rb
193
196
  has_rdoc: