|
|
@@ -58,7 +58,7 @@ describe('local request-image cache', () => {
|
|
|
const stored = await attachments.readImage(attachment)
|
|
|
const fileData = Uint8Array.of(0, 1, 2, 255)
|
|
|
const file = await attachments.saveFile({ data: fileData, name: 'notes.bin' })
|
|
|
- const policy = { maxPixels: 16 * 16, maxBytes: 4_096 }
|
|
|
+ const policy = { width: 22, height: 11, maxBytes: 4_096 }
|
|
|
const initial = await attachments.readImageRequest(attachment, policy)
|
|
|
const hash = String(initial.variantId).slice('sha256:'.length)
|
|
|
const cacheRoot = join(dshHome, 'cache')
|
|
|
@@ -86,7 +86,7 @@ describe('local request-image cache', () => {
|
|
|
const first = await attachments.saveImage({ data: await image(8, 4), mediaType: 'image/png' })
|
|
|
const second = await attachments.saveImage({ data: await image(4, 8), mediaType: 'image/png' })
|
|
|
const firstStored = await attachments.readImage(first)
|
|
|
- const policy = { maxPixels: 1_000, maxBytes: 1024 * 1024 }
|
|
|
+ const policy = { width: 8, height: 8, maxBytes: 1024 * 1024 }
|
|
|
|
|
|
const request = await attachments.readImageRequest(first, policy)
|
|
|
const batch = await Promise.all([first, second].map(
|
|
|
@@ -97,21 +97,61 @@ describe('local request-image cache', () => {
|
|
|
expect(batch.map(value => value.attachment.attachmentId)).toEqual([first.attachmentId, second.attachmentId])
|
|
|
})
|
|
|
|
|
|
- it('rejects invalid request policies', async () => {
|
|
|
+ it('rejects invalid request targets', async () => {
|
|
|
const attachments = await store()
|
|
|
const attachment = await attachments.saveImage({ data: await image(8, 4), mediaType: 'image/png' })
|
|
|
|
|
|
- await expect(attachments.readImageRequest(attachment, { maxPixels: 0, maxBytes: 100 }))
|
|
|
- .rejects.toThrow('Image request maxPixels must be a positive integer')
|
|
|
- await expect(attachments.readImageRequest(attachment, { maxPixels: 100, maxBytes: 0 }))
|
|
|
+ await expect(attachments.readImageRequest(attachment, { width: 0, height: 4, maxBytes: 100 }))
|
|
|
+ .rejects.toThrow('Image request width must be a positive integer')
|
|
|
+ await expect(attachments.readImageRequest(attachment, { width: 8, height: 1.5, maxBytes: 100 }))
|
|
|
+ .rejects.toThrow('Image request height must be a positive integer')
|
|
|
+ await expect(attachments.readImageRequest(attachment, { width: 8, height: 4, maxBytes: 0 }))
|
|
|
.rejects.toThrow('Image request maxBytes must be a positive integer')
|
|
|
})
|
|
|
|
|
|
+ it('resizes by the long edge to the exact target and keys the cache by target', async () => {
|
|
|
+ const attachments = await store()
|
|
|
+ const maxBytes = 2 * 1024 * 1024
|
|
|
+ const square = await attachments.saveImage({ data: await image(2048, 2048), mediaType: 'image/png' })
|
|
|
+ const small = await attachments.saveImage({ data: await image(800, 800), mediaType: 'image/png' })
|
|
|
+ const thin = await attachments.saveImage({ data: await image(8000, 40), mediaType: 'image/png' })
|
|
|
+ const wide = await attachments.saveImage({ data: await image(1920, 1080), mediaType: 'image/png' })
|
|
|
+ const tall = await attachments.saveImage({ data: await image(1080, 1920), mediaType: 'image/png' })
|
|
|
+
|
|
|
+ const squareRequest = await attachments.readImageRequest(square, { width: 1302, height: 1302, maxBytes })
|
|
|
+ const smallRequest = await attachments.readImageRequest(small, { width: 800, height: 800, maxBytes })
|
|
|
+ const thinRequest = await attachments.readImageRequest(thin, { width: 4096, height: 20, maxBytes })
|
|
|
+ const wideRequest = await attachments.readImageRequest(wide, { width: 1708, height: 961, maxBytes })
|
|
|
+ const tallRequest = await attachments.readImageRequest(tall, { width: 961, height: 1708, maxBytes })
|
|
|
+ const smaller = await attachments.readImageRequest(square, { width: 1024, height: 1024, maxBytes })
|
|
|
+ const enlarged = await attachments.readImageRequest(thin, { width: 9000, height: 45, maxBytes })
|
|
|
+
|
|
|
+ expect(squareRequest).toMatchObject({ width: 1302, height: 1302, mediaType: 'image/jpeg' })
|
|
|
+ expect(smallRequest).toMatchObject({ width: 800, height: 800, mediaType: 'image/png' })
|
|
|
+ expect(smallRequest.data).toEqual((await attachments.readImage(small)).data)
|
|
|
+ expect(thinRequest).toMatchObject({ width: 4096, height: 20 })
|
|
|
+ expect(wideRequest).toMatchObject({ width: 1708, height: 961 })
|
|
|
+ expect(tallRequest).toMatchObject({ width: 961, height: 1708 })
|
|
|
+ expect(enlarged).toMatchObject({ width: 8000, height: 40 })
|
|
|
+ expect(smaller.variantId).not.toBe(squareRequest.variantId)
|
|
|
+ expect(enlarged.variantId).not.toBe(thinRequest.variantId)
|
|
|
+ })
|
|
|
+
|
|
|
+ it('encodes the rounded short edge at the route target', async () => {
|
|
|
+ const attachments = await store()
|
|
|
+ const attachment = await attachments.saveImage({ data: await image(1224, 1429), mediaType: 'image/png' })
|
|
|
+ const request = await attachments.readImageRequest(attachment, {
|
|
|
+ width: 1187, height: 1386, maxBytes: 2 * 1024 * 1024,
|
|
|
+ })
|
|
|
+ expect(request).toMatchObject({ width: 1187, height: 1386 })
|
|
|
+ await expect(sharp(request.data).metadata()).resolves.toMatchObject({ width: 1187, height: 1386 })
|
|
|
+ })
|
|
|
+
|
|
|
it('keeps the smallest ladder output when the encoded-byte target is unreachable', async () => {
|
|
|
const attachments = await store()
|
|
|
const attachment = await attachments.saveImage({ data: await image(1, 1), mediaType: 'image/png' })
|
|
|
|
|
|
- const request = await attachments.readImageRequest(attachment, { maxPixels: 1, maxBytes: 1 })
|
|
|
+ const request = await attachments.readImageRequest(attachment, { width: 1, height: 1, maxBytes: 1 })
|
|
|
|
|
|
expect(request.mediaType).toBe('image/jpeg')
|
|
|
expect(request.bytes).toBeGreaterThan(1)
|
|
|
@@ -122,7 +162,7 @@ describe('local request-image cache', () => {
|
|
|
const dshHome = await home()
|
|
|
const attachments = new LocalAttachmentStore(new Context(), { dshHome })
|
|
|
const attachment = await attachments.saveImage({ data: await image(64, 32), mediaType: 'image/png' })
|
|
|
- const policy = { maxPixels: 16 * 16, maxBytes: 4_096 }
|
|
|
+ const policy = { width: 22, height: 11, maxBytes: 4_096 }
|
|
|
const initial = await attachments.readImageRequest(attachment, policy)
|
|
|
const hash = String(initial.variantId).slice('sha256:'.length)
|
|
|
const path = join(dshHome, 'cache', 'attachments', 'request-images', hash.slice(0, 2), hash)
|
|
|
@@ -171,10 +211,10 @@ describe('local request-image cache', () => {
|
|
|
data: await image(2048, 1024), mediaType: 'image/png', name: 'wide.png',
|
|
|
})
|
|
|
|
|
|
- const squareRequest = await attachments.readImageRequest(square, { maxPixels: 640_000, maxBytes: 1024 * 1024 })
|
|
|
- const wideRequest = await attachments.readImageRequest(wide, { maxPixels: 640_000, maxBytes: 1024 * 1024 })
|
|
|
- const repeated = await attachments.readImageRequest(wide, { maxPixels: 640_000, maxBytes: 1024 * 1024 })
|
|
|
- const low = await attachments.readImageRequest(wide, { maxPixels: 512 * 512, maxBytes: 1024 * 1024 })
|
|
|
+ const squareRequest = await attachments.readImageRequest(square, { width: 800, height: 800, maxBytes: 1024 * 1024 })
|
|
|
+ const wideRequest = await attachments.readImageRequest(wide, { width: 1130, height: 565, maxBytes: 1024 * 1024 })
|
|
|
+ const repeated = await attachments.readImageRequest(wide, { width: 1130, height: 565, maxBytes: 1024 * 1024 })
|
|
|
+ const low = await attachments.readImageRequest(wide, { width: 724, height: 362, maxBytes: 1024 * 1024 })
|
|
|
|
|
|
expect(squareRequest).toMatchObject({ width: 800, height: 800 })
|
|
|
expect(wideRequest).toMatchObject({ width: 1130, height: 565 })
|
|
|
@@ -214,8 +254,8 @@ describe('local request-image cache', () => {
|
|
|
const photo = await attachments.saveImage({ data: photoSource, mediaType: 'image/png' })
|
|
|
const alpha = await attachments.saveImage({ data: alphaSource, mediaType: 'image/png' })
|
|
|
|
|
|
- const photoRequest = await attachments.readImageRequest(photo, { maxPixels: 128 * 128, maxBytes: 1024 * 1024 })
|
|
|
- const alphaRequest = await attachments.readImageRequest(alpha, { maxPixels: 128 * 128, maxBytes: 4_096 })
|
|
|
+ const photoRequest = await attachments.readImageRequest(photo, { width: 128, height: 128, maxBytes: 1024 * 1024 })
|
|
|
+ const alphaRequest = await attachments.readImageRequest(alpha, { width: 128, height: 128, maxBytes: 4_096 })
|
|
|
|
|
|
expect(photoRequest.mediaType).toBe('image/jpeg')
|
|
|
expect(alphaRequest.mediaType).toBe('image/webp')
|
|
|
@@ -231,7 +271,7 @@ describe('local request-image cache', () => {
|
|
|
}).toColourspace('rgb16').png().toBuffer())
|
|
|
const attachment = await attachments.saveImage({ data: source, mediaType: 'image/png' })
|
|
|
|
|
|
- const request = await attachments.readImageRequest(attachment, { maxPixels: 16 * 16, maxBytes: 1024 * 1024 })
|
|
|
+ const request = await attachments.readImageRequest(attachment, { width: 22, height: 11, maxBytes: 1024 * 1024 })
|
|
|
|
|
|
expect(request.bytes).toBeLessThanOrEqual(1024 * 1024)
|
|
|
expect(request.width * request.height).toBeLessThanOrEqual(16 * 16)
|
|
|
@@ -245,7 +285,7 @@ describe('local request-image cache', () => {
|
|
|
const source = await complexOpaqueAlphaImage(64, 32)
|
|
|
const attachment = await attachments.saveImage({ data: source, mediaType: 'image/png' })
|
|
|
|
|
|
- const request = await attachments.readImageRequest(attachment, { maxPixels: 16 * 16, maxBytes: 1024 * 1024 })
|
|
|
+ const request = await attachments.readImageRequest(attachment, { width: 22, height: 11, maxBytes: 1024 * 1024 })
|
|
|
|
|
|
expect(request.mediaType).toBe('image/webp')
|
|
|
await expect(sharp(request.data).metadata()).resolves.toMatchObject({ hasAlpha: false })
|
|
|
@@ -267,7 +307,7 @@ describe('local request-image cache', () => {
|
|
|
}).png().toBuffer())
|
|
|
const attachment = await attachments.saveImage({ data: source, mediaType: 'image/png' })
|
|
|
|
|
|
- const request = await attachments.readImageRequest(attachment, { maxPixels: 640_000, maxBytes: 1024 * 1024 })
|
|
|
+ const request = await attachments.readImageRequest(attachment, { width: 800, height: 800, maxBytes: 1024 * 1024 })
|
|
|
|
|
|
expect(request).toMatchObject({ width: 800, height: 800 })
|
|
|
expect(request.bytes).toBeLessThanOrEqual(1024 * 1024)
|
|
|
@@ -280,7 +320,7 @@ describe('local request-image cache', () => {
|
|
|
})
|
|
|
const run = vi.spyOn(CompressionLimiter.prototype, 'run')
|
|
|
const controller = new AbortController()
|
|
|
- const policy = { maxPixels: 640_000, maxBytes: 1024 * 1024 }
|
|
|
+ const policy = { width: 1130, height: 565, maxBytes: 1024 * 1024 }
|
|
|
|
|
|
const cancelled = attachments.readImageRequest(attachment, policy, controller.signal)
|
|
|
const completed = attachments.readImageRequest(attachment, policy)
|
|
|
@@ -310,7 +350,7 @@ describe('local request-image cache', () => {
|
|
|
const controller = new AbortController()
|
|
|
const request = attachments.readImageRequest(
|
|
|
attachment,
|
|
|
- { maxPixels: 640_000, maxBytes: 1024 * 1024 },
|
|
|
+ { width: 1130, height: 565, maxBytes: 1024 * 1024 },
|
|
|
controller.signal,
|
|
|
)
|
|
|
await vi.waitFor(() => {
|
|
|
@@ -343,7 +383,7 @@ describe('local request-image cache', () => {
|
|
|
return actualRead(ref, signal)
|
|
|
})
|
|
|
const controller = new AbortController()
|
|
|
- const policy = { maxPixels: 640_000, maxBytes: 1024 * 1024 }
|
|
|
+ const policy = { width: 1130, height: 565, maxBytes: 1024 * 1024 }
|
|
|
const cancelled = attachments.readImageRequest(attachment, policy, controller.signal)
|
|
|
await vi.waitFor(() => {
|
|
|
expect(calls).toBe(1)
|