fix(search): runSearch bricht pending Debounce ab
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m20s

Enter waehrend Debounce-Fenster feuerte bislang eine zweite Fetch
fuer dieselbe Query. Race-Guard greift nicht, weil q identisch ist.
runSearch clearTimeout am Anfang behebt's, neuer Unit-Test sichert es.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-19 13:03:42 +02:00
parent e7067971a5
commit c45ef2a613
2 changed files with 18 additions and 0 deletions

View File

@@ -73,6 +73,8 @@ export class SearchStore {
}
async runSearch(q: string): Promise<void> {
if (this.debounceTimer) clearTimeout(this.debounceTimer);
this.debounceTimer = null;
this.localExhausted = false;
this.webPageno = 0;
this.webExhausted = false;

View File

@@ -221,6 +221,22 @@ describe('SearchStore', () => {
expect(fetchImpl.mock.calls[1][0]).toMatch(/&domains=chefkoch\.de/);
});
it('runSearch(q) cancels pending debounce to avoid double-fetch', async () => {
vi.useFakeTimers();
const fetchImpl = mockFetch([
{ body: { hits: [{ id: 1, title: 'immediate', description: null, image_path: null, source_domain: null, avg_stars: null, last_cooked_at: null }] } }
]);
const store = new SearchStore({ fetchImpl, debounceMs: 300 });
store.query = 'meal';
store.runDebounced(); // schedules the 300ms timer
// Before the timer fires, call runSearch immediately (e.g. form submit).
await store.runSearch('meal');
expect(fetchImpl).toHaveBeenCalledTimes(1);
// Now advance past the original debounce — timer must not still fire.
await vi.advanceTimersByTimeAsync(400);
expect(fetchImpl).toHaveBeenCalledTimes(1);
});
it('reSearch: immediate re-run with current query on filter change', async () => {
vi.useFakeTimers();
let filter = '';