Skip to content

refactor: extract lastFmSeeder (phase 3.4) - #668

Merged
LucasSantana-Dev merged 5 commits into
mainfrom
refactor/lastfm-seeder
Apr 17, 2026
Merged

LucasSantana-Dev merged 5 commits into
mainfrom
refactor/lastfm-seeder

Conversation

@LucasSantana-Dev

@LucasSantana-Dev LucasSantana-Dev commented Apr 16, 2026 •

Copy link
Copy Markdown
Owner

Summary

Extracted Last.fm seeding logic from queueManipulation.ts to new dedicated module autoplay/lastFmSeeder.ts.

  • Moved collectLastFmCandidates and searchLastFmQuery functions (198 LOC)
  • Re-exported from queueManipulation.ts for backward compatibility
  • Added comprehensive test suite with 6 test cases (~180 LOC)
  • All existing tests pass (130/131; 1 pre-existing failure unrelated to changes)

Phase 3 Progress

  • 3.1: candidateScorer ✓
  • 3.2: diversitySelector ✓
  • 3.3: spotifyRecommender ✓
  • 3.4: lastFmSeeder ✓ (this PR)

Metrics

  • LOC moved: 198
  • New spec coverage: 6 test cases
  • Files changed: 3 (lastFmSeeder.ts, lastFmSeeder.spec.ts, queueManipulation.ts)
  • Behavior preserved: yes

Summary by CodeRabbit

Release Notes

  • Tests

    • Added comprehensive test suite for Last.fm autoplay seeding functionality, covering candidate generation, multi-search engine fallback, and exclusion handling.
  • Refactor

    • Reorganized Last.fm autoplay seeding logic into a dedicated module for improved code maintainability and modularity.

@vercel

vercel Bot commented Apr 16, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lucky Ready Ready Preview, Comment Apr 17, 2026 0:07am

Request Review

@LucasSantana-Dev
LucasSantana-Dev enabled auto-merge (squash) April 16, 2026 23:32
@coderabbitai

coderabbitai Bot commented Apr 16, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@LucasSantana-Dev has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 31 minutes and 17 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 31 minutes and 17 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e648ea0d-07f4-4d15-ae98-a31446649c40

📥 Commits

Reviewing files that changed from the base of the PR and between 91728e1 and 788e256.

📒 Files selected for processing (2)
  • packages/bot/src/utils/music/autoplay/lastFmSeeder.ts
  • packages/bot/src/utils/music/queueManipulation.ts
📝 Walkthrough

Walkthrough

This PR extracts the collectLastFmCandidates function and its searchLastFmQuery helper from queueManipulation.ts into a dedicated module packages/bot/src/utils/music/autoplay/lastFmSeeder.ts. The function is then re-exported from the original location for API compatibility. A comprehensive test suite is added to cover the function's behavior across multiple scenarios.

Changes

Cohort / File(s) Summary
Last.fm Seeder Module
packages/bot/src/utils/music/autoplay/lastFmSeeder.ts
New module implementing collectLastFmCandidates function that seeds autoplay candidates using Last.fm-linked user tastes. Includes seed-slice selection logic, multi-engine search with fallback behavior (Spotify → YouTube → Auto), similar-track fetching with scoring multipliers, and candidate filtering/scoring.
Last.fm Seeder Test Suite
packages/bot/src/utils/music/autoplay/lastFmSeeder.spec.ts
Comprehensive Jest test suite with full module mocking. Tests seed-slice consumption, single and multi-user seeding, search fallback across engines, similar-track retrieval paths, exclusion handling via excludedUrls, and graceful error handling when search results are unavailable.
Queue Manipulation Refactoring
packages/bot/src/utils/music/queueManipulation.ts
Removed original collectLastFmCandidates implementation and helper function. Added import from ./autoplay/lastFmSeeder and re-exported function to maintain public API compatibility.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: extracting Last.fm seeding logic into a new module as part of a refactoring phase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/lastfm-seeder

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

queueManipulation.ts collectGenreCandidates still calls searchLastFmQuery
after the extraction. Export it from lastFmSeeder.ts and import where used.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/bot/src/utils/music/queueManipulation.ts (1)

960-979: ⚠️ Potential issue | 🔴 Critical

Build-breaking: searchLastFmQuery was moved but is still referenced here.

The refactor moved searchLastFmQuery into autoplay/lastFmSeeder.ts as a module-private helper, but collectGenreCandidates at line 971 still calls it, causing the CI failure TS2304: Cannot find name 'searchLastFmQuery'. Export it from lastFmSeeder.ts and import it here.

