chromium/ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_scene_agent_unittest.mm

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

#import "ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_scene_agent.h"

#import "base/feature_list.h"
#import "base/test/scoped_feature_list.h"
#import "components/prefs/testing_pref_service.h"
#import "ios/chrome/browser/shared/coordinator/scene/test/stub_browser_provider_interface.h"
#import "ios/chrome/browser/shared/model/browser/browser_provider_interface.h"
#import "ios/chrome/browser/shared/model/browser/test/test_browser.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_opener.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/common/ui/reauthentication/reauthentication_protocol.h"
#import "ios/web/public/test/fakes/fake_web_state.h"
#import "ios/web/public/test/web_task_environment.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"

#pragma mark - StubReauthenticationModule

@interface StubReauthenticationModule : NSObject <ReauthenticationProtocol>

@property(nonatomic, assign) BOOL canAttemptReauthWithBiometrics;
@property(nonatomic, assign) BOOL canAttemptReauth;
@property(nonatomic, assign) ReauthenticationResult returnedResult;

@end

@implementation StubReauthenticationModule

- (void)attemptReauthWithLocalizedReason:(NSString*)localizedReason
                    canReusePreviousAuth:(BOOL)canReusePreviousAuth
                                 handler:
                                     (void (^)(ReauthenticationResult success))
                                         handler {
  handler(self.returnedResult);
}

@end

namespace {

#pragma mark - IncognitoReauthSceneAgentTest

class IncognitoReauthSceneAgentTest : public PlatformTest {
 public:
  IncognitoReauthSceneAgentTest()
      : browser_state_(TestChromeBrowserState::Builder().Build()),
        scene_state_([[SceneState alloc] initWithAppState:nil]),
        scene_state_mock_(OCMPartialMock(scene_state_)),
        stub_reauth_module_([[StubReauthenticationModule alloc] init]),
        agent_([[IncognitoReauthSceneAgent alloc]
            initWithReauthModule:stub_reauth_module_]) {
    [scene_state_ addAgent:agent_];
  }

 protected:
  void SetUpTestObjects(int tab_count, bool enable_pref) {
    // Stub all calls to be able to mock the following:
    // 1. sceneState.browserProviderInterface.incognitoBrowserProvider
    //            .browser->GetWebStateList()->count()
    // 2. sceneState.browserProviderInterface.hasIncognitoBrowserProvider
    test_browser_ = std::make_unique<TestBrowser>(browser_state_.get());
    for (int i = 0; i < tab_count; ++i) {
      test_browser_->GetWebStateList()->InsertWebState(
          std::make_unique<web::FakeWebState>(),
          WebStateList::InsertionParams::AtIndex(i));
    }

    stub_browser_interface_provider_ =
        [[StubBrowserProviderInterface alloc] init];
    stub_browser_interface_provider_.incognitoBrowserProvider.browser =
        test_browser_.get();

    OCMStub([scene_state_mock_ browserProviderInterface])
        .andReturn(stub_browser_interface_provider_);

    [IncognitoReauthSceneAgent registerLocalState:pref_service_.registry()];
    agent_.localState = &pref_service_;
    pref_service_.SetBoolean(prefs::kIncognitoAuthenticationSetting,
                             enable_pref);
  }

  void SetUp() override {
    // Set up default stub reauth module behavior.
    stub_reauth_module_.canAttemptReauthWithBiometrics = YES;
    stub_reauth_module_.canAttemptReauth = YES;
    stub_reauth_module_.returnedResult = ReauthenticationResult::kSuccess;
  }

  web::WebTaskEnvironment task_environment_;
  std::unique_ptr<TestChromeBrowserState> browser_state_;

