/*
 * 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

import android.content.Context
import android.graphics.Bitmap
import android.location.Location
import android.net.Uri
import android.os.Bundle
import android.os.ParcelFileDescriptor
import android.util.Log
import androidx.test.core.app.ApplicationProvider
import com.facebook.internal.AttributionIdentifiers
import com.facebook.internal.Logger
import com.facebook.internal.ServerProtocol
import java.io.BufferedInputStream
import java.io.File
import java.io.FileNotFoundException
import java.io.PipedInputStream
import java.io.PipedOutputStream
import java.lang.IllegalArgumentException
import java.net.HttpURLConnection
import java.net.URL
import java.net.URLDecoder
import java.util.UUID
import java.util.zip.GZIPInputStream
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONObject
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.times
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(AccessToken::class, FacebookSdk::class, Log::class)
class GraphRequestTest : FacebookPowerMockTestCase() {
  private val facebookGraphUrl = "graph.facebook.com"
  private val gamingGraphUrl = "graph.fb.gg"
  private val instagramGraphUrl = "graph.instagram.com"
  private val mockAppID = "1234"
  private val mockClientToken = "5678"
  private val mockTokenString = "EAAasdf"
  private val mockGamingTokenString = "GGasdf"
  private val mockInstagramTokenString = "IGasdf"
  private val mockAppTokenString = mockAppID + "|" + mockClientToken
  private val mockUserID = "1000"
  private lateinit var mockHttpURLConnection: HttpURLConnection
  private lateinit var mockHttpURLConnectionDataStream: PipedInputStream

  @Before
  override fun setup() {
    super.setup()
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationId()).thenReturn(mockAppID)
    whenever(FacebookSdk.getClientToken()).thenReturn(mockClientToken)
    whenever(FacebookSdk.isDebugEnabled()).thenReturn(false)
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
    whenever(FacebookSdk.getGraphDomain()).thenCallRealMethod()
    whenever(FacebookSdk.getFacebookDomain()).thenCallRealMethod()
    whenever(FacebookSdk.getGraphApiVersion()).thenCallRealMethod()
    mockLoggedInWithTokenDomain("facebook")

    mockHttpURLConnectionDataStream = PipedInputStream()
    mockHttpURLConnection = mock()
    val pipedOutputStreamForMockConnection = PipedOutputStream(mockHttpURLConnectionDataStream)
    whenever(mockHttpURLConnection.outputStream).thenReturn(pipedOutputStreamForMockConnection)
  }

  @Test
  fun testAppendParametersToBaseUrl() {
    val parameters = Bundle()
    parameters.putString("sample_key", "sample_value")
    parameters.putString(GraphRequest.ACCESS_TOKEN_PARAM, "test_access_token")
    val singleGetRequest = GraphRequest(null, "testPath", parameters, HttpMethod.GET, null)
    val singlePostRequest = GraphRequest(null, "testPath", parameters, HttpMethod.POST, null)
    val urlGet = singleGetRequest.urlForSingleRequest
    val urlPost = singlePostRequest.urlForSingleRequest
    val urlBatch = singlePostRequest.relativeUrlForBatchedRequest
    var args = Uri.parse(urlGet).queryParameterNames
    assertThat(args).contains("sample_key")
    args = Uri.parse(urlPost).queryParameterNames
    assertThat(args.isEmpty()).isTrue

    // Batch URL should contain parameters
    args = Uri.parse(urlBatch).queryParameterNames
    assertThat(args).contains("sample_key")
  }

  @Test
  fun testBaseUrlCreationForInstagram() {
    mockLoggedInWithTokenDomain("instagram")
    val igGraphRequest = GraphRequest.newMeRequest(null, null)

    val singleRequestUrl = igGraphRequest.urlForSingleRequest
    val batchRequestUrl = igGraphRequest.relativeUrlForBatchedRequest

    assertThat(singleRequestUrl).contains("graph.instagram.com")
    assertThat(singleRequestUrl).contains(FacebookSdk.getGraphApiVersion())
    assertThat(batchRequestUrl).contains(FacebookSdk.getGraphApiVersion())
  }

  @Test
  fun testApplicationRequestRoutingByTokenDomain() {
    // Application requests should always go to the facebook.com or fb.gg domains
    val appRequest = GraphRequest(null, mockAppID, null, HttpMethod.GET, null)
    val appActivitiesRequest =
        GraphRequest(null, mockAppID + "/activities", null, HttpMethod.GET, null)

    // With no access token set
    assertThat(appRequest.urlForSingleRequest).contains(facebookGraphUrl)
    assertThat(appActivitiesRequest.urlForSingleRequest).contains(facebookGraphUrl)

    mockLoggedInWithTokenDomain("facebook")

    // User is logged in with facebook
    assertThat(appRequest.urlForSingleRequest).contains(facebookGraphUrl)
    assertThat(appActivitiesRequest.urlForSingleRequest).contains(facebookGraphUrl)

    mockLoggedInWithTokenDomain("gaming")

    // User is logged in with gaming
    assertThat(appRequest.urlForSingleRequest).contains(gamingGraphUrl)
    assertThat(appActivitiesRequest.urlForSingleRequest).contains(gamingGraphUrl)

    mockLoggedInWithTokenDomain("instagram")

    // User is logged in with instagram
    assertThat(appRequest.urlForSingleRequest).contains(facebookGraphUrl)
    assertThat(appActivitiesRequest.urlForSingleRequest).contains(facebookGraphUrl)
  }

  @Test
  fun testMeRequestRoutingByTokenDomain() {
    val meRequest = GraphRequest.newMeRequest(null, null)
    val mePermissionsRequest = GraphRequest(null, "/me/permissions", null, HttpMethod.GET, null)

    // With no access token set
    assertThat(meRequest.urlForSingleRequest).contains(facebookGraphUrl)
    assertThat(mePermissionsRequest.urlForSingleRequest).contains(facebookGraphUrl)

    mockLoggedInWithTokenDomain("facebook")

    // User is logged in with facebook
    assertThat(meRequest.urlForSingleRequest).contains(facebookGraphUrl)
    assertThat(mePermissionsRequest.urlForSingleRequest).contains(facebookGraphUrl)

    mockLoggedInWithTokenDomain("gaming")

    // User is logged in with gaming
    assertThat(meRequest.urlForSingleRequest).contains(gamingGraphUrl)
    assertThat(mePermissionsRequest.urlForSingleRequest).contains(gamingGraphUrl)

    mockLoggedInWithTokenDomain("instagram")

    // User is logged in with instagram
    assertThat(meRequest.urlForSingleRequest).contains(instagramGraphUrl)
    assertThat(mePermissionsRequest.urlForSingleRequest).contains(instagramGraphUrl)
  }

  @Test
  fun testRequestShouldUseClientTokenWhenNoTokenProvided() {
    val request = GraphRequest(null, "testPath", Bundle(), HttpMethod.GET, null)
    val requestUri = Uri.parse(request.urlForSingleRequest)
    assertThat(requestUri.getQueryParameter("access_token")).isEqualTo(mockAppTokenString)
  }

  @Test
  fun testFBRequestShouldUseFBTokenWhenPassedAsTokenParam() {
    mockLoggedInWithTokenDomain("facebook")
    val accessToken = createAccessTokenForDomain("facebook")

    val request = GraphRequest(accessToken, "testPath", Bundle(), HttpMethod.GET, null)
    val requestUri = Uri.parse(request.urlForSingleRequest)
    assertThat(requestUri.getQueryParameter("access_token")).isEqualTo(mockTokenString)
  }

  @Test
  fun testFBRequestShouldUseFBTokenWhenPassedAsBundleParam() {
    val parameters = Bundle()
    parameters.putString("access_token", mockTokenString)

    val request = GraphRequest(null, "testPath", parameters, HttpMethod.GET, null)
    val requestUri = Uri.parse(request.urlForSingleRequest)
    assertThat(requestUri.getQueryParameter("access_token")).isEqualTo(mockTokenString)
  }

  @Test
  fun testIGRequestShouldUseClientTokenWhenRerouted() {
    mockLoggedInWithTokenDomain("instagram")
    val accessToken = createAccessTokenForDomain("instagram")

    val request = GraphRequest(accessToken, mockAppID, Bundle(), HttpMethod.GET, null)
    val requestUri = Uri.parse(request.urlForSingleRequest)
    assertThat(requestUri.getQueryParameter("access_token")).isEqualTo(mockAppTokenString)
  }

  @Test
  fun testIGRequestShouldUseIGTokenWhenPassedAsTokenParam() {
    mockLoggedInWithTokenDomain("instagram")
    val accessToken = createAccessTokenForDomain("instagram")

    val request = GraphRequest(accessToken, "me", Bundle(), HttpMethod.GET, null)
    val requestUri = Uri.parse(request.urlForSingleRequest)
    assertThat(requestUri.getQueryParameter("access_token")).isEqualTo(mockInstagramTokenString)
  }

  @Test
  fun testCreateRequest() {
    val request = GraphRequest()
    assertThat(request.httpMethod).isEqualTo(HttpMethod.GET)
    assertThat(request.version).isEqualTo(FacebookSdk.getGraphApiVersion())
  }

  @Test
  fun testCreatePostRequest() {
    val graphObject = JSONObject()
    val parameters = Bundle()
    val graphPath = "me/statuses"
    val request1 = GraphRequest.newPostRequest(null, graphPath, graphObject, null)
    assertThat(request1.accessToken).isNull()
    assertThat(request1.httpMethod).isEqualTo(HttpMethod.POST)
    assertThat(request1.graphPath).isEqualTo(graphPath)
    assertThat(request1.graphObject).isEqualTo(graphObject)
    assertThat(request1.callback).isNull()

    val request2 = GraphRequest(null, graphPath, parameters, HttpMethod.POST, null)
    assertThat(request2.accessToken).isNull()
    assertThat(request2.httpMethod).isEqualTo(HttpMethod.POST)
    assertThat(request2.graphPath).isEqualTo(graphPath)
    FacebookTestUtility.assertEqualContentsWithoutOrder(parameters, request2.parameters)
    assertThat(request2.callback).isNull()
  }

  @Test
  fun testCreateMeRequest() {
    val request = GraphRequest.newMeRequest(null, null)
    assertThat(request.httpMethod).isEqualTo(HttpMethod.GET)
    assertThat(request.graphPath).isEqualTo("me")
  }

  @Test
  fun testCreateIGMeRequest() {
    mockLoggedInWithTokenDomain("instagram")

    val request = GraphRequest.newMeRequest(null, null)

    assertThat(request).isNotNull
    assertThat(request.httpMethod).isEqualTo(HttpMethod.GET)
    assertThat(request.graphPath).isEqualTo("me")

    val requestUrl = request.urlForSingleRequest
    assertThat(requestUrl).contains("graph.instagram.com")
    assertThat(requestUrl).contains(FacebookSdk.getGraphApiVersion())
  }

  @Test
  fun testCreateMyFriendsRequest() {
    val request = GraphRequest.newMyFriendsRequest(null, null)
    assertThat(request.httpMethod).isEqualTo(HttpMethod.GET)
    assertThat(request.graphPath).isEqualTo("me/friends")
  }

  @Test
  fun testCreateDeleteRequest() {
    val request = GraphRequest.newDeleteObjectRequest(mock(), "1111", null)
    assertThat(request.httpMethod).isEqualTo(HttpMethod.DELETE)
    assertThat(request.graphPath).isEqualTo("1111")
  }

  @Test
  fun testCreateUploadPhotoRequest() {
    val image = Bitmap.createBitmap(128, 128, Bitmap.Config.ALPHA_8)
    val request = GraphRequest.newUploadPhotoRequest(null, "me/photos", image, null, null, null)
    val parameters = request.parameters
    assertThat(parameters.containsKey("picture")).isTrue
    assertThat(parameters.getParcelable<Bitmap>("picture")).isEqualTo(image)
    assertThat(request.graphPath).isEqualTo("me/photos")
  }

  @Test(expected = FileNotFoundException::class)
  fun `test create upload photo request with an invalid file`() {
    val testFile = File("no_exist")
    GraphRequest.newUploadPhotoRequest(null, "me/photos", testFile, null, null, null)
  }

  @Test
  fun `test create upload photo request with a valid file`() {
    val testFileContent = byteArrayOf(1, 2, 3, 4)
    val testFilename = UUID.randomUUID().toString()
    val testFile = File(testFilename)
    testFile.deleteOnExit()
    testFile.createNewFile()
    testFile.writeBytes(testFileContent)

    val request =
        GraphRequest.newUploadPhotoRequest(null, "me/photos", testFile, "test caption", null, null)

    val parameters = request.parameters
    assertThat(parameters.containsKey("picture")).isTrue
    val fileDescriptor = parameters.getParcelable<ParcelFileDescriptor>("picture")
    val fileInputStream = ParcelFileDescriptor.AutoCloseInputStream(fileDescriptor)
    val readContent = fileInputStream.readBytes()
    assertThat(readContent).isEqualTo(testFileContent)
    assertThat(parameters.getString("caption")).isEqualTo("test caption")
    assertThat(request.graphPath).isEqualTo("me/photos")
  }

  @Test(expected = FacebookException::class)
  fun `test create upload photo request with an invalid uri`() {
    val httpUri = Uri.parse("https://facebook.com")
    GraphRequest.newUploadPhotoRequest(null, "me/photos", httpUri, null, null, null)
  }

  @Test(expected = FileNotFoundException::class)
  fun `test create upload photo request with an invalid file uri`() {
    val fileUri = Uri.parse("file://no-exists")
    GraphRequest.newUploadPhotoRequest(null, "me/photos", fileUri, null, null, null)
  }

  @Test
  fun `test create upload photo request with a content uri`() {
    val contentUri = Uri.parse("content://test_content_uri")

    val request =
        GraphRequest.newUploadPhotoRequest(null, "me/photos", contentUri, null, null, null)

    val parameters = request.parameters
    assertThat(parameters.containsKey("picture")).isTrue
    assertThat(parameters.getParcelable<Uri>("picture")).isEqualTo(contentUri)
    assertThat(request.graphPath).isEqualTo("me/photos")
  }

  @Test
  fun testCreatePlacesSearchRequestWithLocation() {
    val location = Location("")
    location.latitude = 47.6_204
    location.longitude = -122.3_491
    val request = GraphRequest.newPlacesSearchRequest(null, location, 1_000, 50, null, null)
    assertThat(request.httpMethod).isEqualTo(HttpMethod.GET)
    assertThat(request.graphPath).isEqualTo("search")
  }

  @Test
  fun testCreatePlacesSearchRequestWithSearchText() {
    val request = GraphRequest.newPlacesSearchRequest(null, null, 1_000, 50, "Starbucks", null)
    assertThat(request.httpMethod).isEqualTo(HttpMethod.GET)
    assertThat(request.graphPath).isEqualTo("search")
  }

  @Test(expected = FacebookException::class)
  fun testCreatePlacesSearchRequestRequiresLocationOrSearchText() {
    GraphRequest.newPlacesSearchRequest(null, null, 1_000, 50, null, null)
  }

  @Test
  fun testSetHttpMethodToNilGivesDefault() {
    val request = GraphRequest()
    assertThat(request.httpMethod).isEqualTo(HttpMethod.GET)
    request.httpMethod = null
    assertThat(request.httpMethod).isEqualTo(HttpMethod.GET)
  }

  @Test(expected = IllegalArgumentException::class)
  fun testExecuteBatchWithZeroRequestsThrows() {
    GraphRequest.executeBatchAndWait(*arrayOf())
  }

  @Test(expected = IllegalArgumentException::class)
  fun testToHttpConnectionWithZeroRequestsThrows() {
    GraphRequest.toHttpConnection(*arrayOf())
  }

  @Test
  fun testSingleGetToHttpRequest() {
    val requestMe = GraphRequest(null, "TourEiffel")
    val connection = GraphRequest.toHttpConnection(requestMe)
    assertThat(connection.requestMethod).isEqualTo("GET")
    assertThat(connection.url.path)
        .isEqualTo("/" + FacebookSdk.getGraphApiVersion() + "/TourEiffel")
    assertThat(connection.getRequestProperty("User-Agent")).startsWith("FBAndroidSDK")
    val uri = Uri.parse(connection.url.toString())
    assertThat(uri.getQueryParameter("sdk")).isEqualTo("android")
    assertThat(uri.getQueryParameter("format")).isEqualTo("json")
  }

  @Test
  fun testBuildsClientTokenIfNeeded() {
    val requestMe = GraphRequest(null, "TourEiffel")
    val connection = GraphRequest.toHttpConnection(requestMe)
    val uri = Uri.parse(connection.url.toString())
    val accessToken = uri.getQueryParameter("access_token")
    checkNotNull(accessToken)
    assertThat(accessToken).contains(mockAppID)
    assertThat(accessToken).contains(mockClientToken)
  }

  @Test
  fun testCallback() {
    // Mock http connection response
    val response = mock<GraphResponse>()
    val responses = arrayListOf(response)

    val mockGraphResponseCompanion = mock<GraphResponse.Companion>()
    whenever(mockGraphResponseCompanion.fromHttpConnection(any(), any())).thenReturn(responses)
    Whitebox.setInternalState(GraphResponse::class.java, "Companion", mockGraphResponseCompanion)
    val callback = mock<GraphRequest.Callback>()
    val request = GraphRequest(null, "me/photos", null, null, callback)
    request.executeAndWait()
    verify(callback, times(1)).onCompleted(any())
  }

  @Test
  fun testRequestForCustomAudienceThirdPartyID() {
    val mockAttributionIdentifiersCompanionObject = mock<AttributionIdentifiers.Companion>()
    whenever(mockAttributionIdentifiersCompanionObject.getAttributionIdentifiers(any()))
        .thenReturn(null)
    Whitebox.setInternalState(
        AttributionIdentifiers::class.java, "Companion", mockAttributionIdentifiersCompanionObject)
    whenever(FacebookSdk.getLimitEventAndDataUsage(any<Context>())).thenReturn(false)
    val expectedRequest =
        GraphRequest(
            null, "mockAppID/custom_audience_third_party_id", Bundle(), HttpMethod.GET, null)
    val request =
        GraphRequest.newCustomAudienceThirdPartyIdRequest(
            mock(), FacebookSdk.getApplicationContext(), "mockAppID", null)
    assertThat(request.graphPath).isEqualTo(expectedRequest.graphPath)
    assertThat(request.httpMethod).isEqualTo(expectedRequest.httpMethod)
    FacebookTestUtility.assertEqualContentsWithoutOrder(
        expectedRequest.parameters, request.parameters)
  }

  @Test(expected = FacebookException::class)
  fun `test request for custom audience third party ID when the access token and attribution identifier are not available`() {
    val mockAttributionIdentifiersCompanionObject = mock<AttributionIdentifiers.Companion>()
    whenever(mockAttributionIdentifiersCompanionObject.getAttributionIdentifiers(any()))
        .thenReturn(null)
    Whitebox.setInternalState(
        AttributionIdentifiers::class.java, "Companion", mockAttributionIdentifiersCompanionObject)
    GraphRequest.newCustomAudienceThirdPartyIdRequest(
        null, FacebookSdk.getApplicationContext(), "123456789", null)
  }

  @Test
  fun `test request for custom audience third party ID when the access token is not available and attribution id is available`() {
    val mockAttributionIdentifiersCompanionObject = mock<AttributionIdentifiers.Companion>()
    val mockAttributionIdentifiers = mock<AttributionIdentifiers>()
    whenever(mockAttributionIdentifiersCompanionObject.getAttributionIdentifiers(any()))
        .thenReturn(mockAttributionIdentifiers)
    Whitebox.setInternalState(
        AttributionIdentifiers::class.java, "Companion", mockAttributionIdentifiersCompanionObject)
    whenever(mockAttributionIdentifiers.attributionId).thenReturn("test_attribution_id")
    val request =
        GraphRequest.newCustomAudienceThirdPartyIdRequest(
            null, FacebookSdk.getApplicationContext(), "123456789", null)
    assertThat(request.graphPath).isEqualTo("123456789/custom_audience_third_party_id")
    assertThat(request.parameters.getString("udid")).isEqualTo("test_attribution_id")
  }

  @Test
  fun `test request for custom audience third party ID when the access token is not available and android adid is available`() {
    val mockAttributionIdentifiersCompanionObject = mock<AttributionIdentifiers.Companion>()
    val mockAttributionIdentifiers = mock<AttributionIdentifiers>()
    whenever(mockAttributionIdentifiersCompanionObject.getAttributionIdentifiers(any()))
        .thenReturn(mockAttributionIdentifiers)
    Whitebox.setInternalState(
        AttributionIdentifiers::class.java, "Companion", mockAttributionIdentifiersCompanionObject)
    whenever(mockAttributionIdentifiers.androidAdvertiserId).thenReturn("test_android_adid")
    val request =
        GraphRequest.newCustomAudienceThirdPartyIdRequest(
            null, FacebookSdk.getApplicationContext(), "123456789", null)
    assertThat(request.graphPath).isEqualTo("123456789/custom_audience_third_party_id")
    assertThat(request.parameters.getString("udid")).isEqualTo("test_android_adid")
  }

  @Test
  fun `test request for custom audience third party id will get appid from the access token`() {
    val mockAttributionIdentifiersCompanionObject = mock<AttributionIdentifiers.Companion>()
    whenever(mockAttributionIdentifiersCompanionObject.getAttributionIdentifiers(any()))
        .thenReturn(null)
    Whitebox.setInternalState(
        AttributionIdentifiers::class.java, "Companion", mockAttributionIdentifiersCompanionObject)
    whenever(FacebookSdk.getLimitEventAndDataUsage(any<Context>())).thenReturn(false)
    val mockAccessToken = mock<AccessToken>()
    whenever(mockAccessToken.applicationId).thenReturn("111111111")
    val expectedRequest =
        GraphRequest(
            null, "111111111/custom_audience_third_party_id", Bundle(), HttpMethod.GET, null)
    val request =
        GraphRequest.newCustomAudienceThirdPartyIdRequest(
            mockAccessToken, FacebookSdk.getApplicationContext(), null)
    assertThat(request.graphPath).isEqualTo(expectedRequest.graphPath)
    assertThat(request.httpMethod).isEqualTo(expectedRequest.httpMethod)
    FacebookTestUtility.assertEqualContentsWithoutOrder(
        expectedRequest.parameters, request.parameters)
  }

  @Test
  fun testRoutingNoTokenFacebookDomainApplicationRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null, null, "facebook", mockAppID, "graph.facebook.com", mockAppTokenString)
  }

  @Test
  fun testRoutingNoTokenFacebookDomainMeRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null, null, "facebook", "me", "graph.facebook.com", mockAppTokenString)
  }

  @Test
  fun testRoutingNoTokenGamingDomainApplicationRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null, null, "gaming", mockAppID, "graph.fb.gg", mockAppTokenString)
  }

  @Test
  fun testRoutingNoTokenGamingDomainMeRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null, null, "gaming", "me", "graph.fb.gg", mockAppTokenString)
  }

  @Test
  fun testRoutingNoTokenInstagramDomainApplicationRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null, null, "instagram", mockAppID, "graph.facebook.com", mockAppTokenString)
  }

  @Test
  fun testRoutingNoTokenInstagramDomainMeRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null, null, "instagram", "me", "graph.instagram.com", mockAppTokenString)
  }

  @Test
  fun testRoutingFacebookTokenFacebookDomainApplicationRequest() {
    val accessToken = createAccessTokenForDomain("facebook")
    createTestCaseForDomainRoutingAndTokenType(
        accessToken, null, "facebook", mockAppID, "graph.facebook.com", mockTokenString)
  }

  @Test
  fun testRoutingFacebookTokenFacebookDomainMeRequest() {
    val accessToken = createAccessTokenForDomain("facebook")
    createTestCaseForDomainRoutingAndTokenType(
        accessToken, null, "facebook", "me", "graph.facebook.com", mockTokenString)
  }

  @Test
  fun testRoutingFacebookTokenStringFacebookDomainApplicationRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null, mockTokenString, "facebook", mockAppID, "graph.facebook.com", mockTokenString)
  }

  @Test
  fun testRoutingFacebookTokenStringFacebookDomainMeRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null, mockTokenString, "facebook", "me", "graph.facebook.com", mockTokenString)
  }

  @Test
  fun testRoutingGamingTokenGamingDomainApplicationRequest() {
    val accessToken = createAccessTokenForDomain("gaming")
    createTestCaseForDomainRoutingAndTokenType(
        accessToken, null, "gaming", mockAppID, "graph.fb.gg", mockGamingTokenString)
  }

  @Test
  fun testRoutingGamingTokenGamingDomainMeRequest() {
    val accessToken = createAccessTokenForDomain("gaming")
    createTestCaseForDomainRoutingAndTokenType(
        accessToken, null, "gaming", "me", "graph.fb.gg", mockGamingTokenString)
  }

  @Test
  fun testRoutingGamingTokenStringGamingDomainApplicationRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null, mockGamingTokenString, "gaming", mockAppID, "graph.fb.gg", mockGamingTokenString)
  }

  @Test
  fun testRoutingGamingTokenStringGamingDomainMeRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null, mockGamingTokenString, "gaming", "me", "graph.fb.gg", mockGamingTokenString)
  }

  @Test
  fun testRoutingInstagramTokenInstagramDomainApplicationRequest() {
    val accessToken = createAccessTokenForDomain("instagram")
    createTestCaseForDomainRoutingAndTokenType(
        accessToken, null, "instagram", mockAppID, "graph.facebook.com", mockAppTokenString)
  }

  @Test
  fun testRoutingInstagramTokenInstagramDomainMeRequest() {
    val accessToken = createAccessTokenForDomain("instagram")
    createTestCaseForDomainRoutingAndTokenType(
        accessToken, null, "instagram", "me", "graph.instagram.com", mockInstagramTokenString)
  }

  @Test
  fun testRoutingInstagramTokenStringInstagramDomainApplicationRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null,
        mockInstagramTokenString,
        "instagram",
        mockAppID,
        "graph.facebook.com",
        mockAppTokenString)
  }

  @Test
  fun testRoutingInstagramTokenStringInstagramDomainMeRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null,
        mockInstagramTokenString,
        "instagram",
        "me",
        "graph.instagram.com",
        mockInstagramTokenString)
  }

  @Test
  fun testRoutingFacebookTokenGamingDomainApplicationRequest() {
    val accessToken = createAccessTokenForDomain("facebook")
    createTestCaseForDomainRoutingAndTokenType(
        accessToken, null, "gaming", mockAppID, "graph.fb.gg", mockTokenString)
  }

  @Test
  fun testRoutingFacebookTokenGamingDomainMeRequest() {
    val accessToken = createAccessTokenForDomain("facebook")
    createTestCaseForDomainRoutingAndTokenType(
        accessToken, null, "gaming", "me", "graph.fb.gg", mockTokenString)
  }

  @Test
  fun testRoutingFacebookTokenStringGamingDomainApplicationRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null, mockTokenString, "gaming", mockAppID, "graph.fb.gg", mockTokenString)
  }

  @Test
  fun testRoutingFacebookTokenStringGamingDomainMeRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null, mockTokenString, "gaming", "me", "graph.fb.gg", mockTokenString)
  }

  @Test
  fun testRoutingGamingTokenFacebookDomainApplicationRequest() {
    val accessToken = createAccessTokenForDomain("gaming")
    createTestCaseForDomainRoutingAndTokenType(
        accessToken, null, "facebook", mockAppID, "graph.facebook.com", mockGamingTokenString)
  }

  @Test
  fun testRoutingGamingTokenFacebookDomainMeRequest() {
    val accessToken = createAccessTokenForDomain("gaming")
    createTestCaseForDomainRoutingAndTokenType(
        accessToken, null, "facebook", "me", "graph.facebook.com", mockGamingTokenString)
  }

  @Test
  fun testRoutingGamingTokenStringFacebookDomainApplicationRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null,
        mockGamingTokenString,
        "facebook",
        mockAppID,
        "graph.facebook.com",
        mockGamingTokenString)
  }

  @Test
  fun testRoutingGamingTokenStringFacebookDomainMeRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null, mockGamingTokenString, "facebook", "me", "graph.facebook.com", mockGamingTokenString)
  }

  @Test
  fun testRoutingInstagramTokenFacebookDomainApplicationRequest() {
    val accessToken = createAccessTokenForDomain("instagram")
    createTestCaseForDomainRoutingAndTokenType(
        accessToken, null, "facebook", mockAppID, "graph.facebook.com", mockAppTokenString)
  }

  @Test
  fun testRoutingInstagramTokenFacebookDomainMeRequest() {
    val accessToken = createAccessTokenForDomain("instagram")
    createTestCaseForDomainRoutingAndTokenType(
        accessToken, null, "facebook", "me", "graph.facebook.com", mockInstagramTokenString)
  }

  @Test
  fun testRoutingInstagramTokenStringFacebookDomainApplicationRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null,
        mockInstagramTokenString,
        "facebook",
        mockAppID,
        "graph.facebook.com",
        mockAppTokenString)
  }

  @Test
  fun testRoutingInstagramTokenStringFacebookDomainMeRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null,
        mockInstagramTokenString,
        "facebook",
        "me",
        "graph.facebook.com",
        mockInstagramTokenString)
  }

  @Test
  fun testRoutingFacebookTokenInstagramDomainApplicationRequest() {
    val accessToken = createAccessTokenForDomain("facebook")
    createTestCaseForDomainRoutingAndTokenType(
        accessToken, null, "instagram", mockAppID, "graph.facebook.com", mockAppTokenString)
  }

  @Test
  fun testRoutingFacebookTokenInstagramDomainMeRequest() {
    val accessToken = createAccessTokenForDomain("facebook")
    createTestCaseForDomainRoutingAndTokenType(
        accessToken, null, "instagram", "me", "graph.instagram.com", mockTokenString)
  }

  @Test
  fun testRoutingFacebookTokenStringInstagramDomainApplicationRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null, mockTokenString, "instagram", mockAppID, "graph.facebook.com", mockAppTokenString)
  }

  @Test
  fun testRoutingFacebookTokenStringInstagramDomainMeRequest() {
    createTestCaseForDomainRoutingAndTokenType(
        null, mockTokenString, "instagram", "me", "graph.instagram.com", mockTokenString)
  }

  @Test
  fun testPrefersTokenStringOverAccessTokenObj() {
    val accessToken = createAccessTokenForDomain("instagram")
    val parameters = Bundle()
    parameters.putString("access_token", mockTokenString)
    val request = GraphRequest(accessToken, "me", parameters, HttpMethod.GET, null)
    val requestUri = Uri.parse(request.urlForSingleRequest)
    assertThat(requestUri.getQueryParameter("access_token")).isEqualTo(mockTokenString)
  }

  @Test
  fun testForceApplicationRequestWithProvidedClientToken() {
    mockLoggedInWithTokenDomain("instagram")
    val customClientToken = "0000|0000"
    val parameters = Bundle()
    parameters.putString("access_token", customClientToken)
    val request = GraphRequest(null, "", parameters, HttpMethod.GET, null)
    request.setForceApplicationRequest(true)
    val requestUri = Uri.parse(request.urlForSingleRequest)
    assertThat(requestUri.getHost()).isEqualTo("graph.facebook.com")
    assertThat(requestUri.getQueryParameter("access_token")).isEqualTo(customClientToken)
  }

  @Test
  fun testForceApplicationRequestWithNoToken() {
    mockLoggedInWithTokenDomain("instagram")
    val parameters = Bundle()
    val request = GraphRequest(null, "", parameters, HttpMethod.GET, null)
    request.setForceApplicationRequest(true)
    val requestUri = Uri.parse(request.urlForSingleRequest)
    assertThat(requestUri.getHost()).isEqualTo("graph.facebook.com")
    assertThat(requestUri.getQueryParameter("access_token")).isEqualTo(mockAppTokenString)
  }

  @Test
  fun testForceApplicationRequestWithInstagramUserToken() {
    mockLoggedInWithTokenDomain("instagram")
    val parameters = Bundle()
    parameters.putString("access_token", mockInstagramTokenString)
    val request = GraphRequest(null, "", parameters, HttpMethod.GET, null)
    request.setForceApplicationRequest(true)
    val requestUri = Uri.parse(request.urlForSingleRequest)
    assertThat(requestUri.getHost()).isEqualTo("graph.facebook.com")
    assertThat(requestUri.getQueryParameter("access_token")).isEqualTo(mockAppTokenString)
  }

  @Test
  fun `test serializing request batch to url connection`() {
    val mockConnection = mockHttpURLConnection
    whenever(mockConnection.url).thenReturn(URL("https", "graph.facebook.com", 443, "testfile"))
    whenever(mockConnection.requestMethod).thenReturn(HttpMethod.POST.name)
    whenever(mockConnection.getRequestProperty(any())).thenReturn("test property value")
    val requestBatch =
        GraphRequestBatch(
            GraphRequest.newPostRequest(
                null, "testPostPath", JSONObject("{\"key\":\"value\"}"), null),
            GraphRequest.newGraphPathRequest(null, "testGetPath", null))
    GraphRequest.serializeToUrlConnection(requestBatch, mockConnection)
    verify(mockConnection).requestMethod = HttpMethod.POST.name
    val dataInputStream = BufferedInputStream(GZIPInputStream(mockHttpURLConnectionDataStream))
    val data = dataInputStream.reader().readText()
    val decodeData = URLDecoder.decode(data, "UTF-8")
    // check requests are in the decoded data
    assertThat(decodeData).contains("batch_app_id=$mockAppID")
    assertThat(decodeData)
        .contains("\"relative_url\":\"\\/${ServerProtocol.getDefaultAPIVersion()}\\/testPostPath")
    assertThat(decodeData)
        .contains("\"relative_url\":\"\\/${ServerProtocol.getDefaultAPIVersion()}\\/testGetPath")
    assertThat(decodeData).contains("\"body\":\"key=value\"")
  }

  @Test
  fun `test serializing bytearray attachment to url connection`() {
    val mockConnection = mockHttpURLConnection
    whenever(mockConnection.url).thenReturn(URL("https", "graph.facebook.com", 443, "testfile"))
    whenever(mockConnection.requestMethod).thenReturn(HttpMethod.POST.name)
    whenever(mockConnection.getRequestProperty(any())).thenReturn("test property value")
    val request = GraphRequest.newPostRequest(null, "testPath", null, null)
    request.parameters.putByteArray("attachment", "test attachment data".toByteArray())
    val requestBatch = GraphRequestBatch(request)
    GraphRequest.serializeToUrlConnection(requestBatch, mockConnection)
    verify(mockConnection).requestMethod = HttpMethod.POST.name
    val dataInputStream = BufferedInputStream((mockHttpURLConnectionDataStream))
    val data = dataInputStream.reader().readText()
    val decodeData = URLDecoder.decode(data, "UTF-8")
    // check requests are in the decoded data
    assertThat(decodeData).contains("test attachment data")
  }

  @Test
  fun `test serializing graph object to url connection`() {
    val mockConnection = mockHttpURLConnection
    whenever(mockConnection.url).thenReturn(URL("https", "graph.facebook.com", 443, "testfile"))
    whenever(mockConnection.requestMethod).thenReturn(HttpMethod.POST.name)
    whenever(mockConnection.getRequestProperty(any())).thenReturn("test property value")

    val graphObjectToSerialized =
        JSONObject("""
      {"v1":1,"v2":"test","v3":true,"v4":[1,2,3]}
      """.trim())
    val request = GraphRequest.newPostRequest(null, "testPath", graphObjectToSerialized, null)
    GraphRequest.serializeToUrlConnection(GraphRequestBatch(request), mockConnection)

    val dataInputStream = BufferedInputStream(GZIPInputStream(mockHttpURLConnectionDataStream))
    val data = dataInputStream.reader().readText()
    val decodeData = URLDecoder.decode(data, "UTF-8")
    val dataItems = decodeData.split("&")
    assertThat(dataItems)
        .containsAll(listOf("v1=1", "v2=test", "v3=true", "v4[0]=1", "v4[1]=2", "v4[2]=3"))
  }

  @Test
  fun `test serializing graph object of id reference to url connection`() {
    val mockConnection = mockHttpURLConnection
    whenever(mockConnection.url).thenReturn(URL("https", "graph.facebook.com", 443, "testfile"))
    whenever(mockConnection.requestMethod).thenReturn(HttpMethod.POST.name)
    whenever(mockConnection.getRequestProperty(any())).thenReturn("test property value")

    val graphObjectToSerialized = JSONObject("""
      {"obj":{"id":"12345"}}
      """.trim())
    val request = GraphRequest.newPostRequest(null, "testPath", graphObjectToSerialized, null)
    GraphRequest.serializeToUrlConnection(GraphRequestBatch(request), mockConnection)

    val dataInputStream = BufferedInputStream(GZIPInputStream(mockHttpURLConnectionDataStream))
    val data = dataInputStream.reader().readText()
    val decodeData = URLDecoder.decode(data, "UTF-8")
    val dataItems = decodeData.split("&")
    assertThat(dataItems).contains("obj=12345")
  }

  @Test
  fun `test serializing graph object of url reference to url connection`() {
    val mockConnection = mockHttpURLConnection
    whenever(mockConnection.url).thenReturn(URL("https", "graph.facebook.com", 443, "testfile"))
    whenever(mockConnection.requestMethod).thenReturn(HttpMethod.POST.name)
    whenever(mockConnection.getRequestProperty(any())).thenReturn("test property value")

    val graphObjectToSerialized =
        JSONObject("""
      {"obj":{"url":"http://facebook.com/"}}
      """.trim())
    val request = GraphRequest.newPostRequest(null, "testPath", graphObjectToSerialized, null)
    GraphRequest.serializeToUrlConnection(GraphRequestBatch(request), mockConnection)

    val dataInputStream = BufferedInputStream(GZIPInputStream(mockHttpURLConnectionDataStream))
    val data = dataInputStream.reader().readText()
    val decodeData = URLDecoder.decode(data, "UTF-8")
    val dataItems = decodeData.split("&")
    assertThat(dataItems).contains("obj=http://facebook.com/")
  }

  @Test
  fun `test validating get requests with null or empty fields params will generate an error log`() {
    val mockLoggerCompanion = mock<Logger.Companion>()
    Whitebox.setInternalState(Logger::class.java, "Companion", mockLoggerCompanion)
    val graphRequestWithNullFieldsParams = GraphRequest.newGraphPathRequest(null, "/test/", null)
    graphRequestWithNullFieldsParams.parameters.remove(GraphRequest.FIELDS_PARAM)
    val graphRequestWithEmptyFieldsParams = GraphRequest.newGraphPathRequest(null, "/test/", null)
    graphRequestWithEmptyFieldsParams.parameters.putString(GraphRequest.FIELDS_PARAM, "")
    GraphRequest.validateFieldsParamForGetRequests(
        GraphRequestBatch(graphRequestWithNullFieldsParams, graphRequestWithEmptyFieldsParams))
    verify(mockLoggerCompanion, times(2))
        .log(eq(LoggingBehavior.DEVELOPER_ERRORS), eq(Log.WARN), any(), any())
  }

  @Test
  fun `test validating get requests with correct fields params`() {
    val mockLoggerCompanion = mock<Logger.Companion>()
    Whitebox.setInternalState(Logger::class.java, "Companion", mockLoggerCompanion)
    val graphRequest = GraphRequest.newGraphPathRequest(null, "/test/", null)
    graphRequest.parameters.putString(GraphRequest.FIELDS_PARAM, "id,name")
    GraphRequest.validateFieldsParamForGetRequests(GraphRequestBatch(graphRequest))
    verify(mockLoggerCompanion, never())
        .log(eq(LoggingBehavior.DEVELOPER_ERRORS), eq(Log.WARN), any(), any())
  }

  fun createAccessTokenForDomain(domain: String): AccessToken {
    var tokenString: String? = null
    if (domain == "gaming") {
      tokenString = mockGamingTokenString
    } else if (domain == "instagram") {
      tokenString = mockInstagramTokenString
    } else {
      tokenString = mockTokenString
    }
    return AccessToken(
        tokenString, mockAppID, mockUserID, null, null, null, null, null, null, null, domain)
  }

  fun createTestCaseForDomainRoutingAndTokenType(
      accessToken: AccessToken?,
      tokenString: String?,
      currentTokenDomain: String,
      graphPath: String,
      expectedHost: String,
      expectedTokenToUse: String
  ) {
    mockLoggedInWithTokenDomain(currentTokenDomain)

    val parameters = Bundle()
    if (tokenString != null) {
      parameters.putString("access_token", tokenString)
    }
    val request = GraphRequest(accessToken, graphPath, parameters, HttpMethod.GET, null)
    val requestUri = Uri.parse(request.urlForSingleRequest)

    assertThat(requestUri.getHost()).isEqualTo(expectedHost)
    assertThat(requestUri.getQueryParameter("access_token")).isEqualTo(expectedTokenToUse)
  }

  fun mockLoggedInWithTokenDomain(tokenDomain: String) {
    val mockAccessToken = createAccessTokenForDomain(tokenDomain)
    val mockTokenCompanionObject = mock<AccessToken.Companion>()
    Whitebox.setInternalState(AccessToken::class.java, "Companion", mockTokenCompanionObject)
    whenever(mockTokenCompanionObject.getCurrentAccessToken()).thenReturn(mockAccessToken)
  }
}


/*
 * 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

import java.net.HttpURLConnection
import org.json.JSONArray
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
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, HttpURLConnection::class)
class GraphResponseTest : FacebookPowerMockTestCase() {

  private val validJsonSingle = "{\"anything\":\"swag\"}"

  private val validJsonSingleNested =
      "{\n" +
          "  \"data\": [\n" +
          "    {\n" +
          "      \"gatekeepers\": [\n" +
          "        {\n" +
          "          \"key\": \"" +
          "FBSDKFeatureInstrument" +
          "\",\n" +
          "          \"value\": true\n" +
          "        },\n" +
          "        {\n" +
          "          \"key\": \"" +
          "app_events_killswitch" +
          "\",\n" +
          "          \"value\": \"false\"\n" +
          "        }\n" +
          "      ]\n" +
          "    }\n" +
          "  ]\n" +
          "}"

  private val validJson =
      "[\n" +
          "     { \"body\": {\"number\": \"420 12345\" }},\n" +
          "     { \"body\": {\"number\": \"1337 9000\" }}\n" +
          " ]"

  private val validJsonWithErrorCode =
      "[\n" +
          "     { \"body\": {\"number\": \"420 12345\"}," +
          "        \"code\": \"400\" " +
          "      },\n" +
          "     { \"body\": {\"number\": \"1337 9000\"}," +
          "\"code\": \"400\"" +
          "}\n" +
          " ]"

  private val validJsonWithSuccessCode =
      "[\n" +
          "     { \"body\": {\"number\": \"420 12345\"}," +
          "        \"code\": \"200\" " +
          "      },\n" +
          "     { \"body\": {\"number\": \"1337 9000\"}," +
          "\"code\": \"200\"" +
          "}\n" +
          " ]"
  @Before
  fun init() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.isFullyInitialized()).thenReturn(true)
  }

  @Test
  fun `test get 400 response from batch`() {
    val connection = PowerMockito.mock(HttpURLConnection::class.java)
    whenever(connection.responseCode).thenReturn(400)
    whenever(connection.errorStream).thenReturn(validJson.byteInputStream())
    val graphRequestBatch = GraphRequestBatch()
    graphRequestBatch.add(GraphRequest())
    graphRequestBatch.add(GraphRequest())
    val res = GraphResponse.fromHttpConnection(connection, graphRequestBatch)
    assertEquals(2, res.size)
    assertEquals("{\"number\":\"420 12345\"}", res[0].rawResponse)
    assertEquals("420 12345", res[0].jsonObject?.getString("number"))
    assertEquals("{\"number\":\"1337 9000\"}", res[1].rawResponse)
    assertEquals("1337 9000", res[1].jsonObject?.getString("number"))
  }

  @Test
  fun `test get 200 response from batch`() {
    // notice no difference from 400 in terms of still getting parseable response
    // compare to single request
    val connection = PowerMockito.mock(HttpURLConnection::class.java)
    whenever(connection.responseCode).thenReturn(200)
    whenever(connection.inputStream).thenReturn(validJson.byteInputStream())
    val graphRequestBatch = GraphRequestBatch()
    graphRequestBatch.add(GraphRequest())
    graphRequestBatch.add(GraphRequest())
    val res = GraphResponse.fromHttpConnection(connection, graphRequestBatch)
    assertEquals(2, res.size)
    assertEquals("{\"number\":\"420 12345\"}", res[0].rawResponse)
    assertEquals("420 12345", res[0].jsonObject?.getString("number"))
    assertEquals("{\"number\":\"1337 9000\"}", res[1].rawResponse)
    assertEquals("1337 9000", res[1].jsonObject?.getString("number"))
  }

  @Test
  fun `test get 400 response from batch with 400 code in json`() {
    val connection = PowerMockito.mock(HttpURLConnection::class.java)
    whenever(connection.responseCode).thenReturn(400)
    whenever(connection.errorStream).thenReturn(validJsonWithErrorCode.byteInputStream())
    val graphRequestBatch = GraphRequestBatch()
    graphRequestBatch.add(GraphRequest())
    graphRequestBatch.add(GraphRequest())
    val res = GraphResponse.fromHttpConnection(connection, graphRequestBatch)
    assertEquals(2, res.size)
    for (response in res) {
      assertNotNull(response.error)
      assertNull(response.rawResponse)
      assertEquals(400, response.error?.requestStatusCode)
      assertEquals(FacebookRequestError.INVALID_ERROR_CODE, response.error?.errorCode)
    }
  }

  @Test
  fun `test get 200 response from batch with 200 code in json`() {
    val connection = PowerMockito.mock(HttpURLConnection::class.java)
    whenever(connection.responseCode).thenReturn(200)
    whenever(connection.inputStream).thenReturn(validJsonWithSuccessCode.byteInputStream())
    val graphRequestBatch = GraphRequestBatch()
    graphRequestBatch.add(GraphRequest())
    graphRequestBatch.add(GraphRequest())
    val res = GraphResponse.fromHttpConnection(connection, graphRequestBatch)
    assertEquals(2, res.size)
    assertEquals("{\"number\":\"420 12345\"}", res[0].rawResponse)
    assertEquals("420 12345", res[0].jsonObject?.getString("number"))
    assertEquals("{\"number\":\"1337 9000\"}", res[1].rawResponse)
    assertEquals("1337 9000", res[1].jsonObject?.getString("number"))
  }

  @Test
  fun `test get 200 response single request`() {
    val connection = PowerMockito.mock(HttpURLConnection::class.java)
    whenever(connection.responseCode).thenReturn(200)
    whenever(connection.inputStream).thenReturn(validJsonSingle.byteInputStream())
    val graphRequestBatch = GraphRequestBatch()
    graphRequestBatch.add(GraphRequest())
    val res = GraphResponse.fromHttpConnection(connection, graphRequestBatch)
    assertEquals(1, res.size)
    assertEquals(validJsonSingle, res[0].rawResponse)
    assertEquals("swag", res[0].jsonObject?.getString("anything"))
  }

  @Test
  fun `test get 200 response single request nested`() {
    val connection = PowerMockito.mock(HttpURLConnection::class.java)
    whenever(connection.responseCode).thenReturn(200)
    whenever(connection.inputStream).thenReturn(validJsonSingleNested.byteInputStream())
    val graphRequestBatch = GraphRequestBatch()
    graphRequestBatch.add(GraphRequest())
    val res = GraphResponse.fromHttpConnection(connection, graphRequestBatch)
    assertEquals(1, res.size)
    assertEquals(
        "{\"data\":[{\"gatekeepers\":[{\"key\":\"FBSDKFeatureInstrument\",\"value\":true},{\"key\":\"app_events_killswitch\",\"value\":\"false\"}]}]}",
        res[0].rawResponse)
  }

  @Test
  fun `test get 400 response single request`() {
    val connection = PowerMockito.mock(HttpURLConnection::class.java)
    whenever(connection.responseCode).thenReturn(400)
    whenever(connection.errorStream).thenReturn(validJsonSingle.byteInputStream())
    val graphRequestBatch = GraphRequestBatch()
    graphRequestBatch.add(GraphRequest())
    val res = GraphResponse.fromHttpConnection(connection, graphRequestBatch)
    assertEquals(1, res.size)
    val response = res[0]
    assertNotNull(response.error)
    assertNull(response.rawResponse)
    assertEquals(400, response.error?.requestStatusCode)
    assertEquals(FacebookRequestError.INVALID_ERROR_CODE, response.error?.errorCode)
  }

  @Test
  fun `test obtaining object from response`() {
    val graphObjects = JSONArray(validJson)
    val mockRequest = PowerMockito.mock(GraphRequest::class.java)
    val graphResponse = GraphResponse(mockRequest, null, validJson, graphObjects)
    val retrievedArray = graphResponse.jsonArray
    assertEquals(graphObjects, retrievedArray)
  }
}


/*
 * 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

import android.os.Bundle
import com.facebook.FacebookTestUtility.assertEqualContentsWithoutOrder
import com.facebook.FacebookTestUtility.assertSameCollectionContents
import com.facebook.FacebookTestUtility.nowPlusSeconds
import com.facebook.internal.Utility
import java.lang.reflect.Array
import java.util.ArrayList
import java.util.Date
import java.util.Random
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONObject
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.api.support.membermodification.MemberModifier
import org.powermock.core.classloader.annotations.PrepareForTest
import org.robolectric.RuntimeEnvironment

@PrepareForTest(Utility::class, FacebookSdk::class)
class LegacyTokenCacheTest : FacebookPowerMockTestCase() {
  @Before
  override fun setup() {
    super.setup()
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationId()).thenReturn("123456789")
    whenever(FacebookSdk.getApplicationContext()).thenReturn(RuntimeEnvironment.application)
    MemberModifier.stub<Any>(
            PowerMockito.method(
                Utility::class.java, "awaitGetGraphMeRequestWithCache", String::class.java))
        .toReturn(JSONObject().put("id", "1000"))
  }

  @Test
  fun testAllTypes() {
    val originalBundle = Bundle()
    putBoolean(BOOLEAN_KEY, originalBundle)
    putBooleanArray(BOOLEAN_ARRAY_KEY, originalBundle)
    putByte(BYTE_KEY, originalBundle)
    putByteArray(BYTE_ARRAY_KEY, originalBundle)
    putShort(SHORT_KEY, originalBundle)
    putShortArray(SHORT_ARRAY_KEY, originalBundle)
    putInt(INT_KEY, originalBundle)
    putIntArray(INT_ARRAY_KEY, originalBundle)
    putLong(LONG_KEY, originalBundle)
    putLongArray(LONG_ARRAY_KEY, originalBundle)
    putFloat(FLOAT_KEY, originalBundle)
    putFloatArray(FLOAT_ARRAY_KEY, originalBundle)
    putDouble(DOUBLE_KEY, originalBundle)
    putDoubleArray(DOUBLE_ARRAY_KEY, originalBundle)
    putChar(CHAR_KEY, originalBundle)
    putCharArray(CHAR_ARRAY_KEY, originalBundle)
    putString(STRING_KEY, originalBundle)
    putStringList(STRING_LIST_KEY, originalBundle)
    originalBundle.putSerializable(SERIALIZABLE_KEY, AccessTokenSource.FACEBOOK_APPLICATION_WEB)
    ensureApplicationContext()
    val cache = LegacyTokenHelper(RuntimeEnvironment.application)
    cache.save(originalBundle)
    val cache2 = LegacyTokenHelper(RuntimeEnvironment.application)
    val cachedBundle = checkNotNull(cache2.load())
    Assert.assertEquals(
        originalBundle.getBoolean(BOOLEAN_KEY), cachedBundle.getBoolean(BOOLEAN_KEY))
    assertArrayEquals(
        originalBundle.getBooleanArray(BOOLEAN_ARRAY_KEY),
        cachedBundle.getBooleanArray(BOOLEAN_ARRAY_KEY))
    Assert.assertEquals(
        originalBundle.getByte(BYTE_KEY).toLong(), cachedBundle.getByte(BYTE_KEY).toLong())
    assertArrayEquals(
        originalBundle.getByteArray(BYTE_ARRAY_KEY), cachedBundle.getByteArray(BYTE_ARRAY_KEY))
    Assert.assertEquals(
        originalBundle.getShort(SHORT_KEY).toLong(), cachedBundle.getShort(SHORT_KEY).toLong())
    assertArrayEquals(
        originalBundle.getShortArray(SHORT_ARRAY_KEY), cachedBundle.getShortArray(SHORT_ARRAY_KEY))
    Assert.assertEquals(
        originalBundle.getInt(INT_KEY).toLong(), cachedBundle.getInt(INT_KEY).toLong())
    assertArrayEquals(
        originalBundle.getIntArray(INT_ARRAY_KEY), cachedBundle.getIntArray(INT_ARRAY_KEY))
    Assert.assertEquals(originalBundle.getLong(LONG_KEY), cachedBundle.getLong(LONG_KEY))
    assertArrayEquals(
        originalBundle.getLongArray(LONG_ARRAY_KEY), cachedBundle.getLongArray(LONG_ARRAY_KEY))
    Assert.assertEquals(
        originalBundle.getFloat(FLOAT_KEY).toDouble(),
        cachedBundle.getFloat(FLOAT_KEY).toDouble(),
        FacebookTestUtility.DOUBLE_EQUALS_DELTA)
    assertArrayEquals(
        originalBundle.getFloatArray(FLOAT_ARRAY_KEY), cachedBundle.getFloatArray(FLOAT_ARRAY_KEY))
    Assert.assertEquals(
        originalBundle.getDouble(DOUBLE_KEY),
        cachedBundle.getDouble(DOUBLE_KEY),
        FacebookTestUtility.DOUBLE_EQUALS_DELTA)
    assertArrayEquals(
        originalBundle.getDoubleArray(DOUBLE_ARRAY_KEY),
        cachedBundle.getDoubleArray(DOUBLE_ARRAY_KEY))
    Assert.assertEquals(
        originalBundle.getChar(CHAR_KEY).toLong(), cachedBundle.getChar(CHAR_KEY).toLong())
    assertArrayEquals(
        originalBundle.getCharArray(CHAR_ARRAY_KEY), cachedBundle.getCharArray(CHAR_ARRAY_KEY))
    Assert.assertEquals(originalBundle.getString(STRING_KEY), cachedBundle.getString(STRING_KEY))
    assertListEquals(
        originalBundle.getStringArrayList(STRING_LIST_KEY),
        cachedBundle.getStringArrayList(STRING_LIST_KEY))
    Assert.assertEquals(
        originalBundle.getSerializable(SERIALIZABLE_KEY),
        cachedBundle.getSerializable(SERIALIZABLE_KEY))
  }

  @Test
  fun testMultipleCaches() {
    val bundle1 = Bundle()
    val bundle2 = Bundle()
    bundle1.putInt(INT_KEY, 10)
    bundle1.putString(STRING_KEY, "ABC")
    bundle2.putInt(INT_KEY, 100)
    bundle2.putString(STRING_KEY, "xyz")
    ensureApplicationContext()
    var cache1 = LegacyTokenHelper(RuntimeEnvironment.application)
    var cache2 = LegacyTokenHelper(RuntimeEnvironment.application, "CustomCache")
    cache1.save(bundle1)
    cache2.save(bundle2)

    // Get new references to make sure we are getting persisted data.
    // Reverse the cache references for fun.
    cache1 = LegacyTokenHelper(RuntimeEnvironment.application, "CustomCache")
    cache2 = LegacyTokenHelper(RuntimeEnvironment.application)
    val newBundle1 = checkNotNull(cache1.load())
    val newBundle2 = checkNotNull(cache2.load())
    Assert.assertEquals(bundle2.getInt(INT_KEY).toLong(), newBundle1.getInt(INT_KEY).toLong())
    Assert.assertEquals(bundle2.getString(STRING_KEY), newBundle1.getString(STRING_KEY))
    Assert.assertEquals(bundle1.getInt(INT_KEY).toLong(), newBundle2.getInt(INT_KEY).toLong())
    Assert.assertEquals(bundle1.getString(STRING_KEY), newBundle2.getString(STRING_KEY))
  }

  @Test
  fun testCacheRoundtrip() {
    val permissions: Set<String> = hashSetOf("stream_publish", "go_outside_and_play")
    val token = "AnImaginaryTokenValue"
    val later = nowPlusSeconds(60)
    val earlier = nowPlusSeconds(-60)
    val applicationId = "1234"
    val cache = LegacyTokenHelper(RuntimeEnvironment.application)
    cache.clear()
    var bundle = Bundle()
    LegacyTokenHelper.putToken(bundle, token)
    LegacyTokenHelper.putExpirationDate(bundle, later)
    LegacyTokenHelper.putSource(bundle, AccessTokenSource.FACEBOOK_APPLICATION_NATIVE)
    LegacyTokenHelper.putLastRefreshDate(bundle, earlier)
    LegacyTokenHelper.putPermissions(bundle, permissions)
    LegacyTokenHelper.putDeclinedPermissions(bundle, arrayListOf("whatever"))
    LegacyTokenHelper.putExpiredPermissions(bundle, arrayListOf("anyway"))
    LegacyTokenHelper.putApplicationId(bundle, applicationId)
    cache.save(bundle)
    bundle = checkNotNull(cache.load())
    val accessToken = checkNotNull(AccessToken.createFromLegacyCache(bundle))
    assertSameCollectionContents(permissions, accessToken.permissions)
    Assert.assertEquals(token, accessToken.token)
    Assert.assertEquals(AccessTokenSource.FACEBOOK_APPLICATION_NATIVE, accessToken.source)
    assertThat(accessToken.isExpired).isFalse
    val cachedBundle = AccessTokenTestHelper.toLegacyCacheBundle(accessToken)
    assertEqualContentsWithoutOrder(bundle, cachedBundle)
  }

  private fun ensureApplicationContext() {
    // Since the test case is not running on the UI thread, the applicationContext might
    // not be ready (i.e. it might be null). Wait for a bit to resolve this.
    var waitedFor: Long = 0
    try {
      // Don't hold up execution for too long.
      while (RuntimeEnvironment.application.applicationContext == null && waitedFor <= 2_000) {
        Thread.sleep(50)
        waitedFor += 50
      }
    } catch (e: InterruptedException) {}
  }

  companion object {
    private const val BOOLEAN_KEY = "booleanKey"
    private const val BOOLEAN_ARRAY_KEY = "booleanArrayKey"
    private const val BYTE_KEY = "byteKey"
    private const val BYTE_ARRAY_KEY = "byteArrayKey"
    private const val SHORT_KEY = "shortKey"
    private const val SHORT_ARRAY_KEY = "shortArrayKey"
    private const val INT_KEY = "intKey"
    private const val INT_ARRAY_KEY = "intArrayKey"
    private const val LONG_KEY = "longKey"
    private const val LONG_ARRAY_KEY = "longArrayKey"
    private const val FLOAT_ARRAY_KEY = "floatKey"
    private const val FLOAT_KEY = "floatArrayKey"
    private const val DOUBLE_KEY = "doubleKey"
    private const val DOUBLE_ARRAY_KEY = "doubleArrayKey"
    private const val CHAR_KEY = "charKey"
    private const val CHAR_ARRAY_KEY = "charArrayKey"
    private const val STRING_KEY = "stringKey"
    private const val STRING_LIST_KEY = "stringListKey"
    private const val SERIALIZABLE_KEY = "serializableKey"
    private val random = Random(Date().time)
    private fun assertArrayEquals(a1: Any?, a2: Any?) {
      checkNotNull(a1)
      checkNotNull(a2)
      Assert.assertEquals(a1.javaClass, a2.javaClass)
      assertThat(a1.javaClass.isArray).isTrue
      val length = Array.getLength(a1)
      Assert.assertEquals(length.toLong(), Array.getLength(a2).toLong())
      for (i in 0 until length) {
        val a1Value = Array.get(a1, i)
        val a2Value = Array.get(a2, i)
        Assert.assertEquals(a1Value, a2Value)
      }
    }

    private fun assertListEquals(l1: List<*>?, l2: List<*>?) {
      checkNotNull(l1)
      checkNotNull(l2)
      val i1 = l1.iterator()
      val i2 = l2.iterator()
      while (i1.hasNext() && i2.hasNext()) {
        Assert.assertEquals(i1.next(), i2.next())
      }
      assertThat(i1.hasNext()).isFalse // "Lists not of the same length"
      assertThat(i2.hasNext()).isFalse // "Lists not of the same length"
    }

    private fun putInt(key: String, bundle: Bundle) {
      bundle.putInt(key, random.nextInt())
    }

    private fun putIntArray(key: String, bundle: Bundle) {
      val length = random.nextInt(50)
      val array = IntArray(length)
      for (i in 0 until length) {
        array[i] = random.nextInt()
      }
      bundle.putIntArray(key, array)
    }

    private fun putShort(key: String, bundle: Bundle) {
      bundle.putShort(key, random.nextInt().toShort())
    }

    private fun putShortArray(key: String, bundle: Bundle) {
      val length = random.nextInt(50)
      val array = ShortArray(length)
      for (i in 0 until length) {
        array[i] = random.nextInt().toShort()
      }
      bundle.putShortArray(key, array)
    }

    private fun putByte(key: String, bundle: Bundle) {
      bundle.putByte(key, random.nextInt().toByte())
    }

    private fun putByteArray(key: String, bundle: Bundle) {
      val length = random.nextInt(50)
      val array = ByteArray(length)
      random.nextBytes(array)
      bundle.putByteArray(key, array)
    }

    private fun putBoolean(key: String, bundle: Bundle) {
      bundle.putBoolean(key, random.nextBoolean())
    }

    private fun putBooleanArray(key: String, bundle: Bundle) {
      val length = random.nextInt(50)
      val array = BooleanArray(length)
      for (i in 0 until length) {
        array[i] = random.nextBoolean()
      }
      bundle.putBooleanArray(key, array)
    }

    private fun putLong(key: String, bundle: Bundle) {
      bundle.putLong(key, random.nextLong())
    }

    private fun putLongArray(key: String, bundle: Bundle) {
      val length = random.nextInt(50)
      val array = LongArray(length)
      for (i in 0 until length) {
        array[i] = random.nextLong()
      }
      bundle.putLongArray(key, array)
    }

    private fun putFloat(key: String, bundle: Bundle) {
      bundle.putFloat(key, random.nextFloat())
    }

    private fun putFloatArray(key: String, bundle: Bundle) {
      val length = random.nextInt(50)
      val array = FloatArray(length)
      for (i in 0 until length) {
        array[i] = random.nextFloat()
      }
      bundle.putFloatArray(key, array)
    }

    private fun putDouble(key: String, bundle: Bundle) {
      bundle.putDouble(key, random.nextDouble())
    }

    private fun putDoubleArray(key: String, bundle: Bundle) {
      val length = random.nextInt(50)
      val array = DoubleArray(length)
      for (i in 0 until length) {
        array[i] = random.nextDouble()
      }
      bundle.putDoubleArray(key, array)
    }

    private fun putChar(key: String, bundle: Bundle) {
      bundle.putChar(key, char)
    }

    private fun putCharArray(key: String, bundle: Bundle) {
      bundle.putCharArray(key, charArray)
    }

    private fun putString(key: String, bundle: Bundle) {
      bundle.putString(key, String(charArray))
    }

    private fun putStringList(key: String, bundle: Bundle) {
      var length = random.nextInt(50)
      val stringList = ArrayList<String?>(length)
      while (0 < length--) {
        if (length == 0) {
          stringList.add(null)
        } else {
          stringList.add(String(charArray))
        }
      }
      bundle.putStringArrayList(key, stringList)
    }

    private val charArray: CharArray
      private get() {
        val length = random.nextInt(50)
        val array = CharArray(length)
        for (i in 0 until length) {
          array[i] = char
        }
        return array
      }
    private val char: Char
      private get() = random.nextInt(255).toChar()
  }
}


/*
 * 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

import android.os.Bundle
import com.facebook.internal.Utility
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONObject
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.anyString
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(FacebookSdk::class, Utility::class)
class LegacyTokenHelperTest : FacebookPowerMockTestCase() {

  @Before
  fun init() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    PowerMockito.mockStatic(Utility::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.isFullyInitialized()).thenReturn(true)
    val json = JSONObject("{\"id\":\"swag\"}")
    whenever(Utility.awaitGetGraphMeRequestWithCache(anyString())).thenReturn(json)
  }

  @Test
  fun `test legacy token helper`() {
    val permissions: Set<String> = hashSetOf("stream_publish", "go_outside_and_play")
    val declinedPermissions: Set<String> = hashSetOf("no you may not", "no soup for you")
    val expiredPermissions: Set<String> = hashSetOf("expired", "oh no")
    val token = "AnImaginaryTokenValue"
    val later = FacebookTestUtility.nowPlusSeconds(60)
    val earlier = FacebookTestUtility.nowPlusSeconds(-60)
    val applicationId = "1234"

    val bundle = Bundle()
    LegacyTokenHelper.putToken(bundle, token)
    LegacyTokenHelper.putExpirationDate(bundle, later)
    LegacyTokenHelper.putSource(bundle, AccessTokenSource.FACEBOOK_APPLICATION_WEB)
    LegacyTokenHelper.putLastRefreshDate(bundle, earlier)
    LegacyTokenHelper.putPermissions(bundle, permissions)
    LegacyTokenHelper.putDeclinedPermissions(bundle, declinedPermissions)
    LegacyTokenHelper.putExpiredPermissions(bundle, expiredPermissions)
    LegacyTokenHelper.putApplicationId(bundle, applicationId)

    val accessToken = AccessToken.createFromLegacyCache(bundle)
    Assert.assertNotNull(accessToken)
    checkNotNull(accessToken)
    FacebookTestUtility.assertSameCollectionContents(permissions, accessToken.permissions)
    Assert.assertEquals(token, accessToken.token)
    Assert.assertEquals(AccessTokenSource.FACEBOOK_APPLICATION_WEB, accessToken.source)
    assertThat(accessToken.isExpired).isFalse

    val cache = AccessTokenTestHelper.toLegacyCacheBundle(accessToken)
    FacebookTestUtility.assertEqualContentsWithoutOrder(bundle, cache)
  }
}


/*
 * 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

import android.content.Context
import androidx.test.core.app.ApplicationProvider
import com.facebook.util.common.ProfileTestHelper.assertDefaultObjectGetters
import com.facebook.util.common.ProfileTestHelper.assertMostlyNullsObjectGetters
import com.facebook.util.common.ProfileTestHelper.createDefaultProfile
import com.facebook.util.common.ProfileTestHelper.createMostlyNullsProfile
import org.junit.Assert
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 ProfileCacheTest : FacebookPowerMockTestCase() {
  @Before
  fun before() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.getApplicationId()).thenReturn("123456789")
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    val context = ApplicationProvider.getApplicationContext<Context>()
    context
        .getSharedPreferences(ProfileCache.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
        .edit()
        .clear()
        .commit()
    whenever(FacebookSdk.getApplicationContext()).thenReturn(context)
  }

  @Test
  fun testEmptyCase() {
    val cache = ProfileCache()
    Assert.assertNull(cache.load())
  }

  @Test
  fun testSaveGetAndClear() {
    val cache = ProfileCache()
    var profile1 = createDefaultProfile()
    cache.save(profile1)
    var profile2 = cache.load()
    assertDefaultObjectGetters(profile2)
    Assert.assertEquals(profile1, profile2)
    profile1 = createMostlyNullsProfile()
    cache.save(profile1)
    profile2 = cache.load()
    assertMostlyNullsObjectGetters(profile2)
    Assert.assertEquals(profile1, profile2)
    cache.clear()
    Assert.assertNull(cache.load())
  }
}


/*
 * 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

import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.test.core.app.ApplicationProvider
import com.facebook.util.common.ProfileTestHelper.createDefaultProfile
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.times
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)
class ProfileManagerTest : FacebookPowerMockTestCase() {
  private val mockAppID = "123456789"
  @Before
  fun before() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationId()).thenReturn(mockAppID)

    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
  }

  @Test
  fun testLoadCurrentProfileEmptyCache() {
    val profileCache = mock<ProfileCache>()
    val localBroadcastManager = PowerMockito.mock(LocalBroadcastManager::class.java)
    val profileManager = ProfileManager(localBroadcastManager, profileCache)
    assertThat(profileManager.loadCurrentProfile()).isFalse
    verify(profileCache, times(1)).load()
  }

  @Test
  fun testLoadCurrentProfileWithCache() {
    val profileCache = mock<ProfileCache>()
    val profile = createDefaultProfile()
    whenever(profileCache.load()).thenReturn(profile)
    val localBroadcastManager = mock<LocalBroadcastManager>()
    val profileManager = ProfileManager(localBroadcastManager, profileCache)
    assertThat(profileManager.loadCurrentProfile()).isTrue
    verify(profileCache, times(1)).load()

    profileManager.currentProfile = createDefaultProfile()
    verify(localBroadcastManager, times(1)).sendBroadcast(any())

    // Verify that if we unset the profile there is a broadcast
    profileManager.currentProfile = null
    verify(localBroadcastManager, times(2)).sendBroadcast(any())
  }

  @Test
  fun `test setting a new profile will write to the cache`() {
    val profileCache = mock<ProfileCache>()
    val profile = createDefaultProfile()
    val localBroadcastManager = mock<LocalBroadcastManager>()
    val profileManager = ProfileManager(localBroadcastManager, profileCache)

    profileManager.currentProfile = profile

    verify(profileCache, times(1)).save(profile)
    verify(localBroadcastManager, times(1)).sendBroadcast(any())
    assertThat(profileManager.currentProfile).isEqualTo(profile)
  }
}


/*
 * 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

import android.content.Context
import android.net.Uri
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.test.core.app.ApplicationProvider
import com.facebook.internal.ImageRequest
import com.facebook.internal.Utility
import com.facebook.util.common.ProfileTestHelper.PICTURE_URI
import com.facebook.util.common.ProfileTestHelper.assertDefaultObjectGetters
import com.facebook.util.common.ProfileTestHelper.assertMostlyNullsObjectGetters
import com.facebook.util.common.ProfileTestHelper.createDefaultProfile
import com.facebook.util.common.ProfileTestHelper.createMostlyNullsProfile
import com.facebook.util.common.ProfileTestHelper.createProfileWithPictureUri
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONObject
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.mock
import org.mockito.kotlin.times
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, ImageRequest::class, LocalBroadcastManager::class, Utility::class)
class ProfileTest : FacebookPowerMockTestCase() {
  private lateinit var mockAccessTokenCompanion: AccessToken.Companion
  private lateinit var mockProfileManagerCompanion: ProfileManager.Companion
  private lateinit var mockProfileManager: ProfileManager
  @Before
  fun before() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.getApplicationId()).thenReturn("123456789")
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
    PowerMockito.mockStatic(LocalBroadcastManager::class.java)
    val mockLocalBroadcastManager = PowerMockito.mock(LocalBroadcastManager::class.java)
    whenever(LocalBroadcastManager.getInstance(any<Context>()))
        .thenReturn(mockLocalBroadcastManager)
    PowerMockito.mockStatic(Utility::class.java)
    mockAccessTokenCompanion = mock()
    Whitebox.setInternalState(AccessToken::class.java, "Companion", mockAccessTokenCompanion)
    mockProfileManagerCompanion = mock()
    Whitebox.setInternalState(ProfileManager::class.java, "Companion", mockProfileManagerCompanion)
    mockProfileManager = mock()
    whenever(mockProfileManagerCompanion.getInstance()).thenReturn(mockProfileManager)
  }

  @Test
  fun testProfileCtorAndGetters() {
    var profile = createDefaultProfile()
    assertDefaultObjectGetters(profile)
    profile = createMostlyNullsProfile()
    assertMostlyNullsObjectGetters(profile)
  }

  @Test
  fun testHashCode() {
    val profile1 = createDefaultProfile()
    val profile2 = createDefaultProfile()
    assertThat(profile1.hashCode()).isEqualTo(profile2.hashCode())
    val profile3 = createMostlyNullsProfile()
    assertThat(profile1.hashCode()).isNotEqualTo(profile3.hashCode())

    val profile4 = createProfileWithPictureUri()
    val profile5 = createProfileWithPictureUri()
    assertThat(profile4.hashCode()).isEqualTo(profile5.hashCode())
    assertThat(profile4.hashCode()).isNotEqualTo(profile3.hashCode())
  }

  @Test
  fun testEquals() {
    val profile1 = createDefaultProfile()
    val profile2 = createDefaultProfile()
    assertThat(profile1).isEqualTo(profile2)
    val profile3 = createMostlyNullsProfile()
    assertThat(profile1).isNotEqualTo(profile3)
    assertThat(profile3).isNotEqualTo(profile2)
  }

  @Test
  fun testJsonSerialization() {
    var profile1 = createDefaultProfile()
    var jsonObject = profile1.toJSONObject()
    jsonObject = checkNotNull(jsonObject)
    var profile2 = Profile(jsonObject)
    assertDefaultObjectGetters(profile2)
    assertThat(profile1).isEqualTo(profile2)

    // Check with nulls
    profile1 = createMostlyNullsProfile()
    jsonObject = profile1.toJSONObject()
    jsonObject = checkNotNull(jsonObject)
    profile2 = Profile(jsonObject)
    assertMostlyNullsObjectGetters(profile2)
    assertThat(profile1).isEqualTo(profile2)

    // Check with picture_uri field
    profile1 = createProfileWithPictureUri()
    jsonObject = profile1.toJSONObject()
    jsonObject = checkNotNull(jsonObject)
    profile2 = Profile(jsonObject)
    assertDefaultObjectGetters(profile2)
    assertThat(profile2.pictureUri).isEqualTo(Uri.parse(PICTURE_URI))
    assertThat(profile1).isEqualTo(profile2)
  }

  @Test
  fun testParcelSerialization() {
    var profile1 = createDefaultProfile()
    var profile2 = FacebookTestUtility.parcelAndUnparcel(profile1)
    assertDefaultObjectGetters(profile2)
    assertThat(profile1).isEqualTo(profile2)

    // Check with nulls
    profile1 = createMostlyNullsProfile()
    profile2 = FacebookTestUtility.parcelAndUnparcel(profile1)
    assertMostlyNullsObjectGetters(profile2)
    assertThat(profile1).isEqualTo(profile2)

    // Check with picture_uri field
    profile1 = createProfileWithPictureUri()
    profile2 = FacebookTestUtility.parcelAndUnparcel(profile1)
    assertDefaultObjectGetters(profile2)
    assertThat(profile2?.pictureUri).isEqualTo(Uri.parse(PICTURE_URI))
    assertThat(profile1).isEqualTo(profile2)
  }

  @Test
  fun testSetCurrentProfile() {
    val profile1 = createDefaultProfile()
    Profile.setCurrentProfile(profile1)
    Profile.setCurrentProfile(null)
    val profileCaptor = argumentCaptor<Profile>()
    verify(mockProfileManager, times(2)).currentProfile = profileCaptor.capture()
    assertThat(profileCaptor.firstValue).isEqualTo(profile1)
    assertThat(profileCaptor.secondValue).isNull()
  }

  @Test
  fun testGetCurrentProfile() {
    val profile1 = createDefaultProfile()
    whenever(mockProfileManager.currentProfile).thenReturn(profile1)
    assertThat(Profile.getCurrentProfile()).isEqualTo(profile1)
  }

  @Test
  fun testGetProfilePictureUri() {
    val testFacebookImageUri = Uri.parse("https://scontent.xx.fbcdn.net/")

    val mockImageRequestCompanion = mock<ImageRequest.Companion>()
    Whitebox.setInternalState(ImageRequest::class.java, "Companion", mockImageRequestCompanion)
    whenever(mockImageRequestCompanion.getProfilePictureUri(any(), any(), any(), any()))
        .thenReturn(testFacebookImageUri)
    val instagramProfile = createProfileWithPictureUri()
    Profile.setCurrentProfile(instagramProfile)

    assertThat(instagramProfile.pictureUri).isEqualTo(Uri.parse(PICTURE_URI))
    assertThat(instagramProfile.getProfilePictureUri(100, 100)).isEqualTo(Uri.parse(PICTURE_URI))

    val facebookProfile = createDefaultProfile()
    assertThat(facebookProfile.pictureUri).isNull()
    assertThat(facebookProfile.getProfilePictureUri(100, 100)).isEqualTo(testFacebookImageUri)
  }

  @Test
  fun `test fetch profile for an expired access token`() {
    val mockAccessToken = mock<AccessToken>()
    whenever(mockAccessToken.isExpired).thenReturn(true)
    whenever(mockAccessTokenCompanion.getCurrentAccessToken()).thenReturn(mockAccessToken)
    whenever(mockAccessTokenCompanion.isCurrentAccessTokenActive()).thenReturn(false)
    Profile.fetchProfileForCurrentAccessToken()
    var getGraphMeRequestWithCacheAsyncIsCalled = false
    PowerMockito.`when`(Utility.getGraphMeRequestWithCacheAsync(any(), any())).thenAnswer {
      getGraphMeRequestWithCacheAsyncIsCalled = true
      Unit
    }
    assertThat(getGraphMeRequestWithCacheAsyncIsCalled).isFalse
  }

  @Test
  fun `test fetch profile for an valid access token`() {
    val mockAccessToken = mock<AccessToken>()
    whenever(mockAccessToken.isExpired).thenReturn(false)
    whenever(mockAccessToken.token).thenReturn(TEST_TOKEN)
    whenever(mockAccessTokenCompanion.getCurrentAccessToken()).thenReturn(mockAccessToken)
    whenever(mockAccessTokenCompanion.isCurrentAccessTokenActive()).thenReturn(true)

    var capturedToken: String? = null
    var capturedCallback: Utility.GraphMeRequestWithCacheCallback? = null
    PowerMockito.`when`(Utility.getGraphMeRequestWithCacheAsync(any(), any())).thenAnswer {
      capturedToken = it.arguments[0] as String
      capturedCallback = it.arguments[1] as Utility.GraphMeRequestWithCacheCallback
      Unit
    }
    Profile.fetchProfileForCurrentAccessToken()

    assertThat(capturedToken).isEqualTo(TEST_TOKEN)
    assertThat(capturedCallback).isNotNull

    val profileCaptor = argumentCaptor<Profile>()

    capturedCallback?.onSuccess(JSONObject(mapOf("id" to TEST_USER_ID)))
    verify(mockProfileManager).currentProfile = profileCaptor.capture()
    val capturedProfile = profileCaptor.lastValue
    assertThat(capturedProfile.id).isEqualTo(TEST_USER_ID)
    assertThat(capturedProfile.name).isEqualTo("")
    assertThat(capturedProfile.firstName).isEqualTo("")
    assertThat(capturedProfile.middleName).isEqualTo("")
    assertThat(capturedProfile.lastName).isEqualTo("")
    assertThat(capturedProfile.pictureUri).isNull()
  }

  companion object {
    const val TEST_TOKEN = "123456789"
    const val TEST_USER_ID = "987654321"
  }
}


/*
 * 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

import android.content.Context
import android.content.Intent
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.test.core.app.ApplicationProvider
import com.facebook.util.common.ProfileTestHelper.assertDefaultObjectGetters
import com.facebook.util.common.ProfileTestHelper.assertMostlyNullsObjectGetters
import com.facebook.util.common.ProfileTestHelper.createDefaultProfile
import com.facebook.util.common.ProfileTestHelper.createMostlyNullsProfile
import com.facebook.util.common.mockLocalBroadcastManager
import org.assertj.core.api.Assertions.assertThat
import org.junit.Assert
import org.junit.Test
import org.mockito.Matchers
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.robolectric.annotation.LooperMode

@LooperMode(LooperMode.Mode.LEGACY)
@PrepareForTest(LocalBroadcastManager::class, FacebookSdk::class)
class ProfileTrackerTest : FacebookPowerMockTestCase() {
  @Test
  fun testStartStopTrackingAndBroadcast() {
    val localBroadcastManager =
        mockLocalBroadcastManager(ApplicationProvider.getApplicationContext())
    PowerMockito.mockStatic(LocalBroadcastManager::class.java)
    whenever(LocalBroadcastManager.getInstance(Matchers.isA(Context::class.java)))
        .thenReturn(localBroadcastManager)
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
    val testProfileTracker = TestProfileTracker()
    // Starts tracking
    assertThat(testProfileTracker.isTracking).isTrue
    testProfileTracker.stopTracking()
    assertThat(testProfileTracker.isTracking).isFalse
    sendBroadcast(localBroadcastManager, null, createDefaultProfile())
    assertThat(testProfileTracker.isCallbackCalled).isFalse
    testProfileTracker.startTracking()
    assertThat(testProfileTracker.isTracking).isTrue
    val profile = createDefaultProfile()
    sendBroadcast(localBroadcastManager, null, profile)
    Assert.assertNull(testProfileTracker.oldProfile)
    Assert.assertEquals(profile, testProfileTracker.currentProfile)
    assertThat(testProfileTracker.isCallbackCalled).isTrue
    val profile1 = createMostlyNullsProfile()
    val profile2 = createDefaultProfile()
    sendBroadcast(localBroadcastManager, profile1, profile2)
    assertMostlyNullsObjectGetters(testProfileTracker.oldProfile)
    assertDefaultObjectGetters(testProfileTracker.currentProfile)
    Assert.assertEquals(profile1, testProfileTracker.oldProfile)
    Assert.assertEquals(profile2, testProfileTracker.currentProfile)
    testProfileTracker.stopTracking()
  }

  class TestProfileTracker : ProfileTracker() {
    var oldProfile: Profile? = null
    var currentProfile: Profile? = null
    var isCallbackCalled = false
    override fun onCurrentProfileChanged(oldProfile: Profile?, currentProfile: Profile?) {
      this.oldProfile = oldProfile
      this.currentProfile = currentProfile
      isCallbackCalled = true
    }
  }

  companion object {
    private fun sendBroadcast(
        localBroadcastManager: LocalBroadcastManager,
        oldProfile: Profile?,
        currentProfile: Profile
    ) {
      val intent = Intent(ProfileManager.ACTION_CURRENT_PROFILE_CHANGED)
      intent.putExtra(ProfileManager.EXTRA_OLD_PROFILE, oldProfile)
      intent.putExtra(ProfileManager.EXTRA_NEW_PROFILE, currentProfile)
      localBroadcastManager.sendBroadcast(intent)
    }
  }
}


/*
 * 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

import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Assert
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 ProgressNoopOutputStreamTest : FacebookPowerMockTestCase() {
  private lateinit var stream: ProgressNoopOutputStream
  @Before
  fun before() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    stream = ProgressNoopOutputStream(null)
    val request = PowerMockito.mock(GraphRequest::class.java)
    stream.setCurrentRequest(request)
  }

  @After
  fun after() {
    stream.close()
  }

  @Test
  fun testSetup() {
    Assert.assertEquals(0, stream.maxProgress)
    assertThat(stream.getProgressMap().isEmpty()).isTrue
  }

  @Test
  fun testWriting() {
    Assert.assertEquals(0, stream.maxProgress)
    stream.write(0)
    Assert.assertEquals(1, stream.maxProgress)
    val buf = ByteArray(8)
    stream.write(buf)
    Assert.assertEquals(9, stream.maxProgress)
    stream.write(buf, 2, 2)
    Assert.assertEquals(11, stream.maxProgress)
    stream.addProgress(16)
    Assert.assertEquals(27, stream.maxProgress)
  }
}


/*
 * 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

import androidx.test.core.app.ApplicationProvider
import java.io.ByteArrayOutputStream
import java.io.OutputStream
import org.junit.After
import org.junit.Assert
import org.junit.Before
import org.junit.Test
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)
class ProgressOutputStreamTest : FacebookPowerMockTestCase() {
  private lateinit var r1: GraphRequest
  private lateinit var r2: GraphRequest
  private lateinit var progressMap: MutableMap<GraphRequest, RequestProgress>
  private lateinit var requests: GraphRequestBatch
  private lateinit var stream: ProgressOutputStream

  companion object {
    private const val MAX_PROGRESS = 10L
  }

  @Before
  fun before() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
    r1 = GraphRequest(null, "4")
    r2 = GraphRequest(null, "4")
    progressMap = hashMapOf(r1 to RequestProgress(null, r1), r2 to RequestProgress(null, r2))
    progressMap[r1]?.addToMax(5)
    progressMap[r2]?.addToMax(5)
    requests = GraphRequestBatch(r1, r2)
    val backing = ByteArrayOutputStream()
    stream = ProgressOutputStream(backing, requests, progressMap, MAX_PROGRESS)
  }

  @After
  fun after() {
    stream.close()
  }

  @Test
  fun `test setup stream`() {
    Assert.assertEquals(0, stream.batchProgress)
    Assert.assertEquals(MAX_PROGRESS, stream.maxProgress)
    for (p in progressMap.values) {
      Assert.assertEquals(0, p.progress)
      Assert.assertEquals(5, p.maxProgress)
    }
  }

  @Test
  fun `test writing to stream`() {
    try {
      Assert.assertEquals(0, stream.batchProgress)
      stream.setCurrentRequest(r1)
      stream.write(0)
      Assert.assertEquals(1, stream.batchProgress)
      val buf = ByteArray(4)
      stream.write(buf)
      Assert.assertEquals(5, stream.batchProgress)
      stream.setCurrentRequest(r2)
      stream.write(buf, 2, 2)
      stream.write(buf, 1, 3)
      Assert.assertEquals(MAX_PROGRESS, stream.batchProgress)
      Assert.assertEquals(stream.maxProgress, stream.batchProgress)
      val progress1 = checkNotNull(progressMap[r1])
      val progress2 = checkNotNull(progressMap[r2])
      Assert.assertEquals(progress1.maxProgress, progress1.progress)
      Assert.assertEquals(progress2.maxProgress, progress2.progress)
    } catch (ex: Exception) {
      Assert.fail(ex.message)
    }
  }

  @Test
  fun `test close does all clean work`() {
    val mockProgress1 = mock<RequestProgress>()
    val mockProgress2 = mock<RequestProgress>()
    val progressMap = hashMapOf(r1 to mockProgress1, r2 to mockProgress2)
    val mockOutputStream = mock<OutputStream>()
    val stream = ProgressOutputStream(mockOutputStream, requests, progressMap, MAX_PROGRESS)
    stream.close()
    verify(mockOutputStream).close()
    verify(mockProgress1).reportProgress()
    verify(mockProgress2).reportProgress()
  }
}


/*
 * 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

import android.os.Handler
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.times
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)
class RequestProgressTest : FacebookPowerMockTestCase() {
  private lateinit var mockRequest: GraphRequest
  private lateinit var mockRequestCallback: GraphRequest.Callback
  private lateinit var mockHandler: Handler
  private var capturedProgress: Long = 0
  private var capturedMax: Long = 0

  @Before
  fun init() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getOnProgressThreshold()).thenReturn(100)
    mockRequest = mock()
    mockRequestCallback =
        object : GraphRequest.OnProgressCallback {
          override fun onProgress(progress: Long, max: Long) {
            capturedProgress = progress
            capturedMax = max
          }
          override fun onCompleted(response: GraphResponse) = Unit
        }
    whenever(mockRequest.callback).thenReturn(mockRequestCallback)
    mockHandler = mock()
    capturedProgress = 0
    capturedMax = 0
  }

  @Test
  fun `test report progress when it reaches the threshold`() {
    val progress = RequestProgress(null, mockRequest)
    progress.addProgress(1)
    Assert.assertEquals(capturedProgress, 0)
    progress.addToMax(10_000)
    progress.addProgress(99)
    Assert.assertEquals(capturedProgress, 100)
    progress.addProgress(100)
    Assert.assertEquals(capturedProgress, 200)
  }

  @Test
  fun `test report progress when it reaches max progress`() {
    val progress = RequestProgress(null, mockRequest)
    progress.addToMax(15)
    progress.addProgress(5)
    Assert.assertEquals(capturedProgress, 0)
    progress.addProgress(10)
    Assert.assertEquals(capturedProgress, 15)
    Assert.assertEquals(capturedMax, 15)
  }

  @Test
  fun `test report progress with callback handler`() {
    val progress = RequestProgress(mockHandler, mockRequest)
    progress.addToMax(15)
    progress.addProgress(20)
    Assert.assertEquals(capturedProgress, 0)
    verify(mockHandler).post(any())
  }

  @Test
  fun `test not to report if the progress is not added`() {
    val progress = RequestProgress(mockHandler, mockRequest)
    progress.addToMax(100)
    progress.addProgress(20)
    progress.reportProgress()
    progress.reportProgress()
    verify(mockHandler, times(1)).post(any())
  }
}


/*
 * 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

import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.os.Bundle
import com.facebook.UserSettingsManager.getAdvertiserIDCollectionEnabled
import com.facebook.UserSettingsManager.getAutoInitEnabled
import com.facebook.UserSettingsManager.getAutoLogAppEventsEnabled
import com.facebook.UserSettingsManager.getCodelessSetupEnabled
import com.facebook.UserSettingsManager.setAdvertiserIDCollectionEnabled
import com.facebook.UserSettingsManager.setAutoInitEnabled
import com.facebook.UserSettingsManager.setAutoLogAppEventsEnabled
import com.facebook.appevents.InternalAppEventsLogger
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
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(UserSettingsManager::class, FacebookSdk::class)
class UserSettingsManagerTest : FacebookPowerMockTestCase() {
  private lateinit var mockApplicationContext: Context
  private lateinit var mockPackageManager: PackageManager
  private lateinit var mockApplicationInfo: ApplicationInfo
  private lateinit var mockLogger: InternalAppEventsLogger
  @Before
  override fun setup() {
    super.setup()
    FacebookSdk.setApplicationId("123456789")
    mockPackageManager = mock()
    mockApplicationContext = mock()
    mockApplicationInfo = mock()
    whenever(mockApplicationContext.packageManager).thenReturn(mockPackageManager)
    whenever(mockApplicationContext.applicationContext).thenReturn(mockApplicationContext)
    whenever(mockApplicationContext.packageName).thenReturn("com.facebook.test")
    whenever(mockPackageManager.getApplicationInfo(any(), any())).thenReturn(mockApplicationInfo)
    whenever(mockApplicationContext.getSharedPreferences(any<String>(), any()))
        .thenReturn(MockSharedPreference())
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationId()).thenReturn("123456789")
    whenever(FacebookSdk.getApplicationContext()).thenReturn(mockApplicationContext)
    whenever(FacebookSdk.getAdvertiserIDCollectionEnabled()).thenCallRealMethod()
    whenever(FacebookSdk.setAdvertiserIDCollectionEnabled(any())).thenCallRealMethod()
    whenever(FacebookSdk.getAutoLogAppEventsEnabled()).thenCallRealMethod()
    whenever(FacebookSdk.setAutoLogAppEventsEnabled(any())).thenCallRealMethod()
    whenever(FacebookSdk.getAutoInitEnabled()).thenCallRealMethod()
    whenever(FacebookSdk.setAutoInitEnabled(any())).thenCallRealMethod()
    whenever(FacebookSdk.getCodelessSetupEnabled()).thenCallRealMethod()
    whenever(FacebookSdk.getExecutor()).thenReturn(mock())
    mockLogger = mock()
    PowerMockito.whenNew(InternalAppEventsLogger::class.java)
        .withAnyArguments()
        .thenReturn(mockLogger)
  }

  @Test
  fun testAutoInitEnabled() {
    PowerMockito.mockStatic(UserSettingsManager::class.java)
    var getAutoInitEnabledTimes = 0
    var setAutoInitEnabledValue: Boolean? = null
    whenever(getAutoInitEnabled()).thenAnswer {
      getAutoInitEnabledTimes++
      true
    }
    whenever(setAutoInitEnabled(any())).thenAnswer {
      setAutoInitEnabledValue = it.arguments[0] as Boolean
      Unit
    }

    val enable = FacebookSdk.getAutoInitEnabled()
    assertThat(enable).isTrue
    assertThat(getAutoInitEnabledTimes).isEqualTo(1)

    FacebookSdk.setAutoInitEnabled(false)
    assertThat(setAutoInitEnabledValue).isFalse
  }

  @Test
  fun testAutoLogEnabled() {
    PowerMockito.mockStatic(UserSettingsManager::class.java)
    var getAutoLogAppEventsEnabledTimes = 0
    var setAutoLogAppEventsEnabledbledValue: Boolean? = null
    whenever(getAutoLogAppEventsEnabled()).thenAnswer {
      getAutoLogAppEventsEnabledTimes++
      true
    }
    whenever(setAutoLogAppEventsEnabled(any())).thenAnswer {
      setAutoLogAppEventsEnabledbledValue = it.arguments[0] as Boolean
      Unit
    }

    val enable = FacebookSdk.getAutoLogAppEventsEnabled()
    assertThat(enable).isTrue
    assertThat(getAutoLogAppEventsEnabledTimes).isEqualTo(1)
    FacebookSdk.setAutoLogAppEventsEnabled(false)
    assertThat(setAutoLogAppEventsEnabledbledValue).isFalse
  }

  @Test
  fun testAdvertiserIDCollectionEnabled() {
    PowerMockito.mockStatic(UserSettingsManager::class.java)
    var getAdvertiserIDCollectionEnabledTimes = 0
    var setAdvertiserIDCollectionEnabledValue: Boolean? = null
    whenever(getAdvertiserIDCollectionEnabled()).thenAnswer {
      getAdvertiserIDCollectionEnabledTimes++
      true
    }
    whenever(setAdvertiserIDCollectionEnabled(any())).thenAnswer {
      setAdvertiserIDCollectionEnabledValue = it.arguments[0] as Boolean
      Unit
    }

    val enable = FacebookSdk.getAdvertiserIDCollectionEnabled()
    assertThat(enable).isTrue
    assertThat(getAdvertiserIDCollectionEnabledTimes).isEqualTo(1)
    FacebookSdk.setAdvertiserIDCollectionEnabled(false)
    assertThat(setAdvertiserIDCollectionEnabledValue).isFalse
  }

  @Test
  fun testCodelessSetupEnabled() {
    PowerMockito.mockStatic(UserSettingsManager::class.java)
    var getCodelessSetupEnabledTimes = 0
    whenever(getCodelessSetupEnabled()).thenAnswer {
      getCodelessSetupEnabledTimes++
      true
    }

    val enable = FacebookSdk.getCodelessSetupEnabled()
    assertThat(enable).isTrue
    assertThat(getCodelessSetupEnabledTimes).isEqualTo(1)
  }

  @Test
  fun testLogIfSDKSettingsChanged() {
    setAdvertiserIDCollectionEnabled(false)
    verify(mockLogger).logChangedSettingsEvent(any())
  }

  @Test
  fun `test logIfAutoAppLinkEnabled`() {
    val metaData = Bundle()
    metaData.putBoolean("com.facebook.sdk.AutoAppLinkEnabled", true)
    mockApplicationInfo.metaData = metaData
    UserSettingsManager.logIfAutoAppLinkEnabled()
    verify(mockLogger).logEvent(eq("fb_auto_applink"), any())
  }

  @Test
  fun `test get and set monitorEnabled`() {
    UserSettingsManager.setMonitorEnabled(true)
    assertThat(UserSettingsManager.getMonitorEnabled()).isTrue
    UserSettingsManager.setMonitorEnabled(false)
    assertThat(UserSettingsManager.getMonitorEnabled()).isFalse
  }
}