Skip to content

Commit c784d9b

Browse files
committed
Feat staging: Adjust unit test & gradle to prevent read google-service.json when not exist
1 parent 9cd4658 commit c784d9b

12 files changed

Lines changed: 168 additions & 35 deletions

File tree

app/build.gradle

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,15 @@ plugins {
88
id 'kotlin-parcelize'
99
id 'org.jetbrains.kotlin.plugin.serialization' version '2.2.10'
1010
id 'org.jetbrains.kotlin.plugin.compose' version '2.2.10'
11+
}
1112

12-
// Google Services & Firebase
13-
id 'com.google.gms.google-services'
14-
id 'com.google.firebase.crashlytics'
13+
// Apply Google Services and Crashlytics only if google-services.json exists
14+
if (file('google-services.json').exists()) {
15+
apply plugin: 'com.google.gms.google-services'
16+
apply plugin: 'com.google.firebase.crashlytics'
17+
println 'Google Services and Crashlytics plugins applied'
18+
} else {
19+
println 'google-services.json not found. Skipping Google Services and Crashlytics plugins'
1520
}
1621

1722
android {

app/src/main/java/com/digiventure/ventnote/data/google_drive/GoogleDriveService.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package com.digiventure.ventnote.data.google_drive
22

33
import android.app.Application
44
import com.digiventure.ventnote.data.persistence.NoteModel
5-
import com.digiventure.ventnote.feature.widget.NoteWidgetProvider
5+
import com.digiventure.ventnote.feature.widget.WidgetRefresher
66
import com.digiventure.ventnote.module.proxy.DatabaseProxy
77
import com.google.api.client.http.ByteArrayContent
88
import com.google.api.services.drive.Drive
@@ -16,6 +16,7 @@ import javax.inject.Inject
1616
class GoogleDriveService @Inject constructor(
1717
private val app: Application,
1818
private val proxy: DatabaseProxy,
19+
private val refresher: WidgetRefresher
1920
) {
2021
companion object {
2122
private const val FILE_MIME_TYPE = "application/json"
@@ -70,7 +71,7 @@ class GoogleDriveService @Inject constructor(
7071
proxy.dao().upsertNotes(notes)
7172

7273
// Refresh widget after restore
73-
NoteWidgetProvider.refreshWidgets(app)
74+
refresher.refresh(app)
7475

7576
Result.success(Unit)
7677
} catch (e: Exception) {

app/src/main/java/com/digiventure/ventnote/data/persistence/NoteLocalService.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package com.digiventure.ventnote.data.persistence
22

33
import android.app.Application
44
import com.digiventure.ventnote.commons.ErrorMessage
5-
import com.digiventure.ventnote.feature.widget.NoteWidgetProvider
5+
import com.digiventure.ventnote.feature.widget.WidgetRefresher
66
import com.digiventure.ventnote.module.proxy.DatabaseProxy
77
import kotlinx.coroutines.flow.Flow
88
import kotlinx.coroutines.flow.catch
@@ -13,7 +13,8 @@ import javax.inject.Inject
1313

1414
class NoteLocalService @Inject constructor(
1515
private val app: Application,
16-
private val proxy: DatabaseProxy
16+
private val proxy: DatabaseProxy,
17+
private val refresher: WidgetRefresher
1718
) {
1819
fun getNoteList(sortBy: String, order: String): Flow<Result<List<NoteModel>>> {
1920
return proxy.dao().getNotes(sortBy, order).map {
@@ -28,7 +29,7 @@ class NoteLocalService @Inject constructor(
2829
val result = (proxy.dao().deleteNotes(*notes) == notes.size)
2930
emit(Result.success(result))
3031
}.onEach {
31-
if (it.isSuccess) NoteWidgetProvider.refreshWidgets(app)
32+
if (it.isSuccess) refresher.refresh(app)
3233
}.catch {
3334
emit(Result.failure(RuntimeException(ErrorMessage.FAILED_DELETE_ROOM)))
3435
}
@@ -46,7 +47,7 @@ class NoteLocalService @Inject constructor(
4647
val result = proxy.dao().updateWithTimestamp(note) >= 1
4748
emit(Result.success(result))
4849
}.onEach {
49-
if (it.isSuccess) NoteWidgetProvider.refreshWidgets(app)
50+
if (it.isSuccess) refresher.refresh(app)
5051
}.catch {
5152
emit(Result.failure(RuntimeException(ErrorMessage.FAILED_UPDATE_NOTE_ROOM)))
5253
}
@@ -56,7 +57,7 @@ class NoteLocalService @Inject constructor(
5657
val result = proxy.dao().insertWithTimestamp(note) != -1L
5758
emit(Result.success(result))
5859
}.onEach {
59-
if (it.isSuccess) NoteWidgetProvider.refreshWidgets(app)
60+
if (it.isSuccess) refresher.refresh(app)
6061
}.catch {
6162
emit(Result.failure(RuntimeException(ErrorMessage.FAILED_INSERT_NOTE_ROOM)))
6263
}

app/src/main/java/com/digiventure/ventnote/feature/widget/NoteWidgetFactory.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,20 @@ import android.content.Intent
55
import android.widget.RemoteViews
66
import android.widget.RemoteViewsService
77
import com.digiventure.ventnote.R
8-
import com.digiventure.ventnote.config.NoteDatabase
8+
import com.digiventure.ventnote.module.proxy.DatabaseProxy
99
import com.digiventure.ventnote.data.persistence.NoteModel
1010

11-
class NoteWidgetFactory(private val context: Context) : RemoteViewsService.RemoteViewsFactory {
11+
class NoteWidgetFactory(
12+
private val context: Context,
13+
private val proxy: DatabaseProxy
14+
) : RemoteViewsService.RemoteViewsFactory {
1215
private var notes: List<NoteModel> = emptyList()
1316

1417
override fun onCreate() {}
1518

1619
override fun onDataSetChanged() {
1720
// Fetch notes from database
18-
notes = NoteDatabase.getInstance(context).dao().getSyncNotes()
21+
notes = proxy.dao().getSyncNotes()
1922
}
2023

2124
override fun onDestroy() {

app/src/main/java/com/digiventure/ventnote/feature/widget/NoteWidgetService.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,16 @@ package com.digiventure.ventnote.feature.widget
22

33
import android.content.Intent
44
import android.widget.RemoteViewsService
5+
import com.digiventure.ventnote.module.proxy.DatabaseProxy
6+
import dagger.hilt.android.AndroidEntryPoint
7+
import javax.inject.Inject
58

9+
@AndroidEntryPoint
610
class NoteWidgetService : RemoteViewsService() {
11+
@Inject
12+
lateinit var proxy: DatabaseProxy
13+
714
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
8-
return NoteWidgetFactory(this.applicationContext)
15+
return NoteWidgetFactory(this.applicationContext, proxy)
916
}
1017
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.digiventure.ventnote.feature.widget
2+
3+
import android.content.Context
4+
import javax.inject.Inject
5+
6+
interface WidgetRefresher {
7+
fun refresh(context: Context)
8+
}
9+
10+
class NoteWidgetRefresher @Inject constructor() : WidgetRefresher {
11+
override fun refresh(context: Context) {
12+
NoteWidgetProvider.refreshWidgets(context)
13+
}
14+
}

app/src/main/java/com/digiventure/ventnote/module/ApplicationModule.kt

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package com.digiventure.ventnote.module
22

33
import android.content.Context
44
import com.digiventure.ventnote.data.local.NoteDataStore
5+
import com.digiventure.ventnote.feature.widget.NoteWidgetRefresher
6+
import com.digiventure.ventnote.feature.widget.WidgetRefresher
7+
import dagger.Binds
58
import dagger.Module
69
import dagger.Provides
710
import dagger.hilt.InstallIn
@@ -16,18 +19,24 @@ import javax.inject.Singleton
1619

1720
@Module
1821
@InstallIn(SingletonComponent::class)
19-
class ApplicationModule {
20-
@Provides
22+
abstract class ApplicationModule {
23+
@Binds
2124
@Singleton
22-
fun provideCoroutineScope(): CoroutineScope = CoroutineScope(SupervisorJob())
25+
abstract fun bindWidgetRefresher(refresher: NoteWidgetRefresher): WidgetRefresher
2326

24-
@Provides
25-
@Singleton
26-
fun provideExecutorCoroutineDispatcher(): ExecutorCoroutineDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
27+
companion object {
28+
@Provides
29+
@Singleton
30+
fun provideCoroutineScope(): CoroutineScope = CoroutineScope(SupervisorJob())
2731

28-
@Provides
29-
@Singleton
30-
fun provideNoteDataStore(@ApplicationContext context: Context): NoteDataStore {
31-
return NoteDataStore(context)
32+
@Provides
33+
@Singleton
34+
fun provideExecutorCoroutineDispatcher(): ExecutorCoroutineDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
35+
36+
@Provides
37+
@Singleton
38+
fun provideNoteDataStore(@ApplicationContext context: Context): NoteDataStore {
39+
return NoteDataStore(context)
40+
}
3241
}
3342
}

app/src/test/java/com/digiventure/utils/BaseUnitTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import org.mockito.junit.MockitoJUnitRunner
88
@RunWith(MockitoJUnitRunner::class)
99
abstract class BaseUnitTest {
1010
@get:Rule
11-
var coroutinesTestRule = MainDispatcherRule()
11+
val mainDispatcherRule = MainDispatcherRule()
1212

1313
@get:Rule
1414
var instantTaskExecutor = InstantTaskExecutorRule()

app/src/test/java/com/digiventure/ventnote/commons/DateUtilsTest.kt

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,43 @@ package com.digiventure.ventnote.commons
22

33
import com.digiventure.utils.BaseUnitTest
44
import org.junit.Assert.assertEquals
5+
import org.junit.Assert.assertTrue
56
import org.junit.Test
7+
import java.util.Calendar
8+
import java.util.Date
9+
import java.util.TimeZone
610

711
class DateUtilsTest: BaseUnitTest() {
12+
@Test
13+
fun formatNoteDateShouldReturnFormattedString() {
14+
// Set a fixed date: 2023-10-27 10:30 AM
15+
val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
16+
calendar.set(2023, Calendar.OCTOBER, 27, 10, 30, 0)
17+
val date = calendar.time
18+
19+
// The default formatter uses EEEE, MMMM d h:mm a
20+
val result = DateUtil.formatNoteDate(date)
21+
22+
// Assert that it contains basic info
23+
assertTrue(result.contains("October"))
24+
assertTrue(result.contains("27"))
25+
}
26+
827
@Test
928
fun convertDateStringShouldReturnValidFormattedString() {
1029
val expectedDateString = "Thu, Jan 1"
1130

1231
assertEquals(expectedDateString, DateUtil.convertDateString(
1332
"EEE, MMM d",
14-
"Thu Jan 01 07:00:00 GMT+07:00 1970"
33+
"Thu Jan 01 00:00:00 UTC 1970"
1534
))
1635
}
1736

1837
@Test
1938
fun convertDateStringShouldReturnEmptyStringWhenError() {
20-
val expectedDateString = ""
21-
22-
assertEquals(expectedDateString, DateUtil.convertDateString(
39+
assertEquals("", DateUtil.convertDateString(
2340
"EEE, MMM d",
24-
"Thu Jn 01 07:00:00 GMT+07:00 1970"
41+
"Invalid Date String"
2542
))
2643
}
2744
}

app/src/test/java/com/digiventure/ventnote/data/google_drive/GoogleDriveServiceShould.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.digiventure.ventnote.data.google_drive
22

3+
import android.app.Application
34
import com.digiventure.utils.BaseUnitTest
45
import com.digiventure.ventnote.data.persistence.NoteDAO
56
import com.digiventure.ventnote.data.persistence.NoteModel
7+
import com.digiventure.ventnote.feature.widget.WidgetRefresher
68
import com.digiventure.ventnote.module.proxy.DatabaseProxy
79
import com.google.api.services.drive.Drive
810
import com.google.api.services.drive.model.File
@@ -20,8 +22,10 @@ import org.mockito.kotlin.verify
2022
import org.mockito.kotlin.whenever
2123

2224
class GoogleDriveServiceShould: BaseUnitTest() {
25+
private val app: Application = mock()
2326
private val proxy: DatabaseProxy = mock()
2427
private val dao: NoteDAO = mock()
28+
private val refresher: WidgetRefresher = mock()
2529
private val noteList: List<NoteModel> = listOf()
2630
private val fileName: String = "backup.json"
2731
private val fileId: String = "1"
@@ -31,7 +35,7 @@ class GoogleDriveServiceShould: BaseUnitTest() {
3135

3236
@Before
3337
fun setup() {
34-
service = GoogleDriveService(proxy)
38+
service = GoogleDriveService(app, proxy, refresher)
3539
}
3640

3741
@Test
@@ -72,13 +76,14 @@ class GoogleDriveServiceShould: BaseUnitTest() {
7276
whenever(drive.files()).thenReturn(filesMock)
7377
whenever(filesMock.get(fileId)).thenReturn(getMock)
7478
whenever(getMock.executeMediaAsInputStream()).thenReturn(inputStream)
75-
whenever(dao.upsertNotesWithTimestamp(any())).thenAnswer { }
79+
whenever(dao.upsertNotes(any())).thenAnswer { }
7680
whenever(proxy.dao()).thenReturn(dao)
7781

7882
val result = service.readFile(fileId, drive)
7983

8084
assertTrue(result.isSuccess)
81-
verify(proxy.dao(), times(1)).upsertNotesWithTimestamp(any())
85+
verify(proxy.dao(), times(1)).upsertNotes(any())
86+
verify(refresher, times(1)).refresh(app)
8287
}
8388

8489
@Test
@@ -103,7 +108,7 @@ class GoogleDriveServiceShould: BaseUnitTest() {
103108
whenever(drive.files()).thenReturn(filesMock)
104109
whenever(filesMock.get(fileId)).thenReturn(getMock)
105110
whenever(getMock.executeMediaAsInputStream()).thenReturn(inputStream)
106-
whenever(dao.upsertNotesWithTimestamp(any())).thenAnswer {
111+
whenever(dao.upsertNotes(any())).thenAnswer {
107112
throw exception
108113
}
109114
whenever(proxy.dao()).thenReturn(dao)

0 commit comments

Comments
 (0)