test(mesh-phase5): cover the ScriptedAlarmHostActor + VirtualTagActor telemetry taps

Closes two test-completeness gaps flagged in review of the seam-tap commit — the
ScriptedAlarmHostActor and VirtualTagActor hub emits had no test proving the hub
actually receives them.

- ScriptedAlarm_activation_emits_one_Alarm_item_with_matching_payload: spawns the
  host with a capturing fake hub, drives a real engine Inactive->Active transition
  through the existing ScriptedAlarms harness, and asserts exactly one
  TelemetryItem.Alarm carrying the Activated transition.
- VirtualTag_script_log_emits_one_Script_item_with_same_payload: passes a fake hub
  via Props + a publisherFactory, drives an evaluator failure, and asserts a
  TelemetryItem.Script carrying the SAME ScriptLogEntry instance handed to the
  publisher.

To make the VirtualTagActor tap observable, its hub emit moved to the TOP of
PublishLog, before the test-only publisherFactory early-return branch, so the emit
fires on BOTH the production DPS path and the factory path. Production behavior is
unchanged: production passes publisherFactory: null, so it still reaches the DPS
Tell; the emit just moved a couple lines earlier (both are fire-and-forget).

Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
This commit is contained in:
Joseph Doherty
2026-07-23 15:38:12 -04:00
parent 7bdb6d00ec
commit 909d75357b
2 changed files with 107 additions and 3 deletions
@@ -262,6 +262,12 @@ public sealed class VirtualTagActor : ReceiveActor
AlarmId: null,
EquipmentId: null);
// Phase 5: fan the same entry into the node-local live-telemetry hub (no-op until a gRPC client
// subscribes). Emitted BEFORE the publish branch so BOTH the production DPS path and the test-seam
// publisherFactory path (which returns early below) feed the hub. Both taps are fire-and-forget, so
// emit-then-publish ordering is immaterial; the DPS/factory publish itself is unchanged.
_telemetryHub?.Emit(new TelemetryItem.Script(entry));
if (_publisherFactory is not null)
{
_publisherFactory().Publish(ScriptLogsTopic, entry);
@@ -269,9 +275,6 @@ public sealed class VirtualTagActor : ReceiveActor
}
DistributedPubSub.Get(Context.System).Mediator.Tell(new Publish(ScriptLogsTopic, entry));
// Phase 5: fan the same entry into the node-local live-telemetry hub (no-op until a gRPC client
// subscribes). The DPS publish above is unchanged — a strictly additive tap.
_telemetryHub?.Emit(new TelemetryItem.Script(entry));
}
}