mirror of
https://github.com/renovatebot/renovate.git
synced 2026-11-02 10:33:39 +00:00
196101a6bb
* chore(deps): update dependency oxlint-tsgolint to v0.25.0 | datasource | package | from | to | | ---------- | --------------- | ------ | ------ | | npm | oxlint-tsgolint | 0.23.0 | 0.25.0 | * chore: fix new no-unnecessary-type-assertion findings Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Sebastian Poxhofer <sebastian.poxhofer@stedi.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
486 lines
13 KiB
TypeScript
486 lines
13 KiB
TypeScript
import { GlobalConfig } from '../../../config/global.ts';
|
|
import { getTtlOverride, resolveTtlValues } from './ttl.ts';
|
|
|
|
describe('util/cache/package/ttl', () => {
|
|
beforeEach(() => {
|
|
GlobalConfig.reset();
|
|
});
|
|
|
|
describe('getTtlOverride', () => {
|
|
describe('No configuration', () => {
|
|
it('returns undefined when no cacheTtlOverride config exists', () => {
|
|
GlobalConfig.set({});
|
|
|
|
const res = getTtlOverride('datasource-npm' as never);
|
|
|
|
expect(res).toBeUndefined();
|
|
});
|
|
|
|
it('returns undefined when cacheTtlOverride is empty', () => {
|
|
GlobalConfig.set({ cacheTtlOverride: {} });
|
|
|
|
const res = getTtlOverride('datasource-npm' as never);
|
|
|
|
expect(res).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('Exact match', () => {
|
|
it('returns exact match when namespace exists in config', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'datasource-npm': 120,
|
|
'datasource-docker': 60,
|
|
},
|
|
});
|
|
|
|
const resNpm = getTtlOverride('datasource-npm' as never);
|
|
const resDocker = getTtlOverride('datasource-docker' as never);
|
|
|
|
expect(resNpm).toBe(120);
|
|
expect(resDocker).toBe(60);
|
|
});
|
|
|
|
it('returns undefined when exact match is not a number', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
// @ts-expect-error -- testing
|
|
'datasource-npm': 'invalid',
|
|
},
|
|
});
|
|
|
|
const res = getTtlOverride('datasource-npm' as never);
|
|
|
|
expect(res).toBeUndefined();
|
|
});
|
|
|
|
it('returns undefined when no matching namespace found', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'datasource-npm': 120,
|
|
},
|
|
});
|
|
|
|
const res = getTtlOverride('datasource-docker' as never);
|
|
|
|
expect(res).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('Glob patterns', () => {
|
|
it('matches simple glob patterns', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'datasource-*': 90,
|
|
},
|
|
});
|
|
|
|
const resNpm = getTtlOverride('datasource-npm' as never);
|
|
const resDocker = getTtlOverride('datasource-docker' as never);
|
|
const resMaven = getTtlOverride('datasource-maven' as never);
|
|
|
|
expect(resNpm).toBe(90);
|
|
expect(resDocker).toBe(90);
|
|
expect(resMaven).toBe(90);
|
|
});
|
|
|
|
it('matches wildcard pattern for all namespaces', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'*': 45,
|
|
},
|
|
});
|
|
|
|
const resDatasource = getTtlOverride('datasource-npm' as never);
|
|
const resChangelog = getTtlOverride('changelog-github-notes@v2');
|
|
const resAny = getTtlOverride('any-namespace' as never);
|
|
|
|
expect(resDatasource).toBe(45);
|
|
expect(resChangelog).toBe(45);
|
|
expect(resAny).toBe(45);
|
|
});
|
|
|
|
it('matches complex glob patterns with braces', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'datasource-{npm,docker}': 150,
|
|
},
|
|
});
|
|
|
|
const resNpm = getTtlOverride('datasource-npm' as never);
|
|
const resDocker = getTtlOverride('datasource-docker' as never);
|
|
const resMaven = getTtlOverride('datasource-maven' as never);
|
|
|
|
expect(resNpm).toBe(150);
|
|
expect(resDocker).toBe(150);
|
|
expect(resMaven).toBeUndefined();
|
|
});
|
|
|
|
it('handles special characters in namespace patterns', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'datasource-npm:*': 120,
|
|
'changelog-*@v2': 60,
|
|
},
|
|
});
|
|
|
|
const resColon = getTtlOverride('datasource-npm:cache-provider');
|
|
const resAt = getTtlOverride('changelog-github-notes@v2');
|
|
|
|
expect(resColon).toBe(120);
|
|
expect(resAt).toBe(60);
|
|
});
|
|
});
|
|
|
|
describe('Regex patterns', () => {
|
|
it('matches regex patterns', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'/^datasource-/': 75,
|
|
},
|
|
});
|
|
|
|
const resNpm = getTtlOverride('datasource-npm' as never);
|
|
const resDocker = getTtlOverride('datasource-docker' as never);
|
|
const resChangelog = getTtlOverride('changelog-github-notes@v2');
|
|
|
|
expect(resNpm).toBe(75);
|
|
expect(resDocker).toBe(75);
|
|
expect(resChangelog).toBeUndefined();
|
|
});
|
|
|
|
it('matches patterns with regex escape sequences', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'/datasource-\\w+/': 120,
|
|
},
|
|
});
|
|
|
|
const resNpm = getTtlOverride('datasource-npm' as never);
|
|
const resNumeric = getTtlOverride('datasource-123' as never);
|
|
const resEmpty = getTtlOverride('datasource-' as never);
|
|
|
|
expect(resNpm).toBe(120);
|
|
expect(resNumeric).toBe(120);
|
|
expect(resEmpty).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('Priority and multiple patterns', () => {
|
|
it('prioritizes exact match over glob patterns', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'datasource-*': 90,
|
|
'datasource-npm': 120,
|
|
'*': 45,
|
|
},
|
|
});
|
|
|
|
const resNpm = getTtlOverride('datasource-npm' as never);
|
|
const resDocker = getTtlOverride('datasource-docker' as never);
|
|
|
|
expect(resNpm).toBe(120);
|
|
expect(resDocker).toBe(90);
|
|
});
|
|
|
|
it('returns longest matching pattern when multiple patterns apply', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'datasource-*': 90,
|
|
'datasource-n*': 100,
|
|
'*': 45,
|
|
},
|
|
});
|
|
|
|
const res = getTtlOverride('datasource-npm' as never);
|
|
|
|
expect(res).toBe(100);
|
|
});
|
|
|
|
it('selects longest matching pattern across all configs', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'*': 10,
|
|
'datasource-*': 20,
|
|
'datasource-npm*': 30,
|
|
'datasource-npm-*': 40,
|
|
},
|
|
});
|
|
|
|
const resRegistry = getTtlOverride('datasource-npm-registry' as never);
|
|
const resJs = getTtlOverride('datasource-npmjs' as never);
|
|
const resDocker = getTtlOverride('datasource-docker' as never);
|
|
|
|
expect(resRegistry).toBe(40);
|
|
expect(resJs).toBe(30);
|
|
expect(resDocker).toBe(20);
|
|
});
|
|
|
|
it('skips non-numeric values and selects next longest matching pattern', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
// @ts-expect-error -- testing
|
|
'datasource-*': 'invalid',
|
|
'datasource-n*': 100,
|
|
'*': 45,
|
|
},
|
|
});
|
|
|
|
const res = getTtlOverride('datasource-npm' as never);
|
|
|
|
expect(res).toBe(100);
|
|
});
|
|
|
|
it('returns undefined when no patterns match', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'changelog-*': 90,
|
|
'preset-*': 100,
|
|
},
|
|
});
|
|
|
|
const res = getTtlOverride('datasource-npm' as never);
|
|
|
|
expect(res).toBeUndefined();
|
|
});
|
|
|
|
it('applies patterns consistently regardless of case in config order', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'Datasource-*': 120,
|
|
'datasource-*': 90,
|
|
},
|
|
});
|
|
|
|
const res = getTtlOverride('datasource-docker' as never);
|
|
|
|
expect(res).toBe(120);
|
|
});
|
|
});
|
|
|
|
describe('Edge cases', () => {
|
|
it('handles empty string pattern', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'': 30,
|
|
'datasource-npm': 120,
|
|
},
|
|
});
|
|
|
|
const resNpm = getTtlOverride('datasource-npm' as never);
|
|
const resEmpty = getTtlOverride('' as never);
|
|
|
|
expect(resNpm).toBe(120);
|
|
expect(resEmpty).toBe(30);
|
|
});
|
|
|
|
it('treats null and undefined values as invalid', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
// @ts-expect-error -- testing
|
|
'datasource-npm': null,
|
|
// @ts-expect-error -- testing
|
|
'datasource-docker': undefined,
|
|
'datasource-maven': 90,
|
|
},
|
|
});
|
|
|
|
const resNpm = getTtlOverride('datasource-npm' as never);
|
|
const resDocker = getTtlOverride('datasource-docker' as never);
|
|
const resMaven = getTtlOverride('datasource-maven' as never);
|
|
|
|
expect(resNpm).toBeUndefined();
|
|
expect(resDocker).toBeUndefined();
|
|
expect(resMaven).toBe(90);
|
|
});
|
|
|
|
it('handles very large numbers', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'datasource-npm': 999999999,
|
|
},
|
|
});
|
|
|
|
const res = getTtlOverride('datasource-npm' as never);
|
|
|
|
expect(res).toBe(999999999);
|
|
});
|
|
|
|
it('handles negative numbers', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'datasource-npm': -100,
|
|
},
|
|
});
|
|
|
|
const res = getTtlOverride('datasource-npm' as never);
|
|
|
|
expect(res).toBe(-100);
|
|
});
|
|
|
|
it('treats string numbers as invalid, only accepts number types', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
// @ts-expect-error -- testing
|
|
'datasource-npm': '120',
|
|
'datasource-docker': 90,
|
|
},
|
|
});
|
|
|
|
const resNpm = getTtlOverride('datasource-npm' as never);
|
|
const resDocker = getTtlOverride('datasource-docker' as never);
|
|
|
|
expect(resNpm).toBeUndefined();
|
|
expect(resDocker).toBe(90);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('resolveTtlValues', () => {
|
|
describe('Default values', () => {
|
|
it('returns default values when no overrides set', () => {
|
|
GlobalConfig.set({});
|
|
|
|
const res = resolveTtlValues('datasource-npm' as never, 60);
|
|
|
|
expect(res).toEqual({
|
|
softTtlMinutes: 60,
|
|
hardTtlMinutes: 10080,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Override application', () => {
|
|
it('uses override for softTtlMinutes when available', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'datasource-npm': 120,
|
|
},
|
|
});
|
|
|
|
const res = resolveTtlValues('datasource-npm' as never, 60);
|
|
|
|
expect(res).toEqual({
|
|
softTtlMinutes: 120,
|
|
hardTtlMinutes: 10080,
|
|
});
|
|
});
|
|
|
|
it('applies custom cacheHardTtlMinutes from config', () => {
|
|
GlobalConfig.set({
|
|
cacheHardTtlMinutes: 1440,
|
|
});
|
|
|
|
const res = resolveTtlValues('datasource-npm' as never, 60);
|
|
|
|
expect(res).toEqual({
|
|
softTtlMinutes: 60,
|
|
hardTtlMinutes: 1440,
|
|
});
|
|
});
|
|
|
|
it('resolves TTL with glob pattern overrides', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'datasource-*': 180,
|
|
},
|
|
cacheHardTtlMinutes: 2880,
|
|
});
|
|
|
|
const res = resolveTtlValues('datasource-npm' as never, 60);
|
|
|
|
expect(res).toEqual({
|
|
softTtlMinutes: 180,
|
|
hardTtlMinutes: 2880,
|
|
});
|
|
});
|
|
|
|
it('resolves TTL correctly with multiple overlapping overrides', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'datasource-*': 100,
|
|
'datasource-npm': 200,
|
|
'*': 50,
|
|
},
|
|
cacheHardTtlMinutes: 5760,
|
|
});
|
|
|
|
const res = resolveTtlValues('datasource-npm' as never, 60);
|
|
|
|
expect(res).toEqual({
|
|
softTtlMinutes: 200,
|
|
hardTtlMinutes: 5760,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Hard TTL calculation', () => {
|
|
it('uses maximum of softTtlMinutes and cacheHardTtlMinutes for hardTtlMinutes', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'datasource-npm': 20160,
|
|
},
|
|
cacheHardTtlMinutes: 1440,
|
|
});
|
|
|
|
const res = resolveTtlValues('datasource-npm' as never, 60);
|
|
|
|
expect(res).toEqual({
|
|
softTtlMinutes: 20160,
|
|
hardTtlMinutes: 20160,
|
|
});
|
|
});
|
|
|
|
it('handles negative cacheHardTtlMinutes config', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'datasource-npm': 120,
|
|
},
|
|
cacheHardTtlMinutes: -1,
|
|
});
|
|
|
|
const res = resolveTtlValues('datasource-npm' as never, 60);
|
|
|
|
expect(res).toEqual({
|
|
softTtlMinutes: 120,
|
|
hardTtlMinutes: 120,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Edge cases and special scenarios', () => {
|
|
it('handles zero as valid override value', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
'datasource-npm': 0,
|
|
},
|
|
cacheHardTtlMinutes: 1440,
|
|
});
|
|
|
|
const res = resolveTtlValues('datasource-npm' as never, 60);
|
|
|
|
expect(res).toEqual({
|
|
softTtlMinutes: 0,
|
|
hardTtlMinutes: 1440,
|
|
});
|
|
});
|
|
|
|
it('uses fallback when override is not a number', () => {
|
|
GlobalConfig.set({
|
|
cacheTtlOverride: {
|
|
// @ts-expect-error -- testing
|
|
'datasource-npm': 'invalid',
|
|
},
|
|
});
|
|
|
|
const res = resolveTtlValues('datasource-npm' as never, 60);
|
|
|
|
expect(res).toEqual({
|
|
softTtlMinutes: 60,
|
|
hardTtlMinutes: 10080,
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|