Skip to content

feat(media): collapse singleton media containers#860

Merged
wizzomafizzo merged 7 commits into
mainfrom
feat/singleton-media-alias
May 29, 2026
Merged

feat(media): collapse singleton media containers#860
wizzomafizzo merged 7 commits into
mainfrom
feat/singleton-media-alias

Conversation

@wizzomafizzo
Copy link
Copy Markdown
Member

@wizzomafizzo wizzomafizzo commented May 29, 2026

Summary

  • resolve singleton folder/zip media containers to their sole indexed child for meta/image lookups
  • annotate singleton browse directories with playable media metadata
  • add byte-exact descendant matching and docs/tests for container aliases

Verification

  • go test ./pkg/api/methods/ ./pkg/database/mediadb/
  • golangci-lint run ./pkg/api/methods/ ./pkg/database/mediadb/

Summary by CodeRabbit

  • New Features

    • Directories with exactly one media item now surface that item's metadata, tags, zap script, relative path, and artwork as if the media itself were targeted; metadata/artwork are merged across equivalent/aliased media records.
  • Documentation

    • Clarified when mediaId, zapScript, relativePath, tags, and media meta/image resolution apply, including singleton container behavior on zip-as-directory platforms.
  • Tests

    • Added tests for singleton-container fallbacks, alias/merge behavior, single-descendant lookups, and related image/meta resolution.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 29, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 29df74e5-196b-48b2-acf6-80e7ebb4bc73

📥 Commits

Reviewing files that changed from the base of the PR and between 0d89680 and ecc1768.

📒 Files selected for processing (4)
  • pkg/api/methods/media_batch_test.go
  • pkg/api/methods/media_image_test.go
  • pkg/api/methods/media_meta_test.go
  • pkg/database/mediadb/sql_scraper_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/database/mediadb/sql_scraper_test.go

📝 Walkthrough

Walkthrough

This PR implements singleton container fallback resolution and equivalent-media metadata merging: single-child directories can resolve to their sole descendant, and media handlers merge or select metadata across equivalent media IDs (child and validated parent aliases). DB, resolver, browse, image/meta handlers, tests, and docs were added/updated.

Changes

Singleton Media Container and Alias Resolution

