/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.login

import androidx.fragment.app.Fragment
import androidx.test.core.app.ApplicationProvider
import com.facebook.AccessToken
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.TestUtils
import com.facebook.login.LoginClient.Result.Companion.createTokenResult
import java.util.HashSet
import java.util.UUID
import java.util.concurrent.Executor
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito.mockStatic
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(FacebookSdk::class)
class LoginClientTest : FacebookPowerMockTestCase() {
  private val serialExecutor: Executor = FacebookSerialExecutor()

  var mockFragment: Fragment = mock()

  override fun setup() {
    super.setup()

    mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationId()).thenReturn("123456789")
    whenever(FacebookSdk.getExecutor()).thenReturn(serialExecutor)
    whenever(FacebookSdk.getAutoLogAppEventsEnabled()).thenReturn(false)
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
  }

  @Test
  fun testReauthorizationWithSameFbidSucceeds() {
    val request = createRequest()
    val token =
        AccessToken(ACCESS_TOKEN, APP_ID, USER_ID, PERMISSIONS, null, null, null, null, null, null)
    var result = createTokenResult(request, token)
    val listener = mock<LoginClient.OnCompletedListener>()
    val client = LoginClient(mockFragment)
    client.onCompletedListener = listener
    client.completeAndValidate(result)
    val resultArgumentCaptor = argumentCaptor<LoginClient.Result>()
    verify(listener).onCompleted(resultArgumentCaptor.capture())
    result = resultArgumentCaptor.firstValue
    assertThat(result).isNotNull
    assertThat(LoginClient.Result.Code.SUCCESS).isEqualTo(result.code)
    val resultToken = result.token
    checkNotNull(resultToken)
    assertThat(ACCESS_TOKEN).isEqualTo(resultToken.token)

    // We don't care about ordering.
    assertThat(PERMISSIONS).isEqualTo(resultToken.permissions)
  }

  @Test
  fun testRequestParceling() {
    val request = createRequest()
    val unparceledRequest = checkNotNull(TestUtils.parcelAndUnparcel(request))
    assertThat(LoginBehavior.NATIVE_WITH_FALLBACK).isEqualTo(unparceledRequest.loginBehavior)
    assertThat(LoginTargetApp.FACEBOOK).isEqualTo(unparceledRequest.loginTargetApp)
    assertThat(HashSet(PERMISSIONS)).isEqualTo(unparceledRequest.permissions)
    assertThat(DefaultAudience.FRIENDS).isEqualTo(unparceledRequest.defaultAudience)
    assertThat("1234").isEqualTo(unparceledRequest.applicationId)
    assertThat("5678").isEqualTo(unparceledRequest.authId)
    assertThat(unparceledRequest.isRerequest).isFalse
    assertThat(unparceledRequest.isFamilyLogin).isFalse
    assertThat(unparceledRequest.shouldSkipAccountDeduplication()).isFalse
  }

  @Test
  fun testResultParceling() {
    val request =
        LoginClient.Request(
            LoginBehavior.WEB_ONLY,
            null,
            DefaultAudience.EVERYONE,
            AUTH_TYPE,
            FacebookSdk.getApplicationId(),
            AUTH_ID,
            LoginTargetApp.FACEBOOK)
    request.isRerequest = true
    request.resetMessengerState = false
    request.messengerPageId = "1928"
    request.isFamilyLogin = true
    request.setShouldSkipAccountDeduplication(true)
    val token1 =
        AccessToken(
            "Token2",
            FacebookSdk.getApplicationId(),
            "1000",
            null,
            null,
            null,
            null,
            null,
            null,
            null)
    val result =
        LoginClient.Result(request, LoginClient.Result.Code.SUCCESS, token1, "error 1", "123")
    val unparceledResult = checkNotNull(TestUtils.parcelAndUnparcel(result))
    val unparceledRequest = checkNotNull(unparceledResult.request)
    assertThat(LoginBehavior.WEB_ONLY).isEqualTo(unparceledRequest.loginBehavior)
    assertThat(LoginTargetApp.FACEBOOK).isEqualTo(unparceledRequest.loginTargetApp)
    assertThat(HashSet<String>()).isEqualTo(unparceledRequest.permissions)
    assertThat(DefaultAudience.EVERYONE).isEqualTo(unparceledRequest.defaultAudience)
    assertThat(FacebookSdk.getApplicationId()).isEqualTo(unparceledRequest.applicationId)
    assertThat(AUTH_ID).isEqualTo(unparceledRequest.authId)
    assertThat(unparceledRequest.isRerequest).isTrue
    assertThat(unparceledRequest.isFamilyLogin).isTrue
    assertThat(unparceledRequest.shouldSkipAccountDeduplication()).isTrue
    assertThat(LoginClient.Result.Code.SUCCESS).isEqualTo(unparceledResult.code)
    assertThat(token1).isEqualTo(unparceledResult.token)
    assertThat("error 1").isEqualTo(unparceledResult.errorMessage)
    assertThat("123").isEqualTo(unparceledResult.errorCode)
    assertThat("1928").isEqualTo(unparceledRequest.messengerPageId)
    assertThat(false).isEqualTo(unparceledRequest.resetMessengerState)
  }

  @Test
  fun testGetHandlersForFBSSOLogin() {
    val request =
        LoginClient.Request(
            LoginBehavior.NATIVE_WITH_FALLBACK,
            null,
            DefaultAudience.EVERYONE,
            "test auth",
            FacebookSdk.getApplicationId(),
            "test auth id",
            LoginTargetApp.FACEBOOK)
    val client = LoginClient(mockFragment)
    val handlers = client.getHandlersToTry(request)
    checkNotNull(handlers)
    assertThat(handlers.size).isEqualTo(4)
    assertThat(handlers[0]).isInstanceOf(GetTokenLoginMethodHandler::class.java)
    assertThat(handlers[1]).isInstanceOf(KatanaProxyLoginMethodHandler::class.java)
    assertThat(handlers[2]).isInstanceOf(CustomTabLoginMethodHandler::class.java)
    assertThat(handlers[3]).isInstanceOf(WebViewLoginMethodHandler::class.java)
  }

  @Test
  fun testGetHandlersForFBWebLoginOnly() {
    val request =
        LoginClient.Request(
            LoginBehavior.WEB_ONLY,
            null,
            DefaultAudience.EVERYONE,
            AUTH_TYPE,
            FacebookSdk.getApplicationId(),
            AUTH_ID,
            LoginTargetApp.FACEBOOK)
    val client = LoginClient(mockFragment)
    val handlers = client.getHandlersToTry(request)
    checkNotNull(handlers)
    assertThat(handlers.size).isEqualTo(2)
    assertThat(handlers[0]).isInstanceOf(CustomTabLoginMethodHandler::class.java)
    assertThat(handlers[1]).isInstanceOf(WebViewLoginMethodHandler::class.java)
  }

  @Test
  fun testGetHandlersForIGSSOLogin() {
    val request =
        LoginClient.Request(
            LoginBehavior.NATIVE_WITH_FALLBACK,
            null,
            DefaultAudience.EVERYONE,
            "test auth",
            FacebookSdk.getApplicationId(),
            "test auth id",
            LoginTargetApp.INSTAGRAM)
    val client = LoginClient(mockFragment)
    val handlers = client.getHandlersToTry(request)
    checkNotNull(handlers)
    assertThat(handlers.size).isEqualTo(3)
    assertThat(handlers[0]).isInstanceOf(InstagramAppLoginMethodHandler::class.java)
    assertThat(handlers[1]).isInstanceOf(CustomTabLoginMethodHandler::class.java)
    assertThat(handlers[2]).isInstanceOf(WebViewLoginMethodHandler::class.java)
  }

  @Test
  fun testGetHandlersForIGWebLoginOnly() {
    val request =
        LoginClient.Request(
            LoginBehavior.WEB_ONLY,
            null,
            DefaultAudience.EVERYONE,
            AUTH_TYPE,
            FacebookSdk.getApplicationId(),
            AUTH_ID,
            LoginTargetApp.INSTAGRAM)
    val client = LoginClient(mockFragment)
    val handlers = client.getHandlersToTry(request)
    checkNotNull(handlers)
    assertThat(handlers.size).isEqualTo(2)
    assertThat(handlers[0]).isInstanceOf(CustomTabLoginMethodHandler::class.java)
    assertThat(handlers[1]).isInstanceOf(WebViewLoginMethodHandler::class.java)
  }

  private fun createRequest(): LoginClient.Request {
    return LoginClient.Request(
        LoginBehavior.NATIVE_WITH_FALLBACK,
        HashSet(PERMISSIONS),
        DefaultAudience.FRIENDS,
        "rerequest",
        "1234",
        "5678",
        LoginTargetApp.FACEBOOK)
  }

  companion object {
    private const val ACCESS_TOKEN = "An access token for user 1"
    private const val USER_ID = "1001"
    private const val APP_ID = "2002"
    private const val AUTH_TYPE = "test auth type"
    private val AUTH_ID = UUID.randomUUID().toString()
    private val PERMISSIONS = HashSet(listOf("go outside", "come back in"))
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.login

import com.facebook.FacebookPowerMockTestCase
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test

class LoginConfigurationTest : FacebookPowerMockTestCase() {

  @Test
  fun `test initialize LoginConfiguration without nonce`() {
    val config = LoginConfiguration(listOf("user_email"))
    assertThat(config.nonce).isNotEmpty
  }

  @Test(expected = IllegalArgumentException::class)
  fun `test initialize LoginConfiguration with invalid nonce`() {
    LoginConfiguration(listOf("user_email"), "nonce ")
  }

  @Test
  fun `test initialize LoginConfiguration with correct fields`() {
    val codeVerifier = PKCEUtil.generateCodeVerifier()
    val config =
        LoginConfiguration(listOf("user_email"), AuthenticationTokenTestUtil.NONCE, codeVerifier)
    assertThat(config.permissions).isNotNull
    assertThat(config.permissions).contains(LoginConfiguration.OPENID)
    assertThat(config.nonce).isEqualTo(AuthenticationTokenTestUtil.NONCE)
    assertThat(config.codeVerifier).isEqualTo(codeVerifier)
  }

  @Test(expected = IllegalArgumentException::class)
  fun `test initialize LoginConfiguration with invalid code verifier`() {
    LoginConfiguration(listOf("user_email"), AuthenticationTokenTestUtil.NONCE, "")
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.login

import android.app.Activity
import android.os.Bundle
import android.view.View
import androidx.fragment.app.FragmentActivity
import com.facebook.FacebookTestCase
import com.facebook.common.R
import java.util.UUID
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.robolectric.Robolectric
import org.robolectric.Shadows
import org.robolectric.android.controller.ActivityController

class LoginFragmentTest : FacebookTestCase() {
  // test activity: it's the host for the login fragment during robolectric test
  open class TestActivity : FragmentActivity() {
    lateinit var fragment: LoginFragment
    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      fragment = LoginFragment()
      supportFragmentManager.beginTransaction().add(fragment, "FRAGMENT_TAG").commit()
    }
  }

  private lateinit var activityController: ActivityController<TestActivity>

  override fun setUp() {
    super.setUp()
    activityController = Robolectric.buildActivity(TestActivity::class.java)
  }

  @Test
  fun `test creating the fragment will create a login client`() {
    activityController.create()
    activityController.start()

    val fragment = activityController.get().fragment
    assertThat(fragment.loginClient).isNotNull
    assertThat(fragment.loginClient.onCompletedListener).isNotNull
  }

  @Test
  fun `test finishing the activity when the login client complete `() {
    val request =
        LoginClient.Request(
            LoginBehavior.NATIVE_WITH_FALLBACK,
            null,
            DefaultAudience.EVERYONE,
            "testAuthType",
            "123456789",
            UUID.randomUUID().toString(),
            LoginTargetApp.FACEBOOK)
    val result = LoginClient.Result(request, LoginClient.Result.Code.CANCEL, null, null, null)

    activityController.create()
    activityController.start()

    val fragment = activityController.get().fragment
    val loginClient = fragment.loginClient
    loginClient.complete(result)
    val shadowActivity = Shadows.shadowOf(activityController.get())
    assertThat(shadowActivity.resultCode).isEqualTo(Activity.RESULT_CANCELED)
    assertThat(activityController.get().isFinishing).isTrue()
  }

  @Test
  fun `test fragment display progress bar when login client processing `() {
    activityController.create()
    activityController.start()
    val fragment = activityController.get().fragment
    val loginClient = fragment.loginClient
    loginClient.notifyBackgroundProcessingStart()
    val progressBar =
        fragment.view?.findViewById<View>(R.id.com_facebook_login_fragment_progress_bar)
    assertThat(progressBar?.visibility).isEqualTo(View.VISIBLE)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.login

import androidx.fragment.app.FragmentActivity
import com.facebook.FacebookPowerMockTestCase
import com.facebook.login.AuthenticationTokenTestUtil.getEncodedAuthTokenStringForTest
import java.util.Date
import kotlin.math.abs
import org.assertj.core.api.Assertions.assertThat
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.robolectric.Robolectric

abstract class LoginHandlerTestCase : FacebookPowerMockTestCase() {
  protected lateinit var activity: FragmentActivity
  protected lateinit var mockLoginClient: LoginClient

  override fun setup() {
    super.setup()
    mockLoginClient = mock()
    activity = Robolectric.buildActivity(FragmentActivity::class.java).create().get()
    whenever(mockLoginClient.activity).thenReturn(activity)
  }

  @JvmOverloads
  protected open fun createRequest(previousAccessTokenString: String? = null): LoginClient.Request {
    return LoginClient.Request(
        LoginBehavior.NATIVE_WITH_FALLBACK,
        HashSet(PERMISSIONS),
        DefaultAudience.FRIENDS,
        "rerequest",
        "1234",
        "5678",
        LoginTargetApp.FACEBOOK,
        AuthenticationTokenTestUtil.NONCE,
        CODE_VERIFIER,
        CODE_CHALLENGE,
        CodeChallengeMethod.S256)
  }

  protected open fun createIGAppRequest(): LoginClient.Request {
    return LoginClient.Request(
        LoginBehavior.NATIVE_WITH_FALLBACK,
        HashSet(PERMISSIONS),
        DefaultAudience.FRIENDS,
        "rerequest",
        "1234",
        "5678",
        LoginTargetApp.INSTAGRAM)
  }

  protected open fun createIGWebRequest(): LoginClient.Request {
    return LoginClient.Request(
        LoginBehavior.WEB_ONLY,
        HashSet(PERMISSIONS),
        DefaultAudience.FRIENDS,
        "rerequest",
        "1234",
        "5678",
        LoginTargetApp.INSTAGRAM)
  }

  protected open fun createRequestWithNonce(): LoginClient.Request {
    val codeVerifier = PKCEUtil.generateCodeVerifier()
    return LoginClient.Request(
        LoginBehavior.NATIVE_WITH_FALLBACK,
        HashSet(PERMISSIONS),
        DefaultAudience.FRIENDS,
        "rerequest",
        "1234",
        "5678",
        null,
        AuthenticationTokenTestUtil.NONCE,
        codeVerifier,
        PKCEUtil.generateCodeChallenge(codeVerifier, CodeChallengeMethod.S256),
        CodeChallengeMethod.S256)
  }

  protected fun assertDateDiffersWithinDelta(
      expected: Date,
      actual: Date,
      expectedDifference: Long,
      deltaInMsec: Long
  ) {
    val delta = abs(expected.time - actual.time) - expectedDifference
    assertThat(delta).isLessThan(deltaInMsec)
  }

  protected val encodedAuthTokenString: String
    get() = getEncodedAuthTokenStringForTest()

  companion object {
    const val ACCESS_TOKEN = "An access token"
    const val USER_ID = "123"
    const val EXPIRES_IN_DELTA = (3600 * 24 * 60).toLong()
    @JvmField val PERMISSIONS = setOf("go outside", "come back in")
    const val ERROR_MESSAGE = "This is bad!"
    const val CODE_VERIFIER = "codeVerifier"
    const val CODE_CHALLENGE = "codeChallenge"
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.login

import android.os.Bundle
import androidx.test.core.app.ApplicationProvider
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.appevents.InternalAppEventsLogger
import java.util.concurrent.ScheduledExecutorService
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONObject
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox

@PrepareForTest(FacebookSdk::class)
class LoginLoggerTest : FacebookPowerMockTestCase() {
  private lateinit var mockInternalAppEventsLogger: InternalAppEventsLogger
  private lateinit var loginLogger: LoginLogger
  private lateinit var testLoginRequest: LoginClient.Request
  private lateinit var mockExecutor: ScheduledExecutorService

  override fun setup() {
    super.setup()
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationId()).thenReturn("123456789")
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
    mockInternalAppEventsLogger = mock()
    mockExecutor = mock()
    loginLogger = LoginLogger(ApplicationProvider.getApplicationContext(), "123456789")
    Whitebox.setInternalState(loginLogger, "logger", mockInternalAppEventsLogger)
    Whitebox.setInternalState(LoginLogger::class.java, "worker", mockExecutor)
    testLoginRequest =
        LoginClient.Request(
            LoginBehavior.NATIVE_WITH_FALLBACK,
            setOf("email"),
            DefaultAudience.EVERYONE,
            "rerequest",
            "123456789",
            "5678")
  }

  @Test
  fun `test logStartLogin logging information in login request`() {
    val bundleCaptor = argumentCaptor<Bundle>()

    loginLogger.logStartLogin(testLoginRequest)

    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(eq(LoginLogger.EVENT_NAME_LOGIN_START), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_AUTH_LOGGER_ID))
        .isEqualTo(testLoginRequest.authId)
    val extras = JSONObject(params.getString(LoginLogger.EVENT_PARAM_EXTRAS) ?: "")
    assertThat(extras.getString(LoginLogger.EVENT_EXTRAS_LOGIN_BEHAVIOR))
        .isEqualTo(testLoginRequest.loginBehavior.toString())
    assertThat(extras.getString(LoginLogger.EVENT_EXTRAS_PERMISSIONS)).isEqualTo("email")
    assertThat(extras.getString(LoginLogger.EVENT_EXTRAS_DEFAULT_AUDIENCE))
        .isEqualTo(testLoginRequest.defaultAudience.toString())
  }

  @Test
  fun `test logStartLogin with a custom event name logging information in login request`() {
    val bundleCaptor = argumentCaptor<Bundle>()

    loginLogger.logStartLogin(testLoginRequest, "custom_login_event")

    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(eq("custom_login_event"), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_AUTH_LOGGER_ID))
        .isEqualTo(testLoginRequest.authId)
    val extras = JSONObject(params.getString(LoginLogger.EVENT_PARAM_EXTRAS) ?: "")
    assertThat(extras.getString(LoginLogger.EVENT_EXTRAS_LOGIN_BEHAVIOR))
        .isEqualTo(testLoginRequest.loginBehavior.toString())
    assertThat(extras.getString(LoginLogger.EVENT_EXTRAS_PERMISSIONS)).isEqualTo("email")
    assertThat(extras.getString(LoginLogger.EVENT_EXTRAS_DEFAULT_AUDIENCE))
        .isEqualTo(testLoginRequest.defaultAudience.toString())
  }

  @Test
  fun `test logCompleteLogin with cancel result logging the result logging value`() {
    val bundleCaptor = argumentCaptor<Bundle>()

    loginLogger.logCompleteLogin(
        testLoginRequest.authId, mapOf(), LoginClient.Result.Code.CANCEL, null, null)

    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(eq(LoginLogger.EVENT_NAME_LOGIN_COMPLETE), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_AUTH_LOGGER_ID))
        .isEqualTo(testLoginRequest.authId)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_LOGIN_RESULT))
        .isEqualTo(LoginClient.Result.Code.CANCEL.loggingValue)
  }

  @Test
  fun `test logCompleteLogin with exception logging the error message`() {
    val errorMessage = "Test login failure"
    val bundleCaptor = argumentCaptor<Bundle>()

    loginLogger.logCompleteLogin(
        testLoginRequest.authId,
        mapOf(),
        LoginClient.Result.Code.ERROR,
        null,
        Exception(errorMessage))

    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(eq(LoginLogger.EVENT_NAME_LOGIN_COMPLETE), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_AUTH_LOGGER_ID))
        .isEqualTo(testLoginRequest.authId)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_LOGIN_RESULT))
        .isEqualTo(LoginClient.Result.Code.ERROR.loggingValue)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_ERROR_MESSAGE)).isEqualTo(errorMessage)
  }

  @Test
  fun `test logCompleteLogin with success result logging the result logging value and starting heartbeat logging`() {
    val errorMessage = "Test login failure"
    val bundleCaptor = argumentCaptor<Bundle>()
    val jobCaptor = argumentCaptor<Runnable>()

    loginLogger.logCompleteLogin(
        testLoginRequest.authId,
        mapOf(),
        LoginClient.Result.Code.SUCCESS,
        null,
        Exception(errorMessage))

    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(eq(LoginLogger.EVENT_NAME_LOGIN_COMPLETE), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_AUTH_LOGGER_ID))
        .isEqualTo(testLoginRequest.authId)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_LOGIN_RESULT))
        .isEqualTo(LoginClient.Result.Code.SUCCESS.loggingValue)
    verify(mockExecutor).schedule(jobCaptor.capture(), any(), any())
    val job = jobCaptor.firstValue
    job.run()
    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(eq(LoginLogger.EVENT_NAME_LOGIN_HEARTBEAT), any<Bundle>())
  }

  @Test
  fun `test logCompleteLogin with custom event name`() {
    val bundleCaptor = argumentCaptor<Bundle>()

    loginLogger.logCompleteLogin(
        testLoginRequest.authId,
        mapOf(),
        LoginClient.Result.Code.CANCEL,
        null,
        null,
        "custom_event_name")

    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(eq("custom_event_name"), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_AUTH_LOGGER_ID))
        .isEqualTo(testLoginRequest.authId)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_LOGIN_RESULT))
        .isEqualTo(LoginClient.Result.Code.CANCEL.loggingValue)
  }

  @Test
  fun `test logAuthorizationMethodStart logging the method`() {
    val bundleCaptor = argumentCaptor<Bundle>()

    loginLogger.logAuthorizationMethodStart(testLoginRequest.authId, "test_method")

    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(eq(LoginLogger.EVENT_NAME_LOGIN_METHOD_START), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_METHOD)).isEqualTo("test_method")
  }

  @Test
  fun `test logAuthorizationMethodStart with the custom event name logging the method and event name`() {
    val bundleCaptor = argumentCaptor<Bundle>()

    loginLogger.logAuthorizationMethodStart(
        testLoginRequest.authId, "test_method", "custom_event_name")

    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(eq("custom_event_name"), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_METHOD)).isEqualTo("test_method")
  }

  @Test
  fun `test logAuthorizationMethodComplete logging the result, error message and error code `() {
    val result = "error"
    val errorMessage = "errorMessage"
    val errorCode = "500"
    val bundleCaptor = argumentCaptor<Bundle>()

    loginLogger.logAuthorizationMethodComplete(
        testLoginRequest.authId, "test_method", result, errorMessage, errorCode, null)

    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(
            eq(LoginLogger.EVENT_NAME_LOGIN_METHOD_COMPLETE), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_AUTH_LOGGER_ID))
        .isEqualTo(testLoginRequest.authId)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_METHOD)).isEqualTo("test_method")
    assertThat(params.getString(LoginLogger.EVENT_PARAM_LOGIN_RESULT)).isEqualTo(result)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_ERROR_MESSAGE)).isEqualTo(errorMessage)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_ERROR_CODE)).isEqualTo(errorCode)
  }

  @Test
  fun `test logAuthorizationMethodComplete with custom event name logging the result, error message and error code `() {
    val result = "error"
    val errorMessage = "errorMessage"
    val errorCode = "500"
    val bundleCaptor = argumentCaptor<Bundle>()

    loginLogger.logAuthorizationMethodComplete(
        testLoginRequest.authId,
        "test_method",
        result,
        errorMessage,
        errorCode,
        null,
        "custom_event_name")

    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(eq("custom_event_name"), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_AUTH_LOGGER_ID))
        .isEqualTo(testLoginRequest.authId)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_METHOD)).isEqualTo("test_method")
    assertThat(params.getString(LoginLogger.EVENT_PARAM_LOGIN_RESULT)).isEqualTo(result)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_ERROR_MESSAGE)).isEqualTo(errorMessage)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_ERROR_CODE)).isEqualTo(errorCode)
  }

  @Test
  fun `test logAuthorizationMethodNotTried logging the method`() {
    val bundleCaptor = argumentCaptor<Bundle>()

    loginLogger.logAuthorizationMethodNotTried(testLoginRequest.authId, "test_method")

    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(
            eq(LoginLogger.EVENT_NAME_LOGIN_METHOD_NOT_TRIED), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_METHOD)).isEqualTo("test_method")
  }

  @Test
  fun `test logAuthorizationMethodNotTried with the custom event name logging the method and event name`() {
    val bundleCaptor = argumentCaptor<Bundle>()

    loginLogger.logAuthorizationMethodNotTried(
        testLoginRequest.authId, "test_method", "custom_event_name")

    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(eq("custom_event_name"), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_METHOD)).isEqualTo("test_method")
  }

  @Test
  fun `test logLoginStatusStart logging the auth Id`() {
    val bundleCaptor = argumentCaptor<Bundle>()

    loginLogger.logLoginStatusStart(testLoginRequest.authId)

    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(eq(LoginLogger.EVENT_NAME_LOGIN_STATUS_START), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_AUTH_LOGGER_ID))
        .isEqualTo(testLoginRequest.authId)
  }

  @Test
  fun `test logLoginStatusSuccess logging the auth Id and result`() {
    val bundleCaptor = argumentCaptor<Bundle>()

    loginLogger.logLoginStatusSuccess(testLoginRequest.authId)

    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(
            eq(LoginLogger.EVENT_NAME_LOGIN_STATUS_COMPLETE), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_AUTH_LOGGER_ID))
        .isEqualTo(testLoginRequest.authId)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_LOGIN_RESULT))
        .isEqualTo(LoginClient.Result.Code.SUCCESS.loggingValue)
  }

  @Test
  fun `test logLoginStatusFailure logging the auth Id and result`() {
    val bundleCaptor = argumentCaptor<Bundle>()

    loginLogger.logLoginStatusFailure(testLoginRequest.authId)

    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(
            eq(LoginLogger.EVENT_NAME_LOGIN_STATUS_COMPLETE), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_AUTH_LOGGER_ID))
        .isEqualTo(testLoginRequest.authId)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_LOGIN_RESULT))
        .isEqualTo(LoginLogger.EVENT_EXTRAS_FAILURE)
  }

  @Test
  fun `test logLoginStatusError logging the auth Id, result and error message`() {
    val errorMessage = "error message"
    val error = Exception(errorMessage)
    val bundleCaptor = argumentCaptor<Bundle>()

    loginLogger.logLoginStatusError(testLoginRequest.authId, error)

    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(
            eq(LoginLogger.EVENT_NAME_LOGIN_STATUS_COMPLETE), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_AUTH_LOGGER_ID))
        .isEqualTo(testLoginRequest.authId)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_LOGIN_RESULT))
        .isEqualTo(LoginClient.Result.Code.ERROR.loggingValue)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_ERROR_MESSAGE)).isEqualTo(error.toString())
  }

  @Test
  fun `test logUnexpectedError logging the error result`() {
    val errorMessage = "error message"
    val eventName = "test_event"
    val bundleCaptor = argumentCaptor<Bundle>()

    loginLogger.logUnexpectedError(eventName, errorMessage)

    verify(mockInternalAppEventsLogger).logEventImplicitly(eq(eventName), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_LOGIN_RESULT))
        .isEqualTo(LoginClient.Result.Code.ERROR.loggingValue)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_ERROR_MESSAGE)).isEqualTo(errorMessage)
  }

  @Test
  fun `test logUnexpectedError with method logging the error result`() {
    val errorMessage = "error message"
    val eventName = "test_event"
    val method = "test_method"
    val bundleCaptor = argumentCaptor<Bundle>()

    loginLogger.logUnexpectedError(eventName, errorMessage, method)

    verify(mockInternalAppEventsLogger).logEventImplicitly(eq(eventName), bundleCaptor.capture())
    val params = bundleCaptor.firstValue
    assertThat(params.getString(LoginLogger.EVENT_PARAM_LOGIN_RESULT))
        .isEqualTo(LoginClient.Result.Code.ERROR.loggingValue)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_ERROR_MESSAGE)).isEqualTo(errorMessage)
    assertThat(params.getString(LoginLogger.EVENT_PARAM_METHOD)).isEqualTo(method)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.login

