chromium/extensions/browser/api/feedback_private/feedback_private_api_chromeos_unittest.cc

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "extensions/browser/api/feedback_private/feedback_private_api.h"

#include "base/json/json_writer.h"
#include "base/memory/ref_counted.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/test/values_test_util.h"
#include "base/values.h"
#include "components/feedback/feedback_common.h"
#include "components/feedback/feedback_constants.h"
#include "extensions/browser/api/feedback_private/feedback_private_api_unittest_base_chromeos.h"
#include "extensions/browser/api/feedback_private/feedback_service.h"
#include "extensions/browser/api/feedback_private/log_source_access_manager.h"
#include "extensions/browser/api/feedback_private/mock_feedback_service.h"

namespace extensions {

namespace {

using api::feedback_private::FeedbackInfo;
using api::feedback_private::ReadLogSourceParams;
using api::feedback_private::ReadLogSourceResult;
using api::feedback_private::SendFeedback::Params;
using feedback::FeedbackData;
using testing::_;
using testing::DoAll;
using testing::Invoke;
using testing::SaveArg;

// Converts |params| to a string containing a JSON dictionary within an argument
// list.
template <typename T>
std::string ParamsToJSON(const T& params) {
  base::Value::List params_value;
  params_value.Append(base::Value(params.ToValue()));
  std::string params_json_string;
  EXPECT_TRUE(base::JSONWriter::Write(params_value, &params_json_string));

  return params_json_string;
}

}  // namespace

class FeedbackPrivateApiUnittest : public FeedbackPrivateApiUnittestBase {
 public:
  FeedbackPrivateApiUnittest() = default;

  FeedbackPrivateApiUnittest(const FeedbackPrivateApiUnittest&) = delete;
  FeedbackPrivateApiUnittest& operator=(const FeedbackPrivateApiUnittest&) =
      delete;

  ~FeedbackPrivateApiUnittest() override = default;

  // FeedbackPrivateApiUnittestBase:
  void TearDown() override {
    FeedbackPrivateAPI::GetFactoryInstance()
        ->Get(browser_context())
        ->GetLogSourceAccessManager()
        ->SetTickClockForTesting(nullptr);

    FeedbackPrivateAPI::GetFactoryInstance()
        ->Get(browser_context())
        ->SetFeedbackServiceForTesting(
            base::MakeRefCounted<FeedbackService>(browser_context()));

    FeedbackPrivateApiUnittestBase::TearDown();
  }

  // Runs the feedbackPrivate.readLogSource() function. See API function
  // definition for argument descriptions.
  //
  // The API function is expected to complete successfully. For running the
  // function with an expectation of an error result, call
  // RunReadLogSourceFunctionWithError().
  //
  // Note that the second argument of the result is a list of strings, but the
  // test class TestSingleLogSource always returns a list containing a single
  // string. To simplify things, the single string result will be returned in
  // |*result_string|, while the reader ID is returned in |*result_reader_id|.
  testing::AssertionResult RunReadLogSourceFunction(
      const ReadLogSourceParams& params,
      int* result_reader_id,
      std::string* result_string) {
    scoped_refptr<FeedbackPrivateReadLogSourceFunction> function =
        base::MakeRefCounted<FeedbackPrivateReadLogSourceFunction>();

    std::optional<base::Value> result_value =
        RunFunctionAndReturnValue(function.get(), ParamsToJSON(params));
    if (!result_value) {
      return testing::AssertionFailure() << "No result";
    }

    auto result = ReadLogSourceResult::FromValue(*result_value);
    if (!result) {
      return testing::AssertionFailure()
             << "Unable to parse a valid result from " << *result_value;
    }

    if (result->log_lines.size() != 1) {
      return testing::AssertionFailure()
             << "Expected |log_lines| to contain 1 string, actual number: "
             << result->log_lines.size();
    }

    *result_reader_id = result->reader_id;
    *result_string = result->log_lines[0];

    return testing::AssertionSuccess();
  }

