diff --git a/docs/test_infra/test_infra.md b/docs/test_infra/test_infra.md
index f39916d3..e551f8eb 100644
--- a/docs/test_infra/test_infra.md
+++ b/docs/test_infra/test_infra.md
@@ -52,6 +52,8 @@ In addition to the local Docker services, the following remote services are avai
**Primary/backup testing**: The dual OPC UA test servers (ports 50000 and 50010) in local Docker provide primary/backup endpoint pairs for testing Data Connection Layer failover. Use `docker compose stop opcua` to simulate primary failure and verify automatic failover to the backup.
+**Alarms & Conditions (native alarms)**: The infra OPC PLC server **does** expose OPC UA Alarms & Conditions — a `ConditionRefresh` against its event notifier replays the active condition set and a `SnapshotComplete`, so the native alarm mirror (`IAlarmSubscribableConnection` → `NativeAlarmActor`) can be exercised live. The `OpcUaAlarmLiveSmokeTests.SubscribeAlarms_DeliversConditionRefreshSnapshot` `[SkippableFact]` (Trait `RequiresOpcUa`) round-trips against `opc.tcp://localhost:50000` and asserts the snapshot arrives; it reports **Skipped** (not failed) when the server is unreachable or — on a substitute server that lacks A&C — when no snapshot arrives within the window. The MxAccess Gateway alarm feed (`MxGateway` protocol) requires a live gateway and is verified via the `docker-env2` manual deploy check, not in CI.
+
## Connection Strings
For use in `appsettings.Development.json`:
diff --git a/tests/ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Tests/OpcUaAlarmLiveSmokeTests.cs b/tests/ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Tests/OpcUaAlarmLiveSmokeTests.cs
new file mode 100644
index 00000000..24f6c162
--- /dev/null
+++ b/tests/ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Tests/OpcUaAlarmLiveSmokeTests.cs
@@ -0,0 +1,81 @@
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using ZB.MOM.WW.ScadaBridge.Commons.Types.Alarms;
+using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
+using ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters;
+
+namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Tests;
+
+///
+/// Task 28: live smoke test for the OPC UA Alarms & Conditions adapter
+/// ( as IAlarmSubscribableConnection).
+///
+/// Round-trips against an alarm-capable OPC UA endpoint
+/// (opc.tcp://localhost:50000 — the infra OPC PLC server, see
+/// infra/docker-compose.yml). Marked [SkippableFact] so it reports
+/// Skipped — not failed — when the endpoint is unreachable OR does not expose
+/// Alarms & Conditions (no ConditionRefresh snapshot arrives). The OPC PLC
+/// simulator does not reliably expose A&C; see
+/// docs/test_infra/test_infra.md for the alarm-capable-server requirement.
+///
+/// The pure field→transition mapping is covered without a server by
+/// ; this test proves the live
+/// event-subscription + ConditionRefresh path end to end when infra supports it.
+///
+[Trait("Category", "RequiresOpcUa")]
+public class OpcUaAlarmLiveSmokeTests
+{
+ private const string EndpointUrl = "opc.tcp://localhost:50000";
+
+ [SkippableFact]
+ public async Task SubscribeAlarms_DeliversConditionRefreshSnapshot()
+ {
+ using var loggerFactory = LoggerFactory.Create(_ => { });
+ var clientFactory = new RealOpcUaClientFactory(new OpcUaGlobalOptions(), loggerFactory);
+ var adapter = new OpcUaDataConnection(clientFactory, NullLogger.Instance);
+
+ // Probe the endpoint. An unreachable infra server surfaces a socket/timeout
+ // error from deep in the OPC Foundation SDK — treat as "infra not available".
+ try
+ {
+ await adapter.ConnectAsync(new Dictionary { ["EndpointUrl"] = EndpointUrl });
+ }
+ catch (Exception ex)
+ {
+ Skip.If(true, $"OPC UA test server not reachable on {EndpointUrl}: {ex.Message}");
+ return;
+ }
+
+ try
+ {
+ var snapshotComplete = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+
+ void OnTransition(NativeAlarmTransition t)
+ {
+ if (t.Kind == AlarmTransitionKind.SnapshotComplete)
+ {
+ snapshotComplete.TrySetResult(true);
+ }
+ }
+
+ // Empty source reference = mirror every condition under the server's
+ // event notifier; ConditionRefresh replays the active set then a
+ // SnapshotComplete sentinel.
+ await adapter.SubscribeAlarmsAsync(sourceReference: string.Empty, conditionFilter: null, OnTransition);
+
+ var done = await Task.WhenAny(snapshotComplete.Task, Task.Delay(TimeSpan.FromSeconds(10)));
+
+ // Reachable but no A&C snapshot within the window → the server does not
+ // expose Alarms & Conditions. Skip rather than fail.
+ Skip.IfNot(done == snapshotComplete.Task,
+ $"OPC UA endpoint {EndpointUrl} reachable but delivered no A&C ConditionRefresh snapshot — " +
+ "server likely does not expose Alarms & Conditions.");
+
+ Assert.True(snapshotComplete.Task.IsCompletedSuccessfully);
+ }
+ finally
+ {
+ await adapter.DisconnectAsync();
+ }
+ }
+}