Skip to content

Commit

Permalink
Communication: Reintroduce foreign key constraints and offline prim…
Browse files Browse the repository at this point in the history
…ary keys (#70)

Co-authored-by: Paul Rangger <paul.rangger@timebite.at>
Co-authored-by: Martin Felber <felber.martin3@gmail.com>
  • Loading branch information
3 people authored Nov 30, 2024
1 parent ae68b6b commit 8396c11
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import de.tum.informatics.www1.artemis.native_app.feature.push.communication_not
CommunicationMessageEntity::class
],
exportSchema = true,
version = 10,
version = 11,
)
@TypeConverters(RoomTypeConverters::class)
abstract class AppDatabase : RoomDatabase() {
Expand Down
1 change: 1 addition & 0 deletions feature/metis-test/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ dependencies {
api(libs.koin.test.junit4)
api(libs.robolectric)
api(libs.koin.android.test)
api(libs.androidx.paging.testing)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package de.tum.informatics.www1.artemis.native_app.feature.metistest

import android.annotation.SuppressLint
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingSource
import androidx.paging.testing.asSnapshot


@SuppressLint("VisibleForTests")
suspend fun <T : Any> PagingSource<Int, T>.loadAsList(): List<T> {
return Pager(PagingConfig(pageSize = 10), pagingSourceFactory = { this }).flow.asSnapshot {
scrollTo(50)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ internal fun PostContextBottomSheet(
Column(
modifier = Modifier
.fillMaxWidth()
.padding(start = Spacings.ScreenHorizontalSpacing, end = Spacings.ScreenHorizontalSpacing, bottom = 40.dp)
.padding(horizontal = Spacings.ScreenHorizontalSpacing)
.padding(bottom = 40.dp)
) {
postActions.onClickReaction?.let { onClickReaction ->
EmojiReactionBar(
Expand Down
1 change: 1 addition & 0 deletions feature/metis/shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ android {
}
dependencies {
implementation(project(":core:device"))
testImplementation(project(":feature:metis-test"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@ import androidx.room.Index
@Entity(
tableName = "reactions",
primaryKeys = ["post_id", "emoji", "author_id", "server_id"],
foreignKeys = [
ForeignKey(
entity = BasePostingEntity::class,
parentColumns = ["id"],
childColumns = ["post_id"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = MetisUserEntity::class,
parentColumns = ["server_id", "id"],
childColumns = ["server_id", "author_id"]
)
],
indices = [Index("server_id", "author_id", name = "server_id_author_id_index")]
)
data class PostReactionEntity(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package de.tum.informatics.www1.artemis.native_app.feature.metis.shared.db

import androidx.test.platform.app.InstrumentationRegistry
import de.tum.informatics.www1.artemis.native_app.core.common.test.UnitTest
import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.UserRole
import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.db.entities.BasePostingEntity
import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.db.entities.MetisPostContextEntity
import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.db.entities.MetisUserEntity
import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.db.entities.PostReactionEntity
import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.db.entities.StandalonePostingEntity
import de.tum.informatics.www1.artemis.native_app.feature.metistest.MetisDatabaseProviderMock
import de.tum.informatics.www1.artemis.native_app.feature.metistest.MetisTestDatabase
import de.tum.informatics.www1.artemis.native_app.feature.metistest.loadAsList
import kotlinx.coroutines.runBlocking
import kotlinx.datetime.Clock
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.experimental.categories.Category
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
@Category(UnitTest::class)
class MetisDaoTest {

private val serverId = "host"
private val courseId = 1L
private val conversationId = 1L
private val clientPostId = "clientPostId"

private val user = MetisUserEntity(
serverId = serverId,
id = 4,
displayName = "User4"
)
private val basePost = BasePostingEntity(
postId = clientPostId,
serverId = serverId,
postingType = BasePostingEntity.PostingType.STANDALONE,
authorId = user.id,
creationDate = Clock.System.now(),
updatedDate = Clock.System.now(),
content = "post content",
authorRole = UserRole.USER,
)
private val metisContext = MetisPostContextEntity(
serverId = serverId,
courseId = courseId,
conversationId = conversationId,
serverPostId = 1,
clientPostId = clientPostId,
postingType = BasePostingEntity.PostingType.STANDALONE,
)
private val post = StandalonePostingEntity(
postId = clientPostId,
title = null,
context = null,
displayPriority = BasePostingEntity.DisplayPriority.NONE,
resolved = false,
liveCreated = false
)
private val reaction = PostReactionEntity(
postId = clientPostId,
authorId = user.id,
serverId = serverId,
emojiId = "emojiId",
id = 1,
)

private lateinit var database: MetisTestDatabase
private lateinit var metisDao: MetisDao

@Before
fun setup() {
val databaseProviderMock = MetisDatabaseProviderMock(InstrumentationRegistry.getInstrumentation().context)
database = databaseProviderMock.database
metisDao = database.metisDao()
}

@After
fun teardown() {
database.close()
}

@Test
fun testAddPost() = runBlocking {
// GIVEN: A inserted post
insertStandalonePost()

// WHEN: Querying the post
val storedPosts = metisDao.queryCoursePosts(
courseId = courseId,
conversationId = conversationId,
serverId = serverId,
).loadAsList()

// THEN: Return post with correct userId and clientPostId
assertEquals(1, storedPosts.size)
val storedPost = storedPosts[0]
assertEquals(user.id, storedPost.authorId)
assertEquals(clientPostId, storedPost.clientPostId)
}

@Test
fun testDeletePost() = runBlocking {
// GIVEN: A inserted post
insertStandalonePost()

// WHEN: Deleting the post
metisDao.deletePostingWithClientSideId(clientPostId)

// THEN: The post is deleted in both tables
database.query("SELECT * FROM standalone_postings", args = null).use {
assertEquals(0, it.count)
}
database.query("SELECT * FROM postings", args = null).use {
assertEquals(0, it.count)
}

// AND: The post context is deleted
database.query("SELECT * FROM metis_post_context", args = null).use {
assertEquals(0, it.count)
}
}

@Test
fun testAddPostWithReaction() = runBlocking {
// GIVEN: A inserted post with a reaction
insertStandalonePost()
metisDao.insertReactions(listOf(reaction))

// WHEN: Querying the post
val storedPost = metisDao.queryCoursePosts(
courseId = courseId,
conversationId = conversationId,
serverId = serverId,
).loadAsList()[0]

// THEN: The reaction is stored
assertEquals(1, storedPost.reactions.size)
assertEquals(reaction.emojiId, storedPost.reactions[0].emojiId)
}

@Test
fun testDeletePostWithReaction() = runBlocking {
// GIVEN: A inserted post with a reaction
insertStandalonePost()
metisDao.insertReactions(listOf(reaction))

// WHEN: Deleting the post
metisDao.deletePostingWithClientSideId(clientPostId)

// THEN: The reaction is deleted
database.query("SELECT * FROM reactions", args = null).use {
assertEquals(0, it.count)
}
}


private suspend fun insertStandalonePost() {
metisDao.insertUser(user)
metisDao.insertBasePost(basePost)
metisDao.insertPost(post)
metisDao.insertPostMetisContext(metisContext)
}
}

0 comments on commit 8396c11

Please sign in to comment.