72eba67af1
* 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>
97 lines
2.7 KiB
TypeScript
97 lines
2.7 KiB
TypeScript
import { cidForCbor } from '@atproto/common'
|
|
import { TestNetwork, basicSeed } from '@atproto/dev-env'
|
|
import { CID } from 'multiformats/cid'
|
|
import { Readable } from 'node:stream'
|
|
import { getInfo } from '../../src/image/sharp'
|
|
import { ImageUriBuilder } from '../../src/image/uri'
|
|
|
|
describe('image processing server', () => {
|
|
let network: TestNetwork
|
|
let fileDid: string
|
|
let fileCid: CID
|
|
|
|
beforeAll(async () => {
|
|
network = await TestNetwork.create({
|
|
dbPostgresSchema: 'bsky_image_processing_server',
|
|
})
|
|
const sc = network.getSeedClient()
|
|
await basicSeed(sc)
|
|
await network.processAll()
|
|
fileDid = sc.dids.carol
|
|
fileCid = sc.posts[fileDid][0].images[0].image.ref
|
|
})
|
|
|
|
afterAll(async () => {
|
|
await network.close()
|
|
})
|
|
|
|
it('processes image from blob resolver.', async () => {
|
|
const res = await fetch(
|
|
new URL(
|
|
`/img${ImageUriBuilder.getPath({
|
|
preset: 'feed_fullsize',
|
|
did: fileDid,
|
|
cid: fileCid.toString(),
|
|
})}`,
|
|
network.bsky.url,
|
|
),
|
|
)
|
|
|
|
const bytes = new Uint8Array(await res.arrayBuffer())
|
|
const info = await getInfo(Readable.from([bytes]))
|
|
|
|
expect(info).toEqual({
|
|
height: 580,
|
|
width: 1000,
|
|
size: 127578,
|
|
mime: 'image/jpeg',
|
|
})
|
|
expect(Object.fromEntries(res.headers)).toEqual(
|
|
expect.objectContaining({
|
|
'content-type': 'image/jpeg',
|
|
'cache-control': 'public, max-age=31536000',
|
|
'content-length': '127578',
|
|
}),
|
|
)
|
|
})
|
|
|
|
it('caches results.', async () => {
|
|
const path = ImageUriBuilder.getPath({
|
|
preset: 'avatar',
|
|
did: fileDid,
|
|
cid: fileCid.toString(),
|
|
})
|
|
const url = new URL(`/img${path}`, network.bsky.url)
|
|
|
|
const res1 = await fetch(url)
|
|
expect(res1.headers.get('x-cache')).toEqual('miss')
|
|
const bytes1 = new Uint8Array(await res1.arrayBuffer())
|
|
const res2 = await fetch(url)
|
|
expect(res2.headers.get('x-cache')).toEqual('hit')
|
|
const bytes2 = new Uint8Array(await res2.arrayBuffer())
|
|
const res3 = await fetch(url)
|
|
expect(res3.headers.get('x-cache')).toEqual('hit')
|
|
const bytes3 = new Uint8Array(await res3.arrayBuffer())
|
|
expect(Buffer.compare(bytes1, bytes2)).toEqual(0)
|
|
expect(Buffer.compare(bytes1, bytes3)).toEqual(0)
|
|
})
|
|
|
|
it('errors on missing file.', async () => {
|
|
const missingCid = await cidForCbor('missing-file')
|
|
|
|
const path = ImageUriBuilder.getPath({
|
|
preset: 'feed_fullsize',
|
|
did: fileDid,
|
|
cid: missingCid.toString(),
|
|
})
|
|
|
|
const url = new URL(`/img${path}`, network.bsky.url)
|
|
|
|
const res = await fetch(url)
|
|
expect(res.status).toEqual(404)
|
|
await expect(res.json()).resolves.toMatchObject({
|
|
message: 'Blob not found',
|
|
})
|
|
})
|
|
})
|