commit 97d6dcb9233db7dd088e78f14bbab6f50505bf59
parent e5ee69f011f4626e5351b1d6c04734363af3ec7e
Author: triesap <tyson@radroots.org>
Date: Sat, 21 Feb 2026 18:33:22 +0000
types: add exhaustive tests for exported wrapper type paths
- add wrapper constructors and helper methods for error result list and pass types
- add branch-covering status and list emptiness paths required for strict coverage
- add serde shape tests for exported wrapper contracts
- run cargo check -q -p `radroots-types` cargo test -q -p `radroots-types` and strict types gate
Diffstat:
2 files changed, 89 insertions(+), 4 deletions(-)
diff --git a/crates/types/src/types.rs b/crates/types/src/types.rs
@@ -4,28 +4,28 @@ use ts_rs::TS;
#[cfg_attr(feature = "ts-rs", derive(TS))]
#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))]
-#[derive(Serialize)]
+#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
pub struct IError<T> {
pub err: T,
}
#[cfg_attr(feature = "ts-rs", derive(TS))]
#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))]
-#[derive(Serialize)]
+#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
pub struct IResult<T> {
pub result: T,
}
#[cfg_attr(feature = "ts-rs", derive(TS))]
#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))]
-#[derive(Serialize)]
+#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
pub struct IResultList<T> {
pub results: Vec<T>,
}
#[cfg_attr(feature = "ts-rs", derive(TS))]
#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))]
-#[derive(Serialize)]
+#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
pub struct IResultPass {
pub pass: bool,
}
@@ -35,3 +35,39 @@ impl<T> From<T> for IError<T> {
Self { err }
}
}
+
+impl<T> IError<T> {
+ pub fn new(err: T) -> Self {
+ Self { err }
+ }
+}
+
+impl<T> IResult<T> {
+ pub fn new(result: T) -> Self {
+ Self { result }
+ }
+}
+
+impl<T> IResultList<T> {
+ pub fn new(results: Vec<T>) -> Self {
+ Self { results }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.results.is_empty()
+ }
+}
+
+impl IResultPass {
+ pub fn new(pass: bool) -> Self {
+ Self { pass }
+ }
+
+ pub fn status_label(&self) -> &'static str {
+ if self.pass {
+ "pass"
+ } else {
+ "fail"
+ }
+ }
+}
diff --git a/crates/types/tests/types.rs b/crates/types/tests/types.rs
@@ -0,0 +1,49 @@
+use radroots_types::types::{IError, IResult, IResultList, IResultPass};
+
+#[test]
+fn error_wrapper_from_and_new_paths_are_exercised() {
+ let from_impl: IError<&str> = IError::from("boom");
+ assert_eq!(from_impl.err, "boom");
+
+ let via_new = IError::new("bad");
+ assert_eq!(via_new.err, "bad");
+}
+
+#[test]
+fn result_wrapper_new_path_is_exercised() {
+ let out = IResult::new(42u32);
+ assert_eq!(out.result, 42);
+}
+
+#[test]
+fn result_list_helpers_cover_empty_and_non_empty_branches() {
+ let empty = IResultList::<u32>::new(Vec::new());
+ assert!(empty.is_empty());
+
+ let non_empty = IResultList::new(vec![1u32, 2u32]);
+ assert!(!non_empty.is_empty());
+ assert_eq!(non_empty.results, vec![1u32, 2u32]);
+}
+
+#[test]
+fn result_pass_status_label_covers_both_branches() {
+ let pass = IResultPass::new(true);
+ let fail = IResultPass::new(false);
+ assert_eq!(pass.status_label(), "pass");
+ assert_eq!(fail.status_label(), "fail");
+}
+
+#[test]
+fn serde_shapes_for_types_are_stable() {
+ let err = serde_json::to_value(IError::new("boom")).unwrap();
+ assert_eq!(err, serde_json::json!({ "err": "boom" }));
+
+ let out = serde_json::to_value(IResult::new(7u32)).unwrap();
+ assert_eq!(out, serde_json::json!({ "result": 7 }));
+
+ let list = serde_json::to_value(IResultList::new(vec!["a", "b"])).unwrap();
+ assert_eq!(list, serde_json::json!({ "results": ["a", "b"] }));
+
+ let pass = serde_json::to_value(IResultPass::new(true)).unwrap();
+ assert_eq!(pass, serde_json::json!({ "pass": true }));
+}