Skip to content

fix(paths): harden imports, moves, and root relocation#717

Open
therobbiedavis wants to merge 372 commits into
canaryfrom
bugfix/unix-folder-name-space
Open

fix(paths): harden imports, moves, and root relocation#717
therobbiedavis wants to merge 372 commits into
canaryfrom
bugfix/unix-folder-name-space

Conversation

@therobbiedavis

@therobbiedavis therobbiedavis commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR hardens Listenarr's filesystem path handling across download imports, manual/library destinations, durable audiobook moves, root-folder relocation, metadata-only path repair, scanning, and frontend destination editing.

The main rule this branch enforces is:

  • Download-client-reported source paths are external filesystem identities and must preserve legal whitespace, case, and client-reported shape.
  • Listenarr-owned destination paths are user input and must be validated against explicit filesystem boundaries before any filesystem mutation or metadata rewrite.

It also replaces fragile BasePath pre-save behavior with durable move orchestration, adds root-folder relocation workflows, restores EF migration discoverability/schema coverage from PR #727, and adds guardrails so future path/migration regressions fail tests instead of silently shipping.

Added

  • Filesystem path identity helpers for syntax-aware, case-sensitive, and case-insensitive equality/containment checks.
  • Filesystem semantics resolution for configured roots, output paths, download mappings, scan paths, move source/target paths, and metadata rewrites.
  • Strict user-provided path validation for root folders, library destinations, manual imports, move targets, and frontend path editing.
  • Durable audiobook content moves with persisted source paths, manifests, recovery markers, lease fencing, retry state, phase tracking, source cleanup safeguards, and terminal/attention statuses.
  • Root-folder relocation APIs, frontend flow, active-boundary protection, retry support, metadata-only relocation handling, skipped rewrite tracking, and historical relocation preservation.
  • A shared filesystem mutation coordinator to serialize high-risk move, relocation, retry, and metadata-rewrite operations.
  • Centralized metadata-only destination rewrite service used by both the move endpoint and legacy PUT /library/{id} compatibility path.
  • Background move-job status tracking store on the frontend so queued move updates survive modal close.
  • SQLite migration/schema tests that migrate a real database and verify the resulting schema matches the EF model.
  • Architecture/guardrail tests for path semantics, migration metadata, hosted workers, and backend layering.

Changed

  • Preserves exact download-client source paths for torrent import, remote-path mapping, queue translation, and case-distinct qBittorrent source files.
  • Separates source-path identity handling from user-destination validation instead of routing both through the same normalization rules.
  • Queues physical library moves instead of pre-saving BasePath, so metadata is updated only after move completion or through explicit metadata-only rewrite paths.
  • Allows custom physical move destinations outside configured roots when the nearest existing parent is valid, while keeping metadata-only moves restricted to configured root/output boundaries.
  • Routes legacy PUT /library/{id} BasePath changes through the same destination rewrite service and prevents stale full-object payloads from undoing rewritten path fields.
  • Builds sparse frontend update payloads so destination changes do not resend unchanged basePath, imageUrl, or file-path fields.
  • Applies filesystem-aware comparisons for same-path checks, overlapping roots, active move conflicts, root-folder reassignment/deletion guards, scan dedupe, unmatched filtering, and rename/organize flows.
  • Uses explicit root-folder case-sensitivity settings ahead of legacy output-path auto behavior when both point to the same boundary.
  • Updates frontend root-folder and audiobook destination editing to use the new validation, relocation, and move queue workflows.
  • Keeps retry/requeue state lease-aware so stale workers cannot mutate attempts, overwrite terminal state, or broadcast stale failures.
  • Makes move queue legacy identity reconciliation tolerant of malformed active jobs by marking only the bad job NeedsAttention and continuing recovery.

