mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-04 21:27:21 +00:00
Upgraded to .NET 10 (#1147)
This commit is contained in:
1 parent
faae2951f7
commit
fc7399e81b
327 files changed
+3059
-3049
No files matched your search
@@ -86,7 +86,7 @@ public sealed class ExistingIndexMigrationTests
|
||||
await store.Client.DeleteFileAsync(DATA_SOURCE_ID, PATH, TOKEN);
|
||||
var foundAfterDeleting = (await store.Client.SearchChunksAsync(DATA_SOURCE_ID, "quarterly", 10, TOKEN)).Concat(await store.Client.SearchChunksAsync(DATA_SOURCE_ID, "budget", 10, TOKEN));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(manifest.Files[PATH].Fingerprint, Is.EqualTo(FINGERPRINT), "The file survives the migration as it was.");
|
||||
Assert.That(manifest.Files[PATH].ContentHash, Is.Null, "A file indexed before has no content hash until it is read again.");
|
||||
@@ -97,7 +97,7 @@ public sealed class ExistingIndexMigrationTests
|
||||
Assert.That(foundNewChunk.Select(result => (result.ChunkText, result.StartLine, result.EndLine)), Is.EqualTo(new (string, int?, int?)[] { (NEW_TEXT, 12, 14) }), "A chunk stored after the migration reaches the full-text index, with where it is.");
|
||||
Assert.That(storedColumns, Is.EqualTo(new[] { (null, 0), (ChunkTextHash.Of(NEW_TEXT), 0) }), "The old chunk is left as it was, and a new one keeps its hash.");
|
||||
Assert.That(foundAfterDeleting, Is.Empty, "Deleting the document removes its chunks from the full-text index.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
|
||||
@@ -37,12 +37,12 @@ public sealed class IndexStoreBackupTests
|
||||
var manifest = await copy.GetManifestAsync(DATA_SOURCE_ID, TOKEN);
|
||||
var found = await copy.SearchChunksAsync(DATA_SOURCE_ID, "quarterly", 10, TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(Path.GetFileName(copyPath), Is.EqualTo("rag-index.sqlite3"), "A restore puts the copy where the index belongs, under the same name.");
|
||||
Assert.That(manifest.Files.Keys, Is.EqualTo(new[] { FILE_PATH }), "Without the fingerprint, the file would be embedded anew.");
|
||||
Assert.That(found.Select(result => result.ChunkText), Is.EqualTo(new[] { "The quarterly report is due." }));
|
||||
});
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -72,11 +72,11 @@ public sealed class IndexStoreChunkLocationTests
|
||||
var ofAnotherDataSource = await store.Client.GetChunkLocationsAsync("0e2f4c6a-8b1d-4e3f-a5c7-9d1b3f5e7a90", [chunkId], TOKEN);
|
||||
|
||||
var expected = new ChunkLocation(SheetName: "Budget", StartRow: 3, EndRow: 7);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(found.Location, Is.EqualTo(expected));
|
||||
Assert.That(locations, Is.EquivalentTo(new Dictionary<string, ChunkLocation> { [chunkId] = expected }), "A chunk the index does not know is left out.");
|
||||
Assert.That(ofAnotherDataSource, Is.Empty, "A chunk is looked up within its own data source only.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,10 +41,10 @@ public sealed class IndexStoreDataSourceStateTests
|
||||
await using var context = store.CreateContext();
|
||||
var failuresAfterDeletion = await context.PermanentIndexingFailures.CountAsync(token);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(stateAfterDeletion, Is.Null);
|
||||
Assert.That(failuresAfterDeletion, Is.Zero, "The failures go with the data source.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,13 +35,13 @@ public sealed class IndexStoreDisplayInfoTests
|
||||
await AddFailureAsync(store, MAILBOX_ID, MailKey('b'));
|
||||
|
||||
var lines = await ReadDisplayInfoAsync(store);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(lines["Indexed files"], Is.EqualTo("2"));
|
||||
Assert.That(lines["Permanently skipped files"], Is.EqualTo("1"));
|
||||
Assert.That(lines["Indexed mails"], Is.EqualTo("1"));
|
||||
Assert.That(lines["Permanently skipped mails"], Is.EqualTo("1"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -52,12 +52,12 @@ public sealed class IndexStoreDisplayInfoTests
|
||||
await AddDocumentAsync(store, FOLDER_ID, "/tmp/test-data/minutes.md");
|
||||
|
||||
var lines = await ReadDisplayInfoAsync(store);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(lines["Indexed files"], Is.EqualTo("1"));
|
||||
Assert.That(lines, Does.Not.ContainKey("Indexed mails"));
|
||||
Assert.That(lines, Does.Not.ContainKey("Permanently skipped mails"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static string MailKey(char filler) => MailContentKey.PREFIX + new string(filler, 64);
|
||||
|
||||
@@ -32,11 +32,11 @@ public sealed class IndexStoreFullTextSearchTests
|
||||
var all = await store.Client.SearchChunksAsync(DATA_SOURCE_ID, "report", 10, TOKEN);
|
||||
var firstTwo = await store.Client.SearchChunksAsync(DATA_SOURCE_ID, "report", 2, TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(all.Select(result => (result.ParentFileId, result.ChunkIndex)), Is.EqualTo(new[] { (best, 0), (ties[0], 0), (ties[1], 0) }), "The chunk without the word is not found, and the tie falls by document.");
|
||||
Assert.That(firstTwo.Select(result => result.ParentFileId), Is.EqualTo(new[] { best, ties[0] }), "A smaller limit cuts the same order short.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -53,11 +53,11 @@ public sealed class IndexStoreFullTextSearchTests
|
||||
|
||||
var found = (await store.Client.SearchChunksAsync(DATA_SOURCE_ID, "report", 10, TOKEN)).Single();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(found.PageNumber, Is.EqualTo(3));
|
||||
Assert.That(found.EndPageNumber, Is.EqualTo(5), "The passage the AI receives from this chunk names pages 3 to 5.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<string> StoreFileAsync(TemporaryIndexStore store, string path, params string[] chunkTexts)
|
||||
|
||||
@@ -33,11 +33,11 @@ public sealed class IndexStoreManifestTests
|
||||
|
||||
var manifest = await store.Client.GetManifestAsync(DATA_SOURCE_ID, TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(manifest.Files.Keys, Is.EquivalentTo([indexedFile]), "A file whose indexing never came to an end has to be indexed again.");
|
||||
Assert.That(manifest.UnfinishedFiles, Is.EquivalentTo([unfinishedFile]), "Without this, nothing removes its leftovers once the file is gone or no longer selected.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -54,11 +54,11 @@ public sealed class IndexStoreManifestTests
|
||||
|
||||
var manifest = await store.Client.GetManifestAsync(DATA_SOURCE_ID, TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(manifest.Files[recording].TranscriptKey, Is.EqualTo(transcriptKey), "Without the key, AI Studio cannot remove the transcript once the recording is gone.");
|
||||
Assert.That(manifest.Files[document].TranscriptKey, Is.Null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
@@ -79,11 +79,11 @@ public sealed class IndexStoreManifestTests
|
||||
|
||||
var manifest = await store.Client.GetManifestAsync(DATA_SOURCE_ID, TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(manifest.Files[hashedFile].ContentHash, Is.EqualTo(contentHash));
|
||||
Assert.That(manifest.Files[olderFile].ContentHash, Is.Null, "A file indexed before the hash was kept is read again the next time it looks changed.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static EmbeddingStateFile Row(string filePath, int chunkCount)
|
||||
|
||||
@@ -26,7 +26,7 @@ public sealed class IndexStoreRelocationTests
|
||||
var manifest = await store.Client.GetManifestAsync(sourceId, token);
|
||||
var hits = await store.Client.SearchChunksAsync(sourceId, "relocation", 10, token);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(manifest.Files.Keys, Is.EquivalentTo(new[] { newPath }));
|
||||
Assert.That(manifest.Files[newPath].Fingerprint, Is.EqualTo("new-fingerprint"));
|
||||
@@ -39,7 +39,7 @@ public sealed class IndexStoreRelocationTests
|
||||
Assert.That(hits[0].FileName, Is.EqualTo("renamed.md"));
|
||||
Assert.That(hits[0].PageNumber, Is.EqualTo(2));
|
||||
Assert.That(hits[0].ChunkText, Is.EqualTo("report about relocation"));
|
||||
});
|
||||
}
|
||||
|
||||
await store.Client.DeleteFileAsync(sourceId, newPath, token);
|
||||
Assert.That(await store.Client.SearchChunksAsync(sourceId, "relocation", 10, token), Is.Empty, "Ordinary removal must still find a relocated document's chunks.");
|
||||
|
||||
@@ -129,12 +129,12 @@ public sealed class MailQueryStoreTests
|
||||
var second = await this.Client.QueryMailsAsync(MAILBOX, new MailFilter(), 2, 2, TOKEN);
|
||||
var beyond = await this.Client.QueryMailsAsync(MAILBOX, new MailFilter(), 3, 2, TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(first, Is.EqualTo(this.IdsOf(BUDGET, REPORT)));
|
||||
Assert.That(second, Is.EqualTo(this.IdsOf(NEWSLETTER)));
|
||||
Assert.That(beyond, Is.Empty);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -145,12 +145,12 @@ public sealed class MailQueryStoreTests
|
||||
var unread = await this.Client.QueryMailsAsync(MAILBOX, new MailFilter { IsUnread = true }, 0, 10, TOKEN);
|
||||
var summary = (await this.Client.GetMailSummariesAsync(MAILBOX, [this.mailIds[BUDGET]], TOKEN)).Single();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(unread, Is.Empty, "Whoever read the copy in the archive read the mail.");
|
||||
Assert.That(summary.Flags, Is.EqualTo(new MailFlags(true, true, false)));
|
||||
Assert.That(summary.FolderPaths, Is.EqualTo(new[] { ARCHIVE, INBOX }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -159,11 +159,11 @@ public sealed class MailQueryStoreTests
|
||||
var flagged = await this.Client.GetMailChunkIdsAsync(MAILBOX, new MailFilter { IsFlagged = true }, TOKEN);
|
||||
var all = await this.Client.GetMailChunkIdsAsync(MAILBOX, new MailFilter(), TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(flagged, Is.EqualTo(new[] { ChunkIdOf(MAILBOX, BUDGET) }));
|
||||
Assert.That(all, Is.EquivalentTo(new[] { ChunkIdOf(MAILBOX, REPORT), ChunkIdOf(MAILBOX, BUDGET), ChunkIdOf(MAILBOX, NEWSLETTER) }), "The chunks of the deleted mail are still in the vector store, but must not be searched.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -173,12 +173,12 @@ public sealed class MailQueryStoreTests
|
||||
var flagged = await this.Client.SearchMailChunksAsync(MAILBOX, "report", new MailFilter { IsFlagged = true }, 10, TOKEN);
|
||||
var bySubject = await this.Client.SearchMailChunksAsync(MAILBOX, "newsletter", new MailFilter(), 10, TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(everywhere.Select(result => result.ParentFileId), Is.EquivalentTo(this.IdsOf(REPORT, BUDGET)));
|
||||
Assert.That(flagged.Select(result => result.ParentFileId), Is.EqualTo(this.IdsOf(BUDGET)));
|
||||
Assert.That(bySubject.Select(result => result.ParentFileId), Is.EqualTo(this.IdsOf(NEWSLETTER)), "The subject is searched along with the text.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -189,7 +189,7 @@ public sealed class MailQueryStoreTests
|
||||
var bySender = await this.Client.CountMailsAsync(MAILBOX, new MailFilter(), MailCountGrouping.SENDER, 2, TOKEN);
|
||||
var flaggedBySender = await this.Client.CountMailsAsync(MAILBOX, new MailFilter { IsFlagged = true }, MailCountGrouping.SENDER, 20, TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(total.TotalCount, Is.EqualTo(3));
|
||||
Assert.That(total.Groups, Is.Empty);
|
||||
@@ -198,7 +198,7 @@ public sealed class MailQueryStoreTests
|
||||
Assert.That(bySender.Groups, Is.EqualTo(new MailCountGroup[] { new("alice@example.org", "Alice Müller", 1), new("bob@example.org", "Bob", 1) }), "Groups of the same size by their key, and no more than asked for.");
|
||||
Assert.That(flaggedBySender.TotalCount, Is.EqualTo(1));
|
||||
Assert.That(flaggedBySender.Groups, Is.EqualTo(new MailCountGroup[] { new("bob@example.org", "Bob", 1) }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -208,11 +208,11 @@ public sealed class MailQueryStoreTests
|
||||
|
||||
var byFolder = await this.Client.CountMailsAsync(MAILBOX, new MailFilter(), MailCountGrouping.FOLDER, 20, TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(byFolder.TotalCount, Is.EqualTo(3));
|
||||
Assert.That(byFolder.Groups, Is.EqualTo(new MailCountGroup[] { new(ARCHIVE, string.Empty, 2), new(INBOX, string.Empty, 2) }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -224,7 +224,7 @@ public sealed class MailQueryStoreTests
|
||||
|
||||
Assert.That(summaries.Select(summary => summary.MailId), Is.EqualTo(this.IdsOf(BUDGET, REPORT)), "In the order asked for, without unknown mails, deleted ones, or those of another mailbox.");
|
||||
var budget = summaries[0];
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(budget.Subject, Is.EqualTo("Budget 2027"));
|
||||
Assert.That(budget.ReceivedAtUtc, Is.EqualTo(new DateTimeOffset(2026, 9, 10, 9, 30, 0, TimeSpan.Zero)));
|
||||
@@ -236,7 +236,7 @@ public sealed class MailQueryStoreTests
|
||||
Assert.That(budget.Importance, Is.EqualTo(MailImportance.HIGH));
|
||||
Assert.That(budget.EncryptionKind, Is.EqualTo(MailEncryptionKind.NONE));
|
||||
Assert.That(budget.AttachmentNames, Is.EqualTo(new[] { "budget.xlsx" }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -247,13 +247,13 @@ public sealed class MailQueryStoreTests
|
||||
var deleted = await this.Client.FindMailByMessageIdAsync(MAILBOX, "deleted@example.org", TOKEN);
|
||||
var none = await this.Client.FindMailByMessageIdAsync(MAILBOX, string.Empty, TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(answered, Is.EqualTo(this.mailIds[REPORT]));
|
||||
Assert.That(answeredElsewhere, Is.EqualTo(this.mailIds[ELSEWHERE]), "The same Message-ID in another mailbox is that mailbox's own copy.");
|
||||
Assert.That(deleted, Is.Null);
|
||||
Assert.That(none, Is.Null, "Mails without a Message-ID must not all answer each other.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StoreMailAsync(string dataSourceId, string name, string subject, string text, MailRecord mail) =>
|
||||
|
||||
@@ -50,14 +50,14 @@ public sealed class MailboxIndexStoreTests
|
||||
var stored = await this.Client.GetMailAsync(DATA_SOURCE_ID, mail.MailId, TOKEN);
|
||||
|
||||
Assert.That(stored, Is.Not.Null);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(stored! with { ReferenceMessageIds = mail.ReferenceMessageIds, Addresses = mail.Addresses, Parts = mail.Parts, Locations = mail.Locations }, Is.EqualTo(mail), "The dates come back in UTC, but have to name the same moments.");
|
||||
Assert.That(stored.ReferenceMessageIds, Is.EqualTo(mail.ReferenceMessageIds));
|
||||
Assert.That(stored.Addresses, Is.EqualTo(mail.Addresses));
|
||||
Assert.That(stored.Parts, Is.EqualTo(mail.Parts));
|
||||
Assert.That(stored.Locations, Is.EquivalentTo(mail.Locations));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -70,11 +70,11 @@ public sealed class MailboxIndexStoreTests
|
||||
|
||||
var folders = await this.Client.GetMailFoldersAsync(DATA_SOURCE_ID, TOKEN);
|
||||
var inboxLocations = await this.Client.GetMailLocationsAsync(DATA_SOURCE_ID, INBOX, TOKEN);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(folders, Is.EqualTo(new[] { Folder(ARCHIVE, 1), synced }), "Ordered by path.");
|
||||
Assert.That(inboxLocations.Keys, Is.EquivalentTo(new long[] { 7 }), "The UIDVALIDITY did not change, so the UIDs stay valid.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -93,14 +93,14 @@ public sealed class MailboxIndexStoreTests
|
||||
|
||||
var stored = await this.Client.GetMailAsync(DATA_SOURCE_ID, mailId, TOKEN);
|
||||
var inboxLocations = await this.Client.GetMailLocationsAsync(DATA_SOURCE_ID, INBOX, TOKEN);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(stored!.FirstSeenUtc, Is.EqualTo(FIRST_SEEN), "The mail was found before, and when it was first seen stays the earlier moment.");
|
||||
Assert.That(stored.Addresses, Is.EqualTo(again.Addresses));
|
||||
Assert.That(stored.Parts, Is.EqualTo(again.Parts));
|
||||
Assert.That(stored.Locations, Is.EqualTo(again.Locations));
|
||||
Assert.That(inboxLocations, Is.Empty, "The places are replaced as well, not added to.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -116,13 +116,13 @@ public sealed class MailboxIndexStoreTests
|
||||
var orphanedAfterLinking = await this.Client.GetOrphanedMailsAsync(DATA_SOURCE_ID, DateTimeOffset.UtcNow.AddMinutes(1), TOKEN);
|
||||
var archiveLocations = await this.Client.GetMailLocationsAsync(DATA_SOURCE_ID, ARCHIVE, TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(orphanedAfterRemoval, Is.EqualTo(new[] { KEY_A }), "Without any place left, the mail is an orphan.");
|
||||
Assert.That(linked, Is.True);
|
||||
Assert.That(orphanedAfterLinking, Is.Empty, "Found in another folder, the mail is no orphan any more and keeps its embedding.");
|
||||
Assert.That(archiveLocations, Is.EqualTo(new Dictionary<long, MailFlags> { [3] = new(true, false, false) }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -145,11 +145,11 @@ public sealed class MailboxIndexStoreTests
|
||||
var dueThisRun = await this.Client.GetOrphanedMailsAsync(DATA_SOURCE_ID, runStart, TOKEN);
|
||||
var dueNextRun = await this.Client.GetOrphanedMailsAsync(DATA_SOURCE_ID, nextRunStart, TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(dueThisRun, Is.Empty, "The run which lost the mail may still find it in a folder it gets to later.");
|
||||
Assert.That(dueNextRun, Is.EqualTo(new[] { KEY_A }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -166,13 +166,13 @@ public sealed class MailboxIndexStoreTests
|
||||
var orphaned = await this.Client.GetOrphanedMailsAsync(DATA_SOURCE_ID, DateTimeOffset.UtcNow.AddMinutes(1), TOKEN);
|
||||
var orphan = await this.Client.GetMailAsync(DATA_SOURCE_ID, onlyInInbox, TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(inboxLocations, Is.Empty, "Under a new UIDVALIDITY, the stored UIDs name nothing any more.");
|
||||
Assert.That(archiveLocations.Keys, Is.EquivalentTo(new long[] { 3 }), "Other folders are not affected.");
|
||||
Assert.That(orphaned, Is.EqualTo(new[] { KEY_A }), "Only the mail which lies nowhere else is an orphan.");
|
||||
Assert.That(orphan, Is.Not.Null, "Orphaned, not deleted: found again under its new UID, it is only linked anew.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -185,11 +185,11 @@ public sealed class MailboxIndexStoreTests
|
||||
|
||||
var folders = await this.Client.GetMailFoldersAsync(DATA_SOURCE_ID, TOKEN);
|
||||
var orphaned = await this.Client.GetOrphanedMailsAsync(DATA_SOURCE_ID, DateTimeOffset.UtcNow.AddMinutes(1), TOKEN);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(folders.Select(folder => folder.Path), Is.EqualTo(new[] { ARCHIVE }));
|
||||
Assert.That(orphaned, Is.EqualTo(new[] { KEY_A }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -201,11 +201,11 @@ public sealed class MailboxIndexStoreTests
|
||||
|
||||
var inboxLocations = await this.Client.GetMailLocationsAsync(DATA_SOURCE_ID, INBOX, TOKEN);
|
||||
var archiveLocations = await this.Client.GetMailLocationsAsync(DATA_SOURCE_ID, ARCHIVE, TOKEN);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(inboxLocations, Is.EqualTo(new Dictionary<long, MailFlags> { [7] = new(true, true, false) }), "A UID the index does not hold is ignored, not added.");
|
||||
Assert.That(archiveLocations, Is.EqualTo(new Dictionary<long, MailFlags> { [3] = new(false, false, false) }), "IMAP keeps flags per folder, so the copy in the archive keeps its own.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -220,12 +220,12 @@ public sealed class MailboxIndexStoreTests
|
||||
var firstStored = await this.Client.GetMailAsync(DATA_SOURCE_ID, first, TOKEN);
|
||||
var secondStored = await this.Client.GetMailAsync(DATA_SOURCE_ID, second, TOKEN);
|
||||
var orphaned = await this.Client.GetOrphanedMailsAsync(DATA_SOURCE_ID, DateTimeOffset.UtcNow.AddMinutes(1), TOKEN);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(firstStored!.Locations, Is.Empty, "The server says another mail lies there now, so the stored place was stale.");
|
||||
Assert.That(secondStored!.Locations, Is.EqualTo(new[] { At(INBOX, 7) }));
|
||||
Assert.That(orphaned, Is.EqualTo(new[] { KEY_A }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -233,13 +233,13 @@ public sealed class MailboxIndexStoreTests
|
||||
{
|
||||
var mailId = await this.StoreDocumentAsync(KEY_A);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.ThrowsAsync<ArgumentException>(() => this.Client.UpsertMailAsync(DATA_SOURCE_ID, Mail(mailId), TOKEN), "A mail which lies nowhere would be an orphan from the start.");
|
||||
Assert.ThrowsAsync<ArgumentException>(() => this.Client.UpsertMailAsync(DATA_SOURCE_ID, Mail(mailId, At(INBOX, 7), At(INBOX, 7)), TOKEN));
|
||||
Assert.ThrowsAsync<InvalidOperationException>(() => this.Client.UpsertMailAsync(DATA_SOURCE_ID, Mail(mailId, At("Unknown", 1)), TOKEN));
|
||||
Assert.ThrowsAsync<ArgumentException>(() => this.Client.UpsertMailAsync(DATA_SOURCE_ID, Mail(mailId, At(INBOX, 7)) with { ReferenceMessageIds = ["one two@example.org"] }, TOKEN), "Stored separated by spaces, this one would come back as two.");
|
||||
});
|
||||
await Assert.ThrowsAsync<ArgumentException>(() => this.Client.UpsertMailAsync(DATA_SOURCE_ID, Mail(mailId), TOKEN), "A mail which lies nowhere would be an orphan from the start.");
|
||||
await Assert.ThrowsAsync<ArgumentException>(() => this.Client.UpsertMailAsync(DATA_SOURCE_ID, Mail(mailId, At(INBOX, 7), At(INBOX, 7)), TOKEN));
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() => this.Client.UpsertMailAsync(DATA_SOURCE_ID, Mail(mailId, At("Unknown", 1)), TOKEN));
|
||||
await Assert.ThrowsAsync<ArgumentException>(() => this.Client.UpsertMailAsync(DATA_SOURCE_ID, Mail(mailId, At(INBOX, 7)) with { ReferenceMessageIds = ["one two@example.org"] }, TOKEN), "Stored separated by spaces, this one would come back as two.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -67,14 +67,14 @@ public sealed class MailboxStateStoreTests
|
||||
await this.Client.HoldBackMailRemovalAsync(DATA_SOURCE_ID, 800, TOKEN);
|
||||
var stateAfterMoreMails = await this.Client.GetMailboxSyncStateAsync(DATA_SOURCE_ID, TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(approvedAnotherCount, Is.False, "A sync changed the count while the user looked at the old one.");
|
||||
Assert.That(stateAfterAnotherCount.PendingRemovalApprovedUtc, Is.Null);
|
||||
Assert.That(approvedTheCount, Is.True);
|
||||
Assert.That(stateAfterTheSameCountAgain.PendingRemovalApprovedUtc, Is.Not.Null, "The next sync finds the same count and may go ahead.");
|
||||
Assert.That(stateAfterMoreMails, Is.EqualTo(new MailboxSyncState(null, 800, null)), "The user agreed to 500 mails, not to whatever comes.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -83,17 +83,17 @@ public sealed class MailboxStateStoreTests
|
||||
var approved = await this.Client.ApprovePendingMailRemovalAsync(DATA_SOURCE_ID, 500, TOKEN);
|
||||
var state = await this.Client.GetMailboxSyncStateAsync(DATA_SOURCE_ID, TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(approved, Is.False);
|
||||
Assert.That(state, Is.EqualTo(new MailboxSyncState(null, null, null)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ARemovalOfNoMailIsNothingToHoldBack()
|
||||
public async Task ARemovalOfNoMailIsNothingToHoldBack()
|
||||
{
|
||||
Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => this.Client.HoldBackMailRemovalAsync(DATA_SOURCE_ID, 0, TOKEN));
|
||||
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => this.Client.HoldBackMailRemovalAsync(DATA_SOURCE_ID, 0, TOKEN));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -111,12 +111,12 @@ public sealed class MailboxStateStoreTests
|
||||
await this.Client.ClearMailboxAuthFailureAsync(DATA_SOURCE_ID, TOKEN);
|
||||
var afterClearing = await this.Client.GetMailboxAuthFailureAsync(DATA_SOURCE_ID, TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(afterFirst, Is.EqualTo(first));
|
||||
Assert.That(afterSecond, Is.EqualTo(second), "The one try the user asked for failed again.");
|
||||
Assert.That(afterClearing, Is.Null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -38,14 +38,14 @@ public sealed class ProvenanceStoreTests
|
||||
|
||||
Assert.That(entries, Contains.Key(id.Value));
|
||||
var entry = entries[id.Value];
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(entry.Id, Is.EqualTo(id.Value));
|
||||
Assert.That(entry.Kind, Is.EqualTo(ProvenanceKind.DATA_SOURCE_PASSAGE));
|
||||
Assert.That(entry.Content, Is.EqualTo("The budget grew by 4 %.\nÄnderungen folgen."));
|
||||
Assert.That(entry.SurroundingContent, Is.EqualTo(new[] { "Before the passage.", "After the passage." }));
|
||||
Assert.That(entry.CreatedAtUtc, Is.GreaterThan(before));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -59,12 +59,12 @@ public sealed class ProvenanceStoreTests
|
||||
|
||||
await using var context = store.CreateContext();
|
||||
var rowsWithSurroundingContent = await context.Entries.CountAsync(entry => entry.SurroundingContent != null);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(entries[withNull.Value].SurroundingContent, Is.Empty);
|
||||
Assert.That(entries[withEmpty.Value].SurroundingContent, Is.Empty);
|
||||
Assert.That(rowsWithSurroundingContent, Is.Zero, "Only an ERI passage brings content around it, so every other row keeps the column empty.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -111,11 +111,11 @@ public sealed class ProvenanceStoreTests
|
||||
await store.Client.DeleteChatsAsync([assistantChat], TOKEN);
|
||||
var entries = await store.Client.GetEntriesAsync([passage!.Value, page!.Value], TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(entries[passage.Value].Content, Is.EqualTo("a passage"));
|
||||
Assert.That(entries[page.Value].Content, Is.EqualTo("a page"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -146,11 +146,11 @@ public sealed class ProvenanceStoreTests
|
||||
var entries = await store.Client.GetEntriesAsync([deleted!.Value, kept!.Value], TOKEN);
|
||||
var chatIds = await store.Client.GetChatIdsAsync(TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(entries.Keys, Is.EquivalentTo(new[] { kept.Value }));
|
||||
Assert.That(chatIds, Is.EquivalentTo(new[] { keptChat }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -168,11 +168,11 @@ public sealed class ProvenanceStoreTests
|
||||
var deleted = await store.Client.DeleteChatsAsync([firstChat, secondChat], TOKEN);
|
||||
var chatIds = await store.Client.GetChatIdsAsync(TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(deleted, Is.EqualTo(3));
|
||||
Assert.That(chatIds, Is.EquivalentTo(new[] { keptChat }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -237,12 +237,12 @@ public sealed class ProvenanceStoreTests
|
||||
await store.Client.DeleteChatsAsync([copy], TOKEN);
|
||||
var entriesWithoutTheCopy = await store.Client.GetEntriesAsync([sent.Value, unsent.Value], TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(deleted, Is.EqualTo(1), "Only the entry of the chat goes, not the one of its copy, which keeps the same id.");
|
||||
Assert.That(entries.Keys, Is.EquivalentTo(new[] { sent.Value, unsent.Value }), "The copy still holds the entry.");
|
||||
Assert.That(entriesWithoutTheCopy.Keys, Is.EquivalentTo(new[] { sent.Value }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -257,13 +257,13 @@ public sealed class ProvenanceStoreTests
|
||||
var kept = await store.Client.GetEntriesAsync([entry!.Value], TOKEN);
|
||||
var deletedAfter = await store.Client.DeleteChatsAsync([chat], TOKEN, DateTimeOffset.UtcNow.AddMinutes(1));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(deletedBefore, Is.Zero);
|
||||
Assert.That(deletedExceptBefore, Is.Zero);
|
||||
Assert.That(kept.Keys, Is.EquivalentTo(new[] { entry.Value }), "The time is compared as stored text, which has to keep the order of time.");
|
||||
Assert.That(deletedAfter, Is.EqualTo(1));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -289,11 +289,11 @@ public sealed class ProvenanceStoreTests
|
||||
var sinceBefore = await store.Client.GetChatIdsAsync(TOKEN, DateTimeOffset.UtcNow.AddMinutes(-1));
|
||||
var sinceAfter = await store.Client.GetChatIdsAsync(TOKEN, DateTimeOffset.UtcNow.AddMinutes(1));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(sinceBefore, Is.EquivalentTo(new[] { chat }));
|
||||
Assert.That(sinceAfter, Is.Empty, "The time is compared as stored text, which has to keep the order of time.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -306,11 +306,11 @@ public sealed class ProvenanceStoreTests
|
||||
await store.Client.SetLastCleanupAsync(startedAt, TOKEN);
|
||||
var afterTheCleanup = await store.Client.GetLastCleanupAsync(TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(beforeAnyCleanup, Is.Null, "A store without a cleanup, e.g., one a restore brought back, needs every chat read.");
|
||||
Assert.That(afterTheCleanup, Is.EqualTo(startedAt));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -326,12 +326,12 @@ public sealed class ProvenanceStoreTests
|
||||
var copy = new SqliteProvenanceStoreClientImplementation("SQLite", copyPath, copyDirectory, string.Empty);
|
||||
var entries = await copy.GetEntriesAsync([id!.Value], TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(Path.GetFileName(copyPath), Is.EqualTo(SqliteProvenanceStoreClientImplementation.DATABASE_FILENAME), "A restore puts the copy where the store belongs, under the same name.");
|
||||
Assert.That(entries[id.Value].Content, Is.EqualTo("a passage"));
|
||||
Assert.That(entries[id.Value].SurroundingContent, Is.EqualTo(new[] { "around it" }));
|
||||
});
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -362,7 +362,7 @@ public sealed class ProvenanceStoreTests
|
||||
var chatIds = await store.Client.GetChatIdsAsync(TOKEN);
|
||||
var original = await backup.Client.GetEntriesAsync([passage.Value], TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(imported, Is.EqualTo(2));
|
||||
Assert.That(importedAgain, Is.Zero, "Entries the chat holds already stay as they are.");
|
||||
@@ -371,7 +371,7 @@ public sealed class ProvenanceStoreTests
|
||||
Assert.That(entries[passage.Value].SurroundingContent, Is.EqualTo(new[] { "around it" }));
|
||||
Assert.That(entries[passage.Value].CreatedAtUtc, Is.EqualTo(original[passage.Value].CreatedAtUtc), "The time says when the AI received the content, not when the chat came back.");
|
||||
Assert.That(chatIds, Is.EquivalentTo(new[] { importedChat }));
|
||||
});
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -409,7 +409,7 @@ public sealed class ProvenanceStoreTests
|
||||
|
||||
var lines = await ReadDisplayInfoAsync(store);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(lines["Stored items across all chats"], Is.EqualTo("4"));
|
||||
Assert.That(lines["Chats with stored items"], Is.EqualTo("2"));
|
||||
@@ -419,7 +419,7 @@ public sealed class ProvenanceStoreTests
|
||||
Assert.That(lines, Does.Not.ContainKey("Passages of data sources"), "A kind no chat received would only say zero.");
|
||||
Assert.That(lines["Journal mode"], Is.EqualTo("WAL"));
|
||||
Assert.That(lines["Schema version"], Does.StartWith("20261024000000_InitialProvenanceStore"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -429,12 +429,12 @@ public sealed class ProvenanceStoreTests
|
||||
|
||||
var lines = await ReadDisplayInfoAsync(store);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(lines["Stored items across all chats"], Is.EqualTo("0"));
|
||||
Assert.That(lines["Chats with stored items"], Is.EqualTo("0"));
|
||||
Assert.That(lines, Does.Not.ContainKey("Attached files"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static string CreateCopyDirectory() => Path.Combine(Path.GetTempPath(), $"ai-studio-provenance-copy-{Guid.NewGuid():N}");
|
||||
|
||||
Reference in new issue
Block a user