  // Similar to RunReadLogSourceFunction(), but expects to return an error.
  // Returns a string containing the error message. Does not return any result
  // from the API function.
  std::string RunReadLogSourceFunctionWithError(
      const ReadLogSourceParams& params) {
    scoped_refptr<FeedbackPrivateReadLogSourceFunction> function =
        base::MakeRefCounted<FeedbackPrivateReadLogSourceFunction>();

    return RunFunctionAndReturnError(function.get(), ParamsToJSON(params));
  }

  // Runs the feedbackPrivate.sendFeedback() function. See API function
  // definition for argument descriptions.
  scoped_refptr<FeedbackData> RunSendFeedbackFunction(
      const std::string& args,  // The payload that comes from client
      const FeedbackParams& expected_params) {
    base::Value values = base::test::ParseJson(args);
    EXPECT_TRUE(values.is_list());

    std::optional<api::feedback_private::SendFeedback::Params> params =
        api::feedback_private::SendFeedback::Params::Create(values.GetList());
    EXPECT_TRUE(params);

    scoped_refptr<FeedbackData> actual_feedback_data;
    SetupMockFeedbackService(expected_params, actual_feedback_data);

    auto function = base::MakeRefCounted<FeedbackPrivateSendFeedbackFunction>();

    std::optional<base::Value> result_value =
        RunFunctionAndReturnValue(function.get(), args);
    EXPECT_TRUE(result_value);

    return actual_feedback_data;
  }