import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.os.Bundle
import android.os.Looper
import android.util.Pair
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.ActivityResultRegistry
import androidx.activity.result.contract.ActivityResultContract
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.facebook.AccessToken
import com.facebook.AccessTokenSource
import com.facebook.AuthenticationToken
import com.facebook.FacebookActivity
import com.facebook.FacebookCallback
import com.facebook.FacebookException
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.FacebookSdkNotInitializedException
import com.facebook.LoginStatusCallback
import com.facebook.MockSharedPreference
import com.facebook.Profile
import com.facebook.internal.CallbackManagerImpl
import com.facebook.internal.NativeProtocol
import com.facebook.internal.PlatformServiceClient
import com.facebook.internal.ServerProtocol
import com.facebook.internal.security.OidcSecurityUtil
import com.facebook.login.AuthenticationTokenTestUtil.getEncodedAuthTokenStringForTest
import java.util.Date
import java.util.concurrent.ThreadPoolExecutor
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.mockito.Mock
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox

@PrepareForTest(FacebookSdk::class, OidcSecurityUtil::class)
class LoginManagerTest : FacebookPowerMockTestCase() {
  @Mock lateinit var mockActivity: Activity
  @Mock lateinit var mockFragment: Fragment
  @Mock lateinit var mockApplicationContext: Context
  @Mock lateinit var mockPackageManager: PackageManager
  @Mock lateinit var mockCallback: FacebookCallback<LoginResult>
  @Mock lateinit var threadExecutor: ThreadPoolExecutor
  @Mock lateinit var mockFragmentActivity: FragmentActivity
  @Mock lateinit var mockLooper: Looper
  @Mock lateinit var mockAccessTokenCompanion: AccessToken.Companion
  @Mock lateinit var mockAuthenticationTokenCompanion: AuthenticationToken.Companion
  @Mock lateinit var mockProfileCompanion: Profile.Companion

