News: Cache-Buster für Bild-URLs, HEAD, Limiter-Feinschliff

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Opus 5.5 committed 2026-09-30 16:52:47 +00:00
1 parent 319f8937aa
commit a421308019
5 files changed
+57 -12

No files matched your search

+2 -1
View File
@@ -91,7 +91,8 @@ test('oeffentlich_nur_haengende_ohne_interna', async () => {
assert.equal(p1.posts.length, 6)
assert.equal(p1.hasMore, true)
assert.equal(p1.posts[0].title, 'Mit Foto')
assert.deepEqual(p1.posts[0].image, { w: 40, h: 30 })
assert.deepEqual({ ...p1.posts[0].image, v: undefined }, { w: 40, h: 30, v: undefined })
assert.ok(Number.isFinite(p1.posts[0].image.v), 'v = Cache-Buster fuer die Route')
assert.equal(p1.posts[0].date, '2026-10-01')
assert.equal(p1.posts[1].image, null)
for (const p of p1.posts) {
+39 -3
View File
@@ -96,11 +96,11 @@ test('oeffentlich_cors_cache', async () => {
assert.equal(res.headers.get('access-control-allow-origin'), '*')
assert.equal(res.headers.get('cache-control'), 'public, max-age=60')
const text = await res.text()
assert.ok(!text.includes('c1') && !text.includes('"p1"') && !text.includes('customerId') && !text.includes('projectId'))
assert.ok(!text.includes('"c1"') && !text.includes('"p1"') && !text.includes('customerId') && !text.includes('projectId'))
const body = JSON.parse(text)
assert.equal(body.posts.length, 1)
assert.equal(body.posts[0].image.src, `/api/public/news/${KEY}/img/${created.body.$id}.webp`)
assert.equal(body.posts[0].image.srcSmall, `/api/public/news/${KEY}/img/${created.body.$id}-s.webp`)
assert.equal(body.posts[0].image.src, `/api/public/news/${KEY}/img/${created.body.$id}.webp?v=${Date.parse(created.body.updatedAt)}`)
assert.equal(body.posts[0].image.srcSmall, `/api/public/news/${KEY}/img/${created.body.$id}-s.webp?v=${Date.parse(created.body.updatedAt)}`)
const etag = res.headers.get('etag')
assert.match(etag, /^W\//)
const again = await call('GET', `/api/public/news/${KEY}`, { cookie: null, headers: { 'if-none-match': etag } })
@@ -140,3 +140,39 @@ test('rate_limit_schreiben', async () => {
assert.ok(statuses.slice(0, 30).every((s) => s === 201), statuses.join())
assert.equal(statuses[30], 429)
})
test('cache_buster_aendert_sich_beim_bildtausch', async () => {
const first = await (await upload('p1')).json()
const created = await post('/api/news', { ...note, type: 'photo', imageId: first.imageId }, { cookie })
const src = async () => (await (await call('GET', `/api/public/news/${KEY}`, { cookie: null })).json()).posts.find((p) => p.id === created.body.$id).image
const before = await src()
assert.match(before.src, /\.webp\?v=\d+$/)
assert.match(before.srcSmall, /-s\.webp\?v=\d+$/)
await new Promise((r) => setTimeout(r, 15))
const second = await (await upload('p1')).json()
const res = await call('PATCH', `/api/news/${created.body.$id}`, { body: { ...note, type: 'photo', imageId: second.imageId } })
assert.equal(res.status, 200)
const now = await src()
assert.notEqual(now.src, before.src)
assert.equal((await call('GET', now.src, { cookie: null })).status, 200)
})
test('tippfehler_zaehlt_nicht_gegen_schreiblimit', async () => {
aw.seed('websiteProjects', { $id: 'p4', customerId: 'c3', projectName: 'Tippfehler' })
feature('p4', 'c3', 'active', 'defghijklmnopqrs')
const c = customerCookie('c3')
for (let i = 0; i < 35; i++) assert.equal((await post('/api/news', { ...note, projectId: 'gibtsnicht' }, { cookie: c })).status, 403)
assert.equal((await post('/api/news', { ...note, projectId: 'p4' }, { cookie: c })).status, 201)
})
test('head_erlaubt', async () => {
assert.equal((await call('HEAD', `/api/public/news/${KEY}`, { cookie: null })).status, 200)
})
test('oeffentlich_rate_limit_429', async () => {
const ip = { 'x-forwarded-for': '203.0.113.77' }
const statuses = []
for (let i = 0; i < 121; i++) statuses.push((await call('GET', `/api/public/news/${KEY2}`, { cookie: null, headers: ip })).status)
assert.ok(statuses.slice(0, 120).every((s) => s === 200))
assert.equal(statuses[120], 429)
})