Skip to content

Commit

Permalink
cover new logic with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cmonfortep committed Nov 27, 2023
1 parent 3e772cd commit 987b615
Show file tree
Hide file tree
Showing 8 changed files with 327 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import com.duckduckgo.savedsites.store.SavedSitesRelationsDao
import com.duckduckgo.sync.api.SyncCrypto
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
Expand All @@ -58,6 +59,7 @@ import org.mockito.kotlin.*
import org.threeten.bp.OffsetDateTime
import org.threeten.bp.ZoneOffset

@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class SavedSitesSyncDataProviderTest {

Expand Down Expand Up @@ -115,7 +117,11 @@ class SavedSitesSyncDataProviderTest {
favoritesDelegate,
coroutinesTestRule.testDispatcherProvider,
)
store = RealSavedSitesSyncStore(InstrumentationRegistry.getInstrumentation().context)
store = RealSavedSitesSyncStore(
InstrumentationRegistry.getInstrumentation().context,
coroutinesTestRule.testScope,
coroutinesTestRule.testDispatcherProvider,
)

savedSitesFormFactorSyncMigration = RealSavedSitesFormFactorSyncMigration(
savedSitesEntitiesDao,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.duckduckgo.savedsites.impl.sync

import android.annotation.SuppressLint
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
Expand All @@ -33,6 +34,7 @@ import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch

@SuppressLint("NoLifecycleObserver") // we don't observe app lifecycle
class SavedSiteRateLimitViewModel(
private val savedSitesSyncStore: SavedSitesSyncStore,
private val dispatcherProvider: DispatcherProvider,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal">

<TextView
tools:ignore="DeprecatedWidgetInXml"
android:id="@+id/notificationSyncPaused"
style="@style/TextAppearance.Compat.Notification"
android:layout_width="0dp"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* 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.savedsites.impl.sync

import android.content.Intent
import androidx.core.app.NotificationManagerCompat
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.duckduckgo.common.test.CoroutineTestRule
import com.duckduckgo.common.test.FileUtilities
import com.duckduckgo.navigation.api.GlobalActivityStarter
import com.duckduckgo.sync.api.SyncActivityWithEmptyParams
import com.duckduckgo.sync.api.engine.FeatureSyncError
import com.duckduckgo.sync.api.engine.SyncChangesResponse
import com.duckduckgo.sync.api.engine.SyncableType.BOOKMARKS
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Assert.*
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class AppSavedSitesSyncFeatureListenerTest {

@get:Rule
var coroutineRule = CoroutineTestRule()
private val realContext = InstrumentationRegistry.getInstrumentation().targetContext
private val notificationManager = NotificationManagerCompat.from(realContext)
private val savedSitesSyncStore = RealSavedSitesSyncStore(realContext, coroutineRule.testScope, coroutineRule.testDispatcherProvider)
private val globalActivityStarterMock = mock<GlobalActivityStarter>().apply {
whenever(this.startIntent(realContext, SyncActivityWithEmptyParams)).thenReturn(Intent())
}

private val testee = AppSavedSitesSyncFeatureListener(
realContext,
savedSitesSyncStore,
notificationManager,
AppSavedSitesSyncNotificationBuilder(globalActivityStarterMock),
)

@Test
fun whenNoValuesThenIsSyncPausedIsFalse() {
assertFalse(savedSitesSyncStore.isSyncPaused)
}

@Test
fun whenSyncPausedAndOnSuccessWithChangesThenIsSyncPausedIsFalse() {
savedSitesSyncStore.isSyncPaused = true
val updatesJSON = FileUtilities.loadText(javaClass.classLoader!!, "json/merger_first_get.json")
val validChanges = SyncChangesResponse(BOOKMARKS, updatesJSON)

testee.onSuccess(validChanges)

assertFalse(savedSitesSyncStore.isSyncPaused)
}

@Test
fun whenSyncPausedAndOnSuccessWithoutChangesThenSyncPaused() {
savedSitesSyncStore.isSyncPaused = true
val validChanges = SyncChangesResponse.empty(BOOKMARKS)

testee.onSuccess(validChanges)

assertTrue(savedSitesSyncStore.isSyncPaused)
}

@Test
fun whenSyncPausedAndOnErrorThenSyncPaused() {
savedSitesSyncStore.isSyncPaused = true

testee.onError(FeatureSyncError.COLLECTION_LIMIT_REACHED)

assertTrue(savedSitesSyncStore.isSyncPaused)
}

@Test
fun whenSyncActiveAndOnErrorThenSyncPaused() {
savedSitesSyncStore.isSyncPaused = false

testee.onError(FeatureSyncError.COLLECTION_LIMIT_REACHED)

assertTrue(savedSitesSyncStore.isSyncPaused)
}

@Test
fun whenOnSyncDisabledThenSyncPausedFalse() {
savedSitesSyncStore.isSyncPaused = true

testee.onSyncDisabled()

assertFalse(savedSitesSyncStore.isSyncPaused)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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.savedsites.impl.sync

import android.content.Context
import app.cash.turbine.test
import com.duckduckgo.common.test.CoroutineTestRule
import com.duckduckgo.common.test.api.InMemorySharedPreferences
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Assert.*
import org.junit.Rule
import org.junit.Test
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

@OptIn(ExperimentalCoroutinesApi::class)
class RealSavedSitesSyncStoreTest {

@get:Rule
var coroutineRule = CoroutineTestRule()

private val preferences = InMemorySharedPreferences()

private val mockContext: Context = mock<Context>().apply {
whenever(this.getSharedPreferences(anyString(), anyInt())).thenReturn(preferences)
}

val testee = RealSavedSitesSyncStore(mockContext, coroutineRule.testScope, coroutineRule.testDispatcherProvider)

@Test
fun whenNoValueIsSyncPausedThenReturnFalse() {
assertFalse(testee.isSyncPaused)
}

@Test
fun whenIsSyncPausedUpdatedThenEmitNewValue() = runTest {
testee.isSyncPausedFlow().test {
awaitItem()
testee.isSyncPaused = true
assertEquals(true, awaitItem())
cancelAndConsumeRemainingEvents()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* 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.savedsites.impl.sync

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import app.cash.turbine.test
import com.duckduckgo.common.test.CoroutineTestRule
import com.duckduckgo.savedsites.impl.sync.SavedSiteRateLimitViewModel.Command.NavigateToBookmarks
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Assert.*
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class SavedSiteRateLimitViewModelTest {
@get:Rule
var coroutineRule = CoroutineTestRule()

private val realContext = InstrumentationRegistry.getInstrumentation().targetContext
val savedSitesSyncStore = RealSavedSitesSyncStore(realContext, coroutineRule.testScope, coroutineRule.testDispatcherProvider)

val testee = SavedSiteRateLimitViewModel(
savedSitesSyncStore,
coroutineRule.testDispatcherProvider,
)

@Test
fun whenSyncPausedThenWarningVisible() = runTest {
savedSitesSyncStore.isSyncPaused = true
testee.viewState().test {
assertTrue(awaitItem().warningVisible)
cancelAndConsumeRemainingEvents()
}
}

@Test
fun whenUserClicksWarningActionThenNavigateToBookmarks() = runTest {
testee.commands().test {
testee.onWarningActionClicked()
assertEquals(NavigateToBookmarks, awaitItem())
cancelAndConsumeRemainingEvents()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import com.duckduckgo.common.test.FileUtilities
import com.duckduckgo.common.utils.formatters.time.DatabaseDateFormatter
import com.duckduckgo.savedsites.api.SavedSitesRepository
import com.duckduckgo.savedsites.impl.sync.algorithm.SavedSitesSyncPersisterAlgorithm
import com.duckduckgo.sync.api.engine.FeatureSyncError.COLLECTION_LIMIT_REACHED
import com.duckduckgo.sync.api.engine.SyncChangesResponse
import com.duckduckgo.sync.api.engine.SyncErrorResponse
import com.duckduckgo.sync.api.engine.SyncMergeResult.Error
import com.duckduckgo.sync.api.engine.SyncMergeResult.Success
import com.duckduckgo.sync.api.engine.SyncableDataPersister.SyncConflictResolution.TIMESTAMP
Expand All @@ -34,6 +36,7 @@ import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever

class SavedSitesSyncPersisterTest {
Expand All @@ -49,12 +52,19 @@ class SavedSitesSyncPersisterTest {
private val store: SavedSitesSyncStore = mock()
private val persisterAlgorithm: SavedSitesSyncPersisterAlgorithm = mock()
private val savedSitesFormFactorSyncMigration: SavedSitesFormFactorSyncMigration = mock()
private val savedSitesSyncFeatureListener: SavedSitesSyncFeatureListener = mock()

private lateinit var syncPersister: SavedSitesSyncPersister

@Before
fun setup() {
syncPersister = SavedSitesSyncPersister(repository, store, persisterAlgorithm, savedSitesFormFactorSyncMigration)
syncPersister = SavedSitesSyncPersister(
repository,
store,
persisterAlgorithm,
savedSitesFormFactorSyncMigration,
savedSitesSyncFeatureListener,
)
}

@Test
Expand Down Expand Up @@ -111,4 +121,26 @@ class SavedSitesSyncPersisterTest {

Assert.assertTrue(result is Success)
}

@Test
fun whenOnSuccessThenNotifyListener() {
val updatesJSON = FileUtilities.loadText(javaClass.classLoader!!, "json/merger_first_get.json")
val validChanges = SyncChangesResponse(BOOKMARKS, updatesJSON)

syncPersister.onSuccess(validChanges, TIMESTAMP)

verify(savedSitesSyncFeatureListener).onSuccess(validChanges)
}

@Test
fun whenOnErrorThenNotifyListener() {
syncPersister.onError(SyncErrorResponse(BOOKMARKS, COLLECTION_LIMIT_REACHED))
verify(savedSitesSyncFeatureListener).onError(COLLECTION_LIMIT_REACHED)
}

@Test
fun whenOnSyncDisabledTheNotifyListener() {
syncPersister.onSyncDisabled()
verify(savedSitesSyncFeatureListener).onSyncDisabled()
}
}
Loading

0 comments on commit 987b615

Please sign in to comment.