  // The scene state that the agent works with.
  SceneState* scene_state_;
  // Partial mock for stubbing scene_state_'s methods
  id scene_state_mock_;
  StubReauthenticationModule* stub_reauth_module_;
  // The tested agent
  IncognitoReauthSceneAgent* agent_;
  StubBrowserProviderInterface* stub_browser_interface_provider_;
  std::unique_ptr<TestBrowser> test_browser_;
  TestingPrefServiceSimple pref_service_;
  base::test::ScopedFeatureList feature_list_;
};

// Test that when the feature pref is disabled, auth isn't required.
TEST_F(IncognitoReauthSceneAgentTest, PrefDisabled) {
  SetUpTestObjects(/*tab_count=*/1,
                   /*enable_pref=*/false);

  // Go foreground.
  scene_state_.activationLevel = SceneActivationLevelForegroundActive;

  EXPECT_FALSE(agent_.authenticationRequired);
}

// Test that when the feature is enabled, we're foregrounded with some incognito
// content already present, auth is required
TEST_F(IncognitoReauthSceneAgentTest, NeedsAuth) {
  SetUpTestObjects(/*tab_count=*/1, /*enable_pref=*/true);

  // Go foreground.
  scene_state_.activationLevel = SceneActivationLevelForegroundActive;

  EXPECT_TRUE(agent_.authenticationRequired);
}

// Test that when auth is required and is successfully performed, it's not
// required anymore.
TEST_F(IncognitoReauthSceneAgentTest, SuccessfulAuth) {
  SetUpTestObjects(/*tab_count=*/1, /*enable_pref=*/true);

  // Go foreground.
  scene_state_.activationLevel = SceneActivationLevelForegroundActive;

  EXPECT_TRUE(agent_.authenticationRequired);

  [agent_ authenticateIncognitoContent];

  // Auth not required
  EXPECT_FALSE(agent_.authenticationRequired);

  // Auth required after backgrounding.
  scene_state_.activationLevel = SceneActivationLevelBackground;
  scene_state_.activationLevel = SceneActivationLevelForegroundActive;
  EXPECT_TRUE(agent_.authenticationRequired);
}

// Tests that authentication is still required if authentication fails.
TEST_F(IncognitoReauthSceneAgentTest, FailedSkippedAuth) {
  SetUpTestObjects(/*tab_count=*/1, /*enable_pref=*/true);

  // Go foreground.
  scene_state_.activationLevel = SceneActivationLevelForegroundActive;

  EXPECT_TRUE(agent_.authenticationRequired);

  stub_reauth_module_.returnedResult = ReauthenticationResult::kFailure;

  [agent_ authenticateIncognitoContent];
  // Auth still required
  EXPECT_TRUE(agent_.authenticationRequired);

  stub_reauth_module_.returnedResult = ReauthenticationResult::kSkipped;
  [agent_ authenticateIncognitoContent];
  // Auth still required
  EXPECT_TRUE(agent_.authenticationRequired);
}

// Test that when the feature is enabled, auth isn't required if we foreground
// without any incognito tabs.
TEST_F(IncognitoReauthSceneAgentTest, AuthNotRequiredWhenNoIncognitoTabs) {
  SetUpTestObjects(/*tab_count=*/0, /*enable_pref=*/true);

  // Go foreground.
  scene_state_.activationLevel = SceneActivationLevelForegroundActive;

  EXPECT_FALSE(agent_.authenticationRequired);
}

// Test that when the feature is enabled, we're foregrounded with some incognito
// content already present, auth is required
TEST_F(IncognitoReauthSceneAgentTest,
       AuthNotRequiredWhenNoIncognitoTabsOnForeground) {
  SetUpTestObjects(/*tab_count=*/0, /*enable_pref=*/true);

  // Go foreground.
  scene_state_.activationLevel = SceneActivationLevelForegroundActive;

  EXPECT_FALSE(agent_.authenticationRequired);

  // Open another tab.
  test_browser_->GetWebStateList()->InsertWebState(
      std::make_unique<web::FakeWebState>(),
      WebStateList::InsertionParams::AtIndex(0));

  EXPECT_FALSE(agent_.authenticationRequired);
}

}  // namespace