@atproto/ozone 0.0.12 → 0.0.14

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 (47) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/api/label/queryLabels.d.ts +3 -0
  3. package/dist/api/label/subscribeLabels.d.ts +3 -0
  4. package/dist/config/config.d.ts +3 -0
  5. package/dist/config/env.d.ts +3 -0
  6. package/dist/context.d.ts +3 -0
  7. package/dist/db/index.js +3 -1
  8. package/dist/db/index.js.map +2 -2
  9. package/dist/db/schema/label.d.ts +4 -0
  10. package/dist/index.js +593 -244
  11. package/dist/index.js.map +3 -3
  12. package/dist/logger.d.ts +1 -0
  13. package/dist/mod-service/util.d.ts +3 -0
  14. package/dist/sequencer/index.d.ts +2 -0
  15. package/dist/sequencer/outbox.d.ts +16 -0
  16. package/dist/sequencer/sequencer.d.ts +33 -0
  17. package/package.json +10 -10
  18. package/src/api/admin/emitModerationEvent.ts +16 -10
  19. package/src/api/index.ts +4 -0
  20. package/src/api/label/queryLabels.ts +58 -0
  21. package/src/api/label/subscribeLabels.ts +25 -0
  22. package/src/api/temp/fetchLabels.ts +2 -4
  23. package/src/config/config.ts +6 -0
  24. package/src/config/env.ts +6 -0
  25. package/src/context.ts +12 -0
  26. package/src/db/migrations/20231219T205730722Z-init.ts +7 -1
  27. package/src/db/schema/label.ts +7 -0
  28. package/src/index.ts +2 -0
  29. package/src/lexicon/lexicons.ts +1 -1
  30. package/src/logger.ts +2 -0
  31. package/src/mod-service/index.ts +73 -72
  32. package/src/mod-service/status.ts +3 -0
  33. package/src/mod-service/util.ts +17 -0
  34. package/src/mod-service/views.ts +2 -5
  35. package/src/sequencer/index.ts +2 -0
  36. package/src/sequencer/outbox.ts +122 -0
  37. package/src/sequencer/sequencer.ts +143 -0
  38. package/tests/__snapshots__/moderation-events.test.ts.snap +53 -75
  39. package/tests/__snapshots__/moderation.test.ts.snap +4 -4
  40. package/tests/moderation-appeals.test.ts +19 -7
  41. package/tests/moderation-events.test.ts +7 -7
  42. package/tests/moderation-statuses.test.ts +2 -2
  43. package/tests/moderation.test.ts +14 -13
  44. package/tests/query-labels.test.ts +163 -0
  45. package/tests/repo-search.test.ts +0 -1
  46. package/tests/sequencer.test.ts +222 -0
  47. package/tests/server.test.ts +2 -0
@@ -24,6 +24,7 @@ import {
24
24
  } from './types'
25
25
  import { REASONOTHER } from '../lexicon/types/com/atproto/moderation/defs'
26
26
  import { subjectFromEventRow, subjectFromStatusRow } from './subject'
27
+ import { formatLabel } from './util'
27
28
 
28
29
  export type AppviewAuth = () => Promise<
