Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/cache/sqlite-cache-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ module.exports = class SqliteCacheStore {
const now = Date.now()
for (const value of values) {
if (now >= value.deleteAt && !canBeExpired) {
return undefined
continue
}

let matches = true
Expand Down
75 changes: 75 additions & 0 deletions test/cache-interceptor/sqlite-cache-store-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const { test } = require('node:test')
const { notEqual, strictEqual, deepStrictEqual } = require('node:assert')
const { rm } = require('node:fs/promises')
const FakeTimers = require('@sinonjs/fake-timers')
const { cacheStoreTests, writeBody, compareGetResults } = require('./cache-store-test-utils.js')
const { runtimeFeatures } = require('../../lib/util/runtime-features.js')

Expand Down Expand Up @@ -156,6 +157,80 @@ test('SqliteCacheStore two writes', { skip: runtimeFeatures.has('sqlite') === fa
}
})

test('SqliteCacheStore skips expired entry to find non-expired match', { skip: runtimeFeatures.has('sqlite') === false }, async (t) => {
const SqliteCacheStore = require('../../lib/cache/sqlite-cache-store.js')

const clock = FakeTimers.install({
shouldClearNativeTimers: true
})
t.after(() => clock.uninstall())

const store = new SqliteCacheStore({
maxCount: 100
})

const keyA = {
origin: 'localhost',
path: '/',
method: 'GET',
headers: { 'x-vary': 'a' }
}

const valueA = {
statusCode: 200,
statusMessage: '',
headers: { foo: 'bar' },
vary: { 'x-vary': 'a' },
cacheControlDirectives: {},
cachedAt: Date.now(),
staleAt: Date.now() + 1000,
deleteAt: Date.now() + 1000
}

const bodyA = [Buffer.from('first')]

{
const writable = store.createWriteStream(keyA, valueA)
notEqual(writable, undefined)
writeBody(writable, bodyA)
}

// Advance past valueA's deleteAt
clock.tick(2000)

const keyB = {
origin: 'localhost',
path: '/',
method: 'GET',
headers: { 'x-vary': 'b' }
}

const valueB = {
statusCode: 200,
statusMessage: '',
headers: { foo: 'baz' },
vary: { 'x-vary': 'b' },
cacheControlDirectives: {},
cachedAt: Date.now(),
staleAt: Date.now() + 10000,
deleteAt: Date.now() + 10000
}

const bodyB = [Buffer.from('second')]

{
const writable = store.createWriteStream(keyB, valueB)
notEqual(writable, undefined)
writeBody(writable, bodyB)
}

// The expired entry (valueA) is sorted first by deleteAt ASC; the
// store must skip it and still return the matching valueB.
const result = store.get(structuredClone(keyB))
notEqual(result, undefined)
await compareGetResults(result, valueB, bodyB)
})

test('SqliteCacheStore write & read', { skip: runtimeFeatures.has('sqlite') === false }, async () => {
const SqliteCacheStore = require('../../lib/cache/sqlite-cache-store.js')

Expand Down
Loading