using System.Text.Json.Nodes;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Uns;
using ZB.MOM.WW.OtOpcUa.AdminUI.Uns.TagEditors;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns;
///
/// Pins the browse-picker → typed-editor round trip for MTConnect, the driver's primary
/// authoring path (a tag is committed from the universal browser, then confirmed/overridden in the
/// typed editor).
///
///
///
/// The defect this guards: had no MTConnect
/// branch, so a committed tag landed as the generic {"address": "<dataItemId>"}
/// while read only fullName. The data plane kept
/// working (the driver accepts all three id spellings), so the ONLY symptom was an empty
/// DataItem-id field in the editor — and saving from that state stamped fullName: ""
/// over the tag. Razor is exactly what this repo cannot unit-test, so both halves of the fix
/// are pinned here in plain C#.
///
///
/// Both halves are asserted: the mapper now writes the canonical fullName (fixes new
/// commits), and the model accepts dataItemId/address as fallbacks (fixes tags
/// already committed before the fix).
///
///
[Trait("Category", "Unit")]
public sealed class MTConnectBrowseCommitRoundTripTests
{
[Fact]
public void BuildTagConfig_writes_the_canonical_fullName_key_for_MTConnect()
{
var json = RawBrowseCommitMapper.BuildTagConfig(DriverTypeNames.MTConnect, "avail");
var o = JsonNode.Parse(json)!.AsObject();
o["fullName"]!.GetValue().ShouldBe("avail");
o.ContainsKey("address").ShouldBeFalse(
"the generic fallback key must not be used now that MTConnect has a typed editor");
}
[Fact]
public void A_browse_committed_tag_round_trips_through_the_typed_editor_model()
{
var committed = RawBrowseCommitMapper.BuildTagConfig(DriverTypeNames.MTConnect, "Xact");
var model = MTConnectTagConfigModel.FromJson(committed);
model.FullName.ShouldBe("Xact");
model.Validate().ShouldBeNull("a browse-committed tag must open in the editor already valid");
MTConnectTagConfigModel.FromJson(model.ToJson()).FullName.ShouldBe("Xact");
}
// ---- legacy blobs committed before the mapper fix -------------------------------------------
[Theory]
[InlineData("address")]
[InlineData("dataItemId")]
public void FromJson_accepts_the_drivers_alternate_id_spellings(string key)
{
var model = MTConnectTagConfigModel.FromJson($$"""{"{{key}}":"Ypos"}""");
model.FullName.ShouldBe("Ypos");
model.Validate().ShouldBeNull();
}
[Fact]
public void FullName_wins_over_dataItemId_which_wins_over_address()
{
MTConnectTagConfigModel
.FromJson("""{"fullName":"a","dataItemId":"b","address":"c"}""")
.FullName.ShouldBe("a");
MTConnectTagConfigModel
.FromJson("""{"dataItemId":"b","address":"c"}""")
.FullName.ShouldBe("b");
}
[Fact]
public void A_blank_alias_does_not_shadow_a_populated_one()
=> MTConnectTagConfigModel
.FromJson("""{"fullName":" ","address":"c"}""")
.FullName.ShouldBe("c");
[Fact]
public void Saving_a_legacy_blob_normalises_it_onto_fullName_and_drops_the_aliases()
{
var model = MTConnectTagConfigModel.FromJson("""{"address":"Zpos","units":"MILLIMETER"}""");
var o = JsonNode.Parse(model.ToJson())!.AsObject();
o["fullName"]!.GetValue().ShouldBe("Zpos");
o.ContainsKey("address").ShouldBeFalse(
"leaving a second id spelling behind lets the two drift apart on the next edit");
o.ContainsKey("dataItemId").ShouldBeFalse();
// Unrelated keys are still preserved — normalisation is not a purge.
o["units"]!.GetValue().ShouldBe("MILLIMETER");
}
}