@atproto/ozone 0.0.16 → 0.0.17-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/api/moderation/util.d.ts +1 -1
  2. package/dist/auth-verifier.d.ts +7 -11
  3. package/dist/config/config.d.ts +1 -0
  4. package/dist/config/env.d.ts +1 -2
  5. package/dist/config/secrets.d.ts +0 -2
  6. package/dist/daemon/event-pusher.d.ts +2 -0
  7. package/dist/db/index.js.map +1 -1
  8. package/dist/db/schema/moderation_subject_status.d.ts +2 -2
  9. package/dist/index.js +821 -1055
  10. package/dist/index.js.map +3 -3
  11. package/dist/lexicon/index.d.ts +8 -0
  12. package/dist/lexicon/lexicons.d.ts +298 -0
  13. package/dist/lexicon/types/app/bsky/actor/defs.d.ts +23 -1
  14. package/dist/lexicon/types/app/bsky/embed/record.d.ts +2 -1
  15. package/dist/lexicon/types/app/bsky/labeler/defs.d.ts +41 -0
  16. package/dist/lexicon/types/app/bsky/labeler/getServices.d.ts +36 -0
  17. package/dist/lexicon/types/app/bsky/labeler/service.d.ts +14 -0
  18. package/dist/lexicon/types/com/atproto/admin/defs.d.ts +2 -1
  19. package/dist/lexicon/types/com/atproto/label/defs.d.ts +18 -0
  20. package/dist/mod-service/index.d.ts +2 -2
  21. package/package.json +5 -4
  22. package/src/api/admin/createCommunicationTemplate.ts +1 -1
  23. package/src/api/admin/deleteCommunicationTemplate.ts +1 -1
  24. package/src/api/admin/emitModerationEvent.ts +6 -2
  25. package/src/api/admin/getModerationEvent.ts +1 -1
  26. package/src/api/admin/getRecord.ts +1 -1
  27. package/src/api/admin/getRepo.ts +1 -1
  28. package/src/api/admin/listCommunicationTemplates.ts +1 -1
  29. package/src/api/admin/queryModerationEvents.ts +1 -1
  30. package/src/api/admin/queryModerationStatuses.ts +1 -1
  31. package/src/api/admin/searchRepos.ts +1 -1
  32. package/src/api/admin/updateCommunicationTemplate.ts +1 -1
  33. package/src/api/admin/util.ts +1 -1
  34. package/src/api/moderation/createReport.ts +1 -2
  35. package/src/api/proxied.ts +8 -8
  36. package/src/api/temp/fetchLabels.ts +1 -1
  37. package/src/auth-verifier.ts +19 -29
  38. package/src/config/config.ts +10 -7
  39. package/src/config/env.ts +2 -4
  40. package/src/config/secrets.ts +0 -6
  41. package/src/context.ts +1 -3
  42. package/src/daemon/context.ts +2 -2
  43. package/src/daemon/event-pusher.ts +9 -1
  44. package/src/db/schema/moderation_subject_status.ts +6 -1
  45. package/src/lexicon/index.ts +23 -0
  46. package/src/lexicon/lexicons.ts +327 -1
  47. package/src/lexicon/types/app/bsky/actor/defs.ts +57 -1
  48. package/src/lexicon/types/app/bsky/embed/record.ts +2 -0
  49. package/src/lexicon/types/app/bsky/labeler/defs.ts +93 -0
  50. package/src/lexicon/types/app/bsky/labeler/getServices.ts +51 -0
  51. package/src/lexicon/types/app/bsky/labeler/service.ts +31 -0
  52. package/src/lexicon/types/com/atproto/admin/defs.ts +3 -0
  53. package/src/lexicon/types/com/atproto/label/defs.ts +68 -0
  54. package/src/mod-service/index.ts +4 -3
  55. package/src/mod-service/status.ts +42 -26
  56. package/tests/__snapshots__/get-record.test.ts.snap +4 -4
  57. package/tests/__snapshots__/get-repo.test.ts.snap +2 -2
  58. package/tests/communication-templates.test.ts +7 -7
  59. package/tests/get-record.test.ts +17 -7
  60. package/tests/get-repo.test.ts +24 -12
  61. package/tests/moderation-appeals.test.ts +24 -52
  62. package/tests/moderation-events.test.ts +87 -130
  63. package/tests/moderation-status-tags.test.ts +16 -31
  64. package/tests/moderation-statuses.test.ts +125 -58
  65. package/tests/moderation.test.ts +140 -287
  66. package/tests/repo-search.test.ts +11 -4
