50c0ec176c
* add scopes to service auth impl * add error to getServiceAuth * send scoped tokens from pds * clean up privileged access scopes & allow simple service auth tokens for app passwords * integration into ozone * fix up bsky tests * cleanup xrpc-server tests * fix up tests & types * one more test * fix read after write tests * fix mod auth test * convert scopes to be a single method name * add scope check callback for auth verifier * pds changes only * fix feed generation tests * use scope for ozone service profile * dont verify scopes on pds yet * tidy * tidy imports * changeset * add tests * tidy * another changeset * scope -> lxm * tidy * clean up scope references * update nonce size * pr feedback * trim trailing slash * nonce -> jti * fix xrpc-server test * allow service auth on uploadBlob * fix build error * changeset * build, tidy * xrpc-server: update lxm claim check error * appview: temporarily permit labeler service calls to omit lxm claim * xrpc-server: fix test * changeset * fix merged tests --------- Co-authored-by: Devin Ivy <devinivy@gmail.com>
294 lines
7.8 KiB
TypeScript
294 lines
7.8 KiB
TypeScript
import { AtpAgent } from '@atproto/api'
|
|
import {
|
|
TestNetwork,
|
|
SeedClient,
|
|
basicSeed,
|
|
usersBulkSeed,
|
|
} from '@atproto/dev-env'
|
|
import { forSnapshot, paginateAll } from '../_util'
|
|
import { ids } from '../../src/lexicon/lexicons'
|
|
|
|
describe('mute views', () => {
|
|
let network: TestNetwork
|
|
let agent: AtpAgent
|
|
let sc: SeedClient
|
|
let alice: string
|
|
let bob: string
|
|
let carol: string
|
|
let dan: string
|
|
|
|
let mutes: string[]
|
|
|
|
beforeAll(async () => {
|
|
network = await TestNetwork.create({
|
|
dbPostgresSchema: 'bsky_views_mutes',
|
|
})
|
|
agent = network.bsky.getClient()
|
|
sc = network.getSeedClient()
|
|
await basicSeed(sc)
|
|
await usersBulkSeed(sc, 10)
|
|
alice = sc.dids.alice
|
|
bob = sc.dids.bob
|
|
carol = sc.dids.carol
|
|
dan = sc.dids.dan
|
|
mutes = [
|
|
bob,
|
|
carol,
|
|
'aliya-hodkiewicz.test',
|
|
'adrienne49.test',
|
|
'jeffrey-sawayn87.test',
|
|
'nicolas-krajcik10.test',
|
|
'magnus53.test',
|
|
'elta48.test',
|
|
]
|
|
await network.processAll()
|
|
for (const did of mutes) {
|
|
await agent.api.app.bsky.graph.muteActor(
|
|
{ actor: did },
|
|
{
|
|
headers: await network.serviceHeaders(
|
|
alice,
|
|
ids.AppBskyGraphMuteActor,
|
|
),
|
|
encoding: 'application/json',
|
|
},
|
|
)
|
|
}
|
|
})
|
|
|
|
afterAll(async () => {
|
|
await network.close()
|
|
})
|
|
|
|
it('flags mutes in threads', async () => {
|
|
const res = await agent.api.app.bsky.feed.getPostThread(
|
|
{ depth: 1, uri: sc.posts[alice][1].ref.uriStr },
|
|
{
|
|
headers: await network.serviceHeaders(
|
|
alice,
|
|
ids.AppBskyFeedGetPostThread,
|
|
),
|
|
},
|
|
)
|
|
expect(forSnapshot(res.data.thread)).toMatchSnapshot()
|
|
})
|
|
|
|
it('does not show reposted content from a muted account in author feed', async () => {
|
|
await sc.repost(dan, sc.posts[bob][0].ref)
|
|
await sc.repost(dan, sc.posts[bob][1].ref)
|
|
await network.processAll()
|
|
|
|
const res = await agent.api.app.bsky.feed.getAuthorFeed(
|
|
{ actor: dan },
|
|
{
|
|
headers: await network.serviceHeaders(
|
|
alice,
|
|
ids.AppBskyFeedGetAuthorFeed,
|
|
),
|
|
},
|
|
)
|
|
expect(
|
|
res.data.feed.some((post) => [bob, carol].includes(post.post.author.did)),
|
|
).toBe(false)
|
|
})
|
|
|
|
it('removes content from muted users on getTimeline', async () => {
|
|
const res = await agent.api.app.bsky.feed.getTimeline(
|
|
{ limit: 100 },
|
|
{
|
|
headers: await network.serviceHeaders(
|
|
alice,
|
|
ids.AppBskyFeedGetTimeline,
|
|
),
|
|
},
|
|
)
|
|
expect(
|
|
res.data.feed.some((post) => [bob, carol].includes(post.post.author.did)),
|
|
).toBe(false)
|
|
})
|
|
|
|
it('removes content from muted users on getListFeed', async () => {
|
|
const listRef = await sc.createList(bob, 'test list', 'curate')
|
|
await sc.addToList(alice, bob, listRef)
|
|
await sc.addToList(alice, carol, listRef)
|
|
await sc.addToList(alice, dan, listRef)
|
|
const res = await agent.api.app.bsky.feed.getListFeed(
|
|
{ list: listRef.uriStr },
|
|
{
|
|
headers: await network.serviceHeaders(
|
|
alice,
|
|
ids.AppBskyFeedGetListFeed,
|
|
),
|
|
},
|
|
)
|
|
expect(
|
|
res.data.feed.some((post) => [bob, carol].includes(post.post.author.did)),
|
|
).toBe(false)
|
|
})
|
|
|
|
it('returns mute status on getProfile', async () => {
|
|
const res = await agent.api.app.bsky.actor.getProfile(
|
|
{ actor: bob },
|
|
{
|
|
headers: await network.serviceHeaders(
|
|
alice,
|
|
ids.AppBskyActorGetProfile,
|
|
),
|
|
},
|
|
)
|
|
expect(res.data.viewer?.muted).toBe(true)
|
|
})
|
|
|
|
it('returns mute status on getProfiles', async () => {
|
|
const res = await agent.api.app.bsky.actor.getProfiles(
|
|
{ actors: [bob, carol, dan] },
|
|
{
|
|
headers: await network.serviceHeaders(
|
|
alice,
|
|
ids.AppBskyActorGetProfiles,
|
|
),
|
|
},
|
|
)
|
|
expect(res.data.profiles[0].viewer?.muted).toBe(true)
|
|
expect(res.data.profiles[1].viewer?.muted).toBe(true)
|
|
expect(res.data.profiles[2].viewer?.muted).toBe(false)
|
|
})
|
|
|
|
it('does not return notifs for muted accounts', async () => {
|
|
const res = await agent.api.app.bsky.notification.listNotifications(
|
|
{
|
|
limit: 100,
|
|
},
|
|
{
|
|
headers: await network.serviceHeaders(
|
|
alice,
|
|
ids.AppBskyNotificationListNotifications,
|
|
),
|
|
},
|
|
)
|
|
expect(
|
|
res.data.notifications.some((notif) =>
|
|
[bob, carol].includes(notif.author.did),
|
|
),
|
|
).toBeFalsy()
|
|
})
|
|
|
|
it('flags muted accounts in get suggestions', async () => {
|
|
// unfollow so they _would_ show up in suggestions if not for mute
|
|
await sc.unfollow(alice, bob)
|
|
await sc.unfollow(alice, carol)
|
|
|
|
await network.processAll()
|
|
|
|
const res = await agent.api.app.bsky.actor.getSuggestions(
|
|
{
|
|
limit: 100,
|
|
},
|
|
{
|
|
headers: await network.serviceHeaders(
|
|
alice,
|
|
ids.AppBskyActorGetSuggestions,
|
|
),
|
|
},
|
|
)
|
|
for (const actor of res.data.actors) {
|
|
if (mutes.includes(actor.did) || mutes.includes(actor.handle)) {
|
|
expect(actor.viewer?.muted).toBe(true)
|
|
} else {
|
|
expect(actor.viewer?.muted).toBe(false)
|
|
}
|
|
}
|
|
})
|
|
|
|
it('fetches mutes for the logged-in user.', async () => {
|
|
const { data: view } = await agent.api.app.bsky.graph.getMutes(
|
|
{},
|
|
{
|
|
headers: await network.serviceHeaders(alice, ids.AppBskyGraphGetMutes),
|
|
},
|
|
)
|
|
expect(forSnapshot(view.mutes)).toMatchSnapshot()
|
|
})
|
|
|
|
it('paginates.', async () => {
|
|
const results = (results) => results.flatMap((res) => res.mutes)
|
|
const paginator = async (cursor?: string) => {
|
|
const { data: view } = await agent.api.app.bsky.graph.getMutes(
|
|
{ cursor, limit: 2 },
|
|
{
|
|
headers: await network.serviceHeaders(
|
|
alice,
|
|
ids.AppBskyGraphGetMutes,
|
|
),
|
|
},
|
|
)
|
|
return view
|
|
}
|
|
|
|
const paginatedAll = await paginateAll(paginator)
|
|
paginatedAll.forEach((res) =>
|
|
expect(res.mutes.length).toBeLessThanOrEqual(2),
|
|
)
|
|
|
|
const full = await agent.api.app.bsky.graph.getMutes(
|
|
{},
|
|
{
|
|
headers: await network.serviceHeaders(alice, ids.AppBskyGraphGetMutes),
|
|
},
|
|
)
|
|
|
|
expect(full.data.mutes.length).toEqual(8)
|
|
expect(results(paginatedAll)).toEqual(results([full.data]))
|
|
})
|
|
|
|
it('removes mute.', async () => {
|
|
const { data: initial } = await agent.api.app.bsky.graph.getMutes(
|
|
{},
|
|
{
|
|
headers: await network.serviceHeaders(alice, ids.AppBskyGraphGetMutes),
|
|
},
|
|
)
|
|
expect(initial.mutes.length).toEqual(8)
|
|
expect(initial.mutes.map((m) => m.handle)).toContain('elta48.test')
|
|
|
|
await agent.api.app.bsky.graph.unmuteActor(
|
|
{ actor: sc.dids['elta48.test'] },
|
|
{
|
|
headers: await network.serviceHeaders(
|
|
alice,
|
|
ids.AppBskyGraphUnmuteActor,
|
|
),
|
|
encoding: 'application/json',
|
|
},
|
|
)
|
|
|
|
const { data: final } = await agent.api.app.bsky.graph.getMutes(
|
|
{},
|
|
{
|
|
headers: await network.serviceHeaders(alice, ids.AppBskyGraphGetMutes),
|
|
},
|
|
)
|
|
expect(final.mutes.length).toEqual(7)
|
|
expect(final.mutes.map((m) => m.handle)).not.toContain('elta48.test')
|
|
|
|
await agent.api.app.bsky.graph.muteActor(
|
|
{ actor: sc.dids['elta48.test'] },
|
|
{
|
|
headers: await network.serviceHeaders(alice, ids.AppBskyGraphMuteActor),
|
|
encoding: 'application/json',
|
|
},
|
|
)
|
|
})
|
|
|
|
it('does not allow muting self.', async () => {
|
|
const promise = agent.api.app.bsky.graph.muteActor(
|
|
{ actor: alice },
|
|
{
|
|
headers: await network.serviceHeaders(alice, ids.AppBskyGraphMuteActor),
|
|
encoding: 'application/json',
|
|
},
|
|
)
|
|
await expect(promise).rejects.toThrow() // @TODO check error message w/ grpc error passthru
|
|
})
|
|
})
|