  void SetupMockFeedbackService(
      const FeedbackParams& expected_params,
      scoped_refptr<FeedbackData>& actual_feedback_data) {
    auto mock = base::MakeRefCounted<MockFeedbackService>(browser_context());

    // scoped_refptr<FeedbackData> actual_feedback_data;
    EXPECT_CALL(*mock, RedactThenSendFeedback(_, _, _))
        .WillOnce([&](const FeedbackParams& params,
                      scoped_refptr<FeedbackData> feedback_data,
                      SendFeedbackCallback callback) {
          // Pass the feedback data out to verify its properties
          actual_feedback_data = feedback_data;
          // Verify that the flags in params are set correctly
          EXPECT_EQ(expected_params.is_internal_email,
                    params.is_internal_email);
          EXPECT_EQ(expected_params.load_system_info, params.load_system_info);
          EXPECT_EQ(expected_params.send_tab_titles, params.send_tab_titles);
          EXPECT_EQ(expected_params.send_histograms, params.send_histograms);
          EXPECT_EQ(expected_params.send_bluetooth_logs,
                    params.send_bluetooth_logs);
          EXPECT_EQ(expected_params.send_autofill_metadata,
                    params.send_autofill_metadata);

          std::move(callback).Run(true);
        });

    FeedbackPrivateAPI::GetFactoryInstance()
        ->Get(browser_context())
        ->SetFeedbackServiceForTesting(
            static_cast<scoped_refptr<FeedbackService>>(std::move(mock)));
  }
};

TEST_F(FeedbackPrivateApiUnittest, ReadLogSourceInvalidId) {
  const base::TimeDelta timeout(base::Milliseconds(0));
  LogSourceAccessManager::SetRateLimitingTimeoutForTesting(&timeout);

  ReadLogSourceParams params;
  params.source = api::feedback_private::LogSource::kMessages;
  params.incremental = true;
  params.reader_id = 9999;

  EXPECT_NE("", RunReadLogSourceFunctionWithError(params));
}

TEST_F(FeedbackPrivateApiUnittest, ReadLogSourceNonIncremental) {
  const base::TimeDelta timeout(base::Milliseconds(0));
  LogSourceAccessManager::SetRateLimitingTimeoutForTesting(&timeout);

  ReadLogSourceParams params;
  params.source = api::feedback_private::LogSource::kMessages;
  params.incremental = false;

  // Test multiple non-incremental reads.
  int result_reader_id = -1;
  std::string result_string;
  EXPECT_TRUE(
      RunReadLogSourceFunction(params, &result_reader_id, &result_string));
  EXPECT_EQ(0, result_reader_id);
  EXPECT_EQ("a", result_string);

  result_reader_id = -1;
  result_string.clear();
  EXPECT_TRUE(
      RunReadLogSourceFunction(params, &result_reader_id, &result_string));
  EXPECT_EQ(0, result_reader_id);
  EXPECT_EQ("a", result_string);

  result_reader_id = -1;
  result_string.clear();
  EXPECT_TRUE(
      RunReadLogSourceFunction(params, &result_reader_id, &result_string));
  EXPECT_EQ(0, result_reader_id);
  EXPECT_EQ("a", result_string);
}

TEST_F(FeedbackPrivateApiUnittest, ReadLogSourceIncremental) {
  const base::TimeDelta timeout(base::Milliseconds(0));
  LogSourceAccessManager::SetRateLimitingTimeoutForTesting(&timeout);

  ReadLogSourceParams params;
  params.source = api::feedback_private::LogSource::kMessages;
  params.incremental = true;

  int result_reader_id = 0;
  std::string result_string;
  EXPECT_TRUE(
      RunReadLogSourceFunction(params, &result_reader_id, &result_string));
  EXPECT_GT(result_reader_id, 0);
  EXPECT_EQ("a", result_string);
  params.reader_id = result_reader_id;

  EXPECT_TRUE(
      RunReadLogSourceFunction(params, &result_reader_id, &result_string));
  EXPECT_EQ(*params.reader_id, result_reader_id);
  EXPECT_EQ(" bb", result_string);

  EXPECT_TRUE(
      RunReadLogSourceFunction(params, &result_reader_id, &result_string));
  EXPECT_EQ(*params.reader_id, result_reader_id);
  EXPECT_EQ("  ccc", result_string);

  // End the incremental read.
  params.incremental = false;
  EXPECT_TRUE(
      RunReadLogSourceFunction(params, &result_reader_id, &result_string));
  EXPECT_EQ(0, result_reader_id);
  EXPECT_EQ("   dddd", result_string);

  // The log source will no longer be valid if we try to read it.
  params.incremental = true;
  EXPECT_NE("", RunReadLogSourceFunctionWithError(params));
}

TEST_F(FeedbackPrivateApiUnittest, Redact) {
  const base::TimeDelta timeout(base::Milliseconds(0));
  LogSourceAccessManager::SetRateLimitingTimeoutForTesting(&timeout);

  ReadLogSourceParams params;
  params.source = api::feedback_private::LogSource::kMessages;
  params.incremental = true;

  int result_reader_id = 0;
  std::string result_string;
  // Skip over all the alphabetic results, to test redaction of the subsequent
  // MAC address.
  for (int i = 0; i < 26; ++i) {
    EXPECT_TRUE(
        RunReadLogSourceFunction(params, &result_reader_id, &result_string));
    EXPECT_GT(result_reader_id, 0);
    params.reader_id = result_reader_id;
  }

  EXPECT_TRUE(
      RunReadLogSourceFunction(params, &result_reader_id, &result_string));
  EXPECT_EQ(*params.reader_id, result_reader_id);
  EXPECT_EQ("(MAC OUI=11:22:33 IFACE=1)", result_string);
}

TEST_F(FeedbackPrivateApiUnittest, ReadLogSourceMultipleSources) {
  const base::TimeDelta timeout(base::Milliseconds(0));
  LogSourceAccessManager::SetRateLimitingTimeoutForTesting(&timeout);

  int result_reader_id = 0;
  std::string result_string;

  // Attempt to open LOG_SOURCE_MESSAGES twice.
  ReadLogSourceParams params_1st_read;
  params_1st_read.source = api::feedback_private::LogSource::kMessages;
  params_1st_read.incremental = true;
  EXPECT_TRUE(RunReadLogSourceFunction(params_1st_read, &result_reader_id,
                                       &result_string));
  EXPECT_GT(result_reader_id, 0);
  // Store the reader ID back into the params to set up for the next call.
  params_1st_read.reader_id = result_reader_id;

  // Create a second reader from the same log source.
  ReadLogSourceParams params_1st_read_repeated;
  params_1st_read_repeated.source = api::feedback_private::LogSource::kMessages;
  params_1st_read_repeated.incremental = true;
  EXPECT_TRUE(RunReadLogSourceFunction(params_1st_read_repeated,
                                       &result_reader_id, &result_string));

  // Attempt to open LOG_SOURCE_UI_LATEST twice.
  ReadLogSourceParams params_2nd_read;
  params_2nd_read.source = api::feedback_private::LogSource::kUiLatest;
  params_2nd_read.incremental = true;
  result_reader_id = -1;
  EXPECT_TRUE(RunReadLogSourceFunction(params_2nd_read, &result_reader_id,
                                       &result_string));
  EXPECT_GT(result_reader_id, 0);
  EXPECT_NE(*params_1st_read.reader_id, result_reader_id);
  // Store the reader ID back into the params to set up for the next call.
  params_2nd_read.reader_id = result_reader_id;

  // Create a second reader from the same log source.
  ReadLogSourceParams params_2nd_read_repeated;
  params_2nd_read_repeated.source = api::feedback_private::LogSource::kUiLatest;
  params_2nd_read_repeated.incremental = true;
  EXPECT_TRUE(RunReadLogSourceFunction(params_2nd_read_repeated,
                                       &result_reader_id, &result_string));

  // Close the two open log source readers, and make sure new ones can be
  // opened.
  params_1st_read.incremental = false;
  result_reader_id = -1;
  EXPECT_TRUE(RunReadLogSourceFunction(params_1st_read, &result_reader_id,
                                       &result_string));
  EXPECT_EQ(0, result_reader_id);

  params_2nd_read.incremental = false;
  result_reader_id = -1;
  EXPECT_TRUE(RunReadLogSourceFunction(params_2nd_read, &result_reader_id,
                                       &result_string));
  EXPECT_EQ(0, result_reader_id);

  EXPECT_TRUE(RunReadLogSourceFunction(params_1st_read_repeated,
                                       &result_reader_id, &result_string));
  EXPECT_GT(result_reader_id, 0);
  const int new_read_result_reader_id = result_reader_id;

  EXPECT_TRUE(RunReadLogSourceFunction(params_2nd_read_repeated,
                                       &result_reader_id, &result_string));
  EXPECT_GT(result_reader_id, 0);
  EXPECT_NE(new_read_result_reader_id, result_reader_id);
}

TEST_F(FeedbackPrivateApiUnittest, ReadLogSourceWithAccessTimeouts) {
  const base::TimeDelta timeout(base::Milliseconds(100));
  LogSourceAccessManager::SetMaxNumBurstAccessesForTesting(1);
  LogSourceAccessManager::SetRateLimitingTimeoutForTesting(&timeout);

  base::SimpleTestTickClock test_clock;
  FeedbackPrivateAPI::GetFactoryInstance()
      ->Get(browser_context())
      ->GetLogSourceAccessManager()
      ->SetTickClockForTesting(&test_clock);

  ReadLogSourceParams params;
  params.source = api::feedback_private::LogSource::kMessages;
  params.incremental = true;
  int result_reader_id = 0;
  std::string result_string;

  // |test_clock| must start out at something other than 0, which is interpreted
  // as an invalid value.
  test_clock.Advance(base::Milliseconds(100));

  EXPECT_TRUE(
      RunReadLogSourceFunction(params, &result_reader_id, &result_string));
  EXPECT_EQ(1, result_reader_id);
  params.reader_id = result_reader_id;

  // Immediately perform another read. This is not allowed. (empty result)
  EXPECT_FALSE(
      RunReadLogSourceFunction(params, &result_reader_id, &result_string));

  // Advance to t=120, but it will not be allowed. (empty result)
  test_clock.Advance(base::Milliseconds(20));
  EXPECT_FALSE(
      RunReadLogSourceFunction(params, &result_reader_id, &result_string));

  // Advance to t=150, but still not allowed.
  test_clock.Advance(base::Milliseconds(30));
  EXPECT_FALSE(
      RunReadLogSourceFunction(params, &result_reader_id, &result_string));

  // Advance to t=199, but still not allowed. (empty result)
  test_clock.Advance(base::Milliseconds(49));
  EXPECT_FALSE(
      RunReadLogSourceFunction(params, &result_reader_id, &result_string));

  // Advance to t=210, annd the access is finally allowed.
  test_clock.Advance(base::Milliseconds(11));
  EXPECT_TRUE(
      RunReadLogSourceFunction(params, &result_reader_id, &result_string));

  // Advance to t=309, but it will not be allowed. (empty result)
  test_clock.Advance(base::Milliseconds(99));
  EXPECT_FALSE(
      RunReadLogSourceFunction(params, &result_reader_id, &result_string));

  // Another read is finally allowed at t=310.
  test_clock.Advance(base::Milliseconds(1));
  EXPECT_TRUE(
      RunReadLogSourceFunction(params, &result_reader_id, &result_string));
}

TEST_F(FeedbackPrivateApiUnittest, SendFeedbackWithSysInfo) {
  const std::string args = R"([
  {
    "attachedFile": {
      "data": {},
      "name": "C:\\fakepath\\chrome_40px.svg"
    },
    "assistantDebugInfoAllowed": true,
    "attachedFileBlobUuid": "2e3996de-db9e-4c3d-b62c-80d19b6418b9",
    "autofillMetadata": "",
    "categoryTag": "test-tag",
    "description": "test-desc",
    "descriptionPlaceholder": "",
    "email": "[email protected]",
    "flow": "regular",
    "fromAssistant": true,
    "fromAutofill": false,
    "includeBluetoothLogs": true,
    "pageUrl": "https://test.com",
    "productId": 1122,
    "screenshot": {},
    "screenshotBlobUuid": "3e72cc3c-550f-49f0-b5d2-bf21f3fbab15",
    "sendAutofillMetadata": false,
    "sendBluetoothLogs": true,
    "sendWifiDebugLogs": false,
    "sendHistograms": true,
    "sendTabTitles": true,
    "systemInformation": [
      {"key": "mem_usage_with_title", "value": "some sensitive info"}
    ],
    "traceId": 9966,
    "useSystemWindowFrame": false
  }
])";

  const FeedbackParams expected_params{/*is_internal_email=*/false,
                                       /*load_system_info=*/false,
                                       /*send_tab_titles=*/true,
                                       /*send_histograms=*/true,
                                       /*send_bluetooth_logs=*/true,
                                       /*send_wifi_debug_logs=*/false,
                                       /*send_autofill_metadata=*/false};
  auto feedback_data = RunSendFeedbackFunction(args, expected_params);

  EXPECT_EQ(9966, feedback_data->trace_id());
  EXPECT_EQ(1122, feedback_data->product_id());
  EXPECT_EQ("chrome_40px.svg", feedback_data->attached_filename());
  EXPECT_EQ("test-desc", feedback_data->description());
  EXPECT_EQ("test-tag", feedback_data->category_tag());
  EXPECT_EQ("[email protected]", feedback_data->user_email());
  EXPECT_EQ("2e3996de-db9e-4c3d-b62c-80d19b6418b9",
            feedback_data->attached_file_uuid());
  EXPECT_EQ("3e72cc3c-550f-49f0-b5d2-bf21f3fbab15",
            feedback_data->screenshot_uuid());
  EXPECT_EQ("https://test.com", feedback_data->page_url());

  EXPECT_TRUE(feedback_data->from_assistant());
  EXPECT_TRUE(feedback_data->assistant_debug_info_allowed());
  EXPECT_TRUE(feedback_data->sys_info());
  EXPECT_TRUE(feedback_data->sys_info()->count("mem_usage_with_title"));
}

