@atproto/ozone 0.0.11 → 0.0.13

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 (54) 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 +875 -454
  11. package/dist/index.js.map +3 -3
  12. package/dist/lexicon/index.d.ts +2 -0
  13. package/dist/lexicon/lexicons.d.ts +27 -0
  14. package/dist/lexicon/types/app/bsky/actor/defs.d.ts +1 -1
  15. package/dist/lexicon/types/com/atproto/admin/updateAccountPassword.d.ts +26 -0
  16. package/dist/logger.d.ts +1 -0
  17. package/dist/mod-service/util.d.ts +3 -0
  18. package/dist/sequencer/index.d.ts +2 -0
  19. package/dist/sequencer/outbox.d.ts +16 -0
  20. package/dist/sequencer/sequencer.d.ts +33 -0
  21. package/package.json +10 -10
  22. package/src/api/admin/emitModerationEvent.ts +16 -10
  23. package/src/api/index.ts +4 -0
  24. package/src/api/label/queryLabels.ts +58 -0
  25. package/src/api/label/subscribeLabels.ts +25 -0
  26. package/src/api/temp/fetchLabels.ts +2 -4
  27. package/src/config/config.ts +6 -0
  28. package/src/config/env.ts +6 -0
  29. package/src/context.ts +12 -0
  30. package/src/db/migrations/20231219T205730722Z-init.ts +7 -1
  31. package/src/db/schema/label.ts +7 -0
  32. package/src/index.ts +2 -0
  33. package/src/lexicon/index.ts +12 -0
  34. package/src/lexicon/lexicons.ts +32 -1
  35. package/src/lexicon/types/app/bsky/actor/defs.ts +2 -0
  36. package/src/lexicon/types/com/atproto/admin/updateAccountPassword.ts +39 -0
  37. package/src/logger.ts +2 -0
  38. package/src/mod-service/index.ts +73 -72
  39. package/src/mod-service/status.ts +3 -0
  40. package/src/mod-service/util.ts +17 -0
  41. package/src/mod-service/views.ts +2 -5
  42. package/src/sequencer/index.ts +2 -0
  43. package/src/sequencer/outbox.ts +122 -0
  44. package/src/sequencer/sequencer.ts +143 -0
  45. package/tests/__snapshots__/moderation-events.test.ts.snap +53 -75
  46. package/tests/__snapshots__/moderation.test.ts.snap +4 -4
  47. package/tests/moderation-appeals.test.ts +19 -7
  48. package/tests/moderation-events.test.ts +7 -7
  49. package/tests/moderation-statuses.test.ts +2 -2
  50. package/tests/moderation.test.ts +14 -13
  51. package/tests/query-labels.test.ts +163 -0
  52. package/tests/repo-search.test.ts +0 -1
  53. package/tests/sequencer.test.ts +222 -0
  54. package/tests/server.test.ts +2 -0
@@ -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 () => {
@@ -607,7 +607,7 @@ describe('moderation', () => {
607
607
  },
608
608
  {
609
609
  encoding: 'application/json',
610
- headers: network.bsky.adminAuthHeaders('triage'),
610
+ headers: network.ozone.adminAuthHeaders('triage'),
611
611
  },
612
612
  )
613
613
  await expect(attemptLabel).rejects.toThrow(
@@ -748,7 +748,7 @@ describe('moderation', () => {
748
748
  },
749
749
  {
750
750
  encoding: 'application/json',
751
- headers: network.bsky.adminAuthHeaders('moderator'),
751
+ headers: network.ozone.adminAuthHeaders('moderator'),
752
752
  },
753
753
  )
754
754
  // cleanup
@@ -775,11 +775,11 @@ describe('moderation', () => {
775
775
  },
776
776
  {
777
777
  encoding: 'application/json',
778
- headers: network.bsky.adminAuthHeaders('triage'),
778
+ headers: network.ozone.adminAuthHeaders('triage'),
779
779
  },
780
780
  )
781
781
  await expect(attemptTakedownTriage).rejects.toThrow(
782
- 'Must be a full moderator to perform an account takedown',
782
+ 'Must be a full moderator to take this type of action',
783
783
  )
784
784
  })
785
785
 
