From b9f69b172dabd7f5af5b25700c884e302df352c5 Mon Sep 17 00:00:00 2001 From: Karl Dimla Date: Tue, 19 Dec 2023 13:34:23 +0100 Subject: [PATCH] Address comments --- .../duckduckgo/common/test/api/FakeChain.kt | 7 +- .../impl/configuration/WgServerApi.kt | 67 ++++---- .../configuration/WgVpnControllerService.kt | 3 +- .../impl/store/NetworkProtectionRepository.kt | 10 +- .../FakeWgVpnControllerService.kt | 7 +- .../impl/configuration/RealWgServerApiTest.kt | 49 ------ .../fakes/FakeNetworkProtectionRepository.kt | 8 +- .../build.gradle | 3 +- .../subscription/NetpSubscriptionChecker.kt | 2 +- .../NetpSubscriptionRequestInterceptor.kt | 16 +- .../NetAccessRevokedNotificationScheduler.kt | 2 +- .../SubsNetpDisabledNotificationBuilder.kt | 2 +- .../NetpSubscriptionRequestInterceptorTest.kt | 152 ++++++++++++++++++ 13 files changed, 212 insertions(+), 116 deletions(-) create mode 100644 network-protection/network-protection-subscription-internal/src/test/java/com/duckduckgo/networkprotection/subscription/configuration/NetpSubscriptionRequestInterceptorTest.kt diff --git a/common/common-test/src/main/java/com/duckduckgo/common/test/api/FakeChain.kt b/common/common-test/src/main/java/com/duckduckgo/common/test/api/FakeChain.kt index 70ac0f20eec4..98c36efa960a 100644 --- a/common/common-test/src/main/java/com/duckduckgo/common/test/api/FakeChain.kt +++ b/common/common-test/src/main/java/com/duckduckgo/common/test/api/FakeChain.kt @@ -19,7 +19,10 @@ package com.duckduckgo.common.test.api import java.util.concurrent.TimeUnit import okhttp3.* -class FakeChain(private val url: String) : Interceptor.Chain { +class FakeChain( + private val url: String, + private val expectedResponseCode: Int? = null, +) : Interceptor.Chain { override fun call(): Call { TODO("Not yet implemented") } @@ -37,7 +40,7 @@ class FakeChain(private val url: String) : Interceptor.Chain { .request(request) .headers(request.headers) // echo the headers .protocol(Protocol.HTTP_2) - .code(200) + .code(expectedResponseCode ?: 200) .message("") .build() } diff --git a/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/configuration/WgServerApi.kt b/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/configuration/WgServerApi.kt index 314784da16bc..b5f1245af34e 100644 --- a/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/configuration/WgServerApi.kt +++ b/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/configuration/WgServerApi.kt @@ -21,7 +21,6 @@ import com.duckduckgo.di.scopes.VpnScope import com.duckduckgo.networkprotection.impl.configuration.WgServerApi.WgServerData import com.duckduckgo.networkprotection.impl.di.UnprotectedVpnControllerService import com.duckduckgo.networkprotection.impl.settings.geoswitching.NetpEgressServersProvider -import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository import com.squareup.anvil.annotations.ContributesBinding import javax.inject.Inject import logcat.logcat @@ -45,54 +44,44 @@ class RealWgServerApi @Inject constructor( @UnprotectedVpnControllerService private val wgVpnControllerService: WgVpnControllerService, private val serverDebugProvider: WgServerDebugProvider, private val netNetpEgressServersProvider: NetpEgressServersProvider, - private val netpRepository: NetworkProtectionRepository, ) : WgServerApi { override suspend fun registerPublicKey(publicKey: String): WgServerData? { - val registerKeyBody = try { - // This bit of code gets all possible egress servers which should be order by proximity, caches them for internal builds and then - // returns the closest one or null if list is empty - val selectedServer = wgVpnControllerService.getServers().map { it.server } - .also { fetchedServers -> - logcat { "Fetched servers ${fetchedServers.map { it.name }}" } - serverDebugProvider.cacheServers(fetchedServers) - } - .map { it.name } - .firstOrNull { serverName -> - serverDebugProvider.getSelectedServerName()?.let { userSelectedServer -> - serverName == userSelectedServer - } ?: false - } - - logcat { "Register key in $selectedServer" } - val userPreferredLocation = netNetpEgressServersProvider.updateServerLocationsAndReturnPreferred() - if (selectedServer != null) { - RegisterKeyBody(publicKey = publicKey, server = selectedServer) - } else if (userPreferredLocation != null) { - if (userPreferredLocation.cityName != null) { - RegisterKeyBody(publicKey = publicKey, country = userPreferredLocation.countryCode, city = userPreferredLocation.cityName) - } else { - RegisterKeyBody(publicKey = publicKey, country = userPreferredLocation.countryCode) - } + // This bit of code gets all possible egress servers which should be order by proximity, caches them for internal builds and then + // returns the closest one or null if list is empty + val selectedServer = wgVpnControllerService.getServers().map { it.server } + .also { fetchedServers -> + logcat { "Fetched servers ${fetchedServers.map { it.name }}" } + serverDebugProvider.cacheServers(fetchedServers) + } + .map { it.name } + .firstOrNull { serverName -> + serverDebugProvider.getSelectedServerName()?.let { userSelectedServer -> + serverName == userSelectedServer + } ?: false + } + + logcat { "Register key in $selectedServer" } + val userPreferredLocation = netNetpEgressServersProvider.updateServerLocationsAndReturnPreferred() + val registerKeyBody = if (selectedServer != null) { + RegisterKeyBody(publicKey = publicKey, server = selectedServer) + } else if (userPreferredLocation != null) { + if (userPreferredLocation.cityName != null) { + RegisterKeyBody(publicKey = publicKey, country = userPreferredLocation.countryCode, city = userPreferredLocation.cityName) } else { - RegisterKeyBody(publicKey = publicKey, server = "*") + RegisterKeyBody(publicKey = publicKey, country = userPreferredLocation.countryCode) } - } catch (e: Exception) { + } else { RegisterKeyBody(publicKey = publicKey, server = "*") } return wgVpnControllerService.registerKey(registerKeyBody) .run { - if (isSuccessful) { - logcat { "Register key returned ${this.body()?.map { it.server.name }}" } - val server = this.body()?.firstOrNull()?.toWgServerData() - logcat { "Selected Egress server is $server" } - netpRepository.disabledDueToAccessRevoked = false - server - } else { - netpRepository.disabledDueToAccessRevoked = this.code() == 403 - null - } + logcat { "Register key in $selectedServer" } + logcat { "Register key returned ${this.map { it.server.name }}" } + val server = this.firstOrNull()?.toWgServerData() + logcat { "Selected Egress server is $server" } + server } } diff --git a/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/configuration/WgVpnControllerService.kt b/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/configuration/WgVpnControllerService.kt index 037b0b9daf56..078ad411d97c 100644 --- a/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/configuration/WgVpnControllerService.kt +++ b/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/configuration/WgVpnControllerService.kt @@ -37,7 +37,6 @@ import javax.net.ssl.TrustManagerFactory import javax.net.ssl.X509TrustManager import okhttp3.OkHttpClient import org.conscrypt.Conscrypt -import retrofit2.Response import retrofit2.Retrofit import retrofit2.http.Body import retrofit2.http.GET @@ -121,7 +120,7 @@ interface WgVpnControllerService { @POST("$NETP_ENVIRONMENT_URL/register") suspend fun registerKey( @Body registerKeyBody: RegisterKeyBody, - ): Response> + ): List @GET("$NETP_ENVIRONMENT_URL/locations") suspend fun getEligibleLocations(): List diff --git a/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/store/NetworkProtectionRepository.kt b/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/store/NetworkProtectionRepository.kt index 792e4f05321d..0afe4b0fb998 100644 --- a/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/store/NetworkProtectionRepository.kt +++ b/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/store/NetworkProtectionRepository.kt @@ -36,7 +36,7 @@ interface NetworkProtectionRepository { var enabledTimeInMillis: Long var serverDetails: ServerDetails? var clientInterface: ClientInterface? - var disabledDueToAccessRevoked: Boolean + var vpnAccessRevoked: Boolean enum class ReconnectStatus { NotReconnecting, @@ -145,10 +145,10 @@ class RealNetworkProtectionRepository @Inject constructor( } } - override var disabledDueToAccessRevoked: Boolean - get() = networkProtectionPrefs.getBoolean(KEY_DISABLED_DUE_TO_ACCESS_REVOKED, false) + override var vpnAccessRevoked: Boolean + get() = networkProtectionPrefs.getBoolean(KEY_VPN_ACCESS_REVOKED, false) set(value) { - networkProtectionPrefs.putBoolean(KEY_DISABLED_DUE_TO_ACCESS_REVOKED, value) + networkProtectionPrefs.putBoolean(KEY_VPN_ACCESS_REVOKED, value) } companion object { @@ -160,6 +160,6 @@ class RealNetworkProtectionRepository @Inject constructor( private const val KEY_WG_SERVER_ENABLE_TIME = "wg_server_enable_time" private const val KEY_WG_RECONNECT_STATUS = "wg_reconnect_status" private const val KEY_WG_CLIENT_IFACE_TUNNEL_IP = "wg_client_iface_tunnel_ip" - private const val KEY_DISABLED_DUE_TO_ACCESS_REVOKED = "key_disabled_due_to_access_revoked" + private const val KEY_VPN_ACCESS_REVOKED = "key_vpn_access_revoked" } } diff --git a/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/configuration/FakeWgVpnControllerService.kt b/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/configuration/FakeWgVpnControllerService.kt index acff256b07cc..67353f6d9e83 100644 --- a/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/configuration/FakeWgVpnControllerService.kt +++ b/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/configuration/FakeWgVpnControllerService.kt @@ -19,7 +19,6 @@ package com.duckduckgo.networkprotection.impl.configuration import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.squareup.moshi.Types -import retrofit2.Response class FakeWgVpnControllerService : WgVpnControllerService { private val moshi: Moshi = Moshi.Builder().build() @@ -44,7 +43,7 @@ class FakeWgVpnControllerService : WgVpnControllerService { return serverLocations } - override suspend fun registerKey(registerKeyBody: RegisterKeyBody): Response> { + override suspend fun registerKey(registerKeyBody: RegisterKeyBody): List { return servers.filter { return@filter if (registerKeyBody.server != "*") { it.server.name == registerKeyBody.server @@ -57,9 +56,7 @@ class FakeWgVpnControllerService : WgVpnControllerService { } else { true } - }.map { - it.toEligibleServerInfo() - }.run { Response.success(this) } + }.map { it.toEligibleServerInfo() } } private fun RegisteredServerInfo.toEligibleServerInfo(): EligibleServerInfo { diff --git a/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/configuration/RealWgServerApiTest.kt b/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/configuration/RealWgServerApiTest.kt index 9a10ba0f0ed6..bbbb8294dc41 100644 --- a/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/configuration/RealWgServerApiTest.kt +++ b/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/configuration/RealWgServerApiTest.kt @@ -17,22 +17,16 @@ package com.duckduckgo.networkprotection.impl.configuration import com.duckduckgo.networkprotection.impl.configuration.WgServerApi.WgServerData -import com.duckduckgo.networkprotection.impl.fakes.FakeNetworkProtectionRepository import com.duckduckgo.networkprotection.impl.settings.geoswitching.NetpEgressServersProvider import com.duckduckgo.networkprotection.impl.settings.geoswitching.NetpEgressServersProvider.PreferredLocation -import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository import kotlinx.coroutines.test.runTest -import okhttp3.ResponseBody.Companion.toResponseBody import org.junit.Assert.* import org.junit.Before import org.junit.Test import org.mockito.Mock -import org.mockito.Mockito import org.mockito.MockitoAnnotations -import org.mockito.kotlin.any import org.mockito.kotlin.verify import org.mockito.kotlin.whenever -import retrofit2.Response class RealWgServerApiTest { private val wgVpnControllerService = FakeWgVpnControllerService() @@ -41,7 +35,6 @@ class RealWgServerApiTest { private lateinit var internalWgServerDebugProvider: FakeWgServerDebugProvider private lateinit var productionApi: RealWgServerApi private lateinit var internalApi: RealWgServerApi - private lateinit var networkProtectionRepository: NetworkProtectionRepository @Mock private lateinit var netpEgressServersProvider: NetpEgressServersProvider @@ -52,19 +45,16 @@ class RealWgServerApiTest { productionWgServerDebugProvider = DefaultWgServerDebugProvider() internalWgServerDebugProvider = FakeWgServerDebugProvider() - networkProtectionRepository = FakeNetworkProtectionRepository() internalApi = RealWgServerApi( wgVpnControllerService, internalWgServerDebugProvider, netpEgressServersProvider, - networkProtectionRepository, ) productionApi = RealWgServerApi( wgVpnControllerService, productionWgServerDebugProvider, netpEgressServersProvider, - networkProtectionRepository, ) } @@ -224,45 +214,6 @@ class RealWgServerApiTest { internalApi.registerPublicKey("testpublickey"), ) } - - @Test - fun whenServerThrowExceptionAndRegisterReturns403ThenSetDisabledDueToInvalidSubscription() = runTest { - val mockService = Mockito.mock(WgVpnControllerService::class.java) - val testee = RealWgServerApi( - mockService, - internalWgServerDebugProvider, - netpEgressServersProvider, - networkProtectionRepository, - ) - whenever(mockService.getServers()).thenThrow(RuntimeException()) - whenever(mockService.registerKey(any())).thenReturn( - Response.error(403, "Test".toResponseBody()), - ) - - testee.registerPublicKey("test") - - verify(mockService).registerKey(RegisterKeyBody(publicKey = "test", server = "*")) - assertTrue(networkProtectionRepository.disabledDueToAccessRevoked) - } - - @Test - fun whenServerThrowExceptionAndRegisterReturns400ThenDoNotSetDisabledDueToInvalidSubscription() = runTest { - val mockService = Mockito.mock(WgVpnControllerService::class.java) - val testee = RealWgServerApi( - mockService, - internalWgServerDebugProvider, - netpEgressServersProvider, - networkProtectionRepository, - ) - whenever(mockService.getServers()).thenThrow(RuntimeException()) - whenever(mockService.registerKey(any())).thenReturn( - Response.error(400, "Test".toResponseBody()), - ) - - testee.registerPublicKey("test") - - assertFalse(networkProtectionRepository.disabledDueToAccessRevoked) - } } private class FakeWgServerDebugProvider() : WgServerDebugProvider { diff --git a/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/fakes/FakeNetworkProtectionRepository.kt b/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/fakes/FakeNetworkProtectionRepository.kt index 3036849c6f2a..367166dad767 100644 --- a/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/fakes/FakeNetworkProtectionRepository.kt +++ b/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/fakes/FakeNetworkProtectionRepository.kt @@ -24,7 +24,7 @@ import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository.S class FakeNetworkProtectionRepository : NetworkProtectionRepository { private var _reconnectStatus: ReconnectStatus? = null private var _serverDetails: ServerDetails? = null - private var _disabledDueToInvalidSubscription: Boolean = false + private var _vpnAccessRevoked: Boolean = false override var reconnectStatus: ReconnectStatus get() = _reconnectStatus ?: NotReconnecting @@ -50,9 +50,9 @@ class FakeNetworkProtectionRepository : NetworkProtectionRepository { get() = null set(_) {} - override var disabledDueToAccessRevoked: Boolean - get() = _disabledDueToInvalidSubscription + override var vpnAccessRevoked: Boolean + get() = _vpnAccessRevoked set(value) { - _disabledDueToInvalidSubscription = value + _vpnAccessRevoked = value } } diff --git a/network-protection/network-protection-subscription-internal/build.gradle b/network-protection/network-protection-subscription-internal/build.gradle index f6a282425f9e..1f0e20fb43c2 100644 --- a/network-protection/network-protection-subscription-internal/build.gradle +++ b/network-protection/network-protection-subscription-internal/build.gradle @@ -50,7 +50,7 @@ dependencies { implementation "com.squareup.logcat:logcat:_" // Testing dependencies - testImplementation project(path: ':common-test') + testImplementation project(':common-test') testImplementation "org.mockito.kotlin:mockito-kotlin:_" testImplementation Testing.junit4 testImplementation AndroidX.archCore.testing @@ -59,7 +59,6 @@ dependencies { testImplementation "androidx.test:runner:_" testImplementation Testing.robolectric testImplementation 'app.cash.turbine:turbine:_' - testImplementation project(path: ':common-test') testImplementation (KotlinX.coroutines.test) { // https://github.com/Kotlin/kotlinx.coroutines/issues/2023 // conflicts with mockito due to direct inclusion of byte buddy diff --git a/network-protection/network-protection-subscription-internal/src/main/java/com/duckduckgo/networkprotection/subscription/NetpSubscriptionChecker.kt b/network-protection/network-protection-subscription-internal/src/main/java/com/duckduckgo/networkprotection/subscription/NetpSubscriptionChecker.kt index 729f7839b552..7fec71c5d5e6 100644 --- a/network-protection/network-protection-subscription-internal/src/main/java/com/duckduckgo/networkprotection/subscription/NetpSubscriptionChecker.kt +++ b/network-protection/network-protection-subscription-internal/src/main/java/com/duckduckgo/networkprotection/subscription/NetpSubscriptionChecker.kt @@ -109,7 +109,7 @@ class NetpSubscriptionCheckWorker( logcat { "Sub check: checking entitlement" } if (!netpSubscriptionManager.hasValidEntitlement() && networkProtectionState.isEnabled()) { logcat { "Sub check: disabling" } - netpRepository.disabledDueToAccessRevoked = true + netpRepository.vpnAccessRevoked = true networkProtectionState.stop() } else if (!networkProtectionState.isEnabled()) { logcat { "Sub check: cancelling scheduled checker" } diff --git a/network-protection/network-protection-subscription-internal/src/main/java/com/duckduckgo/networkprotection/subscription/configuration/NetpSubscriptionRequestInterceptor.kt b/network-protection/network-protection-subscription-internal/src/main/java/com/duckduckgo/networkprotection/subscription/configuration/NetpSubscriptionRequestInterceptor.kt index 41cbad51a69d..8c3f9bbe2bb5 100644 --- a/network-protection/network-protection-subscription-internal/src/main/java/com/duckduckgo/networkprotection/subscription/configuration/NetpSubscriptionRequestInterceptor.kt +++ b/network-protection/network-protection-subscription-internal/src/main/java/com/duckduckgo/networkprotection/subscription/configuration/NetpSubscriptionRequestInterceptor.kt @@ -21,6 +21,7 @@ import com.duckduckgo.appbuildconfig.api.isInternalBuild import com.duckduckgo.di.scopes.AppScope import com.duckduckgo.networkprotection.impl.BuildConfig import com.duckduckgo.networkprotection.impl.configuration.NetpRequestInterceptor +import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository import com.duckduckgo.networkprotection.subscription.NetpSubscriptionManager import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding.Priority.HIGHEST @@ -37,12 +38,13 @@ import okhttp3.Response class NetpSubscriptionRequestInterceptor @Inject constructor( private val appBuildConfig: AppBuildConfig, private val netpSubscriptionManager: NetpSubscriptionManager, + private val networkProtectionRepository: NetworkProtectionRepository, ) : NetpRequestInterceptor { override fun intercept(chain: Chain): Response { val url = chain.request().url val newRequest = chain.request().newBuilder() - if (ENDPOINTS_PATTERN_MATCHER.any { url.toString().endsWith(it) }) { + return if (ENDPOINTS_PATTERN_MATCHER.any { url.toString().endsWith(it) }) { logcat { "Adding Authorization Bearer token to request $url" } newRequest.addHeader( name = "Authorization", @@ -56,11 +58,15 @@ class NetpSubscriptionRequestInterceptor @Inject constructor( value = BuildConfig.NETP_DEBUG_SERVER_TOKEN, ) } - } - return chain.proceed( - newRequest.build().also { logcat { "headers: ${it.headers}" } }, - ) + chain.proceed( + newRequest.build().also { logcat { "headers: ${it.headers}" } }, + ).also { + networkProtectionRepository.vpnAccessRevoked = it.code == 403 + } + } else { + chain.proceed(newRequest.build()) + } } companion object { diff --git a/network-protection/network-protection-subscription-internal/src/main/java/com/duckduckgo/networkprotection/subscription/notification/NetAccessRevokedNotificationScheduler.kt b/network-protection/network-protection-subscription-internal/src/main/java/com/duckduckgo/networkprotection/subscription/notification/NetAccessRevokedNotificationScheduler.kt index 403774a7f797..7ac063f5424d 100644 --- a/network-protection/network-protection-subscription-internal/src/main/java/com/duckduckgo/networkprotection/subscription/notification/NetAccessRevokedNotificationScheduler.kt +++ b/network-protection/network-protection-subscription-internal/src/main/java/com/duckduckgo/networkprotection/subscription/notification/NetAccessRevokedNotificationScheduler.kt @@ -48,7 +48,7 @@ class NetAccessRevokedNotificationScheduler @Inject constructor( override fun onVpnStartFailed(coroutineScope: CoroutineScope) { super.onVpnStartFailed(coroutineScope) - if (networkProtectionRepository.disabledDueToAccessRevoked) { + if (networkProtectionRepository.vpnAccessRevoked) { notificationManager.notify( NetPDisabledNotificationScheduler.NETP_REMINDER_NOTIFICATION_ID, buildVpnAccessRevokedNotification(context), diff --git a/network-protection/network-protection-subscription-internal/src/main/java/com/duckduckgo/networkprotection/subscription/notification/SubsNetpDisabledNotificationBuilder.kt b/network-protection/network-protection-subscription-internal/src/main/java/com/duckduckgo/networkprotection/subscription/notification/SubsNetpDisabledNotificationBuilder.kt index b36f2dffebf9..9b4ed9d35e85 100644 --- a/network-protection/network-protection-subscription-internal/src/main/java/com/duckduckgo/networkprotection/subscription/notification/SubsNetpDisabledNotificationBuilder.kt +++ b/network-protection/network-protection-subscription-internal/src/main/java/com/duckduckgo/networkprotection/subscription/notification/SubsNetpDisabledNotificationBuilder.kt @@ -36,7 +36,7 @@ class SubsNetpDisabledNotificationBuilder @Inject constructor( ) : NetPDisabledNotificationBuilder { override fun buildDisabledNotification(context: Context): Notification { - return if (netpRepository.disabledDueToAccessRevoked) { + return if (netpRepository.vpnAccessRevoked) { buildVpnAccessRevokedNotification(context) } else { realNetPDisabledNotificationBuilder.buildDisabledNotification(context) diff --git a/network-protection/network-protection-subscription-internal/src/test/java/com/duckduckgo/networkprotection/subscription/configuration/NetpSubscriptionRequestInterceptorTest.kt b/network-protection/network-protection-subscription-internal/src/test/java/com/duckduckgo/networkprotection/subscription/configuration/NetpSubscriptionRequestInterceptorTest.kt new file mode 100644 index 000000000000..c857564845c2 --- /dev/null +++ b/network-protection/network-protection-subscription-internal/src/test/java/com/duckduckgo/networkprotection/subscription/configuration/NetpSubscriptionRequestInterceptorTest.kt @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2023 DuckDuckGo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.duckduckgo.networkprotection.subscription.configuration + +import com.duckduckgo.appbuildconfig.api.AppBuildConfig +import com.duckduckgo.appbuildconfig.api.BuildFlavor.INTERNAL +import com.duckduckgo.appbuildconfig.api.BuildFlavor.PLAY +import com.duckduckgo.common.test.api.FakeChain +import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository +import com.duckduckgo.networkprotection.subscription.NetpSubscriptionManager +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoInteractions +import org.mockito.kotlin.whenever + +class NetpSubscriptionRequestInterceptorTest { + private lateinit var testee: NetpSubscriptionRequestInterceptor + + @Mock + private lateinit var appBuildConfig: AppBuildConfig + + @Mock + private lateinit var subscriptionManager: NetpSubscriptionManager + + @Mock + private lateinit var networkProtectionRepository: NetworkProtectionRepository + + @Before + fun setUp() { + MockitoAnnotations.openMocks(this) + testee = NetpSubscriptionRequestInterceptor( + appBuildConfig, + subscriptionManager, + networkProtectionRepository, + ) + } + + @Test + fun whenUrlIsServersAndFlavorIsPlayThenOnlyAddTokenToHeader() = runTest { + val fakeChain = FakeChain(url = "https://controller.netp.duckduckgo.com/servers") + whenever(appBuildConfig.flavor).thenReturn(PLAY) + whenever(subscriptionManager.getToken()).thenReturn("token123") + + testee.intercept(fakeChain).run { + assertEquals("bearer ddg:token123", headers["Authorization"]) + assertFalse(headers.names().contains("NetP-Debug-Code")) + } + } + + @Test + fun whenUrlIsLocationsAndFlavorIsPlayThenOnlyAddTokenToHeader() = runTest { + val fakeChain = FakeChain(url = "https://controller.netp.duckduckgo.com/locations") + whenever(appBuildConfig.flavor).thenReturn(PLAY) + whenever(subscriptionManager.getToken()).thenReturn("token123") + + testee.intercept(fakeChain).run { + assertEquals("bearer ddg:token123", headers["Authorization"]) + assertFalse(headers.names().contains("NetP-Debug-Code")) + } + } + + @Test + fun whenUrlIsRegisterAndFlavorIsPlayThenOnlyAddTokenToHeader() = runTest { + val fakeChain = FakeChain(url = "https://staging1.netp.duckduckgo.com/register") + whenever(appBuildConfig.flavor).thenReturn(PLAY) + whenever(subscriptionManager.getToken()).thenReturn("token123") + + testee.intercept(fakeChain).run { + assertEquals("bearer ddg:token123", headers["Authorization"]) + assertFalse(headers.names().contains("NetP-Debug-Code")) + } + } + + @Test + fun whenUrlIsNotNetPAndFlavorIsInternalThenDoNothingWithHeaders() = runTest { + val fakeChain = FakeChain(url = "https://improving.duckduckgo.com/t/m_netp_ev_enabled_android_phone?atb=v336-7&appVersion=5.131.0&test=1") + + testee.intercept(fakeChain).run { + assertNull(headers["Authorization"]) + assertFalse(headers.names().contains("NetP-Debug-Code")) + } + } + + @Test + fun whenUrlIsNetPAndFlavorIsInternalThenAddTokenAndDebugCodeToHeader() = runTest { + val fakeChain = FakeChain(url = "https://controller.netp.duckduckgo.com/servers") + whenever(appBuildConfig.flavor).thenReturn(INTERNAL) + whenever(subscriptionManager.getToken()).thenReturn("token123") + + testee.intercept(fakeChain).run { + assertEquals("bearer ddg:token123", headers["Authorization"]) + assertTrue(headers.names().contains("NetP-Debug-Code")) + } + } + + @Test + fun whenUrlIsNotNetpThenDoNothingWithVPNAccessRevoked() = runTest { + val fakeChain = FakeChain(url = "https://improving.duckduckgo.com/t/m_netp_ev_enabled_android_phone?atb=v336-7&appVersion=5.131.0&test=1") + + testee.intercept(fakeChain) + + verifyNoInteractions(networkProtectionRepository) + } + + @Test + fun whenUrlIsNetPAndResponseCodeIs200ThenSetVPNAccessRevokedFalse() = runTest { + val fakeChain = FakeChain(url = "https://controller.netp.duckduckgo.com/servers") + whenever(appBuildConfig.flavor).thenReturn(INTERNAL) + whenever(subscriptionManager.getToken()).thenReturn("token123") + + testee.intercept(fakeChain) + + verify(networkProtectionRepository).vpnAccessRevoked = false + } + + @Test + fun whenUrlIsNetPAndResponseCodeIs403ThenSetVPNAccessRevokedFalse() = runTest { + val url = "https://controller.netp.duckduckgo.com/servers" + val fakeChain = FakeChain( + url = url, + expectedResponseCode = 403, + ) + whenever(appBuildConfig.flavor).thenReturn(INTERNAL) + whenever(subscriptionManager.getToken()).thenReturn("token123") + + testee.intercept(fakeChain) + + verify(networkProtectionRepository).vpnAccessRevoked = true + } +}