rack-spec 0.1.3 → 0.1.4

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