TEST_F(FeedbackPrivateApiUnittest, SendFeedbackWithoutSysInfo) {
  const std::string args = R"([
  {
    "attachedFile": {
      "data": {},
      "name":""
    },
    "assistantDebugInfoAllowed": false,
    "autofillMetadata": "",
    "categoryTag": "",
    "description": "test-desc",
    "descriptionPlaceholder": "",
    "email": "",
    "flow": "regular",
    "fromAssistant": false,
    "fromAutofill": false,
    "includeBluetoothLogs": false,
    "pageUrl": "",
    "screenshot": {},
    "screenshotBlobUuid": "",
    "sendAutofillMetadata": false,
    "sendBluetoothLogs": false,
    "sendWifiDebugLogs": false,
    "sendHistograms": false,
    "sendTabTitles": false,
    "systemInformation": [],
    "useSystemWindowFrame": false
  }
])";

  const FeedbackParams expected_params{/*is_internal_email=*/false,
                                       /*load_system_info=*/false,
                                       /*send_tab_titles=*/false,
                                       /*send_histograms=*/false,
                                       /*send_bluetooth_logs=*/false,
                                       /*send_wifi_debug_logs=*/false,
                                       /*send_autofill_metadata=*/false};
  auto feedback_data = RunSendFeedbackFunction(args, expected_params);

  EXPECT_EQ(0, feedback_data->trace_id());
  EXPECT_EQ(-1, feedback_data->product_id());
  EXPECT_EQ("", feedback_data->attached_filename());
  EXPECT_EQ("test-desc", feedback_data->description());
  EXPECT_EQ("", feedback_data->category_tag());
  EXPECT_EQ("", feedback_data->user_email());
  EXPECT_EQ("", feedback_data->attached_file_uuid());
  EXPECT_EQ("", feedback_data->screenshot_uuid());
  EXPECT_EQ("", feedback_data->page_url());

  EXPECT_FALSE(feedback_data->from_assistant());
  EXPECT_FALSE(feedback_data->assistant_debug_info_allowed());
  EXPECT_TRUE(feedback_data->sys_info());
  EXPECT_FALSE(feedback_data->sys_info()->count("mem_usage_with_title"));
}

