@atproto/ozone 0.2.4 → 0.2.6

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 (217) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +2 -0
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/api/label/queryLabels.d.ts.map +1 -1
  6. package/dist/api/label/queryLabels.js +13 -21
  7. package/dist/api/label/queryLabels.js.map +1 -1
  8. package/dist/api/report/queryActivities.d.ts +4 -0
  9. package/dist/api/report/queryActivities.d.ts.map +1 -0
  10. package/dist/api/report/queryActivities.js +36 -0
  11. package/dist/api/report/queryActivities.js.map +1 -0
  12. package/dist/assignment/index.d.ts.map +1 -1
  13. package/dist/assignment/index.js +9 -12
  14. package/dist/assignment/index.js.map +1 -1
  15. package/dist/background.d.ts +5 -3
  16. package/dist/background.d.ts.map +1 -1
  17. package/dist/background.js +13 -4
  18. package/dist/background.js.map +1 -1
  19. package/dist/context.js +1 -1
  20. package/dist/context.js.map +1 -1
  21. package/dist/daemon/context.js +1 -1
  22. package/dist/daemon/context.js.map +1 -1
  23. package/dist/daemon/event-pusher.d.ts +7 -1
  24. package/dist/daemon/event-pusher.d.ts.map +1 -1
  25. package/dist/daemon/verification-listener.d.ts +1 -1
  26. package/dist/daemon/verification-listener.d.ts.map +1 -1
  27. package/dist/daemon/verification-listener.js +10 -4
  28. package/dist/daemon/verification-listener.js.map +1 -1
  29. package/dist/db/index.d.ts +4 -5
  30. package/dist/db/index.d.ts.map +1 -1
  31. package/dist/db/index.js +2 -1
  32. package/dist/db/index.js.map +1 -1
  33. package/dist/db/migrations/20241220T144630860Z-stats-materialized-views.d.ts +1 -2
  34. package/dist/db/migrations/20241220T144630860Z-stats-materialized-views.d.ts.map +1 -1
  35. package/dist/db/migrations/20241220T144630860Z-stats-materialized-views.js.map +1 -1
  36. package/dist/db/migrations/20250718T150931000Z-update-appeal-reason-stats.d.ts +1 -2
  37. package/dist/db/migrations/20250718T150931000Z-update-appeal-reason-stats.d.ts.map +1 -1
  38. package/dist/db/migrations/20250718T150931000Z-update-appeal-reason-stats.js.map +1 -1
  39. package/dist/db/migrations/20260602T120000000Z-add-report-activity-created-index.d.ts +4 -0
  40. package/dist/db/migrations/20260602T120000000Z-add-report-activity-created-index.d.ts.map +1 -0
  41. package/dist/db/migrations/20260602T120000000Z-add-report-activity-created-index.js +15 -0
  42. package/dist/db/migrations/20260602T120000000Z-add-report-activity-created-index.js.map +1 -0
  43. package/dist/db/migrations/index.d.ts +1 -0
  44. package/dist/db/migrations/index.d.ts.map +1 -1
  45. package/dist/db/migrations/index.js +1 -0
  46. package/dist/db/migrations/index.js.map +1 -1
  47. package/dist/db/migrations/provider.d.ts +2 -1
  48. package/dist/db/migrations/provider.d.ts.map +1 -1
  49. package/dist/db/migrations/provider.js.map +1 -1
  50. package/dist/db/pagination.d.ts +4 -3
  51. package/dist/db/pagination.d.ts.map +1 -1
  52. package/dist/db/pagination.js +4 -4
  53. package/dist/db/pagination.js.map +1 -1
  54. package/dist/db/types.d.ts +1 -1
  55. package/dist/db/types.d.ts.map +1 -1
  56. package/dist/db/types.js.map +1 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +23 -13
  59. package/dist/index.js.map +1 -1
  60. package/dist/jetstream/service.d.ts +1 -1
  61. package/dist/jetstream/service.d.ts.map +1 -1
  62. package/dist/jetstream/service.js +3 -1
  63. package/dist/jetstream/service.js.map +1 -1
  64. package/dist/lexicon/index.d.ts +11 -0
  65. package/dist/lexicon/index.d.ts.map +1 -1
  66. package/dist/lexicon/index.js +18 -0
  67. package/dist/lexicon/index.js.map +1 -1
  68. package/dist/lexicon/lexicons.d.ts +338 -0
  69. package/dist/lexicon/lexicons.d.ts.map +1 -1
  70. package/dist/lexicon/lexicons.js +173 -0
  71. package/dist/lexicon/lexicons.js.map +1 -1
  72. package/dist/lexicon/types/app/bsky/notification/defs.d.ts +1 -0
  73. package/dist/lexicon/types/app/bsky/notification/defs.d.ts.map +1 -1
  74. package/dist/lexicon/types/app/bsky/notification/defs.js.map +1 -1
  75. package/dist/lexicon/types/chat/bsky/notification/defs.d.ts +19 -0
  76. package/dist/lexicon/types/chat/bsky/notification/defs.d.ts.map +1 -0
  77. package/dist/lexicon/types/chat/bsky/notification/defs.js +19 -0
  78. package/dist/lexicon/types/chat/bsky/notification/defs.js.map +1 -0
  79. package/dist/lexicon/types/chat/bsky/notification/getPreferences.d.ts +20 -0
  80. package/dist/lexicon/types/chat/bsky/notification/getPreferences.d.ts.map +1 -0
  81. package/dist/lexicon/types/chat/bsky/notification/getPreferences.js +5 -0
  82. package/dist/lexicon/types/chat/bsky/notification/getPreferences.js.map +1 -0
  83. package/dist/lexicon/types/chat/bsky/notification/putPreferences.d.ts +26 -0
  84. package/dist/lexicon/types/chat/bsky/notification/putPreferences.d.ts.map +1 -0
  85. package/dist/lexicon/types/chat/bsky/notification/putPreferences.js +5 -0
  86. package/dist/lexicon/types/chat/bsky/notification/putPreferences.js.map +1 -0
  87. package/dist/lexicon/types/tools/ozone/report/defs.d.ts +1 -0
  88. package/dist/lexicon/types/tools/ozone/report/defs.d.ts.map +1 -1
  89. package/dist/lexicon/types/tools/ozone/report/defs.js.map +1 -1
  90. package/dist/lexicon/types/tools/ozone/report/queryActivities.d.ts +32 -0
  91. package/dist/lexicon/types/tools/ozone/report/queryActivities.d.ts.map +1 -0
  92. package/dist/lexicon/types/tools/ozone/report/queryActivities.js +5 -0
  93. package/dist/lexicon/types/tools/ozone/report/queryActivities.js.map +1 -0
  94. package/dist/mod-service/index.d.ts.map +1 -1
  95. package/dist/mod-service/index.js +28 -52
  96. package/dist/mod-service/index.js.map +1 -1
  97. package/dist/mod-service/report.d.ts +1 -0
  98. package/dist/mod-service/report.d.ts.map +1 -1
  99. package/dist/mod-service/report.js +16 -0
  100. package/dist/mod-service/report.js.map +1 -1
  101. package/dist/mod-service/status.d.ts +23 -128
  102. package/dist/mod-service/status.d.ts.map +1 -1
  103. package/dist/mod-service/views.js +7 -11
  104. package/dist/mod-service/views.js.map +1 -1
  105. package/dist/queue/service.js +1 -3
  106. package/dist/queue/service.js.map +1 -1
  107. package/dist/report/activity.d.ts +31 -2
  108. package/dist/report/activity.d.ts.map +1 -1
  109. package/dist/report/activity.js +27 -1
  110. package/dist/report/activity.js.map +1 -1
  111. package/dist/report/stats.d.ts.map +1 -1
  112. package/dist/report/stats.js.map +1 -1
  113. package/dist/scheduled-action/service.d.ts.map +1 -1
  114. package/dist/scheduled-action/service.js +16 -20
  115. package/dist/scheduled-action/service.js.map +1 -1
  116. package/dist/set/service.d.ts +10 -1
  117. package/dist/set/service.d.ts.map +1 -1
  118. package/dist/set/service.js +5 -2
  119. package/dist/set/service.js.map +1 -1
  120. package/dist/team/index.d.ts.map +1 -1
  121. package/dist/team/index.js +5 -4
  122. package/dist/team/index.js.map +1 -1
  123. package/dist/verification/issuer.d.ts +13 -3
  124. package/dist/verification/issuer.d.ts.map +1 -1
  125. package/dist/verification/service.d.ts +13 -1
  126. package/dist/verification/service.d.ts.map +1 -1
  127. package/dist/verification/service.js +1 -1
  128. package/dist/verification/service.js.map +1 -1
  129. package/package.json +12 -11
  130. package/src/api/index.ts +2 -0
  131. package/src/api/label/queryLabels.ts +11 -14
  132. package/src/api/report/queryActivities.ts +64 -0
  133. package/src/assignment/index.ts +15 -18
  134. package/src/background.ts +19 -4
  135. package/src/context.ts +1 -1
  136. package/src/daemon/context.ts +1 -1
  137. package/src/daemon/verification-listener.ts +9 -4
  138. package/src/db/index.ts +1 -1
  139. package/src/db/migrations/20241220T144630860Z-stats-materialized-views.ts +1 -2
  140. package/src/db/migrations/20250718T150931000Z-update-appeal-reason-stats.ts +1 -2
  141. package/src/db/migrations/20260602T120000000Z-add-report-activity-created-index.ts +17 -0
  142. package/src/db/migrations/index.ts +1 -0
  143. package/src/db/migrations/provider.ts +2 -1
  144. package/src/db/pagination.ts +18 -18
  145. package/src/db/types.ts +3 -1
  146. package/src/index.ts +25 -15
  147. package/src/jetstream/service.ts +3 -1
  148. package/src/mod-service/index.ts +78 -71
  149. package/src/mod-service/report.ts +24 -3
  150. package/src/mod-service/views.ts +16 -16
  151. package/src/queue/service.ts +5 -5
  152. package/src/report/activity.ts +47 -0
  153. package/src/report/stats.ts +5 -3
  154. package/src/scheduled-action/service.ts +22 -20
  155. package/src/set/service.ts +17 -14
  156. package/src/team/index.ts +6 -5
  157. package/src/verification/service.ts +2 -2
  158. package/tests/3p-labeler.test.ts +2 -2
  159. package/tests/_util.ts +8 -25
  160. package/tests/account-strikes.test.ts +1 -1
  161. package/tests/ack-all-subjects-of-account.test.ts +1 -1
  162. package/tests/age-assurance.test.ts +1 -1
  163. package/tests/blob-divert.test.ts +1 -1
  164. package/tests/communication-templates.test.ts +1 -1
  165. package/tests/content-tagger.test.ts +1 -1
  166. package/tests/db.test.ts +1 -1
  167. package/tests/expiring-label.test.ts +1 -1
  168. package/tests/expiring-tags.test.ts +1 -1
  169. package/tests/get-account-timeline.test.ts +1 -1
  170. package/tests/get-config.test.ts +1 -1
  171. package/tests/get-lists.test.ts +2 -1
  172. package/tests/get-profiles.test.ts +1 -1
  173. package/tests/get-record.test.ts +1 -1
  174. package/tests/get-records.test.ts +1 -1
  175. package/tests/get-repo.test.ts +1 -1
  176. package/tests/get-report.test.ts +1 -1
  177. package/tests/get-reporter-stats.test.ts +1 -1
  178. package/tests/get-repos.test.ts +1 -1
  179. package/tests/get-starter-pack.test.ts +1 -1
  180. package/tests/get-subjects.test.ts +1 -1
  181. package/tests/mod-tool.test.ts +1 -1
  182. package/tests/moderation-appeals.test.ts +1 -1
  183. package/tests/moderation-events.test.ts +1 -1
  184. package/tests/moderation-status-tags.test.ts +1 -1
  185. package/tests/moderation-statuses.test.ts +1 -1
  186. package/tests/moderation.test.ts +1 -1
  187. package/tests/protected-tags.test.ts +1 -1
  188. package/tests/query-labels.test.ts +1 -1
  189. package/tests/query-reports.test.ts +1 -1
  190. package/tests/queue-assignment.test.ts +1 -1
  191. package/tests/queue-router.test.ts +1 -1
  192. package/tests/queues.test.ts +1 -1
  193. package/tests/record-and-account-events.test.ts +1 -1
  194. package/tests/repo-search.test.ts +2 -2
  195. package/tests/report-action.test.ts +1 -1
  196. package/tests/report-activity.test.ts +145 -1
  197. package/tests/report-assignment.test.ts +1 -1
  198. package/tests/report-muting.test.ts +1 -1
  199. package/tests/report-reason.test.ts +1 -1
  200. package/tests/report-reassign-queue.test.ts +1 -1
  201. package/tests/report-routing.test.ts +1 -1
  202. package/tests/report-stats.test.ts +1 -1
  203. package/tests/revoke-account-credentials.test.ts +1 -1
  204. package/tests/safelink.test.ts +1 -1
  205. package/tests/scheduled-action-processor.test.ts +1 -1
  206. package/tests/scheduled-action.test.ts +1 -1
  207. package/tests/sequencer.test.ts +1 -1
  208. package/tests/server.test.ts +9 -12
  209. package/tests/sets.test.ts +1 -1
  210. package/tests/settings.test.ts +1 -1
  211. package/tests/strike-expiry-processor.test.ts +1 -1
  212. package/tests/subject-priority-score.test.ts +1 -1
  213. package/tests/takedown.test.ts +1 -1
  214. package/tests/team.test.ts +1 -1
  215. package/tests/verification-listener.test.ts +40 -13
  216. package/tests/verification.test.ts +1 -1
  217. package/tsconfig.build.tsbuildinfo +1 -1
