test: migrate AdminUI/ControlPlane/Runtime/Host tests to xunit.v3 (#756)
v2-ci / unit-tests (push) Failing after 13m22s
v2-ci / unit-tests (push) Failing after 13m22s
Akka.TestKit.Xunit2 -> Akka.TestKit.Xunit 1.5.62 (xunit.v3 TestKit; needs xunit.v3 3.2.2, set via VersionOverride on the four projects). Removes the xunit 2.9.3 pin. IAsyncLifetime returns ValueTask, SqlServerFactAttribute gains the source-info ctor, and xUnit1051 call sites pass TestContext.Current.CancellationToken. Test counts unchanged. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
+3
-3
@@ -50,9 +50,9 @@ public sealed class AnalysisCompilationReuseTests
|
||||
// A clean script stays clean and an error stays an error across interleaved analyses on one service
|
||||
// — a stale tree leaking from the previous call would surface here.
|
||||
var svc = new ScriptAnalysisService();
|
||||
svc.Diagnose(new DiagnoseRequest("return ctx.Nope();")).Markers.ShouldContain(m => m.Severity == 8);
|
||||
svc.Diagnose(new DiagnoseRequest("""return (double)ctx.GetTag("A").Value * 2.0;""")).Markers.ShouldBeEmpty();
|
||||
svc.Diagnose(new DiagnoseRequest("var a = 1;\nreturn ctx.Nope();")).Markers
|
||||
svc.Diagnose(new DiagnoseRequest("return ctx.Nope();"), TestContext.Current.CancellationToken).Markers.ShouldContain(m => m.Severity == 8);
|
||||
svc.Diagnose(new DiagnoseRequest("""return (double)ctx.GetTag("A").Value * 2.0;"""), TestContext.Current.CancellationToken).Markers.ShouldBeEmpty();
|
||||
svc.Diagnose(new DiagnoseRequest("var a = 1;\nreturn ctx.Nope();"), TestContext.Current.CancellationToken).Markers
|
||||
.ShouldContain(m => m.Severity == 8 && m.StartLineNumber == 2);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -23,34 +23,34 @@ public sealed class CtxCompletionGuardTests
|
||||
[Fact] public async Task Completion_on_ctx_GetTag_literal_offers_catalog_paths()
|
||||
{
|
||||
// "return ctx.GetTag(" + '""' + ')' — col 20 lands on the closing quote of the empty string literal.
|
||||
var res = await Svc.CompleteAsync(new CompletionsRequest("return ctx.GetTag(\"\")", 1, 20));
|
||||
var res = await Svc.CompleteAsync(new CompletionsRequest("return ctx.GetTag(\"\")", 1, 20), TestContext.Current.CancellationToken);
|
||||
res.Items.ShouldContain(i => i.InsertText == "Line1.Speed");
|
||||
}
|
||||
|
||||
[Fact] public async Task Completion_on_non_ctx_receiver_does_NOT_offer_catalog_paths()
|
||||
{
|
||||
// Same shape but receiver is "foo" — must NOT offer catalog paths.
|
||||
var res = await Svc.CompleteAsync(new CompletionsRequest("return foo.GetTag(\"\")", 1, 20));
|
||||
var res = await Svc.CompleteAsync(new CompletionsRequest("return foo.GetTag(\"\")", 1, 20), TestContext.Current.CancellationToken);
|
||||
res.Items.ShouldNotContain(i => i.InsertText == "Line1.Speed");
|
||||
}
|
||||
|
||||
[Fact] public async Task Hover_on_ctx_SetVirtualTag_literal_warns_write_is_dropped()
|
||||
{
|
||||
var md = (await Svc.Hover(new HoverRequest("return ctx.SetVirtualTag(\"V\", 1);", 1, 27))).Markdown;
|
||||
var md = (await Svc.Hover(new HoverRequest("return ctx.SetVirtualTag(\"V\", 1);", 1, 27), TestContext.Current.CancellationToken)).Markdown;
|
||||
md.ShouldNotBeNull();
|
||||
md!.ShouldContain("single-tag mode");
|
||||
}
|
||||
|
||||
[Fact] public async Task Hover_on_ctx_GetTag_literal_has_no_dropped_write_note()
|
||||
{
|
||||
var md = (await Svc.Hover(new HoverRequest("return ctx.GetTag(\"V\").Value;", 1, 20))).Markdown;
|
||||
var md = (await Svc.Hover(new HoverRequest("return ctx.GetTag(\"V\").Value;", 1, 20), TestContext.Current.CancellationToken)).Markdown;
|
||||
md.ShouldNotBeNull();
|
||||
md!.ShouldNotContain("single-tag mode");
|
||||
}
|
||||
|
||||
[Fact] public async Task Hover_on_non_ctx_receiver_literal_is_not_treated_as_a_tag_path()
|
||||
{
|
||||
var md = (await Svc.Hover(new HoverRequest("return bar.GetTag(\"V\");", 1, 20))).Markdown;
|
||||
var md = (await Svc.Hover(new HoverRequest("return bar.GetTag(\"V\");", 1, 20), TestContext.Current.CancellationToken)).Markdown;
|
||||
(md is null || !md.Contains("Tag path")).ShouldBeTrue();
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -65,7 +65,7 @@ public sealed class EquipTokenEditorTests
|
||||
[Fact] public async Task Hover_on_equip_token_literal_notes_equipment_relative()
|
||||
{
|
||||
// caret inside ctx.GetTag("{{equip}}/Speed")
|
||||
var md = (await Svc.Hover(new HoverRequest("""return ctx.GetTag("{{equip}}/Speed").Value;""", 1, 24))).Markdown;
|
||||
var md = (await Svc.Hover(new HoverRequest("""return ctx.GetTag("{{equip}}/Speed").Value;""", 1, 24), TestContext.Current.CancellationToken)).Markdown;
|
||||
md.ShouldNotBeNull();
|
||||
md!.ShouldContain("Equipment-relative");
|
||||
// the {{equip}} token must render literally (non-interpolated markdown segment)
|
||||
@@ -78,14 +78,14 @@ public sealed class EquipTokenEditorTests
|
||||
// "Missing" is not one of the equipment's references (Speed/Temp) → a diagnostic, matching the
|
||||
// deploy gate. Uses the equipment context via the request's EquipmentId.
|
||||
var resp = await Svc.DiagnoseAsync(
|
||||
new DiagnoseRequest("""return ctx.GetTag("{{equip}}/Missing").Value;""", "EQ-1"));
|
||||
new DiagnoseRequest("""return ctx.GetTag("{{equip}}/Missing").Value;""", "EQ-1"), TestContext.Current.CancellationToken);
|
||||
resp.Markers.ShouldContain(m => m.Code == "OTSCRIPT_EQUIPREF" && m.Message.Contains("Missing"));
|
||||
}
|
||||
|
||||
[Fact] public async Task Diagnostic_clean_for_resolved_equip_reference()
|
||||
{
|
||||
var resp = await Svc.DiagnoseAsync(
|
||||
new DiagnoseRequest("""return ctx.GetTag("{{equip}}/Speed").Value;""", "EQ-1"));
|
||||
new DiagnoseRequest("""return ctx.GetTag("{{equip}}/Speed").Value;""", "EQ-1"), TestContext.Current.CancellationToken);
|
||||
resp.Markers.ShouldNotContain(m => m.Code == "OTSCRIPT_EQUIPREF");
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public sealed class EquipTokenEditorTests
|
||||
// No EquipmentId (shared ScriptEdit page) → the equip-ref check is inert (base markers only),
|
||||
// matching the deploy gate which only resolves per owning equipment.
|
||||
var resp = await Svc.DiagnoseAsync(
|
||||
new DiagnoseRequest("""return ctx.GetTag("{{equip}}/Missing").Value;"""));
|
||||
new DiagnoseRequest("""return ctx.GetTag("{{equip}}/Missing").Value;"""), TestContext.Current.CancellationToken);
|
||||
resp.Markers.ShouldNotContain(m => m.Code == "OTSCRIPT_EQUIPREF");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,18 +23,18 @@ public sealed class HoverSignatureTests
|
||||
[Fact] public async Task Hover_on_GetTag_returns_member_markdown()
|
||||
{
|
||||
// hover over the "GetTag" identifier in ctx.GetTag("A")
|
||||
var md = (await Svc.Hover(new HoverRequest("""return ctx.GetTag("A").Value;""", 1, 16))).Markdown;
|
||||
var md = (await Svc.Hover(new HoverRequest("""return ctx.GetTag("A").Value;""", 1, 16), TestContext.Current.CancellationToken)).Markdown;
|
||||
md.ShouldNotBeNull();
|
||||
md!.ShouldContain("GetTag");
|
||||
}
|
||||
|
||||
[Fact] public async Task Hover_on_nothing_returns_null()
|
||||
=> (await Svc.Hover(new HoverRequest(" ", 1, 1))).Markdown.ShouldBeNull();
|
||||
=> (await Svc.Hover(new HoverRequest(" ", 1, 1), TestContext.Current.CancellationToken)).Markdown.ShouldBeNull();
|
||||
|
||||
[Fact] public void SignatureHelp_inside_GetTag_call_shows_the_signature()
|
||||
{
|
||||
// caret just after the open paren of ctx.GetTag(
|
||||
var sh = Svc.SignatureHelp(new SignatureHelpRequest("""return ctx.GetTag();""", 1, 19));
|
||||
var sh = Svc.SignatureHelp(new SignatureHelpRequest("""return ctx.GetTag();""", 1, 19), TestContext.Current.CancellationToken);
|
||||
sh.Label.ShouldNotBeNull();
|
||||
sh.Label!.ShouldContain("GetTag");
|
||||
sh.Parameters.ShouldNotBeNull();
|
||||
@@ -44,7 +44,7 @@ public sealed class HoverSignatureTests
|
||||
[Fact] public async Task Hover_on_a_plain_local_resolves_the_local_not_an_enclosing_member()
|
||||
{
|
||||
// hovering the local `x` (not a member) must resolve the LOCAL, and must NOT climb to GetTag/Value.
|
||||
var md = (await Svc.Hover(new HoverRequest("var x = 1;\nreturn ctx.GetTag(\"A\").Value + x;", 2, 33))).Markdown;
|
||||
var md = (await Svc.Hover(new HoverRequest("var x = 1;\nreturn ctx.GetTag(\"A\").Value + x;", 2, 33), TestContext.Current.CancellationToken)).Markdown;
|
||||
md.ShouldNotBeNull();
|
||||
md!.ShouldContain("x");
|
||||
md.ShouldNotContain("GetTag");
|
||||
@@ -54,12 +54,12 @@ public sealed class HoverSignatureTests
|
||||
{
|
||||
// ctx.Now is a property, not a call; use a parameterless method if one exists, else assert empty-safe behavior.
|
||||
// Deadband is static on ScriptContext (3 params) — instead verify a 0-arg edge via an empty arg list doesn't throw:
|
||||
var sh = Svc.SignatureHelp(new SignatureHelpRequest("return ctx.GetTag();", 1, 19));
|
||||
var sh = Svc.SignatureHelp(new SignatureHelpRequest("return ctx.GetTag();", 1, 19), TestContext.Current.CancellationToken);
|
||||
sh.ActiveParameter.ShouldBe(0); // clamped; no exception
|
||||
}
|
||||
|
||||
[Fact] public async Task Hover_with_out_of_range_position_returns_null_without_throwing()
|
||||
=> (await Svc.Hover(new HoverRequest("return 1;", 99, 99))).Markdown.ShouldBeNull();
|
||||
=> (await Svc.Hover(new HoverRequest("return 1;", 99, 99), TestContext.Current.CancellationToken)).Markdown.ShouldBeNull();
|
||||
|
||||
// ── tag-path hover (driven by the catalog) ─────────────────────────────────────────────────
|
||||
|
||||
@@ -67,7 +67,7 @@ public sealed class HoverSignatureTests
|
||||
{
|
||||
var svc = new ScriptAnalysisService(new FakeCatalog());
|
||||
// caret on the path literal inside ctx.GetTag("Line1.Speed")
|
||||
var md = (await svc.Hover(new HoverRequest("""return ctx.GetTag("Line1.Speed").Value;""", 1, 24))).Markdown;
|
||||
var md = (await svc.Hover(new HoverRequest("""return ctx.GetTag("Line1.Speed").Value;""", 1, 24), TestContext.Current.CancellationToken)).Markdown;
|
||||
md.ShouldNotBeNull();
|
||||
md!.ShouldContain("Line1.Speed");
|
||||
md.ShouldContain("Double");
|
||||
@@ -79,7 +79,7 @@ public sealed class HoverSignatureTests
|
||||
{
|
||||
var svc = new ScriptAnalysisService(new FakeCatalog());
|
||||
// caret on the path literal inside ctx.GetTag("Unknownz")
|
||||
var md = (await svc.Hover(new HoverRequest("""return ctx.GetTag("Unknownz").Value;""", 1, 22))).Markdown;
|
||||
var md = (await svc.Hover(new HoverRequest("""return ctx.GetTag("Unknownz").Value;""", 1, 22), TestContext.Current.CancellationToken)).Markdown;
|
||||
md.ShouldNotBeNull();
|
||||
md!.ShouldContain("Not a known");
|
||||
}
|
||||
|
||||
+3
-3
@@ -26,7 +26,7 @@ public sealed class MarkerPayloadBoundTests
|
||||
[Fact]
|
||||
public void A_non_csharp_paste_returns_a_bounded_marker_set_that_still_holds_the_errors()
|
||||
{
|
||||
var markers = new ScriptAnalysisService().Diagnose(new DiagnoseRequest(NonCSharpPaste())).Markers;
|
||||
var markers = new ScriptAnalysisService().Diagnose(new DiagnoseRequest(NonCSharpPaste()), TestContext.Current.CancellationToken).Markers;
|
||||
|
||||
markers.Count.ShouldBe(ScriptEditorLimits.MaxDiagnosticMarkers); // positive control: the cap was hit
|
||||
markers.ShouldAllBe(m => m.Message.Length <= ScriptEditorLimits.MaxDiagnosticMessageChars);
|
||||
@@ -39,7 +39,7 @@ public sealed class MarkerPayloadBoundTests
|
||||
public void A_long_quoted_token_has_its_message_truncated()
|
||||
{
|
||||
var giant = new string('x', 20_000);
|
||||
var markers = new ScriptAnalysisService().Diagnose(new DiagnoseRequest($"return {giant};")).Markers;
|
||||
var markers = new ScriptAnalysisService().Diagnose(new DiagnoseRequest($"return {giant};"), TestContext.Current.CancellationToken).Markers;
|
||||
|
||||
markers.ShouldNotBeEmpty();
|
||||
markers.ShouldAllBe(m => m.Message.Length <= ScriptEditorLimits.MaxDiagnosticMessageChars);
|
||||
@@ -50,7 +50,7 @@ public sealed class MarkerPayloadBoundTests
|
||||
public void A_small_script_is_not_truncated()
|
||||
{
|
||||
// Positive control for the cap: ordinary diagnostics pass through untouched.
|
||||
var markers = new ScriptAnalysisService().Diagnose(new DiagnoseRequest("return ctx.Nope();")).Markers;
|
||||
var markers = new ScriptAnalysisService().Diagnose(new DiagnoseRequest("return ctx.Nope();"), TestContext.Current.CancellationToken).Markers;
|
||||
markers.ShouldContain(m => m.Severity == 8 && !m.Message.EndsWith("…"));
|
||||
}
|
||||
|
||||
|
||||
+28
-28
@@ -168,7 +168,7 @@ public sealed class ScriptTagCatalogTests
|
||||
var (catalog, opts) = Fresh();
|
||||
Seed(opts);
|
||||
|
||||
var paths = await catalog.GetPathsAsync(null, default);
|
||||
var paths = await catalog.GetPathsAsync(null, TestContext.Current.CancellationToken);
|
||||
|
||||
paths.ShouldContain("Plant/Modbus/dev1/Speed"); // directly under the device
|
||||
paths.ShouldContain("Plant/Modbus/dev1/Motors/Torque"); // nested under a tag group
|
||||
@@ -190,7 +190,7 @@ public sealed class ScriptTagCatalogTests
|
||||
var (catalog, opts) = Fresh();
|
||||
Seed(opts);
|
||||
|
||||
var paths = await catalog.GetPathsAsync(null, default);
|
||||
var paths = await catalog.GetPathsAsync(null, TestContext.Current.CancellationToken);
|
||||
|
||||
paths.Distinct(StringComparer.OrdinalIgnoreCase).Count().ShouldBe(paths.Count);
|
||||
}
|
||||
@@ -203,7 +203,7 @@ public sealed class ScriptTagCatalogTests
|
||||
var (catalog, opts) = Fresh();
|
||||
Seed(opts);
|
||||
|
||||
var paths = await catalog.GetPathsAsync("Plant/Modbus/dev1/", default);
|
||||
var paths = await catalog.GetPathsAsync("Plant/Modbus/dev1/", TestContext.Current.CancellationToken);
|
||||
|
||||
paths.ShouldContain("Plant/Modbus/dev1/Speed");
|
||||
paths.ShouldContain("Plant/Modbus/dev1/Motors/Torque");
|
||||
@@ -218,7 +218,7 @@ public sealed class ScriptTagCatalogTests
|
||||
var (catalog, opts) = Fresh();
|
||||
Seed(opts);
|
||||
|
||||
(await catalog.GetPathsAsync("plant/modbus", default)).ShouldContain("Plant/Modbus/dev1/Speed");
|
||||
(await catalog.GetPathsAsync("plant/modbus", TestContext.Current.CancellationToken)).ShouldContain("Plant/Modbus/dev1/Speed");
|
||||
}
|
||||
|
||||
/// <summary>A tag whose ancestry chain is broken (device row missing) has no RawPath. The runtime could
|
||||
@@ -302,7 +302,7 @@ public sealed class ScriptTagCatalogTests
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
(await catalog.GetPathsAsync(null, default)).ShouldContain("RootDrv/d0/Scalar");
|
||||
(await catalog.GetPathsAsync(null, TestContext.Current.CancellationToken)).ShouldContain("RootDrv/d0/Scalar");
|
||||
}
|
||||
|
||||
/// <summary>An empty database yields an empty list rather than throwing.</summary>
|
||||
@@ -311,7 +311,7 @@ public sealed class ScriptTagCatalogTests
|
||||
{
|
||||
var (catalog, _) = Fresh();
|
||||
|
||||
(await catalog.GetPathsAsync(null, default)).ShouldBeEmpty();
|
||||
(await catalog.GetPathsAsync(null, TestContext.Current.CancellationToken)).ShouldBeEmpty();
|
||||
}
|
||||
|
||||
/// <summary>Hover: an absolute RawPath resolves to Tag-kind info carrying the configured DataType and —
|
||||
@@ -322,7 +322,7 @@ public sealed class ScriptTagCatalogTests
|
||||
var (catalog, opts) = Fresh();
|
||||
Seed(opts);
|
||||
|
||||
var info = await catalog.GetTagInfoAsync("Plant/Modbus/dev1/Motors/Torque", default);
|
||||
var info = await catalog.GetTagInfoAsync("Plant/Modbus/dev1/Motors/Torque", TestContext.Current.CancellationToken);
|
||||
|
||||
info.ShouldNotBeNull();
|
||||
info!.Path.ShouldBe("Plant/Modbus/dev1/Motors/Torque");
|
||||
@@ -339,8 +339,8 @@ public sealed class ScriptTagCatalogTests
|
||||
var (catalog, opts) = Fresh();
|
||||
Seed(opts);
|
||||
|
||||
(await catalog.GetTagInfoAsync("Computed", default)).ShouldBeNull();
|
||||
var info = await catalog.GetTagInfoAsync("EQ-1/Computed", default);
|
||||
(await catalog.GetTagInfoAsync("Computed", TestContext.Current.CancellationToken)).ShouldBeNull();
|
||||
var info = await catalog.GetTagInfoAsync("EQ-1/Computed", TestContext.Current.CancellationToken);
|
||||
|
||||
info.ShouldNotBeNull();
|
||||
info!.Kind.ShouldBe("Virtual tag");
|
||||
@@ -356,7 +356,7 @@ public sealed class ScriptTagCatalogTests
|
||||
var (catalog, opts) = Fresh();
|
||||
Seed(opts);
|
||||
|
||||
(await catalog.GetTagInfoAsync("Motor.Speed", default)).ShouldBeNull();
|
||||
(await catalog.GetTagInfoAsync("Motor.Speed", TestContext.Current.CancellationToken)).ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>An unknown path resolves to null.</summary>
|
||||
@@ -366,7 +366,7 @@ public sealed class ScriptTagCatalogTests
|
||||
var (catalog, opts) = Fresh();
|
||||
Seed(opts);
|
||||
|
||||
(await catalog.GetTagInfoAsync("Plant/Modbus/dev1/NoSuchTag", default)).ShouldBeNull();
|
||||
(await catalog.GetTagInfoAsync("Plant/Modbus/dev1/NoSuchTag", TestContext.Current.CancellationToken)).ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>The lookup is case-SENSITIVE (Ordinal), mirroring the runtime DependencyMuxActor's
|
||||
@@ -378,8 +378,8 @@ public sealed class ScriptTagCatalogTests
|
||||
var (catalog, opts) = Fresh();
|
||||
Seed(opts);
|
||||
|
||||
(await catalog.GetTagInfoAsync("plant/modbus/dev1/speed", default)).ShouldBeNull();
|
||||
(await catalog.GetTagInfoAsync("Plant/Modbus/dev1/Speed", default)).ShouldNotBeNull();
|
||||
(await catalog.GetTagInfoAsync("plant/modbus/dev1/speed", TestContext.Current.CancellationToken)).ShouldBeNull();
|
||||
(await catalog.GetTagInfoAsync("Plant/Modbus/dev1/Speed", TestContext.Current.CancellationToken)).ShouldNotBeNull();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
@@ -396,8 +396,8 @@ public sealed class ScriptTagCatalogTests
|
||||
var (catalog, opts, factory, _) = FreshInstrumented();
|
||||
Seed(opts);
|
||||
|
||||
var first = await catalog.GetPathsAsync(null, default);
|
||||
var second = await catalog.GetPathsAsync(null, default);
|
||||
var first = await catalog.GetPathsAsync(null, TestContext.Current.CancellationToken);
|
||||
var second = await catalog.GetPathsAsync(null, TestContext.Current.CancellationToken);
|
||||
|
||||
factory.Created.ShouldBe(1);
|
||||
second.ShouldBe(first);
|
||||
@@ -412,7 +412,7 @@ public sealed class ScriptTagCatalogTests
|
||||
var (catalog, opts, factory, time) = FreshInstrumented();
|
||||
Seed(opts);
|
||||
|
||||
(await catalog.GetPathsAsync(null, default)).ShouldNotContain("Plant/Modbus/dev1/Pressure");
|
||||
(await catalog.GetPathsAsync(null, TestContext.Current.CancellationToken)).ShouldNotContain("Plant/Modbus/dev1/Pressure");
|
||||
factory.Created.ShouldBe(1);
|
||||
|
||||
using (var db = new OtOpcUaConfigDbContext(opts))
|
||||
@@ -427,7 +427,7 @@ public sealed class ScriptTagCatalogTests
|
||||
|
||||
time.Advance(TimeSpan.FromSeconds(6));
|
||||
|
||||
var after = await catalog.GetPathsAsync(null, default);
|
||||
var after = await catalog.GetPathsAsync(null, TestContext.Current.CancellationToken);
|
||||
|
||||
factory.Created.ShouldBe(2);
|
||||
after.ShouldContain("Plant/Modbus/dev1/Pressure");
|
||||
@@ -445,19 +445,19 @@ public sealed class ScriptTagCatalogTests
|
||||
factory.Gate = gate;
|
||||
|
||||
var first = Task.Run(() => catalog.GetPathsAsync(null, default));
|
||||
await factory.FirstRequest.Task.WaitAsync(TimeSpan.FromSeconds(10));
|
||||
await factory.FirstRequest.Task.WaitAsync(TimeSpan.FromSeconds(10), TestContext.Current.CancellationToken);
|
||||
|
||||
var second = Task.Run(() => catalog.GetPathsAsync(null, default));
|
||||
|
||||
// Give the second caller ample time to reach the cache. It must be parked on the rebuild gate,
|
||||
// NOT scanning: a second scan here is exactly the duplicated work this test falsifies.
|
||||
for (var i = 0; i < 50 && factory.Created < 2; i++) await Task.Delay(10);
|
||||
for (var i = 0; i < 50 && factory.Created < 2; i++) await Task.Delay(10, TestContext.Current.CancellationToken);
|
||||
factory.Created.ShouldBe(1);
|
||||
|
||||
gate.SetResult();
|
||||
|
||||
var a = await first.WaitAsync(TimeSpan.FromSeconds(10));
|
||||
var b = await second.WaitAsync(TimeSpan.FromSeconds(10));
|
||||
var a = await first.WaitAsync(TimeSpan.FromSeconds(10), TestContext.Current.CancellationToken);
|
||||
var b = await second.WaitAsync(TimeSpan.FromSeconds(10), TestContext.Current.CancellationToken);
|
||||
|
||||
factory.Created.ShouldBe(1);
|
||||
a.ShouldContain("Plant/Modbus/dev1/Speed");
|
||||
@@ -473,14 +473,14 @@ public sealed class ScriptTagCatalogTests
|
||||
var cache = new ScriptTagCatalogSnapshotCache(time);
|
||||
IReadOnlyList<ScriptTagInfo> good = [new ScriptTagInfo("A/B", "Tag", "Float", "DRV-1")];
|
||||
|
||||
(await cache.GetOrBuildAsync(_ => Task.FromResult(good), default)).ShouldBe(good);
|
||||
(await cache.GetOrBuildAsync(_ => Task.FromResult(good), TestContext.Current.CancellationToken)).ShouldBe(good);
|
||||
|
||||
time.Advance(TimeSpan.FromSeconds(6));
|
||||
await Should.ThrowAsync<InvalidOperationException>(
|
||||
cache.GetOrBuildAsync(_ => Task.FromException<IReadOnlyList<ScriptTagInfo>>(new InvalidOperationException("boom")), default));
|
||||
cache.GetOrBuildAsync(_ => Task.FromException<IReadOnlyList<ScriptTagInfo>>(new InvalidOperationException("boom")), TestContext.Current.CancellationToken));
|
||||
|
||||
// The gate was released and the old snapshot survived, so the next build still runs and succeeds.
|
||||
(await cache.GetOrBuildAsync(_ => Task.FromResult(good), default)).ShouldBe(good);
|
||||
(await cache.GetOrBuildAsync(_ => Task.FromResult(good), TestContext.Current.CancellationToken)).ShouldBe(good);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
@@ -514,7 +514,7 @@ public sealed class ScriptTagCatalogTests
|
||||
Seed(opts);
|
||||
SeedReferences(opts);
|
||||
|
||||
var names = await catalog.GetEquipmentReferenceNamesAsync("EQ-1", null, default);
|
||||
var names = await catalog.GetEquipmentReferenceNamesAsync("EQ-1", null, TestContext.Current.CancellationToken);
|
||||
|
||||
names.ShouldContain("Speed"); // TAG-EQ Name, no override
|
||||
names.ShouldContain("Path"); // TAG-SP under the "Path" override
|
||||
@@ -529,7 +529,7 @@ public sealed class ScriptTagCatalogTests
|
||||
Seed(opts);
|
||||
SeedReferences(opts);
|
||||
|
||||
var names = await catalog.GetEquipmentReferenceNamesAsync("EQ-1", "sp", default);
|
||||
var names = await catalog.GetEquipmentReferenceNamesAsync("EQ-1", "sp", TestContext.Current.CancellationToken);
|
||||
|
||||
names.ShouldContain("Speed");
|
||||
names.ShouldNotContain("Path");
|
||||
@@ -543,7 +543,7 @@ public sealed class ScriptTagCatalogTests
|
||||
Seed(opts);
|
||||
SeedReferences(opts);
|
||||
|
||||
(await catalog.GetEquipmentReferenceNamesAsync("EQ-UNKNOWN", null, default)).ShouldBeEmpty();
|
||||
(await catalog.GetEquipmentReferenceNamesAsync("", null, default)).ShouldBeEmpty();
|
||||
(await catalog.GetEquipmentReferenceNamesAsync("EQ-UNKNOWN", null, TestContext.Current.CancellationToken)).ShouldBeEmpty();
|
||||
(await catalog.GetEquipmentReferenceNamesAsync("", null, TestContext.Current.CancellationToken)).ShouldBeEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user