atproto/packages/ozone/tests/blob-divert.test.ts
Matthieu Sieben 72eba67af1
Drop axios dependency (#3177)
* Minor adaptation of VerifyCidTransform implementation

* refactor: factorize content-encoding negotiation into new lib

* bsky: Use undici to stream blob

* fixup! bsky: Use undici to stream blob

* disable ssrf bsky protection in dev-env

* remove http requests to self to host "/img/"

* drop axios from tests

* fixes

* fix tests

* reviex changes

* properly handle HEAD requests

* handle client disconnection

* fix tests

* drop unrelated change

* tidy

* tidy

* tidy

* remove axios from dev-env

* remove axios from identity package

* use undici 6

* remove axios dependency from ozone

* tidy

* remove axios from PDS package

* avoid killing bsky-pds connections

* improve debugging data

* Better handle invalid CID

* tidy

* tidy

* refactor "allFulfilled" util in @atproto/common

* tidy

---------

Co-authored-by: devin ivy <devinivy@gmail.com>
2025-01-06 18:34:11 +01:00

101 lines
2.8 KiB
TypeScript

import {
ModeratorClient,
SeedClient,
TestNetwork,
basicSeed,
} from '@atproto/dev-env'
import { ResponseType, XRPCError } from '@atproto/xrpc'
import assert from 'node:assert'
import { forSnapshot } from './_util'
describe('blob divert', () => {
let network: TestNetwork
let sc: SeedClient
let modClient: ModeratorClient
beforeAll(async () => {
network = await TestNetwork.create({
dbPostgresSchema: 'ozone_blob_divert_test',
ozone: {
blobDivertUrl: `https://blob-report.com`,
blobDivertAdminPassword: 'test-auth-token',
},
})
sc = network.getSeedClient()
modClient = network.ozone.getModClient()
await basicSeed(sc)
await network.processAll()
})
afterAll(async () => {
await network.close()
})
const mockReportServiceResponse = (succeeds: boolean) => {
const blobDiverter = network.ozone.ctx.blobDiverter
assert(blobDiverter)
return jest
.spyOn(blobDiverter, 'uploadBlob')
.mockImplementation(async () => {
if (!succeeds) {
// Using an XRPCError to trigger retries
throw new XRPCError(ResponseType.Unknown, undefined)
}
})
}
const getSubject = () => ({
$type: 'com.atproto.repo.strongRef',
uri: sc.posts[sc.dids.carol][0].ref.uriStr,
cid: sc.posts[sc.dids.carol][0].ref.cidStr,
})
const getImages = () => sc.posts[sc.dids.carol][0].images
const emitDivertEvent = async () =>
modClient.emitEvent(
{
subject: getSubject(),
event: {
$type: 'tools.ozone.moderation.defs#modEventDivert',
comment: 'Diverting for test',
},
createdBy: sc.dids.alice,
subjectBlobCids: getImages().map((img) => img.image.ref.toString()),
},
'moderator',
)
it('fails and keeps attempt count when report service fails to accept upload.', async () => {
// Simulate failure to fail upload
const reportServiceRequest = mockReportServiceResponse(false)
try {
await expect(emitDivertEvent()).rejects.toThrow('Failed to process blobs')
// 1 initial attempt + 3 retries
expect(reportServiceRequest).toHaveBeenCalledTimes(getImages().length * 4)
} finally {
reportServiceRequest.mockRestore()
}
})
it('sends blobs to configured divert service and marks divert date', async () => {
// Simulate success to accept upload
const reportServiceRequest = mockReportServiceResponse(true)
try {
const divertEvent = await emitDivertEvent()
expect(reportServiceRequest).toHaveBeenCalledTimes(getImages().length)
expect(forSnapshot(divertEvent)).toMatchSnapshot()
const { subjectStatuses } = await modClient.queryStatuses({
subject: getSubject().uri,
})
expect(subjectStatuses[0].takendown).toBe(true)
} finally {
reportServiceRequest.mockRestore()
}
})
})