@@ -71,3 +71,71 @@ export function isSelfLabel(v: unknown): v is SelfLabel {
71
71
  export function validateSelfLabel(v: unknown): ValidationResult {
72
72
  return lexicons.validate('com.atproto.label.defs#selfLabel', v)
73
73
  }
74
+
75
+ /** Declares a label value and its expected interpertations and behaviors. */
76
+ export interface LabelValueDefinition {
77
+ /** The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+). */
78
+ identifier: string
79
+ /** How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing. */
80
+ severity: 'inform' | 'alert' | 'none' | (string & {})
81
+ /** What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing. */
82
+ blurs: 'content' | 'media' | 'none' | (string & {})
83
+ locales: LabelValueDefinitionStrings[]
84
+ [k: string]: unknown
85
+ }
86
+
87
+ export function isLabelValueDefinition(v: unknown): v is LabelValueDefinition {
88
+ return (
89
+ isObj(v) &&
90
+ hasProp(v, '$type') &&
91
+ v.$type === 'com.atproto.label.defs#labelValueDefinition'
92
+ )
93
+ }
94
+
95
+ export function validateLabelValueDefinition(v: unknown): ValidationResult {
96
+ return lexicons.validate('com.atproto.label.defs#labelValueDefinition', v)
97
+ }
98
+
99
+ /** Strings which describe the label in the UI, localized into a specific language. */
100
+ export interface LabelValueDefinitionStrings {
101
+ /** The code of the language these strings are written in. */
102
+ lang: string
103
+ /** A short human-readable name for the label. */
104
+ name: string
105
+ /** A longer description of what the label means and why it might be applied. */
106
+ description: string
107
+ [k: string]: unknown
108
+ }
109
+
110
+ export function isLabelValueDefinitionStrings(
111
+ v: unknown,
112
+ ): v is LabelValueDefinitionStrings {
113
+ return (
114
+ isObj(v) &&
115
+ hasProp(v, '$type') &&
116
+ v.$type === 'com.atproto.label.defs#labelValueDefinitionStrings'
117
+ )
118
+ }
119
+
120
+ export function validateLabelValueDefinitionStrings(
121
+ v: unknown,
122
+ ): ValidationResult {
123
+ return lexicons.validate(
124
+ 'com.atproto.label.defs#labelValueDefinitionStrings',
125
+ v,
126
+ )
127
+ }
128
+
129
+ export type LabelValue =
130
+ | '!hide'
131
+ | '!no-promote'
132
+ | '!warn'
133
+ | '!no-unauthenticated'
134
+ | 'dmca-violation'
135
+ | 'doxxing'
136
+ | 'porn'
137
+ | 'sexual'
138
+ | 'nudity'
139
+ | 'nsfl'
140
+ | 'gore'
141
+ | (string & {})
@@ -455,7 +455,8 @@ export class ModerationService {
455
455
  const takedownRef = `BSKY-${
456
456
  isSuspend ? 'SUSPEND' : 'TAKEDOWN'
457
457
  }-${takedownId}`
458
- const values = TAKEDOWNS.map((eventType) => ({
458
+
459
+ const values = this.eventPusher.takedowns.map((eventType) => ({
459
460
  eventType,
460
461
  subjectDid: subject.did,
461
462
  takedownRef,
@@ -516,7 +517,7 @@ export class ModerationService {
516
517
  async takedownRecord(subject: RecordSubject, takedownId: number) {
517
518
  this.db.assertTransaction()
518
519
  const takedownRef = `BSKY-TAKEDOWN-${takedownId}`
519
- const values = TAKEDOWNS.map((eventType) => ({
520
+ const values = this.eventPusher.takedowns.map((eventType) => ({
520
521
  eventType,
521
522
  subjectDid: subject.did,
522
523
  subjectUri: subject.uri,
@@ -555,7 +556,7 @@ export class ModerationService {
555
556
 
556
557
  if (blobCids && blobCids.length > 0) {
557
558
  const blobValues: Insertable<BlobPushEvent>[] = []
558
- for (const eventType of TAKEDOWNS) {
559
+ for (const eventType of this.eventPusher.takedowns) {
559
560
  for (const cid of blobCids) {
560
561
  blobValues.push({
561
562
  eventType,
@@ -7,6 +7,7 @@ import {
7
7
  REVIEWOPEN,
8
8
  REVIEWCLOSED,
9
9
  REVIEWESCALATED,
10
+ REVIEWNONE,
10
11
  } from '../lexicon/types/com/atproto/admin/defs'
11
12
  import { ModerationEventRow, ModerationSubjectStatusRow } from './types'
12
13
  import { HOUR } from '@atproto/common'
@@ -14,16 +15,22 @@ import { REASONAPPEAL } from '../lexicon/types/com/atproto/moderation/defs'
14
15
  import { jsonb } from '../db/types'
15
16
 
16
17
  const getSubjectStatusForModerationEvent = ({
18
+ currentStatus,
17
19
  action,
18
20
  createdBy,
19
21
  createdAt,
20
22
  durationInHours,
21
23
  }: {
24
+ currentStatus?: ModerationSubjectStatusRow
22
25
  action: string
23
26
  createdBy: string
24
27
  createdAt: string
25
28
  durationInHours: number | null
26
- }): Partial<ModerationSubjectStatusRow> | null => {
29
+ }): Partial<ModerationSubjectStatusRow> => {
30
+ const defaultReviewState = currentStatus
31
+ ? currentStatus.reviewState
32
+ : REVIEWNONE
33
+
27
34
  switch (action) {
28
35
  case 'com.atproto.admin.defs#modEventAcknowledge':
29
36
  return {
@@ -54,7 +61,9 @@ const getSubjectStatusForModerationEvent = ({
54
61
  return {
55
62
  lastReviewedBy: createdBy,
56
63
  muteUntil: null,
57
- reviewState: REVIEWOPEN,
64
+ // It's not likely to receive an unmute event that does not already have a status row
65
+ // but if it does happen, default to unnecessary
66
+ reviewState: defaultReviewState,
58
67
  lastReviewedAt: createdAt,
59
68
  }
60
69
  case 'com.atproto.admin.defs#modEventTakedown':
@@ -70,26 +79,29 @@ const getSubjectStatusForModerationEvent = ({
70
79
  case 'com.atproto.admin.defs#modEventMute':
71
80
  return {
72
81
  lastReviewedBy: createdBy,
73
- reviewState: REVIEWOPEN,
74
82
  lastReviewedAt: createdAt,
75
83
  // By default, mute for 24hrs
76
84
  muteUntil: new Date(
77
85
  Date.now() + (durationInHours || 24) * HOUR,
78
86
  ).toISOString(),
87
+ // It's not likely to receive a mute event on a subject that does not already have a status row
88
+ // but if it does happen, default to unnecessary
89
+ reviewState: defaultReviewState,
79
90
  }
80
91
  case 'com.atproto.admin.defs#modEventComment':
81
92
  return {
82
93
  lastReviewedBy: createdBy,
83
94
  lastReviewedAt: createdAt,
95
+ reviewState: defaultReviewState,
84
96
  }
85
97
  case 'com.atproto.admin.defs#modEventTag':
86
- return { tags: [] }
98
+ return { tags: [], reviewState: defaultReviewState }
87
99
  case 'com.atproto.admin.defs#modEventResolveAppeal':
88
100
  return {
89
101
  appealed: false,
90
102
  }
91
103
  default:
92
- return null
104
+ return {}
93
105
  }
94
106
  }
95
107
 
@@ -114,23 +126,6 @@ export const adjustModerationSubjectStatus = async (
114
126
  createdAt,
115
127
  } = moderationEvent
116
128
 
117
- const isAppealEvent =
118
- action === 'com.atproto.admin.defs#modEventReport' &&
119
- meta?.reportType === REASONAPPEAL
120
-
121
- const subjectStatus = getSubjectStatusForModerationEvent({
122
- action,
123
- createdBy,
124
- createdAt,
125
- durationInHours: moderationEvent.durationInHours,
126
- })
127
-
128
- // If there are no subjectStatus that means there are no side-effect of the incoming event
129
- if (!subjectStatus) {
130
- return null
131
- }
132
-
133
- const now = new Date().toISOString()
134
129
  // If subjectUri exists, it's not a repoRef so pass along the uri to get identifier back
135
130
  const identifier = getStatusIdentifierFromSubject(subjectUri || subjectDid)
136
131
 
@@ -140,25 +135,46 @@ export const adjustModerationSubjectStatus = async (
140
135
  .selectFrom('moderation_subject_status')
141
136
  .where('did', '=', identifier.did)
142
137
  .where('recordPath', '=', identifier.recordPath)
138
+ // Make sure we respect other updates that may be happening at the same time
139
+ .forUpdate()
143
140
  .selectAll()
144
141
  .executeTakeFirst()
145
142
 
143
+ const isAppealEvent =
144
+ action === 'com.atproto.admin.defs#modEventReport' &&
145
+ meta?.reportType === REASONAPPEAL
146
+
147
+ const subjectStatus = getSubjectStatusForModerationEvent({
148
+ currentStatus,
149
+ action,
150
+ createdBy,
151
+ createdAt,
152
+ durationInHours: moderationEvent.durationInHours,
153
+ })
154
+
155
+ const now = new Date().toISOString()
146
156
  if (
147
157
  currentStatus?.reviewState === REVIEWESCALATED &&
148
- subjectStatus.reviewState === REVIEWOPEN
158
+ subjectStatus.reviewState !== REVIEWCLOSED
149
159
  ) {
150
- // If the current status is escalated and the incoming event is to open the review
151
- // We want to keep the status as escalated
160
+ // If the current status is escalated only allow incoming events to move the state to
161
+ // reviewClosed because escalated subjects should never move to any other state
152
162
  subjectStatus.reviewState = REVIEWESCALATED
153
163
  }
154
164
 
165
+ if (currentStatus && subjectStatus.reviewState === REVIEWNONE) {
166
+ // reviewNone is ONLY allowed when there is no current status
167
+ // If there is a current status, it should not be allowed to move back to reviewNone
168
+ subjectStatus.reviewState = currentStatus.reviewState
169
+ }
170
+
155
171
  // Set these because we don't want to override them if they're already set
156
172
  const defaultData = {
157
173
  comment: null,
158
174
  // Defaulting reviewState to open for any event may not be the desired behavior.
159
175
  // For instance, if a subject never had any event and we just want to leave a comment to keep an eye on it
160
176
  // that shouldn't mean we want to review the subject
161
- reviewState: REVIEWOPEN,
177
+ reviewState: REVIEWNONE,
162
178
  recordCid: subjectCid || null,
163
179
  }
164
180
  const newStatus = {
@@ -11,7 +11,7 @@ Object {
11
11
  "cid": "cids(0)",
12
12
  "cts": "1970-01-01T00:00:00.000Z",
13
13
  "neg": false,
14
- "src": "user(1)",
14
+ "src": "user(2)",
15
15
  "uri": "record(0)",
16
16
  "val": "!unspecced-takedown",
17
17
  },
@@ -30,7 +30,7 @@ Object {
30
30
  "id": 1,
31
31
  "lastReportedAt": "1970-01-01T00:00:00.000Z",
32
32
  "lastReviewedAt": "1970-01-01T00:00:00.000Z",
33
- "lastReviewedBy": "did:example:admin",
33
+ "lastReviewedBy": "user(1)",
34
34
  "reviewState": "com.atproto.admin.defs#reviewClosed",
35
35
  "subject": Object {
36
36
  "$type": "com.atproto.repo.strongRef",
@@ -108,7 +108,7 @@ Object {
108
108
  "cid": "cids(0)",
109
109
  "cts": "1970-01-01T00:00:00.000Z",
110
110
  "neg": false,
111
- "src": "user(1)",
111
+ "src": "user(2)",
112
112
  "uri": "record(0)",
113
113
  "val": "!unspecced-takedown",
114
114
  },
@@ -127,7 +127,7 @@ Object {
127
127
  "id": 1,
128
128
  "lastReportedAt": "1970-01-01T00:00:00.000Z",
129
129
  "lastReviewedAt": "1970-01-01T00:00:00.000Z",
130
- "lastReviewedBy": "did:example:admin",
130
+ "lastReviewedBy": "user(1)",
131
131
  "reviewState": "com.atproto.admin.defs#reviewClosed",
132
132
  "subject": Object {
133
133
  "$type": "com.atproto.repo.strongRef",
@@ -12,7 +12,7 @@ Object {
12
12
  Object {
13
13
  "cts": "1970-01-01T00:00:00.000Z",
14
14
  "neg": false,
15
- "src": "user(1)",
15
+ "src": "user(2)",
16
16
  "uri": "user(0)",
17
17
  "val": "!unspecced-takedown",
18
18
  },
@@ -23,7 +23,7 @@ Object {
23
23
  "id": 1,
24
24
  "lastReportedAt": "1970-01-01T00:00:00.000Z",
25
25
  "lastReviewedAt": "1970-01-01T00:00:00.000Z",
26
- "lastReviewedBy": "did:example:admin",
26
+ "lastReviewedBy": "user(1)",
27
27
  "reviewState": "com.atproto.admin.defs#reviewClosed",
28
28
  "subject": Object {
29
29
  "$type": "com.atproto.admin.defs#repoRef",
@@ -31,7 +31,7 @@ describe('communication-templates', () => {
31
31
  await agent.api.com.atproto.admin.listCommunicationTemplates(
32
32
  {},
33
33
  {
34
- headers: network.ozone.adminAuthHeaders('moderator'),
34
+ headers: await network.ozone.modHeaders('moderator'),
35
35
  },
36
36
  )
37
37
  return data.communicationTemplates
@@ -44,7 +44,7 @@ describe('communication-templates', () => {
44
44
  { ...templateOne, createdBy: sc.dids.bob },
45
45
  {
46
46
  encoding: 'application/json',
47
- headers: network.ozone.adminAuthHeaders('moderator'),
47
+ headers: await network.ozone.modHeaders('moderator'),
48
48
  },
49
49
  )
50
50
  await expect(moderatorReq).rejects.toThrow(
@@ -55,7 +55,7 @@ describe('communication-templates', () => {
55
55
  { ...templateOne, createdBy: sc.dids.bob },
56
56
  {
57
57
  encoding: 'application/json',
58
- headers: network.ozone.adminAuthHeaders('admin'),
58
+ headers: await network.ozone.modHeaders('admin'),
59
59
  },
60
60
  )
61
61
 
@@ -79,7 +79,7 @@ describe('communication-templates', () => {
79
79
  { ...templateTwo, createdBy: sc.dids.bob },
80
80
  {
81
81
  encoding: 'application/json',
82
- headers: network.ozone.adminAuthHeaders('admin'),
82
+ headers: await network.ozone.modHeaders('admin'),
83
83
  },
84
84
  )
85
85
 
@@ -95,7 +95,7 @@ describe('communication-templates', () => {
95
95
  { id: '1', updatedBy: sc.dids.bob, name: '1 Test template' },
96
96
  {
97
97
  encoding: 'application/json',
98
- headers: network.ozone.adminAuthHeaders('admin'),
98
+ headers: await network.ozone.modHeaders('admin'),
99
99
  },
100
100
  )
101
101
 
@@ -109,7 +109,7 @@ describe('communication-templates', () => {
109
109
  { id: '1' },
110
110
  {
111
111
  encoding: 'application/json',
112
- headers: network.ozone.adminAuthHeaders('moderator'),
112
+ headers: await network.ozone.modHeaders('moderator'),
113
113
  },
114
114
  )
115
115
 
@@ -121,7 +121,7 @@ describe('communication-templates', () => {
121
121
  { id: '1' },
122
122
  {
123
123
  encoding: 'application/json',
124
- headers: network.ozone.adminAuthHeaders('admin'),
124
+ headers: await network.ozone.modHeaders('admin'),
125
125
  },
126
126
  )
127
127
  const list = await listTemplates()
@@ -1,4 +1,10 @@
1
- import { SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env'
1
+ import {
2
+ SeedClient,
3
+ TestNetwork,
4
+ basicSeed,
5
+ TestOzone,
6
+ ModeratorClient,
7
+ } from '@atproto/dev-env'
2
8
  import AtpAgent from '@atproto/api'
3
9
  import { AtUri } from '@atproto/syntax'
4
10
  import {
@@ -9,15 +15,19 @@ import { forSnapshot } from './_util'
9
15
 
10
16
  describe('admin get record view', () => {
11
17
  let network: TestNetwork
18
+ let ozone: TestOzone
12
19
  let agent: AtpAgent
13
20
  let sc: SeedClient
21
+ let modClient: ModeratorClient
14
22
 
15
23
  beforeAll(async () => {
16
24
  network = await TestNetwork.create({
17
25
  dbPostgresSchema: 'ozone_admin_get_record',
18
26
  })
19
- agent = network.pds.getClient()
27
+ ozone = network.ozone
28
+ agent = ozone.getClient()
20
29
  sc = network.getSeedClient()
30
+ modClient = ozone.getModClient()
21
31
  await basicSeed(sc)
22
32
  await network.processAll()
23
33
  })
@@ -46,7 +56,7 @@ describe('admin get record view', () => {
46
56
  cid: sc.posts[sc.dids.alice][0].ref.cidStr,
47
57
  },
48
58
  })
49
- await sc.emitModerationEvent({
59
+ await modClient.emitModerationEvent({
50
60
  event: { $type: 'com.atproto.admin.defs#modEventTakedown' },
51
61
  subject: {
52
62
  $type: 'com.atproto.repo.strongRef',
@@ -59,7 +69,7 @@ describe('admin get record view', () => {
59
69
  it('gets a record by uri, even when taken down.', async () => {
60
70
  const result = await agent.api.com.atproto.admin.getRecord(
61
71
  { uri: sc.posts[sc.dids.alice][0].ref.uriStr },
62
- { headers: network.pds.adminAuthHeaders() },
72
+ { headers: await ozone.modHeaders() },
63
73
  )
64
74
  expect(forSnapshot(result.data)).toMatchSnapshot()
65
75
  })
@@ -70,7 +80,7 @@ describe('admin get record view', () => {
70
80
  uri: sc.posts[sc.dids.alice][0].ref.uriStr,
71
81
  cid: sc.posts[sc.dids.alice][0].ref.cidStr,
72
82
  },
73
- { headers: network.pds.adminAuthHeaders() },
83
+ { headers: await ozone.modHeaders() },
74
84
  )
75
85
  expect(forSnapshot(result.data)).toMatchSnapshot()
76
86
  })
@@ -84,7 +94,7 @@ describe('admin get record view', () => {
84
94
  'badrkey',
85
95
  ).toString(),
86
96
  },
87
- { headers: network.pds.adminAuthHeaders() },
97
+ { headers: await ozone.modHeaders() },
88
98
  )
89
99
  await expect(promise).rejects.toThrow('Record not found')
90
100
  })
@@ -95,7 +105,7 @@ describe('admin get record view', () => {
95
105
  uri: sc.posts[sc.dids.alice][0].ref.uriStr,
96
106
  cid: sc.posts[sc.dids.alice][1].ref.cidStr, // Mismatching cid
97
107
  },
98
- { headers: network.pds.adminAuthHeaders() },
108
+ { headers: await ozone.modHeaders() },
99
109
  )
100
110
  await expect(promise).rejects.toThrow('Record not found')
101
111
  })
@@ -1,4 +1,10 @@
1
- import { SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env'
1
+ import {
2
+ SeedClient,
3
+ TestNetwork,
4
+ TestOzone,
5
+ basicSeed,
6
+ ModeratorClient,
7
+ } from '@atproto/dev-env'
2
8
  import AtpAgent from '@atproto/api'
3
9
  import {
4
10
  REASONOTHER,
@@ -8,15 +14,21 @@ import { forSnapshot } from './_util'
8
14
 
9
15
  describe('admin get repo view', () => {
10
16
  let network: TestNetwork
17
+ let ozone: TestOzone
11
18
  let agent: AtpAgent
19
+ let pdsAgent: AtpAgent
12
20
  let sc: SeedClient
21
+ let modClient: ModeratorClient
13
22
 
14
23
  beforeAll(async () => {
15
24
  network = await TestNetwork.create({
16
25
  dbPostgresSchema: 'ozone_admin_get_repo',
17
26
  })
18
- agent = network.pds.getClient()
27
+ ozone = network.ozone
28
+ agent = ozone.getClient()
29
+ pdsAgent = network.pds.getClient()
19
30
  sc = network.getSeedClient()
31
+ modClient = ozone.getModClient()
20
32
  await basicSeed(sc)
21
33
  await network.processAll()
22
34
  })
@@ -26,7 +38,7 @@ describe('admin get repo view', () => {
26
38
  })
27
39
 
28
40
  beforeAll(async () => {
29
- await sc.emitModerationEvent({
41
+ await modClient.emitModerationEvent({
30
42
  event: { $type: 'com.atproto.admin.defs#modEventAcknowledge' },
31
43
  subject: {
32
44
  $type: 'com.atproto.admin.defs#repoRef',
@@ -50,7 +62,7 @@ describe('admin get repo view', () => {
50
62
  did: sc.dids.alice,
51
63
  },
52
64
  })
53
- await sc.emitModerationEvent({
65
+ await modClient.emitModerationEvent({
54
66
  event: { $type: 'com.atproto.admin.defs#modEventTakedown' },
55
67
  subject: {
56
68
  $type: 'com.atproto.admin.defs#repoRef',
@@ -62,7 +74,7 @@ describe('admin get repo view', () => {
62
74
  it('gets a repo by did, even when taken down.', async () => {
63
75
  const result = await agent.api.com.atproto.admin.getRepo(
64
76
  { did: sc.dids.alice },
65
- { headers: network.pds.adminAuthHeaders() },
77
+ { headers: await ozone.modHeaders() },
66
78
  )
67
79
  expect(forSnapshot(result.data)).toMatchSnapshot()
68
80
  })
@@ -70,15 +82,15 @@ describe('admin get repo view', () => {
70
82
  it('does not include account emails for triage mods.', async () => {
71
83
  const { data: admin } = await agent.api.com.atproto.admin.getRepo(
72
84
  { did: sc.dids.bob },
73
- { headers: network.pds.adminAuthHeaders() },
85
+ { headers: await ozone.modHeaders() },
74
86
  )
75
87
  const { data: moderator } = await agent.api.com.atproto.admin.getRepo(
76
88
  { did: sc.dids.bob },
77
- { headers: network.pds.adminAuthHeaders('moderator') },
89
+ { headers: await ozone.modHeaders('moderator') },
78
90
  )
79
91
  const { data: triage } = await agent.api.com.atproto.admin.getRepo(
80
92
  { did: sc.dids.bob },
81
- { headers: network.pds.adminAuthHeaders('triage') },
93
+ { headers: await ozone.modHeaders('triage') },
82
94
  )
83
95
  expect(admin.email).toEqual('bob@test.com')
84
96
  expect(moderator.email).toEqual('bob@test.com')
@@ -90,7 +102,7 @@ describe('admin get repo view', () => {
90
102
  const { data: beforeEmailVerification } =
91
103
  await agent.api.com.atproto.admin.getRepo(
92
104
  { did: sc.dids.bob },
93
- { headers: network.pds.adminAuthHeaders() },
105
+ { headers: await ozone.modHeaders() },
94
106
  )
95
107
 
96
108
  expect(beforeEmailVerification.emailConfirmedAt).toBeUndefined()
@@ -101,7 +113,7 @@ describe('admin get repo view', () => {
101
113
  sc.dids.bob,
102
114
  'confirm_email',
103
115
  )
104
- await agent.api.com.atproto.server.confirmEmail(
116
+ await pdsAgent.api.com.atproto.server.confirmEmail(
105
117
  { email: bobsAccount.email, token: verificationToken },
106
118
  {
107
119
  encoding: 'application/json',
@@ -112,7 +124,7 @@ describe('admin get repo view', () => {
112
124
  const { data: afterEmailVerification } =
113
125
  await agent.api.com.atproto.admin.getRepo(
114
126
  { did: sc.dids.bob },
115
- { headers: network.pds.adminAuthHeaders() },
127
+ { headers: await ozone.modHeaders() },
116
128
  )
117
129
 
118
130
  expect(afterEmailVerification.emailConfirmedAt).toBeTruthy()
@@ -124,7 +136,7 @@ describe('admin get repo view', () => {
124
136
  it('fails when repo does not exist.', async () => {
125
137
  const promise = agent.api.com.atproto.admin.getRepo(
126
138
  { did: 'did:plc:doesnotexist' },
127
- { headers: network.pds.adminAuthHeaders() },
139
+ { headers: await ozone.modHeaders() },
128
140
  )
129
141
  await expect(promise).rejects.toThrow('Repo not found')
130
142
  })