Files
lmxopcua/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/CompletionTests.cs
T
Joseph Doherty d2cc071517 feat(scripting): expose the run token to scripts as ctx.CancellationToken
ScriptContext.CancellationToken is backed by an AsyncLocal that
ScriptEvaluator.RunAsync scopes around the compiled delegate, so it is
exactly the token ScriptRunGuard cancels on timeout. A polling script
can now end its own abandoned run and free the slot. AsyncLocal rather
than a field because the scripted-alarm engine reuses one context per
alarm: an orphan keeps seeing its own cancelled token. The scoping
entry point is internal (IVT to Core.Scripting) so a script cannot
replace its own token.

Editor parity: ScriptAnalysisService dot-completion now walks the
receiver's base chain (instance members only, stopping before
System.Object). Before this, a member declared only on ScriptContext
compiled and published but never completed. DiagnoseTests pins the
member as allow-listed; CompletionTests pins the completion.

Refs #731 #734
Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
2026-10-22 14:31:03 -04:00

31 lines
1.2 KiB
C#

using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.ScriptAnalysis;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.ScriptAnalysis;
public sealed class CompletionTests
{
private static readonly ScriptAnalysisService Svc = new();
private static async Task<IReadOnlyList<CompletionItem>> Complete(string code, int line, int col)
=> (await Svc.CompleteAsync(new CompletionsRequest(code, line, col))).Items;
[Fact] public async Task Dot_after_ctx_offers_context_members()
{
// caret immediately after "ctx." — column is 1-based; "ctx." is 4 chars so col 5 is just past the dot.
var labels = (await Complete("ctx.", 1, 5)).Select(i => i.Label).ToList();
labels.ShouldContain("GetTag");
labels.ShouldContain("SetVirtualTag");
labels.ShouldContain("Now");
labels.ShouldContain("Logger");
labels.ShouldContain("CancellationToken");
}
[Fact] public async Task Scope_completion_includes_the_ctx_local()
{
// a partial identifier on its own line; scope completion should surface the `ctx` local.
var labels = (await Complete("var x = c", 1, 10)).Select(i => i.Label).ToList();
labels.ShouldContain("ctx");
}
}