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) }