mirror of
https://github.com/wismna/DoNext.git
synced 2025-12-06 00:02:40 -05:00
Compare commits
2 Commits
f0efc3dade
...
fc3672b17b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc3672b17b | ||
|
|
85f1e66a62 |
26
.idea/androidTestResultsUserPreferences.xml
generated
26
.idea/androidTestResultsUserPreferences.xml
generated
@@ -3,6 +3,32 @@
|
|||||||
<component name="AndroidTestResultsUserPreferences">
|
<component name="AndroidTestResultsUserPreferences">
|
||||||
<option name="androidTestResultsTableState">
|
<option name="androidTestResultsTableState">
|
||||||
<map>
|
<map>
|
||||||
|
<entry key="-2002158262">
|
||||||
|
<value>
|
||||||
|
<AndroidTestResultsTableState>
|
||||||
|
<option name="preferredColumnWidths">
|
||||||
|
<map>
|
||||||
|
<entry key="Duration" value="90" />
|
||||||
|
<entry key="Medium_Phone_API_36.0" value="120" />
|
||||||
|
<entry key="Tests" value="360" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</AndroidTestResultsTableState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="-1209641426">
|
||||||
|
<value>
|
||||||
|
<AndroidTestResultsTableState>
|
||||||
|
<option name="preferredColumnWidths">
|
||||||
|
<map>
|
||||||
|
<entry key="Duration" value="90" />
|
||||||
|
<entry key="Medium_Phone_API_36.0" value="120" />
|
||||||
|
<entry key="Tests" value="360" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</AndroidTestResultsTableState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
<entry key="1337588336">
|
<entry key="1337588336">
|
||||||
<value>
|
<value>
|
||||||
<AndroidTestResultsTableState>
|
<AndroidTestResultsTableState>
|
||||||
|
|||||||
7
.idea/deploymentTargetSelector.xml
generated
7
.idea/deploymentTargetSelector.xml
generated
@@ -5,10 +5,13 @@
|
|||||||
<SelectionState runConfigName="donextv2">
|
<SelectionState runConfigName="donextv2">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
<SelectionState runConfigName="overdueCount_correctlyCalculated()">
|
<SelectionState runConfigName="donext">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
<SelectionState runConfigName="donext">
|
<SelectionState runConfigName="DatabaseMigrationTest">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="migrate_v6_to_v7_preserves_data_and_transforms_columns()">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
</selectionStates>
|
</selectionStates>
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ buildscript {
|
|||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.13.0'
|
classpath 'com.android.tools.build:gradle:8.13.0'
|
||||||
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3'
|
|
||||||
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.21'
|
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.21'
|
||||||
classpath 'org.jetbrains.kotlin:compose-compiler-gradle-plugin:2.0.21'
|
classpath 'org.jetbrains.kotlin:compose-compiler-gradle-plugin:2.0.21'
|
||||||
|
|
||||||
@@ -38,5 +37,3 @@ allprojects {
|
|||||||
tasks.register('clean', Delete) {
|
tasks.register('clean', Delete) {
|
||||||
delete rootProject.layout.buildDirectory
|
delete rootProject.layout.buildDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'org.sonarqube'
|
|
||||||
BIN
donext.backup
Normal file
BIN
donext.backup
Normal file
Binary file not shown.
@@ -22,15 +22,6 @@ android {
|
|||||||
targetCompatibility JavaVersion.VERSION_17
|
targetCompatibility JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sonarqube {
|
|
||||||
properties {
|
|
||||||
property 'sonar.host.url', '#{sonar.host.url}'
|
|
||||||
property 'sonar.login', '#{sonar.login}'
|
|
||||||
property 'sonar.organization', '#{sonar.organization}'
|
|
||||||
property 'sonar.projectKey', '#{sonar.projectkey}'
|
|
||||||
property 'sonar.branch', 'master'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ plugins {
|
|||||||
id("com.google.dagger.hilt.android")
|
id("com.google.dagger.hilt.android")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ksp {
|
||||||
|
arg("room.schemaLocation", "$projectDir/schemas")
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.wismna.geoffroy.donext"
|
namespace = "com.wismna.geoffroy.donext"
|
||||||
compileSdk = 36
|
compileSdk = 36
|
||||||
@@ -20,6 +24,10 @@ android {
|
|||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
getByName("debug").assets.srcDirs(files("$projectDir/schemas"))
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
// Enables code-related app optimization.
|
// Enables code-related app optimization.
|
||||||
@@ -46,6 +54,17 @@ android {
|
|||||||
composeOptions {
|
composeOptions {
|
||||||
kotlinCompilerExtensionVersion = "1.1.1"
|
kotlinCompilerExtensionVersion = "1.1.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configurations.all {
|
||||||
|
resolutionStrategy {
|
||||||
|
eachDependency {
|
||||||
|
when (requested.module.toString()) {
|
||||||
|
// Required for forcing the serialization lib version used by MigrationTestHelper
|
||||||
|
"org.jetbrains.kotlinx:kotlinx-serialization-core-jvm" -> useVersion("1.8.0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -60,15 +79,20 @@ dependencies {
|
|||||||
implementation("androidx.compose.material:material-icons-extended:1.7.8")
|
implementation("androidx.compose.material:material-icons-extended:1.7.8")
|
||||||
implementation("androidx.navigation:navigation-compose:2.9.5")
|
implementation("androidx.navigation:navigation-compose:2.9.5")
|
||||||
implementation("androidx.hilt:hilt-navigation-compose:1.3.0")
|
implementation("androidx.hilt:hilt-navigation-compose:1.3.0")
|
||||||
implementation("androidx.test.ext:junit-ktx:1.3.0")
|
|
||||||
implementation("sh.calvin.reorderable:reorderable:3.0.0")
|
implementation("sh.calvin.reorderable:reorderable:3.0.0")
|
||||||
|
testImplementation("junit:junit:4.13.2")
|
||||||
|
testImplementation("io.mockk:mockk:1.13.12")
|
||||||
|
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0")
|
||||||
|
androidTestImplementation("androidx.test.ext:junit-ktx:1.3.0")
|
||||||
androidTestImplementation(platform("androidx.compose:compose-bom:2025.10.01"))
|
androidTestImplementation(platform("androidx.compose:compose-bom:2025.10.01"))
|
||||||
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
||||||
|
androidTestImplementation("com.google.truth:truth:1.4.4")
|
||||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||||
|
|
||||||
val roomVersion = "2.8.3"
|
val roomVersion = "2.8.3"
|
||||||
implementation("androidx.room:room-runtime:$roomVersion")
|
implementation("androidx.room:room-runtime:$roomVersion")
|
||||||
|
androidTestImplementation("androidx.room:room-testing:$roomVersion")
|
||||||
ksp("androidx.room:room-compiler:$roomVersion")
|
ksp("androidx.room:room-compiler:$roomVersion")
|
||||||
|
|
||||||
val hiltVersion = "2.57.2"
|
val hiltVersion = "2.57.2"
|
||||||
|
|||||||
@@ -0,0 +1,171 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 6,
|
||||||
|
"identityHash": "a911cf75d24949c0b24bd212cacc860c",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "tasks",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `description` TEXT, `cycle` INTEGER NOT NULL, `priority` INTEGER NOT NULL, `done` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `displayorder` INTEGER NOT NULL, `todayorder` INTEGER NOT NULL, `list` INTEGER NOT NULL, `duedate` TEXT, `todaydate` TEXT, FOREIGN KEY(`list`) REFERENCES `tasklist`(`_id`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "_id",
|
||||||
|
"columnName": "_id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "description",
|
||||||
|
"columnName": "description",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "cycle",
|
||||||
|
"columnName": "cycle",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "priority",
|
||||||
|
"columnName": "priority",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "done",
|
||||||
|
"columnName": "done",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "deleted",
|
||||||
|
"columnName": "deleted",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "order",
|
||||||
|
"columnName": "displayorder",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "todayOrder",
|
||||||
|
"columnName": "todayorder",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "taskList",
|
||||||
|
"columnName": "list",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "dueDate",
|
||||||
|
"columnName": "duedate",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "todayDate",
|
||||||
|
"columnName": "todaydate",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"_id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [
|
||||||
|
{
|
||||||
|
"name": "index_tasks_list",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"list"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_tasks_list` ON `${TABLE_NAME}` (`list`)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"foreignKeys": [
|
||||||
|
{
|
||||||
|
"table": "tasklist",
|
||||||
|
"onDelete": "NO ACTION",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"list"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"_id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "tasklist",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `visible` INTEGER NOT NULL, `displayorder` INTEGER NOT NULL, `taskCount` INTEGER NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "_id",
|
||||||
|
"columnName": "_id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "visible",
|
||||||
|
"columnName": "visible",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "order",
|
||||||
|
"columnName": "displayorder",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "taskCount",
|
||||||
|
"columnName": "taskCount",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"_id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"views": [
|
||||||
|
{
|
||||||
|
"viewName": "TodayTasksView",
|
||||||
|
"createSql": "CREATE VIEW `${VIEW_NAME}` AS SELECT * FROM tasks WHERE todaydate = date('now','localtime')"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a911cf75d24949c0b24bd212cacc860c')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 7,
|
||||||
|
"identityHash": "adb2abaced32bebf52bab45ae8069f40",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "tasks",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `description` TEXT, `priority` INTEGER NOT NULL, `done` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `task_list_id` INTEGER NOT NULL, `due_date` INTEGER)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "description",
|
||||||
|
"columnName": "description",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "priority",
|
||||||
|
"columnName": "priority",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isDone",
|
||||||
|
"columnName": "done",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isDeleted",
|
||||||
|
"columnName": "deleted",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "taskListId",
|
||||||
|
"columnName": "task_list_id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "dueDate",
|
||||||
|
"columnName": "due_date",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "task_lists",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `display_order` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "order",
|
||||||
|
"columnName": "display_order",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isDeleted",
|
||||||
|
"columnName": "deleted",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": true,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'adb2abaced32bebf52bab45ae8069f40')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package com.wismna.geoffroy.donext.data.local
|
||||||
|
|
||||||
|
import android.content.ContentValues
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.room.testing.MigrationTestHelper
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import com.wismna.geoffroy.donext.domain.model.Priority
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class DatabaseMigrationTest {
|
||||||
|
|
||||||
|
private val TEST_DB = "migration-test.db"
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
val helper = MigrationTestHelper(
|
||||||
|
InstrumentationRegistry.getInstrumentation(),
|
||||||
|
AppDatabase::class.java,
|
||||||
|
listOf(),
|
||||||
|
FrameworkSQLiteOpenHelperFactory()
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test recreates the old SQLite schema (v6 from DatabaseHelper),
|
||||||
|
* inserts sample legacy rows, runs AppDatabase.MIGRATION_6_7 and then
|
||||||
|
* validates the migrated data by calling the real DAOs you provided.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun migrate_v6_to_v7_preserves_data_and_transforms_columns() {
|
||||||
|
// Arrange
|
||||||
|
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
|
||||||
|
val db: SupportSQLiteDatabase = helper.createDatabase(TEST_DB, 6)
|
||||||
|
|
||||||
|
val listValues = ContentValues().apply {
|
||||||
|
put("_id", 1)
|
||||||
|
put("name", "Legacy List")
|
||||||
|
put("displayorder", 10)
|
||||||
|
put("visible", 1)
|
||||||
|
put("taskCount", 0)
|
||||||
|
}
|
||||||
|
db.insert("tasklist", 0, listValues)
|
||||||
|
|
||||||
|
val taskValues = ContentValues().apply {
|
||||||
|
put("_id", 1) // explicit id
|
||||||
|
put("name", "Legacy Task")
|
||||||
|
put("description", "Old task description")
|
||||||
|
put("priority", 2)
|
||||||
|
put("cycle", 0)
|
||||||
|
put("done", 0)
|
||||||
|
put("deleted", 0)
|
||||||
|
put("displayorder", 5)
|
||||||
|
put("todayorder", 0)
|
||||||
|
put("list", 1) // references tasklist _id = 1
|
||||||
|
put("duedate", "2025-09-15") // legacy text date format that migration converts
|
||||||
|
put("todaydate", null as String?)
|
||||||
|
}
|
||||||
|
db.insert("tasks", 0, taskValues)
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
helper.runMigrationsAndValidate(TEST_DB, 7, true, AppDatabase.MIGRATION_6_7)
|
||||||
|
|
||||||
|
val migratedRoom = Room.databaseBuilder(context, AppDatabase::class.java, TEST_DB)
|
||||||
|
.addMigrations(AppDatabase.MIGRATION_6_7)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
try {
|
||||||
|
val listDao = migratedRoom.taskListDao()
|
||||||
|
val taskDao = migratedRoom.taskDao()
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
val migratedList = listDao.getTaskListById(1L)
|
||||||
|
assertThat(migratedList).isNotNull()
|
||||||
|
assertThat(migratedList!!.id).isEqualTo(1L)
|
||||||
|
assertThat(migratedList.name).isEqualTo("Legacy List")
|
||||||
|
assertThat(migratedList.isDeleted).isEqualTo(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
val migratedTask = taskDao.getTaskById(1L)
|
||||||
|
assertThat(migratedTask).isNotNull()
|
||||||
|
assertThat(migratedTask!!.id).isEqualTo(1L)
|
||||||
|
assertThat(migratedTask.name).isEqualTo("Legacy Task")
|
||||||
|
assertThat(migratedTask.description).isEqualTo("Old task description")
|
||||||
|
assertThat(migratedTask.priority).isEqualTo(Priority.HIGH)
|
||||||
|
assertThat(migratedTask.isDone).isEqualTo(false)
|
||||||
|
assertThat(migratedTask.isDeleted).isEqualTo(false)
|
||||||
|
assertThat(migratedTask.dueDate).isNotNull()
|
||||||
|
assertThat(migratedTask.dueDate!!).isGreaterThan(0L)
|
||||||
|
assertThat(migratedTask.taskListId).isEqualTo(1L)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
migratedRoom.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package com.wismna.geoffroy.donext.presentation.viewmodel
|
||||||
|
|
||||||
|
import com.wismna.geoffroy.donext.R
|
||||||
|
import com.wismna.geoffroy.donext.domain.model.Priority
|
||||||
|
import com.wismna.geoffroy.donext.domain.model.Task
|
||||||
|
import com.wismna.geoffroy.donext.domain.usecase.GetDueTodayTasksUseCase
|
||||||
|
import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDeletedUseCase
|
||||||
|
import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDoneUseCase
|
||||||
|
import com.wismna.geoffroy.donext.presentation.ui.events.UiEvent
|
||||||
|
import com.wismna.geoffroy.donext.presentation.ui.events.UiEventBus
|
||||||
|
import io.mockk.*
|
||||||
|
import junit.framework.TestCase.assertEquals
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.test.*
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
class DueTodayViewModelTest {
|
||||||
|
|
||||||
|
private lateinit var getDueTodayTasksUseCase: GetDueTodayTasksUseCase
|
||||||
|
private lateinit var toggleTaskDeletedUseCase: ToggleTaskDeletedUseCase
|
||||||
|
private lateinit var toggleTaskDoneUseCase: ToggleTaskDoneUseCase
|
||||||
|
private lateinit var uiEventBus: UiEventBus
|
||||||
|
private lateinit var viewModel: DueTodayViewModel
|
||||||
|
|
||||||
|
private val testDispatcher = StandardTestDispatcher()
|
||||||
|
private val tasksFlow = MutableSharedFlow<List<Task>>(replay = 1)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
Dispatchers.setMain(testDispatcher)
|
||||||
|
|
||||||
|
getDueTodayTasksUseCase = mockk()
|
||||||
|
toggleTaskDeletedUseCase = mockk(relaxed = true)
|
||||||
|
toggleTaskDoneUseCase = mockk(relaxed = true)
|
||||||
|
uiEventBus = mockk(relaxed = true)
|
||||||
|
|
||||||
|
coEvery { getDueTodayTasksUseCase.invoke() } returns tasksFlow
|
||||||
|
|
||||||
|
viewModel = DueTodayViewModel(
|
||||||
|
getDueTodayTasksUseCase,
|
||||||
|
toggleTaskDeletedUseCase,
|
||||||
|
toggleTaskDoneUseCase,
|
||||||
|
uiEventBus
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
Dispatchers.resetMain()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `dueTodayTasks updates when flow emits`() = runTest {
|
||||||
|
val taskList = listOf(Task(taskListId = 0, id = 1, name = "Test Task", description = "", priority = Priority.NORMAL, isDone = false, isDeleted = false))
|
||||||
|
tasksFlow.emit(taskList)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
assertEquals(taskList, viewModel.dueTodayTasks)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `onTaskClicked sends EditTask event`() = runTest {
|
||||||
|
val task = Task(taskListId = 0, id = 42, name = "Click me", description = "", priority = Priority.NORMAL, isDone = false, isDeleted = false)
|
||||||
|
|
||||||
|
viewModel.onTaskClicked(task)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
coVerify { uiEventBus.send(UiEvent.EditTask(task)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `updateTaskDone toggles done and sends snackbar with undo`() = runTest {
|
||||||
|
val taskId = 5L
|
||||||
|
|
||||||
|
viewModel.updateTaskDone(taskId)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
coVerify { toggleTaskDoneUseCase(taskId, true) }
|
||||||
|
coVerify {
|
||||||
|
uiEventBus.send(
|
||||||
|
match {
|
||||||
|
it is UiEvent.ShowUndoSnackbar &&
|
||||||
|
it.message == R.string.snackbar_message_task_done
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `deleteTask toggles deleted and sends snackbar with undo`() = runTest {
|
||||||
|
val taskId = 7L
|
||||||
|
|
||||||
|
viewModel.deleteTask(taskId)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
coVerify { toggleTaskDeletedUseCase(taskId, true) }
|
||||||
|
coVerify {
|
||||||
|
uiEventBus.send(
|
||||||
|
match{
|
||||||
|
it is UiEvent.ShowUndoSnackbar &&
|
||||||
|
it.message == R.string.snackbar_message_task_recycle
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user