apipierails3 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (171) hide show
  1. checksums.yaml +17 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +27 -0
  5. data/APACHE-LICENSE-2.0 +202 -0
  6. data/CHANGELOG.md +469 -0
  7. data/Gemfile +1 -0
  8. data/Gemfile.rails32 +6 -0
  9. data/Gemfile.rails41 +6 -0
  10. data/Gemfile.rails42 +11 -0
  11. data/Gemfile.rails50 +6 -0
  12. data/Gemfile.rails51 +7 -0
  13. data/MIT-LICENSE +20 -0
  14. data/NOTICE +4 -0
  15. data/PROPOSAL_FOR_RESPONSE_DESCRIPTIONS.md +244 -0
  16. data/README.rst +1874 -0
  17. data/Rakefile +13 -0
  18. data/apipierails3.gemspec +27 -0
  19. data/app/controllers/apipie/apipies_controller.rb +199 -0
  20. data/app/helpers/apipie_helper.rb +10 -0
  21. data/app/public/apipie/javascripts/apipie.js +6 -0
  22. data/app/public/apipie/javascripts/bundled/bootstrap-collapse.js +138 -0
  23. data/app/public/apipie/javascripts/bundled/bootstrap.js +1726 -0
  24. data/app/public/apipie/javascripts/bundled/jquery.js +5 -0
  25. data/app/public/apipie/javascripts/bundled/prettify.js +28 -0
  26. data/app/public/apipie/stylesheets/application.css +7 -0
  27. data/app/public/apipie/stylesheets/bundled/bootstrap-responsive.min.css +12 -0
  28. data/app/public/apipie/stylesheets/bundled/bootstrap.min.css +689 -0
  29. data/app/public/apipie/stylesheets/bundled/prettify.css +30 -0
  30. data/app/views/apipie/apipies/_disqus.html.erb +13 -0
  31. data/app/views/apipie/apipies/_errors.html.erb +23 -0
  32. data/app/views/apipie/apipies/_headers.html.erb +26 -0
  33. data/app/views/apipie/apipies/_languages.erb +6 -0
  34. data/app/views/apipie/apipies/_metadata.erb +1 -0
  35. data/app/views/apipie/apipies/_method_detail.erb +61 -0
  36. data/app/views/apipie/apipies/_params.html.erb +42 -0
  37. data/app/views/apipie/apipies/_params_plain.html.erb +20 -0
  38. data/app/views/apipie/apipies/apipie_404.html.erb +17 -0
  39. data/app/views/apipie/apipies/apipie_checksum.json.erb +1 -0
  40. data/app/views/apipie/apipies/getting_started.html.erb +6 -0
  41. data/app/views/apipie/apipies/index.html.erb +56 -0
  42. data/app/views/apipie/apipies/method.html.erb +41 -0
  43. data/app/views/apipie/apipies/plain.html.erb +77 -0
  44. data/app/views/apipie/apipies/resource.html.erb +80 -0
  45. data/app/views/apipie/apipies/static.html.erb +103 -0
  46. data/app/views/layouts/apipie/apipie.html.erb +27 -0
  47. data/config/locales/de.yml +28 -0
  48. data/config/locales/en.yml +32 -0
  49. data/config/locales/es.yml +28 -0
  50. data/config/locales/fr.yml +31 -0
  51. data/config/locales/it.yml +31 -0
  52. data/config/locales/ja.yml +31 -0
  53. data/config/locales/pl.yml +28 -0
  54. data/config/locales/pt-BR.yml +28 -0
  55. data/config/locales/ru.yml +28 -0
  56. data/config/locales/tr.yml +28 -0
  57. data/config/locales/zh-CN.yml +28 -0
  58. data/config/locales/zh-TW.yml +28 -0
  59. data/images/screenshot-1.png +0 -0
  60. data/images/screenshot-2.png +0 -0
  61. data/lib/apipie/apipie_module.rb +83 -0
  62. data/lib/apipie/application.rb +462 -0
  63. data/lib/apipie/configuration.rb +186 -0
  64. data/lib/apipie/dsl_definition.rb +607 -0
  65. data/lib/apipie/error_description.rb +44 -0
  66. data/lib/apipie/errors.rb +86 -0
  67. data/lib/apipie/extractor.rb +177 -0
  68. data/lib/apipie/extractor/collector.rb +117 -0
  69. data/lib/apipie/extractor/recorder.rb +166 -0
  70. data/lib/apipie/extractor/writer.rb +454 -0
  71. data/lib/apipie/helpers.rb +73 -0
  72. data/lib/apipie/markup.rb +48 -0
  73. data/lib/apipie/method_description.rb +273 -0
  74. data/lib/apipie/middleware/checksum_in_headers.rb +35 -0
  75. data/lib/apipie/param_description.rb +280 -0
  76. data/lib/apipie/railtie.rb +9 -0
  77. data/lib/apipie/resource_description.rb +124 -0
  78. data/lib/apipie/response_description.rb +131 -0
  79. data/lib/apipie/response_description_adapter.rb +200 -0
  80. data/lib/apipie/routes_formatter.rb +33 -0
  81. data/lib/apipie/routing.rb +16 -0
  82. data/lib/apipie/rspec/response_validation_helper.rb +192 -0
  83. data/lib/apipie/see_description.rb +39 -0
  84. data/lib/apipie/static_dispatcher.rb +69 -0
  85. data/lib/apipie/swagger_generator.rb +707 -0
  86. data/lib/apipie/tag_list_description.rb +11 -0
  87. data/lib/apipie/validator.rb +526 -0
  88. data/lib/apipie/version.rb +3 -0
  89. data/lib/apipierails3.rb +25 -0
  90. data/lib/generators/apipie/install/README +6 -0
  91. data/lib/generators/apipie/install/install_generator.rb +25 -0
  92. data/lib/generators/apipie/install/templates/initializer.rb.erb +7 -0
  93. data/lib/generators/apipie/views_generator.rb +11 -0
  94. data/lib/tasks/apipie.rake +345 -0
  95. data/rel-eng/packages/.readme +3 -0
  96. data/rel-eng/packages/rubygem-apipie-rails +1 -0
  97. data/rel-eng/tito.props +5 -0
  98. data/spec/controllers/api/v1/architectures_controller_spec.rb +29 -0
  99. data/spec/controllers/api/v2/architectures_controller_spec.rb +12 -0
  100. data/spec/controllers/api/v2/nested/resources_controller_spec.rb +11 -0
  101. data/spec/controllers/apipies_controller_spec.rb +273 -0
  102. data/spec/controllers/concerns_controller_spec.rb +42 -0
  103. data/spec/controllers/extended_controller_spec.rb +11 -0
  104. data/spec/controllers/users_controller_spec.rb +740 -0
  105. data/spec/dummy/Rakefile +7 -0
  106. data/spec/dummy/app/controllers/api/base_controller.rb +4 -0
  107. data/spec/dummy/app/controllers/api/v1/architectures_controller.rb +43 -0
  108. data/spec/dummy/app/controllers/api/v1/base_controller.rb +11 -0
  109. data/spec/dummy/app/controllers/api/v2/architectures_controller.rb +30 -0
  110. data/spec/dummy/app/controllers/api/v2/base_controller.rb +11 -0
  111. data/spec/dummy/app/controllers/api/v2/nested/architectures_controller.rb +32 -0
  112. data/spec/dummy/app/controllers/api/v2/nested/resources_controller.rb +33 -0
  113. data/spec/dummy/app/controllers/application_controller.rb +18 -0
  114. data/spec/dummy/app/controllers/concerns/extending_concern.rb +11 -0
  115. data/spec/dummy/app/controllers/concerns/sample_controller.rb +41 -0
  116. data/spec/dummy/app/controllers/concerns_controller.rb +8 -0
  117. data/spec/dummy/app/controllers/extended_controller.rb +14 -0
  118. data/spec/dummy/app/controllers/files_controller.rb +5 -0
  119. data/spec/dummy/app/controllers/overridden_concerns_controller.rb +31 -0
  120. data/spec/dummy/app/controllers/pets_controller.rb +408 -0
  121. data/spec/dummy/app/controllers/pets_using_auto_views_controller.rb +73 -0
  122. data/spec/dummy/app/controllers/pets_using_self_describing_classes_controller.rb +95 -0
  123. data/spec/dummy/app/controllers/tagged_cats_controller.rb +32 -0
  124. data/spec/dummy/app/controllers/tagged_dogs_controller.rb +15 -0
  125. data/spec/dummy/app/controllers/twitter_example_controller.rb +307 -0
  126. data/spec/dummy/app/controllers/users_controller.rb +297 -0
  127. data/spec/dummy/app/views/layouts/application.html.erb +21 -0
  128. data/spec/dummy/config.ru +4 -0
  129. data/spec/dummy/config/application.rb +49 -0
  130. data/spec/dummy/config/boot.rb +10 -0
  131. data/spec/dummy/config/database.yml +21 -0
  132. data/spec/dummy/config/environment.rb +8 -0
  133. data/spec/dummy/config/environments/development.rb +28 -0
  134. data/spec/dummy/config/environments/production.rb +52 -0
  135. data/spec/dummy/config/environments/test.rb +38 -0
  136. data/spec/dummy/config/initializers/apipie.rb +110 -0
  137. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  138. data/spec/dummy/config/initializers/inflections.rb +10 -0
  139. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  140. data/spec/dummy/config/initializers/secret_token.rb +8 -0
  141. data/spec/dummy/config/initializers/session_store.rb +8 -0
  142. data/spec/dummy/config/locales/en.yml +5 -0
  143. data/spec/dummy/config/routes.rb +51 -0
  144. data/spec/dummy/db/.gitkeep +0 -0
  145. data/spec/dummy/doc/apipie_examples.json +1 -0
  146. data/spec/dummy/doc/users/desc_from_file.md +1 -0
  147. data/spec/dummy/public/404.html +26 -0
  148. data/spec/dummy/public/422.html +26 -0
  149. data/spec/dummy/public/500.html +26 -0
  150. data/spec/dummy/public/favicon.ico +0 -0
  151. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  152. data/spec/dummy/script/rails +6 -0
  153. data/spec/lib/application_spec.rb +49 -0
  154. data/spec/lib/extractor/extractor_spec.rb +9 -0
  155. data/spec/lib/extractor/middleware_spec.rb +44 -0
  156. data/spec/lib/extractor/writer_spec.rb +110 -0
  157. data/spec/lib/file_handler_spec.rb +18 -0
  158. data/spec/lib/method_description_spec.rb +98 -0
  159. data/spec/lib/param_description_spec.rb +345 -0
  160. data/spec/lib/param_group_spec.rb +60 -0
  161. data/spec/lib/rake_spec.rb +71 -0
  162. data/spec/lib/resource_description_spec.rb +48 -0
  163. data/spec/lib/swagger/openapi_2_0_schema.json +1607 -0
  164. data/spec/lib/swagger/rake_swagger_spec.rb +139 -0
  165. data/spec/lib/swagger/response_validation_spec.rb +104 -0
  166. data/spec/lib/swagger/swagger_dsl_spec.rb +658 -0
  167. data/spec/lib/validator_spec.rb +113 -0
  168. data/spec/lib/validators/array_validator_spec.rb +85 -0
  169. data/spec/spec_helper.rb +109 -0
  170. data/spec/support/rake.rb +21 -0
  171. metadata +415 -0
