fix(CLI-47,CLI-48,CLI-49,CLI-50): align alarm feed clients

This commit is contained in:
Joseph Doherty
2026-08-25 11:25:47 -04:00
parent feadcf5740
commit 5cdb3da8c0
14 changed files with 325 additions and 61 deletions
+6 -1
View File
@@ -162,6 +162,11 @@ on every verdict change — including the clearing frame sent when the gateway's
alarm monitor restarts and drops a truncated verdict. Track it if you need set
completeness on a live feed without polling `QueryActiveAlarms`.
The CLI's text rows always spell out `degraded=false` and `truncated=false`.
Its protobuf-JSON mode retains the standard proto3 omission rule, so those
false-valued payloads appear as `{"providerStatus":{}}` and
`{"snapshotStatus":{}}`; absent means false.
## Write Semantics And Common Pitfalls
These are MXAccess parity behaviors that surprise new callers. The gateway
@@ -439,7 +444,7 @@ go run ./cmd/mxgw-go write -session-id <id> -server-handle 1 -item-handle 1 -typ
go run ./cmd/mxgw-go write-bulk -session-id <id> -server-handle 1 -item-handles 1,2 -values 10,20 -type int32 -plaintext -json
go run ./cmd/mxgw-go read-bulk -session-id <id> -item-handles 1,2 -plaintext -json
go run ./cmd/mxgw-go stream-events -session-id <id> -plaintext -json
go run ./cmd/mxgw-go stream-alarms -plaintext -json
go run ./cmd/mxgw-go stream-alarms -plaintext -limit 4 -json
go run ./cmd/mxgw-go smoke -item Area001.Tag.Value -plaintext -json
go run ./cmd/mxgw-go galaxy-test-connection -plaintext -json
go run ./cmd/mxgw-go galaxy-last-deploy -plaintext -json
+62
View File
@@ -283,6 +283,68 @@ type pingFakeGateway struct {
pb.UnimplementedMxAccessGatewayServer
}
// alarmStatusFakeGateway emits both status arms with their proto3 boolean
// defaults so the CLI contract covers values that protobuf JSON omits.
type alarmStatusFakeGateway struct {
pb.UnimplementedMxAccessGatewayServer
}
func (g *alarmStatusFakeGateway) StreamAlarms(
_ *pb.StreamAlarmsRequest,
stream grpc.ServerStreamingServer[pb.AlarmFeedMessage],
) error {
if err := stream.Send(&pb.AlarmFeedMessage{
Payload: &pb.AlarmFeedMessage_ProviderStatus{
ProviderStatus: &pb.AlarmProviderStatus{Degraded: false},
},
}); err != nil {
return err
}
return stream.Send(&pb.AlarmFeedMessage{
Payload: &pb.AlarmFeedMessage_SnapshotStatus{
SnapshotStatus: &pb.AlarmSnapshotStatus{Truncated: false},
},
})
}
func startAlarmStatusGateway(t *testing.T) string {
t.Helper()
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("listen: %v", err)
}
server := grpc.NewServer()
pb.RegisterMxAccessGatewayServer(server, &alarmStatusFakeGateway{})
go func() { _ = server.Serve(listener) }()
t.Cleanup(func() {
server.Stop()
_ = listener.Close()
})
return listener.Addr().String()
}
func TestRunStreamAlarmsPrintsStatusArmsInTextAndProtobufJSON(t *testing.T) {
endpoint := startAlarmStatusGateway(t)
baseArgs := []string{"stream-alarms", "-endpoint", endpoint, "-plaintext", "-api-key", "test", "-limit", "2"}
var textOut, textErr bytes.Buffer
if err := runWithIO(t.Context(), baseArgs, &textOut, &textErr); err != nil {
t.Fatalf("text stream-alarms error = %v; stderr = %s", err, textErr.String())
}
if got, want := strings.TrimSpace(textOut.String()), "provider-status mode=ALARM_PROVIDER_MODE_UNSPECIFIED degraded=false reason=\"\"\nsnapshot-status truncated=false"; got != want {
t.Fatalf("text stream-alarms output = %q, want %q", got, want)
}
var jsonOut, jsonErr bytes.Buffer
jsonArgs := append(append([]string{}, baseArgs...), "-json")
if err := runWithIO(t.Context(), jsonArgs, &jsonOut, &jsonErr); err != nil {
t.Fatalf("JSON stream-alarms error = %v; stderr = %s", err, jsonErr.String())
}
if got, want := strings.TrimSpace(jsonOut.String()), "{\"providerStatus\":{}}\n{\"snapshotStatus\":{}}"; got != want {
t.Fatalf("JSON stream-alarms output = %q, want protobuf JSON %q", got, want)
}
}
func (g *pingFakeGateway) Invoke(_ context.Context, req *pb.MxCommandRequest) (*pb.MxCommandReply, error) {
echo := "pong:" + req.GetCommand().GetPing().GetMessage()
return &pb.MxCommandReply{
+7 -2
View File
@@ -127,9 +127,14 @@ type (
QueryActiveAlarmsRequest = pb.QueryActiveAlarmsRequest
// StreamAlarmsRequest is the gateway StreamAlarms request message.
StreamAlarmsRequest = pb.StreamAlarmsRequest
// AlarmFeedMessage is one message on the StreamAlarms feed — an
// active-alarm snapshot row, a snapshot-complete sentinel, or a transition.
// AlarmFeedMessage is one message on the StreamAlarms feed — an active-alarm
// snapshot row, snapshot-complete sentinel, transition, provider status, or
// snapshot-completeness status.
AlarmFeedMessage = pb.AlarmFeedMessage
// AlarmProviderStatus reports the alarm provider mode and degradation state.
AlarmProviderStatus = pb.AlarmProviderStatus
// AlarmSnapshotStatus reports whether the cached alarm snapshot is truncated.
AlarmSnapshotStatus = pb.AlarmSnapshotStatus
// ActiveAlarmSnapshot is one row in a ConditionRefresh stream.
ActiveAlarmSnapshot = pb.ActiveAlarmSnapshot
// OnAlarmTransitionEvent is the body carried by alarm-transition MxEvents.
+17
View File
@@ -0,0 +1,17 @@
package mxgateway_test
import (
"testing"
"gitea.dohertylan.com/dohertj2/mxaccessgw/clients/go/mxgateway"
)
// TestAlarmFeedStatusAliasesArePublic is a compile-time consumer contract for
// the two status payloads carried by AlarmFeedMessage.
func TestAlarmFeedStatusAliasesArePublic(t *testing.T) {
t.Helper()
var provider mxgateway.AlarmProviderStatus
var snapshot mxgateway.AlarmSnapshotStatus
_ = provider
_ = snapshot
}