atproto/packages/api/tests/moderation-custom-labels.test.ts
Paul Frazee 00a567daa0
Additional 3p labeler updates (#2286)
* Rename bsky_labeler_did

* Use labeldef default setting

* Add definedBy to interpretted label defs

* Improve dev-env mocks for labels

* Remove global label defs for dmca-violation, doxxing, and !no-promote

* Change nudity global label def to default to ignore and no longer be adult-only

* Remove old !no-promote tests

* Add mod authorities header and move bsky labeler into it

* Rename modsPref and addModService/removeModService to labelersPref and add/removeLabeler

* Add defaultSetting and adultOnly to custom label value definitions

* Rename InterprettedLabelValueDefinition to InterpretedLabelValueDefinition

* Update dev-env mock

* Move muted words and hidden posts into moderationPrefs

* Add muted word and hidden post handling to moderatePost

* Add mutewords tests

* Finish muteword tests

* Add mod-authority.test to dev-env

* Rename global label value def gore to graphic-media

* Fix typo

* Stop converting old label values

* Update label target=profile behaviors: dont filter content on hide, dont blur display names, but do show the info cards

* Update label target=account behaviors: dont blur media of content

* Add muteword moderation behaviors

* Fix mock label defs

* Implement quote-post moderation handling

* Add adult content test

* Handle sync legacy labels (#2291)

* Handle sync legacy labels

* Remap values on read

* Filter out double-written legacy label values

* Better naming, fix types

* Fix test

* Update moderation docs in sdk

* Update to new atproto-accept-labelers header behavior

* Add getLabelDefinitions() helper method

* Add proxy header support to agent

* Update mock moderation

* lint

* Implement moderation for userlists and feedgens

* Add another test label

* fix labeler in dev-env agents

* fix label hydration test

* fix lint error

* fix agent test

* fix takedown labels test

---------

Co-authored-by: Eric Bailey <git@esb.lol>
Co-authored-by: dholms <dtholmgren@gmail.com>
2024-03-12 21:10:19 -07:00

334 lines
7.4 KiB
TypeScript

import {
moderateProfile,
moderatePost,
mock,
ModerationOpts,
InterpretedLabelValueDefinition,
interpretLabelValueDefinition,
} from '../src'
import './util/moderation-behavior'
interface ScenarioResult {
profileList?: string[]
profileView?: string[]
avatar?: string[]
banner?: string[]
displayName?: string[]
contentList?: string[]
contentView?: string[]
contentMedia?: string[]
}
interface Scenario {
blurs: 'content' | 'media' | 'none'
severity: 'alert' | 'inform' | 'none'
account: ScenarioResult
profile: ScenarioResult
post: ScenarioResult
}
const TESTS: Scenario[] = [
{
blurs: 'content',
severity: 'alert',
account: {
profileList: ['filter', 'alert'],
profileView: ['alert'],
contentList: ['filter', 'blur'],
contentView: ['alert'],
},
profile: {
profileList: ['alert'],
profileView: ['alert'],
},
post: {
contentList: ['filter', 'blur'],
contentView: ['alert'],
},
},
{
blurs: 'content',
severity: 'inform',
account: {
profileList: ['filter', 'inform'],
profileView: ['inform'],
contentList: ['filter', 'blur'],
contentView: ['inform'],
},
profile: {
profileList: ['inform'],
profileView: ['inform'],
},
post: {
contentList: ['filter', 'blur'],
contentView: ['inform'],
},
},
{
blurs: 'content',
severity: 'none',
account: {
profileList: ['filter'],
profileView: [],
contentList: ['filter', 'blur'],
contentView: [],
},
profile: {
profileList: [],
profileView: [],
},
post: {
contentList: ['filter', 'blur'],
contentView: [],
},
},
{
blurs: 'media',
severity: 'alert',
account: {
profileList: ['filter', 'alert'],
profileView: ['alert'],
avatar: ['blur'],
banner: ['blur'],
contentList: ['filter'],
},
profile: {
profileList: ['alert'],
profileView: ['alert'],
avatar: ['blur'],
banner: ['blur'],
},
post: {
contentList: ['filter'],
contentMedia: ['blur'],
},
},
{
blurs: 'media',
severity: 'inform',
account: {
profileList: ['filter', 'inform'],
profileView: ['inform'],
avatar: ['blur'],
banner: ['blur'],
contentList: ['filter'],
},
profile: {
profileList: ['inform'],
profileView: ['inform'],
avatar: ['blur'],
banner: ['blur'],
},
post: {
contentList: ['filter'],
contentMedia: ['blur'],
},
},
{
blurs: 'media',
severity: 'none',
account: {
profileList: ['filter'],
avatar: ['blur'],
banner: ['blur'],
contentList: ['filter'],
},
profile: {
avatar: ['blur'],
banner: ['blur'],
},
post: {
contentList: ['filter'],
contentMedia: ['blur'],
},
},
{
blurs: 'none',
severity: 'alert',
account: {
profileList: ['filter', 'alert'],
profileView: ['alert'],
contentList: ['filter', 'alert'],
contentView: ['alert'],
},
profile: {
profileList: ['alert'],
profileView: ['alert'],
},
post: {
contentList: ['filter', 'alert'],
contentView: ['alert'],
},
},
{
blurs: 'none',
severity: 'inform',
account: {
profileList: ['filter', 'inform'],
profileView: ['inform'],
contentList: ['filter', 'inform'],
contentView: ['inform'],
},
profile: {
profileList: ['inform'],
profileView: ['inform'],
},
post: {
contentList: ['filter', 'inform'],
contentView: ['inform'],
},
},
{
blurs: 'none',
severity: 'none',
account: {
profileList: ['filter'],
contentList: ['filter'],
},
profile: {},
post: {
contentList: ['filter'],
},
},
]
describe('Moderation: custom labels', () => {
const scenarios = TESTS.flatMap((test) => [
{
blurs: test.blurs,
severity: test.severity,
target: 'post',
expected: test.post,
},
{
blurs: test.blurs,
severity: test.severity,
target: 'profile',
expected: test.profile,
},
{
blurs: test.blurs,
severity: test.severity,
target: 'account',
expected: test.account,
},
])
it.each(scenarios)(
'blurs=$blurs, severity=$severity, target=$target',
({ blurs, severity, target, expected }) => {
let res
if (target === 'post') {
res = moderatePost(
mock.postView({
record: {
text: 'Hello',
createdAt: new Date().toISOString(),
},
author: mock.profileViewBasic({
handle: 'bob.test',
displayName: 'Bob',
}),
labels: [
mock.label({
val: 'custom',
uri: 'at://did:web:bob.test/app.bsky.feed.post/fake',
src: 'did:web:labeler.test',
}),
],
}),
modOpts(blurs, severity),
)
} else if (target === 'profile') {
res = moderateProfile(
mock.profileViewBasic({
handle: 'bob.test',
displayName: 'Bob',
labels: [
mock.label({
val: 'custom',
uri: 'at://did:web:bob.test/app.bsky.actor.profile/self',
src: 'did:web:labeler.test',
}),
],
}),
modOpts(blurs, severity),
)
} else {
res = moderateProfile(
mock.profileViewBasic({
handle: 'bob.test',
displayName: 'Bob',
labels: [
mock.label({
val: 'custom',
uri: 'did:web:bob.test',
src: 'did:web:labeler.test',
}),
],
}),
modOpts(blurs, severity),
)
}
expect(res.ui('profileList')).toBeModerationResult(
expected.profileList || [],
)
expect(res.ui('profileView')).toBeModerationResult(
expected.profileView || [],
)
expect(res.ui('avatar')).toBeModerationResult(expected.avatar || [])
expect(res.ui('banner')).toBeModerationResult(expected.banner || [])
expect(res.ui('displayName')).toBeModerationResult(
expected.displayName || [],
)
expect(res.ui('contentList')).toBeModerationResult(
expected.contentList || [],
)
expect(res.ui('contentView')).toBeModerationResult(
expected.contentView || [],
)
expect(res.ui('contentMedia')).toBeModerationResult(
expected.contentMedia || [],
)
},
)
})
function modOpts(blurs: string, severity: string): ModerationOpts {
return {
userDid: 'did:web:alice.test',
prefs: {
adultContentEnabled: true,
labels: {},
labelers: [
{
did: 'did:web:labeler.test',
labels: { custom: 'hide' },
},
],
mutedWords: [],
hiddenPosts: [],
},
labelDefs: {
'did:web:labeler.test': [makeCustomLabel(blurs, severity)],
},
}
}
function makeCustomLabel(
blurs: string,
severity: string,
): InterpretedLabelValueDefinition {
return interpretLabelValueDefinition(
{
identifier: 'custom',
blurs,
severity,
defaultSetting: 'warn',
locales: [],
},
'did:web:labeler.test',
)
}