X-Git-Url: https://git.giacomolli.uk/onehouse/blobdiff_plain/b28ae53ae04c41116acb2beae9d5a2128a187c83..9b7392e6965fe1b2e858f63c52113899e4bf3fb4:/protocol/message_test.go?ds=sidebyside diff --git a/protocol/message_test.go b/protocol/message_test.go index 770ae50..660885d 100644 --- a/protocol/message_test.go +++ b/protocol/message_test.go @@ -154,7 +154,7 @@ func TestPayloadValidationRejectsInvalidContracts(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - data, err := EncodeEnvelope(metadata, json.RawMessage(tc.payload)) + data, err := encodeEnvelope(metadata, json.RawMessage(tc.payload)) if tc.kind == Event { require.Error(t, err) @@ -167,7 +167,7 @@ func TestPayloadValidationRejectsInvalidContracts(t *testing.T) { Kind: tc.kind, ID: deviceID, Target: tc.target, - }).Topic() + }).topic() require.NoError(t, err) _, err = Parse(topic, data, false) @@ -294,3 +294,134 @@ func TestBuildersApplyCommandAndResultRules(t *testing.T) { _, err = BuildDescriptor(deviceID, metadata, descriptor) require.Error(t, err) } + +func TestBuildersKeepNullValuesWithoutChangingInputs(t *testing.T) { + metadata := Metadata{MessageID: messageID} + payload := ValuePayload{} + publication, err := BuildReported(deviceID, "relay", metadata, payload) + require.NoError(t, err) + require.Nil(t, payload.Value) + + parsed, err := ParseReported(publication.Topic, publication.Data, false) + require.NoError(t, err) + require.JSONEq(t, `null`, string(parsed.Payload.Value)) + + deadline := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) + batch := testBatch(deadline) + batch.Commands[0].Payload.Value = nil + publication, err = BuildCommandBatch(deviceID, metadata, batch) + require.NoError(t, err) + require.Nil(t, batch.Commands[0].Payload.Value) + + commands, err := ParseCommandBatch(publication.Topic, publication.Data, false) + require.NoError(t, err) + require.JSONEq(t, `null`, string(commands.Payload.Commands[0].Payload.Value)) +} + +func TestBuildEventRejectsNonObjectAndInvalidValues(t *testing.T) { + testCases := []struct { + name string + payload EventPayload + valid bool + }{ + { + name: "rejects null instead of an object", + }, + { + name: "rejects malformed event data", + payload: EventPayload{"action": json.RawMessage(`bad`)}, + }, + { + name: "rejects empty nonnull event data", + payload: EventPayload{"action": json.RawMessage{}}, + }, + { + name: "keeps null event members", + payload: EventPayload{"action": nil}, + valid: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + publication, err := BuildEvent(deviceID, "press", Metadata{MessageID: messageID}, tc.payload) + if !tc.valid { + require.Error(t, err) + + return + } + + require.NoError(t, err) + + parsed, err := ParseEvent(publication.Topic, publication.Data, false) + require.NoError(t, err) + require.JSONEq(t, `null`, string(parsed.Payload["action"])) + }) + } +} + +func TestBuildRejectsInvalidMetadataBeforeReturningPublications(t *testing.T) { + deadline := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) + metadata := Metadata{MessageID: messageID} + testCases := []struct { + name string + mutate func(*Metadata) + }{ + { + name: "rejects malformed message identity", + mutate: func(metadata *Metadata) { + metadata.MessageID = "bad" + }, + }, + { + name: "rejects malformed correlation identity", + mutate: func(metadata *Metadata) { + metadata.CorrelationID = "bad" + }, + }, + { + name: "rejects malformed causation identity", + mutate: func(metadata *Metadata) { + metadata.CausationID = "bad" + }, + }, + { + name: "rejects non UTC expiry", + mutate: func(metadata *Metadata) { + expiry := deadline.In(time.FixedZone("test", 3600)) + metadata.ExpiresAt = &expiry + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + invalid := metadata + tc.mutate(&invalid) + + _, err := BuildReported(deviceID, "relay", invalid, ValuePayload{Value: json.RawMessage(`true`)}) + require.Error(t, err) + + batch := testBatch(deadline) + tc.mutate(&batch.Commands[1].Metadata) + + _, err = BuildCommandBatch(deviceID, metadata, batch) + require.Error(t, err) + }) + } +} + +func TestDescriptorAttributionLimitUsesEncodedText(t *testing.T) { + payload := testDescriptor() + payload.Attribution = strings.Repeat("\xff", 667) + _, err := BuildDescriptor(deviceID, Metadata{MessageID: messageID}, payload) + require.Error(t, err) + + payload.Attribution = strings.Repeat("\xff", 666) + publication, err := BuildDescriptor(deviceID, Metadata{MessageID: messageID}, payload) + require.NoError(t, err) + + parsed, err := ParseDescriptor(publication.Topic, publication.Data, true) + require.NoError(t, err) + require.Len(t, parsed.Payload.Attribution, 1998) +}