@atproto/ozone 0.2.8 → 0.2.11
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.
- package/CHANGELOG.md +33 -0
- package/package.json +25 -21
- package/bin/migration-create.ts +0 -38
- package/jest.config.cjs +0 -22
- package/src/api/chat/getActorMetadata.ts +0 -23
- package/src/api/chat/getConvo.ts +0 -23
- package/src/api/chat/getConvoMembers.ts +0 -23
- package/src/api/chat/getConvos.ts +0 -23
- package/src/api/chat/getMessageContext.ts +0 -42
- package/src/api/chat/index.ts +0 -16
- package/src/api/communication/createTemplate.ts +0 -51
- package/src/api/communication/deleteTemplate.ts +0 -23
- package/src/api/communication/listTemplates.ts +0 -31
- package/src/api/communication/updateTemplate.ts +0 -51
- package/src/api/health.ts +0 -27
- package/src/api/index.ts +0 -146
- package/src/api/label/fetchLabels.ts +0 -32
- package/src/api/label/queryLabels.ts +0 -57
- package/src/api/label/subscribeLabels.ts +0 -25
- package/src/api/moderation/cancelScheduledActions.ts +0 -72
- package/src/api/moderation/emitEvent.ts +0 -475
- package/src/api/moderation/getAccountTimeline.ts +0 -160
- package/src/api/moderation/getEvent.ts +0 -19
- package/src/api/moderation/getRecord.ts +0 -40
- package/src/api/moderation/getRecords.ts +0 -50
- package/src/api/moderation/getRepo.ts +0 -34
- package/src/api/moderation/getReporterStats.ts +0 -18
- package/src/api/moderation/getRepos.ts +0 -41
- package/src/api/moderation/getSubjects.ts +0 -101
- package/src/api/moderation/listScheduledActions.ts +0 -45
- package/src/api/moderation/queryEvents.ts +0 -72
- package/src/api/moderation/queryStatuses.ts +0 -23
- package/src/api/moderation/scheduleAction.ts +0 -129
- package/src/api/moderation/searchRepos.ts +0 -46
- package/src/api/moderation/util.ts +0 -96
- package/src/api/proxied.ts +0 -327
- package/src/api/queue/assignModerator.ts +0 -31
- package/src/api/queue/createQueue.ts +0 -62
- package/src/api/queue/deleteQueue.ts +0 -56
- package/src/api/queue/getAssignments.ts +0 -19
- package/src/api/queue/listQueues.ts +0 -39
- package/src/api/queue/routeReports.ts +0 -44
- package/src/api/queue/unassignModerator.ts +0 -26
- package/src/api/queue/updateQueue.ts +0 -54
- package/src/api/report/assignModerator.ts +0 -36
- package/src/api/report/createActivity.ts +0 -57
- package/src/api/report/createReport.ts +0 -93
- package/src/api/report/getAssignments.ts +0 -20
- package/src/api/report/getHistoricalStats.ts +0 -41
- package/src/api/report/getLatestReport.ts +0 -44
- package/src/api/report/getLiveStats.ts +0 -26
- package/src/api/report/getReport.ts +0 -55
- package/src/api/report/listActivities.ts +0 -37
- package/src/api/report/queryActivities.ts +0 -64
- package/src/api/report/queryReports.ts +0 -44
- package/src/api/report/reassignQueue.ts +0 -68
- package/src/api/report/refreshStats.ts +0 -27
- package/src/api/report/unassignModerator.ts +0 -21
- package/src/api/safelink/addRule.ts +0 -48
- package/src/api/safelink/queryEvents.ts +0 -32
- package/src/api/safelink/queryRules.ts +0 -58
- package/src/api/safelink/removeRule.ts +0 -42
- package/src/api/safelink/updateRule.ts +0 -48
- package/src/api/server/getConfig.ts +0 -35
- package/src/api/set/addValues.ts +0 -28
- package/src/api/set/deleteSet.ts +0 -34
- package/src/api/set/deleteValues.ts +0 -31
- package/src/api/set/getValues.ts +0 -42
- package/src/api/set/querySets.ts +0 -36
- package/src/api/set/upsertSet.ts +0 -38
- package/src/api/setting/listOptions.ts +0 -44
- package/src/api/setting/removeOptions.ts +0 -64
- package/src/api/setting/upsertOption.ts +0 -156
- package/src/api/team/addMember.ts +0 -51
- package/src/api/team/deleteMember.ts +0 -29
- package/src/api/team/listMembers.ts +0 -20
- package/src/api/team/updateMember.ts +0 -47
- package/src/api/util.ts +0 -265
- package/src/api/verification/grantVerifications.ts +0 -90
- package/src/api/verification/listVerifications.ts +0 -44
- package/src/api/verification/revokeVerifications.ts +0 -43
- package/src/api/well-known.ts +0 -46
- package/src/assignment/index.ts +0 -728
- package/src/auth-verifier.ts +0 -227
- package/src/background.ts +0 -183
- package/src/communication-service/template.ts +0 -110
- package/src/communication-service/util.ts +0 -8
- package/src/config/config.ts +0 -211
- package/src/config/env.ts +0 -95
- package/src/config/index.ts +0 -3
- package/src/config/secrets.ts +0 -17
- package/src/context.ts +0 -399
- package/src/daemon/blob-diverter.ts +0 -186
- package/src/daemon/context.ts +0 -247
- package/src/daemon/event-pusher.ts +0 -363
- package/src/daemon/event-reverser.ts +0 -128
- package/src/daemon/index.ts +0 -33
- package/src/daemon/job-cursor.ts +0 -33
- package/src/daemon/materialized-view-refresher.ts +0 -33
- package/src/daemon/queue-router.ts +0 -101
- package/src/daemon/scheduled-action-processor.ts +0 -304
- package/src/daemon/stats-computer.ts +0 -101
- package/src/daemon/strike-expiry-processor.ts +0 -95
- package/src/daemon/team-profile-synchronizer.ts +0 -15
- package/src/daemon/verification-listener.ts +0 -169
- package/src/db/index.ts +0 -203
- package/src/db/migrations/20231219T205730722Z-init.ts +0 -170
- package/src/db/migrations/20240116T085607200Z-communication-template.ts +0 -23
- package/src/db/migrations/20240201T051104136Z-mod-event-blobs.ts +0 -15
- package/src/db/migrations/20240208T213404429Z-add-tags-column-to-moderation-subject.ts +0 -31
- package/src/db/migrations/20240228T003647759Z-add-label-sigs.ts +0 -25
- package/src/db/migrations/20240408T192432676Z-mute-reporting.ts +0 -15
- package/src/db/migrations/20240506T225055595Z-message-subject.ts +0 -21
- package/src/db/migrations/20240521T211332580Z-member.ts +0 -17
- package/src/db/migrations/20240814T003647759Z-event-created-at-index.ts +0 -13
- package/src/db/migrations/20240903T205730722Z-add-template-lang.ts +0 -12
- package/src/db/migrations/20240904T205730722Z-add-subject-did-index.ts +0 -13
- package/src/db/migrations/20241001T205730722Z-subject-status-review-state-index.ts +0 -15
- package/src/db/migrations/20241008T205730722Z-sets.ts +0 -53
- package/src/db/migrations/20241018T205730722Z-setting.ts +0 -27
- package/src/db/migrations/20241026T205730722Z-add-hosting-status-to-subject-status.ts +0 -57
- package/src/db/migrations/20241220T144630860Z-stats-materialized-views.ts +0 -215
- package/src/db/migrations/20250204T003647759Z-add-subject-priority-score.ts +0 -22
- package/src/db/migrations/20250211T003647759Z-add-reporter-stats-index.ts +0 -38
- package/src/db/migrations/20250211T132135150Z-moderation-event-message-partial-idx.ts +0 -26
- package/src/db/migrations/20250221T132135150Z-member-details.ts +0 -14
- package/src/db/migrations/20250404T201720309Z-subject-status-sort-idxs.ts +0 -18
- package/src/db/migrations/20250415T201720309Z-verification.ts +0 -34
- package/src/db/migrations/20250417T201720309Z-firehose-cursor.ts +0 -16
- package/src/db/migrations/20250609T110704000Z-safelink.ts +0 -53
- package/src/db/migrations/20250618T180246000Z-add-mod-tool-to-moderation-event.ts +0 -18
- package/src/db/migrations/20250701T000000000Z-add-age-assurance-state.ts +0 -25
- package/src/db/migrations/20250715T000000000Z-add-mod-event-external-id.ts +0 -15
- package/src/db/migrations/20250718T150931000Z-update-appeal-reason-stats.ts +0 -310
- package/src/db/migrations/20250813T000000000Z-mod-tool-batch-id-index.ts +0 -14
- package/src/db/migrations/20250923T000000000Z-scheduled-actions.ts +0 -56
- package/src/db/migrations/20251008T120000000Z-add-strike-system.ts +0 -87
- package/src/db/migrations/20260210T154806448Z-mod-event-created-by-indexes.ts +0 -22
- package/src/db/migrations/20260219T164523000Z-create-report-table.ts +0 -155
- package/src/db/migrations/20260219T165302248Z-moderator-assignment.ts +0 -42
- package/src/db/migrations/20260225T000000000Z-add-report-queue-table.ts +0 -41
- package/src/db/migrations/20260313T000000000Z-add-report-activity-table.ts +0 -48
- package/src/db/migrations/20260318T152058935Z-add-report-stat.ts +0 -35
- package/src/db/migrations/20260428T000000000Z-add-expiring-tag-table.ts +0 -32
- package/src/db/migrations/20260513T202941104Z-add-subject-convo-id.ts +0 -114
- package/src/db/migrations/20260602T120000000Z-add-report-activity-created-index.ts +0 -17
- package/src/db/migrations/index.ts +0 -44
- package/src/db/migrations/provider.ts +0 -26
- package/src/db/pagination.ts +0 -335
- package/src/db/schema/account_events_stats.ts +0 -16
- package/src/db/schema/account_record_events_stats.ts +0 -15
- package/src/db/schema/account_record_status_stats.ts +0 -15
- package/src/db/schema/account_strike.ts +0 -13
- package/src/db/schema/blob_push_event.ts +0 -21
- package/src/db/schema/communication_template.ts +0 -19
- package/src/db/schema/expiring_tag.ts +0 -18
- package/src/db/schema/firehose_cursor.ts +0 -13
- package/src/db/schema/index.ts +0 -60
- package/src/db/schema/job_cursor.ts +0 -13
- package/src/db/schema/label.ts +0 -22
- package/src/db/schema/member.ts +0 -22
- package/src/db/schema/moderation_event.ts +0 -61
- package/src/db/schema/moderation_subject_status.ts +0 -52
- package/src/db/schema/moderator_assignment.ts +0 -16
- package/src/db/schema/ozone_set.ts +0 -24
- package/src/db/schema/record_events_stats.ts +0 -15
- package/src/db/schema/record_push_event.ts +0 -21
- package/src/db/schema/repo_push_event.ts +0 -19
- package/src/db/schema/report.ts +0 -28
- package/src/db/schema/report_activity.ts +0 -22
- package/src/db/schema/report_queue.ts +0 -21
- package/src/db/schema/report_stat.ts +0 -27
- package/src/db/schema/safelink.ts +0 -39
- package/src/db/schema/scheduled-action.ts +0 -25
- package/src/db/schema/setting.ts +0 -24
- package/src/db/schema/signing_key.ts +0 -10
- package/src/db/schema/verification.ts +0 -21
- package/src/db/types.ts +0 -24
- package/src/error.ts +0 -12
- package/src/image-invalidator.ts +0 -7
- package/src/index.ts +0 -154
- package/src/jetstream/service.ts +0 -107
- package/src/logger.ts +0 -29
- package/src/mod-service/expiring-tags.ts +0 -104
- package/src/mod-service/index.ts +0 -1842
- package/src/mod-service/profile.ts +0 -139
- package/src/mod-service/report.ts +0 -429
- package/src/mod-service/status.ts +0 -549
- package/src/mod-service/strike.ts +0 -96
- package/src/mod-service/subject.ts +0 -311
- package/src/mod-service/types.ts +0 -96
- package/src/mod-service/util.ts +0 -99
- package/src/mod-service/views.ts +0 -912
- package/src/queue/service.ts +0 -603
- package/src/report/activity.ts +0 -281
- package/src/report/handle-report-update.ts +0 -209
- package/src/report/reassign.ts +0 -109
- package/src/report/stats.ts +0 -852
- package/src/report/views.ts +0 -239
- package/src/safelink/service.ts +0 -304
- package/src/scheduled-action/service.ts +0 -281
- package/src/scheduled-action/types.ts +0 -17
- package/src/sequencer/index.ts +0 -2
- package/src/sequencer/outbox.ts +0 -123
- package/src/sequencer/sequencer.ts +0 -147
- package/src/set/service.ts +0 -230
- package/src/setting/constants.ts +0 -3
- package/src/setting/service.ts +0 -148
- package/src/setting/types.ts +0 -3
- package/src/setting/validators.ts +0 -333
- package/src/tag-service/content-tagger.ts +0 -30
- package/src/tag-service/embed-tagger.ts +0 -70
- package/src/tag-service/index.ts +0 -70
- package/src/tag-service/language-data.ts +0 -561
- package/src/tag-service/language-tagger.ts +0 -101
- package/src/tag-service/util.ts +0 -13
- package/src/team/index.ts +0 -296
- package/src/util.ts +0 -230
- package/src/verification/issuer.ts +0 -146
- package/src/verification/service.ts +0 -208
- package/src/verification/util.ts +0 -53
- package/test.env +0 -2
- package/tests/3p-labeler.test.ts +0 -288
- package/tests/__snapshots__/account-strikes.test.ts.snap +0 -159
- package/tests/__snapshots__/age-assurance.test.ts.snap +0 -66
- package/tests/__snapshots__/blob-divert.test.ts.snap +0 -219
- package/tests/__snapshots__/get-account-timeline.test.ts.snap +0 -36
- package/tests/__snapshots__/get-record.test.ts.snap +0 -271
- package/tests/__snapshots__/get-records.test.ts.snap +0 -175
- package/tests/__snapshots__/get-repo.test.ts.snap +0 -91
- package/tests/__snapshots__/get-repos.test.ts.snap +0 -127
- package/tests/__snapshots__/get-starter-pack.test.ts.snap +0 -535
- package/tests/__snapshots__/get-subjects.test.ts.snap +0 -529
- package/tests/__snapshots__/moderation-events.test.ts.snap +0 -347
- package/tests/__snapshots__/moderation-statuses.test.ts.snap +0 -276
- package/tests/__snapshots__/moderation.test.ts.snap +0 -85
- package/tests/__snapshots__/report-reason.test.ts.snap +0 -14
- package/tests/__snapshots__/safelink.test.ts.snap +0 -179
- package/tests/__snapshots__/scheduled-action.test.ts.snap +0 -61
- package/tests/__snapshots__/sets.test.ts.snap +0 -46
- package/tests/__snapshots__/settings.test.ts.snap +0 -52
- package/tests/__snapshots__/team.test.ts.snap +0 -374
- package/tests/__snapshots__/verification-listener.test.ts.snap +0 -152
- package/tests/__snapshots__/verification.test.ts.snap +0 -302
- package/tests/_util.ts +0 -242
- package/tests/account-strikes.test.ts +0 -184
- package/tests/ack-all-subjects-of-account.test.ts +0 -177
- package/tests/age-assurance.test.ts +0 -372
- package/tests/blob-divert.test.ts +0 -106
- package/tests/communication-templates.test.ts +0 -149
- package/tests/content-tagger.test.ts +0 -170
- package/tests/db.test.ts +0 -184
- package/tests/expiring-label.test.ts +0 -72
- package/tests/expiring-tags.test.ts +0 -232
- package/tests/get-account-timeline.test.ts +0 -85
- package/tests/get-config.test.ts +0 -55
- package/tests/get-lists.test.ts +0 -111
- package/tests/get-profiles.test.ts +0 -70
- package/tests/get-record.test.ts +0 -130
- package/tests/get-records.test.ts +0 -91
- package/tests/get-repo.test.ts +0 -171
- package/tests/get-report.test.ts +0 -136
- package/tests/get-reporter-stats.test.ts +0 -132
- package/tests/get-repos.test.ts +0 -91
- package/tests/get-starter-pack.test.ts +0 -115
- package/tests/get-subjects.test.ts +0 -81
- package/tests/mod-tool.test.ts +0 -268
- package/tests/moderation-appeals.test.ts +0 -260
- package/tests/moderation-events.test.ts +0 -756
- package/tests/moderation-status-tags.test.ts +0 -140
- package/tests/moderation-statuses.test.ts +0 -495
- package/tests/moderation.test.ts +0 -992
- package/tests/protected-tags.test.ts +0 -218
- package/tests/query-labels.test.ts +0 -238
- package/tests/query-reports.test.ts +0 -608
- package/tests/queue-assignment.test.ts +0 -428
- package/tests/queue-router.test.ts +0 -306
- package/tests/queues.test.ts +0 -690
- package/tests/record-and-account-events.test.ts +0 -197
- package/tests/repo-search.test.ts +0 -136
- package/tests/report-action.test.ts +0 -308
- package/tests/report-activity.test.ts +0 -711
- package/tests/report-assignment.test.ts +0 -517
- package/tests/report-muting.test.ts +0 -100
- package/tests/report-reason.test.ts +0 -154
- package/tests/report-reassign-queue.test.ts +0 -340
- package/tests/report-routing.test.ts +0 -245
- package/tests/report-stats.test.ts +0 -545
- package/tests/revoke-account-credentials.test.ts +0 -54
- package/tests/safelink.test.ts +0 -534
- package/tests/scheduled-action-processor.test.ts +0 -488
- package/tests/scheduled-action.test.ts +0 -334
- package/tests/sequencer.test.ts +0 -227
- package/tests/server.test.ts +0 -62
- package/tests/sets.test.ts +0 -246
- package/tests/settings.test.ts +0 -308
- package/tests/strike-expiry-processor.test.ts +0 -299
- package/tests/subject-priority-score.test.ts +0 -96
- package/tests/takedown.test.ts +0 -105
- package/tests/team.test.ts +0 -216
- package/tests/verification-listener.test.ts +0 -129
- package/tests/verification.test.ts +0 -186
- package/tsconfig.build.json +0 -9
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -7
- package/tsconfig.tests.json +0 -8
|
@@ -1,334 +0,0 @@
|
|
|
1
|
-
import { AtpAgent } from '@atproto/api'
|
|
2
|
-
import { SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env'
|
|
3
|
-
import { ids } from '../src/lexicon/lexicons.js'
|
|
4
|
-
import { forSnapshot } from './_util.js'
|
|
5
|
-
|
|
6
|
-
const allStatuses = ['pending', 'executed', 'cancelled', 'failed']
|
|
7
|
-
|
|
8
|
-
describe('scheduled action management', () => {
|
|
9
|
-
let network: TestNetwork
|
|
10
|
-
let adminAgent: AtpAgent
|
|
11
|
-
let modAgent: AtpAgent
|
|
12
|
-
let triageAgent: AtpAgent
|
|
13
|
-
let sc: SeedClient
|
|
14
|
-
|
|
15
|
-
const getAdminHeaders = async (route: string) => {
|
|
16
|
-
return {
|
|
17
|
-
headers: await network.ozone.modHeaders(route, 'admin'),
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const getModHeaders = async (route: string) => {
|
|
22
|
-
return {
|
|
23
|
-
headers: await network.ozone.modHeaders(route, 'moderator'),
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const getModEvent = async (params: {
|
|
28
|
-
subject: string
|
|
29
|
-
cancellation?: boolean
|
|
30
|
-
}) => {
|
|
31
|
-
const {
|
|
32
|
-
data: { events },
|
|
33
|
-
} = await adminAgent.tools.ozone.moderation.queryEvents(
|
|
34
|
-
{
|
|
35
|
-
subject: params.subject,
|
|
36
|
-
types: [
|
|
37
|
-
params.cancellation
|
|
38
|
-
? 'tools.ozone.moderation.defs#cancelScheduledTakedownEvent'
|
|
39
|
-
: 'tools.ozone.moderation.defs#scheduleTakedownEvent',
|
|
40
|
-
],
|
|
41
|
-
},
|
|
42
|
-
await getAdminHeaders(ids.ToolsOzoneModerationQueryEvents),
|
|
43
|
-
)
|
|
44
|
-
return events
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
beforeAll(async () => {
|
|
48
|
-
network = await TestNetwork.create({
|
|
49
|
-
dbPostgresSchema: 'ozone_scheduled_action_test',
|
|
50
|
-
})
|
|
51
|
-
adminAgent = network.ozone.getAgent()
|
|
52
|
-
modAgent = network.ozone.getAgent()
|
|
53
|
-
triageAgent = network.ozone.getAgent()
|
|
54
|
-
sc = network.getSeedClient()
|
|
55
|
-
await basicSeed(sc)
|
|
56
|
-
await network.processAll()
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
afterAll(async () => {
|
|
60
|
-
await network?.close()
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
describe('scheduleAction', () => {
|
|
64
|
-
const getTestAction = () => ({
|
|
65
|
-
action: {
|
|
66
|
-
$type: 'tools.ozone.moderation.scheduleAction#takedown',
|
|
67
|
-
comment: 'test',
|
|
68
|
-
policies: ['spam'],
|
|
69
|
-
},
|
|
70
|
-
subjects: [sc.dids.carol, sc.dids.bob],
|
|
71
|
-
createdBy: 'did:plc:moderator',
|
|
72
|
-
scheduling: {
|
|
73
|
-
executeAt: new Date(Date.now() + 60 * 60 * 1000).toISOString(), // 1 hour from now
|
|
74
|
-
},
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
it('allows admins to schedule actions', async () => {
|
|
78
|
-
const { data: result } =
|
|
79
|
-
await adminAgent.tools.ozone.moderation.scheduleAction(
|
|
80
|
-
getTestAction(),
|
|
81
|
-
await getAdminHeaders(ids.ToolsOzoneModerationScheduleAction),
|
|
82
|
-
)
|
|
83
|
-
const bobsModEvents = await getModEvent({ subject: sc.dids.bob })
|
|
84
|
-
|
|
85
|
-
expect(result.succeeded.length).toBe(2)
|
|
86
|
-
expect(result.failed.length).toBe(0)
|
|
87
|
-
expect(result.succeeded).toContain(sc.dids.carol)
|
|
88
|
-
expect(result.succeeded).toContain(sc.dids.bob)
|
|
89
|
-
expect(bobsModEvents.length).toBe(1)
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
it('rejects triage role from scheduling actions', async () => {
|
|
93
|
-
await expect(
|
|
94
|
-
triageAgent.tools.ozone.moderation.scheduleAction(getTestAction(), {
|
|
95
|
-
headers: await network.ozone.modHeaders(
|
|
96
|
-
ids.ToolsOzoneModerationScheduleAction,
|
|
97
|
-
'triage',
|
|
98
|
-
),
|
|
99
|
-
}),
|
|
100
|
-
).rejects.toThrow('Must be a moderator to schedule actions')
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
it('supports scheduling with time range (executeAfter/executeUntil)', async () => {
|
|
104
|
-
const rangeAction = {
|
|
105
|
-
...getTestAction(),
|
|
106
|
-
subjects: [sc.dids.alice],
|
|
107
|
-
scheduling: {
|
|
108
|
-
executeAfter: new Date(Date.now() + 30 * 60 * 1000).toISOString(), // 30 min from now
|
|
109
|
-
executeUntil: new Date(Date.now() + 90 * 60 * 1000).toISOString(), // 90 min from now
|
|
110
|
-
},
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const { data: result } =
|
|
114
|
-
await adminAgent.tools.ozone.moderation.scheduleAction(
|
|
115
|
-
rangeAction,
|
|
116
|
-
await getAdminHeaders(ids.ToolsOzoneModerationScheduleAction),
|
|
117
|
-
)
|
|
118
|
-
expect(result.succeeded.length).toBe(1)
|
|
119
|
-
expect(result.succeeded).toContain(sc.dids.alice)
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
it('prevents scheduling multiple actions for same subject', async () => {
|
|
123
|
-
const duplicateAction = {
|
|
124
|
-
...getTestAction(),
|
|
125
|
-
subjects: [sc.dids.carol],
|
|
126
|
-
scheduling: {
|
|
127
|
-
executeAt: new Date(Date.now() + 3 * 60 * 60 * 1000).toISOString(),
|
|
128
|
-
},
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const { data: result } =
|
|
132
|
-
await adminAgent.tools.ozone.moderation.scheduleAction(
|
|
133
|
-
duplicateAction,
|
|
134
|
-
await getAdminHeaders(ids.ToolsOzoneModerationScheduleAction),
|
|
135
|
-
)
|
|
136
|
-
expect(result.succeeded.length).toBe(0)
|
|
137
|
-
expect(result.failed.length).toBe(1)
|
|
138
|
-
expect(result.failed[0].subject).toBe(sc.dids.carol)
|
|
139
|
-
expect(result.failed[0].error).toContain(
|
|
140
|
-
'A pending scheduled action already exists',
|
|
141
|
-
)
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
it('validates scheduling parameters', async () => {
|
|
145
|
-
const invalidAction = {
|
|
146
|
-
...getTestAction(),
|
|
147
|
-
subjects: ['did:plc:test_invalid'],
|
|
148
|
-
scheduling: {
|
|
149
|
-
executeAfter: new Date(Date.now() + 90 * 60 * 1000).toISOString(),
|
|
150
|
-
executeUntil: new Date(Date.now() + 30 * 60 * 1000).toISOString(), // executeUntil before executeAfter
|
|
151
|
-
},
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const { data } = await adminAgent.tools.ozone.moderation.scheduleAction(
|
|
155
|
-
invalidAction,
|
|
156
|
-
await getAdminHeaders(ids.ToolsOzoneModerationScheduleAction),
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
expect(data.failed.length).toBe(1)
|
|
160
|
-
expect(data.failed[0].subject).toBe('did:plc:test_invalid')
|
|
161
|
-
expect(data.failed[0].error).toContain(
|
|
162
|
-
'executeAfter must be before executeUntil',
|
|
163
|
-
)
|
|
164
|
-
})
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
describe('listScheduledActions', () => {
|
|
168
|
-
it('allows moderators to list all scheduled actions', async () => {
|
|
169
|
-
const { data: result } =
|
|
170
|
-
await modAgent.tools.ozone.moderation.listScheduledActions(
|
|
171
|
-
{ statuses: allStatuses },
|
|
172
|
-
await getModHeaders(ids.ToolsOzoneModerationListScheduledActions),
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
expect(result.actions.length).toBeGreaterThan(0)
|
|
176
|
-
expect(forSnapshot(result.actions)).toMatchSnapshot()
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
it('allows filtering by subjects', async () => {
|
|
180
|
-
const { data: result } =
|
|
181
|
-
await adminAgent.tools.ozone.moderation.listScheduledActions(
|
|
182
|
-
{
|
|
183
|
-
subjects: [sc.dids.carol],
|
|
184
|
-
statuses: allStatuses,
|
|
185
|
-
},
|
|
186
|
-
await getAdminHeaders(ids.ToolsOzoneModerationListScheduledActions),
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
expect(result.actions.length).toBeGreaterThan(0)
|
|
190
|
-
result.actions.forEach((action) => {
|
|
191
|
-
expect(action.did).toBe(sc.dids.carol)
|
|
192
|
-
})
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
it('allows filtering by status', async () => {
|
|
196
|
-
const { data: result } =
|
|
197
|
-
await adminAgent.tools.ozone.moderation.listScheduledActions(
|
|
198
|
-
{
|
|
199
|
-
statuses: ['pending'],
|
|
200
|
-
},
|
|
201
|
-
await getAdminHeaders(ids.ToolsOzoneModerationListScheduledActions),
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
expect(result.actions.length).toBeGreaterThan(0)
|
|
205
|
-
result.actions.forEach((action) => {
|
|
206
|
-
expect(action.status).toBe('pending')
|
|
207
|
-
})
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
it('supports time range filtering', async () => {
|
|
211
|
-
const now = new Date()
|
|
212
|
-
const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000)
|
|
213
|
-
const twoHoursFromNow = new Date(now.getTime() + 2 * 60 * 60 * 1000)
|
|
214
|
-
|
|
215
|
-
const { data: result } =
|
|
216
|
-
await adminAgent.tools.ozone.moderation.listScheduledActions(
|
|
217
|
-
{
|
|
218
|
-
startsAfter: oneHourAgo.toISOString(),
|
|
219
|
-
endsBefore: twoHoursFromNow.toISOString(),
|
|
220
|
-
statuses: allStatuses,
|
|
221
|
-
},
|
|
222
|
-
await getAdminHeaders(ids.ToolsOzoneModerationListScheduledActions),
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
expect(result.actions.length).toBeGreaterThan(0)
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
it('supports pagination', async () => {
|
|
229
|
-
const headers = await getAdminHeaders(
|
|
230
|
-
ids.ToolsOzoneModerationListScheduledActions,
|
|
231
|
-
)
|
|
232
|
-
const { data: page1 } =
|
|
233
|
-
await adminAgent.tools.ozone.moderation.listScheduledActions(
|
|
234
|
-
{ limit: 2, statuses: allStatuses },
|
|
235
|
-
headers,
|
|
236
|
-
)
|
|
237
|
-
|
|
238
|
-
expect(page1.actions.length).toBe(2)
|
|
239
|
-
expect(page1.cursor).toBeDefined()
|
|
240
|
-
|
|
241
|
-
const { data: page2 } =
|
|
242
|
-
await adminAgent.tools.ozone.moderation.listScheduledActions(
|
|
243
|
-
{
|
|
244
|
-
limit: 2,
|
|
245
|
-
statuses: allStatuses,
|
|
246
|
-
cursor: page1.cursor,
|
|
247
|
-
},
|
|
248
|
-
headers,
|
|
249
|
-
)
|
|
250
|
-
|
|
251
|
-
expect(page2.actions.length).toBeGreaterThan(0)
|
|
252
|
-
expect(page1.actions.map((a) => a.did)).not.toContain(
|
|
253
|
-
page2.actions[0].did,
|
|
254
|
-
)
|
|
255
|
-
})
|
|
256
|
-
})
|
|
257
|
-
|
|
258
|
-
describe('cancelScheduledActions', () => {
|
|
259
|
-
it('allows moderators to cancel scheduled actions', async () => {
|
|
260
|
-
const { data: result } =
|
|
261
|
-
await modAgent.tools.ozone.moderation.cancelScheduledActions(
|
|
262
|
-
{
|
|
263
|
-
subjects: [sc.dids.bob],
|
|
264
|
-
},
|
|
265
|
-
await getModHeaders(ids.ToolsOzoneModerationCancelScheduledActions),
|
|
266
|
-
)
|
|
267
|
-
const bobsModEvents = await getModEvent({
|
|
268
|
-
subject: sc.dids.bob,
|
|
269
|
-
cancellation: true,
|
|
270
|
-
})
|
|
271
|
-
expect(result.succeeded.length).toBe(1)
|
|
272
|
-
expect(result.failed.length).toBe(0)
|
|
273
|
-
expect(result.succeeded).toContain(sc.dids.bob)
|
|
274
|
-
expect(bobsModEvents.length).toBe(1)
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
it('allows admins to cancel scheduled actions', async () => {
|
|
278
|
-
const { data: result } =
|
|
279
|
-
await adminAgent.tools.ozone.moderation.cancelScheduledActions(
|
|
280
|
-
{
|
|
281
|
-
subjects: [sc.dids.carol],
|
|
282
|
-
},
|
|
283
|
-
await getAdminHeaders(ids.ToolsOzoneModerationCancelScheduledActions),
|
|
284
|
-
)
|
|
285
|
-
|
|
286
|
-
expect(result.succeeded.length).toBe(1)
|
|
287
|
-
expect(result.failed.length).toBe(0)
|
|
288
|
-
expect(result.succeeded).toContain(sc.dids.carol)
|
|
289
|
-
|
|
290
|
-
const {
|
|
291
|
-
data: { actions },
|
|
292
|
-
} = await adminAgent.tools.ozone.moderation.listScheduledActions(
|
|
293
|
-
{
|
|
294
|
-
statuses: allStatuses,
|
|
295
|
-
subjects: [sc.dids.carol],
|
|
296
|
-
},
|
|
297
|
-
await getAdminHeaders(ids.ToolsOzoneModerationListScheduledActions),
|
|
298
|
-
)
|
|
299
|
-
|
|
300
|
-
expect(actions[0].status).toBe('cancelled')
|
|
301
|
-
})
|
|
302
|
-
|
|
303
|
-
it('handles cancellation of non-existent actions', async () => {
|
|
304
|
-
const { data: result } =
|
|
305
|
-
await adminAgent.tools.ozone.moderation.cancelScheduledActions(
|
|
306
|
-
{
|
|
307
|
-
subjects: ['did:plc:nonexistent'],
|
|
308
|
-
},
|
|
309
|
-
await getAdminHeaders(ids.ToolsOzoneModerationCancelScheduledActions),
|
|
310
|
-
)
|
|
311
|
-
|
|
312
|
-
expect(result.succeeded.length).toBe(0)
|
|
313
|
-
expect(result.failed.length).toBe(1)
|
|
314
|
-
expect(result.failed[0].did).toBe('did:plc:nonexistent')
|
|
315
|
-
expect(result.failed[0].error).toContain(
|
|
316
|
-
'No pending scheduled actions found',
|
|
317
|
-
)
|
|
318
|
-
})
|
|
319
|
-
|
|
320
|
-
it('rejects triage moderators from cancelling actions', async () => {
|
|
321
|
-
await expect(
|
|
322
|
-
triageAgent.tools.ozone.moderation.cancelScheduledActions(
|
|
323
|
-
{ subjects: [sc.dids.carol] },
|
|
324
|
-
{
|
|
325
|
-
headers: await network.ozone.modHeaders(
|
|
326
|
-
ids.ToolsOzoneModerationCancelScheduledActions,
|
|
327
|
-
'triage',
|
|
328
|
-
),
|
|
329
|
-
},
|
|
330
|
-
),
|
|
331
|
-
).rejects.toThrow('Must be a moderator to cancel scheduled actions')
|
|
332
|
-
})
|
|
333
|
-
})
|
|
334
|
-
})
|
package/tests/sequencer.test.ts
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import { readFromGenerator, wait } from '@atproto/common'
|
|
2
|
-
import { randomStr } from '@atproto/crypto'
|
|
3
|
-
import { EXAMPLE_LABELER, TestNetwork } from '@atproto/dev-env'
|
|
4
|
-
import { Label } from '../src/lexicon/types/com/atproto/label/defs.js'
|
|
5
|
-
import { LabelsEvt, Sequencer } from '../src/sequencer/index.js'
|
|
6
|
-
import { Outbox } from '../src/sequencer/outbox.js'
|
|
7
|
-
|
|
8
|
-
describe('sequencer', () => {
|
|
9
|
-
let network: TestNetwork
|
|
10
|
-
let sequencer: Sequencer
|
|
11
|
-
|
|
12
|
-
let totalEvts = 0
|
|
13
|
-
let lastSeen: number
|
|
14
|
-
|
|
15
|
-
beforeAll(async () => {
|
|
16
|
-
network = await TestNetwork.create({
|
|
17
|
-
dbPostgresSchema: 'ozone_sequencer',
|
|
18
|
-
})
|
|
19
|
-
// @ts-expect-error
|
|
20
|
-
sequencer = network.ozone.ctx.sequencer
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
afterAll(async () => {
|
|
24
|
-
await network?.close()
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
const loadFromDb = (lastSeen: number) => {
|
|
28
|
-
return sequencer.db.db
|
|
29
|
-
.selectFrom('label')
|
|
30
|
-
.selectAll()
|
|
31
|
-
.where('id', '>', lastSeen)
|
|
32
|
-
.orderBy('id', 'asc')
|
|
33
|
-
.execute()
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const evtToDbRow = (e: LabelsEvt) => {
|
|
37
|
-
const { ver: _, ...label } = e.labels[0]
|
|
38
|
-
return {
|
|
39
|
-
id: e.seq,
|
|
40
|
-
...label,
|
|
41
|
-
neg: !!label.neg,
|
|
42
|
-
cid: label.cid ? label.cid : '',
|
|
43
|
-
exp: null,
|
|
44
|
-
sig: label.sig ? Buffer.from(label.sig) : null,
|
|
45
|
-
signingKeyId: network.ozone.ctx.signingKeyId,
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const caughtUp = (outbox: Outbox): (() => Promise<boolean>) => {
|
|
50
|
-
return async () => {
|
|
51
|
-
const lastEvt = await outbox.sequencer.curr()
|
|
52
|
-
if (lastEvt === null) return true
|
|
53
|
-
return outbox.lastSeen >= (lastEvt ?? 0)
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const createLabels = async (count: number): Promise<Label[]> => {
|
|
58
|
-
const labels: Label[] = []
|
|
59
|
-
for (let i = 0; i < count; i++) {
|
|
60
|
-
const did = `did:example:${randomStr(10, 'base32')}`
|
|
61
|
-
const label = {
|
|
62
|
-
src: EXAMPLE_LABELER,
|
|
63
|
-
uri: did,
|
|
64
|
-
val: 'spam',
|
|
65
|
-
neg: false,
|
|
66
|
-
cts: new Date().toISOString(),
|
|
67
|
-
}
|
|
68
|
-
await network.ozone.ctx.db.transaction((dbTxn) =>
|
|
69
|
-
network.ozone.ctx.modService(dbTxn).createLabels([label]),
|
|
70
|
-
)
|
|
71
|
-
labels.push(label)
|
|
72
|
-
}
|
|
73
|
-
return labels
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
it('sends to outbox', async () => {
|
|
77
|
-
const count = 20
|
|
78
|
-
totalEvts += count
|
|
79
|
-
await createLabels(count)
|
|
80
|
-
const outbox = new Outbox(sequencer)
|
|
81
|
-
const evts = await readFromGenerator(outbox.events(-1), caughtUp(outbox))
|
|
82
|
-
expect(evts.length).toBe(totalEvts)
|
|
83
|
-
|
|
84
|
-
const fromDb = await loadFromDb(-1)
|
|
85
|
-
expect(evts.map(evtToDbRow)).toEqual(fromDb)
|
|
86
|
-
|
|
87
|
-
lastSeen = evts.at(-1)?.seq ?? lastSeen
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('sequences negative labels', async () => {
|
|
91
|
-
const count = 5
|
|
92
|
-
totalEvts += count
|
|
93
|
-
const created = await createLabels(count)
|
|
94
|
-
const toNegate = created
|
|
95
|
-
.slice(0, 2)
|
|
96
|
-
.map((l) => ({ ...l, neg: true, cts: new Date().toISOString() }))
|
|
97
|
-
await network.ozone.ctx
|
|
98
|
-
.modService(network.ozone.ctx.db)
|
|
99
|
-
.createLabels(toNegate)
|
|
100
|
-
|
|
101
|
-
const outbox = new Outbox(sequencer)
|
|
102
|
-
const evts = await readFromGenerator(
|
|
103
|
-
outbox.events(lastSeen),
|
|
104
|
-
caughtUp(outbox),
|
|
105
|
-
)
|
|
106
|
-
expect(evts.length).toBe(count)
|
|
107
|
-
|
|
108
|
-
const fromDb = await loadFromDb(lastSeen)
|
|
109
|
-
expect(evts.map(evtToDbRow)).toEqual(fromDb)
|
|
110
|
-
expect(evts[3].labels[0].uri).toEqual(toNegate[0].uri)
|
|
111
|
-
expect(evts[3].labels[0].neg).toBe(true)
|
|
112
|
-
expect(evts[4].labels[0].uri).toEqual(toNegate[1].uri)
|
|
113
|
-
expect(evts[4].labels[0].neg).toBe(true)
|
|
114
|
-
|
|
115
|
-
lastSeen = evts.at(-1)?.seq ?? lastSeen
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
it('handles cut over', async () => {
|
|
119
|
-
const count = 20
|
|
120
|
-
totalEvts += count
|
|
121
|
-
const outbox = new Outbox(sequencer)
|
|
122
|
-
const createPromise = createLabels(count)
|
|
123
|
-
const [evts] = await Promise.all([
|
|
124
|
-
readFromGenerator(outbox.events(-1), caughtUp(outbox), createPromise),
|
|
125
|
-
createPromise,
|
|
126
|
-
])
|
|
127
|
-
expect(evts.length).toBe(totalEvts)
|
|
128
|
-
|
|
129
|
-
const fromDb = await loadFromDb(-1)
|
|
130
|
-
expect(evts.map(evtToDbRow)).toEqual(fromDb)
|
|
131
|
-
|
|
132
|
-
lastSeen = evts.at(-1)?.seq ?? lastSeen
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
it('only gets events after cursor', async () => {
|
|
136
|
-
const count = 20
|
|
137
|
-
totalEvts += count
|
|
138
|
-
const outbox = new Outbox(sequencer)
|
|
139
|
-
const createPromise = createLabels(count)
|
|
140
|
-
const [evts] = await Promise.all([
|
|
141
|
-
readFromGenerator(
|
|
142
|
-
outbox.events(lastSeen),
|
|
143
|
-
caughtUp(outbox),
|
|
144
|
-
createPromise,
|
|
145
|
-
),
|
|
146
|
-
createPromise,
|
|
147
|
-
])
|
|
148
|
-
|
|
149
|
-
// +1 because we send the lastSeen date as well
|
|
150
|
-
expect(evts.length).toBe(count)
|
|
151
|
-
|
|
152
|
-
const fromDb = await loadFromDb(lastSeen)
|
|
153
|
-
expect(evts.map(evtToDbRow)).toEqual(fromDb)
|
|
154
|
-
|
|
155
|
-
lastSeen = evts.at(-1)?.seq ?? lastSeen
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
it('buffers events that are not being read', async () => {
|
|
159
|
-
const count = 20
|
|
160
|
-
totalEvts += count
|
|
161
|
-
const outbox = new Outbox(sequencer)
|
|
162
|
-
const createPromise = createLabels(count)
|
|
163
|
-
const gen = outbox.events(lastSeen)
|
|
164
|
-
// read enough to start streaming then wait so that the rest go into the buffer,
|
|
165
|
-
// then stream out from buffer
|
|
166
|
-
const [firstPart] = await Promise.all([
|
|
167
|
-
readFromGenerator(gen, caughtUp(outbox), createPromise, 5),
|
|
168
|
-
createPromise,
|
|
169
|
-
])
|
|
170
|
-
const secondPart = await readFromGenerator(
|
|
171
|
-
gen,
|
|
172
|
-
caughtUp(outbox),
|
|
173
|
-
createPromise,
|
|
174
|
-
)
|
|
175
|
-
const evts = [...firstPart, ...secondPart]
|
|
176
|
-
expect(evts.length).toBe(count)
|
|
177
|
-
|
|
178
|
-
const fromDb = await loadFromDb(lastSeen)
|
|
179
|
-
expect(evts.map(evtToDbRow)).toEqual(fromDb)
|
|
180
|
-
|
|
181
|
-
lastSeen = evts.at(-1)?.seq ?? lastSeen
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
it('errors when buffer is overloaded', async () => {
|
|
185
|
-
const count = 20
|
|
186
|
-
totalEvts += count
|
|
187
|
-
const outbox = new Outbox(sequencer, { maxBufferSize: 5 })
|
|
188
|
-
const gen = outbox.events(lastSeen)
|
|
189
|
-
const createPromise = createLabels(count)
|
|
190
|
-
// read enough to start streaming then wait to stream rest until buffer is overloaded
|
|
191
|
-
const overloadBuffer = async () => {
|
|
192
|
-
await Promise.all([
|
|
193
|
-
readFromGenerator(gen, caughtUp(outbox), createPromise, 5),
|
|
194
|
-
createPromise,
|
|
195
|
-
])
|
|
196
|
-
await wait(500)
|
|
197
|
-
await readFromGenerator(gen, caughtUp(outbox), createPromise)
|
|
198
|
-
}
|
|
199
|
-
await expect(overloadBuffer).rejects.toThrow('Stream consumer too slow')
|
|
200
|
-
|
|
201
|
-
await createPromise
|
|
202
|
-
|
|
203
|
-
const fromDb = await loadFromDb(lastSeen)
|
|
204
|
-
lastSeen = fromDb.at(-1)?.id ?? lastSeen
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
it('handles many open connections', async () => {
|
|
208
|
-
const count = 20
|
|
209
|
-
const outboxes: Outbox[] = []
|
|
210
|
-
for (let i = 0; i < 50; i++) {
|
|
211
|
-
outboxes.push(new Outbox(sequencer))
|
|
212
|
-
}
|
|
213
|
-
const createPromise = createLabels(count)
|
|
214
|
-
const readOutboxes = Promise.all(
|
|
215
|
-
outboxes.map((o) =>
|
|
216
|
-
readFromGenerator(o.events(lastSeen), caughtUp(o), createPromise),
|
|
217
|
-
),
|
|
218
|
-
)
|
|
219
|
-
const [results] = await Promise.all([readOutboxes, createPromise])
|
|
220
|
-
const fromDb = await loadFromDb(lastSeen)
|
|
221
|
-
for (const result of results) {
|
|
222
|
-
expect(result.length).toBe(count)
|
|
223
|
-
expect(result.map(evtToDbRow)).toEqual(fromDb)
|
|
224
|
-
}
|
|
225
|
-
lastSeen = results[0].at(-1)?.seq ?? lastSeen
|
|
226
|
-
})
|
|
227
|
-
})
|
package/tests/server.test.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import express from 'express'
|
|
2
|
-
import { TestNetwork, TestOzone } from '@atproto/dev-env'
|
|
3
|
-
import { handler as errorHandler } from '../src/error.js'
|
|
4
|
-
import { startServer } from './_util.js'
|
|
5
|
-
|
|
6
|
-
describe('server', () => {
|
|
7
|
-
let network: TestNetwork
|
|
8
|
-
let ozone: TestOzone
|
|
9
|
-
|
|
10
|
-
beforeAll(async () => {
|
|
11
|
-
network = await TestNetwork.create({
|
|
12
|
-
dbPostgresSchema: 'ozone_server',
|
|
13
|
-
})
|
|
14
|
-
ozone = network.ozone
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
afterAll(async () => {
|
|
18
|
-
await network?.close()
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it('preserves 404s.', async () => {
|
|
22
|
-
const response = await fetch(`${ozone.url}/unknown`)
|
|
23
|
-
expect(response.status).toEqual(404)
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('error handler turns unknown errors into 500s.', async () => {
|
|
27
|
-
const app = express()
|
|
28
|
-
.get('/oops', () => {
|
|
29
|
-
throw new Error('Oops!')
|
|
30
|
-
})
|
|
31
|
-
.use(errorHandler)
|
|
32
|
-
|
|
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
|
-
})
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('healthcheck succeeds when database is available.', async () => {
|
|
44
|
-
const response = await fetch(`${network.bsky.url}/xrpc/_health`)
|
|
45
|
-
expect(response.status).toEqual(200)
|
|
46
|
-
await expect(response.json()).resolves.toEqual({ version: 'unknown' })
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('healthcheck fails when database is unavailable.', async () => {
|
|
50
|
-
// destroy sequencer to release connection that would prevent the db from closing
|
|
51
|
-
await ozone.ctx.sequencer.destroy()
|
|
52
|
-
await ozone.ctx.db.close()
|
|
53
|
-
|
|
54
|
-
const res = await fetch(`${ozone.url}/xrpc/_health`)
|
|
55
|
-
|
|
56
|
-
expect(res.status).toEqual(503)
|
|
57
|
-
await expect(res.json()).resolves.toEqual({
|
|
58
|
-
version: '0.0.0',
|
|
59
|
-
error: 'Service Unavailable',
|
|
60
|
-
})
|
|
61
|
-
})
|
|
62
|
-
})
|