Testing
Test the code around s3nd without credentials, without network.
A bucket in memory
The highest-value stand-in is a fake S3Client that actually stores what it is given, because
then a snapshot genuinely round-trips — including the conditional headers, which is where the
interesting bugs live:
import type { S3Client } from '@aws-sdk/client-s3'
import { createBucket } from 's3nd'
export function memoryBucket() {
const objects = new Map<string, { body: Uint8Array; etag: string }>()
let counter = 0
const client = {
send: async (command: any) => {
const { Key, Body, IfMatch, IfNoneMatch } = command.input
const stored = objects.get(Key)
switch (command.constructor.name) {
case 'PutObjectCommand': {
if (IfNoneMatch === '*' && stored) throw precondition()
if (IfMatch != null && (!stored || IfMatch !== `"${stored.etag}"`)) throw precondition()
counter += 1
objects.set(Key, { body: Buffer.from(Body), etag: `etag-${counter}` })
return { ETag: `"etag-${counter}"` }
}
case 'GetObjectCommand': {
if (!stored) throw notFound()
return {
Body: {
transformToByteArray: async () => stored.body,
transformToString: async () => Buffer.from(stored.body).toString('utf8'),
},
ETag: `"${stored.etag}"`,
ContentLength: stored.body.byteLength,
}
}
case 'DeleteObjectCommand':
objects.delete(Key)
return {}
default:
throw new Error(`Unexpected command: ${command.constructor.name}`)
}
},
} as unknown as S3Client
return createBucket({ bucket: 'test', client })
}
const precondition = () =>
Object.assign(new Error('precondition failed'), {
name: 'PreconditionFailed',
$metadata: { httpStatusCode: 412 },
})
const notFound = () =>
Object.assign(new Error('no such key'), { name: 'NoSuchKey', $metadata: { httpStatusCode: 404 } })With that, the tests read like the feature:
it('carries the database to the other device', async () => {
const bucket = memoryBucket()
const code = store.codes.create()
await store.putSnapshot(code, { notes: [{ id: 1, title: 'Roof' }] }, { version: 3 })
const restored = await store.getSnapshot(store.codes.normalize(code.toLowerCase()), { maxVersion: 3 })
expect(restored?.data).toEqual({ notes: [{ id: 1, title: 'Roof' }] })
})
it('refuses a write when another device got there first', async () => {
const bucket = memoryBucket()
const first = await store.putSnapshot('user-42', { notes: [] })
await store.putSnapshot('user-42', { notes: ['from the phone'] })
await expect(
store.putSnapshot('user-42', { notes: ['from the laptop'] }, { ifMatch: first.etag }),
).rejects.toMatchObject({ code: 'PRECONDITION_FAILED' })
})Testing expiry
Expiry is decided from the envelope's expiresAt against the current time, so fake timers are
enough — no waiting, no sleeping:
it('stops answering once the code expires', async () => {
vi.useFakeTimers()
vi.setSystemTime(new Date('2026-08-27T12:00:00Z'))
const bucket = memoryBucket()
await store.putSnapshot('K7QP2M4X', { notes: [] }, { expiresIn: 3600 })
vi.setSystemTime(new Date('2026-08-27T13:00:01Z'))
expect(await store.getSnapshot('K7QP2M4X')).toBeNull()
})A recording stub is enough for simpler cases
When you only want to assert what was sent, a stub that records commands does the job:
const calls: any[] = []
const client = {
send: async (command: any) => {
calls.push(command)
return { ETag: '"stub"' }
},
} as unknown as S3ClientIt covers putSnapshot, upload, put and delete. It does not cover getUrl(), which
signs with the real SDK.
Testing signed URLs
Signing is a local computation: it needs credentials, but no network. Pass static ones:
const store = createBucket({
bucket: 'test',
region: 'eu-west-3',
credentials: {
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
},
})
const url = new URL(await store.getUrl('a.txt', { expiresIn: 60 }))
expect(url.searchParams.get('X-Amz-Expires')).toBe('60')
expect(url.searchParams.get('X-Amz-Signature')).toBeTruthy()Against a real bucket
An in-memory double cannot catch what only the real protocol does: checksum headers, path-style
addressing, and whether your provider actually honours If-Match on PutObject — support for
that arrived late, and not every S3-compatible service has it. Run MinIO and point an example at
it; see S3-compatible providers for the container command.