rename: prefix gateway projects/namespaces with ZB.MOM.WW + sln→slnx
Apply the ZB.MOM.WW. prefix to all gateway-side projects, folders,
.csproj/.sln contents, C# namespaces, using directives, generated proto
C# (csharp_namespace + checked-in generated files), InternalsVisibleTo
attributes, project-name string literals (LoadProject, .sln lookups,
worker exe paths, staticwebassets manifest), and the install/script/doc
references that point at any of the above. Migrate the solution from
.sln to .slnx via `dotnet sln migrate` and delete the old file.
External-runtime identifiers are intentionally NOT prefixed so external
configuration keeps working:
- GatewayMetrics.cs MeterName ("MxGateway.Server")
- DashboardAuthenticationDefaults Scheme/Policy ("MxGateway.Dashboard")
- GatewayRequestLoggingMiddleware logger category ("MxGateway.Request")
- StaRuntime thread name ("MxGateway.Worker.STA")
- appsettings.json root section "MxGateway" + env-var prefix
MxGateway__... and secret-name MxGateway:ApiKeyPepper
- C:\ProgramData\MxGateway\ data dir paths
Also fixes two tests that were not rename-related but became visible
while validating the rename:
- WorkerLiveMxAccessSmokeTests.ShutDownAsync: cancellation that the
gateway service correctly maps to RpcException(Cancelled) per gRPC
convention was being misclassified as a stream fault. Added a sibling
catch on RpcException with StatusCode.Cancelled.
- IntegrationTestEnvironment.ResolveRepositoryRoot: extracted IsRepositoryRoot
and made it accept either a .git marker OR a .sln/.slnx next to src/
so the worker-exe walker works in non-git working copies.
clients/proto/proto-inputs.json's protoRoot updated to point at
src/ZB.MOM.WW.MxGateway.Contracts/Protos.
Verified by `dotnet build` and a full `dotnet test` of the .slnx with
MXGATEWAY_RUN_LIVE_{MXACCESS,LDAP,GALAXY}_TESTS=1:
Tests: 472/472 pass
Worker.Tests: 280/280 pass (4 dev-rig [Fact(Skip=...)] skipped)
IntegrationTests: 18/18 pass
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||
using ZB.MOM.WW.MxGateway.Worker.Conversion;
|
||||
|
||||
namespace ZB.MOM.WW.MxGateway.Worker.Tests.Conversion;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for <see cref="HResultConverter"/>.
|
||||
/// </summary>
|
||||
public sealed class HResultConverterTests
|
||||
{
|
||||
private readonly HResultConverter _converter = new();
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that Convert captures the HResult from a COM exception.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Convert_WithComException_CapturesExceptionHResult()
|
||||
{
|
||||
COMException exception = new("Sensitive provider text should not be copied.", unchecked((int)0x80070057));
|
||||
|
||||
HResultConversion converted = _converter.Convert(exception);
|
||||
|
||||
Assert.Equal(unchecked((int)0x80070057), converted.HResult);
|
||||
Assert.Equal(ProtocolStatusCode.MxaccessFailure, converted.ProtocolStatus.Code);
|
||||
Assert.Contains("0x80070057", converted.ProtocolStatus.Message);
|
||||
Assert.Contains(typeof(COMException).FullName!, converted.DiagnosticMessage);
|
||||
Assert.DoesNotContain("Sensitive provider text", converted.DiagnosticMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that CreateProtocolStatus returns OK for a success HResult.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CreateProtocolStatus_WithSuccessHResult_ReturnsOk()
|
||||
{
|
||||
ProtocolStatus status = _converter.CreateProtocolStatus(0);
|
||||
|
||||
Assert.Equal(ProtocolStatusCode.Ok, status.Code);
|
||||
Assert.Equal("HRESULT 0x00000000", status.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that Convert captures the HResult from a non-COM exception.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Convert_WithNonComException_CapturesExceptionHResult()
|
||||
{
|
||||
InvalidOperationException exception = new("do not include this");
|
||||
|
||||
HResultConversion converted = _converter.Convert(exception);
|
||||
|
||||
Assert.Equal(exception.HResult, converted.HResult);
|
||||
Assert.Equal(ProtocolStatusCode.MxaccessFailure, converted.ProtocolStatus.Code);
|
||||
Assert.Contains("0x", converted.DiagnosticMessage);
|
||||
Assert.DoesNotContain("do not include this", converted.DiagnosticMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||
using ZB.MOM.WW.MxGateway.Worker.Conversion;
|
||||
|
||||
namespace ZB.MOM.WW.MxGateway.Worker.Tests.Conversion;
|
||||
|
||||
public sealed class MxStatusProxyConverterTests
|
||||
{
|
||||
private readonly MxStatusProxyConverter _converter = new();
|
||||
|
||||
/// <summary>Verifies that status struct fields are preserved during conversion.</summary>
|
||||
[Fact]
|
||||
public void Convert_WithStatusStruct_PreservesStatusFields()
|
||||
{
|
||||
FakeMxStatusProxy status = new()
|
||||
{
|
||||
success = 1,
|
||||
category = 5,
|
||||
detectedBy = 3,
|
||||
detail = 21,
|
||||
};
|
||||
|
||||
MxStatusProxy converted = _converter.Convert(status);
|
||||
|
||||
Assert.Equal(1, converted.Success);
|
||||
Assert.Equal(MxStatusCategory.OperationalError, converted.Category);
|
||||
Assert.Equal(MxStatusSource.RespondingNmx, converted.DetectedBy);
|
||||
Assert.Equal(21, converted.Detail);
|
||||
Assert.Equal(5, converted.RawCategory);
|
||||
Assert.Equal(3, converted.RawDetectedBy);
|
||||
Assert.Equal("Invalid reference", converted.DiagnosticText);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that status arrays are converted without collapsing duplicate entries.</summary>
|
||||
[Fact]
|
||||
public void ConvertMany_WithStatusArray_DoesNotCollapseEntries()
|
||||
{
|
||||
FakeMxStatusProxy[] statuses =
|
||||
[
|
||||
new()
|
||||
{
|
||||
success = 1,
|
||||
category = 0,
|
||||
detectedBy = 0,
|
||||
detail = 0,
|
||||
},
|
||||
new()
|
||||
{
|
||||
success = 0,
|
||||
category = 6,
|
||||
detectedBy = 5,
|
||||
detail = 33,
|
||||
},
|
||||
];
|
||||
|
||||
IReadOnlyList<MxStatusProxy> converted = _converter.ConvertMany(statuses);
|
||||
|
||||
Assert.Equal(2, converted.Count);
|
||||
Assert.Equal(MxStatusCategory.Ok, converted[0].Category);
|
||||
Assert.Equal(MxStatusCategory.SecurityError, converted[1].Category);
|
||||
Assert.Equal(MxStatusSource.RespondingAutomationObject, converted[1].DetectedBy);
|
||||
Assert.Equal("Write access denied", converted[1].DiagnosticText);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that unknown category and source values preserve raw field values.</summary>
|
||||
[Fact]
|
||||
public void Convert_WithUnknownCategoryAndSource_PreservesRawFields()
|
||||
{
|
||||
FakeMxStatusProxy status = new()
|
||||
{
|
||||
success = -1,
|
||||
category = 99,
|
||||
detectedBy = 42,
|
||||
detail = 1234,
|
||||
};
|
||||
|
||||
MxStatusProxy converted = _converter.Convert(status);
|
||||
|
||||
Assert.Equal(-1, converted.Success);
|
||||
Assert.Equal(MxStatusCategory.Unknown, converted.Category);
|
||||
Assert.Equal(MxStatusSource.Unknown, converted.DetectedBy);
|
||||
Assert.Equal(99, converted.RawCategory);
|
||||
Assert.Equal(42, converted.RawDetectedBy);
|
||||
Assert.Equal(1234, converted.Detail);
|
||||
Assert.Equal(string.Empty, converted.DiagnosticText);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that completion-only status bytes are preserved as hex metadata.</summary>
|
||||
[Fact]
|
||||
public void PreserveCompletionOnlyStatusBytes_ReturnsRawHexMetadata()
|
||||
{
|
||||
string rawStatus = _converter.PreserveCompletionOnlyStatusBytes(
|
||||
[0x00, 0x00, 0x50, 0x80, 0x00]);
|
||||
|
||||
Assert.Equal("completion_only_status_hex=0000508000", rawStatus);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that missing status fields throw a conversion exception.</summary>
|
||||
[Fact]
|
||||
public void Convert_WithMissingStatusField_ThrowsConversionException()
|
||||
{
|
||||
MxStatusConversionException exception =
|
||||
Assert.Throws<MxStatusConversionException>(() => _converter.Convert(new MissingFields()));
|
||||
|
||||
Assert.Contains("success", exception.Message);
|
||||
}
|
||||
|
||||
public struct FakeMxStatusProxy
|
||||
{
|
||||
public short success;
|
||||
|
||||
public int category;
|
||||
|
||||
public int detectedBy;
|
||||
|
||||
public short detail;
|
||||
}
|
||||
|
||||
private sealed class MissingFields
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
using System;
|
||||
using Google.Protobuf;
|
||||
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||
using ZB.MOM.WW.MxGateway.Worker.Conversion;
|
||||
using ProtobufTimestamp = Google.Protobuf.WellKnownTypes.Timestamp;
|
||||
|
||||
namespace ZB.MOM.WW.MxGateway.Worker.Tests.Conversion;
|
||||
|
||||
public sealed class VariantConverterTests
|
||||
{
|
||||
private readonly VariantConverter _converter = new();
|
||||
|
||||
/// <summary>Verifies that supported scalar types are converted with correct data type and value kind.</summary>
|
||||
/// <param name="value">Scalar value to convert.</param>
|
||||
/// <param name="expectedDataType">Expected MxDataType of the converted value.</param>
|
||||
/// <param name="expectedKind">Expected KindOneofCase of the converted value.</param>
|
||||
[Theory]
|
||||
[InlineData(true, MxDataType.Boolean, MxValue.KindOneofCase.BoolValue)]
|
||||
[InlineData(42, MxDataType.Integer, MxValue.KindOneofCase.Int32Value)]
|
||||
[InlineData(42L, MxDataType.Integer, MxValue.KindOneofCase.Int64Value)]
|
||||
[InlineData(1.25f, MxDataType.Float, MxValue.KindOneofCase.FloatValue)]
|
||||
[InlineData(2.5d, MxDataType.Double, MxValue.KindOneofCase.DoubleValue)]
|
||||
[InlineData("value", MxDataType.String, MxValue.KindOneofCase.StringValue)]
|
||||
public void Convert_WithSupportedScalar_ProjectsTypedValue(
|
||||
object value,
|
||||
MxDataType expectedDataType,
|
||||
MxValue.KindOneofCase expectedKind)
|
||||
{
|
||||
MxValue converted = _converter.Convert(value);
|
||||
|
||||
Assert.Equal(expectedDataType, converted.DataType);
|
||||
Assert.Equal(expectedKind, converted.KindCase);
|
||||
Assert.False(string.IsNullOrWhiteSpace(converted.VariantType));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that DateTime values are converted to protobuf timestamps.</summary>
|
||||
[Fact]
|
||||
public void Convert_WithDateTime_ProjectsTimestamp()
|
||||
{
|
||||
DateTime dateTime = new(2026, 4, 26, 17, 45, 0, DateTimeKind.Utc);
|
||||
|
||||
MxValue converted = _converter.Convert(dateTime);
|
||||
|
||||
Assert.Equal(MxDataType.Time, converted.DataType);
|
||||
Assert.Equal(ProtobufTimestamp.FromDateTime(dateTime), converted.TimestampValue);
|
||||
Assert.Equal("VT_DATE", converted.VariantType);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that scalar MxValue kinds convert to the matching boxed CLR type for a COM write.</summary>
|
||||
[Fact]
|
||||
public void ConvertToComValue_WithInt32_ReturnsBoxedInt()
|
||||
{
|
||||
object? result = _converter.ConvertToComValue(new MxValue { Int32Value = 123 });
|
||||
|
||||
Assert.Equal(123, Assert.IsType<int>(result));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a boolean MxValue converts to a boxed bool for a COM write.</summary>
|
||||
[Fact]
|
||||
public void ConvertToComValue_WithBool_ReturnsBoxedBool()
|
||||
{
|
||||
object? result = _converter.ConvertToComValue(new MxValue { BoolValue = true });
|
||||
|
||||
Assert.True(Assert.IsType<bool>(result));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a string MxValue converts to a string for a COM write.</summary>
|
||||
[Fact]
|
||||
public void ConvertToComValue_WithString_ReturnsString()
|
||||
{
|
||||
object? result = _converter.ConvertToComValue(new MxValue { StringValue = "abc" });
|
||||
|
||||
Assert.Equal("abc", Assert.IsType<string>(result));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a timestamp MxValue converts to a UTC DateTime the COM marshaler renders as VT_DATE.</summary>
|
||||
[Fact]
|
||||
public void ConvertToComValue_WithTimestamp_ReturnsUtcDateTime()
|
||||
{
|
||||
DateTime dateTime = new(2026, 5, 19, 12, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
object? result = _converter.ConvertToComValue(
|
||||
new MxValue { TimestampValue = ProtobufTimestamp.FromDateTime(dateTime) });
|
||||
|
||||
Assert.Equal(dateTime, Assert.IsType<DateTime>(result));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an MXAccess-null MxValue converts to a CLR null.</summary>
|
||||
[Fact]
|
||||
public void ConvertToComValue_WithNull_ReturnsNull()
|
||||
{
|
||||
object? result = _converter.ConvertToComValue(new MxValue { IsNull = true });
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an integer-array MxValue converts to an int array the COM marshaler renders as a SAFEARRAY.</summary>
|
||||
[Fact]
|
||||
public void ConvertToComValue_WithInt32Array_ReturnsInt32Array()
|
||||
{
|
||||
MxValue value = new()
|
||||
{
|
||||
ArrayValue = new MxArray
|
||||
{
|
||||
Int32Values = new Int32Array { Values = { 1, 2, 3 } },
|
||||
},
|
||||
};
|
||||
|
||||
object? result = _converter.ConvertToComValue(value);
|
||||
|
||||
Assert.Equal(new[] { 1, 2, 3 }, Assert.IsType<int[]>(result));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an MxValue with no value kind set cannot be converted for a COM write.</summary>
|
||||
[Fact]
|
||||
public void ConvertToComValue_WithNoKind_Throws()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => _converter.ConvertToComValue(new MxValue()));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that file time values with expected time data type are converted to protobuf timestamps.</summary>
|
||||
[Fact]
|
||||
public void Convert_WithFileTimeAndExpectedTime_ProjectsTimestamp()
|
||||
{
|
||||
DateTime dateTime = new(2026, 4, 26, 17, 45, 0, DateTimeKind.Utc);
|
||||
|
||||
MxValue converted = _converter.Convert(dateTime.ToFileTimeUtc(), MxDataType.Time);
|
||||
|
||||
Assert.Equal(MxDataType.Time, converted.DataType);
|
||||
Assert.Equal(ProtobufTimestamp.FromDateTime(dateTime), converted.TimestampValue);
|
||||
Assert.Equal("VT_I8", converted.VariantType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker-010 regression: a 32-bit <see cref="uint"/> with an expected
|
||||
/// data type of <see cref="MxDataType.Time"/> must not be projected as a
|
||||
/// Windows FILETIME. A uint can only hold the low 32 bits of a FILETIME,
|
||||
/// which would silently render as a near-1601 timestamp; the converter
|
||||
/// must fall through to an integer projection instead.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Convert_WithUInt32AndExpectedTime_DoesNotProjectFileTime()
|
||||
{
|
||||
const uint value = 123456789u;
|
||||
|
||||
MxValue converted = _converter.Convert(value, MxDataType.Time);
|
||||
|
||||
Assert.Equal(MxDataType.Integer, converted.DataType);
|
||||
Assert.Equal(MxValue.KindOneofCase.Int64Value, converted.KindCase);
|
||||
Assert.Equal(value, converted.Int64Value);
|
||||
Assert.Equal("VT_UI4", converted.VariantType);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that null-like values preserve their null semantics and variant type.</summary>
|
||||
/// <param name="value">Null-like value to convert.</param>
|
||||
/// <param name="expectedVariantType">Expected variant type string.</param>
|
||||
[Theory]
|
||||
[InlineData(null, "VT_EMPTY")]
|
||||
[InlineData(typeof(DBNull), "VT_NULL")]
|
||||
public void Convert_WithNullLikeValue_PreservesNull(
|
||||
object? value,
|
||||
string expectedVariantType)
|
||||
{
|
||||
object? actualValue = value is System.Type ? DBNull.Value : value;
|
||||
|
||||
MxValue converted = _converter.Convert(actualValue);
|
||||
|
||||
Assert.True(converted.IsNull);
|
||||
Assert.Equal(MxDataType.NoData, converted.DataType);
|
||||
Assert.Equal(expectedVariantType, converted.VariantType);
|
||||
Assert.Equal(MxValue.KindOneofCase.None, converted.KindCase);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that supported array types are converted with correct element type and dimensions.</summary>
|
||||
[Fact]
|
||||
public void ConvertArray_WithSupportedArrays_ProjectsTypedValuesAndDimensions()
|
||||
{
|
||||
MxValue bools = _converter.Convert(new[] { true, false });
|
||||
MxValue ints = _converter.Convert(new[] { 1, 2, 3 });
|
||||
MxValue floats = _converter.Convert(new[] { 1.25f, 2.5f });
|
||||
MxValue doubles = _converter.Convert(new[] { 1.25d, 2.5d });
|
||||
MxValue strings = _converter.Convert(new[] { "one", "two" });
|
||||
MxValue times = _converter.Convert(new[]
|
||||
{
|
||||
new DateTime(2026, 4, 26, 17, 45, 0, DateTimeKind.Utc),
|
||||
new DateTime(2026, 4, 26, 17, 46, 0, DateTimeKind.Utc),
|
||||
});
|
||||
|
||||
Assert.Equal(new[] { true, false }, bools.ArrayValue.BoolValues.Values);
|
||||
Assert.Equal(new[] { 1, 2, 3 }, ints.ArrayValue.Int32Values.Values);
|
||||
Assert.Equal(new[] { 1.25f, 2.5f }, floats.ArrayValue.FloatValues.Values);
|
||||
Assert.Equal(new[] { 1.25d, 2.5d }, doubles.ArrayValue.DoubleValues.Values);
|
||||
Assert.Equal(new[] { "one", "two" }, strings.ArrayValue.StringValues.Values);
|
||||
Assert.Equal(2, times.ArrayValue.TimestampValues.Values.Count);
|
||||
Assert.Equal(new uint[] { 2 }, bools.ArrayValue.Dimensions);
|
||||
Assert.Equal(MxDataType.Boolean, bools.ArrayValue.ElementDataType);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that multidimensional arrays preserve rank and dimension information.</summary>
|
||||
[Fact]
|
||||
public void ConvertArray_WithMultidimensionalArray_PreservesRankAndDimensions()
|
||||
{
|
||||
int[,] values =
|
||||
{
|
||||
{ 1, 2, 3 },
|
||||
{ 4, 5, 6 },
|
||||
};
|
||||
|
||||
MxValue converted = _converter.Convert(values);
|
||||
|
||||
Assert.Equal(new uint[] { 2, 3 }, converted.ArrayValue.Dimensions);
|
||||
Assert.Equal(new[] { 1, 2, 3, 4, 5, 6 }, converted.ArrayValue.Int32Values.Values);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that file time arrays with expected time data type are converted to timestamp arrays.</summary>
|
||||
[Fact]
|
||||
public void ConvertArray_WithExpectedTimeAndFileTimeValues_ProjectsTimestampArray()
|
||||
{
|
||||
DateTime first = new(2026, 4, 26, 17, 45, 0, DateTimeKind.Utc);
|
||||
DateTime second = new(2026, 4, 26, 17, 46, 0, DateTimeKind.Utc);
|
||||
|
||||
MxValue converted = _converter.Convert(
|
||||
new[] { first.ToFileTimeUtc(), second.ToFileTimeUtc() },
|
||||
MxDataType.Time);
|
||||
|
||||
Assert.Equal(MxDataType.Time, converted.ArrayValue.ElementDataType);
|
||||
Assert.Equal(
|
||||
new[] { ProtobufTimestamp.FromDateTime(first), ProtobufTimestamp.FromDateTime(second) },
|
||||
converted.ArrayValue.TimestampValues.Values);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that unknown scalar types preserve raw value and diagnostic metadata.</summary>
|
||||
[Fact]
|
||||
public void Convert_WithUnknownScalar_PreservesRawMetadata()
|
||||
{
|
||||
UnsupportedVariant value = new("opaque");
|
||||
|
||||
MxValue converted = _converter.Convert(value);
|
||||
|
||||
Assert.Equal(MxDataType.Unknown, converted.DataType);
|
||||
Assert.Equal(MxValue.KindOneofCase.RawValue, converted.KindCase);
|
||||
Assert.Contains(typeof(UnsupportedVariant).FullName!, converted.VariantType);
|
||||
Assert.Contains(typeof(UnsupportedVariant).FullName!, converted.RawDiagnostic);
|
||||
Assert.Equal(ByteString.CopyFromUtf8("opaque"), converted.RawValue);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that unknown array types preserve raw values and diagnostic metadata.</summary>
|
||||
[Fact]
|
||||
public void ConvertArray_WithUnknownArray_PreservesRawMetadata()
|
||||
{
|
||||
UnsupportedVariant[] values =
|
||||
[
|
||||
new("first"),
|
||||
new("second"),
|
||||
];
|
||||
|
||||
MxValue converted = _converter.Convert(values);
|
||||
|
||||
Assert.Equal(MxDataType.Unknown, converted.ArrayValue.ElementDataType);
|
||||
Assert.Equal(MxArray.ValuesOneofCase.RawValues, converted.ArrayValue.ValuesCase);
|
||||
Assert.Equal(new uint[] { 2 }, converted.ArrayValue.Dimensions);
|
||||
Assert.Equal("first", converted.ArrayValue.RawValues.Values[0].ToStringUtf8());
|
||||
Assert.Contains(typeof(UnsupportedVariant).FullName!, converted.ArrayValue.RawDiagnostic);
|
||||
}
|
||||
|
||||
/// <summary>Fake unsupported variant type for testing unknown type handling.</summary>
|
||||
private sealed class UnsupportedVariant
|
||||
{
|
||||
private readonly string _value;
|
||||
|
||||
/// <summary>Initializes a new instance of the UnsupportedVariant class.</summary>
|
||||
/// <param name="value">The opaque value.</param>
|
||||
public UnsupportedVariant(string value)
|
||||
{
|
||||
_value = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user