  override fun setup() {
    super.setup()
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(mockAccessTokenCompanion.getCurrentAccessToken()).thenReturn(null)
    Whitebox.setInternalState(AccessToken::class.java, "Companion", mockAccessTokenCompanion)
    whenever(mockAuthenticationTokenCompanion.getCurrentAuthenticationToken()).thenReturn(null)
    Whitebox.setInternalState(
        AuthenticationToken::class.java, "Companion", mockAuthenticationTokenCompanion)
    Whitebox.setInternalState(Profile::class.java, "Companion", mockProfileCompanion)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationId()).thenReturn(MOCK_APP_ID)
    whenever(FacebookSdk.getApplicationContext()).thenReturn(mockApplicationContext)
    whenever(FacebookSdk.getExecutor()).thenReturn(threadExecutor)
    whenever(FacebookSdk.getGraphApiVersion()).thenReturn(ServerProtocol.getDefaultAPIVersion())
    whenever(mockFragment.activity).thenReturn(mockFragmentActivity)
    whenever(mockActivity.applicationContext).thenReturn(mockApplicationContext)
    whenever(FacebookSdk.getApplicationContext().getSharedPreferences(any<String>(), any()))
        .thenReturn(MockSharedPreference())

    // We use mocks rather than RobolectricPackageManager because it's simpler to not
    // have to specify Intents. Default to resolving all intents to something.
    val resolveInfo = ResolveInfo()
    whenever(mockApplicationContext.packageManager).thenReturn(mockPackageManager)
    whenever(mockApplicationContext.mainLooper).thenReturn(mockLooper)
    whenever(mockApplicationContext.applicationContext).thenReturn(mockApplicationContext)
    whenever(mockPackageManager.resolveActivity(any(), any())).thenReturn(resolveInfo)
    whenever(mockPackageManager.resolveService(any(), any())).thenReturn(resolveInfo)

