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

grain.social/grain-android

1 collaborator
0
1
main
142 lines · 4.3 KB Raw History
package social.grain

import android.net.Uri
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test
import org.junit.runner.RunWith
import social.grain.navigation.DeepLink

/**
 * The manifest claims `https://grain.social/profile/...`, so every link the
 * system hands over has to resolve to something — a claimed link that lands on
 * the feed is worse than one that opened the browser.
 *
 * Instrumented rather than a plain unit test because `Uri` is stubbed out on
 * the JVM and every parse would return null for the wrong reason.
 */
@RunWith(AndroidJUnit4::class)
class DeepLinkTest {

    private fun parse(url: String) = DeepLink.from(Uri.parse(url))

    @Test
    fun profileLinkResolvesToTheProfile() {
        assertEquals(
            DeepLink.Profile("did:plc:abc"),
            parse("https://grain.social/profile/did:plc:abc"),
        )
    }

    /** Shared links usually name the handle, not the DID. */
    @Test
    fun profileLinkAcceptsAHandle() {
        assertEquals(
            DeepLink.Profile("alice.grain.social"),
            parse("https://grain.social/profile/alice.grain.social"),
        )
    }

    @Test
    fun galleryLinkCarriesTheAtUri() {
        val link = parse("https://grain.social/profile/did:plc:abc/gallery/3kabc")
        assertEquals(DeepLink.Gallery("did:plc:abc", "3kabc"), link)
        assertEquals(
            "at://did:plc:abc/social.grain.gallery/3kabc",
            (link as DeepLink.Gallery).uri,
        )
    }

    @Test
    fun storyLinkResolvesToTheAuthorsReel() {
        assertEquals(
            DeepLink.Story("did:plc:abc", "3kxyz"),
            parse("https://grain.social/profile/did:plc:abc/story/3kxyz"),
        )
    }

    /** A trailing slash is what a share sheet hands over half the time. */
    @Test
    fun trailingSlashesAreIgnored() {
        assertEquals(
            DeepLink.Profile("did:plc:abc"),
            parse("https://grain.social/profile/did:plc:abc/"),
        )
    }

    @Test
    fun grainSchemeUsesTheHostAsTheFirstSegment() {
        assertEquals(
            DeepLink.Gallery("did:plc:abc", "3kabc"),
            parse("grain://profile/did:plc:abc/gallery/3kabc"),
        )
    }

    /** MainActivity handles this one before the parser sees it; it must not be
     *  mistaken for a profile either way. */
    @Test
    fun oauthCallbackIsNotADeepLink() {
        assertNull(parse("grain://oauth/callback?code=abc"))
    }

    @Test
    fun unknownPathsAreRejectedRatherThanFallingBackToTheFeed() {
        assertNull(parse("https://grain.social/about"))
        assertNull(parse("https://grain.social/"))
        assertNull(parse("https://grain.social/profile"))
        assertNull(parse("https://example.com/profile/did:plc:abc"))
    }

    /** A sub-path we don't recognise still names an account, so the profile is
     *  a better answer than dropping the link. */
    @Test
    fun unknownSubPathFallsBackToTheProfile() {
        assertEquals(
            DeepLink.Profile("did:plc:abc"),
            parse("https://grain.social/profile/did:plc:abc/photos/3kabc"),
        )
    }

    // MARK: - Push payloads

    @Test
    fun followPushOpensTheFollowersProfile() {
        assertEquals(
            DeepLink.Profile("did:plc:abc"),
            DeepLink.fromPush(type = "follow", uri = null, did = "did:plc:abc"),
        )
    }

    @Test
    fun galleryFavoritePushOpensTheGallery() {
        assertEquals(
            DeepLink.Gallery("did:plc:abc", "3kabc"),
            DeepLink.fromPush(
                type = "gallery-favorite",
                uri = "at://did:plc:abc/social.grain.gallery/3kabc",
                did = null,
            ),
        )
    }

    /** A comment names itself, not the gallery it hangs off, so the author's
     *  profile is the honest destination. */
    @Test
    fun commentReplyPushFallsBackToTheAuthor() {
        assertEquals(
            DeepLink.Profile("did:plc:abc"),
            DeepLink.fromPush(
                type = "comment-reply",
                uri = "at://did:plc:abc/social.grain.comment/3kabc",
                did = null,
            ),
        )
    }

    @Test
    fun payloadWithNoTypeIsIgnored() {
        assertNull(DeepLink.fromPush(type = null, uri = null, did = null))
        assertNull(DeepLink.fromPush(type = "follow", uri = null, did = null))
    }
}