fix(multivalue): Wave-2 review fixes (MV-2/MV-4/MV-12)

- MV-2: guard unsupported element type before parse (no misleading re-wrap); add Float round-trip test
- MV-4: carry ElementDataType through the two validation-flatten ResolvedAttribute sites (ManagementActor.HandleValidateTemplate, BundleImporter.BuildFlattenedConfigForValidation) so MV-5 validation sees element type via every entry point
- MV-12: include ElementDataType in TemplateAttribute add/update audit payloads + fix stale docstring
This commit is contained in:
Joseph Doherty
2026-06-16 15:33:27 -04:00
parent 02aff2436e
commit 492b41f0fd
4 changed files with 16 additions and 2 deletions
@@ -72,6 +72,8 @@ public static class AttributeValueCodec
private static object? ParseScalar(string? s, DataType t)
{
if (s is null) throw new FormatException("List elements may not be null.");
if (!IsValidElementType(t))
throw new FormatException($"Unsupported list element type '{t}'.");
var c = CultureInfo.InvariantCulture;
try
{
@@ -524,6 +524,7 @@ public class ManagementActor : ReceiveActor
CanonicalName = a.Name,
Value = a.Value,
DataType = a.DataType.ToString(),
ElementDataType = a.ElementDataType?.ToString(),
IsLocked = a.IsLocked,
DataSourceReference = a.DataSourceReference
}).ToList(),
@@ -1083,8 +1083,8 @@ public sealed class BundleImporter : IBundleImporter
/// enumerates for the "Template overwritten" action.
/// <para>
/// Update detection compares every scalar field (Value, DataType,
/// IsLocked, Description, DataSourceReference) — no field change → no
/// audit row, so an idempotent overwrite produces no noise.
/// ElementDataType, IsLocked, Description, DataSourceReference) — no field
/// change → no audit row, so an idempotent overwrite produces no noise.
/// </para>
/// </summary>
private async Task SyncTemplateAttributesAsync(
@@ -1146,6 +1146,7 @@ public sealed class BundleImporter : IBundleImporter
AttributeName = current.Name,
current.Value,
current.DataType,
current.ElementDataType,
current.IsLocked,
current.Description,
current.DataSourceReference,
@@ -1176,6 +1177,7 @@ public sealed class BundleImporter : IBundleImporter
AttributeName = newAttr.Name,
newAttr.Value,
newAttr.DataType,
newAttr.ElementDataType,
newAttr.IsLocked,
newAttr.Description,
newAttr.DataSourceReference,
@@ -2304,6 +2306,7 @@ public sealed class BundleImporter : IBundleImporter
CanonicalName = a.Name,
Value = a.Value,
DataType = a.DataType.ToString(),
ElementDataType = a.ElementDataType?.ToString(),
IsLocked = a.IsLocked,
Description = a.Description,
DataSourceReference = a.DataSourceReference,
@@ -49,6 +49,14 @@ public class AttributeValueCodecTests
Assert.Equal("[\"ACME, Inc.\"]",
AttributeValueCodec.Encode(new List<string> { "ACME, Inc." }));
[Fact]
public void RoundTrip_FloatList()
{
var json = AttributeValueCodec.Encode(new List<float> { 1.5f, 2.25f, -3.75f });
var back = (IList<float>)AttributeValueCodec.Decode(json, DataType.List, DataType.Float)!;
Assert.Equal(new[] { 1.5f, 2.25f, -3.75f }, back);
}
[Fact]
public void Encode_DoubleList_IsInvariant()
{