@@ -0,0 +1,139 @@
1
+ require 'spec_helper'
2
+ require "json-schema"
3
+
4
+ require File.expand_path("../../../dummy/app/controllers/twitter_example_controller.rb", __FILE__)
5
+ require File.expand_path("../../../dummy/app/controllers/users_controller.rb", __FILE__)
6
+ require File.expand_path("../../../dummy/app/controllers/pets_controller.rb", __FILE__)
7
+
8
+ describe 'rake tasks' do
9
+ include_context "rake"
10
+
11
+ let(:doc_path) { "user_specified_doc_path" }
12
+
13
+ before do
14
+ Apipie.configuration.doc_path = doc_path
15
+ Apipie.configuration.swagger_suppress_warnings = true
16
+ allow(Apipie).to receive(:reload_documentation)
17
+ subject.invoke(*task_args)
18
+ end
19
+
20
+ describe 'static swagger specification files' do
21
+
22
+ after do
23
+ Dir["#{doc_output}*"].each { |static_file| FileUtils.rm_rf(static_file) }
24
+ end
25
+
26
+ let(:swagger_schema) do
27
+ File.read(File.join(File.dirname(__FILE__),"openapi_2_0_schema.json"))
28
+ end
29
+
30
+ let(:apidoc_swagger_json) do
31
+ # note: the filename ends with '_tmp' because this suffix is passed as a parameter to the rake task
32
+ File.read("#{doc_output}/schema_swagger_tmp.json")
33
+ end
34
+
35
+ let(:apidoc_swagger) do
36
+ JSON.parse(apidoc_swagger_json)
37
+ end
38
+
39
+ let(:doc_output) do
40
+ File.join(::Rails.root, doc_path, 'apidoc')
41
+ end
42
+
43
+ let(:ref_output) do
44
+ File.join(::Rails.root, doc_path, 'apidoc_ref')
45
+ end
46
+
47
+
48
+ def expect_param_def(http_method, path, param_name, field, value)
49
+ params = apidoc_swagger["paths"][path][http_method]["parameters"]
50
+ param = params.select {|p| p if p["name"]==param_name}[0]
51
+ expect(param[field]).to eq(value)
52
+ end
53
+
54
+ def expect_tags_def(http_method, path, value)
55
+ params = apidoc_swagger["paths"][path][http_method]["tags"]
56
+ expect(params).to eq(value)
57
+ end
58
+
59
+ def body_param_def(http_method, path, param_name)
60
+ params = apidoc_swagger["paths"][path][http_method]["parameters"]
61
+ body = params.select {|p| p if p["name"]=="body"}[0]
62
+ schema_properties = body["schema"]["properties"]
63
+ # print JSON.pretty_generate(schema_properties)
64
+ param = (schema_properties.select {|k,v| v if k == param_name })[param_name]
65
+ # print JSON.pretty_generate(param)
66
+ param
67
+ end
68
+
69
+ def expect_body_param_def(http_method, path, param_name, field, value)
70
+ param = body_param_def(http_method, path, param_name)
71
+ expect(param[field]).to eq(value)
72
+ end
73
+
74
+
75
+ describe 'apipie:static_swagger_json[development,json,_tmp]' do
76
+ it "generates static swagger files for the default version of apipie docs" do
77
+ # print apidoc_swagger_json
78
+
79
+ expect(apidoc_swagger["info"]["title"]).to eq("Test app (params in:body)")
80
+ expect(apidoc_swagger["info"]["version"]).to eq("#{Apipie.configuration.default_version}")
81
+ end
82
+
83
+ it "includes expected values in the generated swagger file" do
84
+ expect_param_def("get", "/twitter_example/{id}", "screen_name", "in", "query")
85
+ expect_param_def("put", "/users/{id}", "id", "in", "path")
86
+ expect_body_param_def("put", "/users/{id}", "oauth", "type", "string")
87
+ expect_body_param_def("put", "/users/{id}", "user", "type", "object")
88
+
89
+ user = body_param_def("put", "/users/{id}", "user")
90
+ expect(user["properties"]["name"]["type"]).to eq("string")
91
+
92
+ expect_param_def("get", "/users/by_department", "department", "in", "query")
93
+ expect_param_def("get", "/users/by_department", "department", "enum",
94
+ ["finance", "operations", "sales", "marketing", "HR"])
95
+
96
+ expect_tags_def("get", "/twitter_example/{id}/followers", %w[twitter_example following index search])
97
+ end
98
+
99
+ it "generates a valid swagger file" do
100
+ # print apidoc_swagger_json
101
+ expect(JSON::Validator.validate(swagger_schema, apidoc_swagger_json)).to be_truthy
102
+ end
103
+ end
104
+
105
+ describe 'apipie:static_swagger_json[development,form_data,_tmp]' do
106
+ it "generates static swagger files for the default version of apipie docs" do
107
+ # print apidoc_swagger_json
108
+
109
+ expect(apidoc_swagger["info"]["title"]).to eq("Test app (params in:formData)")
110
+ expect(apidoc_swagger["info"]["version"]).to eq("#{Apipie.configuration.default_version}")
111
+
112
+ end
113
+
114
+ it "includes expected values in the generated swagger file" do
115
+ expect_param_def("get", "/twitter_example/{id}", "screen_name", "in", "query")
116
+ expect_param_def("put", "/users/{id}", "id", "in", "path")
117
+ expect_param_def("put", "/users/{id}", "oauth", "in", "formData")
118
+ expect_param_def("get", "/users/by_department", "department", "in", "query")
119
+ expect_param_def("get", "/users/by_department", "department", "enum",
120
+ ["finance", "operations", "sales", "marketing", "HR"])
121
+
122
+ expect_tags_def("get", "/twitter_example/{id}/followers", %w[twitter_example following index search])
123
+
124
+ end
125
+
126
+ it "generates a valid swagger file" do
127
+ # print apidoc_swagger_json
128
+ expect(JSON::Validator.validate(swagger_schema, apidoc_swagger_json)).to be_truthy
129
+ end
130
+ end
131
+
132
+ describe 'apipie:did_swagger_change[development,form_data,_tmp]' do
133
+ it "keeps a reference file" do
134
+ expect(Pathname(ref_output).children.count).to eq(2) # one file for each language
135
+ end
136
+ end
137
+ end
138
+
139
+ end
@@ -0,0 +1,104 @@
1
+ require 'spec_helper'
2
+ require 'rack/utils'
3
+ require 'rspec/expectations'
4
+ require 'apipie/rspec/response_validation_helper'
5
+ require "json-schema"
6
+
7
+ RSpec.describe PetsController, :type => :controller do
8
+ before :each do
9
+ Apipie.configuration.swagger_allow_additional_properties_in_response = false
10
+ end
11
+
12
+ it "does not raise error when rendered output matches the described response" do
13
+ response = get :return_and_validate_expected_response, {format: :json}
14
+ expect(response).to match_declared_responses
15
+ end
16
+
17
+ it "does not raise error when rendered output (array) matches the described response" do
18
+ response = get :return_and_validate_expected_array_response, {format: :json}
19
+ expect(response).to match_declared_responses
20
+ end
21
+
22
+ it "does not raises error when rendered output includes null in the response" do
23
+ response = get :return_and_validate_expected_response_with_null, {format: :json}
24
+ expect(response).to match_declared_responses
25
+ end
26
+
27
+ it "does not raise error when rendered output includes null (instead of an object) in the response" do
28
+ response = get :return_and_validate_expected_response_with_null_object, {format: :json}
29
+ expect(response).to match_declared_responses
30
+ end
31
+
32
+ it "raises error when a response field has the wrong type" do
33
+ response = get :return_and_validate_type_mismatch, {format: :json}
34
+ expect(response).not_to match_declared_responses
35
+ end
36
+
37
+ it "raises error when a response has a missing field" do
38
+ response = get :return_and_validate_missing_field, {format: :json}
39
+ expect(response).not_to match_declared_responses
40
+ end
41
+
42
+ it "raises error when a response has an extra property and 'swagger_allow_additional_properties_in_response' is false" do
43
+ response = get :return_and_validate_extra_property, {format: :json}
44
+ expect(response).not_to match_declared_responses
45
+ end
46
+
47
+ it "raises error when a response has is array instead of object" do
48
+ # note: this action returns HTTP 201, not HTTP 200!
49
+ response = get :return_and_validate_unexpected_array_response, {format: :json}
50
+ expect(response).not_to match_declared_responses
51
+ end
52
+
53
+ it "does not raise error when a response has an extra property and 'swagger_allow_additional_properties_in_response' is true" do
54
+ Apipie.configuration.swagger_allow_additional_properties_in_response = true
55
+ response = get :return_and_validate_extra_property, {format: :json}
56
+ expect(response).to match_declared_responses
57
+ end
58
+
59
+ it "does not raise error when a response has an extra field and 'additional_properties' is specified in the response" do
60
+ Apipie.configuration.swagger_allow_additional_properties_in_response = false
61
+ response = get :return_and_validate_allowed_extra_property, {format: :json}
62
+ expect(response).to match_declared_responses
63
+ end
64
+
65
+ it "raises error when a response sub-object has an extra field and 'additional_properties' is not specified on it, but specified on the top level of the response" do
66
+ Apipie.configuration.swagger_allow_additional_properties_in_response = false
67
+ response = get :sub_object_invalid_extra_property, {format: :json}
68
+ expect(response).not_to match_declared_responses
69
+ end
70
+
71
+ it "does not rais error when a response sub-object has an extra field and 'additional_properties' is specified on it" do
72
+ Apipie.configuration.swagger_allow_additional_properties_in_response = false
73
+ response = get :sub_object_allowed_extra_property, {format: :json}
74
+ expect(response).to match_declared_responses
75
+ end
76
+
77
+ describe "auto validation" do
78
+ auto_validate_rendered_views
79
+ it "raises exception when a response field has the wrong type and auto validation is turned on" do
80
+ expect { get :return_and_validate_type_mismatch, {format: :json} }.to raise_error(Apipie::ResponseDoesNotMatchSwaggerSchema)
81
+ end
82
+
83
+ it "does not raise an exception when calling an undocumented method" do
84
+ expect { get :undocumented_method, {format: :json} }.not_to raise_error
85
+ end
86
+
87
+ end
88
+
89
+
90
+ describe "with array field" do
91
+ it "no error for valid response" do
92
+ response = get :returns_response_with_valid_array, {format: :json}
93
+ expect(response).to match_declared_responses
94
+ end
95
+
96
+ it "error if type of element in the array is wrong" do
97
+ response = get :returns_response_with_invalid_array, {format: :json}
98
+ expect(response).not_to match_declared_responses
99
+ end
100
+ end
101
+
102
+
103
+
104
+ end
@@ -0,0 +1,658 @@
1
+ require 'spec_helper'
2
+ require 'rack/utils'
3
+ require 'rspec/expectations'
4
+
5
+ describe "Swagger Responses" do
6
+ let(:desc) { Apipie.get_resource_description(controller_class, Apipie.configuration.default_version) }
7
+
8
+ let(:swagger) {
9
+ Apipie.configuration.swagger_suppress_warnings = true
10
+ Apipie.to_swagger_json(Apipie.configuration.default_version, controller_class.to_s.underscore.sub("_controller", ""))
11
+ }
12
+
13
+ let(:controller_class ) { described_class }
14
+
15
+ def get_ref(ref)
16
+ name = ref.split('#/definitions/')[1].to_sym
17
+ swagger[:definitions][name]
18
+ end
19
+
20
+ def resolve_refs(schema)
21
+ if schema['$ref']
22
+ return get_ref(schema['$ref'])
23
+ end
24
+ schema
25
+ end
26
+
27
+ def swagger_response_for(path, code=200, method='get')
28
+ response = swagger[:paths][path][method][:responses][code]
29
+ response[:schema] = resolve_refs(response[:schema])
30
+ response
31
+ end
32
+
33
+ def swagger_params_for(path, method='get')
34
+ swagger[:paths][path][method][:parameters]
35
+ end
36
+
37
+ def swagger_param_by_name(param_name, path, method='get')
38
+ params = swagger_params_for(path, method)
39
+ matching = params.select{|p| p[:name] == param_name }
40
+ raise "multiple params named [#{param_name}] in swagger definition for [#{method } #{path}]" if matching.length > 1
41
+
42
+ nil if matching.length == 0
43
+
44
+ matching[0]
45
+ end
46
+
47
+
48
+
49
+
50
+ #
51
+ # Matcher to validate the hierarchy of fields described in an internal 'returns' object (without checking their type)
52
+ #
53
+ # For example, code such as:
54
+ # returns_obj = Apipie.get_resource_description(...)._methods.returns.detect{|e| e.code=200})
55
+ # expect(returns_obj).to match_param_structure([:pet_name, :animal_type, :pet_measurements => [:weight, :height]])
56
+ #
57
+ # will verify that the payload structure described for the response of return code 200 is:
58
+ # {
59
+ # "pet_name": <any>,
60
+ # "animal_type": <any>,
61
+ # "pet_measurements": {
62
+ # "weight": <any>,
63
+ # "height": <any>
64
+ # }
65
+ # }
66
+ #
67
+ #
68
+ RSpec::Matchers.define :match_field_structure do |expected|
69
+ @last_message = nil
70
+
71
+ match do |actual|
72
+ deep_match?(actual, expected)
73
+ end
74
+
75
+ def deep_match?(actual, expected, breadcrumb=[])
76
+ pending_params = actual.params_ordered.dup
77
+ expected.each do |expected_param|
78
+ expected_param_name = expected_param.is_a?(Hash) ? expected_param.keys.first : expected_param
79
+ actual_param = pending_params.find { |param| param.name.to_s == expected_param_name.to_s }
80
+ unless actual_param
81
+ @fail_message = "Couldn't find #{expected_param_name.inspect} among #{pending_params.map(&:name)} in #{breadcrumb.join('.')}"
82
+ return false
83
+ else
84
+ pending_params.delete_if { |p| p.object_id == actual_param.object_id }
85
+ end
86
+
87
+ return false unless fields_match?(actual_param, expected_param_name, breadcrumb)
88
+ if expected_param.is_a? Hash
89
+ return false unless deep_match?(actual_param.validator, expected_param.values[0], breadcrumb + [expected_param.keys.first])
90
+ end
91
+ end
92
+
93
+ unless pending_params.empty?
94
+ @fail_message = "Unexpected properties #{pending_params.map(&:name)} in #{breadcrumb.join('.')}"
95
+ return false
96
+ end
97
+ true
98
+ end
99
+
100
+ def fields_match?(param, expected_name, breadcrumb)
101
+ return false unless have_field?(param, expected_name, breadcrumb)
102
+ @fail_message = "expected #{(breadcrumb + [param.name]).join('.')} to eq #{(breadcrumb + [expected_name]).join('.')}"
103
+ param.name.to_s == expected_name.to_s
104
+ end
105
+
106
+ def have_field?(field, expected_name, breadcrumb)
107
+ @fail_message = "expected property #{(breadcrumb+[expected_name]).join('.')}"
108
+ !field.nil?
109
+ end
110
+
111
+ failure_message do |actual|
112
+ @fail_message
113
+ end
114
+ end
115
+
116
+
117
+ describe PetsController do
118
+
119
+
120
+ describe "PetsController#index" do
121
+ subject do
122
+ desc._methods[:index]
123
+ end
124
+
125
+ it "should return code 200 with array of entries of the format {'pet_name', 'animal_type'}" do
126
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
127
+
128
+ puts returns_obj.to_json
129
+ expect(returns_obj.code).to eq(200)
130
+ expect(returns_obj.is_array?).to eq(true)
131
+
132
+ expect(returns_obj).to match_field_structure([:pet_name, :animal_type])
133
+ end
134
+
135
+ it 'should have the response described in the swagger' do
136
+ response = swagger_response_for('/pets')
137
+ expect(response[:description]).to eq("list of pets")
138
+
139
+ schema = response[:schema]
140
+ expect(schema[:type]).to eq("array")
141
+
142
+ a_schema = resolve_refs(schema[:items])
143
+ expect(a_schema).to have_field(:pet_name, 'string', {:description => 'Name of pet', :required => false})
144
+ expect(a_schema).to have_field(:animal_type, 'string', {:description => 'Type of pet', :enum => ['dog','cat','iguana','kangaroo']})
145
+ end
146
+
147
+
148
+ it "should return code 401 with a String description field" do
149
+ returns_obj = subject.returns.detect{|e| e.code == 404 }
150
+
151
+ expect(returns_obj.code).to eq(404)
152
+ expect(returns_obj.is_array?).to eq(false)
153
+
154
+ expect(returns_obj).to match_field_structure([:error_message])
155
+ end
156
+
157
+
158
+ it "should return code 401 with a :reason field (defined in the superclass)" do
159
+ returns_obj = subject.returns.detect{|e| e.code == 401 }
160
+
161
+ expect(returns_obj.code).to eq(401)
162
+ expect(returns_obj.is_array?).to eq(false)
163
+
164
+ expect(returns_obj).to match_field_structure([:reason])
165
+ end
166
+
167
+ it 'should have the 404 response described in the swagger' do
168
+ response = swagger_response_for('/pets', 404)
169
+ expect(response[:description]).to eq("Not Found")
170
+
171
+ schema = response[:schema]
172
+ expect(schema[:type]).to eq("object")
173
+
174
+ expect(schema).to have_field(:error_message, 'string', {:description => 'description of the error', :required => true})
175
+ end
176
+
177
+ end
178
+
179
+ describe "PetsController#show_plain_response_with_tags" do
180
+ subject do
181
+ desc._methods[:show_plain_response_with_tags]
182
+ end
183
+
184
+ it "should return tags with 'Dogs', 'Cats', and 'LivingBeings'" do
185
+ returns_obj = subject.tag_list
186
+ puts returns_obj.inspect
187
+
188
+ expect(returns_obj.tags).to eq(%w[Dogs Cats LivingBeings])
189
+ end
190
+ end
191
+
192
+ describe "PetsController#show_as_properties" do
193
+ subject do
194
+ desc._methods[:show_as_properties]
195
+ end
196
+
197
+ it "should return code 200 with 'pet_name' and 'animal_type'" do
198
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
199
+
200
+ puts returns_obj.to_json
201
+ expect(returns_obj.code).to eq(200)
202
+ expect(returns_obj.is_array?).to eq(false)
203
+
204
+ expect(returns_obj).to match_field_structure([:pet_name, :animal_type])
205
+ end
206
+
207
+ it 'should have the response described in the swagger' do
208
+ response = swagger_response_for('/pets/{id}/as_properties')
209
+ expect(response[:description]).to eq("OK")
210
+
211
+ schema = response[:schema]
212
+ expect(schema).to have_field(:pet_name, 'string', {:description => 'Name of pet', :required => false})
213
+ expect(schema).to have_field(:animal_type, 'string', {:description => 'Type of pet', :enum => ['dog','cat','iguana','kangaroo']})
214
+ end
215
+
216
+ it 'should have the 404 response description overridden' do
217
+ returns_obj = subject.returns.detect{|e| e.code == 404 }
218
+
219
+ # puts returns_obj.to_json
220
+ expect(returns_obj.code).to eq(404)
221
+ expect(returns_obj.is_array?).to eq(false)
222
+
223
+ expect(returns_obj).to match_field_structure([:another_error_message])
224
+ end
225
+ end
226
+
227
+ describe "PetsController#show_as_param_group_of_properties" do
228
+ subject do
229
+ desc._methods[:show_as_param_group_of_properties]
230
+ end
231
+
232
+ it "should return code 200 with 'pet_name' and 'animal_type'" do
233
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
234
+
235
+ puts returns_obj.to_json
236
+ expect(returns_obj.code).to eq(200)
237
+ expect(returns_obj.is_array?).to eq(false)
238
+
239
+ expect(returns_obj).to match_field_structure([:pet_name, :animal_type])
240
+ expect(returns_obj.params_ordered[0].is_required?).to be_falsey
241
+ expect(returns_obj.params_ordered[1].is_required?).to be_truthy
242
+ end
243
+
244
+ it 'should have the response described in the swagger' do
245
+ response = swagger_response_for('/pets/{id}/as_param_group_of_properties')
246
+ expect(response[:description]).to eq("The pet")
247
+
248
+ schema = response[:schema]
249
+ expect(schema).to have_field(:pet_name, 'string', {:description => 'Name of pet', :required => false})
250
+ expect(schema).to have_field(:animal_type, 'string', {:description => 'Type of pet', :enum => ['dog','cat','iguana','kangaroo']})
251
+ end
252
+ end
253
+
254
+ describe "PetsController#show_pet_by_id" do
255
+ subject do
256
+ desc._methods[:show_pet_by_id]
257
+ end
258
+
259
+ it "should have only oauth (from ApplicationController), common_param (from resource) and pet_id as an input parameters" do
260
+ params_obj = subject.params_ordered
261
+
262
+ expect(params_obj[0].name).to eq(:oauth)
263
+ expect(params_obj[1].name).to eq(:common_param)
264
+ expect(params_obj[2].name).to eq(:pet_id)
265
+ end
266
+
267
+ it "should return code 200 with 'pet_id', pet_name' and 'animal_type'" do
268
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
269
+
270
+ puts returns_obj.to_json
271
+ expect(returns_obj.code).to eq(200)
272
+ expect(returns_obj.is_array?).to eq(false)
273
+
274
+ # note that the response is expected NOT to return the parameters marked ':only_in => :request'
275
+ expect(returns_obj).to match_field_structure([:pet_id, :pet_name, :animal_type])
276
+ end
277
+
278
+ it 'should have the response described in the swagger' do
279
+ response = swagger_response_for('/pets/pet_by_id')
280
+ expect(response[:description]).to eq("OK")
281
+
282
+ schema = response[:schema]
283
+ expect(schema).to have_field(:pet_id, 'number', {:description => 'id of pet'})
284
+ expect(schema).to have_field(:pet_name, 'string', {:description => 'Name of pet', :required => false})
285
+ expect(schema).to have_field(:animal_type, 'string', {:description => 'Type of pet', :enum => ['dog','cat','iguana','kangaroo']})
286
+ expect(schema).not_to have_field(:partial_match_allowed, 'boolean', {:required => false})
287
+ end
288
+
289
+ it "creates a swagger definition with all input parameters" do
290
+ # a parameter defined for this method
291
+ expect(swagger_param_by_name(:pet_id, '/pets/pet_by_id')[:type]).to eq('number')
292
+
293
+ # a parameter defined for the resource
294
+ expect(swagger_param_by_name(:common_param, '/pets/pet_by_id')[:type]).to eq('number')
295
+
296
+ # a parameter defined in the controller's superclass
297
+ expect(swagger_param_by_name(:oauth, '/pets/pet_by_id')[:type]).to eq('string')
298
+ end
299
+
300
+ end
301
+
302
+ describe "PetsController#get_vote_by_owner_name" do
303
+ subject do
304
+ desc._methods[:get_vote_by_owner_name]
305
+ end
306
+
307
+ it "should return code 200 with 'owner_name' and 'vote'" do
308
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
309
+
310
+ puts returns_obj.to_json
311
+ expect(returns_obj.code).to eq(200)
312
+ expect(returns_obj.is_array?).to eq(false)
313
+
314
+ expect(returns_obj).to match_field_structure([:owner_name, :vote])
315
+ end
316
+
317
+ it 'should have the response described in the swagger' do
318
+ response = swagger_response_for('/pets/by_owner_name/did_vote')
319
+ expect(response[:description]).to eq("OK")
320
+
321
+ schema = response[:schema]
322
+ expect(schema).to have_field(:owner_name, 'string', {:required => false}) # optional because defined using 'param', not 'property'
323
+ expect(schema).to have_field(:vote, 'boolean')
324
+ end
325
+ end
326
+
327
+ describe "PetsController#show_extra_info" do
328
+ subject do
329
+ desc._methods[:show_extra_info]
330
+ end
331
+
332
+ it "should return code 201 with 'pet_name' and 'animal_type'" do
333
+ returns_obj = subject.returns.detect{|e| e.code == 201 }
334
+
335
+ puts returns_obj.to_json
336
+ expect(returns_obj.code).to eq(201)
337
+ expect(returns_obj.is_array?).to eq(false)
338
+
339
+ expect(returns_obj).to match_field_structure([:pet_name, :animal_type])
340
+ end
341
+ it 'should have the 201 response described in the swagger' do
342
+ response = swagger_response_for('/pets/{id}/extra_info', 201)
343
+ expect(response[:description]).to eq("Found a pet")
344
+
345
+ schema = response[:schema]
346
+ expect(schema).to have_field(:pet_name, 'string', {:required => false})
347
+ expect(schema).to have_field(:animal_type, 'string')
348
+ end
349
+
350
+ it "should return code 202 with spread out 'pet' and encapsulated 'pet_measurements'" do
351
+ returns_obj = subject.returns.detect{|e| e.code == 202 }
352
+
353
+ puts returns_obj.to_json
354
+ expect(returns_obj.code).to eq(202)
355
+ expect(returns_obj.is_array?).to eq(false)
356
+
357
+ expect(returns_obj).to match_field_structure([:pet_name,
358
+ :animal_type,
359
+ {:pet_measurements => [:weight, :height, :num_legs]}
360
+ ])
361
+ end
362
+ it 'should have the 202 response described in the swagger' do
363
+ response = swagger_response_for('/pets/{id}/extra_info', 202)
364
+ expect(response[:description]).to eq('Accepted')
365
+
366
+ schema = response[:schema]
367
+ expect(schema).to have_field(:pet_name, 'string', {:required => false})
368
+ expect(schema).to have_field(:animal_type, 'string')
369
+ expect(schema).to have_field(:pet_measurements, 'object')
370
+
371
+ pm_schema = schema[:properties][:pet_measurements]
372
+ expect(pm_schema).to have_field(:weight, 'number', {:description => "Weight in pounds"})
373
+ expect(pm_schema).to have_field(:height, 'number', {:description => "Height in inches"})
374
+ expect(pm_schema).to have_field(:num_legs, 'number', {:description => "Number of legs", :required => false})
375
+ end
376
+
377
+ it "should return code 203 with spread out 'pet', encapsulated 'pet_measurements' and encapsulated 'pet_history'" do
378
+ returns_obj = subject.returns.detect{|e| e.code == 203 }
379
+
380
+ puts returns_obj.to_json
381
+ expect(returns_obj.code).to eq(203)
382
+ expect(returns_obj.is_array?).to eq(false)
383
+
384
+ expect(returns_obj).to match_field_structure([:pet_name,
385
+ :animal_type,
386
+ {:pet_measurements => [:weight, :height,:num_legs]},
387
+ {:pet_history => [:did_visit_vet, :avg_meals_per_day]},
388
+ {:additional_histories => [:did_visit_vet, :avg_meals_per_day]}
389
+ ])
390
+ end
391
+ it 'should have the 203 response described in the swagger' do
392
+ response = swagger_response_for('/pets/{id}/extra_info', 203)
393
+ expect(response[:description]).to eq('Non-Authoritative Information')
394
+
395
+ schema = response[:schema]
396
+ expect(schema).to have_field(:pet_name, 'string', {:required => false})
397
+ expect(schema).to have_field(:animal_type, 'string')
398
+ expect(schema).to have_field(:pet_measurements, 'object')
399
+ expect(schema).to have_field(:pet_history, 'object')
400
+ expect(schema).to have_field(:additional_histories, 'array')
401
+
402
+ pm_schema = schema[:properties][:pet_measurements]
403
+ expect(pm_schema).to have_field(:weight, 'number', {:description => "Weight in pounds"})
404
+ expect(pm_schema).to have_field(:height, 'number', {:description => "Height in inches"})
405
+ expect(pm_schema).to have_field(:num_legs, 'number', {:description => "Number of legs", :required => false})
406
+
407
+ ph_schema = schema[:properties][:pet_history]
408
+ expect(ph_schema).to have_field(:did_visit_vet, 'boolean')
409
+ expect(ph_schema).to have_field(:avg_meals_per_day, 'number')
410
+
411
+ pa_schema = schema[:properties][:additional_histories]
412
+ expect(pa_schema[:type]).to eq('array')
413
+ pai_schema = schema[:properties][:additional_histories][:items]
414
+ expect(pai_schema).to have_field(:did_visit_vet, 'boolean')
415
+ expect(pai_schema).to have_field(:avg_meals_per_day, 'number')
416
+ end
417
+
418
+ it "should return code 204 with array of integer" do
419
+ returns_obj = subject.returns.detect{|e| e.code == 204 }
420
+
421
+ puts returns_obj.to_json
422
+ expect(returns_obj.code).to eq(204)
423
+ expect(returns_obj.is_array?).to eq(false)
424
+
425
+ expect(returns_obj).to match_field_structure([:int_array, :enum_array])
426
+ end
427
+ it 'should have the 204 response described in the swagger' do
428
+ response = swagger_response_for('/pets/{id}/extra_info', 204)
429
+
430
+ schema = response[:schema]
431
+ expect(schema).to have_field(:int_array, 'array', {items: {type: 'number'}})
432
+ expect(schema).to have_field(:enum_array, 'array', {items: {type: 'string', enum: ['v1','v2','v3']}})
433
+ end
434
+
435
+
436
+ it "should return code matching :unprocessable_entity (422) with spread out 'pet' and 'num_fleas'" do
437
+ returns_obj = subject.returns.detect{|e| e.code == 422 }
438
+
439
+ puts returns_obj.to_json
440
+ expect(returns_obj.code).to eq(422)
441
+
442
+ expect(returns_obj).to match_field_structure([:pet_name,
443
+ :animal_type,
444
+ :num_fleas
445
+ ])
446
+ end
447
+ it 'should have the 422 response described in the swagger' do
448
+ response = swagger_response_for('/pets/{id}/extra_info', 422)
449
+ expect(response[:description]).to eq('Fleas were discovered on the pet')
450
+
451
+ schema = response[:schema]
452
+ expect(schema).to have_field(:pet_name, 'string', {:required => false})
453
+ expect(schema).to have_field(:animal_type, 'string')
454
+ expect(schema).to have_field(:num_fleas, 'number')
455
+ end
456
+
457
+ end
458
+
459
+ end
460
+
461
+ #==============================================================================
462
+ # TaggedDogsController is a demonstration of how tags may be defined in a simple
463
+ # controller class without defining either the controller resource-description
464
+ # block or the controller's superclass's resource-description block.
465
+ #==============================================================================
466
+
467
+ describe TaggedDogsController do
468
+ describe "TaggedDogsController#show_as_properties" do
469
+ subject do
470
+ desc._methods[:show_as_properties]
471
+ end
472
+
473
+ it "should return tags with 'Dogs', and 'Wolves'" do
474
+ returns_obj = subject.tag_list
475
+ puts returns_obj.inspect
476
+
477
+ expect(returns_obj.tags).to eq(%w[Dogs Wolves])
478
+ end
479
+ end
480
+ end
481
+
482
+ #==============================================================================
483
+ # TaggedCatsController is a demonstration of how tags may be defined in the
484
+ # controller's resource description so that they may be automatically prefixed
485
+ # to a particular operation's tags.
486
+ #==============================================================================
487
+
488
+ describe TaggedCatsController do
489
+ describe "TaggedCatsController#show_as_properties" do
490
+ subject do
491
+ desc._methods[:show_as_properties]
492
+ end
493
+
494
+ it "should return tags with 'Dogs', 'Pets', and 'Animals'" do
495
+ returns_obj = subject.tag_list
496
+ puts returns_obj.inspect
497
+
498
+ expect(returns_obj.tags).to eq(%w[Dogs Pets Animals])
499
+ end
500
+ end
501
+
502
+ describe "TaggedCatsController#show_as_same_properties" do
503
+ subject do
504
+ desc._methods[:show_as_same_properties]
505
+ end
506
+
507
+ it "should return tags with 'Dogs', 'Pets', 'Puma', and 'Animals'" do
508
+ returns_obj = subject.tag_list
509
+ puts returns_obj.inspect
510
+
511
+ expect(returns_obj.tags).to eq(%w[Dogs Pets Puma Animals])
512
+ end
513
+ end
514
+ end
515
+
516
+ #==============================================================================
517
+ # PetsUsingSelfDescribingClassesController is a demonstration of how
518
+ # responses can be described using manual generation of a property description
519
+ # array
520
+ #==============================================================================
521
+
522
+
523
+ describe PetsUsingSelfDescribingClassesController do
524
+
525
+ describe "PetsController#pets_described_as_class" do
526
+ subject do
527
+ desc._methods[:pets_described_as_class]
528
+ end
529
+
530
+ it "should return code 200 with array of entries of the format {'pet_name', 'animal_type'}" do
531
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
532
+
533
+ puts returns_obj.to_json
534
+ expect(returns_obj.code).to eq(200)
535
+ expect(returns_obj.is_array?).to eq(true)
536
+
537
+ expect(returns_obj).to match_field_structure([:pet_name, :animal_type])
538
+ end
539
+
540
+ it 'should have the response described in the swagger' do
541
+ response = swagger_response_for('/pets_described_as_class')
542
+ expect(response[:description]).to eq("list of pets")
543
+
544
+ schema = response[:schema]
545
+ expect(schema[:type]).to eq("array")
546
+
547
+ a_schema = schema[:items]
548
+ expect(a_schema).to have_field(:pet_name, 'string', {:description => 'Name of pet', :required => false})
549
+ expect(a_schema).to have_field(:animal_type, 'string', {:description => 'Type of pet', :enum => ['dog','cat','iguana','kangaroo']})
550
+ end
551
+ end
552
+
553
+
554
+ describe "PetsController#pets_with_measurements_described_as_class" do
555
+ subject do
556
+ desc._methods[:pets_with_measurements_described_as_class]
557
+ end
558
+
559
+ it "should return code 200 with spread out 'pet' and encapsulated 'pet_measurements'" do
560
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
561
+
562
+ puts returns_obj.to_json
563
+ expect(returns_obj.code).to eq(200)
564
+ expect(returns_obj.is_array?).to eq(false)
565
+
566
+ expect(returns_obj).to match_field_structure([:pet_name,
567
+ :animal_type,
568
+ {:pet_measurements => [:weight, :height, :num_legs]}
569
+ ])
570
+ end
571
+ it 'should have the 200 response described in the swagger' do
572
+ response = swagger_response_for('/pets_with_measurements_described_as_class/{id}', 200)
573
+ expect(response[:description]).to eq('measurements of the pet')
574
+
575
+ schema = response[:schema]
576
+ expect(schema).to have_field(:pet_name, 'string', {:required => false})
577
+ expect(schema).to have_field(:animal_type, 'string')
578
+ expect(schema).to have_field(:pet_measurements, 'object')
579
+
580
+ pm_schema = schema[:properties][:pet_measurements]
581
+ expect(pm_schema).to have_field(:weight, 'number', {:description => "Weight in pounds"})
582
+ expect(pm_schema).to have_field(:height, 'number', {:description => "Height in inches"})
583
+ expect(pm_schema).to have_field(:num_legs, 'number', {:description => "Number of legs", :required => false})
584
+ end
585
+ end
586
+
587
+ describe "PetsController#pets_with_many_measurements_as_class" do
588
+ subject do
589
+ desc._methods[:pets_with_many_measurements_as_class]
590
+ end
591
+
592
+ it "should return code 200 with pet_name (string) and many_pet_measurements (array of objects)" do
593
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
594
+
595
+ puts returns_obj.to_json
596
+ expect(returns_obj.code).to eq(200)
597
+ expect(returns_obj.is_array?).to eq(false)
598
+
599
+ expect(returns_obj).to match_field_structure([:pet_name,
600
+ {:many_pet_measurements => [:weight, :height]}
601
+ ])
602
+ end
603
+
604
+
605
+ it 'should have the 200 response described in the swagger' do
606
+ response = swagger_response_for('/pets_with_many_measurements_as_class/{id}', 200)
607
+ expect(response[:description]).to eq('measurements of the pet')
608
+
609
+ schema = response[:schema]
610
+ expect(schema).to have_field(:pet_name, 'string', {:required => false})
611
+ expect(schema).to have_field(:many_pet_measurements, 'array')
612
+
613
+ pm_schema = schema[:properties][:many_pet_measurements][:items]
614
+ expect(pm_schema).to have_field(:weight, 'number', {:description => "Weight in pounds"})
615
+ expect(pm_schema).to have_field(:height, 'number', {:description => "Height in inches"})
616
+ end
617
+ end
618
+
619
+ end
620
+
621
+
622
+ #=========================================================
623
+ # PetsUsingAutoViewsController is a demonstration of how
624
+ # responses can be described using logic
625
+ #=========================================================
626
+
627
+ describe PetsUsingAutoViewsController do
628
+
629
+ describe "PetsController#pet_described_using_automated_view" do
630
+ subject do
631
+ desc._methods[:pet_described_using_automated_view]
632
+ end
633
+
634
+ it "should return code 200 with array of entries of the format {'pet_name', 'animal_type'}" do
635
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
636
+
637
+ expect(returns_obj.code).to eq(200)
638
+ expect(returns_obj.is_array?).to eq(false)
639
+
640
+ expect(returns_obj).to match_field_structure([:pet_name, :animal_type, :age])
641
+ end
642
+
643
+ it 'should have the response described in the swagger' do
644
+ response = swagger_response_for('/pet_described_using_automated_view/{id}')
645
+ expect(response[:description]).to eq("like Pet, but different")
646
+
647
+ schema = response[:schema]
648
+ expect(schema[:type]).to eq("object")
649
+
650
+ expect(schema).to have_field(:pet_name, 'string', {:required => true})
651
+ expect(schema).to have_field(:animal_type, 'string', {:required => true})
652
+ expect(schema).to have_field(:age, 'number', {:required => true})
653
+ end
654
+ end
655
+ end
656
+
657
+
658
+ end