@@ -870,4 +870,117 @@ mod tests {
870870 . expect ( "serialize" ) ;
871871 assert ! ( v. get( "logs" ) . is_some( ) || v. get( "result" ) . is_some( ) ) ;
872872 }
873+
874+ // ── Field builder helpers ────────────────────────────────────
875+
876+ #[ test]
877+ fn required_string_builds_required_string_field ( ) {
878+ let f = required_string ( "api_key" , "Auth key" ) ;
879+ assert_eq ! ( f. name, "api_key" ) ;
880+ assert_eq ! ( f. comment, "Auth key" ) ;
881+ assert ! ( f. required) ;
882+ assert ! ( matches!( f. ty, TypeSchema :: String ) ) ;
883+ }
884+
885+ #[ test]
886+ fn optional_string_builds_option_string_field ( ) {
887+ let f = optional_string ( "model" , "model name" ) ;
888+ assert ! ( !f. required) ;
889+ match & f. ty {
890+ TypeSchema :: Option ( inner) => assert ! ( matches!( * * inner, TypeSchema :: String ) ) ,
891+ other => panic ! ( "expected Option<String>, got {other:?}" ) ,
892+ }
893+ }
894+
895+ #[ test]
896+ fn optional_bool_builds_option_bool_field ( ) {
897+ let f = optional_bool ( "enabled" , "Whether enabled" ) ;
898+ assert ! ( !f. required) ;
899+ match & f. ty {
900+ TypeSchema :: Option ( inner) => assert ! ( matches!( * * inner, TypeSchema :: Bool ) ) ,
901+ other => panic ! ( "expected Option<Bool>, got {other:?}" ) ,
902+ }
903+ }
904+
905+ // ── deserialize_params helper ────────────────────────────────
906+
907+ #[ test]
908+ fn deserialize_params_parses_model_settings_update ( ) {
909+ let mut m = Map :: new ( ) ;
910+ m. insert ( "api_key" . into ( ) , Value :: String ( "sk-123" . into ( ) ) ) ;
911+ m. insert (
912+ "default_temperature" . into ( ) ,
913+ Value :: Number ( serde_json:: Number :: from_f64 ( 0.7 ) . unwrap ( ) ) ,
914+ ) ;
915+ let out: ModelSettingsUpdate = deserialize_params ( m) . unwrap ( ) ;
916+ assert_eq ! ( out. api_key. as_deref( ) , Some ( "sk-123" ) ) ;
917+ assert_eq ! ( out. default_temperature, Some ( 0.7 ) ) ;
918+ assert ! ( out. api_url. is_none( ) ) ;
919+ assert ! ( out. default_model. is_none( ) ) ;
920+ }
921+
922+ #[ test]
923+ fn deserialize_params_parses_memory_settings_update ( ) {
924+ let mut m = Map :: new ( ) ;
925+ m. insert ( "backend" . into ( ) , Value :: String ( "sqlite" . into ( ) ) ) ;
926+ m. insert ( "auto_save" . into ( ) , Value :: Bool ( true ) ) ;
927+ m. insert (
928+ "embedding_dimensions" . into ( ) ,
929+ Value :: Number ( serde_json:: Number :: from ( 1536 ) ) ,
930+ ) ;
931+ let out: MemorySettingsUpdate = deserialize_params ( m) . unwrap ( ) ;
932+ assert_eq ! ( out. backend. as_deref( ) , Some ( "sqlite" ) ) ;
933+ assert_eq ! ( out. auto_save, Some ( true ) ) ;
934+ assert_eq ! ( out. embedding_dimensions, Some ( 1536 ) ) ;
935+ }
936+
937+ #[ test]
938+ fn deserialize_params_parses_workspace_onboarding_flag_params ( ) {
939+ let out: WorkspaceOnboardingFlagParams = deserialize_params ( Map :: new ( ) ) . unwrap ( ) ;
940+ assert ! ( out. flag_name. is_none( ) ) ;
941+
942+ let mut m = Map :: new ( ) ;
943+ m. insert ( "flag_name" . into ( ) , Value :: String ( ".custom_marker" . into ( ) ) ) ;
944+ let out: WorkspaceOnboardingFlagParams = deserialize_params ( m) . unwrap ( ) ;
945+ assert_eq ! ( out. flag_name. as_deref( ) , Some ( ".custom_marker" ) ) ;
946+ }
947+
948+ #[ test]
949+ fn deserialize_params_parses_workspace_onboarding_flag_set_params ( ) {
950+ let mut m = Map :: new ( ) ;
951+ m. insert ( "value" . into ( ) , Value :: Bool ( true ) ) ;
952+ let out: WorkspaceOnboardingFlagSetParams = deserialize_params ( m) . unwrap ( ) ;
953+ assert_eq ! ( out. value, true ) ;
954+ assert ! ( out. flag_name. is_none( ) ) ;
955+ }
956+
957+ #[ test]
958+ fn deserialize_params_rejects_wrong_types_with_invalid_params_prefix ( ) {
959+ let mut m = Map :: new ( ) ;
960+ m. insert (
961+ "default_temperature" . into ( ) ,
962+ Value :: String ( "not-a-number" . into ( ) ) ,
963+ ) ;
964+ let err = deserialize_params :: < ModelSettingsUpdate > ( m) . unwrap_err ( ) ;
965+ assert ! ( err. starts_with( "invalid params" ) ) ;
966+ }
967+
968+ #[ test]
969+ fn deserialize_params_requires_value_on_set_onboarding ( ) {
970+ let err = deserialize_params :: < OnboardingCompletedSetParams > ( Map :: new ( ) ) . unwrap_err ( ) ;
971+ assert ! ( err. contains( "invalid params" ) ) ;
972+ }
973+
974+ #[ test]
975+ fn deserialize_params_rejects_missing_required_for_set_browser_allow_all ( ) {
976+ let err = deserialize_params :: < SetBrowserAllowAllParams > ( Map :: new ( ) ) . unwrap_err ( ) ;
977+ assert ! ( err. contains( "invalid params" ) ) ;
978+ }
979+
980+ #[ test]
981+ fn default_onboarding_flag_constant_points_to_hidden_marker ( ) {
982+ // Keeps the constant's observable value pinned so tool behavior
983+ // stays stable across refactors.
984+ assert_eq ! ( DEFAULT_ONBOARDING_FLAG_NAME , ".skip_onboarding" ) ;
985+ }
873986}
0 commit comments