Layer / File(s) Summary
Database layer: FindSingleDescendantMedia interface and implementation
pkg/database/database.go, pkg/database/mediadb/sql_scraper.go, pkg/database/mediadb/sql_scraper_test.go, pkg/testing/helpers/db_mocks.go
New MediaDBI.FindSingleDescendantMedia method queries and returns exactly one non-missing media descendant beneath a directory path for a system, or nil, nil for zero/multiple matches. Implementation includes byte-exact prefix matching, missing-record filtering, tests for single/multiple/cross-system/special-character cases, and a mock method.
Alias resolution helpers: equivalent IDs and metadata merging
pkg/api/methods/media_alias.go, pkg/api/methods/media_alias_test.go
New helpers singletonMediaAliasesEnabled, resolveSingletonMediaPath, equivalentMediaIDs, mergeMediaTags, and mergeMediaProperties enable discovery of child/parent alias relationships and deduplicated merging of tags/properties across equivalent media IDs with tests validating behavior and precedence.
Media resolution with singleton fallback
pkg/api/methods/media_resolve.go, pkg/api/methods/media_resolve_test.go, pkg/api/methods/media_batch.go
Resolvers now attempt singleton-descendant fallback when relative-path resolution yields no match; batch resolver assigns fallback errors to relevant request indices. Tests cover success, miss, and disabled-gate cases and make platform Settings deterministic in negative tests.
Browse response: singleton directory annotation
pkg/api/methods/media_browse.go, pkg/api/methods/media_browse_test.go
Directories with exactly one file are enriched via annotateSingletonDirectoryEntry, which resolves the single descendant media and copies media-level fields (IDs, relative path, ZapScript, tags) onto the directory entry when found; tests validate enabled/disabled gate behavior.
Metadata and image handlers: equivalent-ID merging and multi-source selection
pkg/api/methods/media_meta.go, pkg/api/methods/media_image.go, pkg/api/methods/media_meta_test.go, pkg/api/methods/media_image_test.go
media.meta prefetches tags/properties by DBID and uses mergedMediaMeta to merge alias metadata when equivalent IDs exist. media.image builds multiple media-property sources from equivalent IDs and selects images across those sources in preference order while preserving stale-property handling and normalizing paths in errors; tests added for merged selection and mediaId success.
Batch parsing tests
pkg/api/methods/media_batch_test.go
Adds tests validating parseMediaRequest rejects mixing top-level mediaId with batch items, rejects invalid/empty imageTypes in batch items, and rejects top-level imageTypes in batch requests; also tests batch singleton fallback resolution.
Handler-level tests
pkg/api/methods/media_browse_test.go, pkg/api/methods/media_image_test.go, pkg/api/methods/media_meta_test.go, pkg/api/methods/media_resolve_test.go
Adds tests validating annotateSingletonDirectoryEntry, mediaImagePropSources includes singleton alias sources, mergedMediaMeta merges child+parent metadata, and resolve fallback success/miss/disabled behavior.
API documentation: behavior clarifications
docs/api/methods.md, docs/scraper.md
Updated endpoint documentation to describe singleton container resolution behavior and alias fallback for media.browse, media.meta, and media.image when a path resolves to a directory or zip-as-directory.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • ZaparooProject/zaparoo-core#740: Related changes to media.meta/media.image handlers and selection logic; this PR updates those handlers to merge metadata across equivalent/aliased media IDs.
  • ZaparooProject/zaparoo-core#858: Overlapping changes to media image/property selection logic that are related to the multi-source selection adjustments here.
  • ZaparooProject/zaparoo-core#770: Prior batch/media resolution work that this PR extends with singleton ZIP-as-dir alias fallback.

Poem

🐰 I sniffed a folder with just one tune,
child or parent — the same gentle moon.
Tags hop together, merged in a spree,
one path, one story, shared metadata tea.
The rabbit nods: unified, merry, and free.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(media): collapse singleton media containers' accurately captures the main change across the PR, which implements singleton media container resolution and aliasing throughout the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/singleton-media-alias

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

@sentry
Copy link
Copy Markdown

sentry Bot commented May 29, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
pkg/api/methods/media_alias.go (1)

30-132: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add direct tests for the new alias helper behaviors.

This file introduces new resolution/merge logic, but I don’t see helper-focused tests in the provided changes for: nil-guard behavior, parent↔child equivalence cases, and de-dupe precedence in mergeMediaTags/mergeMediaProperties. Please add tests for these paths before merge.

As per coding guidelines "Write tests for all new code — see TESTING.md and pkg/testing/README.md".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/api/methods/media_alias.go` around lines 30 - 132, Add unit tests
covering the new helper behaviors: for equivalentMediaIDs add nil-guard tests
(call equivalentMediaIDs with env == nil, row == nil, env.Database == nil and
env.Database.MediaDB == nil and assert it returns non-error nil slice),
parent↔child equivalence tests (mock/stub MediaDB.FindSingleDescendantMedia and
FindMediaBySystemAndPath so child is returned, onlyChild equals row.DBID for
parentPath and parent is returned; assert returned IDs include parent and child
appropriately), and edge-case where parentPath == "" or parentPath == row.Path
returns only the original id; for mergeMediaTags and mergeMediaProperties add
tests verifying de-duplication and precedence (when duplicates exist ensure
primary items are kept first and aliases do not overwrite them, and that
duplicate Type+Tag or TypeTag entries are removed). Use the functions
equivalentMediaIDs, mergeMediaTags and mergeMediaProperties and mock the MediaDB
methods (FindSingleDescendantMedia, FindMediaBySystemAndPath) to control
scenarios.
🧹 Nitpick comments (3)
pkg/api/methods/media_resolve_test.go (1)

139-140: ⚡ Quick win

Use filepath.Join (with slash normalization) instead of manual "/" concatenation in test paths.

The new setup uses containerPath + "/Game.nes" and containerPath + "/"; convert these to join-based construction for consistency and portability.

As per coding guidelines "Use filepath.Join for path construction everywhere, including test files — never hardcode POSIX-style paths like "/roms/snes/game.sfc" as string literals".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/api/methods/media_resolve_test.go` around lines 139 - 140, Replace manual
string concatenation for paths: construct childPath with
filepath.Join(containerPath, "Game.nes") and set Media.ParentDir by normalizing
the containerPath with filepath.ToSlash(containerPath) and appending a single
"/" (to preserve the trailing-dir semantics). Add the appropriate imports
(path/filepath and the one providing ToSlash if needed) and update the media :=
database.Media{DBID: 20, Path: childPath, ParentDir: ...} initializer to use
these join/normalization helpers instead of containerPath + "/".
pkg/database/mediadb/sql_scraper_test.go (1)

