atproto/packages/bsky/tests/admin/moderation.test.ts
Matthieu Sieben b934b396b1
Client SDK rework (#2483)
* feat(api): support creation of oauth based AtpAgents

* oauth: misc fixes for confidential clients

* fix(xprc): remove ReadableStream.from polyfill

* OAuth docs tweaks (#2679)

* OAuth: clarification about client_name being shown

* OAuth: re-write handle resolution privacy concern

* avoid relying on ReadableStream.from in xrpc-server tests

* feat(oauth-types): expose "ALLOW_UNSECURE_ORIGINS" constant

* feat(handle-resolver): expose "AtprotoIdentityDidMethods" type

* fix(oauth-client): ensure that the oauth metadata document contains client_id_metadata_document_supported

* fix(oauth-types): prevent unknown query string in loopback client id

* fix(identity-resolver): check that handle is in did doc's "alsoKnownAs"

* feat(oauth-client:oauth-resolver): allow logging in using either the PDS URL or Entryway URL

* fix(oauth-client): return better error in case of invalid "oauth-protected-resource" status code

* refactor(did): group atproto specific checks in own

* feat(api): relax typing of "appLabelers" and "labelers" AtpClient properties

* allow any did as labeller (for tests mainly)

* fix(api): allow to override "atproto-proxy" on a per-request basis

* remove release candidate versions from changelog

* update changeset for api and xrpc packages

* Add missing changeset

* revert RC versions

* Proper wording in OAUTH.md api example

* remove "pre" changeset file

* xrpc: restore original behavior of setHEader and unsetHeader

* docs: add comment for XrpcClient 's constructor arg

* feat(api): expose "schemas" publicly

* feat(api): allow customizing the whatwg fetch function of the AtpAgent

* docs(api): improve migration docs

* docs: change reference to BskyAgent to AtpAgent

* docs: mention the breaking change regarding setSessionPersistHandler

* fix(api): better split AtpClient concerns

* fix(xrpc): remove unused import

* refactor(api): simplify class hierarchu by removeing AtpClient

* fix(api): mock proper method for facets detection

* restore ability to restore session asynchronously

* feat(api): allow instantiating Agent with same argument as super class

* docs(api): properly extend Agent class

* style(xrpc): var name

* docs(api): remove "async" to header getter

---------

Co-authored-by: Devin Ivy <devinivy@gmail.com>
Co-authored-by: bnewbold <bnewbold@robocracy.org>
Co-authored-by: Hailey <me@haileyok.com>
2024-08-12 19:57:21 +02:00

210 lines
6.1 KiB
TypeScript

import { ImageRef, SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env'
import { AtpAgent } from '@atproto/api'
import {
RepoBlobRef,
RepoRef,
} from '../../src/lexicon/types/com/atproto/admin/defs'
import { Main as StrongRef } from '../../src/lexicon/types/com/atproto/repo/strongRef'
describe('moderation', () => {
let network: TestNetwork
let agent: AtpAgent
let sc: SeedClient
let repoSubject: RepoRef
let recordSubject: StrongRef
let blobSubject: RepoBlobRef
let blobRef: ImageRef
beforeAll(async () => {
network = await TestNetwork.create({
dbPostgresSchema: 'bsky_moderation',
})
agent = network.bsky.getClient()
sc = network.getSeedClient()
await basicSeed(sc)
await network.processAll()
repoSubject = {
$type: 'com.atproto.admin.defs#repoRef',
did: sc.dids.bob,
}
const post = sc.posts[sc.dids.carol][0]
recordSubject = {
$type: 'com.atproto.repo.strongRef',
uri: post.ref.uriStr,
cid: post.ref.cidStr,
}
blobRef = post.images[1]
blobSubject = {
$type: 'com.atproto.admin.defs#repoBlobRef',
did: sc.dids.carol,
cid: blobRef.image.ref.toString(),
}
})
afterAll(async () => {
await network.close()
})
it('takes down accounts', async () => {
await agent.api.com.atproto.admin.updateSubjectStatus(
{
subject: repoSubject,
takedown: { applied: true, ref: 'test-repo' },
},
{
encoding: 'application/json',
headers: network.bsky.adminAuthHeaders(),
},
)
const res = await agent.api.com.atproto.admin.getSubjectStatus(
{
did: repoSubject.did,
},
{ headers: network.bsky.adminAuthHeaders() },
)
expect(res.data.subject.did).toEqual(sc.dids.bob)
expect(res.data.takedown?.applied).toBe(true)
// expect(res.data.takedown?.ref).toBe('test-repo') @TODO add these checks back in once takedown refs make it into dataplane
})
it('restores takendown accounts', async () => {
await agent.api.com.atproto.admin.updateSubjectStatus(
{
subject: repoSubject,
takedown: { applied: false },
},
{
encoding: 'application/json',
headers: network.bsky.adminAuthHeaders(),
},
)
const res = await agent.api.com.atproto.admin.getSubjectStatus(
{
did: repoSubject.did,
},
{ headers: network.bsky.adminAuthHeaders() },
)
expect(res.data.subject.did).toEqual(sc.dids.bob)
expect(res.data.takedown?.applied).toBe(false)
expect(res.data.takedown?.ref).toBeUndefined()
})
it('takes down records', async () => {
await agent.api.com.atproto.admin.updateSubjectStatus(
{
subject: recordSubject,
takedown: { applied: true, ref: 'test-record' },
},
{
encoding: 'application/json',
headers: network.bsky.adminAuthHeaders(),
},
)
const res = await agent.api.com.atproto.admin.getSubjectStatus(
{
uri: recordSubject.uri,
},
{ headers: network.bsky.adminAuthHeaders() },
)
expect(res.data.subject.uri).toEqual(recordSubject.uri)
expect(res.data.takedown?.applied).toBe(true)
// expect(res.data.takedown?.ref).toBe('test-record')
})
it('restores takendown records', async () => {
await agent.api.com.atproto.admin.updateSubjectStatus(
{
subject: recordSubject,
takedown: { applied: false },
},
{
encoding: 'application/json',
headers: network.bsky.adminAuthHeaders(),
},
)
const res = await agent.api.com.atproto.admin.getSubjectStatus(
{
uri: recordSubject.uri,
},
{ headers: network.bsky.adminAuthHeaders() },
)
expect(res.data.subject.uri).toEqual(recordSubject.uri)
expect(res.data.takedown?.applied).toBe(false)
expect(res.data.takedown?.ref).toBeUndefined()
})
describe('blob takedown', () => {
let blobUri: string
let imageUri: string
beforeAll(async () => {
blobUri = `${network.bsky.url}/blob/${blobSubject.did}/${blobSubject.cid}`
imageUri = network.bsky.ctx.views.imgUriBuilder
.getPresetUri('feed_thumbnail', blobSubject.did, blobSubject.cid)
.replace(network.bsky.ctx.cfg.publicUrl || '', network.bsky.url)
// Warm image server cache
await fetch(imageUri)
const cached = await fetch(imageUri)
expect(cached.headers.get('x-cache')).toEqual('hit')
})
it('takes down blobs', async () => {
await agent.api.com.atproto.admin.updateSubjectStatus(
{
subject: blobSubject,
takedown: { applied: true, ref: 'test-blob' },
},
{
encoding: 'application/json',
headers: network.bsky.adminAuthHeaders(),
},
)
const res = await agent.api.com.atproto.admin.getSubjectStatus(
{
did: blobSubject.did,
blob: blobSubject.cid,
},
{ headers: network.bsky.adminAuthHeaders() },
)
expect(res.data.subject.did).toEqual(blobSubject.did)
expect(res.data.subject.cid).toEqual(blobSubject.cid)
expect(res.data.takedown?.applied).toBe(true)
// expect(res.data.takedown?.ref).toBe('test-blob')
})
it('prevents resolution of blob', async () => {
const resolveBlob = await fetch(blobUri)
expect(resolveBlob.status).toEqual(404)
expect(await resolveBlob.json()).toEqual({
error: 'NotFoundError',
message: 'Blob not found',
})
})
it('restores blob when takedown is removed', async () => {
await agent.api.com.atproto.admin.updateSubjectStatus(
{
subject: blobSubject,
takedown: { applied: false },
},
{
encoding: 'application/json',
headers: network.bsky.adminAuthHeaders(),
},
)
// Can resolve blob
const resolveBlob = await fetch(blobUri)
expect(resolveBlob.status).toEqual(200)
// Can fetch through image server
const fetchImage = await fetch(imageUri)
expect(fetchImage.status).toEqual(200)
const size = Number(fetchImage.headers.get('content-length'))
expect(size).toBeGreaterThan(9000)
})
})
})