Files
story/internal/httpapi/telemetry_handlers_test.go
T

66 lines
4.1 KiB
Go
Raw Normal View History

2026-08-26 06:41:07 +08:00
package httpapi
import (
"context"
"net/http"
"net/http/httptest"
"strings"
"testing"
"data-terminal-cloud/internal/device"
"data-terminal-cloud/internal/deviceauth"
)
type fakeTelemetrySubmitter struct {
response device.TelemetryBatchResponse
replayed bool
err error
calls int
}
func (s *fakeTelemetrySubmitter) Submit(context.Context, deviceauth.Identity, string, []byte, device.TelemetryBatchInput) (device.TelemetryBatchResponse, bool, error) {
s.calls++
return s.response, s.replayed, s.err
}
func TestSubmitDeviceTelemetryAcceptsTypedBatch(t *testing.T) {
service := &fakeTelemetrySubmitter{response: device.TelemetryBatchResponse{
AcceptedCount: 2, DesiredConfigRevision: 2, Actions: []device.TelemetryAction{{Type: "refresh_config"}},
}, replayed: true}
body := `{"events":[{"event_key":"status-101","sequence_no":101,"occurred_at":"2026-08-23T12:00:00Z","event_type":"status","status":{"firmware_version":"1.0.1","reported_config_revision":1,"network":{"type":"wifi","rssi":-50},"rtc":{"joined":false,"uplink":false,"downlink":false},"recording_queue":{"pending_segments":2,"failed_segments":1,"last_failure_code":"MEDIA_UPLOAD_TIMEOUT","last_failure_at":"2026-08-23T11:59:58Z"}}},{"event_key":"error-102","sequence_no":102,"occurred_at":"2026-08-23T12:00:01Z","event_type":"error","error":{"code":"RTC_RECONNECTING","severity":"warning"}}]}`
request := httptest.NewRequest(http.MethodPost, "/device/v2/telemetry:batch", strings.NewReader(body))
request.Header.Set("Idempotency-Key", "telemetry-0123456789abcdef")
request = request.WithContext(deviceauth.WithIdentity(request.Context(), deviceauth.Identity{PublicID: "device-1"}))
recorder := httptest.NewRecorder()
SubmitDeviceTelemetryHandler(service).ServeHTTP(recorder, request)
if recorder.Code != http.StatusOK || service.calls != 1 || recorder.Header().Get("Idempotency-Replayed") != "true" || !containsText(recorder.Body.String(), "refresh_config") {
t.Fatalf("status=%d calls=%d replay=%q body=%s", recorder.Code, service.calls, recorder.Header().Get("Idempotency-Replayed"), recorder.Body.String())
}
}
func TestSubmitDeviceTelemetryRejectsMixedPayloadAndSensitiveCommandResult(t *testing.T) {
service := &fakeTelemetrySubmitter{}
body := `{"events":[{"event_key":"command-1","sequence_no":1,"occurred_at":"2026-08-23T12:00:00Z","event_type":"command_result","error":{"code":"X","severity":"error"},"command_result":{"command_id":"10000000-0000-0000-0000-000000000001","status":"SUCCEEDED","result_summary":{"api_token":"must-not-pass"}}}]}`
request := httptest.NewRequest(http.MethodPost, "/device/v2/telemetry:batch", strings.NewReader(body))
request.Header.Set("Idempotency-Key", "telemetry-0123456789abcdef")
request = request.WithContext(deviceauth.WithIdentity(request.Context(), deviceauth.Identity{PublicID: "device-1"}))
recorder := httptest.NewRecorder()
SubmitDeviceTelemetryHandler(service).ServeHTTP(recorder, request)
if recorder.Code != http.StatusBadRequest || service.calls != 0 {
t.Fatalf("status=%d calls=%d body=%s", recorder.Code, service.calls, recorder.Body.String())
}
}
func TestSubmitDeviceTelemetryReturnsStructuredEventConflict(t *testing.T) {
service := &fakeTelemetrySubmitter{err: &device.TelemetryConflictError{Conflicts: []device.TelemetryConflict{{EventKey: "error-1", SequenceNo: 1, Reason: "EVENT_KEY_OR_SEQUENCE_ALREADY_USED"}}}}
body := `{"events":[{"event_key":"error-1","sequence_no":1,"occurred_at":"2026-08-23T12:00:00Z","event_type":"error","error":{"code":"RTC_FAILED","severity":"error"}}]}`
request := httptest.NewRequest(http.MethodPost, "/device/v2/telemetry:batch", strings.NewReader(body))
request.Header.Set("Idempotency-Key", "telemetry-0123456789abcdef")
request = request.WithContext(deviceauth.WithIdentity(request.Context(), deviceauth.Identity{PublicID: "device-1"}))
recorder := httptest.NewRecorder()
SubmitDeviceTelemetryHandler(service).ServeHTTP(recorder, request)
if recorder.Code != http.StatusConflict || !containsText(recorder.Body.String(), "TELEMETRY_EVENT_CONFLICT") || !containsText(recorder.Body.String(), "EVENT_KEY_OR_SEQUENCE_ALREADY_USED") {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
}