~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain-android

1 collaborator
0
1
main
79 lines · 3.1 KB Raw History
package social.grain

import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.Json
import okhttp3.OkHttpClient
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import social.grain.data.api.XrpcClient
import social.grain.data.api.getCommentThread
import social.grain.data.api.getFeed
import social.grain.data.api.getGallery
import social.grain.data.auth.AuthManager
import java.util.concurrent.TimeUnit

/**
 * Decodes live gallery-detail and comment responses into the ported models.
 *
 * Same class of risk the feed models carried: a field typed wrong here shows up
 * only as an empty comment sheet with a decode error buried in the log.
 */
@RunWith(AndroidJUnit4::class)
class CommentDecodeTest {

    private val client = XrpcClient(
        baseUrl = AuthManager.SERVER_URL,
        http = OkHttpClient.Builder()
            .connectTimeout(20, TimeUnit.SECONDS)
            .readTimeout(20, TimeUnit.SECONDS)
            .build(),
        json = Json {
            ignoreUnknownKeys = true
            explicitNulls = false
            coerceInputValues = true
        },
    )

    @Test
    fun galleryDetailDecodesForARealGallery() = runBlocking {
        val uri = client.getFeed(feed = "recent", limit = 1).items.orEmpty().first().uri

        val gallery = client.getGallery(uri).gallery
        assertEquals(uri, gallery.uri)
        assertTrue("Creator DID missing", gallery.creator.did.startsWith("did:"))
        assertTrue("Detail returned no photos", gallery.items.orEmpty().isNotEmpty())
    }

    @Test
    fun commentThreadDecodesAndRepliesNameTheirRoot() = runBlocking {
        // Walk the feed for a gallery that actually has comments rather than
        // asserting against whatever happens to be newest.
        val withComments = client.getFeed(feed = "recent", limit = 60).items.orEmpty()
            .firstOrNull { (it.commentCount ?: 0) > 0 }

        if (withComments == null) {
            // Nothing to assert against; still exercise the decode path so a
            // broken response type fails rather than silently skipping.
            client.getCommentThread(client.getFeed(feed = "recent", limit = 1).items!!.first().uri)
            return@runBlocking
        }

        val thread = client.getCommentThread(withComments.uri)
        assertTrue("Gallery reported comments but the thread was empty", thread.comments.isNotEmpty())

        val uris = thread.comments.map { it.uri }.toSet()
        for (comment in thread.comments) {
            assertTrue("Comment URI should be an at:// URI", comment.uri.startsWith("at://"))
            assertTrue("Comment author DID missing", comment.author.did.startsWith("did:"))
            comment.replyTo?.let {
                assertTrue("replyTo should be an at:// URI, was '$it'", it.startsWith("at://"))
            }
            // Exercise the facet path against whatever real records contain.
            comment.facets?.forEach { it.parsedFeatures }
        }
        assertEquals("Comment URIs should be unique", thread.comments.size, uris.size)
    }
}