Commit caaf5c3d authored by Lucas Tenório's avatar Lucas Tenório Committed by Commit Bot

Add unit tests for the Onboarding Controller.

This change also makes the supervision server flags simpler, since we
are getting close to a final server implementation.

Bug: 958995
Change-Id: Ic3c07ef4a992faf6238bbd2a492848d26174a461
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1621010
Commit-Queue: Lucas Tenório <ltenorio@chromium.org>
Reviewed-by: default avatarMichael Giuffrida <michaelpg@chromium.org>
Reviewed-by: default avatarAlexander Alekseev <alemate@chromium.org>
Cr-Commit-Position: refs/heads/master@{#662196}
parent d9acaffc
......@@ -1940,6 +1940,8 @@ source_set("chromeos") {
"smb_client/temp_file_manager.h",
"startup_settings_cache.cc",
"startup_settings_cache.h",
"supervision/onboarding_constants.cc",
"supervision/onboarding_constants.h",
"supervision/onboarding_controller_impl.cc",
"supervision/onboarding_controller_impl.h",
"system/automatic_reboot_manager.cc",
......@@ -2607,6 +2609,7 @@ source_set("unit_tests") {
"smb_client/smb_url_unittest.cc",
"smb_client/temp_file_manager_unittest.cc",
"startup_settings_cache_unittest.cc",
"supervision/onboarding_controller_impl_unittest.cc",
"system/automatic_reboot_manager_unittest.cc",
"system/device_disabling_manager_unittest.cc",
"system/procfs_util_unittest.cc",
......
......@@ -24,6 +24,7 @@
#include "chrome/browser/chromeos/login/test/user_policy_mixin.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include "chrome/browser/chromeos/supervision/onboarding_constants.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/webui/chromeos/login/supervision_onboarding_screen_handler.h"
......@@ -43,10 +44,6 @@ namespace chromeos {
namespace {
constexpr char kTestStartPageRelativeUrl[] = "/families/onboarding/start";
constexpr char kTestCustomHttpHeaderKey[] =
"test-supervision-onboarding-header";
chromeos::OobeUI* GetOobeUI() {
auto* host = chromeos::LoginDisplayHost::default_host();
return host ? host->GetOobeUI() : nullptr;
......@@ -86,23 +83,18 @@ class FakeSupervisionServer {
return received_auth_header_values_.size();
}
GURL url_filter_pattern() const { return test_server_->GetURL("/*"); }
GURL start_page_url() {
return test_server_->GetURL(kTestStartPageRelativeUrl);
}
private:
std::unique_ptr<HttpResponse> HandleRequest(const HttpRequest& request) {
// We are not interested in other URLs hitting the server at this point.
// This will filter bogus requests like favicon fetches and stop us from
// handling requests that are targeting gaia.
if (request.relative_url != kTestStartPageRelativeUrl)
if (request.relative_url != supervision::kOnboardingStartPageRelativeUrl)
return nullptr;
UpdateVerificationData(request);
auto response = std::make_unique<BasicHttpResponse>();
if (custom_http_header_value_.has_value()) {
response->AddCustomHeader(kTestCustomHttpHeaderKey,
response->AddCustomHeader(supervision::kExperimentHeaderName,
custom_http_header_value_.value());
}
......@@ -149,21 +141,15 @@ class SupervisionOnboardingBaseTest : public MixinBasedInProcessBrowserTest {
void SetUpCommandLine(base::CommandLine* command_line) override {
if (IsFeatureOn()) {
command_line->AppendSwitchASCII(
chromeos::switches::kSupervisionOnboardingPageUrlPattern,
supervision_server()->url_filter_pattern().spec());
command_line->AppendSwitchASCII(
chromeos::switches::kSupervisionOnboardingStartPageUrl,
supervision_server()->start_page_url().spec());
command_line->AppendSwitchASCII(
chromeos::switches::kSupervisionOnboardingHttpResponseHeader,
kTestCustomHttpHeaderKey);
switches::kSupervisionOnboardingUrlPrefix,
embedded_test_server()->base_url().spec());
// To turn on the feature properly we also ask the server to return the
// expected custom http header value. Tests that want to simulate other
// server responses can call these methods again to override this
// behavior.
ExpectCustomHttpHeaderValue("user-eligible");
supervision_server()->set_custom_http_header_value("user-eligible");
supervision_server()->set_custom_http_header_value(
supervision::kDeviceOnboardingExperimentName);
}
MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line);
......@@ -210,15 +196,6 @@ class SupervisionOnboardingBaseTest : public MixinBasedInProcessBrowserTest {
supervision_onboarding_screen_->Show();
}
// Sets the flow to expect the given header value in server responses. If
// the value is not present, we should exit the flow.
void ExpectCustomHttpHeaderValue(
const std::string& custom_http_header_value) {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
chromeos::switches::kSupervisionOnboardingHttpResponseHeaderValue,
custom_http_header_value);
}
void WaitForScreen() {
OobeScreenWaiter screen_waiter(SupervisionOnboardingScreenView::kScreenId);
screen_waiter.set_assert_next_screen();
......@@ -348,8 +325,7 @@ IN_PROC_BROWSER_TEST_F(SupervisionOnboardingTest,
#endif
IN_PROC_BROWSER_TEST_F(SupervisionOnboardingTest,
MAYBE_ExitWhenServerSendsWrongHeader) {
ExpectCustomHttpHeaderValue("user-eligible-for-supervision");
supervision_server()->set_custom_http_header_value("user-not-eligible");
supervision_server()->set_custom_http_header_value("wrong_header_value");
LoginAndShowScreen();
WaitForScreenExit();
......
// Copyright 2019 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 "chrome/browser/chromeos/supervision/onboarding_constants.h"
namespace chromeos {
namespace supervision {
// Default URL prefix for the Supervision Onboarding pages.
const char kSupervisionServerUrlPrefix[] =
"https://families.google.com/kids/deviceonboarding";
// Relative URL for the onboarding start page.
const char kOnboardingStartPageRelativeUrl[] = "/kids/deviceonboarding/start";
// Name of the custom HTTP header returned by the Supervision server containing
// a list of experiments that this version of the onboarding supports.
const char kExperimentHeaderName[] = "supervision-experiments";
// Experiment name for the first version of the onboarding flow.
const char kDeviceOnboardingExperimentName[] = "DeviceOnboardingV1";
} // namespace supervision
} // namespace chromeos
// Copyright 2019 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.
#ifndef CHROME_BROWSER_CHROMEOS_SUPERVISION_ONBOARDING_CONSTANTS_H_
#define CHROME_BROWSER_CHROMEOS_SUPERVISION_ONBOARDING_CONSTANTS_H_
namespace chromeos {
namespace supervision {
extern const char kSupervisionServerUrlPrefix[];
extern const char kOnboardingStartPageRelativeUrl[];
extern const char kExperimentHeaderName[];
extern const char kDeviceOnboardingExperimentName[];
} // namespace supervision
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_SUPERVISION_ONBOARDING_CONSTANTS_H_
......@@ -4,13 +4,16 @@
#include "chrome/browser/chromeos/supervision/onboarding_controller_impl.h"
#include "ash/public/cpp/ash_pref_names.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "chrome/browser/chromeos/supervision/onboarding_constants.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chromeos/constants/chromeos_switches.h"
#include "components/prefs/pref_service.h"
#include "services/identity/public/cpp/access_token_fetcher.h"
#include "url/gurl.h"
......@@ -22,9 +25,23 @@ namespace {
const char kSupervisionScope[] =
"https://www.googleapis.com/auth/kid.family.readonly";
GURL SupervisionServerBaseUrl() {
GURL command_line_prefix(
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kSupervisionOnboardingUrlPrefix));
if (command_line_prefix.is_valid())
return command_line_prefix;
return GURL(kSupervisionServerUrlPrefix);
}
} // namespace
OnboardingControllerImpl::OnboardingControllerImpl() = default;
OnboardingControllerImpl::OnboardingControllerImpl(Profile* profile)
: profile_(profile) {
DCHECK(profile);
}
OnboardingControllerImpl::~OnboardingControllerImpl() = default;
void OnboardingControllerImpl::BindRequest(
......@@ -36,11 +53,8 @@ void OnboardingControllerImpl::BindWebviewHost(
mojom::OnboardingWebviewHostPtr webview_host) {
webview_host_ = std::move(webview_host);
Profile* profile = ProfileManager::GetPrimaryUserProfile();
DCHECK(profile);
identity::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile);
IdentityManagerFactory::GetForProfile(profile_);
std::string account_id = identity_manager->GetPrimaryAccountId();
......@@ -77,23 +91,11 @@ void OnboardingControllerImpl::AccessTokenCallback(
mojom::OnboardingPage page;
page.access_token = access_token_info.token;
page.url_filter_pattern =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
chromeos::switches::kSupervisionOnboardingPageUrlPattern);
page.custom_header_name =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
chromeos::switches::kSupervisionOnboardingHttpResponseHeader);
page.custom_header_name = kExperimentHeaderName;
page.url = GURL(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
chromeos::switches::kSupervisionOnboardingStartPageUrl));
if (!page.url.is_valid() || page.url_filter_pattern.empty() ||
page.custom_header_name->empty()) {
DVLOG(1) << "Exiting Supervision Onboarding flow since the required flags "
"are unset or invalid.";
webview_host_->ExitFlow();
return;
}
page.url_filter_pattern = SupervisionServerBaseUrl().Resolve("/*").spec();
page.url =
SupervisionServerBaseUrl().Resolve(kOnboardingStartPageRelativeUrl);
webview_host_->LoadPage(
page.Clone(), base::BindOnce(&OnboardingControllerImpl::LoadPageCallback,
......@@ -104,13 +106,9 @@ void OnboardingControllerImpl::LoadPageCallback(
const base::Optional<std::string>& custom_header_value) {
DCHECK(webview_host_);
std::string expected_header_value =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
chromeos::switches::kSupervisionOnboardingHttpResponseHeaderValue);
if (!custom_header_value.has_value() ||
!base::EqualsCaseInsensitiveASCII(custom_header_value.value(),
expected_header_value)) {
kDeviceOnboardingExperimentName)) {
webview_host_->ExitFlow();
}
}
......
......@@ -13,6 +13,9 @@
#include "chrome/browser/chromeos/supervision/mojom/onboarding_controller.mojom.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "services/identity/public/cpp/identity_manager.h"
#include "url/gurl.h"
class Profile;
namespace identity {
class AccessTokenFetcher;
......@@ -23,7 +26,7 @@ namespace supervision {
class OnboardingControllerImpl : public mojom::OnboardingController {
public:
OnboardingControllerImpl();
explicit OnboardingControllerImpl(Profile* profile);
~OnboardingControllerImpl() override;
void BindRequest(mojom::OnboardingControllerRequest request);
......@@ -40,6 +43,7 @@ class OnboardingControllerImpl : public mojom::OnboardingController {
// Callback to OnboardingWebviewHost::LoadPage.
void LoadPageCallback(const base::Optional<std::string>& custom_header_value);
Profile* profile_;
mojom::OnboardingWebviewHostPtr webview_host_;
mojo::BindingSet<mojom::OnboardingController> bindings_;
std::unique_ptr<identity::AccessTokenFetcher> access_token_fetcher_;
......
// Copyright 2019 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 "chrome/browser/chromeos/supervision/onboarding_controller_impl.h"
#include <memory>
#include "base/macros.h"
#include "chrome/browser/chromeos/supervision/mojom/onboarding_controller.mojom.h"
#include "chrome/browser/chromeos/supervision/onboarding_constants.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/constants/chromeos_switches.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "services/identity/public/cpp/identity_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace supervision {
namespace {
const char kTestAccountId[] = "test-account-id";
} // namespace
class FakeOnboardingWebviewHost : mojom::OnboardingWebviewHost {
public:
explicit FakeOnboardingWebviewHost(
mojom::OnboardingWebviewHostRequest request)
: binding_(this, std::move(request)) {}
void LoadPage(mojom::OnboardingPagePtr page,
LoadPageCallback callback) override {
ASSERT_FALSE(exited_flow_);
page_loaded_ = *page;
std::move(callback).Run(custom_header_value_);
}
void ExitFlow() override {
ASSERT_FALSE(exited_flow_);
exited_flow_ = true;
}
bool exited_flow() const { return exited_flow_; }
const base::Optional<mojom::OnboardingPage>& page_loaded() {
return page_loaded_;
}
void clear_custom_header_value() { custom_header_value_ = base::nullopt; }
void set_custom_header_value(const std::string& custom_header_value) {
custom_header_value_ = custom_header_value;
}
private:
mojo::Binding<mojom::OnboardingWebviewHost> binding_;
bool exited_flow_ = false;
base::Optional<std::string> custom_header_value_;
base::Optional<mojom::OnboardingPage> page_loaded_;
DISALLOW_COPY_AND_ASSIGN(FakeOnboardingWebviewHost);
};
class OnboardingControllerTest : public testing::Test {
protected:
void SetUp() override {
profile_ = IdentityTestEnvironmentProfileAdaptor::
CreateProfileForIdentityTestEnvironment();
identity_test_env_adaptor_ =
std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile_.get());
identity_test_env()->MakeAccountAvailable(kTestAccountId);
identity_test_env()->SetPrimaryAccount(kTestAccountId);
controller_impl_ = std::make_unique<OnboardingControllerImpl>(profile());
controller_impl_->BindRequest(mojo::MakeRequest(&controller_));
}
void BindWebviewHost() {
mojom::OnboardingWebviewHostPtr webview_host_proxy;
webview_host_ = std::make_unique<FakeOnboardingWebviewHost>(
mojo::MakeRequest(&webview_host_proxy));
controller_->BindWebviewHost(std::move(webview_host_proxy));
}
void WaitForAuthRequestAndReturnError() {
identity_test_env()
->WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
base::RunLoop().RunUntilIdle();
}
void WaitForAuthRequestAndReturnToken(const std::string& access_token) {
identity_test_env()
->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
access_token, base::Time::Now() + base::TimeDelta::FromHours(1));
base::RunLoop().RunUntilIdle();
}
Profile* profile() { return profile_.get(); }
identity::IdentityTestEnvironment* identity_test_env() {
return identity_test_env_adaptor_->identity_test_env();
}
FakeOnboardingWebviewHost* webview_host() { return webview_host_.get(); }
private:
content::TestBrowserThreadBundle test_browser_thread_bundle_;
std::unique_ptr<TestingProfile> profile_;
std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
identity_test_env_adaptor_;
std::unique_ptr<OnboardingControllerImpl> controller_impl_;
mojom::OnboardingControllerPtr controller_;
std::unique_ptr<FakeOnboardingWebviewHost> webview_host_;
};
TEST_F(OnboardingControllerTest, ExitFlowOnAuthError) {
BindWebviewHost();
WaitForAuthRequestAndReturnError();
EXPECT_FALSE(webview_host()->page_loaded().has_value());
EXPECT_TRUE(webview_host()->exited_flow());
}
TEST_F(OnboardingControllerTest, RequestWebviewHostToLoadStartPageCorrectly) {
BindWebviewHost();
WaitForAuthRequestAndReturnToken("fake_access_token");
ASSERT_TRUE(webview_host()->page_loaded().has_value());
EXPECT_EQ(webview_host()->page_loaded()->url,
GURL("https://families.google.com/kids/deviceonboarding/start"));
EXPECT_EQ(webview_host()->page_loaded()->access_token, "fake_access_token");
EXPECT_EQ(webview_host()->page_loaded()->custom_header_name,
kExperimentHeaderName);
EXPECT_EQ(webview_host()->page_loaded()->url_filter_pattern,
"https://families.google.com/*");
}
TEST_F(OnboardingControllerTest, OverridePageUrlsByCommandLine) {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
chromeos::switches::kSupervisionOnboardingUrlPrefix,
"https://example.com/");
BindWebviewHost();
WaitForAuthRequestAndReturnToken("fake_access_token");
ASSERT_TRUE(webview_host()->page_loaded().has_value());
EXPECT_EQ(webview_host()->page_loaded()->url,
GURL("https://example.com/kids/deviceonboarding/start"));
EXPECT_EQ(webview_host()->page_loaded()->access_token, "fake_access_token");
EXPECT_EQ(webview_host()->page_loaded()->custom_header_name,
kExperimentHeaderName);
EXPECT_EQ(webview_host()->page_loaded()->url_filter_pattern,
"https://example.com/*");
}
TEST_F(OnboardingControllerTest, StayInFlowWhenHeaderValueIsCorrect) {
BindWebviewHost();
webview_host()->set_custom_header_value(kDeviceOnboardingExperimentName);
WaitForAuthRequestAndReturnToken("fake_access_token");
EXPECT_FALSE(webview_host()->exited_flow());
}
TEST_F(OnboardingControllerTest, ExitFlowWhenHeaderValueIsMissing) {
BindWebviewHost();
webview_host()->clear_custom_header_value();
WaitForAuthRequestAndReturnToken("fake_access_token");
EXPECT_TRUE(webview_host()->exited_flow());
}
TEST_F(OnboardingControllerTest, ExitFlowWhenHeaderValueIsWrong) {
BindWebviewHost();
webview_host()->set_custom_header_value("clearly-wrong-header-value");
WaitForAuthRequestAndReturnToken("fake_access_token");
EXPECT_TRUE(webview_host()->exited_flow());
}
} // namespace supervision
} // namespace chromeos
......@@ -21,8 +21,6 @@ SupervisionOnboardingScreenHandler::SupervisionOnboardingScreenHandler(
JSCallsContainer* js_calls_container)
: BaseScreenHandler(kScreenId, js_calls_container) {
set_user_acted_method_path("login.SupervisionOnboardingScreen.userActed");
supervision_onboarding_controller_ =
std::make_unique<supervision::OnboardingControllerImpl>();
}
SupervisionOnboardingScreenHandler::~SupervisionOnboardingScreenHandler() {
......@@ -62,6 +60,12 @@ void SupervisionOnboardingScreenHandler::Initialize() {}
void SupervisionOnboardingScreenHandler::BindSupervisionOnboardingController(
supervision::mojom::OnboardingControllerRequest request) {
if (!supervision_onboarding_controller_) {
supervision_onboarding_controller_ =
std::make_unique<supervision::OnboardingControllerImpl>(
ProfileManager::GetPrimaryUserProfile());
}
supervision_onboarding_controller_->BindRequest(std::move(request));
}
......
......@@ -479,26 +479,10 @@ const char kRegulatoryLabelDir[] = "regulatory-label-dir";
// This makes it easier to test layout logic.
const char kShowLoginDevOverlay[] = "show-login-dev-overlay";
// Url for the Supervision Onboarding starting page.
const char kSupervisionOnboardingStartPageUrl[] =
"supervision-onboarding-start-page-url";
// Matcher pattern for authenticated requests made by the Supervision
// onboarding.
// TODO(958995): Hardcode this value when the server implementation is ready.
const char kSupervisionOnboardingPageUrlPattern[] =
"supervision-onboarding-page-url-pattern";
// Custom HTTP header expected in responses coming from the supervision server.
// TODO(958995): Hardcode this value when the server implementation is ready.
const char kSupervisionOnboardingHttpResponseHeader[] =
"supervision-onboarding-http-response-header";
// Value expected to be found in custom HTTP header coming from the supervision
// server.
// TODO(958995): Hardcode this value when the server implementation is ready.
const char kSupervisionOnboardingHttpResponseHeaderValue[] =
"supervision-onboarding-http-response-header-value";
// Url prefix for the Supervision Onboarding remote web pages.
// This makes it easier to test with fake HTTP servers or local dev versions.
const char kSupervisionOnboardingUrlPrefix[] =
"supervision-onboarding-url-prefix";
// Enables testing for encryption migration UI.
const char kTestEncryptionMigrationUI[] = "test-encryption-migration-ui";
......
......@@ -179,13 +179,7 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kShelfHoverPreviews[];
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const char kShowAndroidFilesInFilesApp[];
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const char kSupervisionOnboardingStartPageUrl[];
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const char kSupervisionOnboardingPageUrlPattern[];
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const char kSupervisionOnboardingHttpResponseHeader[];
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const char kSupervisionOnboardingHttpResponseHeaderValue[];
extern const char kSupervisionOnboardingUrlPrefix[];
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kShowLoginDevOverlay[];
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const char kTestEncryptionMigrationUI[];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment