Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775package social.grain
import kotlinx.coroutines.test.runTestimport kotlinx.serialization.json.JsonObjectimport kotlinx.serialization.json.buildJsonObjectimport kotlinx.serialization.json.jsonArrayimport kotlinx.serialization.json.jsonObjectimport kotlinx.serialization.json.jsonPrimitiveimport kotlinx.serialization.json.putimport org.junit.Assert.assertEqualsimport org.junit.Assert.assertNullimport org.junit.Assert.assertTrueimport org.junit.Testimport org.junit.runner.RunWithimport androidx.test.ext.junit.runners.AndroidJUnit4import social.grain.data.api.ApplyWriteimport social.grain.data.api.BlockServiceimport social.grain.data.api.CommentServiceimport social.grain.data.api.FavoriteServiceimport social.grain.data.api.FollowServiceimport social.grain.data.api.GalleryServiceimport social.grain.data.api.PinnedFeedimport social.grain.data.api.ReportSubjectimport social.grain.data.api.StoryServiceimport social.grain.data.api.applyWritesimport social.grain.data.api.createReportimport social.grain.data.api.createRecordimport social.grain.data.api.deleteAccountimport social.grain.data.api.deleteGalleryimport social.grain.data.api.deleteRecordimport social.grain.data.api.describeLabelsimport social.grain.data.api.getActorFavoritesimport social.grain.data.api.getActorProfileimport social.grain.data.api.getBlocksimport social.grain.data.api.getCamerasimport social.grain.data.api.getCommentThreadimport social.grain.data.api.getFeedimport social.grain.data.api.getFollowersimport social.grain.data.api.getFollowingimport social.grain.data.api.getGalleryimport social.grain.data.api.getGalleryFavoritesimport social.grain.data.api.getKnownFollowersimport social.grain.data.api.getLocationsimport social.grain.data.api.getMutesimport social.grain.data.api.getNotificationsimport social.grain.data.api.getPreferencesimport social.grain.data.api.getRecordimport social.grain.data.api.getStoriesimport social.grain.data.api.getStoryimport social.grain.data.api.getStoryArchiveimport social.grain.data.api.getStoryAuthorsimport social.grain.data.api.markNotificationsSeenimport social.grain.data.api.muteActorimport social.grain.data.api.putPreferenceimport social.grain.data.api.putRecordimport social.grain.data.api.registerPushTokenimport social.grain.data.api.searchActorsTypeaheadimport social.grain.data.api.searchGalleriesimport social.grain.data.api.searchProfilesimport social.grain.data.api.unmuteActorimport social.grain.data.api.unregisterPushTokenimport social.grain.data.model.BlobRefimport social.grain.support.Fixturesimport social.grain.support.StubServerimport social.grain.support.jsonimport social.grain.support.testAuthimport social.grain.support.xrpc
/** * Every call the app can make to the appview, exercised against a stub. * * These functions are one-liners each, which is exactly why they are worth * pinning: a query parameter named wrong or a body key spelled differently * compiles fine and fails only as an empty screen against the real server. The * assertions are on the wire format — NSID, method, parameters, body — because * that is the whole content of the layer. */@RunWith(AndroidJUnit4::class)class EndpointsTest {
private val server = StubServer() private val client = server.xrpc() private val auth = testAuth()
private fun body(nsid: String): JsonObject = server.lastCall(nsid).json
// MARK: - Feed
@Test fun getFeedSendsEveryOptionalFilterItWasGiven() = runTest { server.on( "dev.hatk.getFeed", """{"items":[${json(Fixtures.gallery())}],"cursor":"page2"}""", ) val response = client.getFeed( feed = "camera", limit = 15, cursor = "page1", actor = "did:plc:alice", camera = "Fujifilm X100V", location = "8928308280fffff", name = "Mission District, San Francisco, US", tag = "fog", )
assertEquals(1, response.items?.size) assertEquals("page2", response.cursor)
val call = server.lastCall("dev.hatk.getFeed") assertEquals("GET", call.method) assertEquals( mapOf( "feed" to "camera", "limit" to "15", "cursor" to "page1", "actor" to "did:plc:alice", "camera" to "Fujifilm X100V", "location" to "8928308280fffff", "name" to "Mission District, San Francisco, US", "tag" to "fog", ), call.params, ) }
@Test fun getFeedOmitsFiltersThatWereNotSet() = runTest { server.on("dev.hatk.getFeed", """{"items":[]}""") client.getFeed(feed = "recent")
assertEquals( mapOf("feed" to "recent", "limit" to "30"), server.lastCall("dev.hatk.getFeed").params, ) }
@Test fun getGalleryAsksForOneGalleryByUri() = runTest { val gallery = Fixtures.gallery() server.on("social.grain.unspecced.getGallery", """{"gallery":${json(gallery)}}""")
val response = client.getGallery(gallery.uri)
assertEquals(gallery.uri, response.gallery.uri) assertEquals( mapOf("gallery" to gallery.uri), server.lastCall("social.grain.unspecced.getGallery").params, ) }
@Test fun getCommentThreadPagesBySubject() = runTest { server.on( "social.grain.unspecced.getCommentThread", """{"comments":[${json(Fixtures.comment())}],"totalCount":9}""", ) val response = client.getCommentThread("at://x/gallery/1", limit = 5, cursor = "c")
assertEquals(9, response.totalCount) assertEquals( mapOf("subject" to "at://x/gallery/1", "limit" to "5", "cursor" to "c"), server.lastCall("social.grain.unspecced.getCommentThread").params, ) }
@Test fun getActorFavoritesReadsBackAFeedPage() = runTest { server.on("social.grain.unspecced.getActorFavorites", """{"items":[],"cursor":null}""") client.getActorFavorites("did:plc:alice", limit = 7)
assertEquals( mapOf("actor" to "did:plc:alice", "limit" to "7"), server.lastCall("social.grain.unspecced.getActorFavorites").params, ) }
@Test fun getPreferencesDecodesPinnedFeeds() = runTest { server.on( "dev.hatk.getPreferences", """{"preferences":{"pinnedFeeds":[{"id":"camera:Leica M6","label":"Leica M6",""" + """"type":"camera","path":"/cameras/leica"}],"includeExif":true}}""", ) val preferences = client.getPreferences().preferences
assertEquals(true, preferences.includeExif) val pin = preferences.pinnedFeeds!!.single() assertEquals("camera", pin.feedName) assertEquals("Leica M6", pin.feedValue) }
@Test fun searchActorsTypeaheadRunsUnauthenticated() = runTest { server.on( "social.grain.unspecced.searchActorsTypeahead", """{"actors":[{"did":"did:plc:alice","handle":"alice.grain.social"}]}""", ) val response = client.searchActorsTypeahead("ali", limit = 3)
assertEquals("alice.grain.social", response.actors.single().handle) val call = server.lastCall("social.grain.unspecced.searchActorsTypeahead") assertEquals(mapOf("q" to "ali", "limit" to "3"), call.params) assertNull("Typeahead must work before there is a session", call.authorization) }
// MARK: - Notifications
@Test fun getNotificationsSendsCountOnlyOnlyWhenAsked() = runTest { server.on( "social.grain.unspecced.getNotifications", """{"notifications":[],"unseenCount":4}""", ) client.getNotifications(limit = 1, countOnly = true, auth = auth) assertEquals( mapOf("limit" to "1", "countOnly" to "true"), server.lastCall("social.grain.unspecced.getNotifications").params, )
client.getNotifications(limit = 100, cursor = "c", auth = auth) assertEquals( mapOf("limit" to "100", "cursor" to "c"), server.lastCall("social.grain.unspecced.getNotifications").params, ) }
@Test fun markingNotificationsSeenWritesATimestampPreference() = runTest { client.markNotificationsSeen(auth)
val sent = body("dev.hatk.putPreference") assertEquals("lastSeenNotifications", sent["key"]!!.jsonPrimitive.content) assertTrue( "The value should be an ISO timestamp", sent["value"]!!.jsonPrimitive.content.endsWith("Z"), ) }
// MARK: - Stories
@Test fun storyEndpointsAddressStoriesByActorAndUri() = runTest { server.on( "social.grain.unspecced.getStories", """{"stories":[${json(Fixtures.story())}]}""", ) server.on("social.grain.unspecced.getStory", """{"story":${json(Fixtures.story())}}""") server.on("social.grain.unspecced.getStoryArchive", """{"stories":[],"cursor":"c2"}""") server.on( "social.grain.unspecced.getStoryAuthors", """{"authors":[${json(Fixtures.storyAuthor())}]}""", )
assertEquals(1, client.getStories("did:plc:alice").stories.size) assertEquals( mapOf("actor" to "did:plc:alice"), server.lastCall("social.grain.unspecced.getStories").params, )
assertEquals("story1", client.getStory("at://x/story/1").story!!.rkey) assertEquals( mapOf("story" to "at://x/story/1"), server.lastCall("social.grain.unspecced.getStory").params, )
assertEquals("c2", client.getStoryArchive("did:plc:alice", limit = 4, cursor = "c1").cursor) assertEquals( mapOf("actor" to "did:plc:alice", "limit" to "4", "cursor" to "c1"), server.lastCall("social.grain.unspecced.getStoryArchive").params, )
assertEquals(1, client.getStoryAuthors().authors.size) assertTrue(server.lastCall("social.grain.unspecced.getStoryAuthors").params.isEmpty()) }
// MARK: - Search
@Test fun searchGalleriesCarriesTheFuzzyFlag() = runTest { server.on("social.grain.unspecced.searchGalleries", """{"items":[],"cursor":null}""") client.searchGalleries("fog", limit = 12, cursor = "c", fuzzy = false)
assertEquals( mapOf("q" to "fog", "limit" to "12", "fuzzy" to "false", "cursor" to "c"), server.lastCall("social.grain.unspecced.searchGalleries").params, ) }
@Test fun searchProfilesDecodesActorListItems() = runTest { server.on( "social.grain.unspecced.searchProfiles", """{"items":[{"did":"did:plc:bob","handle":"bob.grain.social"}]}""", ) val response = client.searchProfiles("bob")
assertEquals("did:plc:bob", response.items!!.single().did) assertEquals( mapOf("q" to "bob", "limit" to "30"), server.lastCall("social.grain.unspecced.searchProfiles").params, ) }
// MARK: - Discovery
@Test fun getCamerasDecodesTheIndexWithItsThumbnails() = runTest { server.on( "social.grain.unspecced.getCameras", """{"cameras":[{"camera":"Leica M6","photoCount":120,"thumbs":["https://cdn.grain.test/thumb/1.jpg"]}]}""", ) val response = client.getCameras()
val camera = response.cameras!!.single() assertEquals("Leica M6", camera.camera) assertEquals(120, camera.photoCount) assertEquals(listOf("https://cdn.grain.test/thumb/1.jpg"), camera.thumbs) assertTrue(server.lastCall("social.grain.unspecced.getCameras").params.isEmpty()) }
@Test fun getLocationsAsksForPinsOnlyWhenToldTo() = runTest { server.on( "social.grain.unspecced.getLocations", """{"locations":[{"name":"Lisbon, PT","h3Index":"85390cb7fffffff","galleryCount":3}]}""", ) val full = client.getLocations() assertEquals("Lisbon, PT", full.locations!!.single().name) assertEquals("85390cb7fffffff", full.locations!!.single().h3Index) assertTrue(full.locations!!.single().thumbs.isEmpty()) assertTrue(server.lastCall("social.grain.unspecced.getLocations").params.isEmpty())
client.getLocations(pins = true) assertEquals( mapOf("pins" to "true"), server.lastCall("social.grain.unspecced.getLocations").params, ) }
// MARK: - Profile
@Test fun getActorProfileSendsTheViewerOnlyWhenSignedIn() = runTest { server.on( "social.grain.unspecced.getActorProfile", json(Fixtures.profileDetailed(following = "at://follow/1")), )
val signedOut = client.getActorProfile("alice.grain.social") assertEquals("did:plc:alice", signedOut.did) assertEquals( mapOf("actor" to "alice.grain.social"), server.lastCall("social.grain.unspecced.getActorProfile").params, )
client.getActorProfile("alice.grain.social", viewer = "did:plc:me", auth = auth) assertEquals( mapOf("actor" to "alice.grain.social", "viewer" to "did:plc:me"), server.lastCall("social.grain.unspecced.getActorProfile").params, ) }
@Test fun followerAndFollowingListsPaginate() = runTest { server.on("social.grain.unspecced.getFollowers", """{"totalCount":3,"items":[]}""") server.on("social.grain.unspecced.getFollowing", """{"totalCount":4,"items":[]}""")
assertEquals(3, client.getFollowers("did:plc:alice", viewer = "did:plc:me", cursor = "c").totalCount) assertEquals( mapOf( "actor" to "did:plc:alice", "limit" to "50", "viewer" to "did:plc:me", "cursor" to "c", ), server.lastCall("social.grain.unspecced.getFollowers").params, )
assertEquals(4, client.getFollowing("did:plc:alice", limit = 10).totalCount) assertEquals( mapOf("actor" to "did:plc:alice", "limit" to "10"), server.lastCall("social.grain.unspecced.getFollowing").params, ) }
@Test fun galleryFavoritesAndKnownFollowersAddressTheirSubject() = runTest { server.on("social.grain.unspecced.getGalleryFavorites", """{"totalCount":8,"items":[]}""") server.on("social.grain.unspecced.getKnownFollowers", """{"items":[]}""")
assertEquals(8, client.getGalleryFavorites("at://x/gallery/1").totalCount) assertEquals( mapOf("gallery" to "at://x/gallery/1", "limit" to "50"), server.lastCall("social.grain.unspecced.getGalleryFavorites").params, )
client.getKnownFollowers("did:plc:alice", "did:plc:me") assertEquals( mapOf("actor" to "did:plc:alice", "viewer" to "did:plc:me"), server.lastCall("social.grain.unspecced.getKnownFollowers").params, ) }
// MARK: - Records
@Test fun getRecordAddressesARecordByUri() = runTest { server.on("dev.hatk.getRecord", """{"uri":"at://x/y/1","cid":"cid1","record":{"a":1}}""") val response = client.getRecord("at://x/y/1")
assertEquals("cid1", response.cid) assertEquals(mapOf("uri" to "at://x/y/1"), server.lastCall("dev.hatk.getRecord").params) }
@Test fun createRecordPostsCollectionRepoAndRecord() = runTest { server.on("dev.hatk.createRecord", """{"uri":"at://new","cid":"cid-new"}""") val response = client.createRecord( collection = "social.grain.favorite", repo = "did:plc:me", record = buildJsonObject { put("subject", "at://x") }, auth = auth, )
assertEquals("at://new", response.uri) val sent = body("dev.hatk.createRecord") assertEquals("social.grain.favorite", sent["collection"]!!.jsonPrimitive.content) assertEquals("did:plc:me", sent["repo"]!!.jsonPrimitive.content) assertEquals("at://x", sent["record"]!!.jsonObject["subject"]!!.jsonPrimitive.content) assertEquals("POST", server.lastCall("dev.hatk.createRecord").method) }
@Test fun putRecordCarriesTheRkeyAndOmitsAnAbsentRepo() = runTest { server.on("dev.hatk.putRecord", """{"uri":"at://put","cid":"cid"}""")
client.putRecord("social.grain.actor.profile", "self", buildJsonObject { }, auth = auth) assertNull(body("dev.hatk.putRecord")["repo"])
client.putRecord( "social.grain.actor.profile", "self", buildJsonObject { }, repo = "did:plc:me", auth = auth, ) val sent = body("dev.hatk.putRecord") assertEquals("self", sent["rkey"]!!.jsonPrimitive.content) assertEquals("did:plc:me", sent["repo"]!!.jsonPrimitive.content) }
@Test fun deleteRecordNamesTheCollectionAndRkey() = runTest { client.deleteRecord("social.grain.comment", "abc", auth)
val sent = body("dev.hatk.deleteRecord") assertEquals("social.grain.comment", sent["collection"]!!.jsonPrimitive.content) assertEquals("abc", sent["rkey"]!!.jsonPrimitive.content) }
@Test fun applyWritesBatchesEveryCreateIntoOneCommit() = runTest { server.on("dev.hatk.applyWrites", """{"results":[{"uri":"at://a"},{"uri":"at://b"}]}""") val response = client.applyWrites( listOf( ApplyWrite("social.grain.photo", "p1", buildJsonObject { put("alt", "one") }), ApplyWrite("social.grain.gallery", "g1", buildJsonObject { put("title", "two") }), ), auth, )
assertEquals(2, response.results?.size) val writes = body("dev.hatk.applyWrites")["writes"]!!.jsonArray assertEquals(2, writes.size) val first = writes[0].jsonObject assertEquals("dev.hatk.applyWrites#create", first["\$type"]!!.jsonPrimitive.content) assertEquals("social.grain.photo", first["collection"]!!.jsonPrimitive.content) assertEquals("p1", first["rkey"]!!.jsonPrimitive.content) assertEquals("one", first["value"]!!.jsonObject["alt"]!!.jsonPrimitive.content) }
@Test fun deletingAGalleryGoesThroughTheUnspeccedEndpoint() = runTest { client.deleteGallery("g1", auth)
assertEquals( "g1", body("social.grain.unspecced.deleteGallery")["rkey"]!!.jsonPrimitive.content, ) }
// MARK: - Moderation
@Test fun describeLabelsTakesNoParameters() = runTest { server.on( "dev.hatk.describeLabels", """{"definitions":[{"identifier":"spoiler","blurs":"none"}]}""", ) assertEquals("spoiler", client.describeLabels().definitions!!.single().identifier) assertTrue(server.lastCall("dev.hatk.describeLabels").params.isEmpty()) }
@Test fun reportingARecordSendsAStrongRef() = runTest { server.on("dev.hatk.createReport", """{"id":42}""") val response = client.createReport( ReportSubject.Record("at://x/gallery/1", "cid1"), label = "spam", reason = "Repeated posts", auth = auth, )
assertEquals(42, response.id) val sent = body("dev.hatk.createReport") val subject = sent["subject"]!!.jsonObject assertEquals("com.atproto.repo.strongRef", subject["\$type"]!!.jsonPrimitive.content) assertEquals("at://x/gallery/1", subject["uri"]!!.jsonPrimitive.content) assertEquals("cid1", subject["cid"]!!.jsonPrimitive.content) assertEquals("spam", sent["label"]!!.jsonPrimitive.content) assertEquals("Repeated posts", sent["reason"]!!.jsonPrimitive.content) }
@Test fun reportingAnAccountSendsABareDidAndNoReason() = runTest { server.on("dev.hatk.createReport", """{"id":7}""") client.createReport(ReportSubject.Account("did:plc:bob"), label = "spam", auth = auth)
val sent = body("dev.hatk.createReport") assertEquals("did:plc:bob", sent["subject"]!!.jsonObject["did"]!!.jsonPrimitive.content) assertNull(sent["reason"]) }
// MARK: - Push
@Test fun pushTokensRegisterAndUnregisterForThisPlatform() = runTest { client.registerPushToken("fcm-token", auth) val registered = body("dev.hatk.push.registerToken") assertEquals("fcm-token", registered["token"]!!.jsonPrimitive.content) assertEquals("fcm", registered["platform"]!!.jsonPrimitive.content)
client.unregisterPushToken("fcm-token", auth) assertEquals( "fcm-token", body("dev.hatk.push.unregisterToken")["token"]!!.jsonPrimitive.content, ) }
// MARK: - Moderation lists
@Test fun blockAndMuteListsAreReadWholeAndAlwaysAuthenticated() = runTest { server.on( "social.grain.unspecced.getBlocks", """{"items":[{"did":"did:plc:bob","blockUri":"at://block/1"}]}""", ) server.on("social.grain.unspecced.getMutes", """{"items":[{"did":"did:plc:carol"}]}""")
assertEquals("at://block/1", client.getBlocks(auth).items!!.single().blockUri) assertEquals("did:plc:carol", client.getMutes(auth).items!!.single().did)
assertTrue( server.lastCall("social.grain.unspecced.getBlocks") .authorization!!.startsWith("DPoP "), ) }
@Test fun mutingAndUnmutingNameTheActor() = runTest { client.muteActor("did:plc:bob", auth) assertEquals( "did:plc:bob", body("social.grain.graph.muteActor")["actor"]!!.jsonPrimitive.content, )
client.unmuteActor("did:plc:bob", auth) assertEquals( "did:plc:bob", body("social.grain.graph.unmuteActor")["actor"]!!.jsonPrimitive.content, ) }
@Test fun blockServiceCreatesARecordAndDeletesByRkey() = runTest { server.on("dev.hatk.createRecord", """{"uri":"at://did:plc:me/block/xyz"}""") BlockService.block("did:plc:bob", "did:plc:me", client, auth)
val record = body("dev.hatk.createRecord")["record"]!!.jsonObject assertEquals("did:plc:bob", record["subject"]!!.jsonPrimitive.content) assertTrue(record.containsKey("createdAt"))
BlockService.unblock("at://did:plc:me/social.grain.graph.block/xyz", client, auth) val deleted = body("dev.hatk.deleteRecord") assertEquals(BlockService.COLLECTION, deleted["collection"]!!.jsonPrimitive.content) assertEquals("xyz", deleted["rkey"]!!.jsonPrimitive.content) }
@Test fun preferencesAndAccountDeletionPostTheirOwnShapes() = runTest { client.putPreference("includeExif", value = false, auth = auth) val sent = body("dev.hatk.putPreference") assertEquals("includeExif", sent["key"]!!.jsonPrimitive.content) assertEquals("false", sent["value"]!!.jsonPrimitive.content)
client.deleteAccount(auth) assertTrue(body("social.grain.unspecced.deleteAccount").isEmpty()) }
// MARK: - Services
@Test fun favoriteAndFollowServicesWriteTheirOwnCollections() = runTest { server.on("dev.hatk.createRecord", """{"uri":"at://fav"}""") FavoriteService.create("at://x/gallery/1", "did:plc:me", client, auth) assertEquals( FavoriteService.COLLECTION, body("dev.hatk.createRecord")["collection"]!!.jsonPrimitive.content, )
FavoriteService.delete("at://did:plc:me/social.grain.favorite/f1", client, auth) assertEquals("f1", body("dev.hatk.deleteRecord")["rkey"]!!.jsonPrimitive.content)
FollowService.create("did:plc:bob", "did:plc:me", client, auth) val follow = body("dev.hatk.createRecord") assertEquals(FollowService.COLLECTION, follow["collection"]!!.jsonPrimitive.content) assertEquals( "did:plc:bob", follow["record"]!!.jsonObject["subject"]!!.jsonPrimitive.content, )
FollowService.delete("at://did:plc:me/social.grain.graph.follow/fl1", client, auth) assertEquals("fl1", body("dev.hatk.deleteRecord")["rkey"]!!.jsonPrimitive.content) }
@Test fun galleryServiceDeletesByTheRkeyInTheUri() = runTest { GalleryService.delete("at://did:plc:me/social.grain.gallery/g9", client, auth) assertEquals( "g9", body("social.grain.unspecced.deleteGallery")["rkey"]!!.jsonPrimitive.content, ) }
@Test fun commentServiceCarriesTheReplyTargetOnlyWhenReplying() = runTest { server.on("dev.hatk.createRecord", """{"uri":"at://c"}""")
CommentService.create("at://x/gallery/1", "Nice", "did:plc:me", client = client, auth = auth) val top = body("dev.hatk.createRecord")["record"]!!.jsonObject assertEquals("Nice", top["text"]!!.jsonPrimitive.content) assertEquals("at://x/gallery/1", top["subject"]!!.jsonPrimitive.content) assertNull(top["replyTo"])
CommentService.create( "at://x/gallery/1", "Agreed", "did:plc:me", replyTo = "at://x/comment/1", client = client, auth = auth, ) assertEquals( "at://x/comment/1", body("dev.hatk.createRecord")["record"]!! .jsonObject["replyTo"]!!.jsonPrimitive.content, )
CommentService.delete("at://did:plc:me/social.grain.comment/c1", client, auth) assertEquals("c1", body("dev.hatk.deleteRecord")["rkey"]!!.jsonPrimitive.content) }
@Test fun storyServiceBuildsTheMediaBlobAndAspectRatio() = runTest { server.on("dev.hatk.createRecord", """{"uri":"at://s"}""") StoryService.create( blob = BlobRef( type = "blob", ref = BlobRef.BlobLink("bafyabc"), mimeType = "image/jpeg", size = 1234, ), width = 1080, height = 1920, repo = "did:plc:me", client = client, auth = auth, )
val record = body("dev.hatk.createRecord")["record"]!!.jsonObject val media = record["media"]!!.jsonObject assertEquals("bafyabc", media["ref"]!!.jsonObject["\$link"]!!.jsonPrimitive.content) assertEquals("image/jpeg", media["mimeType"]!!.jsonPrimitive.content) assertEquals("1234", media["size"]!!.jsonPrimitive.content) assertEquals("1080", record["aspectRatio"]!!.jsonObject["width"]!!.jsonPrimitive.content) assertNull(record["location"]) assertNull(record["labels"]) }
@Test fun storyServiceAttachesLocationAndSelfLabelsWhenGiven() = runTest { server.on("dev.hatk.createRecord", """{"uri":"at://s"}""") StoryService.create( blob = BlobRef(ref = BlobRef.BlobLink("bafy")), width = 9, height = 16, repo = "did:plc:me", labels = listOf("nudity"), locationCell = "8928308280fffff", locationName = "Mission District", address = buildJsonObject { put("country", "US") }, client = client, auth = auth, )
val record = body("dev.hatk.createRecord")["record"]!!.jsonObject val location = record["location"]!!.jsonObject assertEquals("8928308280fffff", location["value"]!!.jsonPrimitive.content) assertEquals("Mission District", location["name"]!!.jsonPrimitive.content) assertEquals("US", record["address"]!!.jsonObject["country"]!!.jsonPrimitive.content)
val labels = record["labels"]!!.jsonObject assertEquals( "com.atproto.label.defs#selfLabels", labels["\$type"]!!.jsonPrimitive.content, ) assertEquals( "nudity", labels["values"]!!.jsonArray.single().jsonObject["val"]!!.jsonPrimitive.content, ) }
@Test fun storyServiceDeletesByRkey() = runTest { StoryService.delete("at://did:plc:me/social.grain.story/s1", client, auth) assertEquals("s1", body("dev.hatk.deleteRecord")["rkey"]!!.jsonPrimitive.content) }
@Test fun storyServiceFallsBackWhenTheBlobIsMissingItsFields() = runTest { server.on("dev.hatk.createRecord", """{"uri":"at://s"}""") StoryService.create( blob = BlobRef(), width = 1, height = 1, repo = "did:plc:me", client = client, auth = auth, )
val media = body("dev.hatk.createRecord")["record"]!!.jsonObject["media"]!!.jsonObject assertEquals("blob", media["\$type"]!!.jsonPrimitive.content) assertEquals("", media["ref"]!!.jsonObject["\$link"]!!.jsonPrimitive.content) assertEquals("image/jpeg", media["mimeType"]!!.jsonPrimitive.content) assertEquals("0", media["size"]!!.jsonPrimitive.content) }
// MARK: - Pinned feeds
@Test fun aPinnedFeedNamesItselfByTypeOnlyWhenItIsACompoundId() = runTest { val camera = PinnedFeed("camera:Leica M6", "Leica M6", "camera", "/c") assertEquals("camera", camera.feedName) assertEquals("Leica M6", camera.feedValue)
val recent = PinnedFeed.defaults.first() assertEquals("recent", recent.feedName) assertNull(recent.feedValue)
val hashtag = PinnedFeed("hashtag:fog", "#fog", "hashtag", "/t/fog") assertEquals("hashtag", hashtag.feedName) assertEquals("fog", hashtag.feedValue) }}