diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 77b80b94..785d2aee 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -15,9 +15,6 @@ updates: versions: [">=1.4"] - dependency-name: "alembic" versions: [">=1.11"] - # cachetools 6 removed cachetools.func.mru_cache, still used by modules/zim. - - dependency-name: "cachetools" - update-types: ["version-update:semver-major"] groups: python-dependencies: patterns: diff --git a/app/src/reportWebVitals.js b/app/src/reportWebVitals.js index 14ba227b..e3768eef 100644 --- a/app/src/reportWebVitals.js +++ b/app/src/reportWebVitals.js @@ -1,11 +1,12 @@ const reportWebVitals = onPerfEntry => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({getCLS, getFID, getFCP, getLCP, getTTFB}) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); + if (typeof onPerfEntry === 'function') { + // web-vitals 3+ renamed get* to on*; 5+ replaced FID with INP. + return import('web-vitals').then(({onCLS, onINP, onFCP, onLCP, onTTFB}) => { + onCLS(onPerfEntry); + onINP(onPerfEntry); + onFCP(onPerfEntry); + onLCP(onPerfEntry); + onTTFB(onPerfEntry); }); } }; diff --git a/app/src/reportWebVitals.test.js b/app/src/reportWebVitals.test.js new file mode 100644 index 00000000..31d24251 --- /dev/null +++ b/app/src/reportWebVitals.test.js @@ -0,0 +1,28 @@ +jest.mock('web-vitals', () => ({ + onCLS: jest.fn(), + onINP: jest.fn(), + onFCP: jest.fn(), + onLCP: jest.fn(), + onTTFB: jest.fn(), +})); + +import reportWebVitals from './reportWebVitals'; +import * as webVitals from 'web-vitals'; + +describe('reportWebVitals', () => { + test('subscribes the callback to every current web-vitals metric', async () => { + const callback = jest.fn(); + const pending = reportWebVitals(callback); + expect(pending).toBeDefined(); + await pending; + for (const name of ['onCLS', 'onINP', 'onFCP', 'onLCP', 'onTTFB']) { + expect(webVitals[name]).toHaveBeenCalledWith(callback); + } + }); + + test('does nothing without a function', async () => { + jest.clearAllMocks(); + expect(reportWebVitals(undefined)).toBeUndefined(); + expect(webVitals.onCLS).not.toHaveBeenCalled(); + }); +}); diff --git a/modules/zim/lib.py b/modules/zim/lib.py index 95761df2..667d5359 100644 --- a/modules/zim/lib.py +++ b/modules/zim/lib.py @@ -260,7 +260,7 @@ def search_all_zims(session: Session, search_str: str, tag_names: List[str] = No return all_results -@cachetools.func.mru_cache(maxsize=1_000) +@cachetools.func.lru_cache(maxsize=1_000) def get_estimates(search_str: str) -> List[int]: zims = Zims.get_all() estimates = [i.estimate(search_str) for i in zims] diff --git a/modules/zim/test/test_lib.py b/modules/zim/test/test_lib.py index b11532d1..c6b987dc 100644 --- a/modules/zim/test/test_lib.py +++ b/modules/zim/test/test_lib.py @@ -411,3 +411,15 @@ def test_get_custom_zims_directory(async_client, test_directory, test_wrolpi_con get_wrolpi_config().zims_destination = 'custom/zims' assert lib.get_zim_directory() == (test_directory / 'custom/zims') + + +def test_get_estimates_is_cached(): + """Repeated estimates for the same search string do not re-query the Zims.""" + from unittest import mock + from modules.zim import lib + + lib.get_estimates.cache_clear() + with mock.patch.object(lib.Zims, 'get_all', return_value=[]) as get_all: + assert lib.get_estimates('cached search') == [] + assert lib.get_estimates('cached search') == [] + assert get_all.call_count == 1 diff --git a/requirements.txt b/requirements.txt index eb150470..f7b838b4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ SQLAlchemy[asyncio]==1.3.24 aiohttp==3.14.3 alembic==1.10.4 beautifulsoup4==4.15.0 -cachetools==5.5.2 +cachetools==7.2.0 cryptography==50.0.2 ebooklib==0.20 feedparser==6.0.14