Fixed

  • Imports from torrent/download folders with legal leading or trailing whitespace on Unix-like filesystems.
  • Torrent child path escape risks from rooted-looking or parent-segment client paths.
  • Invalid custom add-to-library and move destinations silently falling back to generated paths.
  • Absolute library move destinations bypassing configured root/output boundaries.
  • Physical custom move destinations being incorrectly rejected by metadata-only configured-root validation.
  • Case-only library moves being blocked on roots configured as case-sensitive.
  • Move retry failures caused by stale temp directories, stale leases, or incomplete durable move state.
  • Nested target-inside-source cleanup/recursive copy hazards so unrelated sibling content is not removed or merged.
  • Root-folder relocation and standalone move conflicts racing each other or crossing active boundaries.
  • Root-folder deletion/reassignment returning misleading raw failures instead of conflict responses.
  • Metadata-only path repair requiring source filesystem access or deleting tracked files when BasePath is unavailable.
  • Saved unmatched scan results showing already-tracked files because equivalent paths used different slash/canonical forms.
  • Same-author scans attributing sibling books and collapsing an audiobook's BasePath to the author folder.
  • Durable move UI status mismatch (Processing vs Running/NeedsAttention/Superseded) and modal-local subscriptions being torn down before background updates arrive.
  • EF migration drift by restoring the AddMoveJobSourcePath designer, adding the missing ProcessExecutionLogs migration, regenerating durable move migrations via EF tooling, and adding schema coverage.

Removed / replaced

  • Removed the fragile BasePath pre-save pattern for physical moves.
  • Removed modal-owned long-lived move-job SignalR handling in favor of an app-level move-job store.
  • Removed/debug-cleaned the temporary frontend debug test placeholder.
  • Removed additional host-default path comparison fallbacks from user-library path identity sites, replacing them with explicit resolved semantics.
  • Removed the need for runtime schema repair around missing move-job columns by making migrations discoverable and schema-tested.

Testing

Validated locally on the final branch state with:

  • cd fe && npx vitest run EditAudiobookModal.moveOptions.spec.ts moveJobs.store.spec.ts --reporter=dot --silent=true
  • cd fe && npm run test:unit
  • cd fe && npm run build
  • dotnet test tests/Listenarr.Tests.csproj --no-restore --verbosity minimal --filter "FullyQualifiedName~EfMoveQueuePersistenceTests|FullyQualifiedName~MoveQueueServiceTests|FullyQualifiedName~LibraryController_MoveTests" -p:OutDir=C:/Users/local_oqoz17v/AppData/Local/Temp/listenarr-test-out/
  • dotnet test tests/Listenarr.Tests.csproj --no-build --verbosity minimal --filter "FullyQualifiedName~BackendArchitectureTests|FullyQualifiedName~HostedServicesRegistrationTests.BackgroundWorkerOwnership_DocumentsEveryHostedService"
  • cd fe && npx prettier --check src/__tests__/EditAudiobookModal.moveOptions.spec.ts src/__tests__/moveJobs.store.spec.ts src/components/domain/audiobook/EditAudiobookModal.vue src/stores/moveJobs.ts
  • git diff --check origin/canary...HEAD

Also previously validated the broader backend suite during review; the only temp-output failures were repo-root fixture/doc lookup tests, which passed when rerun from the normal repository output.

Issues and related PRs

Notes

  • Future EF migrations should be generated with dotnet ef migrations add; do not hand-author or hand-split migration files.
  • Future EF migrations must use IDs later than 20260702200000.
  • Download-client source paths remain external filesystem identities and are not routed through user destination validation.
  • Metadata-only moves remain restricted to configured root/output boundaries; physical moves may use a validated custom destination.
  • Source-inside-target moves into a parent containing unrelated sibling content remain intentionally blocked to avoid merging an audiobook into a non-empty unrelated library folder.
  • Docker compatibility follows the Linux container path model, so host paths should still be mounted and referenced as container paths such as /books or /downloads.

@therobbiedavis therobbiedavis added the patch patch version bump - backward compatible bug fixes label Jul 1, 2026
@therobbiedavis
therobbiedavis marked this pull request as ready for review July 1, 2026 21:04
@therobbiedavis
therobbiedavis requested review from a team and Copilot July 1, 2026 21:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes download/import path handling so client-reported torrent paths preserve meaningful leading/trailing whitespace (important on Unix-like filesystems), while adding stricter, OS-aware validation for Listenarr-owned destination paths (root folders, add-to-library, and move operations).

