Add Git overlay, operation tools, and virtualized preview so large folders stay responsive.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -136,4 +136,90 @@ public class AnalysisTests
|
||||
var local = await store.Entries.GetByPathAsync(source.Id, "local.bin");
|
||||
Assert.Equal(HashState.Partial, local!.HashState);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Hardlinks_are_grouped_but_excluded_from_default_duplicate_list()
|
||||
{
|
||||
var (store, analysis, source) = await SeedHashedAsync(fileIdA: 42, fileIdB: 42);
|
||||
await using (store)
|
||||
{
|
||||
var raw = await store.Hashes.GetDuplicateGroupsAsync(null, null, 10);
|
||||
var group = Assert.Single(raw);
|
||||
Assert.True(group.SameFileId);
|
||||
|
||||
var classified = await analysis.GetClassifiedDuplicatesAsync();
|
||||
var item = Assert.Single(classified);
|
||||
Assert.Equal(DuplicateClass.Hardlink, item.Classification);
|
||||
Assert.True(DuplicateClassifier.IsHiddenByDefault(item.Classification));
|
||||
Assert.Equal(source.Id, item.Group.Entries[0].SourceId);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Marked_intentional_relation_is_excluded_from_default_list()
|
||||
{
|
||||
var (store, analysis, _) = await SeedHashedAsync(fileIdA: 1, fileIdB: 2);
|
||||
await using (store)
|
||||
{
|
||||
var before = Assert.Single(await analysis.GetClassifiedDuplicatesAsync());
|
||||
Assert.Equal(DuplicateClass.Unknown, before.Classification);
|
||||
Assert.False(DuplicateClassifier.IsHiddenByDefault(before.Classification));
|
||||
|
||||
await analysis.MarkDuplicateGroupAsync(before.Group.Entries, FileRelationKind.IntentionalDuplicate);
|
||||
var after = Assert.Single(await analysis.GetClassifiedDuplicatesAsync());
|
||||
Assert.Equal(DuplicateClass.Intentional, after.Classification);
|
||||
Assert.True(DuplicateClassifier.IsHiddenByDefault(after.Classification));
|
||||
|
||||
await analysis.MarkDuplicateGroupAsync(after.Group.Entries, FileRelationKind.AccidentalDuplicate);
|
||||
var accidental = Assert.Single(await analysis.GetClassifiedDuplicatesAsync());
|
||||
Assert.Equal(DuplicateClass.Accidental, accidental.Classification);
|
||||
Assert.False(DuplicateClassifier.IsHiddenByDefault(accidental.Classification));
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<(SqliteIndexStore Store, AnalysisService Analysis, Source Source)> SeedHashedAsync(
|
||||
long fileIdA, long fileIdB)
|
||||
{
|
||||
var db = Path.Combine(Path.GetTempPath(), "ew-dup", Guid.NewGuid().ToString("N"), "index.db");
|
||||
var store = new SqliteIndexStore(db, NullLogger<SqliteIndexStore>.Instance);
|
||||
await store.OpenAsync();
|
||||
var source = new Source
|
||||
{
|
||||
StableKey = "d",
|
||||
DisplayName = "D",
|
||||
Kind = SourceKind.NtfsLocal,
|
||||
LastRootPath = @"C:\d",
|
||||
Status = SourceStatus.Online
|
||||
};
|
||||
source.Id = await store.Sources.UpsertAsync(source);
|
||||
var hash = Enumerable.Repeat((byte)7, 32).ToArray();
|
||||
var root = new IndexEntry
|
||||
{
|
||||
SourceId = source.Id,
|
||||
Name = "d",
|
||||
NameNorm = "d",
|
||||
IsDirectory = true,
|
||||
PathRel = "",
|
||||
LastSeenUtc = DateTimeOffset.UtcNow
|
||||
};
|
||||
root.Id = await store.Entries.UpsertAsync(root);
|
||||
await store.Entries.UpsertAsync(Hashed("a.bin", fileIdA, source.Id, root.Id, hash));
|
||||
await store.Entries.UpsertAsync(Hashed("b.bin", fileIdB, source.Id, root.Id, hash));
|
||||
return (store, new AnalysisService(store), source);
|
||||
}
|
||||
|
||||
private static IndexEntry Hashed(string name, long fileId, long sourceId, long parentId, byte[] hash)
|
||||
=> new()
|
||||
{
|
||||
SourceId = sourceId,
|
||||
ParentId = parentId,
|
||||
Name = name,
|
||||
NameNorm = name,
|
||||
SizeBytes = 64,
|
||||
PathRel = name,
|
||||
FileId = fileId,
|
||||
ContentHash = hash,
|
||||
HashState = HashState.Full,
|
||||
LastSeenUtc = DateTimeOffset.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user