From: David ‘Bombe’ Roden Date: Thu, 6 Aug 2020 20:03:35 +0000 (+0200) Subject: 🎨 Store shells instead of full posts in in-memory database X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=commitdiff_plain;h=2a9ef0e07136d8c56f4eea32ed49e43bfd5e576f 🎨 Store shells instead of full posts in in-memory database --- diff --git a/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt index 589d977..e2281ac 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt @@ -62,8 +62,8 @@ class MemoryDatabase @Inject constructor(private val configuration: Configuratio private val writeLock: WriteLock by lazy { lock.writeLock() } private val configurationLoader = ConfigurationLoader(configuration) private val allSones = mutableMapOf() - private val allPosts = mutableMapOf() - private val sonePosts: Multimap = HashMultimap.create() + private val allPosts = mutableMapOf() + private val sonePosts: Multimap = HashMultimap.create() private val knownPosts = mutableSetOf() private val allPostReplies = mutableMapOf() private val sonePostReplies: Multimap = TreeMultimap.create(Comparator { leftString, rightString -> leftString.compareTo(rightString) }, newestReplyFirst) @@ -119,8 +119,8 @@ class MemoryDatabase @Inject constructor(private val configuration: Configuratio removeSone(sone) allSones[sone.id] = sone - sonePosts.putAll(sone.id, sone.posts) - for (post in sone.posts) { + sonePosts.putAll(sone.id, sone.posts.map(Post::toShell)) + for (post in sone.posts.map(Post::toShell)) { allPosts[post.id] = post } sonePostReplies.putAll(sone.id, sone.replies) @@ -193,17 +193,17 @@ class MemoryDatabase @Inject constructor(private val configuration: Configuratio override fun getFollowingTime(friendSoneId: String) = memoryFriendDatabase.getFollowingTime(friendSoneId) - override fun getPost(postId: String) = - readLock.withLock { allPosts[postId] } + override fun getPost(postId: String): Post? = + readLock.withLock { allPosts[postId]?.build(newPostBuilder()) } override fun getPosts(soneId: String): Collection = - sonePosts[soneId].toSet() + sonePosts[soneId].map { it.build(newPostBuilder()) }.toSet() override fun getDirectedPosts(recipientId: String) = readLock.withLock { - allPosts.values.filter { - it.recipientId.orNull() == recipientId - } + allPosts.values + .filter { it.recipientId == recipientId } + .map { it.build(newPostBuilder()) } } override fun newPostBuilder(): PostBuilder = MemoryPostBuilder(this, this) @@ -211,8 +211,10 @@ class MemoryDatabase @Inject constructor(private val configuration: Configuratio override fun storePost(post: Post) { checkNotNull(post, "post must not be null") writeLock.withLock { - allPosts[post.id] = post - sonePosts[post.sone.id].add(post) + post.toShell().also { shell -> + allPosts[post.id] = shell + sonePosts[post.sone.id].add(shell) + } } } @@ -220,7 +222,7 @@ class MemoryDatabase @Inject constructor(private val configuration: Configuratio checkNotNull(post, "post must not be null") writeLock.withLock { allPosts.remove(post.id) - sonePosts[post.sone.id].remove(post) + sonePosts[post.sone.id].remove(post.toShell()) post.sone.removePost(post) } } diff --git a/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryPost.kt b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryPost.kt index c435084..b3664c2 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryPost.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryPost.kt @@ -18,6 +18,7 @@ package net.pterodactylus.sone.database.memory import net.pterodactylus.sone.data.Post import net.pterodactylus.sone.data.Sone +import net.pterodactylus.sone.database.PostBuilder import net.pterodactylus.sone.database.SoneProvider import net.pterodactylus.sone.utils.asOptional @@ -25,7 +26,7 @@ import net.pterodactylus.sone.utils.asOptional * A post is a short message that a user writes in his Sone to let other users * know what is going on. */ -internal class MemoryPost( +class MemoryPost( private val postDatabase: MemoryDatabase, private val soneProvider: SoneProvider, override val id: String, @@ -59,4 +60,13 @@ internal class MemoryPost( override fun toString() = "${javaClass.name}[id=$id,sone=$soneId,recipient=$recipientId,time=$time,text=$text]" + data class Shell(val id: String, val soneId: String, val recipientId: String?, val time: Long, val text: String) { + + fun build(postBuilder: PostBuilder) = + postBuilder.withId(id).from(soneId).let { if (recipientId != null) it.to(recipientId) else it }.withTime(time).withText(text).build() + + } + } + +fun Post.toShell() = MemoryPost.Shell(id, sone!!.id, recipient.orNull()?.id, time, text) diff --git a/src/test/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabaseTest.kt b/src/test/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabaseTest.kt index 6e9b31a..caacb68 100644 --- a/src/test/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabaseTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabaseTest.kt @@ -177,26 +177,18 @@ class MemoryDatabaseTest { @Test fun `post recipients are detected correctly`() { - val postWithRecipient = createPost(of(RECIPIENT_ID)) + val postWithRecipient = createPost(id = "p1", recipient = createRemoteSone(RECIPIENT_ID)) memoryDatabase.storePost(postWithRecipient) - val postWithoutRecipient = createPost(absent()) + val postWithoutRecipient = createPost(id = "p2", recipient = null) memoryDatabase.storePost(postWithoutRecipient) - assertThat(memoryDatabase.getDirectedPosts(RECIPIENT_ID), contains(postWithRecipient)) - } - - private fun createPost(recipient: Optional): Post { - val postWithRecipient = mock() - whenever(postWithRecipient.id).thenReturn(randomUUID().toString()) - whenever(postWithRecipient.sone).thenReturn(sone) - whenever(postWithRecipient.recipientId).thenReturn(recipient) - return postWithRecipient + assertThat(memoryDatabase.getDirectedPosts(RECIPIENT_ID), contains(isPost(isRecipientId = equalTo(RECIPIENT_ID)))) } @Test fun `post replies are managed correctly`() { - val firstPost = createPost(absent()) + val firstPost = createPost() val firstPostFirstReply = createPostReply(id = "p1r1", post = firstPost, time = 1000L) - val secondPost = createPost(absent()) + val secondPost = createPost() val secondPostFirstReply = createPostReply(id = "p2r1", post = secondPost, time = 1000L) val secondPostSecondReply = createPostReply(id = "p2r2", post = secondPost, time = 2000L) memoryDatabase.storePost(firstPost) diff --git a/src/test/kotlin/net/pterodactylus/sone/test/Matchers.kt b/src/test/kotlin/net/pterodactylus/sone/test/Matchers.kt index 1f479f6..ecb2049 100644 --- a/src/test/kotlin/net/pterodactylus/sone/test/Matchers.kt +++ b/src/test/kotlin/net/pterodactylus/sone/test/Matchers.kt @@ -1,6 +1,7 @@ package net.pterodactylus.sone.test import freenet.support.* +import net.pterodactylus.sone.data.Post import net.pterodactylus.sone.freenet.wot.* import net.pterodactylus.sone.utils.* import net.pterodactylus.util.web.* @@ -106,6 +107,9 @@ fun hasField(name: String, valueMatcher: Matcher) = object : TypeSafeDia } } +fun isPost(isRecipientId: Matcher = any(String::class.java)) = AttributeMatcher("post") + .addAttribute("recipient ID", { it.recipientId.orNull() }, isRecipientId) + /** * [TypeSafeDiagnosingMatcher] implementation that aims to cut down boilerplate on verifying the attributes * of typical container objects. diff --git a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt index e90c080..54307a8 100644 --- a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt +++ b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt @@ -34,6 +34,7 @@ import net.pterodactylus.sone.freenet.wot.Identity import net.pterodactylus.sone.freenet.wot.OwnIdentity import net.pterodactylus.sone.utils.asFreenetBase64 import net.pterodactylus.sone.utils.asOptional +import java.util.UUID val remoteSone1 = createRemoteSone() val remoteSone2 = createRemoteSone() @@ -71,8 +72,8 @@ fun createRemoteSone(id: String = createId(), identity: Identity = createIdentit override fun getIdentity(): Identity = identity } -fun createPost(text: String = "", sone: Sone? = remoteSone1, known: Boolean = false, time: Long = 1, loaded: Boolean = true, recipient: Sone? = null): Post { - return object : Post.EmptyPost("post-id") { +fun createPost(text: String = "text", sone: Sone? = remoteSone1, known: Boolean = false, time: Long = 1, loaded: Boolean = true, recipient: Sone? = null, id: String = UUID.randomUUID().toString()): Post { + return object : Post.EmptyPost(id) { override fun getRecipientId() = recipient?.id.asOptional() override fun getRecipient() = recipient.asOptional() override fun getSone() = sone diff --git a/src/test/kotlin/net/pterodactylus/sone/test/TestPostBuilder.kt b/src/test/kotlin/net/pterodactylus/sone/test/TestPostBuilder.kt index 4c9efe7..6443cb2 100644 --- a/src/test/kotlin/net/pterodactylus/sone/test/TestPostBuilder.kt +++ b/src/test/kotlin/net/pterodactylus/sone/test/TestPostBuilder.kt @@ -1,8 +1,6 @@ package net.pterodactylus.sone.test -import com.google.common.base.Optional import net.pterodactylus.sone.data.Post -import net.pterodactylus.sone.data.Sone import net.pterodactylus.sone.database.PostBuilder import java.util.UUID @@ -11,44 +9,43 @@ import java.util.UUID */ class TestPostBuilder : PostBuilder { - private val post = mock() + private var id: String? = null + private var soneId: String? = null private var recipientId: String? = null + private var time: Long? = null + private var text: String? = null override fun copyPost(post: Post): PostBuilder = this override fun from(senderId: String): PostBuilder = apply { - val sone = mock() - whenever(sone.id).thenReturn(senderId) - whenever(post.sone).thenReturn(sone) + soneId = senderId } override fun randomId(): PostBuilder = apply { - whenever(post.id).thenReturn(UUID.randomUUID().toString()) + id = UUID.randomUUID().toString() } override fun withId(id: String): PostBuilder = apply { - whenever(post.id).thenReturn(id) + this.id = id } override fun currentTime(): PostBuilder = apply { - whenever(post.time).thenReturn(System.currentTimeMillis()) + time = System.currentTimeMillis() } override fun withTime(time: Long): PostBuilder = apply { - whenever(post.time).thenReturn(time) + this.time = time } override fun withText(text: String): PostBuilder = apply { - whenever(post.text).thenReturn(text) + this.text = text } override fun to(recipientId: String): PostBuilder = apply { this.recipientId = recipientId } - override fun build(): Post = post - .also { - whenever(post.recipientId).thenReturn(Optional.fromNullable(recipientId)) - } + override fun build(): Post = + createPost(text!!, sone = createRemoteSone(soneId!!), time = time!!, recipient = recipientId?.let { createRemoteSone(it) }, id = id!!) }