using System.Globalization;
using AIStudio.Assistants.BeyondFactsLab;
using AIStudio.Chat;
namespace AIStudio.Tests.BeyondFactsLab;
///
/// Checks how the BeyondFacts Lab puts the reflection folder together, without a model.
///
///
/// The folder repeats what the person confirmed word for word, and it is meant to be passed on: personal disclosures
/// stay out unless the person includes them.
///
[TestFixture]
public sealed class BflReflectionFolderTests
{
private static readonly DateTimeOffset NOW = new(2026, 11, 10, 9, 0, 0, TimeSpan.Zero);
[Test]
public void AConfirmedWorkStateAppearsWordForWordUnderItsStep()
{
var workStates = new Dictionary();
Confirm(workStates, BflStep.KNOWLEDGE, new() { ["ESTABLISHED"] = "Gesichert-4711\n\n- mit Liste" });
var established = BflWorkStateSections.ForStep(BflStep.KNOWLEDGE).Single(section => section.Key == "ESTABLISHED");
var folder = Build(workStates);
using (Assert.EnterMultipleScope())
{
Assert.That(folder, Does.Contain($"## {BflStep.KNOWLEDGE.Title}\n"));
Assert.That(folder, Does.Contain($"### {established.Title}\n\nGesichert-4711\n\n- mit Liste\n"));
Assert.That(folder.IndexOf("Gesichert-4711", StringComparison.Ordinal), Is.GreaterThan(folder.IndexOf($"## {BflStep.KNOWLEDGE.Title}", StringComparison.Ordinal)));
}
}
[Test]
public void ADraftNeverAppears()
{
var workStates = new Dictionary
{
[BflStep.KNOWLEDGE] = new() { Draft = new(StringComparer.Ordinal) { ["ESTABLISHED"] = "ENTWURF-4711" } },
};
Assert.That(Build(workStates), Does.Not.Contain("ENTWURF-4711"));
}
[Test]
public void EveryStepAppearsInItsOrderAlsoWithoutAConfirmation()
{
var folder = Build(new Dictionary());
var positions = BflStep.All.Select(step => folder.IndexOf($"## {step.Title}\n", StringComparison.Ordinal)).ToList();
using (Assert.EnterMultipleScope())
{
Assert.That(positions, Has.All.GreaterThanOrEqualTo(0));
Assert.That(positions, Is.Ordered);
}
}
[Test]
public void APersonalSectionStaysOutUnlessThePersonIncludesIt()
{
var workStates = new Dictionary();
Confirm(workStates, BflStep.VALUES_AND_PERSPECTIVES, new() { ["VALUES"] = "Gerechtigkeit", ["PERSONAL_INVOLVEMENT"] = "PERSÖNLICH-4711" });
var without = Build(workStates);
var with = Build(workStates, includePersonalDisclosures: true);
using (Assert.EnterMultipleScope())
{
Assert.That(without, Does.Not.Contain("PERSÖNLICH-4711"));
Assert.That(without, Does.Contain("Gerechtigkeit"));
Assert.That(with, Does.Contain("PERSÖNLICH-4711"));
}
}
[Test]
public void TheOpenPointsOfAllStepsAreGatheredAfterTheSteps()
{
var workStates = new Dictionary();
Confirm(workStates, BflStep.FRAMEWORK, new() { ["FORMAT"] = "Podium", ["OPEN_POINTS"] = "OFFEN-1" });
Confirm(workStates, BflStep.STRATEGY, new() { ["OPEN_POINTS"] = "OFFEN-5" });
var folder = Build(workStates);
var lastStep = folder.IndexOf($"## {BflStep.RESULTS.Title}\n", StringComparison.Ordinal);
using (Assert.EnterMultipleScope())
{
Assert.That(folder.IndexOf("OFFEN-1", StringComparison.Ordinal), Is.GreaterThan(lastStep));
Assert.That(folder.IndexOf("OFFEN-5", StringComparison.Ordinal), Is.GreaterThan(folder.IndexOf("OFFEN-1", StringComparison.Ordinal)));
Assert.That(folder.IndexOf("OFFEN-1", StringComparison.Ordinal), Is.EqualTo(folder.LastIndexOf("OFFEN-1", StringComparison.Ordinal)), "The open points appear once.");
}
}
[Test]
public void TheMaterialIsListedByName()
{
var materials = new List
{
new() { Name = "Studie_2026.pdf", Kind = BflMaterialKind.DOCUMENT, AddedAtUtc = NOW },
new() { Name = "Interview.m4a", Kind = BflMaterialKind.RECORDING, AddedAtUtc = NOW.AddMinutes(1) },
};
var folder = BflReflectionFolder.Build(new BflReflectionFolderInput("Panel", new Dictionary(), Forms(), materials, new Dictionary>(), false, CultureInfo.InvariantCulture));
using (Assert.EnterMultipleScope())
{
Assert.That(folder, Does.Contain("- Studie\\_2026\\.pdf ("));
Assert.That(folder, Does.Contain("- Interview\\.m4a ("));
}
}
[Test]
public void TheConversationsAppearOnlyWhenThePersonAddsThem()
{
var conversations = new Dictionary>
{
[BflStep.STRATEGY] = [new(true, "FRAGE-4711"), new(false, "ANTWORT-4711")],
};
var without = Build(new Dictionary());
var with = Build(new Dictionary(), conversations: conversations);
using (Assert.EnterMultipleScope())
{
Assert.That(without, Does.Not.Contain("FRAGE-4711"));
Assert.That(with, Does.Contain("FRAGE-4711"));
Assert.That(with.IndexOf("ANTWORT-4711", StringComparison.Ordinal), Is.GreaterThan(with.IndexOf("FRAGE-4711", StringComparison.Ordinal)));
}
}
[Test]
public void TheConversationOfAStepWithPersonalDisclosuresStaysOutUnlessThePersonIncludesThem()
{
var conversations = new Dictionary>
{
[BflStep.VALUES_AND_PERSPECTIVES] = [new(true, "BETROFFEN-4711")],
[BflStep.STRATEGY] = [new(true, "STRATEGIE-4711")],
};
var without = Build(new Dictionary(), conversations: conversations);
var with = Build(new Dictionary(), includePersonalDisclosures: true, conversations: conversations);
using (Assert.EnterMultipleScope())
{
Assert.That(BflReflectionFolder.MayHoldPersonalDisclosures(BflStep.VALUES_AND_PERSPECTIVES), Is.True);
Assert.That(BflReflectionFolder.MayHoldPersonalDisclosures(BflStep.STRATEGY), Is.False);
Assert.That(without, Does.Not.Contain("BETROFFEN-4711"));
Assert.That(without, Does.Contain("STRATEGIE-4711"));
Assert.That(with, Does.Contain("BETROFFEN-4711"));
}
}
[Test]
public void TheMessagesOfAConversationLeaveOutWhatThePersonNeverSaw()
{
var thread = BflThreadFactory.Create(new BflStepThreadLocation(Guid.NewGuid(), BflStep.STRATEGY));
thread.Blocks.Add(Block(ChatRole.USER, "VERSTECKT-4711", hidden: true));
thread.Blocks.Add(Block(ChatRole.AI, "Willkommen"));
thread.Blocks.Add(Block(ChatRole.USER, " Meine Frage "));
thread.Blocks.Add(Block(ChatRole.AI, " "));
var messages = BflReflectionFolder.MessagesOf(thread);
Assert.That(messages, Is.EqualTo(new[] { new BflConversationMessage(false, "Willkommen"), new BflConversationMessage(true, "Meine Frage") }));
}
[Test]
public void TheNameOfThePreparationCannotBreakTheHeading()
{
var folder = BflReflectionFolder.Build(new BflReflectionFolderInput("Panel\n# Kopf", new Dictionary(), Forms(), [], new Dictionary>(), false, CultureInfo.InvariantCulture));
Assert.That(folder.Split('\n')[0], Does.StartWith("# ").And.Contain("Panel").And.Contain("Kopf"));
}
private static string Build(IReadOnlyDictionary workStates, bool includePersonalDisclosures = false, IReadOnlyDictionary>? conversations = null) => BflReflectionFolder.Build(new BflReflectionFolderInput("Panel", workStates, Forms(), [], conversations ?? new Dictionary>(), includePersonalDisclosures, CultureInfo.InvariantCulture));
private static Dictionary Forms() => BflStep.All.ToDictionary(step => step, _ => new BflStepFormData());
private static ContentBlock Block(ChatRole role, string text, bool hidden = false) => new()
{
Time = NOW,
ContentType = ContentType.TEXT,
Role = role,
Content = new ContentText { Text = text },
HideFromUser = hidden,
};
private static void Confirm(Dictionary workStates, BflStep step, Dictionary sections)
{
if (!workStates.TryGetValue(step, out var workState))
{
workState = new BflWorkState();
workStates[step] = workState;
}
workState.Confirm(sections, BflStaleness.UpstreamRevisionsOf(step, workStates), BflStaleness.FingerprintOf(new BflStepFormData()), NOW);
}
}