rumtk_core/
lib.rs

1/*
2 * rumtk attempts to implement HL7 and medical protocols for interoperability in medicine.
3 * This toolkit aims to be reliable, simple, performant, and standards compliant.
4 * Copyright (C) 2024  Luis M. Santos, M.D.
5 * Copyright (C) 2025  MedicalMasses L.L.C.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22//#![feature(unboxed_closures)]
23//#![feature(inherent_associated_types)]
24#![feature(type_alias_impl_trait)]
25#![feature(unboxed_closures)]
26#![feature(buf_read_has_data_left)]
27#![feature(mapped_lock_guards)]
28
29pub mod cache;
30pub mod cli;
31pub mod core;
32pub mod dependencies;
33pub mod hash;
34pub mod json;
35pub mod log;
36pub mod maths;
37pub mod net;
38pub mod scripting;
39pub mod search;
40pub mod strings;
41pub mod threading;
42pub mod types;
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47    use crate::cache::RUMCache;
48    use crate::core::{clamp_index, RUMResult};
49    use crate::search::rumtk_search::*;
50    use crate::strings::{
51        rumtk_format, RUMArrayConversions, RUMString, RUMStringConversions, StringUtils,
52    };
53    use crate::types::{RUMDeserialize, RUMDeserializer, RUMSerialize, RUMSerializer};
54    use compact_str::CompactString;
55    use serde_json::to_string;
56    use std::sync::Arc;
57    use tokio::sync::RwLock;
58
59    #[test]
60    fn test_is_escaped_str() {
61        let input = "\r\n\'\\\"";
62        let expected = false;
63        let result = strings::is_escaped_str(input);
64        println!("Input: {} Expected: {} Got: {}", input, expected, result);
65        assert_eq!(
66            expected, result,
67            "Incorrect detection of unescaped string as escaped!!"
68        );
69        println!("Passed!")
70    }
71
72    #[test]
73    fn test_escaping_control() {
74        let input = "\r\n\'\"\\";
75        let expected = "\\r\\n\\'\\\"\\\\";
76        let result = strings::escape(input);
77        println!(
78            "Input: {} Expected: {} Got: {}",
79            input,
80            expected,
81            result.as_str()
82        );
83        assert_eq!(expected, result, "Incorrect string escaping!");
84        println!("Passed!")
85    }
86
87    #[test]
88    fn test_escaping_unicode() {
89        let input = "❤";
90        let expected = "\\u2764";
91        let result = strings::escape(input);
92        println!(
93            "Input: {} Expected: {} Got: {}",
94            input,
95            expected,
96            result.as_str()
97        );
98        assert_eq!(expected, result, "Incorrect string escaping!");
99        println!("Passed!")
100    }
101
102    #[test]
103    fn test_unescaping_unicode() {
104        let input = "❤";
105        let escaped = strings::escape(input);
106        let expected = "❤";
107        let result = RUMString::from_utf8(strings::unescape(escaped.as_str()).unwrap()).unwrap();
108        println!(
109            "Input: {} Expected: {} Got: {}",
110            input,
111            expected,
112            result.as_str()
113        );
114        assert_eq!(expected, result.as_str(), "Incorrect string unescaping!");
115        println!("Passed!")
116    }
117
118    #[test]
119    fn test_unescaping_string() {
120        let input = "I \\u2764 my wife!";
121        let expected = "I ❤ my wife!";
122        let result = strings::unescape_string(input).unwrap();
123        println!(
124            "Input: {} Expected: {} Got: {}",
125            input,
126            expected,
127            result.as_str()
128        );
129        assert_eq!(expected, result.as_str(), "Incorrect string unescaping!");
130        println!("Passed!")
131    }
132
133    #[test]
134    fn test_is_escaped_string() {
135        let input = "I \\u2764 my wife!";
136        let expected = true;
137        let result = strings::is_escaped_str(input);
138        println!("Input: {} Expected: {} Got: {}", input, expected, result);
139        assert_eq!(
140            expected, result,
141            "Escaped string detected as unescaped string!"
142        );
143        println!("Passed!")
144    }
145
146    #[test]
147    fn test_is_unescaped_string() {
148        let input = "I ❤ my wife!";
149        let expected = false;
150        let result = strings::is_escaped_str(input);
151        println!("Input: {} Expected: {} Got: {}", input, expected, result);
152        assert_eq!(
153            expected, result,
154            "Unescaped string detected as escaped string!"
155        );
156        println!("Passed!")
157    }
158
159    #[test]
160    fn test_unique_string() {
161        let input = "I❤mywife!";
162        assert!(input.is_unique(), "String was not detected as unique.");
163    }
164
165    #[test]
166    fn test_non_unique_string() {
167        let input = "I❤❤mywife!";
168        assert!(!input.is_unique(), "String was detected as unique.");
169    }
170
171    #[test]
172    fn test_escaping_string() {
173        let input = "I ❤ my wife!";
174        let expected = "I \\u2764 my wife!";
175        let result = strings::escape(input);
176        println!(
177            "Input: {} Expected: {} Got: {}",
178            input,
179            expected,
180            result.as_str()
181        );
182        assert_eq!(expected, result.as_str(), "Incorrect string escaping!");
183        println!("Passed!")
184    }
185
186    #[test]
187    fn test_autodecode_utf8() {
188        let input = "I ❤ my wife!";
189        let result = strings::try_decode(input.as_bytes());
190        println!(
191            "Input: {} Expected: {} Got: {}",
192            input,
193            input,
194            result.as_str()
195        );
196        assert_eq!(input, result, "Incorrect string decoding!");
197        println!("Passed!")
198    }
199
200    #[test]
201    fn test_autodecode_other() {
202        //TODO: Need an example of other encoding texts.
203        let input = "I ❤ my wife!";
204        let result = input;
205        println!("Input: {} Expected: {} Got: {}", input, input, result);
206        assert_eq!(input, result, "Incorrect string decoding!");
207        println!("Passed!")
208    }
209
210    #[test]
211    fn test_decode() {
212        let input = "I ❤ my wife!";
213        let result = strings::try_decode_with(input.as_bytes(), "utf-8");
214        println!(
215            "Input: {} Expected: {} Got: {}",
216            input,
217            input,
218            result.as_str()
219        );
220        assert_eq!(input, result, "Incorrect string decoding!");
221        println!("Passed!")
222    }
223
224    #[test]
225    fn test_rumcache_insertion() {
226        let mut cache: RUMCache<&str, CompactString> = RUMCache::with_capacity(5);
227        cache.insert("❤", CompactString::from("I ❤ my wife!"));
228        println!("Contents: {:#?}", &cache);
229        assert_eq!(cache.len(), 1, "Incorrect number of items in cache!");
230        println!("Passed!")
231    }
232
233    #[test]
234    fn test_search_string_letters() {
235        let input = "Hello World!";
236        let expr = r"\w";
237        let result = string_search(input, expr, "");
238        let expected: RUMString = RUMString::from("HelloWorld");
239        println!(
240            "Input: {:?} Expected: {:?} Got: {:?}",
241            input, expected, result
242        );
243        assert_eq!(expected, result, "String search results mismatch");
244        println!("Passed!")
245    }
246
247    #[test]
248    fn test_search_string_words() {
249        let input = "Hello World!";
250        let expr = r"\w+";
251        let result = string_search(input, expr, " ");
252        let expected: RUMString = RUMString::from("Hello World");
253        println!(
254            "Input: {:?} Expected: {:?} Got: {:?}",
255            input, expected, result
256        );
257        assert_eq!(expected, result, "String search results mismatch");
258        println!("Passed!")
259    }
260
261    #[test]
262    fn test_search_string_named_groups() {
263        let input = "Hello World!";
264        let expr = r"(?<hello>\w{5}) (?<world>\w{5})";
265        let result = string_search_named_captures(input, expr, "");
266        let expected: RUMString = RUMString::from("World");
267        println!(
268            "Input: {:?} Expected: {:?} Got: {:?}",
269            input, expected, result
270        );
271        assert_eq!(expected, result["world"], "String search results mismatch");
272        println!("Passed!")
273    }
274
275    #[test]
276    fn test_search_string_all_groups() {
277        let input = "Hello World!";
278        let expr = r"(?<hello>\w{5}) (?<world>\w{5})";
279        let result = string_search_all_captures(input, expr, "");
280        let expected: Vec<&str> = vec!["Hello", "World"];
281        println!(
282            "Input: {:?} Expected: {:?} Got: {:?}",
283            input, expected, result
284        );
285        assert_eq!(expected, result, "String search results mismatch");
286        println!("Passed!")
287    }
288
289    ///////////////////////////////////Threading Tests/////////////////////////////////////////////////
290    #[test]
291    fn test_default_num_threads() {
292        use num_cpus;
293        let threads = threading::threading_functions::get_default_system_thread_count();
294        assert_eq!(
295            threads >= num_cpus::get(),
296            true,
297            "Default thread count is incorrect! We got {}, but expected {}!",
298            threads,
299            num_cpus::get()
300        );
301    }
302
303    #[test]
304    fn test_execute_job() {
305        let rt = rumtk_init_threads!();
306        let expected = vec![1, 2, 3];
307        let task_processor = async |args: &SafeTaskArgs<i32>| -> RUMResult<Vec<i32>> {
308            let owned_args = Arc::clone(args);
309            let lock_future = owned_args.read();
310            let locked_args = lock_future.await;
311            let mut results = TaskItems::<i32>::with_capacity(locked_args.len());
312            print!("Contents: ");
313            for arg in locked_args.iter() {
314                results.push(arg.clone());
315                println!("{} ", &arg);
316            }
317            Ok(results)
318        };
319        let locked_args = RwLock::new(expected.clone());
320        let task_args = SafeTaskArgs::<i32>::new(locked_args);
321        let task_result = rumtk_wait_on_task!(rt, task_processor, &task_args);
322        let result = task_result.unwrap();
323        assert_eq!(&result, &expected, "{}", rumtk_format!("Task processing returned a different result than expected! Expected {:?} \nResults {:?}", &expected, &result));
324    }
325
326    #[test]
327    fn test_execute_job_macros() {
328        let rt = rumtk_init_threads!();
329        let expected = vec![1, 2, 3];
330        let task_processor = async |args: &SafeTaskArgs<i32>| -> RUMResult<Vec<i32>> {
331            let owned_args = Arc::clone(args);
332            let lock_future = owned_args.read();
333            let locked_args = lock_future.await;
334            let mut results = TaskItems::<i32>::with_capacity(locked_args.len());
335            print!("Contents: ");
336            for arg in locked_args.iter() {
337                results.push(arg.clone());
338                println!("{} ", &arg);
339            }
340            Ok(results)
341        };
342        let task_args = rumtk_create_task_args!(1, 2, 3);
343        let task_result = rumtk_wait_on_task!(rt, task_processor, &task_args);
344        let result = task_result.unwrap();
345        assert_eq!(&result, &expected, "{}", rumtk_format!("Task processing returned a different result than expected! Expected {:?} \nResults {:?}", &expected, &result));
346    }
347
348    #[test]
349    fn test_execute_job_macros_one_line() {
350        let rt = rumtk_init_threads!();
351        let expected = vec![1, 2, 3];
352        let result = rumtk_exec_task!(
353            async |args: &SafeTaskArgs<i32>| -> RUMResult<Vec<i32>> {
354                let owned_args = Arc::clone(args);
355                let lock_future = owned_args.read();
356                let locked_args = lock_future.await;
357                let mut results = TaskItems::<i32>::with_capacity(locked_args.len());
358                print!("Contents: ");
359                for arg in locked_args.iter() {
360                    results.push(arg.clone());
361                    println!("{} ", &arg);
362                }
363                Ok(results)
364            },
365            vec![1, 2, 3]
366        )
367        .unwrap();
368        assert_eq!(&result, &expected, "{}", rumtk_format!("Task processing returned a different result than expected! Expected {:?} \nResults {:?}", &expected, &result));
369    }
370
371    #[test]
372    fn test_clamp_index_positive_index() {
373        let values = vec![1, 2, 3, 4];
374        let given_index = 3isize;
375        let max_size = values.len() as isize;
376        let index = clamp_index(&given_index, &max_size).unwrap();
377        assert_eq!(
378            index, 3,
379            "Index mismatch! Requested index {} but got {}",
380            &given_index, &index
381        );
382        assert_eq!(
383            values[index], 4,
384            "Value mismatch! Expected {} but got {}",
385            &values[3], &values[index]
386        );
387    }
388
389    #[test]
390    fn test_clamp_index_reverse_index() {
391        let values = vec![1, 2, 3, 4];
392        let given_index = -1isize;
393        let max_size = values.len() as isize;
394        let index = clamp_index(&given_index, &max_size).unwrap();
395        assert_eq!(
396            index, 4,
397            "Index mismatch! Requested index {} but got {}",
398            &given_index, &index
399        );
400        assert_eq!(
401            values[index - 1],
402            4,
403            "Value mismatch! Expected {} but got {}",
404            &values[3],
405            &values[index]
406        );
407    }
408
409    ///////////////////////////////////Queue Tests/////////////////////////////////////////////////
410    use crate::cli::cli_utils::print_license_notice;
411    use crate::net::tcp::LOCALHOST;
412    use crate::threading::threading_functions::sleep;
413    use crate::threading::threading_manager::*;
414
415    #[test]
416    fn test_queue_data() {
417        let expected = vec![
418            RUMString::from("Hello"),
419            RUMString::from("World!"),
420            RUMString::from("Overcast"),
421            RUMString::from("and"),
422            RUMString::from("Sad"),
423        ];
424        type TestResult = RUMResult<Vec<RUMString>>;
425        let mut queue: TaskManager<TestResult> = TaskManager::new(&5).unwrap();
426        let locked_args = RwLock::new(expected.clone());
427        let task_args = SafeTaskArgs::<RUMString>::new(locked_args);
428        let processor = rumtk_create_task!(
429            async |args: &SafeTaskArgs<RUMString>| -> TestResult {
430                let owned_args = Arc::clone(args);
431                let lock_future = owned_args.read();
432                let locked_args = lock_future.await;
433                let mut results = TaskItems::<RUMString>::with_capacity(locked_args.len());
434                print!("Contents: ");
435                for arg in locked_args.iter() {
436                    print!("{} ", &arg);
437                    results.push(RUMString::new(arg));
438                }
439                Ok(results)
440            },
441            task_args
442        );
443
444        queue.add_task::<_>(processor);
445        let results = queue.wait();
446
447        let mut result_data = Vec::<RUMString>::with_capacity(5);
448        for r in results {
449            for v in r.unwrap().result.clone().unwrap().iter() {
450                for value in v.iter() {
451                    result_data.push(value.clone());
452                }
453            }
454        }
455        assert_eq!(result_data, expected, "Results do not match expected!");
456    }
457
458    ///////////////////////////////////Net Tests/////////////////////////////////////////////////
459    #[test]
460    fn test_server_start() {
461        let mut server = match rumtk_create_server!("localhost", 0) {
462            Ok(server) => server,
463            Err(e) => panic!("Failed to create server because {}", e),
464        };
465        match server.start(false) {
466            Ok(_) => (),
467            Err(e) => panic!("Failed to start server because {}", e),
468        }
469    }
470
471    #[test]
472    fn test_server_send() {
473        let msg = RUMString::from("Hello World!");
474        let mut server = match rumtk_create_server!(LOCALHOST, 0, 1) {
475            Ok(server) => server,
476            Err(e) => panic!("Failed to create server because {}", e),
477        };
478        match server.start(false) {
479            Ok(_) => (),
480            Err(e) => panic!("Failed to start server because {}", e),
481        };
482        let address_info = server.get_address_info().unwrap();
483        let (ip, port) = rumtk_get_ip_port!(address_info);
484        println!("Sleeping");
485        rumtk_sleep!(1);
486        let mut client = match rumtk_connect!(port) {
487            Ok(client) => client,
488            Err(e) => panic!("Failed to create server because {}", e),
489        };
490        let client_id = client.get_address().unwrap();
491        rumtk_sleep!(1);
492        match server.send(&client_id, &msg.to_raw()) {
493            Ok(_) => (),
494            Err(e) => panic!("Server failed to send message because {}", e),
495        };
496        rumtk_sleep!(1);
497        let received_message = client.receive().unwrap();
498        assert_eq!(
499            &msg.to_raw(),
500            &received_message,
501            "{}",
502            rumtk_format!(
503                "Received message does not match sent message by server {:?}",
504                &received_message
505            )
506        );
507    }
508
509    #[test]
510    fn test_server_receive() {
511        let msg = RUMString::from("Hello World!");
512        let mut server = match rumtk_create_server!(LOCALHOST, 0) {
513            Ok(server) => server,
514            Err(e) => panic!("Failed to create server because {}", e),
515        };
516        match server.start(false) {
517            Ok(_) => (),
518            Err(e) => panic!("Failed to start server because {}", e),
519        };
520        let address_info = server.get_address_info().unwrap();
521        let (ip, port) = rumtk_get_ip_port!(address_info);
522        println!("Sleeping");
523        rumtk_sleep!(1);
524        let mut client = match rumtk_connect!(port) {
525            Ok(client) => client,
526            Err(e) => panic!("Failed to create server because {}", e),
527        };
528        match client.send(&msg.to_raw()) {
529            Ok(_) => (),
530            Err(e) => panic!("Failed to send message because {}", e),
531        };
532        rumtk_sleep!(1);
533        let client_id = client.get_address().expect("Failed to get client id");
534        let incoming_message = server.receive(&client_id).unwrap().to_rumstring();
535        println!("Received message => {:?}", &incoming_message);
536        assert_eq!(&incoming_message, msg, "Received message corruption!");
537    }
538
539    #[test]
540    fn test_server_get_clients() {
541        let mut server = match rumtk_create_server!(LOCALHOST, 0) {
542            Ok(server) => server,
543            Err(e) => panic!("Failed to create server because {}", e),
544        };
545        match server.start(false) {
546            Ok(_) => (),
547            Err(e) => panic!("Failed to start server because {}", e),
548        };
549        let address_info = server.get_address_info().unwrap();
550        let (ip, port) = rumtk_get_ip_port!(address_info);
551        println!("Sleeping");
552        rumtk_sleep!(1);
553        let mut client = match rumtk_connect!(port) {
554            Ok(client) => client,
555            Err(e) => panic!("Failed to create server because {}", e),
556        };
557        rumtk_sleep!(1);
558        let expected_client_id = client.get_address().expect("Failed to get client id");
559        let clients = server.get_client_ids();
560        let incoming_client_id = clients.get(0).expect("Expected client to have connected!");
561        println!("Connected client id => {}", &incoming_client_id);
562        assert_eq!(
563            &incoming_client_id, &expected_client_id,
564            "Connected client does not match the connecting client! Client id => {}",
565            &incoming_client_id
566        );
567    }
568
569    #[test]
570    fn test_server_stop() {
571        let msg = RUMString::from("Hello World!");
572        let mut server = match rumtk_create_server!("localhost", 0) {
573            Ok(server) => server,
574            Err(e) => panic!("Failed to create server because {}", e),
575        };
576        match server.start(false) {
577            Ok(_) => (),
578            Err(e) => panic!("Failed to start server because {}", e),
579        };
580        println!("Sleeping");
581        rumtk_sleep!(1);
582        match server.stop() {
583            Ok(_) => (),
584            Err(e) => panic!("Failed to stop server because {}", e),
585        };
586    }
587
588    #[test]
589    fn test_server_get_address_info() {
590        let msg = RUMString::from("Hello World!");
591        let mut server = match rumtk_create_server!("localhost", 0) {
592            Ok(server) => server,
593            Err(e) => panic!("Failed to create server because {}", e),
594        };
595        match server.start(false) {
596            Ok(_) => (),
597            Err(e) => panic!("Failed to start server because {}", e),
598        };
599        println!("Sleeping");
600        rumtk_sleep!(1);
601        match server.get_address_info() {
602            Some(addr) => println!("Server address info => {}", addr),
603            None => panic!("No address. Perhaps the server was never initialized?"),
604        };
605    }
606
607    #[test]
608    fn test_client_send() {
609        let msg = RUMString::from("Hello World!");
610        let mut server = match rumtk_create_server!(LOCALHOST, 0) {
611            Ok(server) => server,
612            Err(e) => panic!("Failed to create server because {}", e),
613        };
614        match server.start(false) {
615            Ok(_) => (),
616            Err(e) => panic!("Failed to start server because {}", e),
617        };
618        let address_info = server.get_address_info().unwrap();
619        let (ip, port) = rumtk_get_ip_port!(address_info);
620        println!("Sleeping");
621        rumtk_sleep!(1);
622        let mut client = match rumtk_connect!(port) {
623            Ok(client) => client,
624            Err(e) => panic!("Failed to create server because {}", e),
625        };
626        rumtk_sleep!(2);
627        match client.send(&msg.to_raw()) {
628            Ok(_) => (),
629            Err(e) => panic!("Failed to send message because {}", e),
630        };
631        rumtk_sleep!(1);
632        let clients = server.get_client_ids();
633        let incoming_client_id = clients.first().expect("Expected client to have connected!");
634        let mut received_message = server.receive(incoming_client_id).unwrap();
635        if received_message.is_empty() {
636            rumtk_sleep!(1);
637            received_message = server.receive(incoming_client_id).unwrap();
638        }
639        assert_eq!(
640            &msg.to_raw(),
641            &received_message,
642            "{}",
643            rumtk_format!(
644                "Received message does not match sent message by client {:?}",
645                &received_message
646            )
647        );
648    }
649
650    ////////////////////////////JSON Tests/////////////////////////////////
651
652    #[test]
653    fn test_serialize_json() {
654        #[derive(RUMSerialize)]
655        struct MyStruct {
656            hello: RUMString,
657        }
658
659        let hw = MyStruct {
660            hello: RUMString::from("World"),
661        };
662        let hw_str = rumtk_serialize!(&hw, true).unwrap();
663
664        assert!(
665            !hw_str.is_empty(),
666            "Empty JSON string generated from the test struct!"
667        );
668    }
669
670    #[test]
671    fn test_deserialize_serde_json() {
672        use serde_json::{from_str, to_string};
673
674        #[derive(RUMSerialize, RUMDeserialize, PartialEq, Debug, Clone)]
675        struct MyStruct {
676            hello: RUMString,
677        }
678
679        let hw = MyStruct {
680            hello: RUMString::from("World"),
681        };
682        let hw_str = to_string(&hw).unwrap();
683        let new_hw: MyStruct = from_str(&hw_str).unwrap();
684
685        assert_eq!(
686            new_hw, hw,
687            "Deserialized JSON does not match the expected value!"
688        );
689    }
690
691    #[test]
692    fn test_deserialize_json() {
693        #[derive(RUMSerialize, RUMDeserialize, PartialEq)]
694        struct MyStruct {
695            hello: RUMString,
696        }
697
698        let hw = MyStruct {
699            hello: RUMString::from("World"),
700        };
701        let hw_str = rumtk_serialize!(&hw, true).unwrap();
702        let new_hw: MyStruct = rumtk_deserialize!(&hw_str).unwrap();
703
704        assert!(
705            new_hw == hw,
706            "Deserialized JSON does not match the expected value!"
707        );
708    }
709
710    /*
711    #[test]
712    fn test_escape_unescape_json() {
713        #[derive(Serialize, Deserialize, PartialEq)]
714        struct MyStruct {
715            hello: RUMString,
716        }
717
718        let hw = MyStruct {
719            hello: RUMString::from("World"),
720        };
721
722        let hw_str = rumtk_serialize!(&hw, true).unwrap();
723        let hw_escaped_str = strings::basic_escape(&hw_str);
724        println!("Escaped => {}", hw_escaped_str);
725
726        let hw_unescaped_str = strings::unescape_string(&hw_escaped_str).unwrap();
727        println!("Unescaped => {}", hw_unescaped_str);
728        assert_eq!(
729            hw_str.to_rumstring(),
730            hw_unescaped_str.to_rumstring(),
731            "Unescaped serialized JSON mismatch!"
732        );
733
734        let new_hw: MyStruct = rumtk_deserialize!(&hw_unescaped_str).unwrap();
735
736        assert!(
737            new_hw == hw,
738            "Deserialized JSON does not match the expected value!"
739        );
740    }
741    */
742
743    ////////////////////////////CLI Tests/////////////////////////////////
744
745    #[test]
746    fn test_print_license_notice() {
747        print_license_notice("RUMTK", "2025", &vec!["Luis M. Santos, M.D."]);
748    }
749
750    //////////////////////////////////////////////////////////////////////////////////////////////
751}