136 lines
5.8 KiB
C#
136 lines
5.8 KiB
C#
using System.Net;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using dy.net.model.dto;
|
|
using dy.net.model.entity;
|
|
using dy.net.service;
|
|
using dy.net.storage;
|
|
using dy.net.Tests.TestInfrastructure;
|
|
using Microsoft.AspNetCore.DataProtection;
|
|
using SqlSugar;
|
|
using MediaStorageType = dy.net.model.dto.StorageType;
|
|
|
|
namespace dy.net.Tests;
|
|
|
|
public sealed class OpenListDirectoryRepairServiceTests
|
|
{
|
|
[Fact]
|
|
public async Task Preflight_OnlyIncludesExactTimestampDirectoriesAndKeepsCanonicalDirectory()
|
|
{
|
|
using var temporary = new TemporaryDirectory();
|
|
using var database = new SqlSugarClient(new ConnectionConfig
|
|
{
|
|
ConnectionString = $"DataSource={Path.Combine(temporary.Path, "repair.sqlite")}",
|
|
DbType = DbType.Sqlite,
|
|
InitKeyType = InitKeyType.Attribute,
|
|
IsAutoCloseConnection = true
|
|
});
|
|
database.CodeFirst.InitTables<AppConfig, WebDavSettings, OpenListSettings,
|
|
OpenListDirectoryRepairTask, OpenListDirectoryRepairItem>();
|
|
await database.Insertable(new AppConfig
|
|
{
|
|
Id = "config",
|
|
StorageType = MediaStorageType.OpenList,
|
|
FollowedTitleTemplate = string.Empty,
|
|
FollowedTitleSeparator = string.Empty,
|
|
FullFollowedTitleTemplate = string.Empty
|
|
}).ExecuteCommandAsync();
|
|
|
|
var provider = DataProtectionProvider.Create(new DirectoryInfo(Path.Combine(temporary.Path, "keys")),
|
|
builder => builder.SetApplicationName("dysync.net"));
|
|
var legacy = new WebDavSettingsService(database, provider);
|
|
var settingsService = new OpenListSettingsService(database, provider, legacy);
|
|
var settings = await settingsService.BuildCandidateAsync(new OpenListTestRequest
|
|
{
|
|
Endpoint = "https://openlist.example.test",
|
|
BasePath = "/归档",
|
|
SourcePath = "/源挂载",
|
|
LocalStagingPath = Path.Combine(temporary.Path, "staging"),
|
|
UserName = "account",
|
|
Password = "password"
|
|
});
|
|
await settingsService.SaveAsync(settings, true, "tested");
|
|
|
|
using var factory = new RepairHttpClientFactory();
|
|
var service = new OpenListDirectoryRepairService(database, new DouyinCommonService(database),
|
|
settingsService, new OpenListClient(factory));
|
|
|
|
var preflight = await service.PreflightAsync(
|
|
new OpenListDirectoryRepairPreflightRequest { LogicalPath = "/collect/Kk" },
|
|
CancellationToken.None);
|
|
|
|
Assert.True(preflight.CanStart);
|
|
Assert.Equal("/collect/Kk", preflight.RequestedPath);
|
|
Assert.Equal("/collect/KK", preflight.CanonicalPath);
|
|
Assert.Equal(1, preflight.CandidateCount);
|
|
|
|
var created = await service.CreateAsync(new CreateOpenListDirectoryRepairRequest
|
|
{
|
|
LogicalPath = preflight.RequestedPath,
|
|
ConfigurationFingerprint = preflight.ConfigurationFingerprint
|
|
}, CancellationToken.None);
|
|
var items = await database.Queryable<OpenListDirectoryRepairItem>().ToListAsync();
|
|
|
|
Assert.Equal(1, created.Task.TotalCount);
|
|
var item = Assert.Single(items);
|
|
Assert.Equal("Kk_20260809_123456", item.DirectoryName);
|
|
Assert.DoesNotContain(items, x => x.DirectoryName == "KK");
|
|
}
|
|
|
|
private sealed class RepairHttpClientFactory : IHttpClientFactory, IDisposable
|
|
{
|
|
private readonly Handler _handler = new();
|
|
|
|
public HttpClient CreateClient(string name) => new(_handler, disposeHandler: false);
|
|
|
|
public void Dispose() => _handler.Dispose();
|
|
}
|
|
|
|
private sealed class Handler : HttpMessageHandler
|
|
{
|
|
protected override async Task<HttpResponseMessage> SendAsync(
|
|
HttpRequestMessage request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var api = request.RequestUri!.AbsolutePath;
|
|
if (api.EndsWith("/api/auth/login", StringComparison.Ordinal))
|
|
return Json(new { code = 200, message = "success", data = new { token = "test-token" } });
|
|
|
|
var body = JsonDocument.Parse(await request.Content!.ReadAsStringAsync(cancellationToken)).RootElement;
|
|
var path = body.GetProperty("path").GetString();
|
|
if (api.EndsWith("/api/fs/get", StringComparison.Ordinal))
|
|
{
|
|
if (path == "/归档/collect/KK")
|
|
return Json(new { code = 200, message = "success", data = new { name = "KK", size = 0, is_dir = true, hash_info = new { } } });
|
|
return Json(new { code = 404, message = "object not found", data = (object)null });
|
|
}
|
|
|
|
Assert.EndsWith("/api/fs/list", api, StringComparison.Ordinal);
|
|
var entries = path switch
|
|
{
|
|
"/" => new[] { Entry("归档", true) },
|
|
"/归档" => new[] { Entry("collect", true) },
|
|
"/归档/collect" => new[]
|
|
{
|
|
Entry("KK", true),
|
|
Entry("Kk_20260809_123456", true),
|
|
Entry("Kk_20260809_12345", true),
|
|
Entry("Kk_20260809_123456_extra", true),
|
|
Entry("KK_20260809_123456", true),
|
|
Entry("Kkkk_20260809_123456", true),
|
|
Entry("Kk_20260810_123456", false)
|
|
},
|
|
_ => Array.Empty<object>()
|
|
};
|
|
return Json(new { code = 200, message = "success", data = new { content = entries, write = true } });
|
|
}
|
|
|
|
private static object Entry(string name, bool directory) => new { name, size = 0, is_dir = directory };
|
|
|
|
private static HttpResponseMessage Json(object value) => new(HttpStatusCode.OK)
|
|
{
|
|
Content = new StringContent(JsonSerializer.Serialize(value), Encoding.UTF8, "application/json")
|
|
};
|
|
}
|
|
}
|