apipierails3 0.0.1

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