Changes:

  • Preserve whitespace in torrent client path mapping and remote path translation by avoiding whitespace-trimming/whitespace-only rejection in key path plumbing.
  • Add OS-aware normalization/validation for user-provided destination directories, including explicit support for filesystem roots in root-folder configuration and blocking parent traversal for concrete destinations.
  • Add regression tests covering whitespace-bearing torrent folders (Transmission/qBittorrent), root-folder root paths, and invalid destination rejection in library add/move workflows.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/Mocks/Api/TransmissionApiMock.cs Adds a canned Transmission torrent response with whitespace-bearing folder names for regression coverage.
tests/Features/Infrastructure/DownloadClients/Common/TorrentClientPathMapperTests.cs New tests asserting whitespace preservation and rooted-child handling for torrent file mapping.
tests/Features/Infrastructure/DownloadClients/Common/DownloadClientAdapterTests.cs Adds Transmission adapter regression asserting whitespace-bearing ContentPath/SourceFiles are preserved.
tests/Features/Domain/Utils/FileUtilsTests.cs Adds tests for whitespace-preserving combining/normalization and new destination-path validation rules.
tests/Features/Application/Downloads/Common/DownloadClientGatewayTests.cs Adds tests ensuring remote path mapping and directory expansion keep whitespace-bearing paths intact.
tests/Features/Application/Audiobooks/RootFolders/RootFolderServiceTests.cs Adds coverage for allowing filesystem roots and validating/normalizing root folder paths.
tests/Features/Api/Features/Library/LibraryController_MoveTests.cs Adds API test ensuring invalid move destinations are rejected with 400.
tests/Features/Api/Features/Library/LibraryController_AddToLibraryTests.cs Updates custom path test to be OS-safe and adds parent-traversal/invalid-path rejection tests.
listenarr.infrastructure/DownloadClients/Transmission/TransmissionImportPathResolver.cs Stops treating whitespace-bearing strings as “empty” when building content/source paths.
listenarr.infrastructure/DownloadClients/Qbittorrent/QbittorrentImportPathResolver.cs Same as above for qBittorrent source file translation.
listenarr.infrastructure/DownloadClients/Common/TorrentClientPathMapper.cs Reworks torrent path mapping to avoid trimming path-segment whitespace and better handle rooted-looking child paths.
listenarr.infrastructure/Configuration/Paths/RemotePathMappingService.cs Adjusts empty-path detection to avoid rejecting whitespace-bearing remote paths.
listenarr.domain/Common/FileUtils.UserProvidedPaths.cs New OS-aware user-provided directory path validation/normalization helper with root/traversal options.
listenarr.domain/Common/FileUtils.PathCombining.cs Documents CombineWithOptionalBase’s whitespace-preserving intent and relaxes base-path emptiness check.
listenarr.application/Downloads/Common/DownloadClientGateway.cs Preserves whitespace-bearing paths during mapping/translation and directory-expansion flows.
listenarr.application/Audiobooks/RootFolders/RootFolderService.cs Root folder create/update now uses OS-aware normalization and allows filesystem root paths.
listenarr.application/Audiobooks/Contracts/ILibraryAddService.cs Extends result contract to surface validation failures cleanly to API workflows.
listenarr.application/Audiobooks/Catalog/LibraryAddService.cs Validates custom/generated destination paths with OS-aware normalization and blocks parent traversal.
listenarr.api/Features/Library/LibraryMoveWorkflow.cs Validates and normalizes move destinations (blocking traversal) instead of silently normalizing invalid targets.
listenarr.api/Features/Library/LibraryAddWorkflow.cs Returns 400 for validation failures and validates custom destination paths via OS-aware normalization.

Comment thread listenarr.infrastructure/DownloadClients/Common/TorrentClientPathMapper.cs Outdated
Comment thread tests/Features/Application/Downloads/Common/DownloadClientGatewayTests.cs Outdated
Comment thread tests/Features/Application/Downloads/Common/DownloadClientGatewayTests.cs Outdated
Comment thread fe/src/utils/path.ts Fixed
Comment thread fe/src/utils/path.ts Fixed
Comment thread listenarr.infrastructure/Library/Moving/MoveBackgroundService.cs Fixed
Comment thread listenarr.infrastructure/FileSystem/FileSystemSemanticsResolver.cs Fixed
Comment thread listenarr.infrastructure/Library/Moving/MoveBackgroundService.cs Fixed
@therobbiedavis therobbiedavis changed the title fix(downloads): preserve client path whitespace fix(paths): harden imports and library moves Jul 8, 2026
Comment thread listenarr.infrastructure/FileSystem/FileSystemSemanticsResolver.cs Fixed
@therobbiedavis therobbiedavis changed the title fix(paths): harden imports and library moves fix(paths): harden imports, moves, and root relocation Jul 9, 2026
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Temporary formatter/compiler diagnostic superseded and cleaned up.

