Files
renovate/tools/lint/rules/require-cacheable.spec.ts
Jamie Tanna 295cd65124 chore(lint): require cacheable on withCache() calls (#45958)
As found while fixing the `go` datasource in #45949, calls to
`withCache()` lean on the default of `cacheable: true`, which can lead
to private packages being incorrectly cached.

This could lead to cross-pollination of access to private package
metadata, so we should be clear where we're not explicitly deciding
whether to cache or not, which we can do so with a new linting rule.

We'll make new findings an error, and existing issues appear a sa
warning.

As we improve coverage, we'll then remove this rule and make it so
`cacheable` is a required flag in `CachedOptions`.

Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
2026-09-14 14:41:35 +00:00

49 lines
1.6 KiB
TypeScript

import { RuleTester } from 'oxlint/plugins-dev';
import rule from './require-cacheable.ts';
RuleTester.describe = describe;
RuleTester.it = it;
const ruleTester = new RuleTester({
languageOptions: { parserOptions: { lang: 'ts' } },
});
ruleTester.run('require-cacheable', rule, {
valid: [
// the decision is made
`withCache({ namespace: 'datasource-npm', key, cacheable: false }, fn);`,
`withCache({ namespace: 'datasource-npm', key, cacheable: isPublic(url) }, fn);`,
// shorthand and string literal keys are still the property
`withCache({ namespace, key, cacheable }, fn);`,
`withCache({ namespace, key, 'cacheable': true }, fn);`,
// options we cannot see into
`withCache(options, fn);`,
`withCache({ ...options, key }, fn);`,
`withCache({ namespace, key, [propertyName]: true }, fn);`,
// some other function which happens to take options
`withRetry({ namespace, key }, fn);`,
`cache.withCache({ namespace, key }, fn);`,
// no arguments at all
`withCache();`,
],
invalid: [
{
code: `withCache({ namespace: 'datasource-npm', key }, fn);`,
errors: [{ messageId: 'requireCacheable' }],
},
{
code: `withCache({ namespace, key, ttlMinutes: 60, fallback: true }, fn);`,
errors: [{ messageId: 'requireCacheable' }],
},
{
code: `withCache({}, fn);`,
errors: [{ messageId: 'requireCacheable' }],
},
{
// a computed key we can read is not `cacheable`
code: `withCache({ namespace, key, ['ttlMinutes']: 60 }, fn);`,
errors: [{ messageId: 'requireCacheable' }],
},
],
});