Skip to content

Commit 7b4a96a

Browse files
committed
Check combined registration over MQTT and HTTP
Signed-off-by: Didier Wenzek <[email protected]>
1 parent a8c406b commit 7b4a96a

File tree

2 files changed

+107
-34
lines changed
  • crates/core/tedge_agent

2 files changed

+107
-34
lines changed

crates/core/tedge_agent/proptest-regressions/entity_manager/tests.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,3 @@
55
# It is recommended to check this file in to source control so that
66
# everyone who runs the test benefits from these saved cases.
77
cc eb284ae3160cf4a071a3ca80175b2c72680b7c8ba19b8d577805ef1317eca2db # shrinks to registrations = [(MQTT, AddService { topic: "ac", props: [] }), (MQTT, AddService { topic: "ac", props: [] }), (MQTT, AddDevice { topic: "ac", props: [] }), (HTTP, AddService { topic: "ac", props: [("x", "4")] }), (HTTP, AddDevice { topic: "a", props: [] })]
8-
cc 6e3a5e19daf6c3a4732a0504822f2164c5d0f49ed5652e1e67c779a23aedbe77 # shrinks to registrations = [(HTTP, AddService { topic: "a", props: [] }), (MQTT, AddDevice { topic: "abc", props: [("z", "0")] }), (MQTT, AddDevice { topic: "abc", props: [("z", "8")] }), (HTTP, AddService { topic: "aa", props: [] }), (MQTT, AddDevice { topic: "abc", props: [("y", "3")] }), (MQTT, AddDevice { topic: "ab", props: [] }), (HTTP, AddService { topic: "a", props: [] }), (HTTP, AddDevice { topic: "abc", props: [("y", "7")] }), (MQTT, AddDevice { topic: "a", props: [] })]
9-
cc aa8e0b76fd3142bf3cd1df1f453a4468defd437111b406887d760f68d84ad5c3 # shrinks to registrations = tedge http delete /tedge/entity-store/v1/entities/device/a// && tedge http delete /tedge/entity-store/v1/entities/device/main/service/a && tedge mqtt pub -r device/main/service/a '{"@parent":"device/main//","@type":"service","z":"1"}' && tedge http delete /tedge/entity-store/v1/entities/device/main/service/a && tedge mqtt pub -r device/ab// '' && tedge mqtt pub -r device/a// '' && tedge http post /tedge/entity-store/v1/entities '{"@parent":"device/main//","@topic-id":"device/a//","@type":"child-device"}' && tedge http post /tedge/entity-store/v1/entities '{"@parent":"device/main//","@topic-id":"device/a//","@type":"child-device"}' && tedge mqtt pub -r device/a// '{"@parent":"device/main//","@type":"child-device","z":"4"}'

crates/core/tedge_agent/src/entity_manager/tests.rs

