ConnectivityServiceTest.kt 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /*
  2. * Nextcloud Android client application
  3. *
  4. * @author Chris Narkiewicz
  5. * Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Affero General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Affero General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. package com.nextcloud.client.network
  21. import android.net.ConnectivityManager
  22. import android.net.NetworkInfo
  23. import com.nextcloud.client.account.Server
  24. import com.nextcloud.client.account.User
  25. import com.nextcloud.client.account.UserAccountManager
  26. import com.nextcloud.client.logger.Logger
  27. import com.nhaarman.mockitokotlin2.any
  28. import com.nhaarman.mockitokotlin2.mock
  29. import com.nhaarman.mockitokotlin2.never
  30. import com.nhaarman.mockitokotlin2.verify
  31. import com.nhaarman.mockitokotlin2.whenever
  32. import com.owncloud.android.lib.resources.status.OwnCloudVersion
  33. import org.apache.commons.httpclient.HttpClient
  34. import org.apache.commons.httpclient.HttpStatus
  35. import org.apache.commons.httpclient.methods.GetMethod
  36. import org.junit.Assert.assertFalse
  37. import org.junit.Assert.assertTrue
  38. import org.junit.Before
  39. import org.junit.Test
  40. import org.junit.runner.RunWith
  41. import org.junit.runners.Suite
  42. import org.mockito.ArgumentCaptor
  43. import org.mockito.Mock
  44. import org.mockito.MockitoAnnotations
  45. import java.net.URI
  46. @RunWith(Suite::class)
  47. @Suite.SuiteClasses(
  48. ConnectivityServiceTest.IsConnected::class,
  49. ConnectivityServiceTest.WifiConnectionWalledStatusOnLegacyServer::class,
  50. ConnectivityServiceTest.WifiConnectionWalledStatus::class
  51. )
  52. class ConnectivityServiceTest {
  53. internal abstract class Base {
  54. companion object {
  55. fun mockNetworkInfo(connected: Boolean, connecting: Boolean, type: Int): NetworkInfo {
  56. val networkInfo = mock<NetworkInfo>()
  57. whenever(networkInfo.isConnectedOrConnecting).thenReturn(connected or connecting)
  58. whenever(networkInfo.isConnected).thenReturn(connected)
  59. whenever(networkInfo.type).thenReturn(type)
  60. return networkInfo
  61. }
  62. const val SERVER_BASE_URL = "https://test.server.com"
  63. }
  64. @Mock
  65. lateinit var platformConnectivityManager: ConnectivityManager
  66. @Mock
  67. lateinit var networkInfo: NetworkInfo
  68. @Mock
  69. lateinit var accountManager: UserAccountManager
  70. @Mock
  71. lateinit var clientFactory: ClientFactory
  72. @Mock
  73. lateinit var client: HttpClient
  74. @Mock
  75. lateinit var getRequest: GetMethod
  76. @Mock
  77. lateinit var requestBuilder: ConnectivityServiceImpl.GetRequestBuilder
  78. @Mock
  79. lateinit var logger: Logger
  80. val baseServerUri = URI.create(SERVER_BASE_URL)
  81. val newServer = Server(baseServerUri, OwnCloudVersion.nextcloud_14)
  82. val legacyServer = Server(baseServerUri, OwnCloudVersion.nextcloud_13)
  83. @Mock
  84. lateinit var user: User
  85. lateinit var connectivityService: ConnectivityServiceImpl
  86. @Before
  87. fun setUpMocks() {
  88. MockitoAnnotations.initMocks(this)
  89. connectivityService = ConnectivityServiceImpl(
  90. platformConnectivityManager,
  91. accountManager,
  92. clientFactory,
  93. requestBuilder,
  94. logger
  95. )
  96. whenever(platformConnectivityManager.activeNetworkInfo).thenReturn(networkInfo)
  97. whenever(requestBuilder.invoke(any())).thenReturn(getRequest)
  98. whenever(clientFactory.createPlainClient()).thenReturn(client)
  99. whenever(user.server).thenReturn(newServer)
  100. whenever(accountManager.user).thenReturn(user)
  101. }
  102. }
  103. internal class IsConnected : Base() {
  104. @Test
  105. fun `connected to wifi`() {
  106. whenever(networkInfo.isConnectedOrConnecting).thenReturn(true)
  107. whenever(networkInfo.type).thenReturn(ConnectivityManager.TYPE_WIFI)
  108. assertTrue(connectivityService.isOnlineWithWifi)
  109. }
  110. @Test
  111. fun `connected to wifi and vpn`() {
  112. whenever(networkInfo.isConnectedOrConnecting).thenReturn(true)
  113. whenever(networkInfo.type).thenReturn(ConnectivityManager.TYPE_VPN)
  114. val wifiNetworkInfoList = arrayOf(
  115. mockNetworkInfo(
  116. connected = true,
  117. connecting = true,
  118. type = ConnectivityManager.TYPE_VPN
  119. ),
  120. mockNetworkInfo(
  121. connected = true,
  122. connecting = true,
  123. type = ConnectivityManager.TYPE_WIFI
  124. )
  125. )
  126. whenever(platformConnectivityManager.allNetworkInfo).thenReturn(wifiNetworkInfoList)
  127. assertTrue(connectivityService.isOnlineWithWifi)
  128. }
  129. @Test
  130. fun `connected to mobile network`() {
  131. whenever(networkInfo.isConnectedOrConnecting).thenReturn(true)
  132. whenever(networkInfo.type).thenReturn(ConnectivityManager.TYPE_MOBILE)
  133. assertFalse(connectivityService.isOnlineWithWifi)
  134. }
  135. }
  136. internal class WifiConnectionWalledStatusOnLegacyServer : Base() {
  137. @Before
  138. fun setUp() {
  139. whenever(networkInfo.isConnectedOrConnecting).thenReturn(true)
  140. whenever(networkInfo.type).thenReturn(ConnectivityManager.TYPE_WIFI)
  141. whenever(user.server).thenReturn(legacyServer)
  142. assertTrue("Precondition failed", connectivityService.isOnlineWithWifi)
  143. }
  144. fun mockResponse(maintenance: Boolean = true, httpStatus: Int = HttpStatus.SC_OK) {
  145. whenever(client.executeMethod(getRequest)).thenReturn(httpStatus)
  146. val body = """{"maintenance":$maintenance}"""
  147. whenever(getRequest.responseContentLength).thenReturn(body.length.toLong())
  148. whenever(getRequest.responseBodyAsString).thenReturn(body)
  149. }
  150. @Test
  151. fun `false maintenance status flag is used`() {
  152. mockResponse(maintenance = false, httpStatus = HttpStatus.SC_OK)
  153. assertFalse(connectivityService.isInternetWalled)
  154. }
  155. @Test
  156. fun `true maintenance status flag is used`() {
  157. mockResponse(maintenance = true, httpStatus = HttpStatus.SC_OK)
  158. assertTrue(connectivityService.isInternetWalled)
  159. }
  160. @Test
  161. fun `maintenance flag is ignored when non-200 HTTP code is returned`() {
  162. mockResponse(maintenance = false, httpStatus = HttpStatus.SC_NO_CONTENT)
  163. assertTrue(connectivityService.isInternetWalled)
  164. }
  165. @Test
  166. fun `status endpoint is used to determine internet state`() {
  167. mockResponse()
  168. connectivityService.isInternetWalled
  169. val urlCaptor = ArgumentCaptor.forClass(String::class.java)
  170. verify(requestBuilder).invoke(urlCaptor.capture())
  171. assertTrue("Invalid URL used to check status", urlCaptor.value.endsWith("/status.php"))
  172. }
  173. }
  174. internal class WifiConnectionWalledStatus : Base() {
  175. @Before
  176. fun setUp() {
  177. whenever(networkInfo.isConnectedOrConnecting).thenReturn(true)
  178. whenever(networkInfo.type).thenReturn(ConnectivityManager.TYPE_WIFI)
  179. whenever(accountManager.getServerVersion(any())).thenReturn(OwnCloudVersion.nextcloud_14)
  180. assertTrue("Precondition failed", connectivityService.isOnlineWithWifi)
  181. }
  182. @Test
  183. fun `check request is not sent when server uri is not set`() {
  184. // GIVEN
  185. // network connectivity is present
  186. // user has no server URI (empty)
  187. val serverWithoutUri = Server(URI(""), OwnCloudVersion.nextcloud_14)
  188. whenever(user.server).thenReturn(serverWithoutUri)
  189. // WHEN
  190. // connectivity is checked
  191. val result = connectivityService.isInternetWalled
  192. // THEN
  193. // connection is walled
  194. // request is not sent
  195. assertTrue("Server should not be accessible", result)
  196. verify(requestBuilder, never()).invoke(any())
  197. verify(client, never()).executeMethod(any())
  198. verify(client, never()).executeMethod(any(), any())
  199. verify(client, never()).executeMethod(any(), any(), any())
  200. }
  201. fun mockResponse(contentLength: Long = 0, status: Int = HttpStatus.SC_OK) {
  202. whenever(client.executeMethod(any())).thenReturn(status)
  203. whenever(getRequest.statusCode).thenReturn(status)
  204. whenever(getRequest.responseContentLength).thenReturn(contentLength)
  205. }
  206. @Test
  207. fun `status 204 means internet is not walled`() {
  208. mockResponse(contentLength = 0, status = HttpStatus.SC_NO_CONTENT)
  209. assertFalse(connectivityService.isInternetWalled)
  210. }
  211. @Test
  212. fun `other status than 204 means internet is walled`() {
  213. mockResponse(contentLength = 0, status = HttpStatus.SC_GONE)
  214. assertTrue(connectivityService.isInternetWalled)
  215. }
  216. @Test
  217. fun `index endpoint is used to determine internet state`() {
  218. mockResponse()
  219. connectivityService.isInternetWalled
  220. val urlCaptor = ArgumentCaptor.forClass(String::class.java)
  221. verify(requestBuilder).invoke(urlCaptor.capture())
  222. assertTrue("Invalid URL used to check status", urlCaptor.value.endsWith("/index.php/204"))
  223. }
  224. }
  225. }