TEST_F(FeedbackPrivateApiUnittest, SendFeedbackV2WithOptionsTrue) {
  const std::string args = R"([
  {
    "attachedFile": {
      "data": {},
      "name": "C:\\fakepath\\chrome_40px.svg"
    },
    "assistantDebugInfoAllowed": true,
    "attachedFileBlobUuid": "2e3996de-db9e-4c3d-b62c-80d19b6418b9",
    "autofillMetadata": "",
    "categoryTag": "test-tag",
    "description": "test-desc",
    "descriptionPlaceholder": "",
    "email": "[email protected]",
    "flow": "regular",
    "fromAssistant": true,
    "fromAutofill": false,
    "includeBluetoothLogs": true,
    "pageUrl": "https://test.com",
    "productId": 1122,
    "screenshot": {},
    "screenshotBlobUuid": "3e72cc3c-550f-49f0-b5d2-bf21f3fbab15",
    "sendAutofillMetadata": false,
    "sendBluetoothLogs": true,
    "sendWifiDebugLogs": false,
    "sendHistograms": true,
    "sendTabTitles": true,
    "systemInformation": [],
    "traceId": 9966,
    "useSystemWindowFrame": false
  },
  true,
  1.625859975163e+12
])";

  const FeedbackParams expected_params{/*is_internal_email=*/false,
                                       /*load_system_info=*/true,
                                       /*send_tab_titles=*/true,
                                       /*send_histograms=*/true,
                                       /*send_bluetooth_logs=*/true,
                                       /*send_wifi_debug_logs=*/false,
                                       /*send_autofill_metadata=*/false};
  auto feedback_data = RunSendFeedbackFunction(args, expected_params);

  EXPECT_EQ(9966, feedback_data->trace_id());
  EXPECT_EQ(1122, feedback_data->product_id());
  EXPECT_EQ("chrome_40px.svg", feedback_data->attached_filename());
  EXPECT_EQ("test-desc", feedback_data->description());
  EXPECT_EQ("test-tag", feedback_data->category_tag());
  EXPECT_EQ("[email protected]", feedback_data->user_email());
  EXPECT_EQ("2e3996de-db9e-4c3d-b62c-80d19b6418b9",
            feedback_data->attached_file_uuid());
  EXPECT_EQ("3e72cc3c-550f-49f0-b5d2-bf21f3fbab15",
            feedback_data->screenshot_uuid());
  EXPECT_EQ("https://test.com", feedback_data->page_url());

  EXPECT_TRUE(feedback_data->from_assistant());
  EXPECT_TRUE(feedback_data->assistant_debug_info_allowed());
  EXPECT_TRUE(feedback_data->sys_info());
  EXPECT_TRUE(feedback_data->sys_info()->size() == 0);
}

