Files
Explorer-Workbench/tests/Explorer.Application.Tests/CloudProviderTests.cs
netquick e9aba73552 Add Explorer Workbench with hierarchical, off-UI Storage analysis.
Storage queries run in the background with cancellation and covering indexes so switching views no longer freezes the UI.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-22 12:43:05 +02:00

155 lines
8.1 KiB
C#

using Explorer.Application;
using Explorer.Domain;
using Explorer.Plugin.Abstractions;
using Explorer.Plugin.OneDrive;
using Microsoft.Extensions.Logging.Abstractions;
namespace Explorer.Application.Tests;
public class CloudProviderTests
{
[Fact]
public async Task Enrich_passes_through_when_provider_throws()
{
var registry = new StorageProviderRegistry([new ThrowingProvider()], NullLogger<StorageProviderRegistry>.Instance);
var item = new FileSystemItem { FullPath = @"C:\a.txt", Name = "a.txt", SizeBytes = 10 };
var result = await registry.EnrichAsync([item]);
Assert.Single(result);
Assert.Null(result[0].Cloud);
Assert.Equal("a.txt", result[0].Name);
}
[Fact]
public async Task Enrich_maps_provider_state()
{
var registry = new StorageProviderRegistry([new StubProvider()], NullLogger<StorageProviderRegistry>.Instance);
var item = new FileSystemItem { FullPath = @"C:\OneDrive\photo.jpg", Name = "photo.jpg", SizeBytes = 100 };
var result = await registry.EnrichAsync([item]);
Assert.Equal(Explorer.Domain.CloudAvailability.OnlineOnly, result[0].Cloud?.Availability);
Assert.Equal(8, result[0].AllocatedSizeBytes);
Assert.True(result[0].Cloud?.MayHydrateOnRead);
}
[Fact]
public void Hydration_guard_skips_online_only_and_recall_attributes()
{
var registry = new StorageProviderRegistry([], NullLogger<StorageProviderRegistry>.Instance);
var guard = new HydrationGuard(registry);
Assert.True(guard.WouldHydrateOnRead(AttributeFlags.RecallOnDataAccess, null));
Assert.True(guard.WouldHydrateOnRead(0, Explorer.Domain.CloudAvailability.OnlineOnly));
Assert.False(guard.WouldHydrateOnRead(0, Explorer.Domain.CloudAvailability.LocallyAvailable));
Assert.True(guard.WouldHydrateOnRead(new FileSystemItem
{
FullPath = @"C:\x",
Name = "x",
Cloud = new CloudPresence("onedrive", Explorer.Domain.CloudAvailability.OnlineOnly, 1, 0, true)
}));
}
[Theory]
[InlineData(OneDrivePlaceholderState.RecallOnDataAccess, null, Plugin.Abstractions.CloudAvailability.OnlineOnly)]
[InlineData(OneDrivePlaceholderState.Pinned, null, Plugin.Abstractions.CloudAvailability.Pinned)]
[InlineData(0, CloudFilesNativeStates.Invalid, Plugin.Abstractions.CloudAvailability.Error)]
[InlineData(0, CloudFilesNativeStates.PlaceholderInSync, Plugin.Abstractions.CloudAvailability.LocallyAvailable)]
public void OneDrive_maps_attributes_to_availability(int attributes, uint? cf, Plugin.Abstractions.CloudAvailability expected)
{
Assert.Equal(expected, OneDrivePlaceholderState.MapAvailability(attributes, cf));
var state = OneDrivePlaceholderState.FromLocalSignals("onedrive", @"C:\OneDrive\f", attributes, 0, 50, 4, cf);
Assert.Equal(expected, state.Availability);
if (expected == Plugin.Abstractions.CloudAvailability.OnlineOnly)
{
Assert.True(state.MayHydrateOnRead);
}
}
[Fact]
public void Disabled_provider_is_ignored()
{
var registry = new StorageProviderRegistry([new StubProvider()], NullLogger<StorageProviderRegistry>.Instance);
registry.SetEnabled("onedrive", false);
Assert.Null(registry.Find(@"C:\OneDrive\a"));
Assert.False(registry.HasCapability(@"C:\OneDrive\a", ProviderCapability.Pin));
Assert.Empty(registry.GetPlaces());
}
[Fact]
public void GetPlaces_fail_open_when_provider_throws()
{
var registry = new StorageProviderRegistry([new ThrowingPlacesProvider()], NullLogger<StorageProviderRegistry>.Instance);
Assert.Empty(registry.GetPlaces());
}
[Theory]
[InlineData("Personal", @"C:\Users\x\OneDrive", null, "Dominique", "Dominique - Personal")]
[InlineData("Business1", @"C:\Users\x\OneDrive - Contoso", "Contoso", "Dominique", "Dominique - Contoso")]
[InlineData("Business1", @"C:\Users\x\OneDrive - Contoso", null, null, "OneDrive - Contoso")]
[InlineData("Personal", @"C:\Users\x\OneDrive", null, null, "OneDrive")]
public void OneDrive_place_label(string key, string folder, string? display, string? user, string expected)
=> Assert.Equal(expected, OneDrivePlaceDiscovery.BuildLabel(key, folder, display, user));
[Fact]
public void Cloud_place_store_merges_manual_without_duplicates()
{
var discovered = new ProviderPlace[]
{
new("onedrive", "Dominique - Personal", @"C:\Users\x\OneDrive")
};
var manual = new ProviderPlace[]
{
new("onedrive", "Work", @"C:\Users\x\OneDrive - Contoso"),
new("onedrive", "Dup", @"C:\Users\x\OneDrive")
};
var merged = CloudPlaceStore.Merge(discovered, manual);
Assert.Equal(2, merged.Count);
Assert.Equal("Dominique - Personal", merged[0].DisplayName);
Assert.Equal(@"C:\Users\x\OneDrive - Contoso", merged[1].Path);
}
}
file static class CloudFilesNativeStates
{
public const uint PlaceholderInSync = 0x1 | 0x8;
public const uint Invalid = 0xFFFFFFFF;
}
file sealed class ThrowingPlacesProvider : IStorageProvider
{
public ProviderManifest Manifest { get; } = new("boom", "Boom", "1", ProviderIsolation.InProcess);
public ProviderCapability GetCapabilities() => ProviderCapability.CloudState;
public bool TryMatchRoot(string path) => false;
public IReadOnlyList<ProviderPlace> GetPlaces() => throw new InvalidOperationException("places failed");
public Task<IReadOnlyList<ProviderItemState>> GetItemStatesAsync(IReadOnlyList<string> paths, CancellationToken cancellationToken = default)
=> Task.FromResult<IReadOnlyList<ProviderItemState>>([]);
public Task<ProviderActionResult> TryInvokeAsync(ProviderActionRequest request, CancellationToken cancellationToken = default)
=> Task.FromResult(new ProviderActionResult(ProviderActionStatus.Unsupported));
public Task<ProviderQuota?> TryGetQuotaAsync(string rootPath, CancellationToken cancellationToken = default)
=> Task.FromResult<ProviderQuota?>(null);
}
file sealed class ThrowingProvider : IStorageProvider
{
public ProviderManifest Manifest { get; } = new("boom", "Boom", "1", ProviderIsolation.InProcess);
public ProviderCapability GetCapabilities() => ProviderCapability.CloudState;
public bool TryMatchRoot(string path) => throw new InvalidOperationException("match failed");
public Task<IReadOnlyList<ProviderItemState>> GetItemStatesAsync(IReadOnlyList<string> paths, CancellationToken cancellationToken = default)
=> throw new InvalidOperationException("state failed");
public Task<ProviderActionResult> TryInvokeAsync(ProviderActionRequest request, CancellationToken cancellationToken = default)
=> throw new InvalidOperationException("invoke failed");
public Task<ProviderQuota?> TryGetQuotaAsync(string rootPath, CancellationToken cancellationToken = default)
=> throw new InvalidOperationException("quota failed");
}
file sealed class StubProvider : IStorageProvider
{
public ProviderManifest Manifest { get; } = new("onedrive", "OneDrive", "1", ProviderIsolation.InProcess);
public ProviderCapability GetCapabilities() => ProviderCapability.CloudState | ProviderCapability.Pin | ProviderCapability.Dehydrate;
public bool TryMatchRoot(string path) => path.Contains("OneDrive", StringComparison.OrdinalIgnoreCase);
public Task<IReadOnlyList<ProviderItemState>> GetItemStatesAsync(IReadOnlyList<string> paths, CancellationToken cancellationToken = default)
=> Task.FromResult<IReadOnlyList<ProviderItemState>>(paths.Select(p => new ProviderItemState(
"onedrive", p, Plugin.Abstractions.CloudAvailability.OnlineOnly, 100, 8, true, true, "Online-only", null)).ToList());
public Task<ProviderActionResult> TryInvokeAsync(ProviderActionRequest request, CancellationToken cancellationToken = default)
=> Task.FromResult(new ProviderActionResult(ProviderActionStatus.Succeeded));
public Task<ProviderQuota?> TryGetQuotaAsync(string rootPath, CancellationToken cancellationToken = default)
=> Task.FromResult<ProviderQuota?>(null);
}