Committee::InvalidRequest:
#/paths/~1contracts/post/requestBody/content/multipart~1form-data/schema/properties/original_file expected string, but received ActionDispatch::Http::UploadedFile: #<ActionDispatch::Http::UploadedFile:0x00007fa77e1f36a0>
Il définit une API qui reçoit les téléchargements de fichiers au format multipart / form-data
.
L'API est conçue sur la base des spécifications d'OpenAPI 3.0, par exemple:
requestBody:
original_file:
image/png:
schema:
type: string
format: binary
Il est également dans la documentation de Swagger.
Je veux écrire une Rspec pour cette API en utilisant la gemme comité
.
some_controller_spec.rb
#paramètres de formulaire
let(:file_upload_form) {
{
article_id: 1_000,
name: 'This is good article',
# set real existing file
original_file: fixture_file_upload(
Rails.root.join('sample_files/sample.docx'),
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
)
}
}
Les paramètres utilisés comme Request sont définis comme ci-dessus, et le fichier à télécharger est le fichier Docx préparé pour les tests. Ceci est POSTÉ comme suit et le format de l'API est vérifié par la méthode ʻassert_schema_conform`.
some_controller_spec.rb
it 'uploads file' do
post api_v1_images_path,
headers: authenticated_header(user),
params: file_upload_form
expect(response).to have_http_status(:success)
assert_schema_conform
end
La spécification de la requête API est «type: string», mais dans le test, l'objet de «ActionDispatch» est défini, donc j'obtiens une erreur indiquant que ce n'est pas ce à quoi je m'attendais. Cependant, puisqu'il s'agit d'un test de téléchargement de fichier, le traitement interne ne fonctionne pas avec String, il ne peut donc pas être modifié. C'était un problème.
Situation
L'erreur se produit dans le processus ʻOpenAPIParser` suivant. [lib/openapi_parser/schema_validators/string_validator.rb#L11] (https://github.com/ota42y/openapi_parser/blob/44c640cc103bbbb9e8029e41a8889e8fd9350902/lib/openapi_parser/schema_validators/string_validator.rb#L11)
lib/openapi_parser/schema_validators/string_validator.rb
def coerce_and_validate(value, schema, **_keyword_args)
return OpenAPIParser::ValidateError.build_error_result(value, schema) unless value.kind_of?(String)
# ...Omis ci-dessous...
end
Il regarde simplement si «value» est une classe «String», et si ce n'est pas «String», c'est une erreur. Cette fois, puisqu'il s'agit d'un objet de ʻActionDispatch,
value.kind_of? (String) ʻest faux et une erreur se produit.
Dans le cas de format: binay
comme un fichier, vous pouvez changer les conditions afin de ne pas déclencher l'erreur. Créez un patch pour cela.
Créez un module arbitraire qui définit la méthode correspondante coerce_and_validate (value, schema, ** _keyword_args)
comme un lot.
module StringValidatorPatch
def coerce_and_validate(value, schema, **keyword_args)
#Changer ce processus
# https://github.com/ota42y/openapi_parser/blob/61874f0190a86c09bdfb78de5f51cfb6ae16068b/lib/openapi_parser/schema_validators/string_validator.rb#L11
if !value.is_a?(String) && schema.format != 'binary'
return OpenAPIParser::ValidateError.build_error_result(value, schema)
end
# ---Jusque là
value, err = check_enum_include(value, schema)
return [nil, err] if err
value, err = pattern_validate(value, schema)
return [nil, err] if err
unless @datetime_coerce_class.nil?
value, err = coerce_date_time(value, schema)
return [nil, err] if err
end
value, err = validate_max_min_length(value, schema)
return [nil, err] if err
value, err = validate_email_format(value, schema)
return [nil, err] if err
value, err = validate_uuid_format(value, schema)
return [nil, err] if err
[value, nil]
end
end
De cette manière, définissez un module qui redéfinit uniquement la méthode que vous souhaitez modifier. Rouvrez la classe à laquelle vous voulez patcher ce module, cette fois la classe ʻOpenAPIParser :: SchemaValidator :: StringValidator, et utilisez
Module # prepend` pour écraser la méthode.
Module # prepend reference
class OpenAPIParser::SchemaValidator::StringValidator
prepend StringValidatorPatch
end
Cela éliminera l'erreur Committee :: InvalidRequest
Vous pouvez éviter l'erreur Committee :: InvalidRequest
en appliquant un correctif, mais l'application d'un correctif dans une portée globale affectera le tout.
Je souhaite que cette modification ne prenne effet que pour les tests impliquant des téléchargements de fichiers.
Par conséquent, envisagez de le définir dans un "contexte" dans un contexte "do ... end" afin qu'il ne soit reflété que dans le contexte requis de Rspec.
some_controller_spec.rb
RSpec.describe SomeController, type: :request do
context 'some context' do
#Appliquer les correctifs en contexte
module StringValidatorPatch
def coerce_and_validate(value, schema, **keyword_args)
if !value.is_a?(String) && schema.format != 'binary'
return OpenAPIParser::ValidateError.build_error_result(value, schema)
end
# (Ce qui suit est omis)
end
end
class OpenAPIParser::SchemaValidator::StringValidator
prepend StringValidatorPatch
end
#Patch jusqu'à présent
it 'uploads file' do
post api_v1_images_path,
headers: authenticated_header(user),
params: file_upload_form
expect(response).to have_http_status(:success)
assert_schema_conform
end
end
end
Cependant, étant donné que la portée du bloc ne sépare pas les constantes ni ne définit les espaces de noms, nous voulons éviter un traitement tel que la définition de classe à l'intérieur du bloc.
[Lint / ConstantDefinitionInBlock] de Rubocop (https://docs.rubocop.org/rubocop/cops_lint.html#lintconstantDefinitioninblock) est également bloqué.
Si vous voulez définir une constante similaire dans Rspec, utilisez stub_const ()
pour la définir.
stub_const ()
pour patcher là où vous en avez besoinUtilisez Class.new pour rouvrir une classe sans le mot-clé class
..
Vous pouvez également définir une classe en passant un bloc
Foo = Class.new {|c|
def hello; 'hello'; end
}
puts Foo.new.hello # => 'hello'
Utilisez ceci pour définir une classe corrigée. Enregistrez le module StringValidatorPatch
pour le correctif dans le répertoire spec / support
sous le nom string_validator_patch.rb
afin qu'il puisse être chargé.
patched = Class.new(OpenAPIParser::SchemaValidator::StringValidator) do |klass|
klass.prepend StringValidatorPatch
end
stub_const('OpenAPIParser::SchemaValidator::StringValidator', patched)
Si vous passez une classe à l'argument de Class.new ()
, elle sera traitée comme une classe parente, donc dans le cas ci-dessus, patched
sera une classe enfant de ʻOpenAPIParser :: SchemaValidator :: StringValidator. Si vous définissez une constante en utilisant
stub_const ()`, vous pouvez utiliser la classe corrigée.
Implémentez ce processus avec before
ou let
selon les besoins.
--Patch la classe Validator pour effacer l'erreur Committee :: InvalidRequest
--Pour appliquer un patch, définissez un module qui implémente uniquement la méthode et utilisez prepend
pour le refléter.
--RSpec utilise Class.new ()
et stub_const ()
pour patcher localement
spec/support/string_validator_patch.rb
module StringValidatorPatch
def coerce_and_validate(value, schema, **keyword_args)
#Changer ce processus
# https://github.com/ota42y/openapi_parser/blob/61874f0190a86c09bdfb78de5f51cfb6ae16068b/lib/openapi_parser/schema_validators/string_validator.rb#L11
if !value.is_a?(String) && schema.format != 'binary'
return OpenAPIParser::ValidateError.build_error_result(value, schema)
end
# ---Jusque là
value, err = check_enum_include(value, schema)
return [nil, err] if err
value, err = pattern_validate(value, schema)
return [nil, err] if err
unless @datetime_coerce_class.nil?
value, err = coerce_date_time(value, schema)
return [nil, err] if err
end
value, err = validate_max_min_length(value, schema)
return [nil, err] if err
value, err = validate_email_format(value, schema)
return [nil, err] if err
value, err = validate_uuid_format(value, schema)
return [nil, err] if err
[value, nil]
end
end
some_controller_spec.rb
RSpec.describe SomeController, type: :request do
context 'some context' do
let(:file_upload_form) {
{
article_id: 1_000,
name: 'This is good article',
original_file: fixture_file_upload(
Rails.root.join('sample_files/sample.docx'),
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
)
}
}
before do
#Appliquer les correctifs en contexte
patched = Class.new(OpenAPIParser::SchemaValidator::StringValidator) do |klass|
klass.prepend StringValidatorPatch
end
stub_const('OpenAPIParser::SchemaValidator::StringValidator', patched)
end
it 'uploads file' do
post api_v1_images_path,
headers: authenticated_header(user),
params: file_upload_form
expect(response).to have_http_status(:success)
assert_schema_conform
end
end
end
Recommended Posts