@@ -28,10 +28,10 @@ describe('admin repo search view', () => {
28
28
  ids.ToolsOzoneModerationSearchRepos,
29
29
  )
30
30
  await network.processAll()
31
- }, 20_000) // @NOTE seeding can take a while
31
+ }, 40_000) // @NOTE seeding can take a while
32
32
 
33
33
  afterAll(async () => {
34
- await network.close()
34
+ await network?.close()
35
35
  })
36
36
 
37
37
  beforeAll(async () => {
@@ -29,7 +29,7 @@ describe('report-action', () => {
29
29
  })
30
30
 
31
31
  afterAll(async () => {
32
- await network.close()
32
+ await network?.close()
33
33
  })
34
34
 
35
35
  describe('emitEvent with reportAction', () => {
@@ -75,7 +75,7 @@ describe('report-activity', () => {
75
75
  })
76
76
 
77
77
  afterAll(async () => {
78
- await network.close()
78
+ await network?.close()
79
79
  })
80
80
 
81
81
  describe('createActivity — noteActivity', () => {
@@ -564,4 +564,148 @@ describe('report-activity', () => {
564
564
  expect(data.activities.some((a) => a.reportId === reportB.id)).toBe(false)
565
565
  })
566
566
  })
567
+
568
+ describe('queryActivities', () => {
569
+ const queryActivities = async (
570
+ params: {
571
+ activityTypes?: string[]
572
+ createdAfter?: string
573
+ createdBefore?: string
574
+ sortDirection?: 'asc' | 'desc'
575
+ limit?: number
576
+ cursor?: string
577
+ },
578
+ role: 'admin' | 'triage' = 'admin',
579
+ ) => {
580
+ return agent.tools.ozone.report.queryActivities(params, {
581
+ headers: await network.ozone.modHeaders(
582
+ ids.ToolsOzoneReportQueryActivities,
583
+ role,
584
+ ),
585
+ })
586
+ }
587
+
588
+ it('hydrates the report on each activity', async () => {
589
+ const report = await createReport(sc.dids.alice)
590
+ await createActivity({
591
+ reportId: report.id,
592
+ activity: { $type: `${DEFS}#closeActivity` },
593
+ })
594
+
595
+ const { data } = await queryActivities({
596
+ activityTypes: ['closeActivity'],
597
+ sortDirection: 'desc',
598
+ limit: 100,
599
+ })
600
+ const hit = data.activities.find((a) => a.reportId === report.id)
601
+ expect(hit).toBeDefined()
602
+ expect(hit!.report).toBeDefined()
603
+ expect(hit!.report!.id).toBe(report.id)
604
+ expect(hit!.report!.subject).toBeDefined()
605
+ })
606
+
607
+ it('filters by activity types across reports', async () => {
608
+ const reportA = await createReport(sc.dids.alice)
609
+ const reportB = await createReport(sc.dids.bob)
610
+ await createActivity({
611
+ reportId: reportA.id,
612
+ activity: { $type: `${DEFS}#noteActivity` },
613
+ internalNote: 'note',
614
+ })
615
+ await createActivity({
616
+ reportId: reportA.id,
617
+ activity: { $type: `${DEFS}#closeActivity` },
618
+ })
619
+ await createActivity({
620
+ reportId: reportB.id,
621
+ activity: { $type: `${DEFS}#escalationActivity` },
622
+ })
623
+
624
+ const { data } = await queryActivities({
625
+ activityTypes: ['closeActivity', 'escalationActivity'],
626
+ sortDirection: 'asc',
627
+ })
628
+
629
+ const types = new Set(data.activities.map((a) => a.activity.$type))
630
+ expect(types.has(`${DEFS}#noteActivity`)).toBe(false)
631
+ expect(types.has(`${DEFS}#closeActivity`)).toBe(true)
632
+ expect(types.has(`${DEFS}#escalationActivity`)).toBe(true)
633
+ const reportIds = new Set(data.activities.map((a) => a.reportId))
634
+ expect(reportIds.has(reportA.id)).toBe(true)
635
+ expect(reportIds.has(reportB.id)).toBe(true)
636
+ })
637
+
638
+ it('paginates ascending across multiple reports with stable cursor', async () => {
639
+ const reportA = await createReport(sc.dids.alice)
640
+ const reportB = await createReport(sc.dids.bob)
641
+ const created: number[] = []
642
+ for (let i = 0; i < 3; i++) {
643
+ const r = await createActivity({
644
+ reportId: i % 2 === 0 ? reportA.id : reportB.id,
645
+ activity: { $type: `${DEFS}#noteActivity` },
646
+ internalNote: `n-${i}`,
647
+ })
648
+ created.push(r.data.activity.id)
649
+ }
650
+ const minId = Math.min(...created)
651
+
652
+ const seen: number[] = []
653
+ let cursor: string | undefined
654
+ let pages = 0
655
+ do {
656
+ const { data } = await queryActivities({
657
+ sortDirection: 'asc',
658
+ limit: 2,
659
+ cursor,
660
+ })
661
+ for (const a of data.activities) {
662
+ if (a.id >= minId) seen.push(a.id)
663
+ }
664
+ cursor = data.cursor
665
+ pages++
666
+ if (pages > 50) break // safety net for runaway loops
667
+ } while (cursor)
668
+
669
+ // The created activities should appear in ascending ID order, no dupes.
670
+ const sortedCreated = [...created].sort((a, b) => a - b)
671
+ const seenForCreated = seen.filter((id) => created.includes(id))
672
+ expect(seenForCreated).toEqual(sortedCreated)
673
+ })
674
+
675
+ it('respects createdBefore and createdAfter bounds', async () => {
676
+ const report = await createReport(sc.dids.alice)
677
+ const before = await createActivity({
678
+ reportId: report.id,
679
+ activity: { $type: `${DEFS}#noteActivity` },
680
+ internalNote: 'before',
681
+ })
682
+ // Small wait so the next activity's createdAt is strictly later.
683
+ await new Promise((r) => setTimeout(r, 50))
684
+ const cutoff = new Date().toISOString()
685
+ await new Promise((r) => setTimeout(r, 50))
686
+ const after = await createActivity({
687
+ reportId: report.id,
688
+ activity: { $type: `${DEFS}#noteActivity` },
689
+ internalNote: 'after',
690
+ })
691
+
692
+ const beforeRes = await queryActivities({
693
+ createdBefore: cutoff,
694
+ sortDirection: 'asc',
695
+ limit: 100,
696
+ })
697
+ const beforeIds = new Set(beforeRes.data.activities.map((a) => a.id))
698
+ expect(beforeIds.has(before.data.activity.id)).toBe(true)
699
+ expect(beforeIds.has(after.data.activity.id)).toBe(false)
700
+
701
+ const afterRes = await queryActivities({
702
+ createdAfter: cutoff,
703
+ sortDirection: 'asc',
704
+ limit: 100,
705
+ })
706
+ const afterIds = new Set(afterRes.data.activities.map((a) => a.id))
707
+ expect(afterIds.has(before.data.activity.id)).toBe(false)
708
+ expect(afterIds.has(after.data.activity.id)).toBe(true)
709
+ })
710
+ })
567
711
  })
@@ -132,7 +132,7 @@ describe('report-assignment', () => {
132
132
  })
133
133
 
134
134
  afterAll(async () => {
135
- await network.close()
135
+ await network?.close()
136
136
  })
137
137
 
138
138
  it('can get assignment history', async () => {
@@ -29,7 +29,7 @@ describe('report-muting', () => {
29
29
  })
30
30
 
31
31
  afterAll(async () => {
32
- await network.close()
32
+ await network?.close()
33
33
  })
34
34
 
35
35
  const assertSubjectStatus = async (
@@ -45,7 +45,7 @@ describe('report reason', () => {
45
45
  })
46
46
 
47
47
  afterAll(async () => {
48
- await network.close()
48
+ await network?.close()
49
49
  })
50
50
 
51
51
  describe('createReport', () => {
@@ -143,7 +143,7 @@ describe('report-reassign-queue', () => {
143
143
  })
144
144
 
145
145
  afterAll(async () => {
146
- await network.close()
146
+ await network?.close()
147
147
  })
148
148
 
149
149
  describe('happy path: assigning to a real queue', () => {
@@ -122,7 +122,7 @@ describe('queue-router', () => {
122
122
  })
123
123
 
124
124
  afterAll(async () => {
125
- await network.close()
125
+ await network?.close()
126
126
  })
127
127
 
128
128
  it('routes unassigned AND unmatched reports to a newly created queue', async () => {
@@ -119,7 +119,7 @@ describe('report-stats', () => {
119
119
  })
120
120
 
121
121
  afterAll(async () => {
122
- await network.close()
122
+ await network?.close()
123
123
  })
124
124
 
125
125
  describe('aggregate', () => {
@@ -21,7 +21,7 @@ describe('revoke account credentials event', () => {
21
21
  })
22
22
 
23
23
  afterAll(async () => {
24
- await network.close()
24
+ await network?.close()
25
25
  })
26
26
 
27
27
  it('fails on non account subjects and for non admins', async () => {
@@ -29,7 +29,7 @@ describe('safelink management', () => {
29
29
  })
30
30
 
31
31
  afterAll(async () => {
32
- await network.close()
32
+ await network?.close()
33
33
  })
34
34
 
35
35
  describe('addRule', () => {
@@ -73,7 +73,7 @@ describe('scheduled action processor', () => {
73
73
  })
74
74
 
75
75
  afterAll(async () => {
76
- await network.close()
76
+ await network?.close()
77
77
  })
78
78
 
79
79
  describe('findAndExecuteScheduledActions', () => {
@@ -57,7 +57,7 @@ describe('scheduled action management', () => {
57
57
  })
58
58
 
59
59
  afterAll(async () => {
60
- await network.close()
60
+ await network?.close()
61
61
  })
62
62
 
63
63
  describe('scheduleAction', () => {
@@ -21,7 +21,7 @@ describe('sequencer', () => {
21
21
  })
22
22
 
23
23
  afterAll(async () => {
24
- await network.close()
24
+ await network?.close()
25
25
  })
26
26
 
27
27
  const loadFromDb = (lastSeen: number) => {
@@ -15,7 +15,7 @@ describe('server', () => {
15
15
  })
16
16
 
17
17
  afterAll(async () => {
18
- await network.close()
18
+ await network?.close()
19
19
  })
20
20
 
21
21
  it('preserves 404s.', async () => {
@@ -30,17 +30,14 @@ describe('server', () => {
30
30
  })
31
31
  .use(errorHandler)
32
32
 
33
- const { origin, stop } = await startServer(app)
34
- try {
35
- const response = await fetch(new URL(`/oops`, origin))
36
- expect(response.status).toEqual(500)
37
- await expect(response.json()).resolves.toEqual({
38
- error: 'InternalServerError',
39
- message: 'Internal Server Error',
40
- })
41
- } finally {
42
- await stop()
43
- }
33
+ await using server = await startServer(app)
34
+
35
+ const response = await fetch(`http://localhost:${server.port}/oops`)
36
+ expect(response.status).toEqual(500)
37
+ await expect(response.json()).resolves.toEqual({
38
+ error: 'InternalServerError',
39
+ message: 'Internal Server Error',
40
+ })
44
41
  })
45
42
 
46
43
  it('healthcheck succeeds when database is available.', async () => {
@@ -97,7 +97,7 @@ describe('ozone-sets', () => {
97
97
  })
98
98
 
99
99
  afterAll(async () => {
100
- await network.close()
100
+ await network?.close()
101
101
  })
102
102
 
103
103
  describe('querySets', () => {
@@ -68,7 +68,7 @@ describe('ozone-settings', () => {
68
68
  })
69
69
 
70
70
  afterAll(async () => {
71
- await network.close()
71
+ await network?.close()
72
72
  })
73
73
 
74
74
  describe('upsertOption', () => {
@@ -68,7 +68,7 @@ describe('strike expiry processor', () => {
68
68
  })
69
69
 
70
70
  afterAll(async () => {
71
- await network.close()
71
+ await network?.close()
72
72
  })
73
73
 
74
74
  it('processes expired strikes and updates active strike count', async () => {
@@ -49,7 +49,7 @@ describe('moderation', () => {
49
49
  })
50
50
 
51
51
  afterAll(async () => {
52
- await network.close()
52
+ await network?.close()
53
53
  })
54
54
 
55
55
  it('allows setting a priority score.', async () => {
@@ -37,7 +37,7 @@ describe('moderation', () => {
37
37
  })
38
38
 
39
39
  afterAll(async () => {
40
- await network.close()
40
+ await network?.close()
41
41
  })
42
42
 
43
43
  it('allows specifying policy for takedown actions.', async () => {
@@ -36,7 +36,7 @@ describe('team management', () => {
36
36
  })
37
37
 
38
38
  afterAll(async () => {
39
- await network.close()
39
+ await network?.close()
40
40
  })
41
41
 
42
42
  describe('listMembers', () => {
@@ -1,4 +1,9 @@
1
- import { Sender, WebSocketServer } from 'ws'
1
+ import { once } from 'node:events'
2
+ import { createServer } from 'node:http'
3
+ import { AddressInfo } from 'node:net'
4
+ // eslint-disable-next-line import/default
5
+ import httpTerminator from 'http-terminator'
6
+ import { WebSocket, WebSocketServer } from 'ws'
2
7
  import { AppBskyGraphVerification, AtpAgent } from '@atproto/api'
3
8
  import { SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env'
4
9
  import { forSnapshot } from './_util.js'
@@ -8,16 +13,38 @@ describe('verification-listener', () => {
8
13
  let sc: SeedClient
9
14
  let adminAgent: AtpAgent
10
15
  let jetstream: WebSocketServer
11
- let relay: Sender
16
+ let relay: WebSocket
17
+ let terminator: httpTerminator.HttpTerminator
12
18
 
13
19
  beforeAll(async () => {
14
- const jetstreamPort = 2511
15
- jetstream = new WebSocketServer({
16
- port: jetstreamPort,
17
- })
18
- jetstream.on('connection', (ws) => {
19
- relay = ws
20
+ const server = createServer()
21
+ terminator = httpTerminator.createHttpTerminator({ server })
22
+
23
+ await once(server.listen(0), 'listening')
24
+ const jetstreamPort = (server.address() as AddressInfo).port
25
+
26
+ jetstream = new WebSocketServer({ server })
27
+ const relayPromise = new Promise<WebSocket>((resolve, reject) => {
28
+ const cleanup = () => {
29
+ jetstream.off('connection', onConnection)
30
+ jetstream.off('error', onError)
31
+ }
32
+
33
+ const onConnection = (ws: WebSocket) => {
34
+ cleanup()
35
+ resolve(ws)
36
+ }
37
+
38
+ const onError = (err: Error) => {
39
+ cleanup()
40
+ reject(err)
41
+ }
42
+
43
+ jetstream.on('connection', onConnection)
44
+ jetstream.on('error', onError)
20
45
  })
46
+ relayPromise.catch(() => {})
47
+
21
48
  network = await TestNetwork.create({
22
49
  dbPostgresSchema: 'ozone_verification_listener_test',
23
50
  ozone: {
@@ -37,13 +64,13 @@ describe('verification-listener', () => {
37
64
  })
38
65
  await network.ozone.addAdminDid(sc.dids.alice)
39
66
 
40
- await network.processAll()
67
+ relay = await relayPromise
41
68
  })
42
69
 
43
- afterAll(async () => {
44
- await jetstream.close()
45
- await network.close()
46
- })
70
+ beforeEach(async () => network.processAll())
71
+ afterAll(async () => network?.close())
72
+ afterAll(async () => relay.close())
73
+ afterAll(async () => terminator?.terminate())
47
74
 
48
75
  it('indexes new and revoked verifications', async () => {
49
76
  const { verificationListener } = network.ozone.daemon.ctx
@@ -51,7 +51,7 @@ describe('verification', () => {
51
51
  })
52
52
 
53
53
  afterAll(async () => {
54
- await network.close()
54
+ await network?.close()
55
55
  })
56
56
 
57
57
  describe('list', () => {