124-133: ⚡ Quick win

Replace slash-concatenated test paths with filepath.Join-based construction.

The new tests build paths via parent+"/..." and parent+"/" in multiple places; switch these to filepath.Join (+ filepath.ToSlash where needed) to keep path handling consistent and portable.

As per coding guidelines "Use filepath.Join for path construction everywhere, including test files — never hardcode POSIX-style paths like "/roms/snes/game.sfc" as string literals".

Also applies to: 146-160

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/database/mediadb/sql_scraper_test.go` around lines 124 - 133, The test
constructs paths by concatenating parent with string literals (e.g.
parent+"/one.nes" and parent+"/") inside the mediaDB.sql.ExecContext call;
replace those concatenations with filepath.Join(parent, "one.nes") and
filepath.Join(parent, "") or filepath.Join(parent, ""); if the SQL needs POSIX
slashes, wrap the joined paths with filepath.ToSlash. Update all similar
occurrences in this file (including the other instances around the 146-160 area)
so every test path uses filepath.Join (and filepath.ToSlash only where the
DB/input expects forward slashes) instead of string concatenation.
pkg/api/methods/media_meta.go (1)

102-117: 🏗️ Heavy lift

Precompute alias metadata once for the whole batch.

The batch loop recomputes equivalentMediaIDs() and then mergedMediaMeta() issues fresh GetMediaTagsByMediaDBIDs / GetMediaPropertyMetadataByMediaDBIDs calls for every aliased row. On alias-heavy batches this falls back to an N+1 query pattern and bypasses the maps already loaded at Lines 58-66. Consider collecting the union of equivalent IDs up front and merging from one prefetched result set inside the loop.

Also applies to: 136-173

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/api/methods/media_meta.go` around lines 102 - 117, The loop currently
calls equivalentMediaIDs() and mergedMediaMeta() per item causing N+1 DB calls
(GetMediaTagsByMediaDBIDs / GetMediaPropertyMetadataByMediaDBIDs) and bypasses
the preloaded maps (mediaTagsByID, mediaPropsByID); instead, before the loop
call: collect the union of all equivalentMediaIDs for the batch (using
equivalentMediaIDs on each Row once), fetch merged tag/property results once
(via the same underlying GetMediaTagsByMediaDBIDs /
GetMediaPropertyMetadataByMediaDBIDs used by mergedMediaMeta), merge those
results into the existing maps (mediaTagsByID, mediaPropsByID), and then inside
the loop use those maps to buildMediaMetaResponse without calling
mergedMediaMeta again (apply same change to the second loop region lines
136-173).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/api/methods/media_browse.go`:
- Around line 659-660: The code calls annotateSingletonDirectoryEntry
per-directory which triggers up to five serial MediaDB lookups for each
singleton directory; instead batch these lookups by first collecting all
singleton directories discovered by BrowseDirectories, issuing a single MediaDB
query to fetch the descendant/asset metadata for all involved SystemIDs at once,
then map results back to each directory and annotate entries in a loop.
Concretely: change the flow around BrowseDirectories to accumulate dir.SystemIDs
for singleton dirs, add a batched lookup function (or extend the existing
MediaDB query) that returns metadata keyed by SystemID, and replace per-entry
calls to annotateSingletonDirectoryEntry with a batched annotator that consumes
the pre-fetched metadata (or modify annotateSingletonDirectoryEntry to accept a
metadata map).
- Around line 651-654: Replace the hardcoded path concat that sets dirPath :=
path + "/" + dir.Name with filepath.Join to correctly build filesystem paths
(use filepath.Join(path, dir.Name)); update the import to include
"path/filepath" if absent and ensure the models.BrowseEntry{ Name: dir.Name,
Path: dirPath } uses the joined path; locate this change around the code that
constructs BrowseEntry objects in media_browse.go (the dirPath variable and the
models.BrowseEntry initialization) to avoid generating incorrect paths like
//child or C://child.

In `@pkg/api/methods/media_image.go`:
- Around line 143-155: The current change uses metadata-only queries
(GetMediaTitlePropertyMetadata and mediaImagePropSources) but
loadMediaImageProperty expects full property fields (prop.Binary, prop.BlobDBID,
prop.Text), causing inline-binary images to be dropped; revert to or replace
these metadata calls with the full-property getters (the counterparts to
GetMediaTitlePropertyMetadata/GetMediaPropertyMetadata used elsewhere) when
building mediaPropSources and titleProps passed into
selectMediaImageFromSources, or implement a new batched full-property query that
returns the complete property rows for the same IDs and use that in
mediaImagePropSources (affecting mediaImagePropSources,
db.GetMediaTitlePropertyMetadata usage, and the selectMediaImageFromSources
invocation so loadMediaImageProperty sees prop.Binary/prop.BlobDBID/prop.Text).

---

Outside diff comments:
In `@pkg/api/methods/media_alias.go`:
- Around line 30-132: Add unit tests covering the new helper behaviors: for
equivalentMediaIDs add nil-guard tests (call equivalentMediaIDs with env == nil,
row == nil, env.Database == nil and env.Database.MediaDB == nil and assert it
returns non-error nil slice), parent↔child equivalence tests (mock/stub
MediaDB.FindSingleDescendantMedia and FindMediaBySystemAndPath so child is
returned, onlyChild equals row.DBID for parentPath and parent is returned;
assert returned IDs include parent and child appropriately), and edge-case where
parentPath == "" or parentPath == row.Path returns only the original id; for
mergeMediaTags and mergeMediaProperties add tests verifying de-duplication and
precedence (when duplicates exist ensure primary items are kept first and
aliases do not overwrite them, and that duplicate Type+Tag or TypeTag entries
are removed). Use the functions equivalentMediaIDs, mergeMediaTags and
mergeMediaProperties and mock the MediaDB methods (FindSingleDescendantMedia,
FindMediaBySystemAndPath) to control scenarios.

---

Nitpick comments:
In `@pkg/api/methods/media_meta.go`:
- Around line 102-117: The loop currently calls equivalentMediaIDs() and
mergedMediaMeta() per item causing N+1 DB calls (GetMediaTagsByMediaDBIDs /
GetMediaPropertyMetadataByMediaDBIDs) and bypasses the preloaded maps
(mediaTagsByID, mediaPropsByID); instead, before the loop call: collect the
union of all equivalentMediaIDs for the batch (using equivalentMediaIDs on each
Row once), fetch merged tag/property results once (via the same underlying
GetMediaTagsByMediaDBIDs / GetMediaPropertyMetadataByMediaDBIDs used by
mergedMediaMeta), merge those results into the existing maps (mediaTagsByID,
mediaPropsByID), and then inside the loop use those maps to
buildMediaMetaResponse without calling mergedMediaMeta again (apply same change
to the second loop region lines 136-173).

In `@pkg/api/methods/media_resolve_test.go`:
- Around line 139-140: Replace manual string concatenation for paths: construct
childPath with filepath.Join(containerPath, "Game.nes") and set Media.ParentDir
by normalizing the containerPath with filepath.ToSlash(containerPath) and
appending a single "/" (to preserve the trailing-dir semantics). Add the
appropriate imports (path/filepath and the one providing ToSlash if needed) and
update the media := database.Media{DBID: 20, Path: childPath, ParentDir: ...}
initializer to use these join/normalization helpers instead of containerPath +
"/".

In `@pkg/database/mediadb/sql_scraper_test.go`:
- Around line 124-133: The test constructs paths by concatenating parent with
string literals (e.g. parent+"/one.nes" and parent+"/") inside the
mediaDB.sql.ExecContext call; replace those concatenations with
filepath.Join(parent, "one.nes") and filepath.Join(parent, "") or
filepath.Join(parent, ""); if the SQL needs POSIX slashes, wrap the joined paths
with filepath.ToSlash. Update all similar occurrences in this file (including
the other instances around the 146-160 area) so every test path uses
filepath.Join (and filepath.ToSlash only where the DB/input expects forward
slashes) instead of string concatenation.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 08d569c9-f6f8-4457-b63a-4ece4c8d0a97

📥 Commits

Reviewing files that changed from the base of the PR and between 0ddd02e and bc2d763.

📒 Files selected for processing (13)
  • docs/api/methods.md
  • docs/scraper.md
  • pkg/api/methods/media_alias.go
  • pkg/api/methods/media_batch.go
  • pkg/api/methods/media_browse.go
  • pkg/api/methods/media_image.go
  • pkg/api/methods/media_meta.go
  • pkg/api/methods/media_resolve.go
  • pkg/api/methods/media_resolve_test.go
  • pkg/database/database.go
  • pkg/database/mediadb/sql_scraper.go
  • pkg/database/mediadb/sql_scraper_test.go
  • pkg/testing/helpers/db_mocks.go

Comment thread pkg/api/methods/media_browse.go Outdated
Comment on lines +659 to +660
if dir.FileCount == 1 {
annotateSingletonDirectoryEntry(env, &entry, dirPath, dir.SystemIDs, rootDirs)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Batch singleton-directory metadata lookups instead of querying per entry.

Lines 659-660 call a helper that does up to five serial MediaDB lookups per singleton directory. Because BrowseDirectories is not capped by maxResults, a directory-heavy first page can turn one browse request into dozens of SQLite round-trips and noticeably worse latency. Please batch this enrichment or extend the browse query to return the descendant metadata in one pass.

Also applies to: 742-781

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/api/methods/media_browse.go` around lines 659 - 660, The code calls
annotateSingletonDirectoryEntry per-directory which triggers up to five serial
MediaDB lookups for each singleton directory; instead batch these lookups by
first collecting all singleton directories discovered by BrowseDirectories,
issuing a single MediaDB query to fetch the descendant/asset metadata for all
involved SystemIDs at once, then map results back to each directory and annotate
entries in a loop. Concretely: change the flow around BrowseDirectories to
accumulate dir.SystemIDs for singleton dirs, add a batched lookup function (or
extend the existing MediaDB query) that returns metadata keyed by SystemID, and
replace per-entry calls to annotateSingletonDirectoryEntry with a batched
annotator that consumes the pre-fetched metadata (or modify
annotateSingletonDirectoryEntry to accept a metadata map).

