fix(calculation): read cache carries real input status + timestamps
This commit is contained in:
@@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Serilog;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Scripting;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.Calculation.Tests;
|
||||
@@ -230,6 +231,74 @@ public sealed class CalculationEvaluatorTests
|
||||
return (int)countProp.GetValue(cache)!;
|
||||
}
|
||||
|
||||
// ── input quality reaches the script (archreview 05/STAB-19) ────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// A script reading <c>ctx.GetTag(path).StatusCode</c> sees the input's <b>real</b> status.
|
||||
/// The read cache used to stamp every dependency Good, so a script that checked quality was lied
|
||||
/// to — and, since the driver now holds on Bad, an Uncertain input is precisely the case where a
|
||||
/// script can still make a qualified decision and needs the truth to do it.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void The_read_cache_carries_the_inputs_real_status_code()
|
||||
{
|
||||
const uint uncertain = 0x40000000u;
|
||||
using var sut = new CalculationEvaluator(NullLogger<CalculationEvaluator>.Instance, NoOpScriptRoot());
|
||||
var sourceStamp = new DateTime(2026, 8, 20, 9, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
var result = sut.Evaluate(
|
||||
calcTagId: "calc-quality",
|
||||
scriptSource: "return (long)ctx.GetTag(\"a\").StatusCode;",
|
||||
dependencies: new Dictionary<string, DataValueSnapshot>
|
||||
{
|
||||
["a"] = new(7.0, uncertain, sourceStamp, sourceStamp),
|
||||
});
|
||||
|
||||
result.Success.ShouldBeTrue(result.Reason);
|
||||
result.Value.ShouldBe((long)uncertain);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The dependency's own source timestamp reaches the script too. The old cache stamped
|
||||
/// evaluation time on every input, so a script comparing input ages compared its own clock with
|
||||
/// itself.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void The_read_cache_carries_the_inputs_real_source_timestamp()
|
||||
{
|
||||
using var sut = new CalculationEvaluator(NullLogger<CalculationEvaluator>.Instance, NoOpScriptRoot());
|
||||
var sourceStamp = new DateTime(2026, 8, 20, 9, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
var result = sut.Evaluate(
|
||||
calcTagId: "calc-stamp",
|
||||
scriptSource: "return ctx.GetTag(\"a\").SourceTimestampUtc;",
|
||||
dependencies: new Dictionary<string, DataValueSnapshot>
|
||||
{
|
||||
["a"] = new(7.0, 0u, sourceStamp, DateTime.UtcNow),
|
||||
});
|
||||
|
||||
result.Success.ShouldBeTrue(result.Reason);
|
||||
result.Value.ShouldBe(sourceStamp);
|
||||
}
|
||||
|
||||
/// <summary>The passthrough fast path takes the same snapshot input and returns its value.</summary>
|
||||
[Fact]
|
||||
public void The_passthrough_fast_path_accepts_snapshots()
|
||||
{
|
||||
using var sut = new CalculationEvaluator(NullLogger<CalculationEvaluator>.Instance, NoOpScriptRoot());
|
||||
|
||||
var result = sut.Evaluate(
|
||||
calcTagId: "calc-mirror",
|
||||
scriptSource: "return ctx.GetTag(\"a\").Value;",
|
||||
dependencies: new Dictionary<string, DataValueSnapshot>
|
||||
{
|
||||
["a"] = new(42, 0u, DateTime.UtcNow, DateTime.UtcNow),
|
||||
});
|
||||
|
||||
result.Success.ShouldBeTrue(result.Reason);
|
||||
result.Value.ShouldBe(42);
|
||||
}
|
||||
|
||||
/// <summary>Minimal <see cref="ILogger{T}"/> that records emitted entries so the SetVirtualTag-drop
|
||||
/// test can assert the Debug "dropped" line was logged.</summary>
|
||||
private sealed class CapturingLogger : ILogger<CalculationEvaluator>
|
||||
|
||||
Reference in New Issue
Block a user