@@ -800,7 +800,7 @@ describe('moderation', () => {
800
800
  const { data: statusesAfterTakedown } =
801
801
  await agent.api.com.atproto.admin.queryModerationStatuses(
802
802
  { subject: sc.dids.bob },
803
- { headers: network.bsky.adminAuthHeaders('moderator') },
803
+ { headers: network.ozone.adminAuthHeaders('moderator') },
804
804
  )
805
805
 
806
806
  expect(statusesAfterTakedown.subjectStatuses[0]).toMatchObject({
@@ -818,11 +818,11 @@ describe('moderation', () => {
818
818
  const [{ data: eventList }, { data: statuses }] = await Promise.all([
819
819
  agent.api.com.atproto.admin.queryModerationEvents(
820
820
  { subject: sc.dids.bob },
821
- { headers: network.bsky.adminAuthHeaders('moderator') },
821
+ { headers: network.ozone.adminAuthHeaders('moderator') },
822
822
  ),
823
823
  agent.api.com.atproto.admin.queryModerationStatuses(
824
824
  { subject: sc.dids.bob },
825
- { headers: network.bsky.adminAuthHeaders('moderator') },
825
+ { headers: network.ozone.adminAuthHeaders('moderator') },
826
826
  ),
827
827
  ])
828
828
 
@@ -879,7 +879,7 @@ describe('moderation', () => {
879
879
  },
880
880
  {
881
881
  encoding: 'application/json',
882
- headers: network.bsky.adminAuthHeaders(),
882
+ headers: network.ozone.adminAuthHeaders(),
883
883
  },
884
884
  )
885
885
  return result.data
@@ -901,7 +901,7 @@ describe('moderation', () => {
901
901
  },
902
902
  {
903
903
  encoding: 'application/json',
904
- headers: network.bsky.adminAuthHeaders(),
904
+ headers: network.ozone.adminAuthHeaders(),
905
905
  },
906
906
  )
907
907
  }
@@ -909,7 +909,7 @@ describe('moderation', () => {
909
909
  async function getRecordLabels(uri: string) {
910
910
  const result = await agent.api.com.atproto.admin.getRecord(
911
911
  { uri },
912
- { headers: network.bsky.adminAuthHeaders() },
912
+ { headers: network.ozone.adminAuthHeaders() },
913
913
  )
914
914
  const labels = result.data.labels ?? []
915
915
  return labels.map((l) => l.val)
@@ -918,7 +918,7 @@ describe('moderation', () => {
918
918
  async function getRepoLabels(did: string) {
919
919
  const result = await agent.api.com.atproto.admin.getRepo(
920
920
  { did },
921
- { headers: network.bsky.adminAuthHeaders() },
921
+ { headers: network.ozone.adminAuthHeaders() },
922
922
  )
923
923
  const labels = result.data.labels ?? []
924
924
  return labels.map((l) => l.val)
@@ -933,7 +933,7 @@ describe('moderation', () => {
933
933
  const { ctx } = network.bsky
934
934
  post = sc.posts[sc.dids.carol][0]
935
935
  blob = post.images[1]
936
- imageUri = ctx.imgUriBuilder
936
+ imageUri = ctx.views.imgUriBuilder
937
937
  .getPresetUri(
938
938
  'feed_thumbnail',
939
939
  sc.dids.carol,
@@ -974,7 +974,8 @@ describe('moderation', () => {
974
974
  })
975
975
  })
976
976
 
977
- it('prevents image blob from being served, even when cached.', async () => {
977
+ // @TODO add back in with image invalidation, see bluesky-social/atproto#2087
978
+ it.skip('prevents image blob from being served, even when cached.', async () => {
978
979
  const fetchImage = await fetch(imageUri)
979
980
  expect(fetchImage.status).toEqual(404)
980
981
  expect(await fetchImage.json()).toEqual({ message: 'Image not found' })
@@ -0,0 +1,163 @@
1
+ import AtpAgent from '@atproto/api'
2
+ import { TestNetwork } from '@atproto/dev-env'
3
+ import { DisconnectError, Subscription } from '@atproto/xrpc-server'
4
+ import { ids, lexicons } from '../src/lexicon/lexicons'
5
+ import { Label } from '../src/lexicon/types/com/atproto/label/defs'
6
+ import {
7
+ OutputSchema as LabelMessage,
8
+ isLabels,
9
+ } from '../src/lexicon/types/com/atproto/label/subscribeLabels'
10
+
11
+ describe('ozone query labels', () => {
12
+ let network: TestNetwork
13
+ let agent: AtpAgent
14
+
15
+ let labels: Label[]
16
+
17
+ beforeAll(async () => {
18
+ network = await TestNetwork.create({
19
+ dbPostgresSchema: 'ozone_query_labels',
20
+ })
21
+
22
+ agent = network.ozone.getClient()
23
+
24
+ labels = [
25
+ {
26
+ src: 'did:example:labeler',
27
+ uri: 'did:example:blah',
28
+ val: 'spam',
29
+ neg: false,
30
+ cts: new Date().toISOString(),
31
+ },
32
+ {
33
+ src: 'did:example:labeler',
34
+ uri: 'did:example:blah',
35
+ val: 'impersonation',
36
+ neg: false,
37
+ cts: new Date().toISOString(),
38
+ },
39
+ {
40
+ src: 'did:example:labeler',
41
+ uri: 'at://did:example:blah/app.bsky.feed.post/1234abcde',
42
+ val: 'spam',
43
+ neg: false,
44
+ cts: new Date().toISOString(),
45
+ },
46
+ {
47
+ src: 'did:example:labeler',
48
+ uri: 'at://did:example:blah/app.bsky.feed.post/1234abcfg',
49
+ val: 'spam',
50
+ neg: false,
51
+ cts: new Date().toISOString(),
52
+ },
53
+ {
54
+ src: 'did:example:labeler',
55
+ uri: 'at://did:example:blah/app.bsky.actor.profile/self',
56
+ val: 'spam',
57
+ neg: false,
58
+ cts: new Date().toISOString(),
59
+ },
60
+ {
61
+ src: 'did:example:labeler',
62
+ uri: 'did:example:thing',
63
+ val: 'spam',
64
+ neg: false,
65
+ cts: new Date().toISOString(),
66
+ },
67
+ ]
68
+
69
+ const modService = network.ozone.ctx.modService(network.ozone.ctx.db)
70
+ await modService.createLabels(labels)
71
+ })
72
+
73
+ afterAll(async () => {
74
+ await network.close()
75
+ })
76
+
77
+ it('returns all labels', async () => {
78
+ const res = await agent.api.com.atproto.label.queryLabels({
79
+ uriPatterns: ['*'],
80
+ })
81
+ expect(res.data.labels).toEqual(labels)
82
+ })
83
+
84
+ it('returns all labels even when an additional pattern is supplied', async () => {
85
+ const res = await agent.api.com.atproto.label.queryLabels({
86
+ uriPatterns: ['*', 'did:example:blah'],
87
+ })
88
+ expect(res.data.labels).toEqual(labels)
89
+ })
90
+
91
+ it('returns all labels that match an exact uri pattern', async () => {
92
+ const res = await agent.api.com.atproto.label.queryLabels({
93
+ uriPatterns: ['did:example:blah'],
94
+ })
95
+ expect(res.data.labels).toEqual(labels.slice(0, 2))
96
+ })
97
+
98
+ it('returns all labels that match one of multiple exact uris', async () => {
99
+ const res = await agent.api.com.atproto.label.queryLabels({
100
+ uriPatterns: [
101
+ 'at://did:example:blah/app.bsky.feed.post/1234abcfg',
102
+ 'at://did:example:blah/app.bsky.actor.profile/self',
103
+ ],
104
+ })
105
+ expect(res.data.labels).toEqual(labels.slice(3, 5))
106
+ })
107
+
108
+ it('returns all labels that match one of multiple uris, exact & glob', async () => {
109
+ const res = await agent.api.com.atproto.label.queryLabels({
110
+ uriPatterns: ['at://did:example:blah/app.bsky*', 'did:example:blah'],
111
+ })
112
+ expect(res.data.labels).toEqual(labels.slice(0, 5))
113
+ })
114
+
115
+ it('paginates', async () => {
116
+ const res1 = await agent.api.com.atproto.label.queryLabels({
117
+ uriPatterns: ['at://did:example:blah/app.bsky*', 'did:example:blah'],
118
+ limit: 3,
119
+ })
120
+ const res2 = await agent.api.com.atproto.label.queryLabels({
121
+ uriPatterns: ['at://did:example:blah/app.bsky*', 'did:example:blah'],
122
+ limit: 3,
123
+ cursor: res1.data.cursor,
124
+ })
125
+
126
+ expect([...res1.data.labels, ...res2.data.labels]).toEqual(
127
+ labels.slice(0, 5),
128
+ )
129
+ })
130
+
131
+ describe('subscribeLabels', () => {
132
+ it('streams all labels from initial cursor.', async () => {
133
+ const ac = new AbortController()
134
+ let doneTimer: NodeJS.Timeout
135
+ const resetDoneTimer = () => {
136
+ clearTimeout(doneTimer)
137
+ doneTimer = setTimeout(() => ac.abort(new DisconnectError()), 100)
138
+ }
139
+ const sub = new Subscription({
140
+ signal: ac.signal,
141
+ service: agent.service.origin.replace('http://', 'ws://'),
142
+ method: ids.ComAtprotoLabelSubscribeLabels,
143
+ getParams() {
144
+ return { cursor: 0 }
145
+ },
146
+ validate(obj) {
147
+ return lexicons.assertValidXrpcMessage<LabelMessage>(
148
+ ids.ComAtprotoLabelSubscribeLabels,
149
+ obj,
150
+ )
151
+ },
152
+ })
153
+ const streamedLabels: Label[] = []
154
+ for await (const message of sub) {
155
+ resetDoneTimer()
156
+ if (isLabels(message)) {
157
+ streamedLabels.push(...message.labels)
158
+ }
159
+ }
160
+ expect(streamedLabels).toEqual(labels)
161
+ })
162
+ })
163
+ })
@@ -117,7 +117,6 @@ describe('admin repo search view', () => {
117
117
  { headers },
118
118
  )
119
119
 
120
- expect(full.data.repos.length).toEqual(15)
121
120
  expect(results(paginatedAll)).toEqual(results([full.data]))
122
121
  })
123
122
  })