Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using NUnit.Framework;
using NzbDrone.Core.Books;

namespace Chaptarr.Core.Test.Books
{
[TestFixture]
public class AuthorApplyChangesPathPreservationFixture
{
private static Author StoredAuthor()
{
return new Author
{
Path = "/library/authors/Stored Author",
AudiobookRootFolderPath = "/library/audiobooks",
EbookRootFolderPath = "/library/ebooks"
};
}

[Test]
public void should_preserve_stored_paths_when_incoming_author_omits_them()
{
var stored = StoredAuthor();

// An update that does not mention paths - e.g. a PUT that only changes
// monitoring, or an Author built from remote metadata during a refresh.
stored.ApplyChanges(new Author());

Assert.Multiple(() =>
{
Assert.That(stored.Path, Is.EqualTo("/library/authors/Stored Author"));
Assert.That(stored.AudiobookRootFolderPath, Is.EqualTo("/library/audiobooks"));
Assert.That(stored.EbookRootFolderPath, Is.EqualTo("/library/ebooks"));
});
}

[Test]
public void should_apply_paths_when_incoming_author_supplies_them()
{
var stored = StoredAuthor();

stored.ApplyChanges(new Author
{
Path = "/library/authors/Moved Author",
AudiobookRootFolderPath = "/library/audiobooks-2",
EbookRootFolderPath = "/library/ebooks-2"
});

Assert.Multiple(() =>
{
Assert.That(stored.Path, Is.EqualTo("/library/authors/Moved Author"));
Assert.That(stored.AudiobookRootFolderPath, Is.EqualTo("/library/audiobooks-2"));
Assert.That(stored.EbookRootFolderPath, Is.EqualTo("/library/ebooks-2"));
});
}

[Test]
public void should_preserve_each_path_independently()
{
var stored = StoredAuthor();

// Only the audiobook root is being changed; the other two must survive.
stored.ApplyChanges(new Author { AudiobookRootFolderPath = "/library/audiobooks-2" });

Assert.Multiple(() =>
{
Assert.That(stored.AudiobookRootFolderPath, Is.EqualTo("/library/audiobooks-2"));
Assert.That(stored.Path, Is.EqualTo("/library/authors/Stored Author"));
Assert.That(stored.EbookRootFolderPath, Is.EqualTo("/library/ebooks"));
});
}
}
}
22 changes: 19 additions & 3 deletions src/NzbDrone.Core/Books/Model/Author.cs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,16 @@ public override void UseDbFieldsFrom(Author other)

public override void ApplyChanges(Author other)
{
Path = other.Path;
// Only overwrite when a value is supplied. ApplyChanges is called both from
// the API -- where a PUT may legitimately omit fields it is not changing --
// and from metadata refresh, where the incoming Author is built from remote
// metadata and carries no local paths at all. That is the same reason the
// quality and metadata profiles below are guarded; these three were not, so
// an update that omitted them wiped the stored paths.
if (other.Path != null)
{
Path = other.Path;
}

// Don't overwrite quality profiles if they're already set
// Quality profiles from metadata sources are always null/0
Expand Down Expand Up @@ -326,8 +335,15 @@ public override void ApplyChanges(Author other)

Tags = (AudiobookTags ?? new HashSet<int>()).Concat(EbookTags ?? new HashSet<int>()).ToHashSet();
AddOptions = other.AddOptions;
AudiobookRootFolderPath = other.AudiobookRootFolderPath;
EbookRootFolderPath = other.EbookRootFolderPath;
if (other.AudiobookRootFolderPath != null)
{
AudiobookRootFolderPath = other.AudiobookRootFolderPath;
}

if (other.EbookRootFolderPath != null)
{
EbookRootFolderPath = other.EbookRootFolderPath;
}
Monitored = other.Monitored;
// TRI-STATE MONITORING SYSTEM - Copy only when explicitly provided (not null).
// This prevents partial updates from wiping existing monitoring values.
Expand Down