TEST_F(FeedbackPrivateApiUnittest, SendFeedbackV2WithOptionsFalse) {
  const std::string args = R"([
  {
    "attachedFile": {
      "data": {},
      "name":""
    },
    "assistantDebugInfoAllowed": false,
    "autofillMetadata": "",
    "categoryTag": "",
    "description": "test-desc",
    "descriptionPlaceholder": "",
    "email": "",
    "flow": "regular",
    "fromAssistant": false,
    "fromAutofill": false,
    "includeBluetoothLogs": false,
    "pageUrl": "",
    "screenshot": {},
    "screenshotBlobUuid": "",
    "sendAutofillMetadata": false,
    "sendBluetoothLogs": false,
    "sendWifiDebugLogs": false,
    "sendHistograms": false,
    "sendTabTitles": false,
    "systemInformation": [],
    "useSystemWindowFrame": false
  },
  false,
  1.625859975163e+12
])";

  const FeedbackParams expected_params{/*is_internal_email=*/false,
                                       /*load_system_info=*/false,
                                       /*send_tab_titles=*/false,
                                       /*send_histograms=*/false,
                                       /*send_bluetooth_logs=*/false,
                                       /*send_wifi_debug_logs=*/false,
                                       /*send_autofill_metadata=*/false};
  auto feedback_data = RunSendFeedbackFunction(args, expected_params);

  EXPECT_EQ(0, feedback_data->trace_id());
  EXPECT_EQ(-1, feedback_data->product_id());
  EXPECT_EQ("", feedback_data->attached_filename());
  EXPECT_EQ("test-desc", feedback_data->description());
  EXPECT_EQ("", feedback_data->category_tag());
  EXPECT_EQ("", feedback_data->user_email());
  EXPECT_EQ("", feedback_data->attached_file_uuid());
  EXPECT_EQ("", feedback_data->screenshot_uuid());
  EXPECT_EQ("", feedback_data->page_url());

  EXPECT_FALSE(feedback_data->from_assistant());
  EXPECT_FALSE(feedback_data->assistant_debug_info_allowed());
  EXPECT_TRUE(feedback_data->sys_info());
  EXPECT_TRUE(feedback_data->sys_info()->size() == 0);
}