Comment thread pkg/api/methods/media_image.go
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (2)
pkg/api/methods/media_alias.go (1)

50-142: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add direct tests for alias ID and merge semantics.

This new helper logic adds parent/child alias branching plus tag/property dedup rules, but the supplied tests only exercise resolver fallback. Please add table-driven coverage for equivalentMediaIDs, mergeMediaTags, and mergeMediaProperties so these behaviors are locked in before merge.

As per coding guidelines, **/*.go: Write tests for all new code — see TESTING.md and pkg/testing/README.md

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/api/methods/media_alias.go` around lines 50 - 142, Add table-driven unit
tests for equivalentMediaIDs, mergeMediaTags, and mergeMediaProperties: write
tests covering nil inputs, singleton aliases disabled, child-only, parent-child
branching, and deduplication semantics. For equivalentMediaIDs, create a
RequestEnv with a fake MediaDB that implements FindSingleDescendantMedia and
FindMediaBySystemAndPath to return controlled media rows (including nils) and
assert returned ID slices for DBID ordering and uniqueness; also include cases
where singletonMediaAliasesEnabled(env) is false. For mergeMediaTags, add cases
that check duplicate suppression using the key tag.Type + "\x00" + tag.Tag and
order preservation (primary first, then aliases); for mergeMediaProperties, add
cases asserting uniqueness by prop.TypeTag and order behavior. Use table-driven
tests in a _test.go file with clear test names and assertions.
pkg/api/methods/media_resolve_test.go (1)

147-159: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Assert the platform mock expectations (pl.AssertExpectations(t)).

These tests stub pl.On("Settings", ...) but never call pl.AssertExpectations(t), so they won’t fail if resolveMediaBySystemAndPath stops consulting platform settings.

Suggested fix
 	assert.Equal(t, childPath, result.Path)
 	mockDB.AssertExpectations(t)
+	pl.AssertExpectations(t)
@@
 	assert.Contains(t, err.Error(), "media not found")
 	mockDB.AssertExpectations(t)
+	pl.AssertExpectations(t)
@@
 	assert.Contains(t, err.Error(), "media not found")
 	mockDB.AssertNotCalled(t, "FindSingleDescendantMedia", mock.Anything, system.DBID, containerPath)
 	mockDB.AssertExpectations(t)
+	pl.AssertExpectations(t)
@@
 	assert.Contains(t, err.Error(), "media not found")
 	mockDB.AssertExpectations(t)
 	pl.AssertNotCalled(t, "RootDirs", mock.Anything)
+	pl.AssertExpectations(t)
@@
 	assert.Contains(t, err.Error(), "media not found")
 	mockDB.AssertExpectations(t)
 	pl.AssertNotCalled(t, "RootDirs", mock.Anything)
+	pl.AssertExpectations(t)
@@
 	assert.Contains(t, err.Error(), "media not found")
 	mockDB.AssertExpectations(t)
 	pl.AssertNotCalled(t, "RootDirs", mock.Anything)
+	pl.AssertExpectations(t)

Also applies to: 171-182, 194-204, 219-229, 241-251, 266-276

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/api/methods/media_resolve_test.go` around lines 147 - 159, The platform
mock expectations are never asserted in these tests, so add
pl.AssertExpectations(t) after mockDB.AssertExpectations(t) (or immediately
after the test call) in each test that sets pl.On("Settings"...), e.g., in the
test exercising resolveMediaBySystemAndPath ensure you call
pl.AssertExpectations(t) to verify resolveMediaBySystemAndPath consulted
platform settings; apply the same addition to the other similar test blocks
referenced (the tests around the other ranges that stub pl.Settings).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@pkg/api/methods/media_alias.go`:
- Around line 50-142: Add table-driven unit tests for equivalentMediaIDs,
mergeMediaTags, and mergeMediaProperties: write tests covering nil inputs,
singleton aliases disabled, child-only, parent-child branching, and
deduplication semantics. For equivalentMediaIDs, create a RequestEnv with a fake
MediaDB that implements FindSingleDescendantMedia and FindMediaBySystemAndPath
to return controlled media rows (including nils) and assert returned ID slices
for DBID ordering and uniqueness; also include cases where
singletonMediaAliasesEnabled(env) is false. For mergeMediaTags, add cases that
check duplicate suppression using the key tag.Type + "\x00" + tag.Tag and order
preservation (primary first, then aliases); for mergeMediaProperties, add cases
asserting uniqueness by prop.TypeTag and order behavior. Use table-driven tests
in a _test.go file with clear test names and assertions.

In `@pkg/api/methods/media_resolve_test.go`:
- Around line 147-159: The platform mock expectations are never asserted in
these tests, so add pl.AssertExpectations(t) after mockDB.AssertExpectations(t)
(or immediately after the test call) in each test that sets
pl.On("Settings"...), e.g., in the test exercising resolveMediaBySystemAndPath
ensure you call pl.AssertExpectations(t) to verify resolveMediaBySystemAndPath
consulted platform settings; apply the same addition to the other similar test
blocks referenced (the tests around the other ranges that stub pl.Settings).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 95cfd430-2f2e-466a-8711-6610ba3ca7a9

📥 Commits

Reviewing files that changed from the base of the PR and between bc2d763 and d4eb3b0.

📒 Files selected for processing (5)
  • docs/api/methods.md
  • docs/scraper.md
  • pkg/api/methods/media_alias.go
  • pkg/api/methods/media_browse.go
  • pkg/api/methods/media_resolve_test.go
✅ Files skipped from review due to trivial changes (2)
  • docs/scraper.md
  • docs/api/methods.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/api/methods/media_browse.go

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/api/methods/media_alias_test.go`:
- Around line 68-69: Update the test literals in
pkg/api/methods/media_alias_test.go to build paths with filepath.Join instead of
hardcoded slash strings: replace occurrences used for Path (e.g.,
"roms/Game.zip/Game.nes"), ParentDir (e.g., "roms/Game.zip/"), and archive base
("roms/Game.zip") with filepath.Join calls so path separators are correct on all
platforms; for ParentDir preserve the trailing-separator semantics by appending
os.PathSeparator (or using filepath.Join and adding string(os.PathSeparator)) as
needed when constructing the ParentDir field; update all mentioned instances at
the Path/ParentDir assignments and other comparisons so they use the new
filepath.Join-based values.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8c99cd40-2b38-4cfd-a4d1-f11191e4719a

📥 Commits

Reviewing files that changed from the base of the PR and between d4eb3b0 and b4ff54f.

📒 Files selected for processing (5)
  • pkg/api/methods/media_alias_test.go
  • pkg/api/methods/media_browse.go
  • pkg/api/methods/media_image.go
  • pkg/api/methods/media_resolve_test.go
  • pkg/database/mediadb/sql_scraper_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • pkg/api/methods/media_image.go
  • pkg/database/mediadb/sql_scraper_test.go
  • pkg/api/methods/media_browse.go
  • pkg/api/methods/media_resolve_test.go

Comment on lines +68 to +69
Path: "roms/Game.zip/Game.nes",
ParentDir: "roms/Game.zip/",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify slash-delimited hardcoded paths in Go tests under methods.
rg -nP --type=go '"[^"]*/[^"]*"' pkg/api/methods/media_alias_test.go

Repository: ZaparooProject/zaparoo-core

Length of output: 1022


Use filepath.Join for test path literals in pkg/api/methods/media_alias_test.go
Replace the hardcoded slash-delimited strings (e.g., "roms/Game.zip/Game.nes", "roms/Game.zip/", "roms/Game.zip") at lines 68-69, 73, 77-79, 107, and 111 with filepath.Join-constructed paths, keeping ParentDir’s trailing-separator semantics consistent with the existing expectations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/api/methods/media_alias_test.go` around lines 68 - 69, Update the test
literals in pkg/api/methods/media_alias_test.go to build paths with
filepath.Join instead of hardcoded slash strings: replace occurrences used for
Path (e.g., "roms/Game.zip/Game.nes"), ParentDir (e.g., "roms/Game.zip/"), and
archive base ("roms/Game.zip") with filepath.Join calls so path separators are
correct on all platforms; for ParentDir preserve the trailing-separator
semantics by appending os.PathSeparator (or using filepath.Join and adding
string(os.PathSeparator)) as needed when constructing the ParentDir field;
update all mentioned instances at the Path/ParentDir assignments and other
comparisons so they use the new filepath.Join-based values.

@wizzomafizzo wizzomafizzo merged commit 61ca5e1 into main May 29, 2026
16 of 18 checks passed
@wizzomafizzo wizzomafizzo deleted the feat/singleton-media-alias branch May 29, 2026 08:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant