@@ -15,7 +15,7 @@ async fn new_entity_store() {
1515}
1616
1717proptest ! {
18- #![ proptest_config( proptest:: prelude:: ProptestConfig :: with_cases( 1000 ) ) ]
18+ // #![proptest_config(proptest::prelude::ProptestConfig::with_cases(1000))]
1919 #[ test]
2020 fn it_works_for_any_registration_order( registrations in model:: walk( 10 ) ) {
2121 tokio:: runtime:: Builder :: new_current_thread( )
@@ -26,7 +26,7 @@ proptest! {
2626 let ( mut entity_store, _mqtt_output) = entity:: server( "device-under-test" ) ;
2727 let mut state = model:: State :: new( ) ;
2828
29- for ( protocol, action) in registrations {
29+ for model :: Command { protocol, action} in registrations. 0 {
3030 let expected_updates = state. apply( protocol, action. clone( ) ) ;
3131 let actual_updates = match entity_store. handle( ( protocol, action) . into( ) ) . await {
3232 EntityStoreResponse :: Create ( Ok ( registered_entities) ) => {
@@ -128,11 +128,91 @@ mod model {
128128 use proptest:: prelude:: * ;
129129 use std:: collections:: HashMap ;
130130 use std:: collections:: HashSet ;
131+ use std:: fmt:: Debug ;
132+ use std:: fmt:: Display ;
133+ use std:: fmt:: Formatter ;
131134 use tedge_api:: entity:: EntityType ;
132135 use tedge_api:: entity_store:: EntityRegistrationMessage ;
133136 use tedge_api:: mqtt_topics:: EntityTopicId ;
134137 use tedge_api:: mqtt_topics:: MqttSchema ;
135138
139+ #[ derive( Clone ) ]
140+ pub struct Commands ( pub Vec < Command > ) ;
141+
142+ impl Debug for Commands {
143+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
144+ // mimicking a sequence of cli commands, with no extra quotes
145+ // e.g:
146+ // tedge mqtt pub -r device/main/service/a '{"@parent":"device/main//","@type":"service","x":"9"}' \
147+ // && tedge http post /tedge/entity-store/v1/entities '{"@parent":"device/main//","@topic-id":"device/c//","@type":"child-device","z":"5"}'
148+ let mut sep = if f. alternate ( ) {
149+ "\n " // On test unit output, print each command on a new line
150+ } else {
151+ "" // On proptest log, print all the commands on a single line
152+ } ;
153+
154+ for command in & self . 0 {
155+ f. write_str ( sep) ?;
156+ f. write_str ( format ! ( "{command}" ) . as_str ( ) ) ?;
157+ if f. alternate ( ) {
158+ sep = "\n " ; // On test unit output, print each command on a new line
159+ } else {
160+ sep = " && " ; // On proptest log, print all the commands on a single line
161+ }
162+ }
163+ f. write_str ( "\n " ) ?;
164+ Ok ( ( ) )
165+ }
166+ }
167+
168+ #[ derive( Clone ) ]
169+ pub struct Command {
170+ pub protocol : Protocol ,
171+ pub action : Action ,
172+ }
173+
174+ impl Debug for Command {
175+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
176+ // Print the command line with no extra quotes
177+ f. write_str ( format ! ( "{self}" ) . as_str ( ) )
178+ }
179+ }
180+
181+ impl Display for Command {
182+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
183+ let topic = self . action . topic_id ( ) . to_string ( ) ;
184+
185+ let cmd = match self . action {
186+ Action :: AddDevice { .. } | Action :: AddService { .. } => {
187+ let payload = self . action . payload ( ) ;
188+ match self . protocol {
189+ Protocol :: HTTP => {
190+ let mut payload = payload;
191+ payload. insert ( "@topic-id" . to_string ( ) , topic. into ( ) ) ;
192+ let payload = serde_json:: Value :: Object ( payload) . to_string ( ) ;
193+ format ! ( "tedge http post /tedge/entity-store/v1/entities '{payload}'" )
194+ }
195+ Protocol :: MQTT => {
196+ let payload = serde_json:: Value :: Object ( payload) . to_string ( ) ;
197+ format ! ( "tedge mqtt pub -r te/{topic} '{payload}'" )
198+ }
199+ }
200+ }
201+ Action :: RemDevice { .. } | Action :: RemService { .. } => match self . protocol {
202+ Protocol :: HTTP => {
203+ format ! ( "tedge http delete /tedge/entity-store/v1/entities/{topic}" )
204+ }
205+ Protocol :: MQTT => {
206+ format ! ( "tedge mqtt pub -r te/{topic} ''" )
207+ }
208+ } ,
209+ } ;
210+
211+ // Print the command line with no extra quotes
212+ f. write_str ( cmd. as_str ( ) )
213+ }
214+ }
215+
136216 #[ derive( Debug , Copy , Clone , Eq , PartialEq ) ]
137217 #[ allow( clippy:: upper_case_acronyms) ]
138218 pub enum Protocol {
@@ -228,6 +308,15 @@ mod model {
228308 Action :: RemDevice { .. } | Action :: RemService { .. } => serde_json:: Map :: new ( ) ,
229309 }
230310 }
311+
312+ pub fn payload ( & self ) -> serde_json:: Map < String , serde_json:: Value > {
313+ let mut props = self . properties ( ) ;
314+ if let Some ( parent) = self . parent_topic_id ( ) {
315+ props. insert ( "@parent" . to_string ( ) , parent. to_string ( ) . into ( ) ) ;
316+ }
317+ props. insert ( "@type" . to_string ( ) , self . target_type ( ) . to_string ( ) . into ( ) ) ;
318+ props
319+ }
231320 }
232321
233322 impl From < Action > for EntityRegistrationMessage {
@@ -499,42 +588,48 @@ mod model {
499588 }
500589
501590 prop_compose ! {
502- pub fn random_action_on ( topic: String ) (
591+ pub fn random_command_on ( topic: String ) (
503592 protocol in random_protocol( ) ,
504593 action in 1 ..5 ,
505594 props in random_props( 2 )
506- ) -> ( Protocol , Action ) {
595+ ) -> Command {
507596 let topic = topic. to_owned( ) ;
508597 let action = match action {
509598 1 => Action :: AddDevice { topic, props } ,
510599 2 => Action :: AddService { topic, props } ,
511600 3 => Action :: RemService { topic } ,
512601 _ => Action :: RemDevice { topic } ,
513602 } ;
514- ( protocol, action)
603+ Command { protocol, action }
515604 }
516605 }
517606
518- pub fn random_action ( ) -> impl Strategy < Value = ( Protocol , Action ) > {
519- random_name ( ) . prop_flat_map ( random_action_on )
607+ pub fn random_command ( ) -> impl Strategy < Value = Command > {
608+ random_name ( ) . prop_flat_map ( random_command_on )
520609 }
521610
522- fn step ( actions : Vec < ( Protocol , Action ) > ) -> impl Strategy < Value = Vec < ( Protocol , Action ) > > {
523- let nodes = actions. iter ( ) . map ( |( _, a) | a. target ( ) . to_owned ( ) ) . collect ( ) ;
611+ fn step ( actions : Commands ) -> impl Strategy < Value = Commands > {
612+ let nodes = actions
613+ . 0
614+ . iter ( )
615+ . map ( |c| c. action . target ( ) . to_owned ( ) )
616+ . collect ( ) ;
524617 pick_random_or_new ( nodes)
525- . prop_flat_map ( random_action_on )
618+ . prop_flat_map ( random_command_on )
526619 . prop_flat_map ( move |action| {
527620 let mut actions = actions. clone ( ) ;
528- actions. push ( action) ;
621+ actions. 0 . push ( action) ;
529622 Just ( actions)
530623 } )
531624 }
532625
533- pub fn walk ( max_length : u32 ) -> impl Strategy < Value = Vec < ( Protocol , Action ) > > {
626+ pub fn walk ( max_length : u32 ) -> impl Strategy < Value = Commands > {
534627 if max_length == 0 {
535- Just ( vec ! [ ] ) . boxed ( )
628+ Just ( Commands ( vec ! [ ] ) ) . boxed ( )
536629 } else if max_length == 1 {
537- prop:: collection:: vec ( random_action ( ) , 0 ..=1 ) . boxed ( )
630+ prop:: collection:: vec ( random_command ( ) , 0 ..=1 )
631+ . prop_flat_map ( |cmds| Just ( Commands ( cmds) ) )
632+ . boxed ( )
538633 } else {
539634 walk ( max_length - 1 ) . prop_flat_map ( step) . boxed ( )
540635 }
0 commit comments