TEST_F(FeedbackPrivateApiUnittest, SendFeedbackWithAutofillInfo) {
  const std::string args = R"([
  {
    "attachedFile": {
      "data": {},
      "name": "C:\\fakepath\\chrome_40px.svg"
    },
    "assistantDebugInfoAllowed": true,
    "attachedFileBlobUuid": "2e3996de-db9e-4c3d-b62c-80d19b6418b9",
    "autofillMetadata": "test-metadata",
    "categoryTag": "test-tag",
    "description": "test-desc",
    "descriptionPlaceholder": "",
    "email": "[email protected]",
    "flow": "regular",
    "fromAssistant": false,
    "fromAutofill": true,
    "includeBluetoothLogs": true,
    "pageUrl": "https://test.com",
    "productId": 1122,
    "screenshot": {},
    "screenshotBlobUuid": "3e72cc3c-550f-49f0-b5d2-bf21f3fbab15",
    "sendAutofillMetadata": true,
    "sendBluetoothLogs": true,
    "sendWifiDebugLogs": false,
    "sendHistograms": true,
    "sendTabTitles": true,
    "systemInformation": [],
    "traceId": 9966,
    "useSystemWindowFrame": false
  }
])";

  const FeedbackParams expected_params{/*is_internal_email=*/false,
                                       /*load_system_info=*/false,
                                       /*send_tab_titles=*/true,
                                       /*send_histograms=*/true,
                                       /*send_bluetooth_logs=*/true,
                                       /*send_wifi_debug_logs=*/false,
                                       /*send_autofill_metadata=*/true};
  auto feedback_data = RunSendFeedbackFunction(args, expected_params);

  EXPECT_EQ(9966, feedback_data->trace_id());
  EXPECT_EQ(1122, feedback_data->product_id());
  EXPECT_EQ("chrome_40px.svg", feedback_data->attached_filename());
  EXPECT_EQ("test-desc", feedback_data->description());
  EXPECT_EQ("test-tag", feedback_data->category_tag());
  EXPECT_EQ("[email protected]", feedback_data->user_email());
  EXPECT_EQ("2e3996de-db9e-4c3d-b62c-80d19b6418b9",
            feedback_data->attached_file_uuid());
  EXPECT_EQ("3e72cc3c-550f-49f0-b5d2-bf21f3fbab15",
            feedback_data->screenshot_uuid());
  EXPECT_EQ("https://test.com", feedback_data->page_url());
  EXPECT_EQ("test-metadata", feedback_data->autofill_metadata());

  EXPECT_FALSE(feedback_data->from_assistant());
  EXPECT_TRUE(feedback_data->assistant_debug_info_allowed());
  EXPECT_TRUE(feedback_data->sys_info());
}