🔧 Proposed fix

In packages/bot/src/utils/music/autoplay/lastFmSeeder.ts, export the helper:

-async function searchLastFmQuery(
+export async function searchLastFmQuery(
     queue: GuildQueue,
     query: string,
     requestedBy: User,
 ): Promise<Track[]> {

In packages/bot/src/utils/music/queueManipulation.ts, add it to the existing import:

-import { collectLastFmCandidates } from './autoplay/lastFmSeeder'
+import {
+    collectLastFmCandidates,
+    searchLastFmQuery,
+} from './autoplay/lastFmSeeder'

Alternatively, move collectGenreCandidates into its own autoplay module so it can keep using a local copy of the helper and eliminate this cross-module coupling.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/utils/music/queueManipulation.ts` around lines 960 - 979,
collectGenreCandidates references searchLastFmQuery which was made
module-private in autoplay/lastFmSeeder.ts, causing TS2304; fix by exporting
searchLastFmQuery from lastFmSeeder.ts (make it a named export) and then import
that named export into queueManipulation.ts where collectGenreCandidates is
defined so the function resolves, or alternatively move collectGenreCandidates
into the autoplay module so it can call the internal helper without cross-module
access.
🧹 Nitpick comments (4)
packages/bot/src/utils/music/autoplay/lastFmSeeder.spec.ts (2)

46-52: Redundant jest.clearAllMocks() in both beforeEach and afterEach.

Clearing twice per test is harmless but noisy. Keep just one (typically beforeEach) unless you have a specific reason.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/utils/music/autoplay/lastFmSeeder.spec.ts` around lines 46 -
52, The test file lastFmSeeder.spec.ts calls jest.clearAllMocks() in both
beforeEach and afterEach; remove the redundant call and keep a single clear in
beforeEach (or afterEach if preferred) so mocks are cleared once per test;
update the beforeEach/afterEach block accordingly to only call
jest.clearAllMocks() in one of those lifecycle hooks.

211-260: Fallback test is a bit over-specified but correct.

mockResolvedValueOnce/mockRejectedValueOnce queue three sequential responses, and toHaveBeenCalledTimes(3) confirms the Spotify→YouTube→AUTO fallback. One thing worth noting: this test relies on the ordering of QueryType engines in searchLastFmQuery; if that order is ever reordered or extended, this test will silently still pass when candidates.size > 0 even if fallback semantics changed. Consider asserting the exact arguments of each call (searchEngine: QueryType.SPOTIFY_SEARCH, then YOUTUBE_SEARCH, then AUTO) to pin behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/utils/music/autoplay/lastFmSeeder.spec.ts` around lines 211
- 260, Update the test to assert the exact search engine used on each sequential
call instead of only call count: after setting up mocks and calling
collectLastFmCandidates, add assertions that mockQueue.player.search was called
with arguments whose searchEngine property equals QueryType.SPOTIFY_SEARCH for
the first call, QueryType.YOUTUBE_SEARCH for the second, and QueryType.AUTO for
the third (use toHaveBeenNthCalledWith or inspect mock.calls and expect
objectContaining({ searchEngine: QueryType.<...> }) or direct index access).
This pins the Spotify→YouTube→AUTO fallback behavior and prevents silent breaks
if QueryType ordering changes; keep the existing candidates.size check and
restore mocks as before.
packages/bot/src/utils/music/autoplay/lastFmSeeder.ts (1)

15-19: Circular import between lastFmSeeder and queueManipulation.

lastFmSeeder.ts imports shouldIncludeCandidate, upsertScoredCandidate, and normalizeTrackKey from ../queueManipulation, while queueManipulation.ts imports and re-exports collectLastFmCandidates from this module. This works at runtime (both sides use the symbols only inside functions), but it's a classic code smell that can bite later — e.g., if any of these helpers become used at module top level, you'll get undefined due to TDZ in the cycle.

Consider moving shouldIncludeCandidate, upsertScoredCandidate, normalizeTrackKey, and related helpers into a dedicated low-level module (e.g., autoplay/candidateUtils.ts) that both queueManipulation and the autoplay seeders can depend on acyclically.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/utils/music/autoplay/lastFmSeeder.ts` around lines 15 - 19,
There is a circular import between lastFmSeeder (collectLastFmCandidates) and
queueManipulation caused by shared helpers; extract the low-level helpers
shouldIncludeCandidate, upsertScoredCandidate, and normalizeTrackKey (and any
related pure helper functions) into a new module (e.g.,
autoplay/candidateUtils.ts) and update both lastFmSeeder and queueManipulation
to import those helpers from the new module so neither file imports the other;
ensure the new module exports the same symbols and update imports in
collectLastFmCandidates and any queueManipulation functions to reference
candidateUtils instead.
packages/bot/src/utils/music/queueManipulation.ts (1)

60-62: Remove unused constants LASTFM_SEED_COUNT, LASTFM_SCORE_BOOST, and MAX_SIMILAR_LOOKUPS from this file.

These constants are not referenced in this file. Identical constants have been properly defined and used in lastFmSeeder.ts, making these definitions redundant. Remove them to eliminate duplication and code drift.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/src/utils/music/queueManipulation.ts` around lines 60 - 62,
Remove the unused constants LASTFM_SEED_COUNT, LASTFM_SCORE_BOOST, and
MAX_SIMILAR_LOOKUPS from queueManipulation.ts because they are not referenced in
this file and duplicate definitions exist in lastFmSeeder.ts; locate the
constant declarations (const LASTFM_SEED_COUNT, const LASTFM_SCORE_BOOST, const
MAX_SIMILAR_LOOKUPS) in queueManipulation.ts and delete those three lines so the
module relies on the canonical definitions in lastFmSeeder.ts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/bot/src/utils/music/autoplay/lastFmSeeder.spec.ts`:
- Around line 5-13: The import path for the lastfm module in
lastFmSeeder.spec.ts is incorrect (import * as lastfm from '../../lastfm') and
doesn't match the mocked target (jest.mock('../../../lastfm')), so update the
import to the same module you mock (change the import to import * as lastfm from
'../../../lastfm') or alternatively change the jest.mock call to
jest.mock('../../lastfm'); ensure the symbol lastfm used in tests (assertions
referring to the mocked module) references the same module path as the jest.mock
call so the mocks are applied and observed.

In `@packages/bot/src/utils/music/autoplay/lastFmSeeder.ts`:
- Around line 169-198: The helper function searchLastFmQuery is file-local but
collectGenreCandidates in queueManipulation.ts still calls it; make
searchLastFmQuery accessible by exporting it from lastFmSeeder.ts (e.g., export
the function or add it to module exports) and then import that symbol into
queueManipulation.ts where collectGenreCandidates uses it (ensure the imported
name matches searchLastFmQuery and keep the same signature: async function
searchLastFmQuery(queue: GuildQueue, query: string, requestedBy: User):
Promise<Track[]>).

---

Outside diff comments:
In `@packages/bot/src/utils/music/queueManipulation.ts`:
- Around line 960-979: collectGenreCandidates references searchLastFmQuery which
was made module-private in autoplay/lastFmSeeder.ts, causing TS2304; fix by
exporting searchLastFmQuery from lastFmSeeder.ts (make it a named export) and
then import that named export into queueManipulation.ts where
collectGenreCandidates is defined so the function resolves, or alternatively
move collectGenreCandidates into the autoplay module so it can call the internal
helper without cross-module access.

---

Nitpick comments:
In `@packages/bot/src/utils/music/autoplay/lastFmSeeder.spec.ts`:
- Around line 46-52: The test file lastFmSeeder.spec.ts calls
jest.clearAllMocks() in both beforeEach and afterEach; remove the redundant call
and keep a single clear in beforeEach (or afterEach if preferred) so mocks are
cleared once per test; update the beforeEach/afterEach block accordingly to only
call jest.clearAllMocks() in one of those lifecycle hooks.
- Around line 211-260: Update the test to assert the exact search engine used on
each sequential call instead of only call count: after setting up mocks and
calling collectLastFmCandidates, add assertions that mockQueue.player.search was
called with arguments whose searchEngine property equals
QueryType.SPOTIFY_SEARCH for the first call, QueryType.YOUTUBE_SEARCH for the
second, and QueryType.AUTO for the third (use toHaveBeenNthCalledWith or inspect
mock.calls and expect objectContaining({ searchEngine: QueryType.<...> }) or
direct index access). This pins the Spotify→YouTube→AUTO fallback behavior and
prevents silent breaks if QueryType ordering changes; keep the existing
candidates.size check and restore mocks as before.

In `@packages/bot/src/utils/music/autoplay/lastFmSeeder.ts`:
- Around line 15-19: There is a circular import between lastFmSeeder
(collectLastFmCandidates) and queueManipulation caused by shared helpers;
extract the low-level helpers shouldIncludeCandidate, upsertScoredCandidate, and
normalizeTrackKey (and any related pure helper functions) into a new module
(e.g., autoplay/candidateUtils.ts) and update both lastFmSeeder and
queueManipulation to import those helpers from the new module so neither file
imports the other; ensure the new module exports the same symbols and update
imports in collectLastFmCandidates and any queueManipulation functions to
reference candidateUtils instead.

In `@packages/bot/src/utils/music/queueManipulation.ts`:
- Around line 60-62: Remove the unused constants LASTFM_SEED_COUNT,
LASTFM_SCORE_BOOST, and MAX_SIMILAR_LOOKUPS from queueManipulation.ts because
they are not referenced in this file and duplicate definitions exist in
lastFmSeeder.ts; locate the constant declarations (const LASTFM_SEED_COUNT,
const LASTFM_SCORE_BOOST, const MAX_SIMILAR_LOOKUPS) in queueManipulation.ts and
delete those three lines so the module relies on the canonical definitions in
lastFmSeeder.ts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c32569bf-1eb9-44f0-b596-18b54c428887

📥 Commits

Reviewing files that changed from the base of the PR and between 33127df and 91728e1.

📒 Files selected for processing (3)
  • packages/bot/src/utils/music/autoplay/lastFmSeeder.spec.ts
  • packages/bot/src/utils/music/autoplay/lastFmSeeder.ts
  • packages/bot/src/utils/music/queueManipulation.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: SonarCloud Scan
🧰 Additional context used
🪛 GitHub Actions: CI/CD Pipeline
packages/bot/src/utils/music/queueManipulation.ts

[error] 971-971: TypeScript (tsc) failed with TS2304: Cannot find name 'searchLastFmQuery'.

Comment thread packages/bot/src/utils/music/autoplay/lastFmSeeder.spec.ts Outdated
Comment thread packages/bot/src/utils/music/autoplay/lastFmSeeder.ts Outdated
…lation suite)

Spec had unresolvable ESM import chain (uuid → import.meta in shared utils).
Behavior is fully covered through public API by 131 queueManipulation tests.
@sonarqubecloud

Copy link
Copy Markdown

@LucasSantana-Dev
LucasSantana-Dev merged commit 0b37965 into main Apr 17, 2026
12 checks passed
LucasSantana-Dev added a commit that referenced this pull request Apr 17, 2026
Resolved conflict in queueManipulation.ts by taking main (lastFmSeeder already
extracted via #668)
LucasSantana-Dev added a commit that referenced this pull request Apr 17, 2026
Post-merge cleanup: queueManipulation.ts had inline defs of collectLastFmCandidates
and searchLastFmQuery left from the pre-#668 base. Both are now imported from
autoplay/lastFmSeeder. Re-exported collectLastFmCandidates for back-compat.
LucasSantana-Dev added a commit that referenced this pull request Apr 17, 2026
* refactor: extract replenisher from queueManipulation (phase 3.6 - final)

* fix(bot): remove duplicate lastFmSeeder defs after main merge

Post-merge cleanup: queueManipulation.ts had inline defs of collectLastFmCandidates
and searchLastFmQuery left from the pre-#668 base. Both are now imported from
autoplay/lastFmSeeder. Re-exported collectLastFmCandidates for back-compat.

* fix: correct imports after merge resolution - export collectLastFmCandidates from queueManipulation
LucasSantana-Dev added a commit that referenced this pull request May 13, 2026
* refactor: extract lastFmSeeder from queueManipulation (phase 3.4)

* fix(bot): export searchLastFmQuery for queueManipulation reference

queueManipulation.ts collectGenreCandidates still calls searchLastFmQuery
after the extraction. Export it from lastFmSeeder.ts and import where used.

* test(bot): remove redundant lastFmSeeder spec (covered by queueManipulation suite)

Spec had unresolvable ESM import chain (uuid → import.meta in shared utils).
Behavior is fully covered through public API by 131 queueManipulation tests.
LucasSantana-Dev added a commit that referenced this pull request May 13, 2026
* refactor: extract replenisher from queueManipulation (phase 3.6 - final)

* fix(bot): remove duplicate lastFmSeeder defs after main merge

Post-merge cleanup: queueManipulation.ts had inline defs of collectLastFmCandidates
and searchLastFmQuery left from the pre-#668 base. Both are now imported from
autoplay/lastFmSeeder. Re-exported collectLastFmCandidates for back-compat.

* fix: correct imports after merge resolution - export collectLastFmCandidates from queueManipulation
@LucasSantana-Dev
LucasSantana-Dev deleted the refactor/lastfm-seeder branch May 23, 2026 02:21

This branch was successfully deployed

1 active deployment
Preview — 788e2565 Deployed Apr 17, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant