using AIStudio.Assistants.BeyondFactsLab; namespace AIStudio.Tests.BeyondFactsLab; /// /// Checks when a confirmed work state of the BeyondFacts Lab counts as outdated. /// /// /// A confirmed work state is outdated as soon as the work state of an earlier step is confirmed for the first time, /// confirmed anew, or withdrawn. A changed form of its own step gives a weaker notice. /// [TestFixture] public sealed class BflStalenessTests { private static readonly DateTimeOffset NOW = new(2026, 11, 10, 9, 0, 0, TimeSpan.Zero); private static readonly Dictionary SECTIONS = new(StringComparer.Ordinal) { ["SUMMARY"] = "Text" }; [Test] public void AStepWithoutAConfirmationIsNotConfirmed() { var workStates = new Dictionary { [BflStep.KNOWLEDGE] = new() { Draft = new(SECTIONS) } }; Assert.That(BflStaleness.Of(BflStep.KNOWLEDGE, workStates, Forms()), Is.EqualTo(BflWorkStateStatus.NOT_CONFIRMED)); } [Test] public void AConfirmationStaysValidWhileNothingChanges() { var workStates = new Dictionary(); Confirm(workStates, BflStep.FRAMEWORK); Confirm(workStates, BflStep.KNOWLEDGE); using (Assert.EnterMultipleScope()) { Assert.That(BflStaleness.Of(BflStep.FRAMEWORK, workStates, Forms()), Is.EqualTo(BflWorkStateStatus.CONFIRMED)); Assert.That(BflStaleness.Of(BflStep.KNOWLEDGE, workStates, Forms()), Is.EqualTo(BflWorkStateStatus.CONFIRMED)); } } [TestCase(Change.CONFIRMED_ANEW, TestName = "AnEarlierStepConfirmedAnewOutdatesTheLaterOnes")] [TestCase(Change.WITHDRAWN, TestName = "AnEarlierStepWithdrawnOutdatesTheLaterOnes")] [TestCase(Change.WITHDRAWN_AND_CONFIRMED_AGAIN, TestName = "AnEarlierStepWithdrawnAndConfirmedAgainOutdatesTheLaterOnes")] public void AChangeOfAnEarlierStepOutdatesTheLaterOnes(Change change) { var workStates = new Dictionary(); Confirm(workStates, BflStep.FRAMEWORK); Confirm(workStates, BflStep.GOALS_AND_ROLE); switch (change) { case Change.CONFIRMED_ANEW: Confirm(workStates, BflStep.FRAMEWORK); break; case Change.WITHDRAWN: workStates[BflStep.FRAMEWORK].Withdraw(); break; case Change.WITHDRAWN_AND_CONFIRMED_AGAIN: workStates[BflStep.FRAMEWORK].Withdraw(); Confirm(workStates, BflStep.FRAMEWORK); break; } Assert.That(BflStaleness.Of(BflStep.GOALS_AND_ROLE, workStates, Forms()), Is.EqualTo(BflWorkStateStatus.OUTDATED)); } [Test] public void AnEarlierStepConfirmedForTheFirstTimeOutdatesTheLaterOnes() { var workStates = new Dictionary(); Confirm(workStates, BflStep.STRATEGY); Confirm(workStates, BflStep.KNOWLEDGE); Assert.That(BflStaleness.Of(BflStep.STRATEGY, workStates, Forms()), Is.EqualTo(BflWorkStateStatus.OUTDATED)); } [Test] public void ALaterStepNeverOutdatesAnEarlierOne() { var workStates = new Dictionary(); Confirm(workStates, BflStep.KNOWLEDGE); Confirm(workStates, BflStep.STRATEGY); Confirm(workStates, BflStep.STRATEGY); workStates[BflStep.STRATEGY].Withdraw(); Assert.That(BflStaleness.Of(BflStep.KNOWLEDGE, workStates, Forms()), Is.EqualTo(BflWorkStateStatus.CONFIRMED)); } [Test] public void ConfirmingAnOutdatedStepAgainMakesItValid() { var workStates = new Dictionary(); Confirm(workStates, BflStep.GOALS_AND_ROLE); Confirm(workStates, BflStep.FRAMEWORK); Confirm(workStates, BflStep.GOALS_AND_ROLE); Assert.That(BflStaleness.Of(BflStep.GOALS_AND_ROLE, workStates, Forms()), Is.EqualTo(BflWorkStateStatus.CONFIRMED)); } [Test] public void ChangedDetailsOfTheStepGiveTheWeakerNotice() { var workStates = new Dictionary(); var form = new BflStepFormData(); form.Set(BflFieldCatalog.CONTRIBUTION_SUMMARY, "Erste Fassung"); Confirm(workStates, BflStep.KNOWLEDGE, form); form.Set(BflFieldCatalog.CONTRIBUTION_SUMMARY, "Zweite Fassung"); Assert.That(BflStaleness.Of(BflStep.KNOWLEDGE, workStates, Forms((BflStep.KNOWLEDGE, form))), Is.EqualTo(BflWorkStateStatus.DETAILS_CHANGED)); } [Test] public void AnOutdatedStepWeighsMoreThanChangedDetails() { var workStates = new Dictionary(); var form = new BflStepFormData(); Confirm(workStates, BflStep.KNOWLEDGE, form); form.Set(BflFieldCatalog.CORE_THESES, "Neu"); Confirm(workStates, BflStep.FRAMEWORK); Assert.That(BflStaleness.Of(BflStep.KNOWLEDGE, workStates, Forms((BflStep.KNOWLEDGE, form))), Is.EqualTo(BflWorkStateStatus.OUTDATED)); } [Test] public void AFormThisVersionCannotOpenChangesNothing() { var workStates = new Dictionary(); var form = new BflStepFormData(); form.Set(BflFieldCatalog.CORE_THESES, "These"); Confirm(workStates, BflStep.KNOWLEDGE, form); Assert.That(BflStaleness.Of(BflStep.KNOWLEDGE, workStates, new Dictionary()), Is.EqualTo(BflWorkStateStatus.CONFIRMED)); } [Test] public void TheFingerprintIgnoresLineEndingsAndSpacesAroundValues() { var unix = new BflStepFormData(); unix.Set(BflFieldCatalog.CORE_THESES, "Erste These\nZweite These"); var windows = new BflStepFormData(); windows.Set(BflFieldCatalog.CORE_THESES, " Erste These\r\nZweite These \n"); var changed = new BflStepFormData(); changed.Set(BflFieldCatalog.CORE_THESES, "Erste These\nDritte These"); using (Assert.EnterMultipleScope()) { Assert.That(BflStaleness.FingerprintOf(windows), Is.EqualTo(BflStaleness.FingerprintOf(unix))); Assert.That(BflStaleness.FingerprintOf(changed), Is.Not.EqualTo(BflStaleness.FingerprintOf(unix))); } } [Test] public void TheFingerprintTellsWhichFieldHoldsAValue() { var first = new BflStepFormData(); first.Set(BflFieldCatalog.CORE_THESES, "Text"); var second = new BflStepFormData(); second.Set(BflFieldCatalog.UNCERTAINTIES, "Text"); Assert.That(BflStaleness.FingerprintOf(first), Is.Not.EqualTo(BflStaleness.FingerprintOf(second))); } [Test] public void AConfirmationRemembersOnlyEarlierSteps() { var workStates = new Dictionary(); Confirm(workStates, BflStep.FRAMEWORK); Confirm(workStates, BflStep.STRATEGY); Confirm(workStates, BflStep.KNOWLEDGE); var revisions = BflStaleness.UpstreamRevisionsOf(BflStep.GOALS_AND_ROLE, workStates); Assert.That(revisions.Keys, Is.EquivalentTo(new[] { BflStep.FRAMEWORK, BflStep.KNOWLEDGE })); } [Test] public void ARevisionKeepsCountingAfterAWithdrawal() { var workState = new BflWorkState(); workState.Confirm(SECTIONS, new Dictionary(), string.Empty, NOW); workState.Withdraw(); workState.Confirm(SECTIONS, new Dictionary(), string.Empty, NOW); using (Assert.EnterMultipleScope()) { Assert.That(workState.Confirmed!.Revision, Is.EqualTo(2)); Assert.That(workState.LastRevision, Is.EqualTo(2)); } } [Test] public void AConfirmationDropsTheDraftAndEmptySections() { var workState = new BflWorkState { Draft = new(SECTIONS) }; workState.Confirm(new Dictionary { ["SUMMARY"] = "Text", ["OPEN_POINTS"] = " " }, new Dictionary(), string.Empty, NOW); using (Assert.EnterMultipleScope()) { Assert.That(workState.Draft, Is.Null); Assert.That(workState.Confirmed!.Sections.Keys, Is.EqualTo(new[] { "SUMMARY" })); } } /// /// What happens to an earlier step. /// public enum Change { CONFIRMED_ANEW, WITHDRAWN, WITHDRAWN_AND_CONFIRMED_AGAIN, } private static void Confirm(Dictionary workStates, BflStep step, BflStepFormData? form = null) { if (!workStates.TryGetValue(step, out var workState)) { workState = new BflWorkState(); workStates[step] = workState; } workState.Confirm(SECTIONS, BflStaleness.UpstreamRevisionsOf(step, workStates), BflStaleness.FingerprintOf(form ?? new BflStepFormData()), NOW); } private static Dictionary Forms(params (BflStep Step, BflStepFormData Form)[] forms) { var result = BflStep.All.ToDictionary(step => step, _ => new BflStepFormData()); foreach (var (step, form) in forms) result[step] = form; return result; } }