3 similar comments
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Temporary formatter/compiler diagnostic superseded and cleaned up.

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Temporary formatter/compiler diagnostic superseded and cleaned up.

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Temporary formatter/compiler diagnostic superseded and cleaned up.

Comment thread listenarr.infrastructure/FileSystem/FileSystemSafety.cs Fixed
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Temporary verbose Windows test diagnostic superseded by compact output.

@github-actions

Copy link
Copy Markdown
Contributor

Temporary Windows test diagnostics

Exit code: 1

FAILED: Listenarr.Tests.Features.Application.Audiobooks.RootFolders.RootFolderServiceTests.Delete_ReassignsFilesystemRootChildAudiobookPreservingRelativePath
System.InvalidOperationException : An error was generated for warning 'Microsoft.EntityFrameworkCore.Database.Transaction.TransactionIgnoredWarning': Transactions are not supported by the in-memory store. See https://go.microsoft.com/fwlink/?LinkId=800142 This exception can be suppressed or logged by passing event ID 'InMemoryEventId.TransactionIgnoredWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.
at Microsoft.EntityFrameworkCore.Diagnostics.EventDefinition.Log[TLoggerCategory](IDiagnosticsLogger`1 logger, Exception exception)
   at Microsoft.EntityFrameworkCore.InMemory.Internal.InMemoryLoggerExtensions.TransactionIgnoredWarning(IDiagnosticsLogger`1 diagnostics)
   at Microsoft.EntityFrameworkCore.InMemory.Storage.Internal.InMemoryTransactionManager.BeginTransactionAsync(CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.BeginTransactionAsync(CancellationToken cancellationToken)
   at Listenarr.Infrastructure.Persistence.Repositories.EfRootFolderRepository.ReassignAudiobooksAndRemoveAsync(Int32 sourceRootId, Int32 targetRootId, FileSystemPathSemantics sourceSemantics, FileSystemPathSemantics targetSemantics, CancellationToken ct) in D:\a\Listenarr\Listenarr\listenarr.infrastructure\Persistence\Repositories\EfRootFolderRepository.cs:line 190
   at Listenarr.Infrastructure.Persistence.Repositories.EfRootFolderRepository.ReassignAudiobooksAndRemoveAsync(Int32 sourceRootId, Int32 targetRootId, FileSystemPathSemantics sourceSemantics, FileSystemPathSemantics targetSemantics, CancellationToken ct) in D:\a\Listenarr\Listenarr\listenarr.infrastructure\Persistence\Repositories\EfRootFolderRepository.cs:line 251
   at Listenarr.Application.Audiobooks.RootFolders.RootFolderService.DeleteCoreAsync(Int32 id, Nullable`1 reassignRootId) in D:\a\Listenarr\Listenarr\listenarr.application\Audiobooks\RootFolders\RootFolderService.cs:line 115
   at Listenarr.Application.Common.FilesystemMutationCoordinator.ExecuteExclusiveAsync(Func`2 operation, CancellationToken cancellationToken) in D:\a\Listenarr\Listenarr\listenarr.application\Common\FilesystemMutationCoordinator.cs:line 20
   at Listenarr.Tests.Features.Application.Audiobooks.RootFolders.RootFolderServiceTests.Delete_ReassignsFilesystemRootChildAudiobookPreservingRelativePath() in D:\a\Listenarr\Listenarr\tests\Features\Application\Audiobooks\RootFolders\RootFolderServiceTests.cs:line 560
--- End of stack trace from previous location ---

FAILED: Listenarr.Tests.Features.Infrastructure.Library.Moving.AudiobookContentMoveServiceTests.MoveContentsAsync_DirectCopyMarkerWithoutManifest_BlocksRecovery
Assert.Throws() Failure: No exception was thrown
Expected: typeof(Listenarr.Infrastructure.Library.Moving.MoveNeedsAttentionException)
at Listenarr.Tests.Features.Infrastructure.Library.Moving.AudiobookContentMoveServiceTests.MoveContentsAsync_DirectCopyMarkerWithoutManifest_BlocksRecovery() in D:\a\Listenarr\Listenarr\tests\Features\Infrastructure\Library\Moving\AudiobookContentMoveServiceTests.cs:line 247
--- End of stack trace from previous location ---

FAILED: Listenarr.Tests.Features.Infrastructure.Library.Moving.AudiobookContentMoveServiceTests.MoveContentsAsync_TargetContainsOnlySourceSubtree_AllowsMove
Listenarr.Infrastructure.Library.Moving.MoveNeedsAttentionException : Destination contains an unowned directory: nested
at Listenarr.Infrastructure.Library.Moving.AudiobookContentMoveService.ValidateExistingDestinationContents(String source, String destinationRoot, IReadOnlyCollection`1 manifest, Guid jobId, FileSystemPathSemantics targetSemantics) in D:\a\Listenarr\Listenarr\listenarr.infrastructure\Library\Moving\AudiobookContentMoveService.Copy.cs:line 128
   at Listenarr.Infrastructure.Library.Moving.AudiobookContentMoveService.CopySourceContentsAsync(String source, String copyDestination, IReadOnlyList`1 manifest, Guid jobId, FileSystemPathSemantics sourceSemantics, FileSystemPathSemantics targetSemantics, CancellationToken cancellationToken) in D:\a\Listenarr\Listenarr\listenarr.infrastructure\Library\Moving\AudiobookContentMoveService.Copy.cs:line 17
   at Listenarr.Infrastructure.Library.Moving.AudiobookContentMoveService.MoveContentsAsync(AudiobookContentMoveRequest request, CancellationToken cancellationToken) in D:\a\Listenarr\Listenarr\listenarr.infrastructure\Library\Moving\AudiobookContentMoveService.cs:line 169
   at Listenarr.Tests.Features.Infrastructure.Library.Moving.AudiobookContentMoveServiceTests.MoveContentsAsync_TargetContainsOnlySourceSubtree_AllowsMove() in D:\a\Listenarr\Listenarr\tests\Features\Infrastructure\Library\Moving\AudiobookContentMoveServiceTests.cs:line 583
--- End of stack trace from previous location ---

FAILED: Listenarr.Tests.Features.Infrastructure.Persistence.RootFolderReassignmentTransactionTests.ReassignAudiobooksAndRemoveAsync_RewritesAllReferencesAndDeletesRoot
System.InvalidOperationException : An error was generated for warning 'Microsoft.EntityFrameworkCore.Database.Transaction.TransactionIgnoredWarning': Transactions are not supported by the in-memory store. See https://go.microsoft.com/fwlink/?LinkId=800142 This exception can be suppressed or logged by passing event ID 'InMemoryEventId.TransactionIgnoredWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.
at Microsoft.EntityFrameworkCore.Diagnostics.EventDefinition.Log[TLoggerCategory](IDiagnosticsLogger`1 logger, Exception exception)
   at Microsoft.EntityFrameworkCore.InMemory.Internal.InMemoryLoggerExtensions.TransactionIgnoredWarning(IDiagnosticsLogger`1 diagnostics)
   at Microsoft.EntityFrameworkCore.InMemory.Storage.Internal.InMemoryTransactionManager.BeginTransactionAsync(CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.BeginTransactionAsync(CancellationToken cancellationToken)
   at Listenarr.Infrastructure.Persistence.Repositories.EfRootFolderRepository.ReassignAudiobooksAndRemoveAsync(Int32 sourceRootId, Int32 targetRootId, FileSystemPathSemantics sourceSemantics, FileSystemPathSemantics targetSemantics, CancellationToken ct) in D:\a\Listenarr\Listenarr\listenarr.infrastructure\Persistence\Repositories\EfRootFolderRepository.cs:line 190
   at Listenarr.Infrastructure.Persistence.Repositories.EfRootFolderRepository.ReassignAudiobooksAndRemoveAsync(Int32 sourceRootId, Int32 targetRootId, FileSystemPathSemantics sourceSemantics, FileSystemPathSemantics targetSemantics, CancellationToken ct) in D:\a\Listenarr\Listenarr\listenarr.infrastructure\Persistence\Repositories\EfRootFolderRepository.cs:line 251
   at Listenarr.Tests.Features.Infrastructure.Persistence.RootFolderReassignmentTransactionTests.ReassignAudiobooksAndRemoveAsync_RewritesAllReferencesAndDeletesRoot() in D:\a\Listenarr\Listenarr\tests\Features\Infrastructure\Persistence\RootFolderReassignmentTransactionTests.cs:line 43
--- End of stack trace from previous location ---

FAILED: Listenarr.Tests.Features.Infrastructure.Persistence.RootFolderReassignmentTransactionTests.ReassignAudiobooksAndRemoveAsync_DeleteConflictRollsBackPathRewrites
Assert.Throws() Failure: Exception type was not an exact match
Expected: typeof(Microsoft.EntityFrameworkCore.DbUpdateException)
Actual:   typeof(System.InvalidOperationException)
---- System.InvalidOperationException : An error was generated for warning 'Microsoft.EntityFrameworkCore.Database.Transaction.TransactionIgnoredWarning': Transactions are not supported by the in-memory store. See https://go.microsoft.com/fwlink/?LinkId=800142 This exception can be suppressed or logged by passing event ID 'InMemoryEventId.TransactionIgnoredWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.
at Listenarr.Tests.Features.Infrastructure.Persistence.RootFolderReassignmentTransactionTests.ReassignAudiobooksAndRemoveAsync_DeleteConflictRollsBackPathRewrites() in D:\a\Listenarr\Listenarr\tests\Features\Infrastructure\Persistence\RootFolderReassignmentTransactionTests.cs:line 95
--- End of stack trace from previous location ---
----- Inner Stack Trace -----
   at Microsoft.EntityFrameworkCore.Diagnostics.EventDefinition.Log[TLoggerCategory](IDiagnosticsLogger`1 logger, Exception exception)
   at Microsoft.EntityFrameworkCore.InMemory.Internal.InMemoryLoggerExtensions.TransactionIgnoredWarning(IDiagnosticsLogger`1 diagnostics)
   at Microsoft.EntityFrameworkCore.InMemory.Storage.Internal.InMemoryTransactionManager.BeginTransactionAsync(CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.BeginTransactionAsync(CancellationToken cancellationToken)
   at Listenarr.Infrastructure.Persistence.Repositories.EfRootFolderRepository.ReassignAudiobooksAndRemoveAsync(Int32 sourceRootId, Int32 targetRootId, FileSystemPathSemantics sourceSemantics, FileSystemPathSemantics targetSemantics, CancellationToken ct) in D:\a\Listenarr\Listenarr\listenarr.infrastructure\Persistence\Repositories\EfRootFolderRepository.cs:line 190
   at Listenarr.Infrastructure.Persistence.Repositories.EfRootFolderRepository.ReassignAudiobooksAndRemoveAsync(Int32 sourceRootId, Int32 targetRootId, FileSystemPathSemantics sourceSemantics, FileSystemPathSemantics targetSemantics, CancellationToken ct) in D:\a\Listenarr\Listenarr\listenarr.infrastructure\Persistence\Repositories\EfRootFolderRepository.cs:line 251

@github-actions

Copy link
Copy Markdown
Contributor

Temporary Windows test diagnostics

Exit code: 1

FAILED: Listenarr.Tests.Features.Infrastructure.Library.Moving.AudiobookContentMoveServiceTests.MoveContentsAsync_DirectCopyMarkerWithoutManifest_BlocksRecovery
Assert.Throws() Failure: No exception was thrown
Expected: typeof(Listenarr.Infrastructure.Library.Moving.MoveNeedsAttentionException)
at Listenarr.Tests.Features.Infrastructure.Library.Moving.AudiobookContentMoveServiceTests.MoveContentsAsync_DirectCopyMarkerWithoutManifest_BlocksRecovery() in D:\a\Listenarr\Listenarr\tests\Features\Infrastructure\Library\Moving\AudiobookContentMoveServiceTests.cs:line 247
--- End of stack trace from previous location ---

FAILED: Listenarr.Tests.Features.Infrastructure.Persistence.RootFolderReassignmentTransactionTests.ReassignAudiobooksAndRemoveAsync_RewritesAllReferencesAndDeletesRoot
Assert.Equal() Failure: Strings differ
                                     ↓ (pos 91)
Expected: ···"547ad4a\\root-reassign-target\\Author\\Title"
Actual:   ···"547ad4a\\root-reassign-source\\Author\\Title"
                                     ↑ (pos 91)
at Listenarr.Tests.Features.Infrastructure.Persistence.RootFolderReassignmentTransactionTests.ReassignAudiobooksAndRemoveAsync_RewritesAllReferencesAndDeletesRoot() in D:\a\Listenarr\Listenarr\tests\Features\Infrastructure\Persistence\RootFolderReassignmentTransactionTests.cs:line 53
--- End of stack trace from previous location ---

FAILED: Listenarr.Tests.Features.Infrastructure.Library.Moving.AudiobookContentMoveServiceTests.MoveContentsAsync_TargetContainsOnlySourceSubtree_AllowsMove
Listenarr.Infrastructure.Library.Moving.MoveNeedsAttentionException : Destination contains an unowned directory: nested
at Listenarr.Infrastructure.Library.Moving.AudiobookContentMoveService.ValidateExistingDestinationContents(String source, String destinationRoot, IReadOnlyCollection`1 manifest, Guid jobId, FileSystemPathSemantics targetSemantics) in D:\a\Listenarr\Listenarr\listenarr.infrastructure\Library\Moving\AudiobookContentMoveService.Copy.cs:line 128
   at Listenarr.Infrastructure.Library.Moving.AudiobookContentMoveService.CopySourceContentsAsync(String source, String copyDestination, IReadOnlyList`1 manifest, Guid jobId, FileSystemPathSemantics sourceSemantics, FileSystemPathSemantics targetSemantics, CancellationToken cancellationToken) in D:\a\Listenarr\Listenarr\listenarr.infrastructure\Library\Moving\AudiobookContentMoveService.Copy.cs:line 17
   at Listenarr.Infrastructure.Library.Moving.AudiobookContentMoveService.MoveContentsAsync(AudiobookContentMoveRequest request, CancellationToken cancellationToken) in D:\a\Listenarr\Listenarr\listenarr.infrastructure\Library\Moving\AudiobookContentMoveService.cs:line 169
   at Listenarr.Tests.Features.Infrastructure.Library.Moving.AudiobookContentMoveServiceTests.MoveContentsAsync_TargetContainsOnlySourceSubtree_AllowsMove() in D:\a\Listenarr\Listenarr\tests\Features\Infrastructure\Library\Moving\AudiobookContentMoveServiceTests.cs:line 583
--- End of stack trace from previous location ---

FAILED: Listenarr.Tests.Features.Infrastructure.Persistence.RootFolderReassignmentTransactionTests.ReassignAudiobooksAndRemoveAsync_DeleteConflictRollsBackPathRewrites
Assert.Throws() Failure: No exception was thrown
Expected: typeof(Microsoft.EntityFrameworkCore.DbUpdateException)
at Listenarr.Tests.Features.Infrastructure.Persistence.RootFolderReassignmentTransactionTests.ReassignAudiobooksAndRemoveAsync_DeleteConflictRollsBackPathRewrites() in D:\a\Listenarr\Listenarr\tests\Features\Infrastructure\Persistence\RootFolderReassignmentTransactionTests.cs:line 95
--- End of stack trace from previous location ---

@github-actions

Copy link
Copy Markdown
Contributor

Temporary Windows test diagnostics

Exit code: 1

FAILED: Listenarr.Tests.Features.Infrastructure.Persistence.RootFolderReassignmentTransactionTests.ReassignAudiobooksAndRemoveAsync_RewritesAllReferencesAndDeletesRoot
Assert.Equal() Failure: Strings differ
                                     ↓ (pos 91)
Expected: ···"7e36cc8\\root-reassign-target\\Author\\Title"
Actual:   ···"7e36cc8\\root-reassign-source\\Author\\Title"
                                     ↑ (pos 91)
at Listenarr.Tests.Features.Infrastructure.Persistence.RootFolderReassignmentTransactionTests.ReassignAudiobooksAndRemoveAsync_RewritesAllReferencesAndDeletesRoot() in D:\a\Listenarr\Listenarr\tests\Features\Infrastructure\Persistence\RootFolderReassignmentTransactionTests.cs:line 53
--- End of stack trace from previous location ---

FAILED: Listenarr.Tests.Features.Infrastructure.Library.Moving.AudiobookContentMoveServiceTests.MoveContentsAsync_DirectCopyMarkerWithoutManifest_BlocksRecovery
Assert.Throws() Failure: No exception was thrown
Expected: typeof(Listenarr.Infrastructure.Library.Moving.MoveNeedsAttentionException)
at Listenarr.Tests.Features.Infrastructure.Library.Moving.AudiobookContentMoveServiceTests.MoveContentsAsync_DirectCopyMarkerWithoutManifest_BlocksRecovery() in D:\a\Listenarr\Listenarr\tests\Features\Infrastructure\Library\Moving\AudiobookContentMoveServiceTests.cs:line 247
--- End of stack trace from previous location ---

FAILED: Listenarr.Tests.Features.Infrastructure.Persistence.RootFolderReassignmentTransactionTests.ReassignAudiobooksAndRemoveAsync_DeleteConflictRollsBackPathRewrites
Assert.Throws() Failure: No exception was thrown
Expected: typeof(Microsoft.EntityFrameworkCore.DbUpdateException)
at Listenarr.Tests.Features.Infrastructure.Persistence.RootFolderReassignmentTransactionTests.ReassignAudiobooksAndRemoveAsync_DeleteConflictRollsBackPathRewrites() in D:\a\Listenarr\Listenarr\tests\Features\Infrastructure\Persistence\RootFolderReassignmentTransactionTests.cs:line 95
--- End of stack trace from previous location ---

@github-actions

Copy link
Copy Markdown
Contributor

Temporary Windows test diagnostics

Exit code: 1

FAILED: Listenarr.Tests.Features.Infrastructure.Persistence.RootFolderReassignmentTransactionTests.ReassignAudiobooksAndRemoveAsync_DeleteConflictRollsBackPathRewrites
Assert.Throws() Failure: No exception was thrown
Expected: typeof(Microsoft.EntityFrameworkCore.DbUpdateException)
at Listenarr.Tests.Features.Infrastructure.Persistence.RootFolderReassignmentTransactionTests.ReassignAudiobooksAndRemoveAsync_DeleteConflictRollsBackPathRewrites() in D:\a\Listenarr\Listenarr\tests\Features\Infrastructure\Persistence\RootFolderReassignmentTransactionTests.cs:line 143
--- End of stack trace from previous location ---

FAILED: Listenarr.Tests.Features.Infrastructure.Library.Moving.AudiobookContentMoveServiceTests.MoveContentsAsync_DirectCopyMarkerWithoutManifest_BlocksRecovery
Assert.Throws() Failure: No exception was thrown
Expected: typeof(Listenarr.Infrastructure.Library.Moving.MoveNeedsAttentionException)
at Listenarr.Tests.Features.Infrastructure.Library.Moving.AudiobookContentMoveServiceTests.MoveContentsAsync_DirectCopyMarkerWithoutManifest_BlocksRecovery() in D:\a\Listenarr\Listenarr\tests\Features\Infrastructure\Library\Moving\AudiobookContentMoveServiceTests.cs:line 247
--- End of stack trace from previous location ---

therobbiedavis and others added 29 commits July 22, 2026 14:45
@mels0n

mels0n commented Jul 25, 2026

Copy link
Copy Markdown

Was going to use Listenarr to reorganize my files, this appears to be a solution to many of the problems I got in testing. Is there a release schedule that will include this PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

patch patch version bump - backward compatible bug fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Linux/OSX] Folder names with leading/trailing space

5 participants