| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "extensions/renderer/bindings/api_response_validator.h" |
| |
| #include <vector> |
| |
| #include "base/auto_reset.h" |
| #include "base/bind.h" |
| #include "base/optional.h" |
| #include "extensions/renderer/bindings/api_binding_test.h" |
| #include "extensions/renderer/bindings/api_binding_test_util.h" |
| #include "extensions/renderer/bindings/api_binding_util.h" |
| #include "extensions/renderer/bindings/api_invocation_errors.h" |
| #include "extensions/renderer/bindings/api_signature.h" |
| #include "extensions/renderer/bindings/api_type_reference_map.h" |
| #include "extensions/renderer/bindings/argument_spec.h" |
| #include "extensions/renderer/bindings/argument_spec_builder.h" |
| #include "gin/converter.h" |
| #include "v8/include/v8.h" |
| |
| namespace extensions { |
| namespace { |
| |
| constexpr char kMethodName[] = "oneString"; |
| |
| std::unique_ptr<APISignature> OneStringSignature() { |
| std::vector<std::unique_ptr<ArgumentSpec>> specs; |
| specs.push_back(ArgumentSpecBuilder(ArgumentType::STRING, "str").Build()); |
| return std::make_unique<APISignature>(std::move(specs)); |
| } |
| |
| std::vector<v8::Local<v8::Value>> StringToV8Vector( |
| v8::Local<v8::Context> context, |
| const char* args) { |
| v8::Local<v8::Value> v8_args = V8ValueFromScriptSource(context, args); |
| if (v8_args.IsEmpty()) { |
| ADD_FAILURE() << "Could not convert args: " << args; |
| return std::vector<v8::Local<v8::Value>>(); |
| } |
| EXPECT_TRUE(v8_args->IsArray()); |
| std::vector<v8::Local<v8::Value>> vector_args; |
| EXPECT_TRUE(gin::ConvertFromV8(context->GetIsolate(), v8_args, &vector_args)); |
| return vector_args; |
| } |
| |
| } // namespace |
| |
| class APIResponseValidatorTest : public APIBindingTest { |
| public: |
| APIResponseValidatorTest() |
| : type_refs_(APITypeReferenceMap::InitializeTypeCallback()), |
| test_handler_( |
| base::BindRepeating(&APIResponseValidatorTest::OnValidationFailure, |
| base::Unretained(this))), |
| validator_(&type_refs_) {} |
| ~APIResponseValidatorTest() override = default; |
| |
| void SetUp() override { |
| APIBindingTest::SetUp(); |
| response_validation_override_ = |
| binding::SetResponseValidationEnabledForTesting(true); |
| type_refs_.AddCallbackSignature(kMethodName, OneStringSignature()); |
| } |
| |
| void TearDown() override { |
| response_validation_override_.reset(); |
| APIBindingTest::TearDown(); |
| } |
| |
| APIResponseValidator* validator() { return &validator_; } |
| const base::Optional<std::string>& failure_method() const { |
| return failure_method_; |
| } |
| const base::Optional<std::string>& failure_error() const { |
| return failure_error_; |
| } |
| |
| void reset() { |
| failure_method_ = base::nullopt; |
| failure_error_ = base::nullopt; |
| } |
| |
| private: |
| void OnValidationFailure(const std::string& method, |
| const std::string& error) { |
| failure_method_ = method; |
| failure_error_ = error; |
| } |
| |
| std::unique_ptr<base::AutoReset<bool>> response_validation_override_; |
| |
| APITypeReferenceMap type_refs_; |
| APIResponseValidator::TestHandler test_handler_; |
| APIResponseValidator validator_; |
| |
| base::Optional<std::string> failure_method_; |
| base::Optional<std::string> failure_error_; |
| |
| DISALLOW_COPY_AND_ASSIGN(APIResponseValidatorTest); |
| }; |
| |
| TEST_F(APIResponseValidatorTest, TestValidation) { |
| using namespace api_errors; |
| |
| v8::HandleScope handle_scope(isolate()); |
| v8::Local<v8::Context> context = MainContext(); |
| |
| validator()->ValidateResponse( |
| context, kMethodName, StringToV8Vector(context, "['hi']"), std::string(), |
| APIResponseValidator::CallbackType::kCallerProvided); |
| EXPECT_FALSE(failure_method()); |
| EXPECT_FALSE(failure_error()); |
| |
| validator()->ValidateResponse( |
| context, kMethodName, StringToV8Vector(context, "[1]"), std::string(), |
| APIResponseValidator::CallbackType::kCallerProvided); |
| EXPECT_EQ(kMethodName, failure_method().value_or("no value")); |
| EXPECT_EQ(ArgumentError("str", InvalidType(kTypeString, kTypeInteger)), |
| failure_error().value_or("no value")); |
| } |
| |
| TEST_F(APIResponseValidatorTest, TestDoesNotValidateWithAPIProvidedCallback) { |
| v8::HandleScope handle_scope(isolate()); |
| v8::Local<v8::Context> context = MainContext(); |
| |
| validator()->ValidateResponse( |
| context, kMethodName, StringToV8Vector(context, "['hi']"), std::string(), |
| APIResponseValidator::CallbackType::kCallerProvided); |
| EXPECT_FALSE(failure_method()); |
| EXPECT_FALSE(failure_error()); |
| |
| validator()->ValidateResponse( |
| context, kMethodName, StringToV8Vector(context, "[1]"), std::string(), |
| APIResponseValidator::CallbackType::kAPIProvided); |
| EXPECT_FALSE(failure_method()); |
| EXPECT_FALSE(failure_error()); |
| } |
| |
| TEST_F(APIResponseValidatorTest, TestDoesNotValidateWhenAPIErrorPresent) { |
| v8::HandleScope handle_scope(isolate()); |
| v8::Local<v8::Context> context = MainContext(); |
| |
| validator()->ValidateResponse( |
| context, kMethodName, {}, "Some API Error", |
| APIResponseValidator::CallbackType::kCallerProvided); |
| EXPECT_FALSE(failure_method()); |
| EXPECT_FALSE(failure_error()); |
| } |
| |
| TEST_F(APIResponseValidatorTest, TestDoesNotValidateWithUnknownSignature) { |
| v8::HandleScope handle_scope(isolate()); |
| v8::Local<v8::Context> context = MainContext(); |
| |
| validator()->ValidateResponse( |
| context, "differentMethodName", StringToV8Vector(context, "[true]"), |
| std::string(), APIResponseValidator::CallbackType::kCallerProvided); |
| EXPECT_FALSE(failure_method()); |
| EXPECT_FALSE(failure_error()); |
| } |
| |
| } // namespace extensions |