atproto/packages/bsky/tests/label-hydration.test.ts
Matthieu Sieben 72eba67af1
Drop axios dependency (#3177)
* Minor adaptation of VerifyCidTransform implementation

* refactor: factorize content-encoding negotiation into new lib

* bsky: Use undici to stream blob

* fixup! bsky: Use undici to stream blob

* disable ssrf bsky protection in dev-env

* remove http requests to self to host "/img/"

* drop axios from tests

* fixes

* fix tests

* reviex changes

* properly handle HEAD requests

* handle client disconnection

* fix tests

* drop unrelated change

* tidy

* tidy

* tidy

* remove axios from dev-env

* remove axios from identity package

* use undici 6

* remove axios dependency from ozone

* tidy

* remove axios from PDS package

* avoid killing bsky-pds connections

* improve debugging data

* Better handle invalid CID

* tidy

* tidy

* refactor "allFulfilled" util in @atproto/common

* tidy

---------

Co-authored-by: devin ivy <devinivy@gmail.com>
2025-01-06 18:34:11 +01:00

195 lines
6.0 KiB
TypeScript

import { AtpAgent } from '@atproto/api'
import { TestNetwork, SeedClient, basicSeed } from '@atproto/dev-env'
describe('label hydration', () => {
let network: TestNetwork
let pdsAgent: AtpAgent
let sc: SeedClient
let alice: string
let bob: string
let carol: string
let labelerDid: string
beforeAll(async () => {
network = await TestNetwork.create({
dbPostgresSchema: 'bsky_label_hydration',
})
pdsAgent = network.pds.getClient()
sc = network.getSeedClient()
await basicSeed(sc)
alice = sc.dids.alice
bob = sc.dids.bob
carol = sc.dids.carol
labelerDid = network.bsky.ctx.cfg.labelsFromIssuerDids[0]
await createLabel({ src: alice, uri: carol, cid: '', val: 'spam' })
await createLabel({ src: bob, uri: carol, cid: '', val: 'impersonation' })
await createLabel({
src: labelerDid,
uri: carol,
cid: '',
val: 'misleading',
})
await network.processAll()
})
afterAll(async () => {
await network.close()
})
it('hydrates labels based on a supplied labeler header', async () => {
AtpAgent.configure({ appLabelers: [alice] })
pdsAgent.configureLabelers([])
const res = await pdsAgent.api.app.bsky.actor.getProfile(
{ actor: carol },
{
headers: sc.getHeaders(bob),
},
)
expect(res.data.labels?.length).toBe(1)
expect(res.data.labels?.[0].src).toBe(alice)
expect(res.data.labels?.[0].val).toBe('spam')
expect(res.headers['atproto-content-labelers']).toEqual(`${alice};redact`)
})
it('hydrates labels based on multiple a supplied labelers', async () => {
AtpAgent.configure({ appLabelers: [bob] })
pdsAgent.configureLabelers([alice])
const res = await pdsAgent.api.app.bsky.actor.getProfile(
{ actor: carol },
{
headers: {
'atproto-accept-labelers': labelerDid,
...sc.getHeaders(bob),
},
},
)
expect(res.data.labels?.length).toBe(3)
expect(res.data.labels?.find((l) => l.src === alice)?.val).toEqual('spam')
expect(res.data.labels?.find((l) => l.src === bob)?.val).toEqual(
'impersonation',
)
expect(res.data.labels?.find((l) => l.src === labelerDid)?.val).toEqual(
'misleading',
)
const labelerHeaderDids = res.headers['atproto-content-labelers'].split(',')
expect(labelerHeaderDids.sort()).toEqual(
[alice, `${bob};redact`, labelerDid].sort(),
)
})
it('defaults to service labels when no labeler header is provided', async () => {
const res = await fetch(
`${network.pds.url}/xrpc/app.bsky.actor.getProfile?actor=${carol}`,
{ headers: sc.getHeaders(bob) },
)
const data = await res.json()
expect(data.labels?.length).toBe(1)
expect(data.labels?.[0].src).toBe(labelerDid)
expect(data.labels?.[0].val).toBe('misleading')
expect(res.headers.get('atproto-content-labelers')).toEqual(
network.bsky.ctx.cfg.labelsFromIssuerDids
.map((did) => `${did};redact`)
.join(','),
)
})
it('hydrates labels without duplication', async () => {
AtpAgent.configure({ appLabelers: [alice] })
pdsAgent.configureLabelers([])
const res = await pdsAgent.api.app.bsky.actor.getProfiles(
{ actors: [carol, carol] },
{ headers: sc.getHeaders(bob) },
)
const { labels = [] } = res.data.profiles[0]
expect(labels.map((l) => ({ val: l.val, src: l.src }))).toEqual([
{ src: alice, val: 'spam' },
])
})
it('does not hydrate labels from takendown labeler', async () => {
AtpAgent.configure({ appLabelers: [alice, sc.dids.dan] })
pdsAgent.configureLabelers([])
await network.bsky.ctx.dataplane.takedownActor({ did: alice })
const res = await pdsAgent.api.app.bsky.actor.getProfile(
{ actor: carol },
{ headers: sc.getHeaders(bob) },
)
const { labels = [] } = res.data
expect(labels).toEqual([])
expect(res.headers['atproto-content-labelers']).toEqual(
`${sc.dids.dan};redact`, // does not include alice
)
await network.bsky.ctx.dataplane.untakedownActor({ did: alice })
})
it('hydrates labels onto list views.', async () => {
AtpAgent.configure({ appLabelers: [labelerDid] })
pdsAgent.configureLabelers([])
const list = await pdsAgent.api.app.bsky.graph.list.create(
{ repo: alice },
{
name: "alice's modlist",
purpose: 'app.bsky.graph.defs#modlist',
createdAt: new Date().toISOString(),
},
sc.getHeaders(alice),
)
await network.processAll()
await createLabel({ uri: list.uri, cid: list.cid, val: 'spam' })
const res = await pdsAgent.api.app.bsky.graph.getList(
{ list: list.uri },
{ headers: sc.getHeaders(alice) },
)
const [label, ...others] = res.data.list.labels ?? []
expect(label?.src).toBe(labelerDid)
expect(label?.val).toBe('spam')
expect(others.length).toBe(0)
})
it('hydrates labels onto feed generator views.', async () => {
const feedgen = await pdsAgent.api.app.bsky.feed.generator.create(
{ repo: alice },
{
displayName: "alice's feedgen",
did: alice,
createdAt: new Date().toISOString(),
},
sc.getHeaders(alice),
)
await network.processAll()
await createLabel({ uri: feedgen.uri, cid: feedgen.cid, val: 'spam' })
const res = await pdsAgent.api.app.bsky.feed.getFeedGenerators(
{ feeds: [feedgen.uri] },
{ headers: sc.getHeaders(alice) },
)
expect(res.data.feeds.length).toBe(1)
const [label, ...others] = res.data.feeds[0].labels ?? []
expect(label?.src).toBe(labelerDid)
expect(label?.val).toBe('spam')
expect(others.length).toBe(0)
})
const createLabel = async (opts: {
src?: string
uri: string
cid: string
val: string
}) => {
await network.bsky.db.db
.insertInto('label')
.values({
uri: opts.uri,
cid: opts.cid,
val: opts.val,
cts: new Date().toISOString(),
neg: false,
src: opts.src ?? labelerDid,
})
.execute()
}
})