Files
douyin/tests/dy.net.Tests/OpenListDirectoryRepairServiceTests.cs

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")
};
}
}