using FluentAssertions; using Xunit; using ZB.MOM.WW.LmxProxy.Client.Domain; using ZB.MOM.WW.LmxProxy.Client.Tests.Fakes; namespace ZB.MOM.WW.LmxProxy.Client.Tests; public class LmxProxyClientReadWriteTests { [Fact] public async Task ReadAsync_ReturnsVtqFromResponse() { var (client, fake) = TestableClient.CreateConnected(); fake.ReadResponseToReturn = new ReadResponse { Success = true, Vtq = new VtqMessage { Tag = "TestTag", Value = new TypedValue { DoubleValue = 42.5 }, TimestampUtcTicks = new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks, Quality = new QualityCode { StatusCode = 0x00000000 } } }; var result = await client.ReadAsync("TestTag"); result.Value.Should().Be(42.5); result.Quality.Should().Be(Quality.Good); fake.ReadCalls.Should().HaveCount(1); fake.ReadCalls[0].Tag.Should().Be("TestTag"); fake.ReadCalls[0].SessionId.Should().Be("test-session-123"); client.Dispose(); } [Fact] public async Task ReadAsync_ThrowsOnFailureResponse() { var (client, fake) = TestableClient.CreateConnected(); fake.ReadResponseToReturn = new ReadResponse { Success = false, Message = "Tag not found" }; var act = () => client.ReadAsync("BadTag"); await act.Should().ThrowAsync() .WithMessage("*Tag not found*"); client.Dispose(); } [Fact] public async Task ReadAsync_ThrowsWhenNotConnected() { var client = new LmxProxyClient("localhost", 50051, null, null); var act = () => client.ReadAsync("AnyTag"); await act.Should().ThrowAsync() .WithMessage("*not connected*"); client.Dispose(); } [Fact] public async Task ReadBatchAsync_ReturnsDictionaryOfVtqs() { var (client, fake) = TestableClient.CreateConnected(); fake.ReadBatchResponseToReturn = new ReadBatchResponse { Success = true, Vtqs = [ new VtqMessage { Tag = "Tag1", Value = new TypedValue { Int32Value = 100 }, TimestampUtcTicks = DateTime.UtcNow.Ticks, Quality = new QualityCode { StatusCode = 0x00000000 } }, new VtqMessage { Tag = "Tag2", Value = new TypedValue { BoolValue = true }, TimestampUtcTicks = DateTime.UtcNow.Ticks, Quality = new QualityCode { StatusCode = 0x00000000 } } ] }; var result = await client.ReadBatchAsync(["Tag1", "Tag2"]); result.Should().HaveCount(2); result["Tag1"].Value.Should().Be(100); result["Tag2"].Value.Should().Be(true); client.Dispose(); } [Fact] public async Task WriteAsync_SendsTypedValueDirectly() { var (client, fake) = TestableClient.CreateConnected(); var typedValue = new TypedValue { DoubleValue = 99.9 }; await client.WriteAsync("TestTag", typedValue); fake.WriteCalls.Should().HaveCount(1); fake.WriteCalls[0].Tag.Should().Be("TestTag"); fake.WriteCalls[0].Value.Should().NotBeNull(); fake.WriteCalls[0].Value!.DoubleValue.Should().Be(99.9); client.Dispose(); } [Fact] public async Task WriteAsync_ThrowsOnFailureResponse() { var (client, fake) = TestableClient.CreateConnected(); fake.WriteResponseToReturn = new WriteResponse { Success = false, Message = "Write error" }; var act = () => client.WriteAsync("Tag", new TypedValue { Int32Value = 1 }); await act.Should().ThrowAsync() .WithMessage("*Write error*"); client.Dispose(); } [Fact] public async Task WriteBatchAsync_SendsAllItems() { var (client, fake) = TestableClient.CreateConnected(); var values = new Dictionary { ["Tag1"] = new TypedValue { DoubleValue = 1.0 }, ["Tag2"] = new TypedValue { Int32Value = 2 }, ["Tag3"] = new TypedValue { BoolValue = true } }; await client.WriteBatchAsync(values); fake.WriteBatchCalls.Should().HaveCount(1); fake.WriteBatchCalls[0].Items.Should().HaveCount(3); client.Dispose(); } [Fact] public async Task WriteBatchAndWaitAsync_ReturnsResponse() { var (client, fake) = TestableClient.CreateConnected(); fake.WriteBatchAndWaitResponseToReturn = new WriteBatchAndWaitResponse { Success = true, FlagReached = true, ElapsedMs = 150, WriteResults = [new WriteResult { Tag = "Tag1", Success = true }] }; var values = new Dictionary { ["Tag1"] = new TypedValue { Int32Value = 1 } }; var result = await client.WriteBatchAndWaitAsync( values, "FlagTag", new TypedValue { BoolValue = true }); result.FlagReached.Should().BeTrue(); result.ElapsedMs.Should().Be(150); client.Dispose(); } [Fact] public async Task CheckApiKeyAsync_ReturnsApiKeyInfo() { var (client, fake) = TestableClient.CreateConnected(); fake.CheckApiKeyResponseToReturn = new CheckApiKeyResponse { IsValid = true, Message = "Admin key" }; var result = await client.CheckApiKeyAsync("my-api-key"); result.IsValid.Should().BeTrue(); result.Description.Should().Be("Admin key"); client.Dispose(); } }