-
Notifications
You must be signed in to change notification settings - Fork 56
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
send measurement to main/child device services #2335
send measurement to main/child device services #2335
Conversation
Codecov Report
Additional details and impacted files
|
let out_second_messages = converter.convert(&in_message).await; | ||
assert_eq!(out_second_messages, vec![expected_c8y_json_message]); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add some tests for events and alarms of services as well? They were working magically already, but we never had test coverage. The existing child device tests for the same could be copied and modified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added in tests.rs
); | ||
let expected_c8y_json_message = Message::new( | ||
&Topic::new_unchecked("c8y/measurement/measurements/create"), | ||
r#"{"externalSource":{"externalId":"test-device:device:child1:service:app1","type":"c8y_Serial"},"temp":{"temp":{"value":1.0}},"time":"2021-11-16T17:45:40.571760714+01:00","type":"m_type"}"#, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd strongly recommend the json!
macro to wrap such complex JSONs. I'd use it even for simpler ones with more than a couple of keys. I hope the macro doesn't change the order of keys so that your assertions work seamlessly, without needing assert_json_matches!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed, using json!
and to_string
used, so don't need to use assert_json_matches
.
|
||
let expected_c8y_json_message = Message::new( | ||
&Topic::new_unchecked("c8y/measurement/measurements/create"), | ||
r#"{"externalSource":{"externalId":"test-device:device:main:service:appm","type":"c8y_Serial"},"temp":{"temp":{"value":1.0}},"time":"2021-11-16T17:45:40.571760714+01:00","type":"m_type"}"#, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest to use json!
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
// "externalSource" to tell c8Y identity API to use child-device | ||
if entity.r#type == EntityType::ChildDevice || entity.r#type == EntityType::Service { | ||
let entity_id = &entity.external_id; | ||
// In case the measurement is addressed to a child-device or for the service use fragment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// In case the measurement is addressed to a child-device or for the service use fragment | |
// In case the measurement is addressed to a child-device or a service, use fragment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
Cumulocity.Device Should Exist ${DEVICE_SN}:device:${CHILD_SN}:service:app6 | ||
Execute Command tedge mqtt pub te/device/${CHILD_SN}/service/app6/e/event_002 '{"text": "test event"}' | ||
Device Should Have Event/s expected_text=test event type=event_002 minimum=1 maximum=1 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may want to add similar tests for services on nested child devices as well. The "unregistered nested child service" case is not even needed as nested child devices must have explicit registration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will do it as part of another PR as there are unit tests are also missing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No we should include the tests here as well so that we are sure the code supports it before we merge it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since that is not in the scope of this task, I do not want to bloat this PR. The nested child devices are a completely different thing, I would do it as part of another PR because this needs Unit tests and integration tests.
One question: can nested child devices also have the services? they need telemetry support as well right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No I would disagree, it is very much in the task as a child device also reference to any child device regardless if it is an immediate child or a nested child.
And generally we should get in the habit of ensuring the code does what it is meant to before it gets merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One question: can nested child devices also have the services? they need telemetry support as well right?
Yes, services are supported on any type of child device (immediate or nested)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, thanks for the clarification. I will add the tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, thanks for the clarification. I will update the tests.
Robot Results
|
1f13817
to
cf77cff
Compare
913ad9a
to
a80d5f5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to approve after a little bit of cleanup.
let expected_smart_rest_message_child = Message::new( | ||
&Topic::new_unchecked("c8y/s/us"), | ||
"101,test-device:device:child1,child1,thin-edge.io-child", | ||
); | ||
let expected_smart_rest_message_service = Message::new( | ||
&Topic::new_unchecked("c8y/s/us/test-device:device:child1"), | ||
"102,test-device:device:child1:service:app1,systemd,app1,up", | ||
); | ||
let expected_c8y_json_message = Message::new( | ||
&Topic::new_unchecked("c8y/measurement/measurements/create"), | ||
json!({ | ||
"externalSource":{ | ||
"externalId":"test-device:device:child1:service:app1", | ||
"type":"c8y_Serial" | ||
}, | ||
"temp":{"temp":{"value":1.0}}, | ||
"time":"2021-11-16T17:45:40.571760714+01:00", | ||
"type":"m_type"}) | ||
.to_string(), | ||
); | ||
|
||
// Test the first output messages contains SmartREST and C8Y JSON. | ||
let out_first_messages: Vec<_> = converter | ||
.convert(&in_message) | ||
.await | ||
.into_iter() | ||
.filter(|m| m.topic.name.starts_with("c8y")) | ||
.collect(); | ||
assert_eq!( | ||
out_first_messages, | ||
vec![ | ||
expected_smart_rest_message_child, | ||
expected_smart_rest_message_service, | ||
expected_c8y_json_message.clone() | ||
] | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let expected_smart_rest_message_child = Message::new( | |
&Topic::new_unchecked("c8y/s/us"), | |
"101,test-device:device:child1,child1,thin-edge.io-child", | |
); | |
let expected_smart_rest_message_service = Message::new( | |
&Topic::new_unchecked("c8y/s/us/test-device:device:child1"), | |
"102,test-device:device:child1:service:app1,systemd,app1,up", | |
); | |
let expected_c8y_json_message = Message::new( | |
&Topic::new_unchecked("c8y/measurement/measurements/create"), | |
json!({ | |
"externalSource":{ | |
"externalId":"test-device:device:child1:service:app1", | |
"type":"c8y_Serial" | |
}, | |
"temp":{"temp":{"value":1.0}}, | |
"time":"2021-11-16T17:45:40.571760714+01:00", | |
"type":"m_type"}) | |
.to_string(), | |
); | |
// Test the first output messages contains SmartREST and C8Y JSON. | |
let out_first_messages: Vec<_> = converter | |
.convert(&in_message) | |
.await | |
.into_iter() | |
.filter(|m| m.topic.name.starts_with("c8y")) | |
.collect(); | |
assert_eq!( | |
out_first_messages, | |
vec![ | |
expected_smart_rest_message_child, | |
expected_smart_rest_message_service, | |
expected_c8y_json_message.clone() | |
] | |
); | |
let out_first_messages: Vec<_> = converter | |
.convert(&in_message) | |
.await; | |
assert_messages_matching( | |
out_first_messages, | |
[ | |
( | |
"c8y/s/us", | |
"101,test-device:device:child1,child1,thin-edge.io-child".into() | |
), | |
( | |
""c8y/s/us/test-device:device:child1"", | |
"102,test-device:device:child1:service:app1,systemd,app1,up".into() | |
), | |
( | |
""c8y/measurement/measurements/create"", | |
json!({ | |
"externalSource":{ | |
"externalId":"test-device:device:child1:service:app1", | |
"type":"c8y_Serial" | |
}, | |
"temp":{"temp":{"value":1.0}}, | |
"time":"2021-11-16T17:45:40.571760714+01:00", | |
"type":"m_type" | |
}).into() | |
), | |
] | |
) |
You could reduce a lot a of clutter using the new test helper APIs like that. I know that this API wasn't available while you were writing these tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That can be done in the second phase as a followup task, as there are a lot of tests need to be ported.
.convert(&in_message) | ||
.await | ||
.into_iter() | ||
.filter(|m| m.topic.name.starts_with("c8y")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've seen this filter in many other tests as well. I wonder why we only care about the messages pushed to c8y and not others. Why not assert the other messages like the auto-registration messages sent to the te/
topics as well, if there is one? That's also part of the convert
API contract that we're testing. Skipping them might lead to these tests not catching some unexpected side-effects in future. So, I'll advice against that filter. At least remove them from the new ones that you've added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If not filtered all the messages will get accumulated. And the assert will fail. For this particular test, the purpose of the tests is to assert only the output messages. So, I feel filtering is OK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the purpose of the tests is to assert only the output messages
Yes, but all the converted messages. We could skip all messages before or after the conversion, if not relevant for the test, but at least all the converted messages should have been asserted. But here, with this filter we're only asserting the messages sent to c8y, skipping any messges sent to te/
(typically auto-registration messages) or any other topics during the conversion. So, the validation is not complete if we don't assert all the converted messages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I have updated only to the tests.
@@ -1686,6 +1686,224 @@ mod tests { | |||
assert_eq!(out_second_messages, vec![expected_c8y_json_message]); | |||
} | |||
|
|||
#[tokio::test] | |||
async fn convert_measurement_with_nested_child_id() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
async fn convert_measurement_with_nested_child_id() { | |
async fn convert_measurement_with_nested_child_device() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
.await | ||
.unwrap(); | ||
|
||
mqtt.skip(2).await; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mqtt.skip(2).await; | |
assert_received_contains_str( | |
&mut mqtt, | |
[( | |
"expected/topic", | |
"", | |
)], | |
) |
I'd advise against skips like these and do loose asserts here instead, to be really sure that we're skipping what we think we're skipping. Sometimes I've encountered errors with blinds skips like this were messages were sent to tedge/errors
indicating an actual issue, but we just skipped it. Since string.contains("")
will always pass, we're essentially just validating the topic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But the order of messages is fixed for any given operation. So, I feel that skipping is OK here instead of just asserting the topics. If we do not skip, the assert happens on the wrong topic and tests will fail. The assert_received_contains_str
has to be enhanced to wait for the right message to assert. But this will be kind of wait forever is the worst case right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we do not skip, the assert happens on the wrong topic and tests will fail
This is exactly what we're trying to prevent. When you're skipping 2 messages here, you're not skipping any two messages. You're specifically skipping 2 messages that are probably not relevant for the test. But, still it is important to be sure that those 2 messages are exactly what you expected. Here, for example, you expect to skip the first 2 registration messages. What if one of them failed and sent an error message to tedge/errors
instead? It'd fail in the next assertion but with some really weird errors than what you expected. So, that's why I'm suggesting a loose assertion on ("c8y/s/us", "101")
topic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, updated the tests
.await | ||
.unwrap(); | ||
|
||
mqtt.skip(2).await; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as above for all the skips in the rest of the tests.
) | ||
.await; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most of these newly added tests could have been written at the converter
level itself, with far less clutter and overhead of the actors, with the help of the new assert_messages_matching
test helper. Not asking you to port them now, as it's already written. More of an FYI, for future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, then there were no assert_messages_matching
APIs, it was tough to assert because of the order issues in the payload. Maybe we can do it as a part of a follow-up task. There are a lot of tests that can be ported.
Device Should Have Event/s expected_text=test event type=event_002 minimum=1 maximum=1 | ||
|
||
# Nested child devices | ||
Nested child devices support sending simple measurements |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nested child devices support sending simple measurements | |
Nested child devices support sending measurement |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
Cumulocity.Device Should Exist ${CHILD_SN} | ||
Cumulocity.Should Have Services min_count=1 max_count=1 name=app1 | ||
|
||
Cumulocity.Device Should Exist ${DEVICE_SN}:device:${CHILD_SN}:service:app1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cumulocity.Device Should Exist ${DEVICE_SN}:device:${CHILD_SN}:service:app1 | |
Cumulocity.Service Should Exist ${DEVICE_SN}:device:${CHILD_SN}:service:app1 |
It really should have been a new API like that, or even Cumulocity.Managed Object Should Exist
(which is what the API actually does). Not under the scope of this PR though.
); | ||
|
||
// Test the first output messages contains SmartREST and C8Y JSON. | ||
let out_first_messages: Vec<_> = converter.convert(&in_message).await.into_iter().collect(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let out_first_messages: Vec<_> = converter.convert(&in_message).await.into_iter().collect(); | |
let out_first_messages: Vec<_> = converter.convert(&in_message).await; |
Why iterate and collect when the output of convert
is already a vector? The old code was doing this as they wanted to collect the filtered c8y messages. But, that's not the case anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, fixed
// Test the second output messages doesn't contain SmartREST child device creation. | ||
let out_second_messages = converter.convert(&in_message).await; | ||
assert_eq!(out_second_messages, vec![expected_c8y_json_message]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first convert also did not have the SmartREST child creation message, as the nested child device does not support auto-registeration and had to be explicitly registered. So, this assert can be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
); | ||
|
||
// Test the first output messages contains SmartREST and C8Y JSON. | ||
let out_first_messages: Vec<_> = converter.convert(&in_message).await.into_iter().collect(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary iter and collect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
// Test the second output messages doesn't contain SmartREST child device creation. | ||
let out_second_messages = converter.convert(&in_message).await; | ||
assert_eq!(out_second_messages, vec![expected_c8y_json_message]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assertion is also pointless for the same reason mentioned above, unless this test was relying on auto-registration for the service on nested child device. Auto-registration is not supported for nested child devices, but it is supported for services on any child device, if they follow the default topic scheme.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
); | ||
|
||
// Test the first output messages contains SmartREST and C8Y JSON. | ||
let out_first_messages: Vec<_> = converter.convert(&in_message).await.into_iter().collect(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary iter and collect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
); | ||
|
||
// Test the first output messages contains SmartREST and C8Y JSON. | ||
let out_first_messages: Vec<_> = converter.convert(&in_message).await.into_iter().collect(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary iter and collect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
aea206b
to
2c0fe47
Compare
Signed-off-by: Pradeep Kumar K J <[email protected]>
2c0fe47
to
aee467a
Compare
Proposed changes
Add a feature to push measurements to the child and main device services using the new
te
topic.Types of changes
Paste Link to the issue
Checklist
cargo fmt
as mentioned in CODING_GUIDELINEScargo clippy
as mentioned in CODING_GUIDELINESFurther comments