TEST_F(FeedbackPrivateApiUnittest, SendFeedbackAiFlow) {
  const std::string args = R"([
  {
    "aiMetadata": "test-ai-metadata",
    "categoryTag": "test-category",
    "description": "test-desc",
    "descriptionPlaceholder": "",
    "email": "",
    "flow": "ai",
  }
])";

  const FeedbackParams expected_params{/*is_internal_email=*/false,
                                       /*load_system_info=*/false,
                                       /*send_tab_titles=*/false,
                                       /*send_histograms=*/false,
                                       /*send_bluetooth_logs=*/false,
                                       /*send_wifi_debug_logs=*/false,
                                       /*send_autofill_metadata=*/false};
  auto feedback_data = RunSendFeedbackFunction(args, expected_params);

  EXPECT_EQ(-1, feedback_data->product_id());
  EXPECT_EQ("test-desc", feedback_data->description());
  EXPECT_EQ("test-category", feedback_data->category_tag());
  EXPECT_EQ("test-ai-metadata", feedback_data->ai_metadata());
}

TEST_F(FeedbackPrivateApiUnittest, SendFeedbackInfoAiFlow) {
  extensions::FeedbackPrivateAPI* api =
      FeedbackPrivateAPI::GetFactoryInstance()->Get(browser_context());

  std::string unused;
  auto feedback_info = api->CreateFeedbackInfo(
      /*description_template=*/unused, /*description_placeholder_text=*/unused,
      /*category_tag=*/unused, /*extra_diagnostics=*/unused,
      /*page_url=*/GURL(),
      /*flow=*/api::feedback_private::FeedbackFlow::kAi,
      /*from_assistant=*/false, /*include_bluetooth_logs=*/false,
      /*show_questionnaire=*/false, /*from_chrome_labs_or_kaleidoscope=*/false,
      /*from_autofill=*/false, /*autofill_metadata=*/base::Value::Dict(),
      /*ai_metadata=*/base::Value::Dict());

  EXPECT_EQ(FeedbackCommon::GetChromeBrowserProductId(),
            feedback_info->product_id);

#if BUILDFLAG(IS_CHROMEOS_ASH)
  auto chromeos_ai_metadata = base::Value::Dict();
  chromeos_ai_metadata.Set(feedback::kSeaPenMetadataKey, "true");
  feedback_info = api->CreateFeedbackInfo(
      /*description_template=*/unused, /*description_placeholder_text=*/unused,
      /*category_tag=*/unused, /*extra_diagnostics=*/unused,
      /*page_url=*/GURL(),
      /*flow=*/api::feedback_private::FeedbackFlow::kAi,
      /*from_assistant=*/false, /*include_bluetooth_logs=*/false,
      /*show_questionnaire=*/false, /*from_chrome_labs_or_kaleidoscope=*/false,
      /*from_autofill=*/false, /*autofill_metadata=*/base::Value::Dict(),
      chromeos_ai_metadata);

  EXPECT_EQ(FeedbackCommon::GetChromeOSProductId(), feedback_info->product_id);
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
}

}  // namespace extensions