mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-04 13:47: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
@@ -36,12 +36,12 @@ public sealed class AttachingAFileAgainTests
|
||||
File.WriteAllText(path, "The board met on Monday and on Tuesday.");
|
||||
attachments.AttachInPlaceOfSameFile(FileAttachment.FromPath(path));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(attachments.Count(attachment => attachment.FilePath == path), Is.EqualTo(1), "The message would go with the old and the new text of the file.");
|
||||
Assert.That(attachments.Single(attachment => attachment.FilePath == path).ProvenanceId, Is.Null, "Attaching a file again means the file as it is now.");
|
||||
Assert.That(attachments, Does.Contain(otherFile));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -81,13 +81,13 @@ public sealed class AttachmentProvenanceTests
|
||||
var firstRequest = await this.PrepareAsync(message);
|
||||
var secondRequest = await this.PrepareAsync(message);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(message.FileAttachments.Single().ProvenanceId, Is.Not.Null);
|
||||
Assert.That(firstRequest, Does.Contain("The board met on Monday."));
|
||||
Assert.That(secondRequest, Is.EqualTo(firstRequest));
|
||||
Assert.That(this.runtime.ReadFiles, Has.Count.EqualTo(1), "Every request read every document anew, which a long chat with large documents paid for with every message.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -100,11 +100,11 @@ public sealed class AttachmentProvenanceTests
|
||||
await message.StoreAttachedDocumentsAsync(this.store.Client, Guid.NewGuid(), this.rustService, null, readChangedAgain: false);
|
||||
var prepared = await this.PrepareAsync(message);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(prepared, Does.Contain("The board met on Monday."), "Every request of the chat sends the same text, which is what the user can look at.");
|
||||
Assert.That(prepared, Does.Not.Contain("never met"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -119,13 +119,13 @@ public sealed class AttachmentProvenanceTests
|
||||
await message.StoreAttachedDocumentsAsync(this.store.Client, chatId, this.rustService, null, readChangedAgain: true);
|
||||
var prepared = await this.PrepareAsync(message);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(prepared, Does.Contain("and on Tuesday"), "The user chose that the AI works with the files as they are now.");
|
||||
Assert.That(message.FileAttachments.Single().ProvenanceId, Is.Not.EqualTo(firstId), "The text sent before belongs to what the AI received then.");
|
||||
Assert.That(message.FileAttachments.Single().Version, Is.EqualTo(FileVersion.Of(this.documentPath)));
|
||||
Assert.That(this.runtime.ReadFiles, Has.Count.EqualTo(2));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -138,12 +138,12 @@ public sealed class AttachmentProvenanceTests
|
||||
|
||||
await message.StoreAttachedDocumentsAsync(this.store.Client, chatId, this.rustService, null, readChangedAgain: true);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(message.HasDocumentsToStore(readChangedAgain: true), Is.False);
|
||||
Assert.That(message.FileAttachments.Single().ProvenanceId, Is.EqualTo(firstId));
|
||||
Assert.That(this.runtime.ReadFiles, Has.Count.EqualTo(1));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -155,11 +155,11 @@ public sealed class AttachmentProvenanceTests
|
||||
File.Delete(this.documentPath);
|
||||
var prepared = await this.PrepareAsync(message);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(message.HasDocumentsToStore(readChangedAgain: true), Is.False, "A file which is gone did not change: its stored text is all there is.");
|
||||
Assert.That(prepared, Does.Contain("The board met on Monday."));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -184,12 +184,12 @@ public sealed class AttachmentProvenanceTests
|
||||
var provenanceId = message.FileAttachments.Single().ProvenanceId!.Value;
|
||||
var entries = await this.store.Client.GetEntriesAsync([provenanceId], CancellationToken.None);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(entries[provenanceId].Content, Does.Contain(FakeRuntime.REDACTION_MARKER), "The store keeps what the AI received, not what the file holds.");
|
||||
Assert.That(entries[provenanceId].Content, Does.Not.Contain(SUSPICIOUS_PASSAGE));
|
||||
Assert.That(entries[provenanceId].Kind, Is.EqualTo(ProvenanceKind.ATTACHMENT));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -202,13 +202,13 @@ public sealed class AttachmentProvenanceTests
|
||||
|
||||
var prepared = await this.PrepareAsync(message);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(prepared, Does.Contain("The board met on Monday."));
|
||||
Assert.That(prepared, Does.Not.Contain(SUSPICIOUS_PASSAGE), "Every way of content to the AI goes through the filter, with the rules of today.");
|
||||
Assert.That(prepared, Does.Contain(FakeRuntime.REDACTION_MARKER));
|
||||
Assert.That(this.runtime.ReadFiles, Is.Empty, "The stored text is filtered, not the file read again.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -222,13 +222,13 @@ public sealed class AttachmentProvenanceTests
|
||||
await message.StoreAttachedDocumentsAsync(this.store.Client, chatId, this.rustService, null, readChangedAgain: false);
|
||||
|
||||
await using var context = this.store.CreateContext();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(message.HasDocumentsToStore(readChangedAgain: false), Is.False);
|
||||
Assert.That(message.FileAttachments.Single().ProvenanceId, Is.EqualTo(firstId));
|
||||
Assert.That(context.Entries.Count(), Is.EqualTo(1));
|
||||
Assert.That(this.runtime.ReadFiles, Has.Count.EqualTo(1));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -239,11 +239,11 @@ public sealed class AttachmentProvenanceTests
|
||||
await message.StoreAttachedDocumentsAsync(new NoProvenanceStoreClient("SQLite", "not available"), Guid.NewGuid(), this.rustService, null, readChangedAgain: false);
|
||||
var prepared = await this.PrepareAsync(message);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(message.FileAttachments.Single().ProvenanceId, Is.Null);
|
||||
Assert.That(prepared, Does.Contain("The board met on Monday."), "Without the store, a chat works as it always did.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -253,11 +253,11 @@ public sealed class AttachmentProvenanceTests
|
||||
|
||||
var prepared = await this.PrepareAsync(message);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(prepared, Does.Contain("The board met on Monday."), "A store which lost the entry, e.g., one restored from an older backup, must not cost the document.");
|
||||
Assert.That(this.runtime.ReadFiles, Has.Count.EqualTo(1));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -270,12 +270,12 @@ public sealed class AttachmentProvenanceTests
|
||||
await message.StoreAttachedDocumentsAsync(this.store.Client, Guid.NewGuid(), this.rustService, null, readChangedAgain: false);
|
||||
var chatsWithEntries = await this.store.Client.GetChatIdsAsync(CancellationToken.None);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(message.HasDocumentsToStore(readChangedAgain: false), Is.False, "An image goes to the AI as an image, not as a text to keep.");
|
||||
Assert.That(message.FileAttachments.Single().ProvenanceId, Is.Null);
|
||||
Assert.That(chatsWithEntries, Is.Empty);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -289,7 +289,7 @@ public sealed class AttachmentProvenanceTests
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var storedAttachments = document.RootElement.GetProperty("file_attachments");
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(attachment.Normalize().ProvenanceId, Is.EqualTo(attachment.ProvenanceId), "Every request normalizes its attachments first.");
|
||||
Assert.That(attachment.Normalize().Version, Is.EqualTo(attachment.Version), "Otherwise, a chat which reads changed files again would read every file again.");
|
||||
@@ -300,7 +300,7 @@ public sealed class AttachmentProvenanceTests
|
||||
Assert.That(transcript.WithFilePath(Path.Combine(this.directoryPath, "moved.md")).ProvenanceId, Is.EqualTo(transcript.ProvenanceId));
|
||||
Assert.That(loaded.FileAttachments[0].ProvenanceId, Is.EqualTo(attachment.ProvenanceId));
|
||||
Assert.That(storedAttachments[1].TryGetProperty("provenance_id", out _), Is.False, "An attachment never sent is stored as before, also in a chat template.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private ContentText MessageWithDocument() => new() { Text = "Summarize the minutes.", FileAttachments = [FileAttachment.FromPath(this.documentPath)] };
|
||||
|
||||
@@ -25,11 +25,11 @@ public sealed class ChatBlockActionsTests
|
||||
|
||||
var removed = WhileWriting(chat).TryRemove(chat, answer.Content!);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(removed, Is.False);
|
||||
Assert.That(chat.Blocks.Select(x => x.Content), Is.EqualTo(new[] { question.Content, answer.Content }), "The answer stays, since the job goes on writing into it.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -41,13 +41,13 @@ public sealed class ChatBlockActionsTests
|
||||
var blocks = chat.Blocks.ToList();
|
||||
var actions = WhileWriting(chat);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(actions.TryRemove(chat, firstQuestion.Content!), Is.False);
|
||||
Assert.That(actions.TryRemove(chat, firstAnswer.Content!), Is.False);
|
||||
Assert.That(actions.TryRemoveForRegenerate(chat, firstAnswer.Content!), Is.False);
|
||||
Assert.That(chat.Blocks, Is.EqualTo(blocks), "The job stores the conversation meanwhile, so no message may change, not even an older one.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -58,13 +58,13 @@ public sealed class ChatBlockActionsTests
|
||||
var chat = Chat(question, answer);
|
||||
var actions = WhileWriting(chat);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(actions.TryEditLastUserBlock(chat, question.Content!), Is.False);
|
||||
Assert.That(actions.TryEditLastBlock(chat, question.Content!), Is.False);
|
||||
Assert.That(chat.Blocks, Has.Count.EqualTo(2), "Both the question and the answer being written stay.");
|
||||
Assert.That(chat.Draft.IsEmpty, Is.True, "The question does not become the draft, since it could not be sent anyway.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -75,11 +75,11 @@ public sealed class ChatBlockActionsTests
|
||||
|
||||
var removed = WhileWriting(chat).TryRemoveForRegenerate(chat, answer.Content!);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(removed, Is.False, "Regenerating would leave a second answer next to the one being written.");
|
||||
Assert.That(chat.Blocks, Has.Count.EqualTo(2));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -90,11 +90,11 @@ public sealed class ChatBlockActionsTests
|
||||
|
||||
var rolledBack = WhileWriting(chat).TryRollBack(chat, firstAnswer.Content!);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(rolledBack, Is.False);
|
||||
Assert.That(chat.Blocks, Has.Count.EqualTo(4));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -106,11 +106,11 @@ public sealed class ChatBlockActionsTests
|
||||
|
||||
var removed = Done().TryRemove(chat, answer.Content!);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(removed, Is.True);
|
||||
Assert.That(chat.Blocks.Select(x => x.Content), Is.EqualTo(new[] { question.Content }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -122,11 +122,11 @@ public sealed class ChatBlockActionsTests
|
||||
|
||||
var removed = Done().TryRemoveForRegenerate(chat, answer.Content!);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(removed, Is.True);
|
||||
Assert.That(chat.Blocks.Select(x => x.Content), Is.EqualTo(new[] { question.Content }), "The question stays, to be answered anew.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -138,11 +138,11 @@ public sealed class ChatBlockActionsTests
|
||||
|
||||
var rolledBack = Done().TryRollBack(chat, firstAnswer.Content!);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(rolledBack, Is.True);
|
||||
Assert.That(chat.Blocks.Select(x => x.Content), Is.EqualTo(new[] { firstQuestion.Content, firstAnswer.Content }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -153,12 +153,12 @@ public sealed class ChatBlockActionsTests
|
||||
|
||||
var edited = Done().TryEditLastUserBlock(chat, question.Content!);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(edited, Is.True);
|
||||
Assert.That(chat.Blocks, Is.Empty, "The question goes back into the composer, and the answer to it goes.");
|
||||
Assert.That(chat.Draft.Text, Is.EqualTo("Question"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -169,12 +169,12 @@ public sealed class ChatBlockActionsTests
|
||||
|
||||
var edited = Done().TryEditLastBlock(chat, question.Content!);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(edited, Is.True);
|
||||
Assert.That(chat.Blocks, Is.Empty);
|
||||
Assert.That(chat.Draft.Text, Is.EqualTo("Question"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -185,12 +185,12 @@ public sealed class ChatBlockActionsTests
|
||||
|
||||
var edited = Done().TryEditLastUserBlock(chat, new ContentText { Text = "Question of another chat" });
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(edited, Is.False);
|
||||
Assert.That(chat.Blocks, Has.Count.EqualTo(2), "Removing the last answer only makes sense together with the question to it.");
|
||||
Assert.That(chat.Draft.IsEmpty, Is.True);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -208,11 +208,11 @@ public sealed class ChatBlockActionsTests
|
||||
|
||||
var removed = actions.TryRemove(chat, answer.Content!);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(removed, Is.True);
|
||||
Assert.That(askedChatIds, Is.EqualTo(new[] { chat.ChatId }), "Only the chat being changed decides.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -221,7 +221,7 @@ public sealed class ChatBlockActionsTests
|
||||
var actions = Done();
|
||||
var content = new ContentText { Text = "Question" };
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(actions.AreAllowed(null), Is.False);
|
||||
Assert.That(actions.TryRemove(null, content), Is.False);
|
||||
@@ -229,7 +229,7 @@ public sealed class ChatBlockActionsTests
|
||||
Assert.That(actions.TryRollBack(null, content), Is.False);
|
||||
Assert.That(actions.TryEditLastUserBlock(null, content), Is.False);
|
||||
Assert.That(actions.TryEditLastBlock(null, content), Is.False);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -62,11 +62,11 @@ public sealed class ChatDraftAutosaveTests
|
||||
storage.IsBusy = false;
|
||||
await storage.WaitForStoreAsync();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(storedWhileBusy, Is.Zero, "The AI job changes the conversation meanwhile, so storing it from elsewhere could read it in the middle of a change.");
|
||||
Assert.That(storage.StoredChats, Is.EqualTo(new[] { chat }), "Once the answer is done, the change must still reach the disk.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -46,12 +46,12 @@ public sealed class ChatDraftTests
|
||||
var added = draft.AddAttachments([TRANSCRIPT]);
|
||||
var addedAgain = draft.AddAttachments([TRANSCRIPT with { FileSizeBytes = 1024 }]);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(added, Is.True);
|
||||
Assert.That(addedAgain, Is.False, "The same file counts once, even in a newer state.");
|
||||
Assert.That(draft.FileAttachments, Is.EqualTo(new FileAttachment[] { TRANSCRIPT }), "The composer would show the transcript twice otherwise.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -74,11 +74,11 @@ public sealed class ChatDraftTests
|
||||
|
||||
draft.ReplaceAttachments(attachment => attachment with { FilePath = $"/moved{attachment.FilePath}" });
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(draft.FileAttachments.Select(attachment => attachment.FilePath), Is.EqualTo(new[] { "/moved/documents/agenda.pdf", "/moved/data/tempChats/meeting-transcript-0001.md" }));
|
||||
Assert.That(draft.MediaTranscripts, Has.Count.EqualTo(1), "A transcript stays a transcript, so removing it from the composer still deletes its file.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -90,11 +90,11 @@ public sealed class ChatDraftTests
|
||||
|
||||
draft.RestoreFromMessage(new ContentText { Text = "Summarize the minutes", FileAttachments = [MINUTES] });
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(draft.Text, Is.EqualTo("Summarize the minutes"), "The message to edit replaces the older draft.");
|
||||
Assert.That(draft.FileAttachments, Is.EqualTo(new[] { MINUTES, TRANSCRIPT }), "The draft is the only owner of its transcripts, so dropping them would leave their files behind.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -107,11 +107,11 @@ public sealed class ChatDraftTests
|
||||
|
||||
draft.Clear();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(draft.IsEmpty, Is.True, "The attachments belong to the sent message now, and a transcript has only one owner.");
|
||||
Assert.That(draft.PreviewedTexts, Is.Empty, "A text the preview read went with the message, or is of no use to the next one.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -126,13 +126,13 @@ public sealed class ChatDraftTests
|
||||
draft.NotePreviewedText(otherFile);
|
||||
var replacedBySecond = draft.NotePreviewedText(secondPreview);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(replacedByFirst, Is.Null);
|
||||
Assert.That(replacedBySecond, Is.EqualTo(firstPreview.ProvenanceId), "Nothing refers to the text of the first preview any longer, so the caller removes it from the store.");
|
||||
Assert.That(draft.PreviewedTexts, Is.EquivalentTo(new[] { otherFile, secondPreview }));
|
||||
Assert.That(draft.GetPreviewedText(AGENDA.FilePath), Is.EqualTo(secondPreview));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -148,12 +148,12 @@ public sealed class ChatDraftTests
|
||||
draft.MirrorAttachments([MINUTES]);
|
||||
var forgotten = draft.ForgetPreviewsOfDetachedFiles();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(forgotten, Is.EqualTo(new[] { agendaPreview.ProvenanceId }), "Nothing refers to the text any longer, so the caller removes it from the store.");
|
||||
Assert.That(draft.PreviewedTexts, Is.EqualTo(new[] { minutesPreview }));
|
||||
Assert.That(draft.ForgetPreviewsOfDetachedFiles(), Is.Empty);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -178,12 +178,12 @@ public sealed class ChatDraftTests
|
||||
|
||||
var loaded = JsonSerializer.Deserialize<ChatThread>(JsonSerializer.Serialize(thread, WorkspaceBehaviour.JSON_OPTIONS), WorkspaceBehaviour.JSON_OPTIONS)!;
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(loaded.Draft.Text, Is.EqualTo("Summarize the meeting"));
|
||||
Assert.That(loaded.Draft.FileAttachments, Is.EqualTo(new[] { AGENDA, CHART, STAGED_TRANSCRIPT }), "A transcript loaded as a plain document would no longer be managed, and its file would never be deleted.");
|
||||
Assert.That(loaded.Draft.MediaTranscripts.Single().IsStaged, Is.True, "A staged transcript has to move into the chat directory once the chat is stored.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -216,12 +216,12 @@ public sealed class ChatDraftTests
|
||||
var loaded = JsonSerializer.Deserialize<ChatThread>(CHAT_WITH_PENDING_TRANSCRIPTS, WorkspaceBehaviour.JSON_OPTIONS)!;
|
||||
var stored = JsonSerializer.Serialize(loaded, WorkspaceBehaviour.JSON_OPTIONS);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(loaded.Draft.FileAttachments, Is.EqualTo(new FileAttachment[] { TRANSCRIPT }), "Opening the chat has to bring the transcript back into the composer.");
|
||||
Assert.That(stored, Does.Not.Contain("pending_media_transcripts"), "The draft holds the transcript now, so storing the chat must not write the former list again.");
|
||||
Assert.That(stored, Does.Contain("\"draft\""));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -56,14 +56,14 @@ public sealed class ChatRestrictionsTests
|
||||
|
||||
foreach (var combined in new[] { fromDataSource.StricterOf(fromMailbox), fromMailbox.StricterOf(fromDataSource) })
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(combined.ProviderConfidence, Is.EqualTo(ConfidenceLevel.HIGH));
|
||||
Assert.That(combined.DataSecurity, Is.EqualTo(DataSourceSecurity.SELF_HOSTED), "Data for self-hosted providers only keeps them as the only choice, whichever side brings it.");
|
||||
Assert.That(combined.OutboundData.Restriction, Is.EqualTo(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES));
|
||||
Assert.That(combined.OutboundData.Reference, Is.EqualTo(MAILBOX_ID), "The source which demands the strictest restriction is the one to name.");
|
||||
Assert.That(combined.WebAddressesWrittenByModels, Is.EquivalentTo(new[] { RequestKey("https://example.org/report"), RequestKey("https://example.org/minutes") }), "What models wrote on either side never counts as given.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,11 +72,11 @@ public sealed class ChatRestrictionsTests
|
||||
{
|
||||
var restrictions = ChatRestrictions.NONE.WithWebAddressesWrittenByModelIn("The report is on https://EXAMPLE.org/report#summary, and the minutes are on http://10.20.30.40:8000/minutes/. Write to ftp://example.org/upload.");
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(restrictions.WebAddressesWrittenByModels, Is.EquivalentTo(new[] { RequestKey("https://example.org/report"), RequestKey("http://10.20.30.40:8000/minutes/") }), "Only addresses a web page could be read from count, compared as the server gets asked for them.");
|
||||
Assert.That(ChatRestrictions.NONE.WebAddressesWrittenByModels, Is.Empty, "Adding addresses leaves NONE as it is.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -84,11 +84,11 @@ public sealed class ChatRestrictionsTests
|
||||
{
|
||||
var restrictions = new ChatRestrictions(ConfidenceLevel.MEDIUM, DataSourceSecurity.ALLOW_ANY, new(OutboundDataRestriction.ONLY_LINKS_FROM_CHAT, OutboundDataOrigin.DATA_SOURCE, DATA_SOURCE_ID), []).WithWebAddressesWrittenByModelIn("See https://example.org/report.");
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
AssertSame(restrictions.StricterOf(ChatRestrictions.NONE), restrictions);
|
||||
AssertSame(ChatRestrictions.NONE.StricterOf(restrictions), restrictions);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -131,11 +131,11 @@ public sealed class ChatRestrictionsTests
|
||||
|
||||
chatToStart.RequireRestrictions(new(ConfidenceLevel.HIGH, DataSourceSecurity.SELF_HOSTED, new(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES, OutboundDataOrigin.MAILBOX, MAILBOX_ID), []));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
AssertSame(chatToStart.CaptureRestrictions(), new(ConfidenceLevel.HIGH, DataSourceSecurity.SELF_HOSTED, new(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES, OutboundDataOrigin.MAILBOX, MAILBOX_ID), []));
|
||||
AssertSame(assistantThread.CaptureRestrictions(), ChatRestrictions.NONE);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -148,11 +148,11 @@ public sealed class ChatRestrictionsTests
|
||||
chat.RequireProviderConfidence(ConfidenceLevel.HIGH);
|
||||
chat.RequireDataSecurity(DataSourceSecurity.SELF_HOSTED);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(snapshot.ProviderConfidence, Is.EqualTo(ConfidenceLevel.LOW), "Content which left the chat does not hold what the chat read afterward.");
|
||||
Assert.That(snapshot.DataSecurity, Is.EqualTo(DataSourceSecurity.NOT_SPECIFIED));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -162,12 +162,12 @@ public sealed class ChatRestrictionsTests
|
||||
var selfHostedProvider = this.AddProvider(SELF_HOSTED_PROVIDER_ID, LLMProviders.SELF_HOSTED, isSelfHosted: true);
|
||||
var restrictions = ChatRestrictions.NONE with { DataSecurity = DataSourceSecurity.SELF_HOSTED };
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(restrictions.IsLLMProviderAllowed(cloudProvider), Is.False);
|
||||
Assert.That(restrictions.IsLLMProviderAllowed(selfHostedProvider), Is.True);
|
||||
Assert.That(ChatRestrictions.NONE.IsLLMProviderAllowed(cloudProvider), Is.True, "Content which demands nothing may go to any provider.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -178,11 +178,11 @@ public sealed class ChatRestrictionsTests
|
||||
var higherLevels = Enum.GetValues<ConfidenceLevel>().Where(candidate => candidate > level).ToList();
|
||||
Assume.That(higherLevels, Is.Not.Empty, "The provider has the highest level there is, so no content can demand more of it.");
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That((ChatRestrictions.NONE with { ProviderConfidence = level }).IsLLMProviderAllowed(provider), Is.True);
|
||||
Assert.That((ChatRestrictions.NONE with { ProviderConfidence = higherLevels.Min() }).IsLLMProviderAllowed(provider), Is.False);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -194,21 +194,24 @@ public sealed class ChatRestrictionsTests
|
||||
chat.RequireDataSecurity(DataSourceSecurity.SELF_HOSTED);
|
||||
ChatThread? newChat = null;
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(chat.IsLLMProviderAllowed(cloudProvider), Is.EqualTo(chat.CaptureRestrictions().IsLLMProviderAllowed(cloudProvider)));
|
||||
Assert.That(chat.IsLLMProviderAllowed(selfHostedProvider), Is.EqualTo(chat.CaptureRestrictions().IsLLMProviderAllowed(selfHostedProvider)));
|
||||
Assert.That(newChat.IsLLMProviderAllowed(cloudProvider), Is.True, "A new chat demands nothing yet.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssertSame(ChatRestrictions actual, ChatRestrictions expected) => Assert.Multiple(() =>
|
||||
private static void AssertSame(ChatRestrictions actual, ChatRestrictions expected)
|
||||
{
|
||||
Assert.That(actual.ProviderConfidence, Is.EqualTo(expected.ProviderConfidence));
|
||||
Assert.That(actual.DataSecurity, Is.EqualTo(expected.DataSecurity));
|
||||
Assert.That(actual.OutboundData, Is.EqualTo(expected.OutboundData));
|
||||
Assert.That(actual.WebAddressesWrittenByModels, Is.EquivalentTo(expected.WebAddressesWrittenByModels));
|
||||
});
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(actual.ProviderConfidence, Is.EqualTo(expected.ProviderConfidence));
|
||||
Assert.That(actual.DataSecurity, Is.EqualTo(expected.DataSecurity));
|
||||
Assert.That(actual.OutboundData, Is.EqualTo(expected.OutboundData));
|
||||
Assert.That(actual.WebAddressesWrittenByModels, Is.EquivalentTo(expected.WebAddressesWrittenByModels));
|
||||
}
|
||||
}
|
||||
|
||||
private static string RequestKey(string address) => WebAddresses.CreateRequestKey(new Uri(address));
|
||||
|
||||
|
||||
@@ -41,11 +41,11 @@ public sealed class ChatThreadAugmentedDataTests
|
||||
|
||||
var loaded = JsonSerializer.Deserialize<ChatThread>(CHAT_WITH_AUGMENTED_DATA, WorkspaceBehaviour.JSON_OPTIONS);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(loaded, Is.Not.Null, "An older chat file must still load.");
|
||||
Assert.That(loaded!.AugmentedData, Is.Empty, "It belonged to a request long over; the next message retrieves anew.");
|
||||
Assert.That(loaded.Name, Is.EqualTo("An older chat"));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,14 +67,14 @@ public sealed class ChatThreadEditTests
|
||||
thread.Remove(answer.Content!);
|
||||
composer.RestoreFromDraft(thread.Draft);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(thread.Blocks, Is.Empty, "Both the question and the stopped answer leave the chat.");
|
||||
Assert.That(File.Exists(transcript.FilePath), Is.True, "The composer still carries the transcript, so its file has to stay.");
|
||||
Assert.That(composer.FileAttachments.Single().Exists, Is.True, "The edited message goes out with the transcript, not with a reference to a deleted file.");
|
||||
Assert.That(thread.Draft.MediaTranscripts.Select(x => x.FilePath), Is.EqualTo(new[] { transcript.FilePath }), "The draft owns the transcript now: opening the chat again brings it back, and removing it from the composer deletes the file.");
|
||||
Assert.That(thread.Draft.Text, Is.EqualTo("Summarize the meeting"), "The message being edited is the draft now, its text included.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -101,11 +101,11 @@ public sealed class ChatThreadEditTests
|
||||
|
||||
thread.Remove(question.Content!);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(File.Exists(transcript.FilePath), Is.False, "Nothing refers to the transcript of a deleted message any longer, so its file has to go.");
|
||||
Assert.That(thread.Draft.IsEmpty, Is.True, "A deleted message hands nothing over to the draft.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -121,12 +121,12 @@ public sealed class ChatThreadEditTests
|
||||
|
||||
thread.RemoveForEditing(new ContentText { Text = "Question of another chat", FileAttachments = [transcript] });
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(thread.Blocks, Has.Count.EqualTo(2), "The message to edit is not part of this chat, so no block goes.");
|
||||
Assert.That(thread.Draft.IsEmpty, Is.True, "A message this chat does not hold never becomes its draft.");
|
||||
Assert.That(File.Exists(transcript.FilePath), Is.True);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -38,12 +38,12 @@ public sealed class ChatThreadFilePathTests
|
||||
chat.RewriteFilePaths(Move);
|
||||
|
||||
var message = (ContentText)chat.Blocks[0].Content!;
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(message.FileAttachments.Select(attachment => attachment.FilePath), Is.EqualTo(new[] { New("report.pdf"), New("meeting.md"), New("chart.png") }));
|
||||
Assert.That(photo.Source, Is.EqualTo(New("photo.jpg")), "The model gets such an image from its path.");
|
||||
Assert.That(chat.Draft.FileAttachments.Select(attachment => attachment.FilePath), Is.EqualTo(new[] { New("call.md") }), "The draft is the one place every path rewrite once forgot.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -60,11 +60,11 @@ public sealed class ChatThreadFilePathTests
|
||||
|
||||
chat.RewriteFilePaths(Move);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(((ContentText)chat.Blocks[0].Content!).FileAttachments.Single(), Is.SameAs(elsewhere));
|
||||
Assert.That(linked.Source, Is.EqualTo("https://example.org/chart.png"), "Only an image held by a local path has a path to rewrite.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -78,14 +78,14 @@ public sealed class ChatThreadFilePathTests
|
||||
var movedPicture = picture.WithFilePath(New("chart.png"));
|
||||
var movedDocument = document.WithFilePath(New("report.pdf"));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(movedTranscript, Is.EqualTo(new ManagedTranscriptAttachment("meeting.md", New("meeting.md"), 512, "meeting.mp3", true)), "A transcript has to stay a transcript, or deleting the chat leaves its file behind.");
|
||||
Assert.That(movedPicture, Is.TypeOf<FileAttachmentImage>());
|
||||
Assert.That(((FileAttachmentImage)movedPicture).Source, Is.EqualTo(New("chart.png")), "The model gets the image from its source, not from its path.");
|
||||
Assert.That(movedPicture.FilePathAsUrl, Is.EqualTo(new FileAttachmentImage("chart.png", New("chart.png"), 2048).FilePathAsUrl), "The preview of the image loads it from there.");
|
||||
Assert.That(movedDocument, Is.EqualTo(new FileAttachment(FileAttachmentType.DOCUMENT, "report.pdf", New("report.pdf"), 1024)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static string Old(string fileName) => Path.Combine(OLD_DIRECTORY, fileName);
|
||||
|
||||
@@ -100,12 +100,12 @@ public sealed class ChatThreadOutboundDataRestrictionTests
|
||||
[TestCase(OutboundDataRestriction.UNRESTRICTED)]
|
||||
public void ARestrictionThisVersionDoesNotKnowCountsAsTheStrictest(OutboundDataRestriction known)
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(UNKNOWN_RESTRICTION.StricterOf(known), Is.EqualTo(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES));
|
||||
Assert.That(known.StricterOf(UNKNOWN_RESTRICTION), Is.EqualTo(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES));
|
||||
Assert.That(UNKNOWN_RESTRICTION.StricterOf(UNKNOWN_RESTRICTION), Is.EqualTo(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -28,7 +28,7 @@ public sealed class ChatThreadReportedHistoryTests
|
||||
|
||||
var history = thread.ReportedHistoryFor(MODEL);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(history.IsKnown, Is.True);
|
||||
Assert.That(history.PromptTokens, Is.EqualTo(1200));
|
||||
@@ -38,7 +38,7 @@ public sealed class ChatThreadReportedHistoryTests
|
||||
// would include the reasoning the next request never carries:
|
||||
//
|
||||
Assert.That(history.LastAnswer, Is.EqualTo("Answer 2"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -54,12 +54,12 @@ public sealed class ChatThreadReportedHistoryTests
|
||||
var thread = Thread(Question(1), Block(ChatRole.AI, reasoningOnly, 2));
|
||||
|
||||
var history = thread.ReportedHistoryFor(MODEL);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(history.IsKnown, Is.True);
|
||||
Assert.That(history.PromptTokens, Is.EqualTo(1200));
|
||||
Assert.That(history.LastAnswer, Is.Empty);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -135,12 +135,12 @@ public sealed class ChatThreadReportedHistoryTests
|
||||
thread.Remove(lastAnswer.Content!);
|
||||
|
||||
var history = thread.ReportedHistoryFor(MODEL);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(history.IsKnown, Is.True, "The first answer is the last block again, with exactly the blocks it was reported for.");
|
||||
Assert.That(history.PromptTokens, Is.EqualTo(1200));
|
||||
Assert.That(history.LastAnswer, Is.EqualTo("Answer 2"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -152,12 +152,12 @@ public sealed class ChatThreadReportedHistoryTests
|
||||
thread.RollBackTo(firstAnswer.Content!);
|
||||
|
||||
var history = thread.ReportedHistoryFor(MODEL);
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(history.IsKnown, Is.True);
|
||||
Assert.That(history.PromptTokens, Is.EqualTo(1200));
|
||||
Assert.That(history.LastAnswer, Is.EqualTo("Answer 2"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static ChatThread Thread(params ContentBlock[] blocks) => new()
|
||||
|
||||
@@ -24,13 +24,13 @@ public sealed class ChatThreadRestrictionsOfTests
|
||||
|
||||
assistantThread.RequireRestrictionsOf(researchThread);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(assistantThread.RequiredProviderConfidence, Is.EqualTo(ConfidenceLevel.HIGH));
|
||||
Assert.That(assistantThread.DataSecurity, Is.EqualTo(DataSourceSecurity.SELF_HOSTED));
|
||||
Assert.That(assistantThread.RequiredOutboundDataRestriction.Restriction, Is.EqualTo(OutboundDataRestriction.ONLY_LINKS_FROM_CHAT));
|
||||
Assert.That(assistantThread.RequiredOutboundDataRestriction.Reference, Is.EqualTo(DATA_SOURCE_ID));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -60,12 +60,12 @@ public sealed class ChatThreadRestrictionsOfTests
|
||||
|
||||
assistantThread.RequireRestrictionsOf(researchThread);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(assistantThread.RequiredProviderConfidence, Is.EqualTo(ConfidenceLevel.HIGH));
|
||||
Assert.That(assistantThread.DataSecurity, Is.EqualTo(DataSourceSecurity.SELF_HOSTED));
|
||||
Assert.That(assistantThread.RequiredOutboundDataRestriction.Restriction, Is.EqualTo(OutboundDataRestriction.ONLY_CONFIGURED_SERVICES));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -75,12 +75,12 @@ public sealed class ChatThreadRestrictionsOfTests
|
||||
|
||||
assistantThread.RequireRestrictionsOf(new ChatThread());
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(assistantThread.RequiredProviderConfidence, Is.EqualTo(ConfidenceLevel.NONE));
|
||||
Assert.That(assistantThread.DataSecurity, Is.EqualTo(DataSourceSecurity.NOT_SPECIFIED));
|
||||
Assert.That(assistantThread.RequiredOutboundDataRestriction, Is.EqualTo(OutboundDataRequirement.NONE));
|
||||
Assert.That(assistantThread.WebAddressesWrittenByModels, Is.Empty);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,11 +41,11 @@ public sealed class ChatThreadRollbackTests
|
||||
|
||||
var rolledBack = thread.RollBackTo(answer.Content!);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(rolledBack, Is.True, "Two blocks came after the answer, so the rollback removed something.");
|
||||
Assert.That(Texts(thread), Is.EqualTo(new[] { "First question", "First answer" }), "The hidden prompt goes with the answer to it, although the user never saw it.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -117,7 +117,7 @@ public sealed class ChatThreadRollbackTests
|
||||
|
||||
thread.RollBackTo(answer.Content!);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(thread.AugmentedData, Is.Empty, "Nobody knows whether the retrieved data belongs to a kept or to a removed message, so it must not reach the next system prompt.");
|
||||
Assert.That(thread.AISelectedDataSources, Is.Empty, "The data sources an agent picked belong to the same retrieval as the data.");
|
||||
@@ -125,7 +125,7 @@ public sealed class ChatThreadRollbackTests
|
||||
Assert.That(options.DisableDataSources, Is.False);
|
||||
Assert.That(options.AutomaticDataSourceSelection, Is.True);
|
||||
Assert.That(options.PreselectedDataSourceIds, Is.EqualTo(new[] { "handbook" }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -150,12 +150,12 @@ public sealed class ChatThreadRollbackTests
|
||||
|
||||
thread.RollBackTo(answer.Content!);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(thread.DataSecurity, Is.EqualTo(DataSourceSecurity.SELF_HOSTED), "The confidential data was seen by this chat, so no cloud provider may continue it.");
|
||||
Assert.That(thread.RequiredProviderConfidence, Is.EqualTo(ConfidenceLevel.HIGH), "The confidence the data demanded stays, although the message which brought it in is gone.");
|
||||
Assert.That(thread.RequiredOutboundDataRestriction, Is.EqualTo(restriction), "The mail content was read by this chat, so it may still carry it out no further than before.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -194,13 +194,13 @@ public sealed class ChatThreadRollbackTests
|
||||
|
||||
private static void AssertUnchanged(ChatThread thread, bool rolledBack, params string[] expectedTexts)
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(rolledBack, Is.False, "Nothing came after the content, so there was nothing to roll back.");
|
||||
Assert.That(Texts(thread), Is.EqualTo(expectedTexts), "A rollback without anything to remove leaves the blocks alone.");
|
||||
Assert.That(thread.AugmentedData, Is.EqualTo("A chunk from the handbook"), "A rollback without anything to remove keeps the last retrieval, because it still belongs to the last message.");
|
||||
Assert.That(thread.AISelectedDataSources, Has.Count.EqualTo(1));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static ContentBlock Block(ChatRole role, string text, int minute, bool hidden = false) => new()
|
||||
|
||||
@@ -29,12 +29,12 @@ public sealed class ChatThreadWebAddressTests
|
||||
{
|
||||
var thread = Thread(Block(ChatRole.USER, "Please summarize https://example.org/report for me.", 1));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://example.org/report")), Is.True);
|
||||
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://EXAMPLE.org/report#summary")), Is.True, "The server gets the same request.");
|
||||
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://example.org/report?budget=2026")), Is.False, "The query would carry something the user never wrote.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -43,12 +43,12 @@ public sealed class ChatThreadWebAddressTests
|
||||
// An internal service such as a simple Python web server for the results of a data science run:
|
||||
var thread = Thread(Block(ChatRole.USER, "The results of run 17 are on http://10.20.30.40:8000/run-17/, please compare them with run 16.", 1));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("http://10.20.30.40:8000/run-17/")), Is.True);
|
||||
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("http://10.20.30.40:8000/run-16/")), Is.False, "The user named run 16, but not its address.");
|
||||
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://10.20.30.40:8000/run-17/")), Is.False, "Another scheme is another request.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -89,11 +89,11 @@ public sealed class ChatThreadWebAddressTests
|
||||
using var rustService = new RustService("1", "unused");
|
||||
thread.PrepareSystemPrompt(new SettingsManager(NullLogger<SettingsManager>.Instance, rustService));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://handbook.example.org/")), Is.True);
|
||||
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://intranet.example.org/travel")), Is.True, "The RAG process found it in a data source, the model did not choose it.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -131,11 +131,11 @@ public sealed class ChatThreadWebAddressTests
|
||||
var thread = Thread(Block(ChatRole.USER, "Make slides of this: the details are on https://example.org/chosen, and our handbook is on https://example.org/handbook.", 1));
|
||||
thread.RequireRestrictions(ChatRestrictions.NONE.WithWebAddressesWrittenByModelIn("The details are on https://example.org/chosen."));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://example.org/chosen")), Is.False, "A model wrote it, so it could carry anything of the chat it came from.");
|
||||
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://example.org/handbook")), Is.True, "Another address in the same message still counts.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -151,11 +151,11 @@ public sealed class ChatThreadWebAddressTests
|
||||
thread.RequireRestrictions(ChatRestrictions.NONE.WithWebAddressesWrittenByModelIn("See https://example.org/chosen and https://example.org/sources/2026."));
|
||||
thread.RuntimeWebAddressesFromTools.Add(WebAddresses.CreateRequestKey(new Uri("https://example.org/chosen")));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://example.org/chosen")), Is.True, "A tool returned it in this chat, so it was not only a model which wrote it.");
|
||||
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://example.org/sources/2026")), Is.True, "A document the user attached holds it.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -177,11 +177,11 @@ public sealed class ChatThreadWebAddressTests
|
||||
|
||||
copy.RequireRestrictions(ChatRestrictions.NONE.WithWebAddressesWrittenByModelIn("See https://example.org/chosen."));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(copy.IsWebAddressGivenToTheModel(new Uri("https://example.org/chosen")), Is.False);
|
||||
Assert.That(thread.IsWebAddressGivenToTheModel(new Uri("https://example.org/chosen")), Is.True, "What the copy took over must not reach the chat it was copied from.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static ChatThread Thread(params ContentBlock[] blocks) => new() { Blocks = [..blocks] };
|
||||
|
||||
@@ -21,13 +21,13 @@ public sealed class ContentBlockApartFromChatTests
|
||||
|
||||
var template = new ChatThread { ChatId = Guid.NewGuid(), Blocks = chat.Blocks.Select(block => block.CloneApartFromChat()).ToList() };
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(template.CollectProvenanceIds(), Is.Empty, "The entries belong to the chat and are deleted with it.");
|
||||
Assert.That(template.Blocks.Select(block => ((ContentText)block.Content!).Text), Is.EqualTo(new[] { "Summarize the minutes.", "The board met on Monday." }));
|
||||
Assert.That(template.Blocks[0].Content!.FileAttachments.Select(attachment => attachment.FilePath), Is.EqualTo(new[] { "/tmp/minutes.txt" }), "The file is read anew by every chat it reaches.");
|
||||
Assert.That(chat.CollectProvenanceIds(), Has.Count.EqualTo(2), "The chat itself keeps what it received.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -15,11 +15,11 @@ public sealed class ContentPresentationTests
|
||||
|
||||
presentation.Data = JsonSerializer.SerializeToElement(new { heading = "Second" });
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(firstHashCode, Is.Not.Zero);
|
||||
Assert.That(presentation.GetDataHashCode(), Is.Not.EqualTo(firstHashCode));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -53,12 +53,12 @@ public sealed class ConversationPartsTests
|
||||
|
||||
var parts = ConversationParts.Of(thread, "You are helpful.", "And of Italy?", null, imagesAreSent: true, toolDefinitions: null);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(parts.Texts, Is.EqualTo(new[] { "You are helpful.", "What is the capital of France?", "Paris." }));
|
||||
Assert.That(parts.DraftText, Is.EqualTo("And of Italy?"));
|
||||
Assert.That(parts.GrowingTexts, Is.Empty, "The draft is a part of its own, and it stands nowhere else.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -75,12 +75,12 @@ public sealed class ConversationPartsTests
|
||||
|
||||
var parts = ConversationParts.Of(thread, string.Empty, "a draft", null, imagesAreSent: true, toolDefinitions: null);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(parts.Texts, Is.EqualTo(new[] { "A question." }));
|
||||
Assert.That(parts.GrowingTexts, Is.EqualTo(new[] { "The answer so far" }));
|
||||
Assert.That(parts.DraftText, Is.EqualTo("a draft"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -91,11 +91,11 @@ public sealed class ConversationPartsTests
|
||||
|
||||
var parts = ConversationParts.Of(new() { Blocks = [finished] }, string.Empty, string.Empty, null, imagesAreSent: true, toolDefinitions: null);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(parts.Texts, Is.EqualTo(new[] { "The whole answer." }));
|
||||
Assert.That(parts.GrowingTexts, Is.Empty);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -134,11 +134,11 @@ public sealed class ConversationPartsTests
|
||||
{
|
||||
var parts = ConversationParts.Of(null, string.Empty, "Hello", null, imagesAreSent: true, toolDefinitions: null);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(parts.Texts, Is.Empty);
|
||||
Assert.That(parts.DraftText, Is.EqualTo("Hello"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase("")]
|
||||
@@ -147,12 +147,12 @@ public sealed class ConversationPartsTests
|
||||
{
|
||||
var parts = ConversationParts.Of(null, string.Empty, draft, null, imagesAreSent: true, toolDefinitions: null);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(parts.Texts, Is.Empty);
|
||||
Assert.That(parts.GrowingTexts, Is.Empty);
|
||||
Assert.That(parts.DraftText, Is.Empty);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -169,11 +169,11 @@ public sealed class ConversationPartsTests
|
||||
|
||||
var parts = ConversationParts.Of(new() { Blocks = [empty] }, string.Empty, string.Empty, null, imagesAreSent: true, toolDefinitions: null);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(parts.Texts, Is.Empty);
|
||||
Assert.That(parts.Documents, Is.Empty);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -191,11 +191,11 @@ public sealed class ConversationPartsTests
|
||||
|
||||
var parts = ConversationParts.Of(new() { Blocks = [block] }, string.Empty, "And this one.", [FileAttachment.FromPath(draft)], imagesAreSent: true, toolDefinitions: null);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(parts.Documents.Select(document => document.FileName), Is.EqualTo(new[] { "older.txt" }));
|
||||
Assert.That(parts.DraftDocuments.Select(document => document.FileName), Is.EqualTo(new[] { "draft.txt" }), "Both count, each in the part it belongs to.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -219,11 +219,11 @@ public sealed class ConversationPartsTests
|
||||
|
||||
var parts = ConversationParts.Of(null, string.Empty, "Look", [FileAttachment.FromPath(document), FileAttachment.FromPath(image)], imagesAreSent: true, toolDefinitions: null);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(parts.DraftDocuments.Select(entry => entry.FileName), Is.EqualTo(new[] { "notes.txt" }));
|
||||
Assert.That(parts.DraftImages, Is.EqualTo(1));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -240,11 +240,11 @@ public sealed class ConversationPartsTests
|
||||
|
||||
var parts = ConversationParts.Of(new() { Blocks = [block] }, string.Empty, "And at this.", [FileAttachment.FromPath(waiting)], imagesAreSent: true, toolDefinitions: null);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(parts.Images, Is.EqualTo(1));
|
||||
Assert.That(parts.DraftImages, Is.EqualTo(1));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -275,12 +275,12 @@ public sealed class ConversationPartsTests
|
||||
|
||||
var parts = ConversationParts.Of(new() { Blocks = [running] }, string.Empty, string.Empty, null, imagesAreSent: true, toolDefinitions: null);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(parts.Texts, Is.Empty);
|
||||
Assert.That(parts.ToolConversation, Is.EqualTo(new[] { "What the web search found.", "What the page said." }));
|
||||
Assert.That(parts.GrowingTexts, Is.Empty, "The tool conversation is a part of its own, so that its share can be named.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -314,11 +314,11 @@ public sealed class ConversationPartsTests
|
||||
|
||||
var parts = ConversationParts.Of(new() { Blocks = [answered] }, string.Empty, string.Empty, null, imagesAreSent: true, toolDefinitions: null);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(parts.Texts, Is.EqualTo(new[] { "Here is what I found." }));
|
||||
Assert.That(parts.ToolConversation, Is.Empty);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -335,7 +335,7 @@ public sealed class ConversationPartsTests
|
||||
Tool("read_web_page", "Reads one page.", """{"type":"string"}"""),
|
||||
]);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(parts.Texts, Is.EqualTo(new[]
|
||||
{
|
||||
@@ -344,7 +344,7 @@ public sealed class ConversationPartsTests
|
||||
}));
|
||||
|
||||
Assert.That(parts.GrowingTexts, Is.Empty);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -131,11 +131,11 @@ public sealed class ConversationTokenTrackerTests
|
||||
tracker.Start();
|
||||
await Task.Delay(1_000);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(runs, Is.GreaterThanOrEqualTo(2));
|
||||
Assert.That(asked, Is.EqualTo(runs));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -155,11 +155,11 @@ public sealed class ConversationTokenTrackerTests
|
||||
var afterDisposal = runs;
|
||||
await Task.Delay(400);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(afterDisposal, Is.GreaterThan(0), "The tracker never ran, so this proves nothing about stopping it.");
|
||||
Assert.That(runs, Is.EqualTo(afterDisposal));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -64,11 +64,11 @@ public sealed class ConversationTokensTests
|
||||
ImageLimits = new ImageLimits(8, 100),
|
||||
};
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(counted.TooManyImages, Is.True);
|
||||
Assert.That(counted.ImageLimits.MaxInOneMessage, Is.EqualTo(8));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -111,11 +111,11 @@ public sealed class ConversationTokensTests
|
||||
|
||||
var estimated = reported with { HistoryIsReported = false };
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(reported.UncountedImages, Is.EqualTo(1), "The provider counted the two which were sent.");
|
||||
Assert.That(estimated.UncountedImages, Is.EqualTo(3), "Without a report, nobody counted any of them.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -133,11 +133,11 @@ public sealed class ConversationTokensTests
|
||||
ImageLimits = new ImageLimits(8, null),
|
||||
};
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(counted.UncountedImages, Is.Zero);
|
||||
Assert.That(counted.TooManyImages, Is.True);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -43,12 +43,12 @@ public sealed class FileAttachmentSerializationTests
|
||||
|
||||
var json = JsonSerializer.Serialize(template, SettingsManager.JSON_OPTIONS);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(json, Does.Contain(FILE_PATH.Replace(@"\", @"\\")), "The attachment has to reach the JSON, or this test checks nothing.");
|
||||
Assert.That(json, Does.Contain("chart.png"), "The attachment of the example conversation has to reach the JSON, or this test checks nothing.");
|
||||
Assert.That(json, Does.Not.Contain("\"Exists\""));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -67,11 +67,11 @@ public sealed class FileAttachmentSerializationTests
|
||||
|
||||
var read = JsonSerializer.Deserialize<FileAttachment>(LEGACY_JSON, SettingsManager.JSON_OPTIONS)!;
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(read.FileName, Is.EqualTo("report.pdf"));
|
||||
Assert.That(read.FilePath, Is.EqualTo("/tmp/report.pdf"));
|
||||
Assert.That(read.FileSizeBytes, Is.EqualTo(1024));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,12 +27,12 @@ public sealed class IContentExtensionsTests
|
||||
|
||||
var found = content.TryGetExportMarkdown(out var markdown);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(found, Is.True);
|
||||
Assert.That(markdown, Does.EndWith(content.Sources.ToExportMarkdown()), "What the chat shows below the answer is what the file holds below it.");
|
||||
Assert.That(TopLevelBlocksOf(markdown), Is.EqualTo(new[] { "ParagraphBlock", "h1", "h2", "ListBlock" }), "The answer stays a paragraph of its own; the source list starts under its own heading.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -47,11 +47,11 @@ public sealed class IContentExtensionsTests
|
||||
|
||||
var found = content.TryGetExportMarkdown(out var markdown);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(found, Is.True);
|
||||
Assert.That(TopLevelBlocksOf(markdown), Is.EqualTo(new[] { "ParagraphBlock", "Table", "h1", "h2", "ListBlock" }), "The table ends where it ended; the headings below it are not two more rows.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -66,11 +66,11 @@ public sealed class IContentExtensionsTests
|
||||
|
||||
var found = content.TryGetExportMarkdown(out var markdown);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(found, Is.True);
|
||||
Assert.That(TopLevelBlocksOf(markdown), Is.EqualTo(new[] { "ParagraphBlock", "ListBlock", "h1", "h2", "ListBlock" }), "Two lists, not one: the sources do not become the fourth point of the answer.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -86,11 +86,11 @@ public sealed class IContentExtensionsTests
|
||||
|
||||
var found = content.TryGetExportMarkdown(out var markdown);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(found, Is.True);
|
||||
Assert.That(TopLevelBlocksOf(markdown), Is.EqualTo(new[] { "ParagraphBlock", "FencedCodeBlock", "h1", "h2", "ListBlock" }), "The code block is closed for the model, so the sources stand below it instead of inside it.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -101,11 +101,11 @@ public sealed class IContentExtensionsTests
|
||||
|
||||
var found = content.TryGetExportMarkdown(out var markdown);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(found, Is.True);
|
||||
Assert.That(markdown, Is.EqualTo(ANSWER.Trim()), "The everyday case: no heading, no empty line, nothing anybody has to explain.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -115,11 +115,11 @@ public sealed class IContentExtensionsTests
|
||||
|
||||
var found = content.TryGetExportMarkdown(out var markdown);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(found, Is.True);
|
||||
Assert.That(markdown, Is.EqualTo(content.Sources.ToExportMarkdown()), "Nothing above the heading means no empty line above it either.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -132,11 +132,11 @@ public sealed class IContentExtensionsTests
|
||||
Sources = [TOOL_SOURCE],
|
||||
};
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(picture.TryGetExportMarkdown(out var markdown), Is.False, "There is no text document for a picture, so the caller hears no and says so.");
|
||||
Assert.That(markdown, Is.Empty, "A file writer must not put an excuse into the file it writes.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -147,11 +147,11 @@ public sealed class IContentExtensionsTests
|
||||
|
||||
var found = content.TryGetMarkdownText(out var markdown);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(found, Is.True);
|
||||
Assert.That(markdown, Is.EqualTo(ANSWER), "Neither trimmed nor extended: whoever reads a table out of a message wants what the model wrote and nothing else.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -164,11 +164,11 @@ public sealed class IContentExtensionsTests
|
||||
|
||||
var tables = FilesOf(content);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(tables, Has.Count.EqualTo(1), "One table in the message, one table offered for it.");
|
||||
Assert.That(content.ToExportContent(tables[0]), Is.EqualTo(tables[0].Content), "A data table has no column a link list would fit into.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -201,12 +201,12 @@ public sealed class IContentExtensionsTests
|
||||
var exported = content.ToExportContent(file);
|
||||
var appended = exported[file.Content.Length..].Trim();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(exported, Does.StartWith(file.Content), "The page stays as the model wrote it.");
|
||||
Assert.That(appended, Does.StartWith("<!--").And.EndWith("-->"), "Below the page stands one comment and nothing a browser would show.");
|
||||
Assert.That(appended, Does.Contain(TOOL_SOURCE.URL));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -221,12 +221,12 @@ public sealed class IContentExtensionsTests
|
||||
var exported = content.ToExportContent(file);
|
||||
var appended = exported[file.Content.Length..].Trim();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(exported, Does.StartWith(file.Content), "The document stays as the model wrote it.");
|
||||
Assert.That(appended.Split(Environment.NewLine), Has.All.StartWith("%"), "A line LaTeX would read could stop the whole run.");
|
||||
Assert.That(appended, Does.Contain(TOOL_SOURCE.URL));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -92,11 +92,11 @@ public sealed class ListContentBlockExtensionsTests
|
||||
var messages = await BlocksWithAPicture(this.picturePath).BuildMessagesUsingDirectImageUrlAsync(PictureReadingProvider());
|
||||
|
||||
var picture = (messages.Single() as MultimodalMessage)!.Content.OfType<SubContentImageUrl>().Single();
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(picture.ImageUrl, Is.EqualTo($"data:image/jpeg;base64,{FakeImageRuntime.IMAGE_BASE64}"));
|
||||
Assert.That(this.runtime.RequestedPaths, Is.EqualTo(new[] { new FileInfo(this.picturePath).FullName }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -49,11 +49,11 @@ public sealed class ManagedTranscriptAttachmentTests
|
||||
var chatDirectory = Path.Combine(this.testDirectory, "workspaces", Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
|
||||
var transcript = WriteTranscript(ManagedTranscriptAttachment.GetTranscriptDirectory(chatDirectory));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(ManagedTranscriptAttachment.TryDeleteOwnedFile(transcript), Is.True);
|
||||
Assert.That(File.Exists(transcript.FilePath), Is.False);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -62,11 +62,11 @@ public sealed class ManagedTranscriptAttachmentTests
|
||||
var chatDirectory = Path.Combine(this.testDirectory, "workspaces", Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
|
||||
var file = WriteTranscript(Path.Combine(chatDirectory, "attachments", "images"));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(ManagedTranscriptAttachment.TryDeleteOwnedFile(file), Is.False, "Only the transcript folder belongs to the transcripts.");
|
||||
Assert.That(File.Exists(file.FilePath), Is.True);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -77,11 +77,11 @@ public sealed class ManagedTranscriptAttachmentTests
|
||||
{
|
||||
var file = WriteTranscript(ManagedTranscriptAttachment.GetTranscriptDirectory(Path.Combine(outsideDirectory, "tempChats", Guid.NewGuid().ToString())));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(ManagedTranscriptAttachment.TryDeleteOwnedFile(file), Is.False, "The same folders elsewhere belong to somebody else.");
|
||||
Assert.That(File.Exists(file.FilePath), Is.True);
|
||||
});
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -50,12 +50,12 @@ public sealed class ProvenanceCleanupTests
|
||||
var result = await CleanUpAsync(store, Locations(keptChatId), LoaderOf(chat), DateTimeOffset.UtcNow.AddMinutes(1));
|
||||
var entries = await store.Client.GetEntriesAsync([referenced.Value, ofRemovedMessage!.Value, ofDeletedChat!.Value], TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(entries.Keys, Is.EquivalentTo(new[] { referenced.Value }));
|
||||
Assert.That(result.Orphaned, Is.EqualTo(1));
|
||||
Assert.That(result.Unreferenced, Is.EqualTo(1));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -83,11 +83,11 @@ public sealed class ProvenanceCleanupTests
|
||||
var result = await CleanUpAsync(store, Locations(Guid.NewGuid()), UNREADABLE, DateTimeOffset.UtcNow.AddMinutes(1));
|
||||
var entries = await store.Client.GetEntriesAsync([entry!.Value], TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(entries, Is.Empty, "No chat on the disk has no id, so nothing would ever remove such an entry.");
|
||||
Assert.That(result.Orphaned, Is.EqualTo(1));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -100,11 +100,11 @@ public sealed class ProvenanceCleanupTests
|
||||
var result = await CleanUpAsync(store, Locations(chatId), UNREADABLE, DateTimeOffset.UtcNow.AddMinutes(1));
|
||||
var entries = await store.Client.GetEntriesAsync([entry!.Value], TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(entries.Keys, Is.EquivalentTo(new[] { entry.Value }), "Nothing tells what an unreadable chat refers to, e.g., one written by a newer version.");
|
||||
Assert.That(result.Unreadable, Is.EqualTo(1), "The next start has to read it again.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -119,12 +119,12 @@ public sealed class ProvenanceCleanupTests
|
||||
var result = await CleanUpAsync(store, Locations(chatId), LoaderOf(ChatWith(passageId: null, attachmentId: null, chatId), reads), lastCleanup.AddMinutes(1), lastCleanup, writtenAt: lastCleanup.AddHours(-1));
|
||||
var entries = await store.Client.GetEntriesAsync([entry!.Value], TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(reads, Is.Empty, "Reading every chat at every start costs much on a small computer.");
|
||||
Assert.That(result.Skipped, Is.EqualTo(1));
|
||||
Assert.That(entries.Keys, Is.EquivalentTo(new[] { entry.Value }), "Skipping a chat never removes anything.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -139,11 +139,11 @@ public sealed class ProvenanceCleanupTests
|
||||
await CleanUpAsync(store, Locations(chatId), LoaderOf(ChatWith(passageId: null, attachmentId: null, chatId), reads), DateTimeOffset.UtcNow.AddMinutes(1), lastCleanup, writtenAt: lastCleanup.AddHours(-1));
|
||||
var entries = await store.Client.GetEntriesAsync([previewed!.Value], TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(reads, Is.EquivalentTo(new[] { chatId }), "The last cleanup had to leave alone what was stored after it began.");
|
||||
Assert.That(entries, Is.Empty);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(60, TestName = "AChatWrittenSinceTheLastCleanupIsRead")]
|
||||
@@ -160,11 +160,11 @@ public sealed class ProvenanceCleanupTests
|
||||
await CleanUpAsync(store, Locations(chatId), LoaderOf(ChatWith(passageId: null, attachmentId: null, chatId), reads), lastCleanup.AddMinutes(1), lastCleanup, writtenAt: lastCleanup.AddSeconds(secondsAfterTheLastCleanup));
|
||||
var entries = await store.Client.GetEntriesAsync([ofRemovedMessage!.Value], TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(reads, Is.EquivalentTo(new[] { chatId }));
|
||||
Assert.That(entries, Is.Empty);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -192,12 +192,12 @@ public sealed class ProvenanceCleanupTests
|
||||
var result = await CleanUpAsync(store, Locations(), LoaderOf(ChatWith(passageId: null, attachmentId: null), reads), lastCleanup.AddMinutes(1), lastCleanup, writtenAt: lastCleanup.AddHours(-1));
|
||||
var entries = await store.Client.GetEntriesAsync([entry!.Value], TOKEN);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(entries, Is.Empty, "Telling that a chat no longer exists costs no reading.");
|
||||
Assert.That(result.Orphaned, Is.EqualTo(1));
|
||||
Assert.That(reads, Is.Empty);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static ChatThread ChatWith(Guid? passageId, Guid? attachmentId, Guid? chatId = null) => new()
|
||||
|
||||
Reference in new issue
Block a user