feat(go): add realtime pipeline observability
This commit is contained in:
@@ -430,6 +430,125 @@ func TestRepositoryFastUpdateOnlyWritesPermanentKVAndMinuteOnline(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryListsOnlineStatusesAndPipelineSummary(t *testing.T) {
|
||||
repo, closeFn := newTestRepository(t)
|
||||
defer closeFn()
|
||||
ctx := context.Background()
|
||||
|
||||
for _, env := range []envelope.FrameEnvelope{
|
||||
{
|
||||
Protocol: envelope.ProtocolGB32960,
|
||||
VIN: "VIN001",
|
||||
EventTimeMS: 1000,
|
||||
ReceivedAtMS: 1100,
|
||||
Parsed: map[string]any{
|
||||
"data_units": []any{map[string]any{"type": "0x01", "name": "vehicle", "value": map[string]any{"soc_percent": 88.0}}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Protocol: envelope.ProtocolJT808,
|
||||
VIN: "VIN002",
|
||||
EventTimeMS: 2000,
|
||||
ReceivedAtMS: 2200,
|
||||
MessageID: "0x0200",
|
||||
Parsed: map[string]any{"location": map[string]any{"longitude": 121.1, "latitude": 30.2}},
|
||||
Fields: map[string]any{envelope.FieldLongitude: 121.1, envelope.FieldLatitude: 30.2},
|
||||
},
|
||||
} {
|
||||
if err := repo.FastUpdate(ctx, env); err != nil {
|
||||
t.Fatalf("FastUpdate() error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
items, total, err := repo.ListOnline(ctx, OnlineListQuery{Protocol: envelope.ProtocolJT808, Limit: 10})
|
||||
if err != nil {
|
||||
t.Fatalf("ListOnline() error = %v", err)
|
||||
}
|
||||
if total != 1 || len(items) != 1 || items[0].VIN != "VIN002" || !items[0].Online {
|
||||
t.Fatalf("jt808 online list total=%d items=%#v", total, items)
|
||||
}
|
||||
|
||||
summary, err := repo.PipelineSummary(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("PipelineSummary() error = %v", err)
|
||||
}
|
||||
got := map[envelope.Protocol]ProtocolPipelineSummary{}
|
||||
for _, item := range summary.Protocols {
|
||||
got[item.Protocol] = item
|
||||
}
|
||||
if got[envelope.ProtocolGB32960].IndexedCount != 1 || got[envelope.ProtocolGB32960].OnlineCount != 1 || got[envelope.ProtocolGB32960].LatestSeenMS != 1100 {
|
||||
t.Fatalf("gb32960 summary = %#v", got[envelope.ProtocolGB32960])
|
||||
}
|
||||
if got[envelope.ProtocolJT808].IndexedCount != 1 || got[envelope.ProtocolJT808].OnlineCount != 1 || got[envelope.ProtocolJT808].LatestSeenMS != 2200 {
|
||||
t.Fatalf("jt808 summary = %#v", got[envelope.ProtocolJT808])
|
||||
}
|
||||
}
|
||||
|
||||
func TestOnlineIndexHandlerReturnsPagedOnlineStatuses(t *testing.T) {
|
||||
repo, closeFn := newTestRepository(t)
|
||||
defer closeFn()
|
||||
ctx := context.Background()
|
||||
|
||||
if err := repo.FastUpdate(ctx, envelope.FrameEnvelope{
|
||||
Protocol: envelope.ProtocolGB32960,
|
||||
VIN: "VIN001",
|
||||
EventTimeMS: 1000,
|
||||
ReceivedAtMS: 1100,
|
||||
Parsed: map[string]any{
|
||||
"data_units": []any{map[string]any{"type": "0x01", "name": "vehicle", "value": map[string]any{"soc_percent": 88.0}}},
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("FastUpdate() error = %v", err)
|
||||
}
|
||||
|
||||
handler := NewOnlineIndexHandler(repo)
|
||||
request := httptest.NewRequest(http.MethodGet, "/api/realtime/online?protocol=gb32960&limit=5", nil)
|
||||
response := httptest.NewRecorder()
|
||||
handler.ServeHTTP(response, request)
|
||||
|
||||
if response.Code != http.StatusOK {
|
||||
t.Fatalf("status = %d body=%s", response.Code, response.Body.String())
|
||||
}
|
||||
body := response.Body.String()
|
||||
for _, want := range []string{`"protocol":"GB32960"`, `"vin":"VIN001"`, `"online":true`, `"total":1`} {
|
||||
if !strings.Contains(body, want) {
|
||||
t.Fatalf("response missing %s: %s", want, body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPipelineDebugHandlerReturnsProtocolSummary(t *testing.T) {
|
||||
repo, closeFn := newTestRepository(t)
|
||||
defer closeFn()
|
||||
ctx := context.Background()
|
||||
|
||||
if err := repo.FastUpdate(ctx, envelope.FrameEnvelope{
|
||||
Protocol: envelope.ProtocolYutongMQTT,
|
||||
VIN: "VIN003",
|
||||
EventTimeMS: 3000,
|
||||
ReceivedAtMS: 3300,
|
||||
Parsed: map[string]any{"data": map[string]any{"TOTAL_MILEAGE": 1}},
|
||||
Fields: map[string]any{envelope.FieldTotalMileageKM: 1},
|
||||
}); err != nil {
|
||||
t.Fatalf("FastUpdate() error = %v", err)
|
||||
}
|
||||
|
||||
handler := NewPipelineDebugHandler(repo)
|
||||
request := httptest.NewRequest(http.MethodGet, "/api/debug/pipeline", nil)
|
||||
response := httptest.NewRecorder()
|
||||
handler.ServeHTTP(response, request)
|
||||
|
||||
if response.Code != http.StatusOK {
|
||||
t.Fatalf("status = %d body=%s", response.Code, response.Body.String())
|
||||
}
|
||||
body := response.Body.String()
|
||||
for _, want := range []string{`"protocol":"YUTONG_MQTT"`, `"indexed_count":1`, `"online_count":1`, `"latest_seen_ms":3300`} {
|
||||
if !strings.Contains(body, want) {
|
||||
t.Fatalf("response missing %s: %s", want, body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryReplacesJT808LocationWhenUpdatingRealtimeRaw(t *testing.T) {
|
||||
repo, closeFn := newTestRepository(t)
|
||||
defer closeFn()
|
||||
|
||||
Reference in New Issue
Block a user