package protocol import ( "strings" "testing" "time" "github.com/stretchr/testify/require" ) func TestEnvelopeRoundTripPreservesMetadata(t *testing.T) { now := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) metadata := Metadata{ MessageID: messageID, SentAt: &now, ExpiresAt: &now, CorrelationID: commandID, CausationID: deviceID, } data, err := encodeEnvelope(metadata, map[string]bool{"value": true}) require.NoError(t, err) envelope, err := parseEnvelope(data) require.NoError(t, err) require.Equal(t, Schema, envelope.Schema) require.Equal(t, metadata, envelope.Metadata) require.JSONEq(t, `{"value":true}`, string(envelope.Payload)) require.Equal(t, 5*time.Minute, MeasurementRepeatInterval) require.Equal(t, 5*time.Minute, OnlineRepeatInterval) require.Equal(t, 15*time.Minute, DeviceContactTimeout) } func TestEnvelopeRejectsInvalidWireData(t *testing.T) { valid := `{"schema":1,"message_id":"` + messageID + `","payload":{}}` testCases := []struct { name string data string }{ { name: "rejects unsupported schema", data: strings.Replace(valid, `"schema":1`, `"schema":2`, 1), }, { name: "rejects unknown fields", data: strings.Replace(valid, `"payload":{}`, `"payload":{},"extra":true`, 1), }, { name: "rejects missing payload", data: `{"schema":1,"message_id":"` + messageID + `"}`, }, { name: "rejects null payload", data: strings.Replace(valid, `"payload":{}`, `"payload":null`, 1), }, { name: "rejects non-object payload", data: strings.Replace(valid, `"payload":{}`, `"payload":[]`, 1), }, { name: "rejects missing identity", data: `{"schema":1,"payload":{}}`, }, { name: "rejects invalid correlation", data: strings.Replace(valid, `"payload":{}`, `"payload":{},"correlation_id":"bad"`, 1), }, { name: "rejects invalid causation", data: strings.Replace(valid, `"payload":{}`, `"payload":{},"causation_id":"bad"`, 1), }, { name: "rejects non-UTC time", data: strings.Replace(valid, `"payload":{}`, `"payload":{},"sent_at":"2026-01-01T01:00:00+01:00"`, 1), }, { name: "rejects invalid expiry", data: strings.Replace(valid, `"payload":{}`, `"payload":{},"expires_at":"bad"`, 1), }, { name: "rejects trailing JSON", data: valid + `{}`, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { _, err := parseEnvelope([]byte(tc.data)) require.Error(t, err) }) } _, err := encodeEnvelope(Metadata{MessageID: "bad"}, map[string]bool{}) require.Error(t, err) _, err = encodeEnvelope(Metadata{MessageID: messageID}, make(chan int)) require.Error(t, err) _, err = encodeEnvelope(Metadata{MessageID: messageID}, []int{1}) require.Error(t, err) }