Skip to content

Commit

Permalink
Merge edd2f37 into 550ce3c
Browse files Browse the repository at this point in the history
  • Loading branch information
Kikobeats committed Feb 25, 2019
2 parents 550ce3c + edd2f37 commit a5036da
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 19 deletions.
14 changes: 8 additions & 6 deletions index.js
Expand Up @@ -22,17 +22,19 @@ const getKey = url => {
const toSeconds = ms => Math.floor(ms / 1000)

const createSetCache = ({ revalidate }) => {
return ({ res, createdAt, isHit, ttl, force, etag }) => {
return ({ res, createdAt, isHit, ttl, hasForce, etag }) => {
// Specifies the maximum amount of time a resource
// will be considered fresh in seconds
const diff = force ? 0 : createdAt + ttl - Date.now()
const diff = hasForce ? 0 : createdAt + ttl - Date.now()
const maxAge = toSeconds(diff)

res.setHeader(
'Cache-Control',
`public, max-age=${maxAge}, s-maxage=${maxAge}, stale-while-revalidate=${toSeconds(
revalidate(ttl)
)}`
`public, max-age=${maxAge}, s-maxage=${maxAge}, stale-while-revalidate=${
hasForce ? 0 : toSeconds(revalidate(ttl))
}`
)

res.setHeader('X-Cache-Status', isHit ? 'HIT' : 'MISS')
res.setHeader('X-Cache-Expired-At', prettyMs(diff))
res.setHeader('ETag', etag)
Expand All @@ -54,7 +56,7 @@ module.exports = ({
})

return async ({ req, res, ...opts }) => {
const hasForce = Boolean(req.query ? req.query.force : parse(req.url).force)
const hasForce = Boolean(req.query ? req.query.force : parse(req.url.split('?')[1]).force)
const url = urlResolve('http://localhost', req.url)
const key = getKey(url)
const cachedResult = await cache.get(key)
Expand Down
65 changes: 52 additions & 13 deletions test/index.js
Expand Up @@ -13,6 +13,16 @@ const createServer = props => {
return listen(api)
}

const getMaxAge = headers =>
Number(
headers['cache-control']
.split('=')[1]
.split(' ')[0]
.split(',')[0]
)

const getRevalidate = headers => Number(headers['cache-control'].split('=')[3])

test('.get is required', t => {
const error = t.throws(() => cacheableResponse({}))
t.true(error instanceof AssertionError)
Expand All @@ -30,20 +40,21 @@ test('default ttl and revalidate', async t => {
get: ({ req, res }) => ({ data: { foo: 'bar' } }),
send: ({ data, headers, res, req, ...props }) => res.end('Welcome to Micro')
})

const { headers } = await got(`${url}/kikobeats`)
t.is(headers['cache-control'], 'public, max-age=7200, s-maxage=7200, stale-while-revalidate=300')
t.true([7200, 7199].includes(getMaxAge(headers)))
t.true([300, 299].includes(getRevalidate(headers)))
})

test('custom ttl', async t => {
const url = await createServer({
get: ({ req, res }) => ({ data: { foo: 'bar' }, ttl: 86400000 }),
send: ({ data, headers, res, req, ...props }) => res.end('Welcome to Micro')
})

const { headers } = await got(`${url}/kikobeats`)
t.is(
headers['cache-control'],
'public, max-age=86400, s-maxage=86400, stale-while-revalidate=3600'
)
t.true([86400, 86399].includes(getMaxAge(headers)))
t.true([3600, 3599].includes(getRevalidate(headers)))
})

test('custom revalidate', async t => {
Expand All @@ -52,11 +63,10 @@ test('custom revalidate', async t => {
get: ({ req, res }) => ({ data: { foo: 'bar' }, ttl: 86400000 }),
send: ({ data, headers, res, req, ...props }) => res.end('Welcome to Micro')
})

const { headers } = await got(`${url}/kikobeats`)
t.is(
headers['cache-control'],
'public, max-age=86400, s-maxage=86400, stale-while-revalidate=69120'
)
t.true([86400, 86399].includes(getMaxAge(headers)))
t.true([69120, 69119].includes(getRevalidate(headers)))
})

test('custom fixed revalidate', async t => {
Expand All @@ -65,11 +75,10 @@ test('custom fixed revalidate', async t => {
get: ({ req, res }) => ({ data: { foo: 'bar' }, ttl: 86400000 }),
send: ({ data, headers, res, req, ...props }) => res.end('Welcome to Micro')
})

const { headers } = await got(`${url}/kikobeats`)
t.is(
headers['cache-control'],
'public, max-age=86400, s-maxage=86400, stale-while-revalidate=300'
)
t.true([86400, 86399].includes(getMaxAge(headers)))
t.true([300, 299].includes(getRevalidate(headers)))
})

test('MISS for first access', async t => {
Expand Down Expand Up @@ -109,6 +118,36 @@ test('HIT for second access', async t => {
t.is(headers['x-cache-status'], 'HIT')
})

test('force query params to invalidate', async t => {
const url = await createServer({
get: ({ req, res }) => {
return {
data: { foo: 'bar' },
ttl: 86400000,
createdAt: Date.now(),
foo: { bar: true }
}
},
send: ({ data, headers, res, req, ...props }) => {
res.end('Welcome to Micro')
}
})

const { headers: headersOne } = await got(`${url}/kikobeats`)
t.is(headersOne['x-cache-status'], 'MISS')
t.true([86400, 86399].includes(getMaxAge(headersOne)))
t.true([3600, 3599].includes(getRevalidate(headersOne)))
const { headers: headersTwo } = await got(`${url}/kikobeats`)
t.is(headersTwo['x-cache-status'], 'HIT')
const { headers: headersThree } = await got(`${url}/kikobeats?force=true`)
t.is(headersThree['x-cache-status'], 'MISS')
t.is(headersThree['x-cache-expired-at'], '0ms')
t.is(getMaxAge(headersThree), 0)
t.is(getRevalidate(headersThree), 0)
const { headers: headersFour } = await got(`${url}/kikobeats`)
t.is(headersFour['x-cache-status'], 'HIT')
})

test('MISS after cache expiration', async t => {
const url = await createServer({
get: ({ req, res }) => {
Expand Down

0 comments on commit a5036da

Please sign in to comment.