Files
duplicati/Duplicati/UnitTest/Issue1698.cs
T
JamBalaya56562andClaude Opus 4.8 68dedaa320 Use effective dbpath for show-log and delete-db endpoints (fixes #1698)
A backup can have two notions of its local database path: the stored
Backup.DBPath field and a --dbpath advanced option in its settings. The runner
computes an effective path that lets --dbpath override Backup.DBPath, so most
operations honor it. But the "Show log", "Show remote log" and "Delete database"
endpoints read Backup.DBPath directly, so when --dbpath differs they open/delete
the wrong database - "Show log" then fails with "no such table: LogData".

Add a shared Runner.GetEffectiveDBPath(IBackup) helper (same precedence as
Runner.ApplyOptions) and use it in ExecuteGetLog, ExecuteGetRemotelog and
ExecuteDeleteDb so all operations agree on the database file. Adds a unit test
for the precedence.

The database move/update endpoints are intentionally left unchanged, as they
manage the DBPath field itself.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 22:28:27 +09:00

92 lines
3.8 KiB
C#

// Copyright (C) 2026, The Duplicati Team
// https://duplicati.com, hello@duplicati.com
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Duplicati.Server.Database;
using Duplicati.Server.Serialization.Interface;
using NUnit.Framework;
using Assert = NUnit.Framework.Legacy.ClassicAssert;
namespace Duplicati.UnitTest
{
/// <summary>
/// https://github.com/duplicati/duplicati/issues/1698
/// "Show log" and "Database delete" read/deleted the stored <c>Backup.DBPath</c> directly, while
/// most operations honor a "--dbpath" advanced option. When those disagreed, Show log opened the
/// wrong/empty database ("no such table: LogData"). <see cref="Duplicati.Server.Runner.GetEffectiveDBPath"/>
/// reconciles them with the same precedence as the runner; these tests cover that precedence.
/// </summary>
[TestFixture]
public class Issue1698
{
private static Backup CreateBackup(string? storedDbPath, params (string Name, string Value)[] settings)
{
var backup = new Backup
{
ID = null,
Name = "Test Backup",
Description = "",
Tags = new string[0],
TargetURL = "file:///test",
Sources = new string[0],
Settings = settings.Select(s => (ISetting)new Setting { Name = s.Name, Value = s.Value }).ToArray(),
Filters = new IFilter[0],
Metadata = new Dictionary<string, string>()
};
if (storedDbPath != null)
backup.SetDBPath(storedDbPath);
return backup;
}
[Test]
public void UsesStoredDbPathWhenNoAdvancedOption()
{
var backup = CreateBackup("/stored/path.sqlite");
Assert.AreEqual("/stored/path.sqlite", Duplicati.Server.Runner.GetEffectiveDBPath(backup));
}
[Test]
public void AdvancedDbPathOptionOverridesStoredDbPath()
{
var backup = CreateBackup("/stored/path.sqlite", ("--dbpath", "/override/path.sqlite"));
Assert.AreEqual("/override/path.sqlite", Duplicati.Server.Runner.GetEffectiveDBPath(backup));
}
[Test]
public void AdvancedDbPathMatchIsCaseInsensitiveOnName()
{
var backup = CreateBackup("/stored/path.sqlite", ("--DBPath", "/override/path.sqlite"));
Assert.AreEqual("/override/path.sqlite", Duplicati.Server.Runner.GetEffectiveDBPath(backup));
}
[Test]
public void BlankAdvancedDbPathFallsBackToStoredDbPath()
{
var backup = CreateBackup("/stored/path.sqlite", ("--dbpath", " "));
Assert.AreEqual("/stored/path.sqlite", Duplicati.Server.Runner.GetEffectiveDBPath(backup));
}
}
}