29
30
  | {
@@ -403,11 +404,7 @@ export class ModerationViews {
403
404
  .if(!includeNeg, (qb) => qb.where('neg', '=', false))
404
405
  .selectAll()
405
406
  .execute()
406
- return res.map((l) => ({
407
- ...l,
408
- cid: l.cid === '' ? undefined : l.cid,
409
- neg: l.neg,
410
- }))
407
+ return res.map((l) => formatLabel(l))
411
408
  }
412
409
 
413
410
  async getSubjectStatus(
@@ -0,0 +1,2 @@
1
+ export * from './sequencer'
2
+ export * from './outbox'
@@ -0,0 +1,122 @@
1
+ import { AsyncBuffer, AsyncBufferFullError } from '@atproto/common'
2
+ import { InvalidRequestError } from '@atproto/xrpc-server'
3
+ import { Sequencer, LabelsEvt } from './sequencer'
4
+
5
+ export type OutboxOpts = {
6
+ maxBufferSize: number
7
+ }
8
+
9
+ export class Outbox {
10
+ private caughtUp = false
11
+ lastSeen = -1
12
+
13
+ cutoverBuffer: LabelsEvt[]
14
+ outBuffer: AsyncBuffer<LabelsEvt>
15
+
16
+ constructor(public sequencer: Sequencer, opts: Partial<OutboxOpts> = {}) {
17
+ const { maxBufferSize = 500 } = opts
18
+ this.cutoverBuffer = []
19
+ this.outBuffer = new AsyncBuffer<LabelsEvt>(maxBufferSize)
20
+ }
21
+
22
+ // event stream occurs in 3 phases
23
+ // 1. backfill events: events that have been added to the DB since the last time a connection was open.
24
+ // The outbox is not yet listening for new events from the sequencer
25
+ // 2. cutover: the outbox has caught up with where the sequencer purports to be,
26
+ // but the sequencer might already be halfway through sending out a round of updates.
27
+ // Therefore, we start accepting the sequencer's events in a buffer, while making our own request to the
28
+ // database to ensure we're caught up. We then dedupe the query & the buffer & stream the events in order
29
+ // 3. streaming: we're all caught up on historic state, so the sequencer outputs events and we
30
+ // immediately yield them
31
+ async *events(
32
+ backfillCursor?: number,
33
+ signal?: AbortSignal,
34
+ ): AsyncGenerator<LabelsEvt> {
35
+ // catch up as much as we can
36
+ if (backfillCursor !== undefined) {
37
+ for await (const evt of this.getBackfill(backfillCursor)) {
38
+ if (signal?.aborted) return
39
+ this.lastSeen = evt.seq
40
+ yield evt
41
+ }
42
+ } else {
43
+ // if not backfill, we don't need to cutover, just start streaming
44
+ this.caughtUp = true
45
+ }
46
+
47
+ // streams updates from sequencer, but buffers them for cutover as it makes a last request
48
+
49
+ const addToBuffer = (evts) => {
50
+ if (this.caughtUp) {
51
+ this.outBuffer.pushMany(evts)
52
+ } else {
53
+ this.cutoverBuffer = [...this.cutoverBuffer, ...evts]
54
+ }
55
+ }
56
+
57
+ if (!signal?.aborted) {
58
+ this.sequencer.on('events', addToBuffer)
59
+ }
60
+ signal?.addEventListener('abort', () =>
61
+ this.sequencer.off('events', addToBuffer),
62
+ )
63
+
64
+ const cutover = async () => {
65
+ // only need to perform cutover if we've been backfilling
66
+ if (backfillCursor !== undefined) {
67
+ const cutoverEvts = await this.sequencer.requestLabelRange({
68
+ earliestId: this.lastSeen > -1 ? this.lastSeen : backfillCursor,
69
+ })
70
+ this.outBuffer.pushMany(cutoverEvts)
71
+ // dont worry about dupes, we ensure order on yield
72
+ this.outBuffer.pushMany(this.cutoverBuffer)
73
+ this.caughtUp = true
74
+ this.cutoverBuffer = []
75
+ } else {
76
+ this.caughtUp = true
77
+ }
78
+ }
79
+ cutover()
80
+
81
+ while (true) {
82
+ try {
83
+ for await (const evt of this.outBuffer.events()) {
84
+ if (signal?.aborted) return
85
+ if (evt.seq > this.lastSeen) {
86
+ this.lastSeen = evt.seq
87
+ yield evt
88
+ }
89
+ }
90
+ } catch (err) {
91
+ if (err instanceof AsyncBufferFullError) {
92
+ throw new InvalidRequestError(
93
+ 'Stream consumer too slow',
94
+ 'ConsumerTooSlow',
95
+ )
96
+ } else {
97
+ throw err
98
+ }
99
+ }
100
+ }
101
+ }
102
+
103
+ // yields only historical events
104
+ async *getBackfill(backfillCursor: number) {
105
+ const PAGE_SIZE = 500
106
+ while (true) {
107
+ const evts = await this.sequencer.requestLabelRange({
108
+ earliestId: this.lastSeen > -1 ? this.lastSeen : backfillCursor,
109
+ limit: PAGE_SIZE,
110
+ })
111
+ for (const evt of evts) {
112
+ yield evt
113
+ }
114
+ // if we're within half a pagesize of the sequencer, we call it good & switch to cutover
115
+ const seqCursor = this.sequencer.lastSeen ?? -1
116
+ if (seqCursor - this.lastSeen < PAGE_SIZE / 2) break
117
+ if (evts.length < 1) break
118
+ }
119
+ }
120
+ }
121
+
122
+ export default Outbox
@@ -0,0 +1,143 @@
1
+ import EventEmitter from 'events'
2
+ import TypedEmitter from 'typed-emitter'
3
+ import { seqLogger as log } from '../logger'
4
+ import Database from '../db'
5
+ import { Labels as LabelsEvt } from '../lexicon/types/com/atproto/label/subscribeLabels'
6
+ import { LabelChannel, Label as LabelTable } from '../db/schema/label'
7
+ import { Selectable } from 'kysely'
8
+ import { formatLabel } from '../mod-service/util'
9
+ import { PoolClient } from 'pg'
10
+
11
+ export type { Labels as LabelsEvt } from '../lexicon/types/com/atproto/label/subscribeLabels'
12
+ type LabelRow = Selectable<LabelTable>
13
+
14
+ export class Sequencer extends (EventEmitter as new () => SequencerEmitter) {
15
+ destroyed = false
16
+ pollPromise: Promise<void> | undefined
17
+ queued = false
18
+ conn: PoolClient | undefined
19
+
20
+ constructor(public db: Database, public lastSeen = 0) {
21
+ super()
22
+ // note: this does not err when surpassed, just prints a warning to stderr
23
+ this.setMaxListeners(100)
24
+ }
25
+
26
+ async start() {
27
+ const curr = await this.curr()
28
+ this.lastSeen = curr ?? 0
29
+ this.poll()
30
+ this.conn = await this.db.pool.connect()
31
+ this.conn.query(`listen ${LabelChannel}`) // if this errors, unhandled rejection should cause process to exit
32
+ this.conn.on('notification', (notif) => {
33
+ if (notif.channel === LabelChannel) {
34
+ this.poll()
35
+ }
36
+ })
37
+ }
38
+
39
+ async destroy() {
40
+ if (this.destroyed) return
41
+ this.destroyed = true
42
+ if (this.conn) {
43
+ this.conn.release()
44
+ this.conn = undefined
45
+ }
46
+ if (this.pollPromise) {
47
+ await this.pollPromise
48
+ }
49
+ this.emit('close')
50
+ }
51
+
52
+ async curr(): Promise<number | null> {
53
+ const got = await this.db.db
54
+ .selectFrom('label')
55
+ .selectAll()
56
+ .orderBy('id', 'desc')
57
+ .limit(1)
58
+ .executeTakeFirst()
59
+ return got?.id ?? null
60
+ }
61
+
62
+ async next(cursor: number): Promise<LabelRow | null> {
63
+ const got = await this.db.db
64
+ .selectFrom('label')
65
+ .selectAll()
66
+ .where('id', '>', cursor)
67
+ .limit(1)
68
+ .orderBy('id', 'asc')
69
+ .executeTakeFirst()
70
+ return got || null
71
+ }
72
+
73
+ async requestLabelRange(opts: {
74
+ earliestId?: number
75
+ limit?: number
76
+ }): Promise<LabelsEvt[]> {
77
+ const { earliestId, limit } = opts
78
+
79
+ let seqQb = this.db.db.selectFrom('label').selectAll().orderBy('id', 'asc')
80
+ if (earliestId !== undefined) {
81
+ seqQb = seqQb.where('id', '>', earliestId)
82
+ }
83
+ if (limit !== undefined) {
84
+ seqQb = seqQb.limit(limit)
85
+ }
86
+
87
+ const rows = await seqQb.execute()
88
+ if (rows.length < 1) {
89
+ return []
90
+ }
91
+
92
+ const evts: LabelsEvt[] = []
93
+ for (const row of rows) {
94
+ evts.push({
95
+ seq: row.id,
96
+ labels: [formatLabel(row)],
97
+ })
98
+ }
99
+
100
+ return evts
101
+ }
102
+
103
+ private poll() {
104
+ if (this.destroyed) return
105
+ if (this.pollPromise) {
106
+ this.queued = true
107
+ return
108
+ }
109
+ this.queued = false
110
+ this.pollPromise = this.requestLabelRange({
111
+ earliestId: this.lastSeen,
112
+ limit: 500,
113
+ })
114
+ .then((evts) => {
115
+ this.emit('events', evts)
116
+ this.lastSeen = evts.at(-1)?.seq ?? this.lastSeen
117
+ if (evts.length > 0) {
118
+ this.queued = true
119
+ }
120
+ })
121
+ .catch((err) => {
122
+ log.error(
123
+ { err, lastSeen: this.lastSeen },
124
+ 'sequencer failed to poll db',
125
+ )
126
+ })
127
+ .finally(() => {
128
+ this.pollPromise = undefined
129
+ if (this.queued) {
130
+ this.poll()
131
+ }
132
+ })
133
+ }
134
+ }
135
+
136
+ type SequencerEvents = {
137
+ events: (evts: LabelsEvt[]) => void
138
+ close: () => void
139
+ }
140
+
141
+ export type SequencerEmitter = TypedEmitter<SequencerEvents>
142
+
143
+ export default Sequencer
@@ -5,85 +5,63 @@ Object {
5
5
  "createdAt": "1970-01-01T00:00:00.000Z",
6
6
  "createdBy": "user(2)",
7
7
  "event": Object {
8
- "$type": "com.atproto.admin.defs#modEventLabel",
9
- "comment": "[AutoModerator]: Applying labels",
10
- "createLabelVals": Array [
11
- "test-label",
12
- ],
13
- "negateLabelVals": Array [],
8
+ "$type": "com.atproto.admin.defs#modEventReport",
9
+ "comment": "X",
10
+ "reportType": "com.atproto.moderation.defs#reasonMisleading",
14
11
  },
15
12
  "id": 1,
16
13
  "subject": Object {
17
- "$type": "com.atproto.admin.defs#recordView",
18
- "blobCids": Array [],
19
- "cid": "cids(0)",
14
+ "$type": "com.atproto.admin.defs#repoView",
15
+ "did": "user(0)",
16
+ "handle": "alice.test",
20
17
  "indexedAt": "1970-01-01T00:00:00.000Z",
21
- "moderation": Object {},
22
- "repo": Object {
23
- "did": "user(0)",
24
- "handle": "alice.test",
25
- "indexedAt": "1970-01-01T00:00:00.000Z",
26
- "moderation": Object {
27
- "subjectStatus": Object {
28
- "createdAt": "1970-01-01T00:00:00.000Z",
29
- "id": 1,
30
- "lastReportedAt": "1970-01-01T00:00:00.000Z",
31
- "lastReviewedAt": "1970-01-01T00:00:00.000Z",
32
- "lastReviewedBy": "user(1)",
33
- "reviewState": "com.atproto.admin.defs#reviewEscalated",
34
- "subject": Object {
35
- "$type": "com.atproto.admin.defs#repoRef",
36
- "did": "user(0)",
37
- },
38
- "subjectBlobCids": Array [],
39
- "subjectRepoHandle": "alice.test",
40
- "tags": Array [
41
- "lang:und",
42
- ],
43
- "takendown": false,
44
- "updatedAt": "1970-01-01T00:00:00.000Z",
18
+ "moderation": Object {
19
+ "subjectStatus": Object {
20
+ "createdAt": "1970-01-01T00:00:00.000Z",
21
+ "id": 1,
22
+ "lastReportedAt": "1970-01-01T00:00:00.000Z",
23
+ "lastReviewedAt": "1970-01-01T00:00:00.000Z",
24
+ "lastReviewedBy": "user(1)",
25
+ "reviewState": "com.atproto.admin.defs#reviewEscalated",
26
+ "subject": Object {
27
+ "$type": "com.atproto.admin.defs#repoRef",
28
+ "did": "user(0)",
45
29
  },
30
+ "subjectBlobCids": Array [],
31
+ "subjectRepoHandle": "alice.test",
32
+ "tags": Array [
33
+ "lang:und",
34
+ ],
35
+ "takendown": false,
36
+ "updatedAt": "1970-01-01T00:00:00.000Z",
46
37
  },
47
- "relatedRecords": Array [
48
- Object {
49
- "$type": "app.bsky.actor.profile",
50
- "avatar": Object {
51
- "$type": "blob",
52
- "mimeType": "image/jpeg",
53
- "ref": Object {
54
- "$link": "cids(2)",
55
- },
56
- "size": 3976,
57
- },
58
- "description": "its me!",
59
- "displayName": "ali",
60
- "labels": Object {
61
- "$type": "com.atproto.label.defs#selfLabels",
62
- "values": Array [
63
- Object {
64
- "val": "self-label-a",
65
- },
66
- Object {
67
- "val": "self-label-b",
68
- },
69
- ],
38
+ },
39
+ "relatedRecords": Array [
40
+ Object {
41
+ "$type": "app.bsky.actor.profile",
42
+ "avatar": Object {
43
+ "$type": "blob",
44
+ "mimeType": "image/jpeg",
45
+ "ref": Object {
46
+ "$link": "cids(0)",
70
47
  },
48
+ "size": 3976,
71
49
  },
72
- ],
73
- },
74
- "uri": "record(0)",
75
- "value": Object {
76
- "$type": "app.bsky.feed.post",
77
- "createdAt": "1970-01-01T00:00:00.000Z",
78
- "embed": Object {
79
- "$type": "app.bsky.embed.record",
80
- "record": Object {
81
- "cid": "cids(1)",
82
- "uri": "record(1)",
50
+ "description": "its me!",
51
+ "displayName": "ali",
52
+ "labels": Object {
53
+ "$type": "com.atproto.label.defs#selfLabels",
54
+ "values": Array [
55
+ Object {
56
+ "val": "self-label-a",
57
+ },
58
+ Object {
59
+ "val": "self-label-b",
60
+ },
61
+ ],
83
62
  },
84
63
  },
85
- "text": "yoohoo label_me",
86
- },
64
+ ],
87
65
  },
88
66
  "subjectBlobCids": Array [],
89
67
  "subjectBlobs": Array [],
@@ -101,7 +79,7 @@ Array [
101
79
  "comment": "X",
102
80
  "reportType": "com.atproto.moderation.defs#reasonSpam",
103
81
  },
104
- "id": 13,
82
+ "id": 11,
105
83
  "subject": Object {
106
84
  "$type": "com.atproto.admin.defs#repoRef",
107
85
  "did": "user(0)",
@@ -120,7 +98,7 @@ Array [
120
98
  ],
121
99
  "remove": Array [],
122
100
  },
123
- "id": 8,
101
+ "id": 6,
124
102
  "subject": Object {
125
103
  "$type": "com.atproto.admin.defs#repoRef",
126
104
  "did": "user(0)",
@@ -137,7 +115,7 @@ Array [
137
115
  "comment": "X",
138
116
  "reportType": "com.atproto.moderation.defs#reasonSpam",
139
117
  },
140
- "id": 7,
118
+ "id": 5,
141
119
  "subject": Object {
142
120
  "$type": "com.atproto.admin.defs#repoRef",
143
121
  "did": "user(0)",
@@ -159,7 +137,7 @@ Array [
159
137
  "comment": "X",
160
138
  "reportType": "com.atproto.moderation.defs#reasonSpam",
161
139
  },
162
- "id": 12,
140
+ "id": 10,
163
141
  "subject": Object {
164
142
  "$type": "com.atproto.repo.strongRef",
165
143
  "cid": "cids(0)",
@@ -178,7 +156,7 @@ Array [
178
156
  ],
179
157
  "remove": Array [],
180
158
  },
181
- "id": 6,
159
+ "id": 4,
182
160
  "subject": Object {
183
161
  "$type": "com.atproto.repo.strongRef",
184
162
  "cid": "cids(0)",
@@ -196,7 +174,7 @@ Array [
196
174
  "comment": "X",
197
175
  "reportType": "com.atproto.moderation.defs#reasonSpam",
198
176
  },
199
- "id": 5,
177
+ "id": 3,
200
178
  "subject": Object {
201
179
  "$type": "com.atproto.repo.strongRef",
202
180
  "cid": "cids(0)",
@@ -4,7 +4,7 @@ exports[`moderation reporting creates reports of a record. 1`] = `
4
4
  Array [
5
5
  Object {
6
6
  "createdAt": "1970-01-01T00:00:00.000Z",
7
- "id": 7,
7
+ "id": 5,
8
8
  "reasonType": "com.atproto.moderation.defs#reasonSpam",
9
9
  "reportedBy": "user(0)",
10
10
  "subject": Object {
@@ -15,7 +15,7 @@ Array [
15
15
  },
16
16
  Object {
17
17
  "createdAt": "1970-01-01T00:00:00.000Z",
18
- "id": 9,
18
+ "id": 7,
19
19
  "reason": "defamation",
20
20
  "reasonType": "com.atproto.moderation.defs#reasonOther",
21
21
  "reportedBy": "user(1)",
@@ -32,7 +32,7 @@ exports[`moderation reporting creates reports of a repo. 1`] = `
32
32
  Array [
33
33
  Object {
34
34
  "createdAt": "1970-01-01T00:00:00.000Z",
35
- "id": 3,
35
+ "id": 1,
36
36
  "reasonType": "com.atproto.moderation.defs#reasonSpam",
37
37
  "reportedBy": "user(0)",
38
38
  "subject": Object {
@@ -42,7 +42,7 @@ Array [
42
42
  },
43
43
  Object {
44
44
  "createdAt": "1970-01-01T00:00:00.000Z",
45
- "id": 5,
45
+ "id": 3,
46
46
  "reason": "impersonation",
47
47
  "reasonType": "com.atproto.moderation.defs#reasonOther",
48
48
  "reportedBy": "user(2)",
@@ -118,7 +118,7 @@ describe('moderation-appeals', () => {
118
118
  })
119
119
 
120
120
  // Verify that appeal status changed when appeal report was emitted by moderator
121
- const status = await assertBobsPostStatus(REVIEWOPEN, true)
121
+ const status = await assertBobsPostStatus(REVIEWESCALATED, true)
122
122
  expect(status?.appealedAt).not.toBeNull()
123
123
 
124
124
  // Create a report as normal user for carol's post
@@ -143,7 +143,11 @@ describe('moderation-appeals', () => {
143
143
  subject: getCarolPostSubject(),
144
144
  })
145
145
  // Verify that the appeal status on carol's post is true
146
- await assertSubjectStatus(getCarolPostSubject().uri, REVIEWOPEN, true)
146
+ await assertSubjectStatus(
147
+ getCarolPostSubject().uri,
148
+ REVIEWESCALATED,
149
+ true,
150
+ )
147
151
  })
148
152
  it('allows multiple appeals and updates last appealed timestamp', async () => {
149
153
  // Resolve appeal with acknowledge
@@ -155,7 +159,7 @@ describe('moderation-appeals', () => {
155
159
  createdBy: sc.dids.carol,
156
160
  })
157
161
 
158
- const previousStatus = await assertBobsPostStatus(REVIEWOPEN, false)
162
+ const previousStatus = await assertBobsPostStatus(REVIEWESCALATED, false)
159
163
 
160
164
  await emitModerationEvent({
161
165
  event: {
@@ -167,7 +171,7 @@ describe('moderation-appeals', () => {
167
171
  })
168
172
 
169
173
  // Verify that even after the appeal event by bob for his post, the appeal status is true again with new timestamp
170
- const newStatus = await assertBobsPostStatus(REVIEWOPEN, true)
174
+ const newStatus = await assertBobsPostStatus(REVIEWESCALATED, true)
171
175
  expect(
172
176
  new Date(`${previousStatus?.lastAppealedAt}`).getTime(),
173
177
  ).toBeLessThan(new Date(`${newStatus?.lastAppealedAt}`).getTime())
@@ -210,7 +214,11 @@ describe('moderation-appeals', () => {
210
214
  createdBy: sc.dids.alice,
211
215
  })
212
216
 
213
- await assertSubjectStatus(getAlicesPostSubject().uri, REVIEWOPEN, true)
217
+ await assertSubjectStatus(
218
+ getAlicesPostSubject().uri,
219
+ REVIEWESCALATED,
220
+ true,
221
+ )
214
222
 
215
223
  // Bob reports it again
216
224
  await emitModerationEvent({
@@ -222,8 +230,12 @@ describe('moderation-appeals', () => {
222
230
  createdBy: sc.dids.bob,
223
231
  })
224
232
 
225
- // Assert that the status is still REVIEWOPEN, as report events are meant to do
226
- await assertSubjectStatus(getAlicesPostSubject().uri, REVIEWOPEN, true)
233
+ // Assert that the status is still REVIEWESCALATED, as report events are meant to do
234
+ await assertSubjectStatus(
235
+ getAlicesPostSubject().uri,
236
+ REVIEWESCALATED,
237
+ true,
238
+ )
227
239
 
228
240
  // Emit an escalation event
229
241
  await emitModerationEvent({
@@ -22,13 +22,13 @@ describe('moderation-events', () => {
22
22
  ) => {
23
23
  return pdsAgent.api.com.atproto.admin.emitModerationEvent(eventData, {
24
24
  encoding: 'application/json',
25
- headers: network.bsky.adminAuthHeaders('moderator'),
25
+ headers: network.ozone.adminAuthHeaders('moderator'),
26
26
  })
27
27
  }
28
28
 
29
29
  const queryModerationEvents = (eventQuery) =>
30
30
  agent.api.com.atproto.admin.queryModerationEvents(eventQuery, {
31
- headers: network.bsky.adminAuthHeaders('moderator'),
31
+ headers: network.ozone.adminAuthHeaders('moderator'),
32
32
  })
33
33
 
34
34
  const seedEvents = async () => {
@@ -203,11 +203,11 @@ describe('moderation-events', () => {
203
203
  const defaultEvents = await getPaginatedEvents()
204
204
  const reversedEvents = await getPaginatedEvents('asc')
205
205
 
206
- expect(allEvents.data.events.length).toEqual(7)
206
+ expect(allEvents.data.events.length).toEqual(6)
207
207
  expect(defaultEvents.length).toEqual(allEvents.data.events.length)
208
208
  expect(reversedEvents.length).toEqual(allEvents.data.events.length)
209
209
  // First event in the reversed list is the last item in the default list
210
- expect(reversedEvents[0].id).toEqual(defaultEvents[6].id)
210
+ expect(reversedEvents[0].id).toEqual(defaultEvents[5].id)
211
211
  })
212
212
 
213
213
  it('returns report events matching reportType filters', async () => {
@@ -240,7 +240,7 @@ describe('moderation-events', () => {
240
240
 
241
241
  expect(eventsWithX.data.events.length).toEqual(10)
242
242
  expect(eventsWithTest.data.events.length).toEqual(0)
243
- expect(eventsWithComment.data.events.length).toEqual(12)
243
+ expect(eventsWithComment.data.events.length).toEqual(10)
244
244
  })
245
245
 
246
246
  it('returns events matching filter params for labels', async () => {
@@ -325,7 +325,7 @@ describe('moderation-events', () => {
325
325
  })
326
326
  const addEvent = await tagEvent({ add: ['L1', 'L2'], remove: [] })
327
327
  const addAndRemoveEvent = await tagEvent({ add: ['L3'], remove: ['L2'] })
328
- const [addFinder, addAndRemoveFinder, removeFinder] = await Promise.all([
328
+ const [addFinder, addAndRemoveFinder, _removeFinder] = await Promise.all([
329
329
  queryModerationEvents({
330
330
  addedTags: ['L1'],
331
331
  }),
@@ -356,7 +356,7 @@ describe('moderation-events', () => {
356
356
  it('gets an event by specific id', async () => {
357
357
  const { data } = await pdsAgent.api.com.atproto.admin.getModerationEvent(
358
358
  { id: 1 },
359
- { headers: network.bsky.adminAuthHeaders('moderator') },
359
+ { headers: network.ozone.adminAuthHeaders('moderator') },
360
360
  )
361
361
  expect(forSnapshot(data)).toMatchSnapshot()
362
362
  })
@@ -19,13 +19,13 @@ describe('moderation-statuses', () => {
19
19
  const emitModerationEvent = async (eventData) => {
20
20
  return pdsAgent.api.com.atproto.admin.emitModerationEvent(eventData, {
21
21
  encoding: 'application/json',
22
- headers: network.bsky.adminAuthHeaders('moderator'),
22
+ headers: network.ozone.adminAuthHeaders('moderator'),
23
23
  })
24
24
  }
25
25
 
26
26
  const queryModerationStatuses = (statusQuery) =>
27
27
  agent.api.com.atproto.admin.queryModerationStatuses(statusQuery, {
28
- headers: network.bsky.adminAuthHeaders('moderator'),
28
+ headers: network.ozone.adminAuthHeaders('moderator'),
29
29
  })
30
30
 
31
31
  const seedEvents = async () => {