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