    // mock and bypass signature verification
    PowerMockito.mockStatic(OidcSecurityUtil::class.java)
    whenever(OidcSecurityUtil.getRawKeyFromEndPoint(any())).thenReturn("key")
    whenever(OidcSecurityUtil.getPublicKeyFromString(any())).thenReturn(mock())
    whenever(OidcSecurityUtil.verify(any(), any(), any())).thenReturn(true)
  }

  @Test(expected = FacebookSdkNotInitializedException::class)
  fun testRequiresSdkToBeInitialized() {
    whenever(FacebookSdk.isInitialized()).thenReturn(false)
    LoginManager()
  }

  @Test
  fun testGetInstance() {
    val loginManager = LoginManager.getInstance()
    assertThat(loginManager).isNotNull
  }

  @Test
  fun testLoginBehaviorDefaultsToSsoWithFallback() {
    val loginManager = LoginManager()
    assertThat(loginManager.loginBehavior).isEqualTo(LoginBehavior.NATIVE_WITH_FALLBACK)
  }

  @Test
  fun testCanChangeLoginBehavior() {
    val loginManager = LoginManager()
    loginManager.setLoginBehavior(LoginBehavior.NATIVE_ONLY)
    assertThat(loginManager.loginBehavior).isEqualTo(LoginBehavior.NATIVE_ONLY)
  }

  @Test
  fun testLoginTargetAppDefaultsToFacebook() {
    val loginManager = LoginManager()
    assertThat(loginManager.loginTargetApp).isEqualTo(LoginTargetApp.FACEBOOK)
  }

  @Test
  fun testCanChangeLoginTargetApp() {
    val loginManager = LoginManager()
    loginManager.setLoginTargetApp(LoginTargetApp.INSTAGRAM)
    assertThat(loginManager.loginTargetApp).isEqualTo(LoginTargetApp.INSTAGRAM)
  }

  @Test
  fun testDefaultAudienceDefaultsToFriends() {
    val loginManager = LoginManager()
    assertThat(loginManager.defaultAudience).isEqualTo(DefaultAudience.FRIENDS)
  }

  @Test
  fun testCanChangeDefaultAudience() {
    val loginManager = LoginManager()
    loginManager.setDefaultAudience(DefaultAudience.EVERYONE)
    assertThat(loginManager.defaultAudience).isEqualTo(DefaultAudience.EVERYONE)
  }

  @Test(expected = FacebookException::class)
  fun testLogInWithReadAndActivityThrowsIfPublishPermissionGiven() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "publish_actions"))
  }

  @Test(expected = FacebookException::class)
  fun testLogInWithPublishAndActivityThrowsIfPublishPermissionGiven() {
    val loginManager = LoginManager()
    loginManager.logInWithPublishPermissions(
        mockActivity, listOf("public_profile", "publish_actions"))
  }

  @Test(expected = FacebookException::class)
  fun testLogInThrowsIfCannotResolveFacebookActivity() {
    whenever(mockPackageManager.resolveActivity(any(), any())).thenReturn(null)
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
  }

  @Test(expected = FacebookException::class)
  fun testLogInThrowsIfCannotStartFacebookActivity() {
    whenever(mockActivity.startActivityForResult(any(), any()))
        .thenThrow(ActivityNotFoundException())
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
  }

  @Test
  fun testLogInWithReadDoesNotThrowWithReadPermissions() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
  }

  @Test
  fun testLogInWithReadListCreatesPendingRequestWithCorrectValues() {
    val loginManager = LoginManager()
    // Change some defaults so we can verify the pending request picks them up.
    loginManager.setLoginBehavior(LoginBehavior.NATIVE_ONLY)
    loginManager.setDefaultAudience(DefaultAudience.EVERYONE)
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
    verifyTestLogInCreatesPendingRequestWithCorrectValues(listOf("public_profile", "user_friends"))
  }

  @Test
  fun testLogInWithReadAndAccessTokenCreatesReauthRequest() {
    val accessToken = createAccessToken()
    whenever(AccessToken.getCurrentAccessToken()).thenReturn(accessToken)
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
    val loginRequestCode = CallbackManagerImpl.RequestCodeOffset.Login.toRequestCode()
    verify(mockActivity).startActivityForResult(any(), eq(loginRequestCode))
  }

  @Test
  fun testLogInWithReadAndActivityStartsFacebookActivityWithCorrectRequest() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
    val intentArgumentCaptor = argumentCaptor<Intent>()
    verify(mockActivity).startActivityForResult(intentArgumentCaptor.capture(), any())
    val intent = intentArgumentCaptor.firstValue
    val componentName = intent.component
    assertThat(componentName?.className).isEqualTo(FacebookActivity::class.java.name)
    assertThat(intent.action).isEqualTo(LoginBehavior.NATIVE_WITH_FALLBACK.name)
  }

  @Test
  fun testLogInWithReadAndFragmentStartsFacebookActivityWithCorrectRequest() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockFragment, listOf("public_profile", "user_friends"))
    val intentArgumentCaptor = argumentCaptor<Intent>()
    verify(mockFragment).startActivityForResult(intentArgumentCaptor.capture(), any())
    val intent = intentArgumentCaptor.firstValue
    val componentName = intent.component
    assertThat(componentName?.className).isEqualTo(FacebookActivity::class.java.name)
    assertThat(intent.action).isEqualTo(LoginBehavior.NATIVE_WITH_FALLBACK.name)
  }

  @Test
  fun testLogInWitPublishDoesNotThrowWithPublishPermissions() {
    val loginManager = LoginManager()
    loginManager.logInWithPublishPermissions(
        mockActivity, listOf("publish_actions", "publish_stream"))
  }

  @Test
  fun testLogInWithPublishListCreatesPendingRequestWithCorrectValues() {
    val loginManager = LoginManager()
    // Change some defaults so we can verify the pending request picks them up.
    loginManager.setLoginBehavior(LoginBehavior.NATIVE_ONLY)
    loginManager.setDefaultAudience(DefaultAudience.EVERYONE)
    loginManager.logInWithPublishPermissions(
        mockActivity, listOf("publish_actions", "publish_stream"))
    verifyTestLogInCreatesPendingRequestWithCorrectValues(
        listOf("publish_actions", "publish_stream"))
  }

  @Test
  fun testLogInWithPublishAndAccessTokenCreatesReauthRequest() {
    val accessToken = createAccessToken()
    whenever(AccessToken.getCurrentAccessToken()).thenReturn(accessToken)
    val loginManager = LoginManager()
    loginManager.logInWithPublishPermissions(
        mockActivity, listOf("publish_actions", "publish_stream"))
    val loginRequestCode = CallbackManagerImpl.RequestCodeOffset.Login.toRequestCode()
    verify(mockActivity).startActivityForResult(any(), eq(loginRequestCode))
  }

  @Test
  fun testOnActivityResultReturnsTrueAndCallsCallbackOnCancelResultCode() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
    val result = loginManager.onActivityResult(Activity.RESULT_CANCELED, null, mockCallback)
    assertThat(result).isTrue
    verify(mockCallback).onCancel()
    verify(mockCallback, never()).onSuccess(any())
  }

  @Test
  fun testOnActivityResultReturnsTrueAndCallsCallbackOnCancelResultCodeEvenWithData() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
    val intent = createSuccessResultIntent()
    val result = loginManager.onActivityResult(Activity.RESULT_CANCELED, intent, mockCallback)
    assertThat(result).isTrue
    verify(mockCallback).onCancel()
    verify(mockCallback, never()).onSuccess(any())
  }

  @Test
  fun testOnActivityResultDoesNotModifyCurrentAccessTokenOnCancelResultCode() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
    loginManager.onActivityResult(Activity.RESULT_CANCELED, null, mockCallback)
    verify(mockAccessTokenCompanion, never()).setCurrentAccessToken(any())
  }

  @Test
  fun testOnActivityResultHandlesMissingCallbackOnCancelResultCode() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
    val result = loginManager.onActivityResult(Activity.RESULT_CANCELED, null)
    assertThat(result).isTrue
  }

  @Test
  fun testOnActivityResultReturnsTrueAndCallsCallbackOnNullData() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
    val result = loginManager.onActivityResult(Activity.RESULT_OK, null, mockCallback)
    assertThat(result).isTrue
    verify(mockCallback).onError(any())
    verify(mockCallback, never()).onSuccess(any())
  }

  @Test
  fun testOnActivityResultReturnsTrueAndCallsCallbackOnMissingResult() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
    val intent = createSuccessResultIntent()
    intent.removeExtra(LoginFragment.RESULT_KEY)
    val result = loginManager.onActivityResult(Activity.RESULT_OK, intent, mockCallback)
    assertThat(result)
    verify(mockCallback).onError(any())
    verify(mockCallback, never()).onSuccess(any())
  }

  @Test
  fun testOnActivityResultReturnsTrueAndCallsCallbackOnErrorResult() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
    val result =
        loginManager.onActivityResult(Activity.RESULT_OK, createErrorResultIntent(), mockCallback)
    assertThat(result).isTrue
    val exceptionCaptor = argumentCaptor<FacebookException>()
    verify(mockCallback).onError(exceptionCaptor.capture())
    verify(mockCallback, never()).onSuccess(any())
    assertThat(exceptionCaptor.firstValue.message).isEqualTo("foo: bar")
  }
  @Test
  fun testOnActivityResultReturnsTrueAndCallsCallbackOnCancelResult() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
    val result =
        loginManager.onActivityResult(
            Activity.RESULT_CANCELED, createCancelResultIntent(), mockCallback)
    assertThat(result).isTrue
    verify(mockCallback).onCancel()
    verify(mockCallback, never()).onSuccess(any())
  }
  @Test
  fun testOnActivityResultDoesNotModifyCurrentAccessTokenOnErrorResultCode() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
    loginManager.onActivityResult(Activity.RESULT_CANCELED, createErrorResultIntent(), mockCallback)
    verify(mockAccessTokenCompanion, never()).setCurrentAccessToken(any())
  }

  @Test
  fun testOnActivityResultDoesNotModifyCurrentAuthenticationTokenOnErrorResultCode() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(
        mockActivity, listOf("public_profile", "user_friends", "openid"))
    loginManager.onActivityResult(
        Activity.RESULT_CANCELED, createSuccessResultIntentForOIDC(), mockCallback)
    verify(mockAuthenticationTokenCompanion, never()).setCurrentAuthenticationToken(any())
  }

  @Test
  fun testOnActivityResultReturnsTrueAndCallsCallbackOnSuccessResult() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
    val result =
        loginManager.onActivityResult(Activity.RESULT_OK, createSuccessResultIntent(), mockCallback)
    assertThat(result).isTrue
    verify(mockCallback, never()).onError(any())
    verify(mockCallback).onSuccess(any())
  }

  @Test
  fun testOnHandlesMissingCallbackOnSuccessResult() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
    val result =
        loginManager.onActivityResult(Activity.RESULT_OK, createSuccessResultIntent(), null)
    assertThat(result).isTrue
  }

  @Test
  fun testOnActivityResultSetsCurrentAccessTokenOnSuccessResult() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
    loginManager.onActivityResult(Activity.RESULT_OK, createSuccessResultIntent(), mockCallback)
    verify(mockAccessTokenCompanion).setCurrentAccessToken(eq(createAccessToken()))
  }

  @Test
  fun testOnActivityResultSetsCurrentAuthenticationTokenOnSuccessResult() {
    val loginManager = LoginManager()
    loginManager.logInWithReadPermissions(
        mockActivity, listOf("public_profile", "user_friends", "openid"))
    loginManager.onActivityResult(
        Activity.RESULT_OK, createSuccessResultIntentForOIDC(), mockCallback)
    verify(mockAuthenticationTokenCompanion)
        .setCurrentAuthenticationToken(eq(createAuthenticationToken()))
  }

  @Test
  fun testLogInWithFamilyExperience() {
    createTestForFamilyLoginExperience(true, true)
  }

  @Test
  fun testLogInWithStandardExperience() {
    createTestForFamilyLoginExperience(false, false)
  }

  @Test
  fun testLogInWithFamilyExperienceAndSkipAccountDedupe() {
    createTestForFamilyLoginExperience(true, false)
  }

  @Test
  fun testLogoutToEnsureAccessTokenAndAuthenticationTokenSetToNull() {
    val loginManager = LoginManager()
    loginManager.logOut()
    verify(mockAuthenticationTokenCompanion).setCurrentAuthenticationToken(null)
    verify(mockAccessTokenCompanion).setCurrentAccessToken(null)
  }

  @Test
  fun testLogInWithAndroidxComponentActivity() {
    val mockRegistry = mock<ActivityResultRegistry>()
    val mockActivity = mock<ComponentActivity>()
    val mockLauncher = mock<ActivityResultLauncher<Intent>>()
    whenever(
            mockRegistry.register(
                any(),
                any<ActivityResultContract<Intent, Pair<Int, Intent>>>(),
                any<ActivityResultCallback<Pair<Int, Intent>>>()))
        .thenReturn(mockLauncher)
    whenever(mockActivity.activityResultRegistry).thenReturn(mockRegistry)
    val permissions = setOf("public_profile", "user_friends")
    LoginManager.getInstance()
        .logInWithReadPermissions(mockActivity, CallbackManagerImpl(), permissions)
    verify(mockLauncher).launch(any())
  }

  @Test
  fun testLogInWithAndroidxFragment() {
    val mockRegistry = mock<ActivityResultRegistry>()
    val mockActivity = mock<FragmentActivity>()
    val mockFragment = mock<Fragment>()
    val mockLauncher = mock<ActivityResultLauncher<Intent>>()
    whenever(mockFragment.activity).thenReturn(mockActivity)
    whenever(
            mockRegistry.register(
                any(),
                any<ActivityResultContract<Intent, Pair<Int, Intent>>>(),
                any<ActivityResultCallback<Pair<Int, Intent>>>()))
        .thenReturn(mockLauncher)
    whenever(mockActivity.activityResultRegistry).thenReturn(mockRegistry)
    val permissions = setOf("public_profile", "user_friends")
    LoginManager.getInstance()
        .logInWithReadPermissions(mockFragment, CallbackManagerImpl(), permissions)
    verify(mockLauncher).launch(any())
  }

  @Test
  fun `test retrieve login status will set a completed listener and start connection with login status client`() {
    val mockLoginStatusClientCompanion: LoginStatusClient.Companion = mock()
    val mockLoginStatusClient: LoginStatusClient = mock()
    whenever(
            mockLoginStatusClientCompanion.newInstance(
                eq(mockApplicationContext), any(), any(), any(), any(), anyOrNull()))
        .thenReturn(mockLoginStatusClient)
    whenever(mockLoginStatusClient.start()).thenReturn(true)
    Whitebox.setInternalState(
        LoginStatusClient::class.java, "Companion", mockLoginStatusClientCompanion)

    LoginManager.getInstance().retrieveLoginStatus(mockApplicationContext, mock())

    val completedListenerCaptor = argumentCaptor<PlatformServiceClient.CompletedListener>()
    verify(mockLoginStatusClient).setCompletedListener(completedListenerCaptor.capture())
    verify(mockLoginStatusClient).start()
  }

  @Test
  fun `test retrieve login status if a correct result is received`() {
    val mockLoginStatusClientCompanion: LoginStatusClient.Companion = mock()
    val mockLoginStatusClient: LoginStatusClient = mock()
    whenever(
            mockLoginStatusClientCompanion.newInstance(
                eq(mockApplicationContext), any(), any(), any(), any(), anyOrNull()))
        .thenReturn(mockLoginStatusClient)
    whenever(mockLoginStatusClient.start()).thenReturn(true)
    Whitebox.setInternalState(
        LoginStatusClient::class.java, "Companion", mockLoginStatusClientCompanion)

    val mockLoginStatusCallback = mock<LoginStatusCallback>()
    LoginManager.getInstance().retrieveLoginStatus(mockApplicationContext, mockLoginStatusCallback)

    val completedListenerCaptor = argumentCaptor<PlatformServiceClient.CompletedListener>()
    verify(mockLoginStatusClient).setCompletedListener(completedListenerCaptor.capture())
    val completedListener = checkNotNull(completedListenerCaptor.firstValue)

    val successResult = Bundle()
    successResult.putString(NativeProtocol.EXTRA_ACCESS_TOKEN, TOKEN_STRING)
    successResult.putStringArrayList(
        NativeProtocol.EXTRA_PERMISSIONS, arrayListOf(*PERMISSIONS.toTypedArray()))
    successResult.putString(NativeProtocol.RESULT_ARGS_SIGNED_REQUEST, SIGNATURE_AND_PAYLOAD)
    successResult.putString(NativeProtocol.RESULT_ARGS_GRAPH_DOMAIN, GRAPH_DOMAIN)
    completedListener.completed(successResult)

    // verify a access token will be constructed from the result
    val accessTokenCaptor = argumentCaptor<AccessToken>()
    verify(mockLoginStatusCallback).onCompleted(accessTokenCaptor.capture())
    val capturedAccessToken = checkNotNull(accessTokenCaptor.firstValue)
    assertThat(capturedAccessToken.token).isEqualTo(TOKEN_STRING)
    assertThat(capturedAccessToken.userId).isEqualTo(USER_ID)
    assertThat(capturedAccessToken.permissions)
        .containsExactlyInAnyOrder(*PERMISSIONS.toTypedArray())
    assertThat(capturedAccessToken.graphDomain).isEqualTo(GRAPH_DOMAIN)
    // verify the same access token will be set to global status
    verify(mockAccessTokenCompanion).setCurrentAccessToken(eq(capturedAccessToken))
  }

  @Test
  fun `test retrieve login status when an invalid result is received`() {
    val mockLoginStatusClientCompanion: LoginStatusClient.Companion = mock()
    val mockLoginStatusClient: LoginStatusClient = mock()
    whenever(
            mockLoginStatusClientCompanion.newInstance(
                eq(mockApplicationContext), any(), any(), any(), any(), anyOrNull()))
        .thenReturn(mockLoginStatusClient)
    whenever(mockLoginStatusClient.start()).thenReturn(true)
    Whitebox.setInternalState(
        LoginStatusClient::class.java, "Companion", mockLoginStatusClientCompanion)

    val mockLoginStatusCallback = mock<LoginStatusCallback>()
    LoginManager.getInstance().retrieveLoginStatus(mockApplicationContext, mockLoginStatusCallback)

    val completedListenerCaptor = argumentCaptor<PlatformServiceClient.CompletedListener>()
    verify(mockLoginStatusClient).setCompletedListener(completedListenerCaptor.capture())
    val completedListener = checkNotNull(completedListenerCaptor.firstValue)

    val result = Bundle()
    // empty access token string
    result.putString(NativeProtocol.EXTRA_ACCESS_TOKEN, "")
    result.putStringArrayList(
        NativeProtocol.EXTRA_PERMISSIONS, arrayListOf(*PERMISSIONS.toTypedArray()))
    result.putString(NativeProtocol.RESULT_ARGS_GRAPH_DOMAIN, GRAPH_DOMAIN)
    completedListener.completed(result)

    // verify that onFailure will be called if the received result is invalid
    verify(mockLoginStatusCallback).onFailure()
    // and no access token will be set
    verify(mockAccessTokenCompanion, never()).setCurrentAccessToken(any())
  }

  @Test
  fun `test failing to start connection in retrieving login status will call onFailure at the callback`() {
    val mockLoginStatusClientCompanion: LoginStatusClient.Companion = mock()
    val mockLoginStatusClient: LoginStatusClient = mock()
    whenever(
            mockLoginStatusClientCompanion.newInstance(
                eq(mockApplicationContext), any(), any(), any(), any(), anyOrNull()))
        .thenReturn(mockLoginStatusClient)
    whenever(mockLoginStatusClient.start()).thenReturn(false)
    Whitebox.setInternalState(
        LoginStatusClient::class.java, "Companion", mockLoginStatusClientCompanion)

    val mockLoginStatusCallback = mock<LoginStatusCallback>()
    LoginManager.getInstance().retrieveLoginStatus(mockApplicationContext, mockLoginStatusCallback)
    verify(mockLoginStatusCallback).onFailure()
  }

  @Test
  fun `test retrieve login status when an error result is received`() {
    val mockLoginStatusClientCompanion: LoginStatusClient.Companion = mock()
    val mockLoginStatusClient: LoginStatusClient = mock()
    whenever(
            mockLoginStatusClientCompanion.newInstance(
                eq(mockApplicationContext), any(), any(), any(), any(), anyOrNull()))
        .thenReturn(mockLoginStatusClient)
    whenever(mockLoginStatusClient.start()).thenReturn(true)
    Whitebox.setInternalState(
        LoginStatusClient::class.java, "Companion", mockLoginStatusClientCompanion)

    val mockLoginStatusCallback = mock<LoginStatusCallback>()
    LoginManager.getInstance().retrieveLoginStatus(mockApplicationContext, mockLoginStatusCallback)

    val completedListenerCaptor = argumentCaptor<PlatformServiceClient.CompletedListener>()
    verify(mockLoginStatusClient).setCompletedListener(completedListenerCaptor.capture())
    val completedListener = checkNotNull(completedListenerCaptor.firstValue)

    val result = Bundle()
    result.putString(NativeProtocol.STATUS_ERROR_TYPE, "test_error")
    result.putString(NativeProtocol.STATUS_ERROR_DESCRIPTION, "test error description")
    completedListener.completed(result)

    val exceptionCaptor = argumentCaptor<FacebookException>()
    verify(mockLoginStatusCallback).onError(exceptionCaptor.capture())
    val capturedException = exceptionCaptor.firstValue
    assertThat(capturedException.message)
        .contains(result.getString(NativeProtocol.STATUS_ERROR_TYPE))
    assertThat(capturedException.message)
        .contains(result.getString(NativeProtocol.STATUS_ERROR_DESCRIPTION))
    // and no access token will be set
    verify(mockAccessTokenCompanion, never()).setCurrentAccessToken(any())
  }

  private fun verifyTestLogInCreatesPendingRequestWithCorrectValues(
      expectedPermissions: Collection<String>
  ) {
    val loginRequestCode = CallbackManagerImpl.RequestCodeOffset.Login.toRequestCode()
    val intentCaptor = argumentCaptor<Intent>()
    verify(mockActivity).startActivityForResult(intentCaptor.capture(), eq(loginRequestCode))
    val capturedIntent = intentCaptor.firstValue
    val bundle = capturedIntent.getBundleExtra(LoginFragment.REQUEST_KEY)
    checkNotNull(bundle)
    val request = bundle.getParcelable<LoginClient.Request>(LoginFragment.EXTRA_REQUEST)
    checkNotNull(request)
    assertThat(request.applicationId).isEqualTo(MOCK_APP_ID)
    assertThat(request.loginBehavior).isEqualTo(LoginBehavior.NATIVE_ONLY)
    assertThat(request.defaultAudience).isEqualTo(DefaultAudience.EVERYONE)
    assertThat(request.permissions).containsAll(expectedPermissions)
  }

  private fun createTestForFamilyLoginExperience(isEnabled: Boolean, shouldSkip: Boolean) {
    val loginManager = LoginManager()
    loginManager.setFamilyLogin(isEnabled)
    loginManager.setShouldSkipAccountDeduplication(shouldSkip)
    val loginRequestCode = CallbackManagerImpl.RequestCodeOffset.Login.toRequestCode()
    val intentCaptor = argumentCaptor<Intent>()
    loginManager.logInWithReadPermissions(mockActivity, listOf("public_profile", "user_friends"))
    verify(
            mockActivity,
        )
        .startActivityForResult(intentCaptor.capture(), eq(loginRequestCode))
    val bundle = intentCaptor.firstValue.getBundleExtra(LoginFragment.REQUEST_KEY)
    checkNotNull(bundle)
    val request = bundle.getParcelable<LoginClient.Request>(LoginFragment.EXTRA_REQUEST)
    checkNotNull(request)
    assertThat(request.isFamilyLogin).isEqualTo(isEnabled)
    assertThat(request.shouldSkipAccountDeduplication()).isEqualTo(shouldSkip)
  }
  companion object {
    private const val MOCK_APP_ID = AuthenticationTokenTestUtil.APP_ID
    private const val TOKEN_STRING = "A token of my esteem"
    private const val USER_ID = "54321"
    // user_id = 54321 for this base64 code
    private const val SIGNATURE_AND_PAYLOAD = "signature.eyJ1c2VyX2lkIjo1NDMyMX0="
    private val PERMISSIONS = listOf("walk", "chew gum")
    private val EXPIRES = Date(2025, 5, 3)
    private val LAST_REFRESH = Date(2023, 8, 15)
    private val DATA_ACCESS_EXPIRATION_TIME = Date(2025, 5, 3)
    private val GRAPH_DOMAIN = "test.facebook.com"
    private const val codeVerifier = "codeVerifier"
    private const val codeChallenge = "codeChallenge"
    private val codeChallengeMethod = CodeChallengeMethod.S256

    private fun createAccessToken(): AccessToken {
      return AccessToken(
          TOKEN_STRING,
          MOCK_APP_ID,
          USER_ID,
          PERMISSIONS,
          null,
          null,
          AccessTokenSource.WEB_VIEW,
          EXPIRES,
          LAST_REFRESH,
          DATA_ACCESS_EXPIRATION_TIME)
    }

    private fun createSuccessResultIntent(): Intent {
      val intent = Intent()
      val permissions = setOf("public_profile", "user_friends")
      val request =
          LoginClient.Request(
              LoginBehavior.NATIVE_WITH_FALLBACK,
              permissions,
              DefaultAudience.NONE,
              "test auth",
              "123456789",
              "12345")
      val accessToken = createAccessToken()
      val result = LoginClient.Result.createTokenResult(request, accessToken)
      intent.putExtra(LoginFragment.RESULT_KEY, result)
      return intent
    }

    private fun createErrorResultIntent(): Intent {
      val intent = Intent()
      val request = PowerMockito.mock(LoginClient.Request::class.java)
      val result = LoginClient.Result.createErrorResult(request, "foo", "bar")
      intent.putExtra(LoginFragment.RESULT_KEY, result)
      return intent
    }

    private fun createCancelResultIntent(): Intent {
      val intent = Intent()
      val request = PowerMockito.mock(LoginClient.Request::class.java)
      val result = LoginClient.Result.createCancelResult(request, null)
      intent.putExtra(LoginFragment.RESULT_KEY, result)
      return intent
    }
    private fun createSuccessResultIntentForOIDC(): Intent {
      val intent = Intent()
      val permissions = setOf("public_profile", "user_friends", "openid")
      val request =
          LoginClient.Request(
              LoginBehavior.NATIVE_WITH_FALLBACK,
              permissions,
              DefaultAudience.ONLY_ME,
              "auth type",
              "123456789",
              "12345",
              LoginTargetApp.FACEBOOK,
              AuthenticationTokenTestUtil.NONCE,
              codeVerifier,
              codeChallenge,
              codeChallengeMethod)
      val accessToken = createAccessToken()
      val authenticationToken = createAuthenticationToken()
      val result =
          LoginClient.Result.createCompositeTokenResult(request, accessToken, authenticationToken)
      intent.putExtra(LoginFragment.RESULT_KEY, result)
      return intent
    }
    private fun createAuthenticationToken(): AuthenticationToken {
      return AuthenticationToken(
          getEncodedAuthTokenStringForTest(), AuthenticationTokenTestUtil.NONCE)
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.login

import android.os.Bundle
import android.os.Parcel
import com.facebook.AccessToken
import com.facebook.AuthenticationToken
import com.facebook.FacebookException
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookRequestError
import com.facebook.FacebookSdk
import com.facebook.FacebookServiceException
import com.facebook.GraphRequest
import com.facebook.GraphResponse
import com.facebook.internal.NativeProtocol
import com.facebook.internal.ServerProtocol
import com.facebook.internal.security.OidcSecurityUtil
import java.security.PublicKey
import java.util.HashSet
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONObject
import org.junit.Assert
import org.junit.Test
import org.mockito.Mockito.times
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(FacebookSdk::class, OidcSecurityUtil::class, PKCEUtil::class, GraphRequest::class)
class LoginMethodHandlerTest : FacebookPowerMockTestCase() {

  private lateinit var mockLoginClient: LoginClient
  private lateinit var testHandler: TestLoginMethodHandler
  private lateinit var request: LoginClient.Request
  private lateinit var bundle: Bundle
  private lateinit var graphRequestMock: GraphRequest
  private lateinit var graphResponseMock: GraphResponse

  private class TestLoginMethodHandler(loginClient: LoginClient) : LoginMethodHandler(loginClient) {
    var tryAuthorizeCalledTimes = 0
    var capturedAuthorizeRequest: LoginClient.Request? = null
    override fun describeContents(): Int = 0

    override fun tryAuthorize(request: LoginClient.Request): Int {
      tryAuthorizeCalledTimes += 1
      capturedAuthorizeRequest = request
      return 0
    }

    override fun putChallengeParam(param: JSONObject) {
      super.putChallengeParam(param)
      param.put("challenge", "challenge-value")
    }

    // will be migrated as a property when LoginMethodHandler is migrated to Kotlin
    override val nameForLogging: String = "test_login_handler"

    public override fun getClientState(authId: String): String {
      return super.getClientState(authId)
    }

    public override fun processCodeExchange(request: LoginClient.Request, values: Bundle): Bundle {
      return super.processCodeExchange(request, values)
    }

    public override fun addLoggingExtra(key: String?, value: Any?) {
      super.addLoggingExtra(key, value)
    }
  }

  override fun setup() {
    super.setup()
    PowerMockito.mockStatic(GraphRequest::class.java)
    PowerMockito.mockStatic(PKCEUtil::class.java)
    PowerMockito.mockStatic(FacebookSdk::class.java)
    PowerMockito.`when`(FacebookSdk.isInitialized()).thenReturn(true)
    PowerMockito.`when`(FacebookSdk.getApplicationId()).thenReturn("123456789")
    PowerMockito.mockStatic(OidcSecurityUtil::class.java)
    PowerMockito.`when`(OidcSecurityUtil.getRawKeyFromEndPoint(any()))
        .thenReturn(AuthenticationTokenTestUtil.PUBLIC_KEY_STRING)
    val pubKey = mock<PublicKey>()
    PowerMockito.`when`(
            OidcSecurityUtil.getPublicKeyFromString(AuthenticationTokenTestUtil.PUBLIC_KEY_STRING))
        .thenReturn(pubKey)
    PowerMockito.`when`(OidcSecurityUtil.verify(eq(pubKey), any(), any())).thenReturn(true)
    mockLoginClient = mock()
    testHandler = TestLoginMethodHandler(mockLoginClient)
    val codeVerifier = "code_verifier"
    request =
        LoginClient.Request(
            LoginBehavior.NATIVE_WITH_FALLBACK,
            HashSet(LoginHandlerTestCase.PERMISSIONS),
            DefaultAudience.FRIENDS,
            "rerequest",
            "1234",
            "5678",
            null,
            AuthenticationTokenTestUtil.NONCE,
            codeVerifier,
            PKCEUtil.generateCodeChallenge(codeVerifier, CodeChallengeMethod.S256),
            CodeChallengeMethod.S256)
    bundle = Bundle()
    graphRequestMock = mock()
    graphResponseMock = mock()
  }

  @Test
  fun `test client state contains name for logging, auth id and challenge params`() {
    val clientState = testHandler.getClientState("1234")
    assertThat(clientState).contains("1234")
    assertThat(clientState).contains(testHandler.nameForLogging)
    assertThat(clientState).contains("challenge-value")
  }

  @Test
  fun `test createAuthenticationTokenFromNativeLogin with empty bundle`() {
    val authenticationToken =
        LoginMethodHandler.createAuthenticationTokenFromNativeLogin(
            Bundle(), AuthenticationTokenTestUtil.NONCE)
    assertThat(authenticationToken).isNull()
  }

  @Test
  fun `test createAuthenticationTokenFromNativeLogin with valid bundle`() {
    val authenticationToken = AuthenticationTokenTestUtil.getAuthenticationTokenForTest()
    val tokenString = authenticationToken.token
    val bundle = Bundle()
    bundle.putString(NativeProtocol.EXTRA_AUTHENTICATION_TOKEN, tokenString)
    val createdAuthenticationToken =
        LoginMethodHandler.createAuthenticationTokenFromNativeLogin(
            bundle, AuthenticationTokenTestUtil.NONCE)
    assertThat(createdAuthenticationToken).isEqualTo(authenticationToken)
  }

  @Test
  fun `test createAuthenticationTokenFromWebBundle with invalid bundle`() {
    val authenticationToken =
        LoginMethodHandler.createAuthenticationTokenFromWebBundle(
            Bundle(), AuthenticationTokenTestUtil.NONCE)
    assertThat(authenticationToken).isNull()
  }

  @Test
  fun `test createAuthenticationTokenFromWebBundle with valid bundle`() {
    val authenticationToken = AuthenticationTokenTestUtil.getAuthenticationTokenForTest()
    val tokenString = authenticationToken.token
    val bundle = Bundle()
    bundle.putString(AuthenticationToken.AUTHENTICATION_TOKEN_KEY, tokenString)
    val createdAuthenticationToken =
        LoginMethodHandler.createAuthenticationTokenFromWebBundle(
            bundle, AuthenticationTokenTestUtil.NONCE)
    checkNotNull(createdAuthenticationToken)
    assertThat(createdAuthenticationToken.token).isEqualTo(tokenString)
  }

  @Test
  fun `test createAccessTokenFromNativeLogin with empty bundle`() {
    val accessToken =
        LoginMethodHandler.createAccessTokenFromNativeLogin(
            Bundle(), mock(), FacebookSdk.getApplicationId())
    assertThat(accessToken).isNull()
  }

  @Test
  fun `test createAccessTokenFromNativeLogin with valid bundle`() {
    val bundle = Bundle()
    bundle.putStringArrayList(NativeProtocol.EXTRA_PERMISSIONS, arrayListOf("email"))
    bundle.putString(NativeProtocol.EXTRA_ACCESS_TOKEN, "access_token")
    bundle.putString(NativeProtocol.EXTRA_USER_ID, "user_id")
    bundle.putString(NativeProtocol.RESULT_ARGS_GRAPH_DOMAIN, "test.facebook.com")
    bundle.putLong(NativeProtocol.EXTRA_EXPIRES_SECONDS_SINCE_EPOCH, 36000L)
    bundle.putLong(NativeProtocol.EXTRA_DATA_ACCESS_EXPIRATION_TIME, 36000L)
    val accessToken =
        LoginMethodHandler.createAccessTokenFromNativeLogin(
            bundle, mock(), FacebookSdk.getApplicationId())
    assertThat(accessToken?.token).isEqualTo("access_token")
  }

  @Test
  fun `test createAccessTokenFromWebBundle with invalid bundle`() {
    val accessToken =
        LoginMethodHandler.createAccessTokenFromWebBundle(
            listOf(), Bundle(), mock(), FacebookSdk.getApplicationId())
    assertThat(accessToken).isNull()
  }

  @Test
  fun `test createAccessTokenFromWebBundle with valid bundle`() {
    val bundle = createValidWebLoginResultBundle()
    val accessToken =
        LoginMethodHandler.createAccessTokenFromWebBundle(
            listOf(), bundle, mock(), FacebookSdk.getApplicationId())
    assertThat(accessToken?.userId).isEqualTo(WEB_LOGIN_TEST_USER_ID)
  }

  @Test
  fun `test by default not to track multiple intents`() {
    assertThat(testHandler.shouldKeepTrackOfMultipleIntents()).isFalse
  }

  @Test
  fun `test write to parcel will write logging extras`() {
    testHandler.addLoggingExtra("extra_logging_key", "extra_logging_value")
    val parcel = mock<Parcel>()
    testHandler.writeToParcel(parcel, 0)
    verify(parcel).writeString("extra_logging_key")
    verify(parcel).writeString("extra_logging_value")
  }

  @Test
  fun `test processCodeExchange without code param`() {
    whenever(PKCEUtil.createCodeExchangeRequest(any(), any(), any())).thenReturn(graphRequestMock)
    try {
      testHandler.processCodeExchange(request, bundle)
      Assert.fail()
    } catch (ex: FacebookException) {
      assertThat(ex.message).isEqualTo("No code param found from the request")
    }
    verify(graphRequestMock, times(0)).executeAndWait()
  }

  @Test
  fun `test processCodeExchange with bad request`() {
    bundle.putString("code", "code")

    val requestErrorMock = mock<FacebookRequestError>()
    whenever(requestErrorMock.errorMessage).thenReturn("test")
    whenever(graphRequestMock.executeAndWait()).thenReturn(graphResponseMock)
    whenever(graphResponseMock.error).thenReturn(requestErrorMock)
    whenever(PKCEUtil.createCodeExchangeRequest(any(), any(), any())).thenReturn(graphRequestMock)
    try {
      testHandler.processCodeExchange(request, bundle)
      Assert.fail()
    } catch (ex: FacebookServiceException) {
      assertThat(ex.requestError.errorMessage).isEqualTo("test")
    }
  }

  @Test
  fun `test processCodeExchange with no access_token found from response`() {
    bundle.putString("code", "code")
    val jsonMock = mock<JSONObject>()

    whenever(graphRequestMock.executeAndWait()).thenReturn(graphResponseMock)
    whenever(graphResponseMock.error).thenReturn(null) // make sure no request error
    whenever(graphResponseMock.getJSONObject()).thenReturn(jsonMock)
    whenever(PKCEUtil.createCodeExchangeRequest(any(), any(), any())).thenReturn(graphRequestMock)
    try {
      testHandler.processCodeExchange(request, bundle)
      Assert.fail()
    } catch (ex: FacebookException) {
      assertThat(ex.message).isEqualTo("No access token found from result")
    }
  }

  @Test
  fun `test processCodeExchange successfully with access_token and id_token included`() {
    bundle.putString("code", "code")
    val responseJSONObject = JSONObject()
    responseJSONObject.put(ServerProtocol.DIALOG_PARAM_ACCESS_TOKEN, "123")
    responseJSONObject.put(ServerProtocol.DIALOG_PARAM_AUTHENTICATION_TOKEN, "456")

    whenever(graphRequestMock.executeAndWait()).thenReturn(graphResponseMock)
    whenever(graphResponseMock.error).thenReturn(null) // make sure no request error
    whenever(graphResponseMock.getJSONObject()).thenReturn(responseJSONObject)
    whenever(PKCEUtil.createCodeExchangeRequest(any(), any(), any())).thenReturn(graphRequestMock)
    try {
      val values = testHandler.processCodeExchange(request, bundle)
      assertThat(values[ServerProtocol.DIALOG_PARAM_ACCESS_TOKEN]).isEqualTo("123")
      assertThat(values[ServerProtocol.DIALOG_PARAM_AUTHENTICATION_TOKEN]).isEqualTo("456")
    } catch (ex: FacebookException) {
      Assert.fail()
    }
  }

  @Test
  fun `test processCodeExchange with invalid JSON from response`() {
    bundle.putString("code", "code")

    whenever(graphRequestMock.executeAndWait()).thenReturn(graphResponseMock)
    whenever(graphResponseMock.error).thenReturn(null) // make sure no request error
    whenever(graphResponseMock.getJSONObject()).thenReturn(null) // null JSON or invalid json
    whenever(PKCEUtil.createCodeExchangeRequest(any(), any(), any())).thenReturn(graphRequestMock)
    try {
      testHandler.processCodeExchange(request, bundle)
      Assert.fail()
    } catch (ex: FacebookException) {
      assertThat(ex.message).isEqualTo("No access token found from result")
    }
  }

  companion object {
    // user_id = 54321 for this base64 code
    private const val SIGNATURE_AND_PAYLOAD = "signature.eyJ1c2VyX2lkIjo1NDMyMX0="
    const val WEB_LOGIN_TEST_USER_ID = "54321"

    internal fun createValidWebLoginResultBundle(): Bundle {
      val bundle = Bundle()
      bundle.putLong(AccessToken.EXPIRES_IN_KEY, 36000L)
      bundle.putLong(AccessToken.DATA_ACCESS_EXPIRATION_TIME, 36000L)
      bundle.putString(AccessToken.ACCESS_TOKEN_KEY, "access_token")
      bundle.putString(AccessToken.GRAPH_DOMAIN, "test.facebook.com")
      bundle.putString("signed_request", SIGNATURE_AND_PAYLOAD)
      return bundle
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.login

import androidx.test.core.app.ApplicationProvider
import com.facebook.AccessToken
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.internal.security.OidcSecurityUtil
import com.facebook.login.AuthenticationTokenTestUtil.getAuthenticationTokenForTest
import java.security.PublicKey
import java.util.HashSet
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.anyString
import org.mockito.kotlin.any
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.api.mockito.PowerMockito.mock
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(FacebookSdk::class, OidcSecurityUtil::class)
class LoginResultTest : FacebookPowerMockTestCase() {
  private val EMAIL_SET = setOf("email")
  private val LIKES_EMAIL_SET = setOf("user_likes", "email")
  private val PROFILE_EMAIL_SET = setOf("user_profile", "email")

  @Before
  fun setUp() {
    super.setup()

    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationId()).thenReturn(AuthenticationTokenTestUtil.APP_ID)
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())

    PowerMockito.mockStatic(OidcSecurityUtil::class.java)
    whenever(OidcSecurityUtil.getRawKeyFromEndPoint(anyString())).thenReturn("key")
    whenever(OidcSecurityUtil.getPublicKeyFromString(anyString()))
        .thenReturn(mock(PublicKey::class.java))
    whenever(OidcSecurityUtil.verify(any(), anyString(), anyString())).thenReturn(true)
  }

  @Test
  fun testInitialLogin() {
    val request = createRequest(EMAIL_SET, false)
    val accessToken = createAccessToken(PROFILE_EMAIL_SET, HashSet(), HashSet())
    val authenticationToken = getAuthenticationTokenForTest()
    val result = LoginManager.computeLoginResult(request, accessToken, authenticationToken)
    assertThat(accessToken).isEqualTo(result.accessToken)
    assertThat(authenticationToken).isEqualTo(result.authenticationToken)
    assertThat(PROFILE_EMAIL_SET).isEqualTo(result.recentlyGrantedPermissions)
    assertThat(0).isEqualTo(result.recentlyDeniedPermissions.size)
  }

  @Test
  fun testReAuth() {
    val request = createRequest(EMAIL_SET, true)
    val accessToken = createAccessToken(PROFILE_EMAIL_SET, HashSet(), HashSet())
    val authenticationToken = getAuthenticationTokenForTest()
    val result = LoginManager.computeLoginResult(request, accessToken, authenticationToken)
    assertThat(accessToken).isEqualTo(result.accessToken)
    assertThat(authenticationToken).isEqualTo(result.authenticationToken)
    assertThat(EMAIL_SET).isEqualTo(result.recentlyGrantedPermissions)
    assertThat(0).isEqualTo(result.recentlyDeniedPermissions.size)
  }

  @Test
  fun testDeniedPermissions() {
    val request = createRequest(LIKES_EMAIL_SET, true)
    val accessToken = createAccessToken(EMAIL_SET, HashSet(), HashSet())
    val authenticationToken = getAuthenticationTokenForTest()
    val result = LoginManager.computeLoginResult(request, accessToken, authenticationToken)
    assertThat(accessToken).isEqualTo(result.accessToken)
    assertThat(authenticationToken).isEqualTo(result.authenticationToken)
    assertThat(EMAIL_SET).isEqualTo(result.recentlyGrantedPermissions)
    assertThat(setOf("user_likes")).isEqualTo(result.recentlyDeniedPermissions)
  }

  private fun createAccessToken(
      permissions: Set<String>,
      declinedPermissions: Set<String>,
      expiredPermissions: Set<String>
  ): AccessToken {
    return AccessToken(
        "token",
        "123",
        "234",
        permissions,
        declinedPermissions,
        expiredPermissions,
        null,
        null,
        null,
        null)
  }

  private fun createRequest(permissions: Set<String>, isRerequest: Boolean): LoginClient.Request {
    val request =
        LoginClient.Request(
            LoginBehavior.NATIVE_WITH_FALLBACK,
            permissions,
            DefaultAudience.EVERYONE,
            "rerequest",
            "123",
            "authid")
    request.isRerequest = isRerequest
    return request
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.login

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.test.core.app.ApplicationProvider
import com.facebook.FacebookException
import com.facebook.FacebookSdk
import com.facebook.internal.ServerProtocol
import com.facebook.internal.security.OidcSecurityUtil
import java.util.concurrent.Executor
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(FacebookSdk::class, OidcSecurityUtil::class)
class NativeAppLoginMethodHandlerTest : LoginHandlerTestCase() {
  private lateinit var mockExecutor: Executor
  private lateinit var testLoginHandler: TestNativeAppLoginMethodHandler
  private lateinit var testRequest: LoginClient.Request

  inner class TestNativeAppLoginMethodHandler(loginClient: LoginClient) :
      NativeAppLoginMethodHandler(loginClient) {

    var capturedRequest: LoginClient.Request? = null
    var mockCodeExchangeResult: Bundle? = null

    override fun tryAuthorize(request: LoginClient.Request): Int {
      capturedRequest = request
      return 1
    }

    override fun processCodeExchange(request: LoginClient.Request, values: Bundle): Bundle {
      val result = mockCodeExchangeResult
      if (result == null) {
        throw FacebookException("No code exchange result available")
      } else {
        return result
      }
    }

    override fun describeContents(): Int = 0

    override val nameForLogging: String = "test_native_app_login"
  }

  override fun setup() {
    super.setup()
    mockExecutor = mock()
    mockLoginClient = mock()
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
    whenever(FacebookSdk.getExecutor()).thenReturn(mockExecutor)
    whenever(FacebookSdk.getApplicationId()).thenReturn("123456789")
    testLoginHandler = TestNativeAppLoginMethodHandler(mockLoginClient)
    testRequest = createRequest()
    whenever(mockLoginClient.pendingRequest).thenReturn(testRequest)
  }

  @Test
  fun `test no return data available will be treated as canceled`() {
    testLoginHandler.onActivityResult(0, Activity.RESULT_OK, null)

    val outcomeCaptor = argumentCaptor<LoginClient.Result>()
    verify(mockLoginClient).completeAndValidate(outcomeCaptor.capture())
    val capturedOutcome = outcomeCaptor.firstValue
    assertThat(capturedOutcome.code).isEqualTo(LoginClient.Result.Code.CANCEL)
  }

  @Test
  fun `test cancel result without extras`() {
    val data = Intent()
    testLoginHandler.onActivityResult(0, Activity.RESULT_CANCELED, data)

    val outcomeCaptor = argumentCaptor<LoginClient.Result>()
    verify(mockLoginClient).completeAndValidate(outcomeCaptor.capture())
    val capturedOutcome = outcomeCaptor.firstValue
    assertThat(capturedOutcome.code).isEqualTo(LoginClient.Result.Code.CANCEL)
  }

  @Test
  fun `test cancel result with error`() {
    val data = Intent()
    data.putExtra(ERROR_FIELD, TEST_ERROR_MESSAGE)
    testLoginHandler.onActivityResult(0, Activity.RESULT_CANCELED, data)

    val outcomeCaptor = argumentCaptor<LoginClient.Result>()
    verify(mockLoginClient).completeAndValidate(outcomeCaptor.capture())
    val capturedOutcome = outcomeCaptor.firstValue
    assertThat(capturedOutcome.code).isEqualTo(LoginClient.Result.Code.CANCEL)
    assertThat(capturedOutcome.errorMessage).isEqualTo(TEST_ERROR_MESSAGE)
  }

  @Test
  fun `test cancel result with connection error will be treated as error`() {
    val data = Intent()
    data.putExtra(ERROR_CODE_FIELD, ServerProtocol.getErrorConnectionFailure())
    data.putExtra(ERROR_MESSAGE_FIELD, TEST_ERROR_MESSAGE)

    testLoginHandler.onActivityResult(0, Activity.RESULT_CANCELED, data)

    val outcomeCaptor = argumentCaptor<LoginClient.Result>()
    verify(mockLoginClient).completeAndValidate(outcomeCaptor.capture())
    val capturedOutcome = outcomeCaptor.firstValue
    assertThat(capturedOutcome.code).isEqualTo(LoginClient.Result.Code.ERROR)
    assertThat(capturedOutcome.errorCode).isEqualTo(ServerProtocol.getErrorConnectionFailure())
    assertThat(capturedOutcome.errorMessage).isEqualTo(TEST_ERROR_MESSAGE)
  }

  @Test
  fun `test error result without extras`() {
    val data = Intent()

    testLoginHandler.onActivityResult(0, 15, data)

    val outcomeCaptor = argumentCaptor<LoginClient.Result>()
    verify(mockLoginClient).completeAndValidate(outcomeCaptor.capture())
    val capturedOutcome = outcomeCaptor.firstValue
    assertThat(capturedOutcome.code).isEqualTo(LoginClient.Result.Code.ERROR)
    assertThat(capturedOutcome.errorMessage).contains("Unexpected resultCode")
  }

  @Test
  fun `test ok result without extras`() {
    val data = Intent()

    testLoginHandler.onActivityResult(0, Activity.RESULT_OK, data)

    val outcomeCaptor = argumentCaptor<LoginClient.Result>()
    verify(mockLoginClient).completeAndValidate(outcomeCaptor.capture())
    val capturedOutcome = outcomeCaptor.firstValue
    assertThat(capturedOutcome.code).isEqualTo(LoginClient.Result.Code.ERROR)
    assertThat(capturedOutcome.errorMessage).contains("Unexpected null")
  }

  @Test
  fun `test processing success response with code exchange enabled`() {
    PowerMockito.mockStatic(OidcSecurityUtil::class.java)
    whenever(OidcSecurityUtil.getRawKeyFromEndPoint(any())).thenReturn("key")
    whenever(OidcSecurityUtil.getPublicKeyFromString(any())).thenReturn(mock())
    whenever(OidcSecurityUtil.verify(any(), any(), any())).thenReturn(true)
    val authenticationTokenString = AuthenticationTokenTestUtil.getEncodedAuthTokenStringForTest()
    val data = Intent()
    data.putExtra("code", "code_exchange")
    val codeExchangeResult = Bundle()
    codeExchangeResult.putString(ServerProtocol.DIALOG_PARAM_ACCESS_TOKEN, TEST_ACCESS_TOKEN)
    codeExchangeResult.putString(
        ServerProtocol.DIALOG_PARAM_AUTHENTICATION_TOKEN, authenticationTokenString)
    codeExchangeResult.putString("signed_request", SIGNED_REQUEST_STR)

    testLoginHandler.mockCodeExchangeResult = codeExchangeResult

    testLoginHandler.onActivityResult(0, Activity.RESULT_OK, data)

    val runnableCaptor = argumentCaptor<Runnable>()
    verify(mockExecutor).execute(runnableCaptor.capture())
    val capturedRunnable = runnableCaptor.firstValue
    capturedRunnable.run()

    val outcomeCaptor = argumentCaptor<LoginClient.Result>()
    verify(mockLoginClient).completeAndValidate(outcomeCaptor.capture())
    val capturedOutcome = outcomeCaptor.firstValue
    assertThat(capturedOutcome.code).isEqualTo(LoginClient.Result.Code.SUCCESS)
    assertThat(capturedOutcome.token?.token).isEqualTo(TEST_ACCESS_TOKEN)
    assertThat(capturedOutcome.authenticationToken?.token).isEqualTo(authenticationTokenString)
  }

  @Test
  fun `test processing success response with invalid code exchange result`() {
    val data = Intent()
    data.putExtra("code", "code_exchange")
    val authenticationTokenString = AuthenticationTokenTestUtil.getEncodedAuthTokenStringForTest()
    val codeExchangeResult = Bundle()
    codeExchangeResult.putString(ServerProtocol.DIALOG_PARAM_ACCESS_TOKEN, TEST_ACCESS_TOKEN)
    codeExchangeResult.putString(
        ServerProtocol.DIALOG_PARAM_AUTHENTICATION_TOKEN, authenticationTokenString)
    codeExchangeResult.putString("signed_request", "invalid result")

    testLoginHandler.mockCodeExchangeResult = codeExchangeResult

    testLoginHandler.onActivityResult(0, Activity.RESULT_OK, data)

    val runnableCaptor = argumentCaptor<Runnable>()
    verify(mockExecutor).execute(runnableCaptor.capture())
    val capturedRunnable = runnableCaptor.firstValue
    capturedRunnable.run()

    val outcomeCaptor = argumentCaptor<LoginClient.Result>()
    verify(mockLoginClient).completeAndValidate(outcomeCaptor.capture())
    val capturedOutcome = outcomeCaptor.firstValue
    assertThat(capturedOutcome.code).isEqualTo(LoginClient.Result.Code.ERROR)
  }

  companion object {
    private const val ERROR_FIELD = "error"
    private const val ERROR_CODE_FIELD = "error_code"
    private const val ERROR_MESSAGE_FIELD = "error_message"
    private const val TEST_ERROR_MESSAGE = "test error"
    private const val TEST_ACCESS_TOKEN = "test_access_token"
    private const val SIGNED_REQUEST_STR =
        "ggarbage.eyJhbGdvcml0aG0iOiJITUFDSEEyNTYiLCJjb2RlIjoid2h5bm90IiwiaXNzdWVkX2F0IjoxNDIyNTAyMDkyLCJ1c2VyX2lkIjoiMTIzIn0"
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.login

import com.facebook.FacebookPowerMockTestCase
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test

class NonceUtilTest : FacebookPowerMockTestCase() {

  @Test
  fun `test valid nonce`() {
    assertThat(NonceUtil.isValidNonce(AuthenticationTokenTestUtil.NONCE)).isTrue
  }

  @Test
  fun `test invalid nonce`() {
    assertThat(NonceUtil.isValidNonce("nonce ")).isFalse
  }

  @Test
  fun `test empty nonce`() {
    assertThat(NonceUtil.isValidNonce("")).isFalse
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.login

import androidx.test.core.app.ApplicationProvider
import com.facebook.FacebookException
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(FacebookSdk::class)
class PKCEUtilTest : FacebookPowerMockTestCase() {
  // 128 characters valid code verifier
  val VALID_CODE_VERIFIER =
      "MAmh_Ym~fzC9X0QAtsdI~vHaToXbio7J.aHQIV0.VX-_o5MqtReS2t~3Hw-8RD-vL7mV.KyJGR0xXnBjN9BuMldSH5RM4xBa8rRhu2Yju828y6FzJXB2BCEW7~XrcMkp"

  @Before
  fun before() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
    whenever(FacebookSdk.getApplicationId()).thenReturn(AuthenticationTokenTestUtil.APP_ID)
  }

  @Test
  fun `test isValidCodeVerifier with valid code verifier`() {
    assertThat(PKCEUtil.isValidCodeVerifier(VALID_CODE_VERIFIER)).isTrue
  }

  @Test
  fun `test isValidCodeVerifier with invalid empty or null code verifier`() {
    assertThat(PKCEUtil.isValidCodeVerifier("")).isFalse
    assertThat(PKCEUtil.isValidCodeVerifier(null)).isFalse
  }

  @Test
  fun `test isValidCodeVerifier with less than 43 characters invalid code verifier`() {
    assertThat(PKCEUtil.isValidCodeVerifier("abc")).isFalse
  }

  @Test
  fun `test isValidCodeVerifier with more than 128 characters invalid code verifier`() {
    assertThat(PKCEUtil.isValidCodeVerifier(VALID_CODE_VERIFIER + "a")).isFalse
  }

  @Test
  fun `test isValidCodeVerifier with not-allowed character`() {
    assertThat(PKCEUtil.isValidCodeVerifier("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=")).isFalse
  }

  @Test
  fun `test generateCodeVerifier and make sure generated code verifier is valid`() {
    val codeVerifier = PKCEUtil.generateCodeVerifier()
    assertThat(PKCEUtil.isValidCodeVerifier(codeVerifier)).isTrue
  }

  @Test
  fun `test generateCodeChallenge with plain method`() {
    val codeChallenge =
        PKCEUtil.generateCodeChallenge(VALID_CODE_VERIFIER, CodeChallengeMethod.PLAIN)
    assertThat(codeChallenge).isEqualTo(VALID_CODE_VERIFIER)
  }

  @Test(expected = FacebookException::class)
  fun `test generateCodeChallenge with invalid code verifier`() {
    PKCEUtil.generateCodeChallenge("abc", CodeChallengeMethod.PLAIN)
  }

  @Test
  fun `test generateCodeChallenge with correct code verifier and S256`() {
    val expectedCodeChallenge = "Kwymmz9hbJb0Df_F6y-LaDxntHMdlm8vgM5T0320czQ"
    val codeChallenge =
        PKCEUtil.generateCodeChallenge(VALID_CODE_VERIFIER, CodeChallengeMethod.S256)
    assertThat(codeChallenge).isEqualTo(expectedCodeChallenge)
  }

  @Test
  fun `test createCodeExchangeRequest to make sure create correctly`() {
    val paramCode = "code"
    val paramRedirectUri = "redirect_uri"
    val paramCodeVerifier = "code_verifier"
    val paramAppId = AuthenticationTokenTestUtil.APP_ID

    val graphRequest =
        PKCEUtil.createCodeExchangeRequest(paramCode, paramRedirectUri, paramCodeVerifier)
    val parameters = graphRequest.parameters

    // Make sure correct HttpMethod and parameters are called
    assertThat(parameters["code"]).isEqualTo(paramCode)
    assertThat(parameters["redirect_uri"]).isEqualTo(paramRedirectUri)
    assertThat(parameters["code_verifier"]).isEqualTo(paramCodeVerifier)
    assertThat(parameters["client_id"]).isEqualTo(paramAppId)
  }
}