X-Git-Url: https://git.giacomolli.uk/onehouse/blobdiff_plain/4bba0e163f1a4dc07b34fd9e4dc403de1f9b4cb9..10c345f1d112061688409cef7710b4ac14a966aa:/protocol/builders_test.go?ds=sidebyside diff --git a/protocol/builders_test.go b/protocol/builders_test.go index b2b5a4a..20bd547 100644 --- a/protocol/builders_test.go +++ b/protocol/builders_test.go @@ -239,3 +239,139 @@ func TestTypedParsersRejectWrongOperations(t *testing.T) { require.Error(t, parse(Root+"/devices/"+deviceID+"/unknown", []byte(`{}`), false)) } } + +func TestTypedParsersKeepValidationAfterTopicDispatch(t *testing.T) { + deadline := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) + metadata := Metadata{ + MessageID: messageID, + ExpiresAt: &deadline, + CorrelationID: commandID, + } + testCases := []struct { + name string + kind Kind + target string + payload any + parse func(string, []byte, bool) (any, error) + }{ + { + name: "descriptor keeps its machine metadata", + kind: Descriptor, + payload: testDescriptor(), + parse: func(topic string, data []byte, retained bool) (any, error) { + parsed, err := ParseDescriptor(topic, data, retained) + return parsed.Payload, err + }, + }, + { + name: "availability keeps its reachability", + kind: Availability, + payload: AvailabilityPayload{Status: "online"}, + parse: func(topic string, data []byte, retained bool) (any, error) { + parsed, err := ParseAvailability(topic, data, retained) + return parsed.Payload, err + }, + }, + { + name: "reported keeps its complete value", + kind: Reported, + target: "relay", + payload: ValuePayload{Value: json.RawMessage(`true`)}, + parse: func(topic string, data []byte, retained bool) (any, error) { + parsed, err := ParseReported(topic, data, retained) + return parsed.Payload, err + }, + }, + { + name: "telemetry keeps its sample", + kind: Telemetry, + target: "power", + payload: ValuePayload{Value: json.RawMessage(`12`)}, + parse: func(topic string, data []byte, retained bool) (any, error) { + parsed, err := ParseTelemetry(topic, data, retained) + return parsed.Payload, err + }, + }, + { + name: "event keeps its open fields", + kind: Event, + target: "press", + payload: EventPayload{"action": json.RawMessage(`"press"`)}, + parse: func(topic string, data []byte, retained bool) (any, error) { + parsed, err := ParseEvent(topic, data, retained) + return parsed.Payload, err + }, + }, + { + name: "command keeps its requested value", + kind: Command, + target: "relay", + payload: ValuePayload{Value: json.RawMessage(`true`)}, + parse: func(topic string, data []byte, retained bool) (any, error) { + parsed, err := ParseCommand(topic, data, retained) + return parsed.Payload, err + }, + }, + { + name: "command batch keeps its member order", + kind: CommandBatch, + payload: testBatch(deadline), + parse: func(topic string, data []byte, retained bool) (any, error) { + parsed, err := ParseCommandBatch(topic, data, retained) + return parsed.Payload, err + }, + }, + { + name: "result keeps its correlated outcome", + kind: Result, + target: commandID, + payload: ResultPayload{Status: "completed"}, + parse: func(topic string, data []byte, retained bool) (any, error) { + parsed, err := ParseResult(topic, data, retained) + return parsed.Payload, err + }, + }, + { + name: "hub status keeps its snapshot", + kind: HubStatus, + payload: HubStatusPayload{ + ID: deviceID, + Address: "example.com", + Status: "ready", + }, + parse: func(topic string, data []byte, retained bool) (any, error) { + parsed, err := ParseHubStatus(topic, data, retained) + return parsed.Payload, err + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + topic, err := (Address{ + Kind: tc.kind, + ID: deviceID, + Target: tc.target, + }).topic() + require.NoError(t, err) + + data, err := encodeEnvelope(metadata, tc.payload) + require.NoError(t, err) + + parsed, err := tc.parse(topic, data, false) + require.NoError(t, err) + require.Equal(t, tc.payload, parsed) + + _, err = tc.parse(topic, []byte(`{}`), false) + require.Error(t, err) + + wrongTopic := Root + "/devices/" + deviceID + "/availability" + if tc.kind == Availability { + wrongTopic = Root + "/devices/" + deviceID + "/descriptor" + } + + _, err = tc.parse(wrongTopic, data, false) + require.Error(t, err) + }) + } +}