server(galaxy): GetAlarmAttributesAsync discovery query + alarm-attribute row mapping (Task 11)

This commit is contained in:
Joseph Doherty
2026-06-13 09:18:11 -04:00
parent f3616cc7fa
commit f113ca53a1
5 changed files with 236 additions and 0 deletions
@@ -0,0 +1,64 @@
using ZB.MOM.WW.MxGateway.Server.Galaxy;
namespace ZB.MOM.WW.MxGateway.Tests.Galaxy;
/// <summary>
/// Pure mapper tests for <see cref="GalaxyRepository.MapAlarmRow"/>. These assert the
/// FullTagReference / SourceObjectReference derivation produced by
/// <c>AlarmAttributesSql</c> without touching a database: the SQL projects
/// <c>tag_name</c> as the source object and <c>tag_name + '.' + attribute_name</c> as
/// the full reference, exactly as <c>AttributesSql</c> does.
/// </summary>
public sealed class GalaxyAlarmAttributeMappingTests
{
/// <summary>Verifies the mapper copies both projected columns onto the row.</summary>
[Fact]
public void MapAlarmRow_CopiesProjectedColumns()
{
GalaxyAlarmAttributeRow row = GalaxyRepository.MapAlarmRow(
fullTagReference: "Tank01.Level.HiHi",
sourceObjectReference: "Tank01");
Assert.Equal("Tank01.Level.HiHi", row.FullTagReference);
Assert.Equal("Tank01", row.SourceObjectReference);
}
/// <summary>
/// Verifies <see cref="GalaxyAlarmAttributeRow.AckCommentSubtag"/> is always empty:
/// the schema does not expose an ack-comment address, so the watch-list resolver
/// composes it later from configuration.
/// </summary>
[Fact]
public void MapAlarmRow_LeavesAckCommentSubtagEmpty()
{
GalaxyAlarmAttributeRow row = GalaxyRepository.MapAlarmRow(
fullTagReference: "Tank01.Level.HiHi",
sourceObjectReference: "Tank01");
Assert.Equal(string.Empty, row.AckCommentSubtag);
}
/// <summary>
/// Verifies the SourceObjectReference is the owning object (the SQL <c>tag_name</c>),
/// i.e. the segment that precedes the first attribute dot in the full reference, even
/// when the attribute itself is a multi-segment extension path.
/// </summary>
[Theory]
[InlineData("Tank01", "Level.HiHi", "Tank01.Level.HiHi")]
[InlineData("Pump_001", "Speed", "Pump_001.Speed")]
[InlineData("TestAlarm001", "Alarm001", "TestAlarm001.Alarm001")]
public void MapAlarmRow_SourceObjectIsSegmentBeforeFirstAttributeDot(
string tagName,
string attributeName,
string expectedFullReference)
{
// Mirror the AlarmAttributesSql projection: full_tag_reference = tag_name + '.' + attribute_name.
string fullTagReference = tagName + "." + attributeName;
GalaxyAlarmAttributeRow row = GalaxyRepository.MapAlarmRow(fullTagReference, tagName);
Assert.Equal(expectedFullReference, row.FullTagReference);
Assert.Equal(tagName, row.SourceObjectReference);
Assert.Equal(row.FullTagReference, row.SourceObjectReference + "." + attributeName);
}
}
@@ -378,6 +378,10 @@ public sealed class GalaxyHierarchyCacheTests : IDisposable
/// <inheritdoc />
public Task<List<GalaxyAttributeRow>> GetAttributesAsync(CancellationToken ct = default)
=> throw new InvalidOperationException("GetAttributesAsync should not be reached");
/// <inheritdoc />
public Task<List<GalaxyAlarmAttributeRow>> GetAlarmAttributesAsync(CancellationToken ct = default)
=> throw new InvalidOperationException("GetAlarmAttributesAsync should not be reached");
}
/// <summary>Snapshot store whose <see cref="SaveAsync"/> cancels the token mid-save.</summary>
@@ -465,6 +469,10 @@ public sealed class GalaxyHierarchyCacheTests : IDisposable
GetAttributesCount++;
return Task.FromResult(_attributes);
}
/// <inheritdoc />
public Task<List<GalaxyAlarmAttributeRow>> GetAlarmAttributesAsync(CancellationToken ct = default)
=> Task.FromResult(new List<GalaxyAlarmAttributeRow>());
}
/// <inheritdoc />
@@ -518,6 +526,10 @@ public sealed class GalaxyHierarchyCacheTests : IDisposable
GetAttributesCount++;
throw toThrow;
}
/// <inheritdoc />
public Task<List<GalaxyAlarmAttributeRow>> GetAlarmAttributesAsync(CancellationToken ct = default)
=> throw toThrow;
}
}