Lines changed: 107 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ async fn new_entity_store() {
1616

1717
proptest! {
1818
#[test]
19-
fn it_works_for_any_registration_order(registrations in model::walk(3)) {
19+
fn it_works_for_any_registration_order(registrations in model::walk(6)) {
2020
tokio::runtime::Builder::new_current_thread()
2121
.enable_all()
2222
.build()
@@ -25,9 +25,9 @@ proptest! {
2525
let (mut entity_store, _mqtt_output) = entity::server("device-under-test");
2626
let mut state = model::State::new();
2727

28-
for step in registrations {
29-
let expected_updates = state.apply(step.clone());
30-
let actual_updates = match entity_store.handle(step.into()).await {
28+
for (protocol,action) in registrations {
29+
let expected_updates = state.apply(protocol, action.clone());
30+
let actual_updates = match entity_store.handle((protocol,action).into()).await {
3131
EntityStoreResponse::Create(Ok(registered_entities)) => {
3232
registered_entities
3333
.iter()
@@ -115,6 +115,14 @@ mod model {
115115
use tedge_api::entity::EntityType;
116116
use tedge_api::entity_store::EntityRegistrationMessage;
117117
use tedge_api::mqtt_topics::EntityTopicId;
118+
use tedge_api::mqtt_topics::MqttSchema;
119+
120+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
121+
#[allow(clippy::upper_case_acronyms)]
122+
pub enum Protocol {
123+
HTTP,
124+
MQTT,
125+
}
118126

119127
#[derive(Debug, Clone)]
120128
pub enum Action {
@@ -206,17 +214,23 @@ mod model {
206214
}
207215
}
208216

217+
impl From<Action> for EntityRegistrationMessage {
218+
fn from(action: Action) -> Self {
219+
EntityRegistrationMessage {
220+
topic_id: action.topic_id(),
221+
external_id: None,
222+
r#type: action.target_type(),
223+
parent: action.parent_topic_id(),
224+
other: action.properties(),
225+
}
226+
}
227+
}
228+
209229
impl From<Action> for EntityStoreRequest {
210230
fn from(action: Action) -> Self {
211231
match &action {
212232
Action::AddDevice { .. } | Action::AddService { .. } => {
213-
let registration = EntityRegistrationMessage {
214-
topic_id: action.topic_id(),
215-
external_id: None,
216-
r#type: action.target_type(),
217-
parent: action.parent_topic_id(),
218-
other: action.properties(),
219-
};
233+
let registration = EntityRegistrationMessage::from(action);
220234
EntityStoreRequest::Create(registration)
221235
}
222236

@@ -227,6 +241,19 @@ mod model {
227241
}
228242
}
229243

244+
impl From<(Protocol, Action)> for EntityStoreRequest {
245+
fn from((protocol, action): (Protocol, Action)) -> Self {
246+
match protocol {
247+
Protocol::HTTP => EntityStoreRequest::from(action),
248+
Protocol::MQTT => {
249+
let registration = EntityRegistrationMessage::from(action);
250+
let message = registration.to_mqtt_message(&MqttSchema::default());
251+
EntityStoreRequest::MqttMessage(message)
252+
}
253+
}
254+
}
255+
}
256+
230257
type PropMap = serde_json::Map<String, serde_json::Value>;
231258

232259
pub struct State {
@@ -240,35 +267,60 @@ mod model {
240267
entities: HashMap::default(),
241268
registered: HashSet::default(),
242269
};
243-
state.apply(Action::AddDevice {
244-
topic: "".to_string(),
245-
props: vec![],
246-
});
270+
state.apply(
271+
Protocol::HTTP,
272+
Action::AddDevice {
273+
topic: "".to_string(),
274+
props: vec![],
275+
},
276+
);
247277
state
248278
}
249279

250-
pub fn apply(&mut self, action: Action) -> HashSet<EntityTopicId> {
280+
pub fn apply(&mut self, protocol: Protocol, action: Action) -> HashSet<EntityTopicId> {
251281
let topic = action.topic_id();
252282

253283
match action {
254284
Action::AddDevice { .. } | Action::AddService { .. } => {
285+
let parent = action.parent_topic_id();
286+
287+
if let Some(parent) = parent.as_ref() {
288+
if protocol == Protocol::HTTP && !self.registered.contains(parent) {
289+
// Under HTTP, registering a child before its parent is an error
290+
return HashSet::new();
291+
}
292+
}
293+
255294
if self.entities.contains_key(&topic) {
256295
HashSet::new()
257296
} else {
258297
let entity_type = action.target_type();
259-
let parent = action.parent_topic_id();
260298
self.entities.insert(
261299
topic.clone(),
262300
(entity_type, parent.clone(), action.properties()),
263301
);
264-
self.register(topic, parent)
302+
303+
let new_entities = self.register(topic, parent);
304+
if protocol == Protocol::HTTP {
305+
new_entities
306+
} else {
307+
// Under MQTT, no response is sent back
308+
HashSet::new()
309+
}
265310
}
266311
}
267312

268313
Action::RemDevice { .. } | Action::RemService { .. } => {
269314
if self.entities.contains_key(&topic) {
270315
self.entities.remove(&topic);
271-
self.deregister(topic)
316+
317+
let old_entities = self.deregister(topic);
318+
if protocol == Protocol::HTTP {
319+
old_entities
320+
} else {
321+
// Under MQTT, no response is sent back
322+
HashSet::new()
323+
}
272324
} else {
273325
HashSet::new()
274326
}
@@ -357,6 +409,16 @@ mod model {
357409
}
358410
}
359411

412+
prop_compose! {
413+
pub fn random_protocol()(protocol in "[hm]") -> Protocol {
414+
if protocol == "h" {
415+
Protocol::HTTP
416+
} else {
417+
Protocol::MQTT
418+
}
419+
}
420+
}
421+
360422
prop_compose! {
361423
pub fn random_name()(id in "[abc]{1,3}") -> String {
362424
id.to_string()
@@ -376,43 +438,56 @@ mod model {
376438
}
377439

378440
prop_compose! {
379-
pub fn random_prop()(key in random_key(), value in random_value()) -> (String,String) {
441+
pub fn random_prop()(
442+
key in random_key(),
443+
value in random_value()
444+
) -> (String,String) {
380445
(key, value)
381446
}
382447
}
383448

384449
prop_compose! {
385-
pub fn random_props(max_length: usize)
386-
(vec in prop::collection::vec(random_prop(), 0..max_length)) -> Vec<(String,String)>
387-
{
450+
pub fn random_props(max_length: usize)(
451+
vec in prop::collection::vec(random_prop(),
452+
0..max_length)
453+
) -> Vec<(String,String)>
454+
{
388455
vec
389-
}
456+
}
390457
}
391458

392459
prop_compose! {
393-
pub fn pick_random_or_new(names: Vec<String>)(id in 0..(names.len()+1), name in random_name()) -> String {
460+
pub fn pick_random_or_new(names: Vec<String>)(
461+
id in 0..(names.len()+1),
462+
name in random_name()
463+
) -> String {
394464
names.get(id).map(|n| n.to_owned()).unwrap_or(name)
395465
}
396466
}
397467

398468
prop_compose! {
399-
pub fn random_action_on(topic: String)(action in 1..5, props in random_props(2)) -> Action {
469+
pub fn random_action_on(topic: String)(
470+
protocol in random_protocol(),
471+
action in 1..5,
472+
props in random_props(2)
473+
) -> (Protocol,Action) {
400474
let topic = topic.to_owned();
401-
match action {
475+
let action = match action {
402476
1 => Action::AddDevice{ topic, props },
403477
2 => Action::AddService{ topic, props },
404478
3 => Action::RemService{ topic },
405479
_ => Action::RemDevice{ topic },
406-
}
480+
};
481+
(protocol, action)
407482
}
408483
}
409484

410-
pub fn random_action() -> impl Strategy<Value = Action> {
485+
pub fn random_action() -> impl Strategy<Value = (Protocol, Action)> {
411486
random_name().prop_flat_map(random_action_on)
412487
}
413488

414-
fn step(actions: Vec<Action>) -> impl Strategy<Value = Vec<Action>> {
415-
let nodes = actions.iter().map(|a| a.target().to_owned()).collect();
489+
fn step(actions: Vec<(Protocol, Action)>) -> impl Strategy<Value = Vec<(Protocol, Action)>> {
490+
let nodes = actions.iter().map(|(_, a)| a.target().to_owned()).collect();
416491
pick_random_or_new(nodes)
417492
.prop_flat_map(random_action_on)
418493
.prop_flat_map(move |action| {
@@ -422,7 +497,7 @@ mod model {
422497
})
423498
}
424499

425-
pub fn walk(max_length: u32) -> impl Strategy<Value = Vec<Action>> {
500+
pub fn walk(max_length: u32) -> impl Strategy<Value = Vec<(Protocol, Action)>> {
426501
if max_length == 0 {
427502
Just(vec![]).boxed()
428503
} else if max_length == 1 {

0 commit comments

Comments
 (0)