fix(mtconnect): share MTConnectError text extraction; pin hollow-error refusal

This commit is contained in:
Joseph Doherty
2026-08-05 06:23:08 -04:00
parent 9f17d7efa2
commit 3b8b5a77e1
3 changed files with 93 additions and 21 deletions
@@ -212,6 +212,55 @@ public sealed class MTConnectAgentClientWriteTests
outcome.AgentError.ShouldContain("Cannot find device data item: nosuchitem");
}
/// <summary>
/// An <c>MTConnectError</c> document carrying no readable <c>&lt;Error&gt;</c> detail is
/// still a refusal. The Agent said no; that it said so unhelpfully changes nothing. The trap
/// this pins is that the natural implementation — "return the joined error text, or null if
/// there is none" — hands back the same <c>null</c> that means "this body was not an error
/// document at all", silently converting the refusal into an acceptance.
/// </summary>
[Fact]
public async Task An_error_document_with_no_readable_detail_is_still_a_refusal()
{
const string HollowError =
"""
<?xml version="1.0" encoding="UTF-8"?>
<MTConnectError xmlns="urn:mtconnect.org:MTConnectError:1.3">
<Header creationTime="2026-08-05T12:00:00Z" sender="stub-agent" instanceId="1" version="1.3.0.12" bufferSize="1"/>
<Errors/>
</MTConnectError>
""";
using var client = new MTConnectAgentClient(
Options(), new StubHandler((_, _) => Task.FromResult(XmlOk(HollowError))));
var outcome = await client.WriteObservationAsync("StubDevice", "dev1_msg", "hi", CancellationToken.None);
outcome.Accepted.ShouldBeFalse();
outcome.AgentError.ShouldNotBeNullOrWhiteSpace();
}
/// <summary>The same, for an <c>&lt;Error&gt;</c> element that is present but blank.</summary>
[Fact]
public async Task An_error_document_whose_error_element_is_blank_is_still_a_refusal()
{
const string BlankError =
"""
<?xml version="1.0" encoding="UTF-8"?>
<MTConnectError xmlns="urn:mtconnect.org:MTConnectError:1.3">
<Errors><Error> </Error></Errors>
</MTConnectError>
""";
using var client = new MTConnectAgentClient(
Options(), new StubHandler((_, _) => Task.FromResult(XmlOk(BlankError))));
var outcome = await client.WriteObservationAsync("StubDevice", "dev1_msg", "hi", CancellationToken.None);
outcome.Accepted.ShouldBeFalse();
outcome.AgentError.ShouldNotBeNullOrWhiteSpace();
}
[Fact]
public async Task A_non_success_status_is_a_refusal_naming_the_status()
{