From 18a61f106511f5280971c66f91cb29d02c15b8d4 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sun, 2 Aug 2026 19:09:24 -0400 Subject: [PATCH] =?UTF-8?q?fix(discover):=20complete=20the=20page-test=20m?= =?UTF-8?q?ock=20+=20drop=20a=20return=20from=20returnsIn=20=E2=80=94=20#2?= =?UTF-8?q?375?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two CI failures from 6e39471a, both mechanical. web: src/routes/discover/discover.test.ts mocks $lib/api/suggestions with a factory, and SuggestionFeed now imports createSnoozesQuery from it. A factory-shaped module mock must export everything the component tree imports or rendering throws before any assertion runs — so all 12 of that suite's tests failed on a surface they don't even exercise. Stubbed the three new exports and defaulted the snooze query to empty, which keeps the feed's empty-state copy on the "no signal yet" branch those tests assert. (Same shape as Scribe #2109: when a shared component grows a dependency, the break is in unrelated fixtures, not assertions.) android: detekt ReturnCount — returnsIn had 3 returns against a limit of 2. Folded the two "nothing to state" guards into one by computing the remaining duration as a nullable up front. The Android compile and unit tests never ran on the last push: detekt gates them, so Lucide.Clock is still unproven. Co-Authored-By: Claude Opus 5 (1M context) --- .../com/fabledsword/minstrel/models/Discover.kt | 15 ++++++++------- web/src/routes/discover/discover.test.ts | 16 ++++++++++++++-- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/android/app/src/main/java/com/fabledsword/minstrel/models/Discover.kt b/android/app/src/main/java/com/fabledsword/minstrel/models/Discover.kt index 4c1b39e4..817eb0aa 100644 --- a/android/app/src/main/java/com/fabledsword/minstrel/models/Discover.kt +++ b/android/app/src/main/java/com/fabledsword/minstrel/models/Discover.kt @@ -98,13 +98,14 @@ data class SuggestionSnoozeRef( * better than rendering an empty line. */ fun returnsIn(nowMs: Long = System.currentTimeMillis()): String { - val untilMs = runCatching { Instant.parse(snoozedUntil).toEpochMilliseconds() } - .getOrNull() ?: return "shortly" - // Already lapsed by our clock, yet the server still returned it — the - // two disagree. Say something plausible rather than "today", which - // would read as a real prediction. - if (untilMs <= nowMs) return "shortly" - val days = ((untilMs - nowMs).toDouble() / MILLIS_PER_DAY).roundToInt() + val remainingMs = runCatching { Instant.parse(snoozedUntil).toEpochMilliseconds() } + .getOrNull()?.minus(nowMs) + // Two ways to have nothing to state: an unparseable timestamp, or one + // already lapsed by our clock though the server still returned the row + // (the two disagree). Neither is "today", which would read as a real + // prediction. + if (remainingMs == null || remainingMs <= 0) return "shortly" + val days = (remainingMs.toDouble() / MILLIS_PER_DAY).roundToInt() return when { days < 1 -> "today" days == 1 -> "tomorrow" diff --git a/web/src/routes/discover/discover.test.ts b/web/src/routes/discover/discover.test.ts index c8cf2351..45b77c53 100644 --- a/web/src/routes/discover/discover.test.ts +++ b/web/src/routes/discover/discover.test.ts @@ -21,8 +21,16 @@ vi.mock('$lib/api/lidarr', () => ({ createLidarrSearchQuery: vi.fn() })); +// SuggestionFeed reaches for the snooze surface too (#2375). These are +// stubbed here even though this page-level suite asserts nothing about +// snoozing: a factory-shaped module mock must export everything the +// component tree imports, or rendering the feed throws before any +// assertion runs. vi.mock('$lib/api/suggestions', () => ({ - createSuggestionsQuery: vi.fn() + createSuggestionsQuery: vi.fn(), + createSnoozesQuery: vi.fn(), + snoozeSuggestion: vi.fn().mockResolvedValue(undefined), + unsnoozeSuggestion: vi.fn().mockResolvedValue(undefined) })); vi.mock('$lib/api/requests', () => ({ @@ -42,11 +50,12 @@ vi.mock('@tanstack/svelte-query', async (importOriginal) => { import DiscoverPage from './+page.svelte'; import { createLidarrSearchQuery } from '$lib/api/lidarr'; -import { createSuggestionsQuery } from '$lib/api/suggestions'; +import { createSuggestionsQuery, createSnoozesQuery } from '$lib/api/suggestions'; import { createRequest } from '$lib/api/requests'; const mockedCreateQuery = createLidarrSearchQuery as ReturnType; const mockedCreateSuggestionsQuery = createSuggestionsQuery as ReturnType; +const mockedCreateSnoozesQuery = createSnoozesQuery as ReturnType; const mockedCreateRequest = createRequest as ReturnType; function result(over: Partial = {}): LidarrSearchResult { @@ -69,6 +78,9 @@ beforeEach(() => { // Default: empty suggestion feed so its empty-state copy renders without // interfering with search-mode tests. mockedCreateSuggestionsQuery.mockReturnValue(mockQuery({ data: [] })); + // Nothing parked, which keeps the feed's empty-state copy on the + // "no signal yet" branch that this suite's assertions expect. + mockedCreateSnoozesQuery.mockReturnValue(mockQuery({ data: [] })); }); afterEach(() => {